Ad-hoc mode beacons are not transmitted for a while after a merge
with another network.

http://madwifi.org/ticket/1033

This patch was rebased for madwifi-0.9.3.1

Signed-Off by: Luis Rodriguez <mcgrof@winlab.rutgers.edu>

diff -Naurp madwifi-0.9.3.3.old/ath/if_ath.c madwifi-0.9.3.3/ath/if_ath.c
--- madwifi-0.9.3.3.old/ath/if_ath.c	2007-07-26 09:42:40.000000000 -0400
+++ madwifi-0.9.3.3/ath/if_ath.c	2007-11-07 18:10:37.000000000 -0500
@@ -4426,6 +4426,9 @@ ath_beacon_free(struct ath_softc *sc)
  * the beacon miss handling so we'll receive a BMISS
  * interrupt when we stop seeing beacons from the AP
  * we've associated with.
+ *
+ * Note : TBTT is Target Beacon Transmission Time (see IEEE 
+ * 802.11-1999: 4 & 11.2.1.3).
  */
 static void
 ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap)
@@ -4435,50 +4438,88 @@ ath_beacon_config(struct ath_softc *sc, 
 	struct ieee80211com *ic = &sc->sc_ic;
 	struct ath_hal *ah = sc->sc_ah;
 	struct ieee80211_node *ni;
-	u_int32_t nexttbtt, intval;
+	u_int32_t nexttbtt = 0;
+	u_int32_t intval;
+	u_int64_t tsf, hw_tsf;
+	u_int32_t tsftu, hw_tsftu;
+	int reset_tsf = 0;
 
 	if (vap == NULL)
 		vap = TAILQ_FIRST(&ic->ic_vaps);   /* XXX */
 
 	ni = vap->iv_bss;
 
-	/* extract tstamp from last beacon and convert to TU */
-	nexttbtt = TSF_TO_TU(LE_READ_4(ni->ni_tstamp.data + 4),
-			     LE_READ_4(ni->ni_tstamp.data));
-	/* XXX conditionalize multi-bss support? */
+	hw_tsf = ath_hal_gettsf64(ah);
+	tsf = le64_to_cpu(ni->ni_tstamp.tsf);
+	hw_tsftu = hw_tsf >> 10;
+	tsftu = tsf >> 10;
+
+	/* We should reset hw TSF only once, so we increment
+	 * ni_tstamp.tsf to avoid resetting the hw TSF multiple
+	 * times */
+
+	if (tsf == 0) {
+		reset_tsf = 1;
+		ni->ni_tstamp.tsf = cpu_to_le64(1);
+	}
+
+	/* XXX: Conditionalize multi-bss support? */
 	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
-		/*
-		 * For multi-bss ap support beacons are either staggered
+		/* For multi-bss ap support beacons are either staggered
 		 * evenly over N slots or burst together.  For the former
 		 * arrange for the SWBA to be delivered for each slot.
-		 * Slots that are not occupied will generate nothing. 
-		 */
+		 * Slots that are not occupied will generate nothing. */
 		/* NB: the beacon interval is kept internally in TU's */
 		intval = ic->ic_lintval & HAL_BEACON_PERIOD;
 		if (sc->sc_stagbeacons)
 			intval /= ATH_BCBUF;	/* for staggered beacons */
 		if ((sc->sc_nostabeacons) &&
-		    (vap->iv_opmode == IEEE80211_M_HOSTAP))
-			nexttbtt = 0;
+			(vap->iv_opmode == IEEE80211_M_HOSTAP))
+			reset_tsf = 1;
 	} else
 		intval = ni->ni_intval & HAL_BEACON_PERIOD;
-	if (nexttbtt == 0)		/* e.g. for ap mode */
+
+#define        FUDGE   2
+	sc->sc_syncbeacon = 0;
+
+	if (reset_tsf) {
+		/* We just created the interface and TSF will be reset to
+		* zero, so next beacon will be sent at the next intval
+		* time */
 		nexttbtt = intval;
-	else if (intval)		/* NB: can be 0 for monitor mode */
-		nexttbtt = roundup(nexttbtt, intval);
-	DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u intval %u (%u)\n",
-		__func__, nexttbtt, intval, ni->ni_intval);
+	} else if (intval) {    /* NB: can be 0 for monitor mode */
+		if (tsf == 1) {
+			/* We have not received any beacons or probe responses. 
+			* The next TBTT must be at least FUDGE ms ahead of the 
+			* hw_tsftu. Also, TSF == 0 is a TBTT - IEEE802.11-1999
+			* 11.1.2.2, although I'm not sure it applies here... */
+			nexttbtt = roundup(hw_tsftu + FUDGE, intval);
+		} else {
+			if (tsf > hw_tsf) {
+				/* We received a beacon, but the HW TSF has 
+				* not been updated (otherwise hw_tsf > tsf).
+				* We cannot use the hardware TSF, so we wait 
+				* to synchronise beacons again. */
+				sc->sc_syncbeacon = 1;
+				goto ath_beacon_config_debug;
+			} else {
+				/* Normal case: we received a beacon to which
+				* we have synchornised. Make sure that 
+				* nexttbtt is at least FUDGE ms ahead of 
+				* hw_tsf. */
+				nexttbtt = tsftu + roundup(hw_tsftu + 
+				FUDGE - tsftu, intval);
+			}
+		}
+	}
+
 	if (ic->ic_opmode == IEEE80211_M_STA &&	!(sc->sc_nostabeacons)) {
 		HAL_BEACON_STATE bs;
-		u_int64_t tsf;
-		u_int32_t tsftu;
 		int dtimperiod, dtimcount;
 		int cfpperiod, cfpcount;
 
-		/*
-		 * Setup dtim and cfp parameters according to
-		 * last beacon we received (which may be none).
-		 */
+		/* Setup DTIM and CFP parameters according to the last beacon 
+		 * we have received (which may not have happened). */
 		dtimperiod = vap->iv_dtim_period;
 		if (dtimperiod <= 0)		/* NB: 0 if not known */
 			dtimperiod = 1;
@@ -4487,13 +4528,13 @@ ath_beacon_config(struct ath_softc *sc, 
 			dtimcount = 0;		/* XXX? */
 		cfpperiod = 1;			/* NB: no PCF support yet */
 		cfpcount = 0;
-#define	FUDGE	2
 		/*
 		 * Pull nexttbtt forward to reflect the current
 		 * TSF and calculate dtim+cfp state for the result.
 		 */
-		tsf = ath_hal_gettsf64(ah);
-		tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
+		nexttbtt = tsftu;
+		if (nexttbtt == 0)		/* e.g. for ap mode */
+			nexttbtt = intval;
 		do {
 			nexttbtt += intval;
 			if (--dtimcount < 0) {
@@ -4501,7 +4542,7 @@ ath_beacon_config(struct ath_softc *sc, 
 				if (--cfpcount < 0)
 					cfpcount = cfpperiod - 1;
 			}
-		} while (nexttbtt < tsftu);
+		} while (nexttbtt < hw_tsftu + FUDGE);
 #undef FUDGE
 		memset(&bs, 0, sizeof(bs));
 		bs.bs_intval = intval;
@@ -4575,7 +4616,7 @@ ath_beacon_config(struct ath_softc *sc, 
 		ath_hal_intrset(ah, sc->sc_imask);
 	} else {
 		ath_hal_intrset(ah, 0);
-		if (nexttbtt == intval)
+		if (reset_tsf)
 			intval |= HAL_BEACON_RESET_TSF;
 		if (ic->ic_opmode == IEEE80211_M_IBSS) {
 			/*
@@ -4612,8 +4653,35 @@ ath_beacon_config(struct ath_softc *sc, 
 		if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol)
 			ath_beacon_start_adhoc(sc, vap);
 	}
-	sc->sc_syncbeacon = 0;
 #undef TSF_TO_TU
+
+	ath_beacon_config_debug:
+	/* We print all debug messages here, in order to preserve the
+	 * time critical aspect of this function */
+	DPRINTF(sc, ATH_DEBUG_BEACON,
+		"%s: ni=%p tsf=%llu hw_tsf=%llu tsftu=%u hw_tsftu=%u\n",
+		__func__, ni, tsf, hw_tsf, tsftu, hw_tsftu);
+
+	if (reset_tsf)
+		/* We just created the interface */
+		DPRINTF(sc, ATH_DEBUG_BEACON, "%s: first beacon\n", __func__);
+	else if (tsf == 1)
+		/* We do not receive any beacons or probe response */
+		DPRINTF(sc, ATH_DEBUG_BEACON,
+				"%s: no beacon received...\n",__func__);
+	else if (tsf > hw_tsf)
+		/* We do receive a beacon and the hw TSF has not been updated */
+		DPRINTF(sc, ATH_DEBUG_BEACON,
+				"%s: beacon received, but TSF is incorrect\n", 
+				__func__);
+	else
+		/* We do receive a beacon in the past, normal case */
+		DPRINTF(sc, ATH_DEBUG_BEACON,
+				"%s: beacon received, TSF is correct\n", 
+				__func__);
+
+	DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt=%u intval=%u\n",
+		__func__, nexttbtt, intval & HAL_BEACON_PERIOD);
 }
 
 static int
@@ -5442,48 +5510,53 @@ ath_recv_mgmt(struct ieee80211_node *ni,
 	struct ath_softc *sc = ni->ni_ic->ic_dev->priv;
 	struct ieee80211vap *vap = ni->ni_vap;
 
-	/*
-	 * Call up first so subsequent work can use information
-	 * potentially stored in the node (e.g. for ibss merge).
-	 */
+	/* Call up first so subsequent work can use information
+	 * potentially stored in the node (e.g. for ibss merge). */
 	sc->sc_recv_mgmt(ni, skb, subtype, rssi, rstamp);
 	switch (subtype) {
 	case IEEE80211_FC0_SUBTYPE_BEACON:
-		/* update rssi statistics for use by the HAL */
+		/* Update RSSI statistics for use by the HAL */
 		ATH_RSSI_LPF(ATH_NODE(ni)->an_halstats.ns_avgbrssi, rssi);
 		if ((sc->sc_syncbeacon || (vap->iv_flags_ext & IEEE80211_FEXT_APPIE_UPDATE)) &&
 		    ni == vap->iv_bss && vap->iv_state == IEEE80211_S_RUN) {
-			/*
-			 * Resync beacon timers using the tsf of the
-			 * beacon frame we just received.
-			 */
+			/* Resync beacon timers using the TSF of the
+			 * beacon frame we just received. */
 			vap->iv_flags_ext &= ~IEEE80211_FEXT_APPIE_UPDATE;
 			ath_beacon_config(sc, vap);
 		}
-		/* fall thru... */
+		/* NB: Fall Through */
 	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
 		if (vap->iv_opmode == IEEE80211_M_IBSS &&
-		    vap->iv_state == IEEE80211_S_RUN) {
-			u_int64_t tsf = ath_extend_tsf(sc->sc_ah, rstamp);
-			/*
-			 * Handle ibss merge as needed; check the tsf on the
+				vap->iv_state == IEEE80211_S_RUN) {
+			/* Don't merge if we have a desired BSSID */
+			if (vap->iv_flags & IEEE80211_F_DESBSSID)
+				break;
+
+			/* To handle IBSS merge, we need the struct
+			 * ieee80211_node which has been updated with the
+			 * BSSID and TSF from the last beacon */
+			ni = ieee80211_find_rxnode(ni->ni_ic,
+				(const struct ieee80211_frame_min *) skb->data);
+			if (ni == NULL)
+				break;
+
+			/* Handle IBSS merge as needed; check the TSF on the
 			 * frame before attempting the merge.  The 802.11 spec
 			 * says the station should change it's bssid to match
 			 * the oldest station with the same ssid, where oldest
 			 * is determined by the tsf.  Note that hardware
 			 * reconfiguration happens through callback to
 			 * ath_newstate as the state machine will go from
-			 * RUN -> RUN when this happens.
-			 */
-			/* jal: added: don't merge if we have a desired
-			   BSSID */
-			if (!(vap->iv_flags & IEEE80211_F_DESBSSID) &&
-				le64_to_cpu(ni->ni_tstamp.tsf) >= tsf) {
-				DPRINTF(sc, ATH_DEBUG_STATE,
-					"ibss merge, rstamp %u tsf %llu "
-					"tstamp %llu\n", rstamp, (long long) tsf,
-					(long long) le64_to_cpu(ni->ni_tstamp.tsf));
-				(void) ieee80211_ibss_merge(ni);
+			 * RUN -> RUN when this happens. */
+			DPRINTF(sc, ATH_DEBUG_BEACON,
+				"check for ibss merge for ni=%p TSF1(t4)=%10u TSF2(t3)=%10llu\n",
+				ni, rstamp, le64_to_cpu(ni->ni_tstamp.tsf));
+
+			if (rstamp < le64_to_cpu(ni->ni_tstamp.tsf)) {
+				DPRINTF(sc, ATH_DEBUG_BEACON,
+				"ibss merge, rstamp %10u local tsf %10llu\n",
+				rstamp, le64_to_cpu(ni->ni_tstamp.tsf));
+				ieee80211_ibss_merge(ni);
 			}
 		}
 		break;
diff -Naurp madwifi-0.9.3.3.old/ath/if_ath.c.orig madwifi-0.9.3.3/ath/if_ath.c.orig
--- madwifi-0.9.3.3.old/ath/if_ath.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ madwifi-0.9.3.3/ath/if_ath.c.orig	2007-11-07 18:10:37.000000000 -0500
@@ -0,0 +1,9890 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 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
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: if_ath.c 2616 2007-07-26 13:42:40Z mrenzmann $
+ */
+
+/*
+ * Driver for the Atheros Wireless LAN controller.
+ *
+ * This software is derived from work of Atsushi Onoe; his contribution
+ * is greatly appreciated.
+ */
+#include "opt_ah.h"
+
+#ifndef AUTOCONF_INCLUDED
+#include <linux/config.h>
+#endif
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/random.h>
+#include <linux/delay.h>
+#include <linux/cache.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/rtnetlink.h>
+#include <asm/uaccess.h>
+
+#include "if_ethersubr.h"		/* for ETHER_IS_MULTICAST */
+#include "if_media.h"
+#include "if_llc.h"
+
+#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_monitor.h>
+#include <net80211/ieee80211_rate.h>
+
+#ifdef USE_HEADERLEN_RESV
+#include <net80211/if_llc.h>
+#endif
+
+#define	AR_DEBUG
+
+#include "net80211/if_athproto.h"
+#include "if_athvar.h"
+#include "ah_desc.h"
+#include "ah_devid.h"			/* XXX to identify chipset */
+
+#ifdef ATH_PCI		/* PCI BUS */
+#include "if_ath_pci.h"
+#endif			/* PCI BUS */
+#ifdef ATH_AHB		/* AHB BUS */
+#include "if_ath_ahb.h"
+#endif			/* AHB BUS */
+
+#ifdef ATH_TX99_DIAG
+#include "ath_tx99.h"
+#endif
+
+/* unaligned little endian access */
+#define LE_READ_2(p)							\
+	((u_int16_t)							\
+	 ((((u_int8_t *)(p))[0]      ) | (((u_int8_t *)(p))[1] <<  8)))
+#define LE_READ_4(p)							\
+	((u_int32_t)							\
+	 ((((u_int8_t *)(p))[0]      ) | (((u_int8_t *)(p))[1] <<  8) |	\
+	  (((u_int8_t *)(p))[2] << 16) | (((u_int8_t *)(p))[3] << 24)))
+
+/* Default rate control algorithm */
+#ifdef CONFIG_ATHEROS_RATE_DEFAULT
+#define DEF_RATE_CTL CONFIG_ATHEROS_RATE_DEFAULT
+#else
+#define DEF_RATE_CTL "sample"
+#endif
+
+enum {
+	ATH_LED_TX,
+	ATH_LED_RX,
+	ATH_LED_POLL,
+};
+
+static struct ieee80211vap *ath_vap_create(struct ieee80211com *,
+	const char *, int, int, int, struct net_device *);
+static void ath_vap_delete(struct ieee80211vap *);
+static int ath_init(struct net_device *);
+static int ath_set_ack_bitrate(struct ath_softc *, int);
+static int ath_reset(struct net_device *);
+static void ath_fatal_tasklet(TQUEUE_ARG);
+static void ath_rxorn_tasklet(TQUEUE_ARG);
+static void ath_bmiss_tasklet(TQUEUE_ARG);
+static void ath_bstuck_tasklet(TQUEUE_ARG);
+static void ath_radar_task(struct work_struct *);
+static void ath_dfs_test_return(unsigned long);
+
+static int ath_stop_locked(struct net_device *);
+static int ath_stop(struct net_device *);
+#if 0
+static void ath_initkeytable(struct ath_softc *);
+#endif
+static int ath_key_alloc(struct ieee80211vap *, const struct ieee80211_key *);
+static int ath_key_delete(struct ieee80211vap *, const struct ieee80211_key *,
+	struct ieee80211_node *);
+static int ath_key_set(struct ieee80211vap *, const struct ieee80211_key *,
+	const u_int8_t mac[IEEE80211_ADDR_LEN]);
+static void ath_key_update_begin(struct ieee80211vap *);
+static void ath_key_update_end(struct ieee80211vap *);
+static void ath_mode_init(struct net_device *);
+static void ath_setslottime(struct ath_softc *);
+static void ath_updateslot(struct net_device *);
+static int ath_beaconq_setup(struct ath_hal *);
+static int ath_beacon_alloc(struct ath_softc *, struct ieee80211_node *);
+#ifdef ATH_SUPERG_DYNTURBO
+static void ath_beacon_dturbo_update(struct ieee80211vap *, int *, u_int8_t);
+static void ath_beacon_dturbo_config(struct ieee80211vap *, u_int32_t);
+static void ath_turbo_switch_mode(unsigned long);
+static int ath_check_beacon_done(struct ath_softc *);
+#endif
+static void ath_beacon_send(struct ath_softc *, int *);
+static void ath_beacon_start_adhoc(struct ath_softc *, struct ieee80211vap *);
+static void ath_beacon_return(struct ath_softc *, struct ath_buf *);
+static void ath_beacon_free(struct ath_softc *);
+static void ath_beacon_config(struct ath_softc *, struct ieee80211vap *);
+static int ath_desc_alloc(struct ath_softc *);
+static void ath_desc_free(struct ath_softc *);
+static void ath_desc_swap(struct ath_desc *);
+static struct ieee80211_node *ath_node_alloc(struct ieee80211_node_table *,
+	struct ieee80211vap *);
+static void ath_node_cleanup(struct ieee80211_node *);
+static void ath_node_free(struct ieee80211_node *);
+static u_int8_t ath_node_getrssi(const struct ieee80211_node *);
+static int ath_rxbuf_init(struct ath_softc *, struct ath_buf *);
+static void ath_recv_mgmt(struct ieee80211_node *, struct sk_buff *, int,
+	int, u_int32_t);
+static void ath_setdefantenna(struct ath_softc *, u_int);
+static struct ath_txq *ath_txq_setup(struct ath_softc *, int, int);
+static void ath_rx_tasklet(TQUEUE_ARG);
+static int ath_hardstart(struct sk_buff *, struct net_device *);
+static int ath_mgtstart(struct ieee80211com *, struct sk_buff *);
+#ifdef ATH_SUPERG_COMP
+static u_int32_t ath_get_icvlen(struct ieee80211_key *);
+static u_int32_t ath_get_ivlen(struct ieee80211_key *);
+static void ath_setup_comp(struct ieee80211_node *, int);
+static void ath_comp_set(struct ieee80211vap *, struct ieee80211_node *, int);	
+#endif
+static int ath_tx_setup(struct ath_softc *, int, int);
+static int ath_wme_update(struct ieee80211com *);
+static void ath_uapsd_flush(struct ieee80211_node *);
+static void ath_tx_cleanupq(struct ath_softc *, struct ath_txq *);
+static void ath_tx_cleanup(struct ath_softc *);
+static void ath_tx_uapsdqueue(struct ath_softc *, struct ath_node *,
+	struct ath_buf *);
+
+static int ath_tx_start(struct net_device *, struct ieee80211_node *,
+	struct ath_buf *, struct sk_buff *, int);
+static void ath_tx_tasklet_q0(TQUEUE_ARG);
+static void ath_tx_tasklet_q0123(TQUEUE_ARG);
+static void ath_tx_tasklet(TQUEUE_ARG);
+static void ath_tx_timeout(struct net_device *);
+static void ath_tx_draintxq(struct ath_softc *, struct ath_txq *);
+static int ath_chan_set(struct ath_softc *, struct ieee80211_channel *);
+static void ath_draintxq(struct ath_softc *);
+static __inline void ath_tx_txqaddbuf(struct ath_softc *, struct ieee80211_node *,
+	struct ath_txq *, struct ath_buf *, struct ath_desc *, int);
+static void ath_stoprecv(struct ath_softc *);
+static int ath_startrecv(struct ath_softc *);
+static void ath_flushrecv(struct ath_softc *);
+static void ath_chan_change(struct ath_softc *, struct ieee80211_channel *);
+static void ath_calibrate(unsigned long);
+static int ath_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+
+static void ath_scan_start(struct ieee80211com *);
+static void ath_scan_end(struct ieee80211com *);
+static void ath_set_channel(struct ieee80211com *);
+static void ath_set_coverageclass(struct ieee80211com *);
+static u_int ath_mhz2ieee(struct ieee80211com *, u_int, u_int);
+#ifdef ATH_SUPERG_FF
+static int athff_can_aggregate(struct ath_softc *, struct ether_header *,
+	struct ath_node *, struct sk_buff *, u_int16_t, int *);
+#endif
+static struct net_device_stats *ath_getstats(struct net_device *);
+static void ath_setup_stationkey(struct ieee80211_node *);
+static void ath_setup_stationwepkey(struct ieee80211_node *);
+static void ath_setup_keycacheslot(struct ath_softc *, struct ieee80211_node *);
+static void ath_newassoc(struct ieee80211_node *, int);
+static int ath_getchannels(struct net_device *, u_int, HAL_BOOL, HAL_BOOL);
+static void ath_led_event(struct ath_softc *, int);
+static void ath_update_txpow(struct ath_softc *);
+
+static int ath_set_mac_address(struct net_device *, void *);
+static int ath_change_mtu(struct net_device *, int);
+static int ath_ioctl(struct net_device *, struct ifreq *, int);
+
+static int ath_rate_setup(struct net_device *, u_int);
+static void ath_setup_subrates(struct net_device *);
+#ifdef ATH_SUPERG_XR
+static int ath_xr_rate_setup(struct net_device *);
+static void ath_grppoll_txq_setup(struct ath_softc *, int, int);
+static void ath_grppoll_start(struct ieee80211vap *, int);
+static void ath_grppoll_stop(struct ieee80211vap *);
+static u_int8_t ath_node_move_data(const struct ieee80211_node *);
+static void ath_grppoll_txq_update(struct ath_softc *, int);
+static void ath_grppoll_period_update(struct ath_softc *);
+#endif
+static void ath_setcurmode(struct ath_softc *, enum ieee80211_phymode);
+
+static void ath_dynamic_sysctl_register(struct ath_softc *);
+static void ath_dynamic_sysctl_unregister(struct ath_softc *);
+static void ath_announce(struct net_device *);
+static int ath_descdma_setup(struct ath_softc *, struct ath_descdma *,
+	ath_bufhead *, const char *, int, int);
+static void ath_descdma_cleanup(struct ath_softc *, struct ath_descdma *,
+	ath_bufhead *, int);
+static void ath_check_dfs_clear(unsigned long);
+static const char *ath_get_hal_status_desc(HAL_STATUS status);
+static int ath_rcv_dev_event(struct notifier_block *, unsigned long, void *);
+	
+static int ath_calinterval = ATH_SHORT_CALINTERVAL;		/*
+								 * calibrate every 30 secs in steady state
+								 * but check every second at first.
+								 */
+static int ath_countrycode = CTRY_DEFAULT;	/* country code */
+static int ath_outdoor = AH_FALSE;		/* enable outdoor use */
+static int ath_xchanmode = AH_TRUE;		/* enable extended channels */
+static char *autocreate = NULL;
+static char *ratectl = DEF_RATE_CTL;
+static int rfkill = -1;
+static int countrycode = -1;
+static int outdoor = -1;
+static int xchanmode = -1;
+
+static const char *hal_status_desc[] = {
+	"No error",
+	"No hardware present or device not yet supported",
+	"Memory allocation failed",
+	"Hardware didn't respond as expected",
+	"EEPROM magic number invalid",
+	"EEPROM version invalid",
+	"EEPROM unreadable",
+	"EEPROM checksum invalid",
+	"EEPROM read problem",
+	"EEPROM mac address invalid",
+	"EEPROM size not supported",
+	"Attempt to change write-locked EEPROM",
+	"Invalid parameter to function",
+	"Hardware revision not supported",
+	"Hardware self-test failed",
+	"Operation incomplete"
+};
+
+static struct notifier_block ath_event_block = {
+        .notifier_call = ath_rcv_dev_event
+};
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,52))
+MODULE_PARM(countrycode, "i");
+MODULE_PARM(outdoor, "i");
+MODULE_PARM(xchanmode, "i");
+MODULE_PARM(rfkill, "i");
+MODULE_PARM(autocreate, "s");
+MODULE_PARM(ratectl, "s");
+#else
+#include <linux/moduleparam.h>
+module_param(countrycode, int, 0600);
+module_param(outdoor, int, 0600);
+module_param(xchanmode, int, 0600);
+module_param(rfkill, int, 0600);
+module_param(autocreate, charp, 0600);
+module_param(ratectl, charp, 0600);
+#endif
+MODULE_PARM_DESC(countrycode, "Override default country code");
+MODULE_PARM_DESC(outdoor, "Enable/disable outdoor use");
+MODULE_PARM_DESC(xchanmode, "Enable/disable extended channel mode");
+MODULE_PARM_DESC(rfkill, "Enable/disable RFKILL capability");
+MODULE_PARM_DESC(autocreate, "Create ath device in [sta|ap|wds|adhoc|ahdemo|monitor] mode. defaults to sta, use 'none' to disable");
+MODULE_PARM_DESC(ratectl, "Rate control algorithm [amrr|onoe|sample], defaults to '" DEF_RATE_CTL "'");
+
+static int	ath_debug = 0;
+#ifdef AR_DEBUG
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,52))
+MODULE_PARM(ath_debug, "i");
+#else
+module_param(ath_debug, int, 0600);
+#endif
+MODULE_PARM_DESC(ath_debug, "Load-time debug output enable");
+
+#define	IFF_DUMPPKTS(sc, _m) \
+	((sc->sc_debug & _m))
+static void ath_printrxbuf(struct ath_buf *, int);
+static void ath_printtxbuf(struct ath_buf *, int);
+enum {
+	ATH_DEBUG_XMIT		= 0x00000001,	/* basic xmit operation */
+	ATH_DEBUG_XMIT_DESC	= 0x00000002,	/* xmit descriptors */
+	ATH_DEBUG_RECV		= 0x00000004,	/* basic recv operation */
+	ATH_DEBUG_RECV_DESC	= 0x00000008,	/* recv descriptors */
+	ATH_DEBUG_RATE		= 0x00000010,	/* rate control */
+	ATH_DEBUG_RESET		= 0x00000020,	/* reset processing */
+	/* 0x00000040 was ATH_DEBUG_MODE */
+	ATH_DEBUG_BEACON 	= 0x00000080,	/* beacon handling */
+	ATH_DEBUG_WATCHDOG 	= 0x00000100,	/* watchdog timeout */
+	ATH_DEBUG_INTR		= 0x00001000,	/* ISR */
+	ATH_DEBUG_TX_PROC	= 0x00002000,	/* tx ISR proc */
+	ATH_DEBUG_RX_PROC	= 0x00004000,	/* rx ISR proc */
+	ATH_DEBUG_BEACON_PROC	= 0x00008000,	/* beacon ISR proc */
+	ATH_DEBUG_CALIBRATE	= 0x00010000,	/* periodic calibration */
+	ATH_DEBUG_KEYCACHE	= 0x00020000,	/* key cache management */
+	ATH_DEBUG_STATE		= 0x00040000,	/* 802.11 state transitions */
+	ATH_DEBUG_NODE		= 0x00080000,	/* node management */
+	ATH_DEBUG_LED		= 0x00100000,	/* led management */
+	ATH_DEBUG_FF		= 0x00200000,	/* fast frames */
+	ATH_DEBUG_TURBO		= 0x00400000,	/* turbo/dynamic turbo */
+	ATH_DEBUG_UAPSD		= 0x00800000,	/* uapsd */
+	ATH_DEBUG_DOTH		= 0x01000000,	/* 11.h */
+	ATH_DEBUG_FATAL		= 0x80000000,	/* fatal errors */
+	ATH_DEBUG_ANY		= 0xffffffff
+};
+#define	DPRINTF(sc, _m, _fmt, ...) do {				\
+	if (sc->sc_debug & (_m))				\
+		printk(_fmt, __VA_ARGS__);			\
+} while (0)
+#define	KEYPRINTF(sc, ix, hk, mac) do {				\
+	if (sc->sc_debug & ATH_DEBUG_KEYCACHE)			\
+		ath_keyprint(sc, __func__, ix, hk, mac);	\
+} while (0)
+#else /* defined(AR_DEBUG) */
+#define	IFF_DUMPPKTS(sc, _m)	netif_msg_dumppkts(&sc->sc_ic)
+#define	DPRINTF(sc, _m, _fmt, ...)
+#define	KEYPRINTF(sc, k, ix, mac)
+#endif /* defined(AR_DEBUG) */
+
+#define ATH_SETUP_XR_VAP(sc,vap,rfilt) \
+	do { \
+		if (sc->sc_curchan.privFlags & CHANNEL_4MS_LIMIT) \
+			vap->iv_fragthreshold = XR_4MS_FRAG_THRESHOLD; \
+		else \
+			vap->iv_fragthreshold = vap->iv_xrvap->iv_fragthreshold; \
+		if (!sc->sc_xrgrppoll) { \
+			ath_grppoll_txq_setup(sc, HAL_TX_QUEUE_DATA, GRP_POLL_PERIOD_NO_XR_STA(sc)); \
+			ath_grppoll_start(vap, sc->sc_xrpollcount); \
+			ath_hal_setrxfilter(sc->sc_ah, rfilt|HAL_RX_FILTER_XRPOLL); \
+		} \
+   	} while(0)
+
+/*
+ * Define the scheme that we select MAC address for multiple BSS on the same radio.
+ * The very first VAP will just use the MAC address from the EEPROM.
+ * For the next 3 VAPs, we set the U/L bit (bit 1) in MAC address,
+ * and use the next two bits as the index of the VAP.
+ */
+#define ATH_SET_VAP_BSSID_MASK(bssid_mask)      ((bssid_mask)[0] &= ~(((ATH_BCBUF-1)<<2)|0x02))
+#define ATH_GET_VAP_ID(bssid)                   ((bssid)[0] >> 2)
+#define ATH_SET_VAP_BSSID(bssid, id)            \
+    	do {                                    \
+		if (id)                            \
+            		(bssid)[0] |= (((id) << 2) | 0x02); \
+	} while(0)
+
+int
+ath_attach(u_int16_t devid, struct net_device *dev, HAL_BUS_TAG tag)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah;
+	HAL_STATUS status;
+	int error = 0, i;
+	int autocreatemode = IEEE80211_M_STA;
+	u_int8_t csz;
+
+	sc->devid = devid;
+	sc->sc_debug = ath_debug;
+	DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid);
+
+	/*
+	 * Cache line size is used to size and align various
+	 * structures used to communicate with the hardware.
+	 */
+	bus_read_cachesize(sc, &csz);
+	/* XXX assert csz is non-zero */
+	sc->sc_cachelsz = csz << 2;		/* convert to bytes */
+
+	ATH_LOCK_INIT(sc);
+	ATH_TXBUF_LOCK_INIT(sc);
+	ATH_RXBUF_LOCK_INIT(sc);
+
+	ATH_INIT_TQUEUE(&sc->sc_rxtq,	 ath_rx_tasklet,	dev);
+	ATH_INIT_TQUEUE(&sc->sc_txtq,	 ath_tx_tasklet,	dev);
+	ATH_INIT_TQUEUE(&sc->sc_bmisstq, ath_bmiss_tasklet,	dev);
+	ATH_INIT_TQUEUE(&sc->sc_bstucktq,ath_bstuck_tasklet,	dev);
+	ATH_INIT_TQUEUE(&sc->sc_rxorntq, ath_rxorn_tasklet,	dev);
+	ATH_INIT_TQUEUE(&sc->sc_fataltq, ath_fatal_tasklet,	dev);
+	ATH_INIT_WORK(&sc->sc_radartask, ath_radar_task);
+
+	/*
+	 * Attach the HAL and verify ABI compatibility by checking
+	 * the HAL's ABI signature against the one the driver was
+	 * compiled with.  A mismatch indicates the driver was
+	 * built with an ah.h that does not correspond to the HAL
+	 * module loaded in the kernel.
+	 */
+	ah = _ath_hal_attach(devid, sc, tag, sc->sc_iobase, &status);
+	if (ah == NULL) {
+		printk(KERN_ERR "%s: unable to attach hardware: '%s' (HAL status %u)\n",
+			dev->name, ath_get_hal_status_desc(status), status);
+		error = ENXIO;
+		goto bad;
+	}
+	if (ah->ah_abi != HAL_ABI_VERSION) {
+		printk(KERN_ERR "%s: HAL ABI mismatch; "
+			"driver expects 0x%x, HAL reports 0x%x\n",
+			dev->name, HAL_ABI_VERSION, ah->ah_abi);
+		error = ENXIO;		/* XXX */
+		goto bad;
+	}
+	sc->sc_ah = ah;
+
+	/*
+	 * Check if the MAC has multi-rate retry support.
+	 * We do this by trying to setup a fake extended
+	 * descriptor.  MAC's that don't have support will
+	 * return false w/o doing anything.  MAC's that do
+	 * support it will return true w/o doing anything.
+	 */
+	sc->sc_mrretry = ath_hal_setupxtxdesc(ah, NULL, 0,0, 0,0, 0,0);
+
+	/*
+	 * Check if the device has hardware counters for PHY
+	 * errors.  If so we need to enable the MIB interrupt
+	 * so we can act on stat triggers.
+	 */
+	if (ath_hal_hwphycounters(ah))
+		sc->sc_needmib = 1;
+
+	/*
+	 * Get the hardware key cache size.
+	 */
+	sc->sc_keymax = ath_hal_keycachesize(ah);
+	if (sc->sc_keymax > ATH_KEYMAX) {
+		printk("%s: Warning, using only %u entries in %u key cache\n",
+			dev->name, ATH_KEYMAX, sc->sc_keymax);
+		sc->sc_keymax = ATH_KEYMAX;
+	}
+	/*
+	 * Reset the key cache since some parts do not
+	 * reset the contents on initial power up.
+	 */
+	for (i = 0; i < sc->sc_keymax; i++)
+		ath_hal_keyreset(ah, i);
+
+	/*
+	 * Collect the channel list using the default country
+	 * code and including outdoor channels.  The 802.11 layer
+	 * is responsible for filtering this list based on settings
+	 * like the phy mode.
+	 */
+	if (countrycode != -1)
+		ath_countrycode = countrycode;
+	if (outdoor != -1)
+		ath_outdoor = outdoor;
+	if (xchanmode != -1)
+		ath_xchanmode = xchanmode;
+	error = ath_getchannels(dev, ath_countrycode,
+			ath_outdoor, ath_xchanmode);
+	if (error != 0)
+		goto bad;
+
+	ic->ic_country_code = ath_countrycode;
+	ic->ic_country_outdoor = ath_outdoor;
+
+	if (rfkill != -1) {
+		printk(KERN_INFO "ath_pci: switching rfkill capability %s\n",
+			rfkill ? "on" : "off");	
+		ath_hal_setrfsilent(ah, rfkill);
+	}
+
+	/*
+	 * Setup rate tables for all potential media types.
+	 */
+	ath_rate_setup(dev, IEEE80211_MODE_11A);
+	ath_rate_setup(dev, IEEE80211_MODE_11B);
+	ath_rate_setup(dev, IEEE80211_MODE_11G);
+	ath_rate_setup(dev, IEEE80211_MODE_TURBO_A);
+	ath_rate_setup(dev, IEEE80211_MODE_TURBO_G);
+
+	/* Setup for half/quarter rates */
+	ath_setup_subrates(dev);
+
+	/* NB: setup here so ath_rate_update is happy */
+	ath_setcurmode(sc, IEEE80211_MODE_11A);
+
+	/*
+	 * Allocate tx+rx descriptors and populate the lists.
+	 */
+	error = ath_desc_alloc(sc);
+	if (error != 0) {
+		printk(KERN_ERR "%s: failed to allocate descriptors: %d\n",
+			dev->name, error);
+		goto bad;
+	}
+
+	/*
+	 * Init ic_caps prior to queue init, since WME cap setting
+	 * depends on queue setup.
+	 */
+	ic->ic_caps = 0;
+
+	/*
+	 * Allocate hardware transmit queues: one queue for
+	 * beacon frames and one data queue for each QoS
+	 * priority.  Note that the HAL handles resetting
+	 * these queues at the needed time.
+	 *
+	 * XXX PS-Poll
+	 */
+	sc->sc_bhalq = ath_beaconq_setup(ah);
+	if (sc->sc_bhalq == (u_int) -1) {
+		printk(KERN_ERR "%s: unable to setup a beacon xmit queue!\n",
+			dev->name);
+		error = EIO;
+		goto bad2;
+	}
+	sc->sc_cabq = ath_txq_setup(sc, HAL_TX_QUEUE_CAB, 0);
+	if (sc->sc_cabq == NULL) {
+		printk(KERN_ERR "%s: unable to setup CAB xmit queue!\n",
+			dev->name);
+		error = EIO;
+		goto bad2;
+	}
+	/* NB: ensure BK queue is the lowest priority h/w queue */
+	if (!ath_tx_setup(sc, WME_AC_BK, HAL_WME_AC_BK)) {
+		printk(KERN_ERR "%s: unable to setup xmit queue for %s traffic!\n",
+			dev->name, ieee80211_wme_acnames[WME_AC_BK]);
+		error = EIO;
+		goto bad2;
+	}
+	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,
+		 * we allocate queues when we switch from station to
+		 * AP mode.
+		 */
+		if (sc->sc_ac2q[WME_AC_VI] != NULL)
+			ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_VI]);
+		if (sc->sc_ac2q[WME_AC_BE] != NULL)
+			ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_BE]);
+		sc->sc_ac2q[WME_AC_BE] = sc->sc_ac2q[WME_AC_BK];
+		sc->sc_ac2q[WME_AC_VI] = sc->sc_ac2q[WME_AC_BK];
+		sc->sc_ac2q[WME_AC_VO] = sc->sc_ac2q[WME_AC_BK];
+	} else {
+		/*
+		 * Mark WME capability since we have sufficient
+		 * hardware queues to do proper priority scheduling.
+		 */
+		ic->ic_caps |= IEEE80211_C_WME;
+		sc->sc_uapsdq = ath_txq_setup(sc, HAL_TX_QUEUE_UAPSD, 0);
+		if (sc->sc_uapsdq == NULL)
+			DPRINTF(sc, ATH_DEBUG_UAPSD, "%s: unable to setup UAPSD xmit queue!\n",
+				__func__);
+		else {
+			ic->ic_caps |= IEEE80211_C_UAPSD;
+			/*
+			 * default UAPSD on if HW capable
+			 */
+			IEEE80211_COM_UAPSD_ENABLE(ic);
+		}
+	}
+#ifdef ATH_SUPERG_XR
+	ath_xr_rate_setup(dev);
+	sc->sc_xrpollint = XR_DEFAULT_POLL_INTERVAL;
+	sc->sc_xrpollcount = XR_DEFAULT_POLL_COUNT;
+	strcpy(sc->sc_grppoll_str, XR_DEFAULT_GRPPOLL_RATE_STR);
+	sc->sc_grpplq.axq_qnum = -1;
+	sc->sc_xrtxq = ath_txq_setup(sc, HAL_TX_QUEUE_DATA, HAL_XR_DATA);
+#endif
+
+	/*
+	 * Special case certain configurations.  Note the
+	 * CAB queue is handled by these specially so don't
+	 * include them when checking the txq setup mask.
+	 */
+	switch (sc->sc_txqsetup &~ ((1<<sc->sc_cabq->axq_qnum) |
+				(sc->sc_uapsdq ? (1<<sc->sc_uapsdq->axq_qnum) : 0))) {
+	case 0x01:
+		ATH_INIT_TQUEUE(&sc->sc_txtq, ath_tx_tasklet_q0, dev);
+		break;
+	case 0x0f:
+		ATH_INIT_TQUEUE(&sc->sc_txtq, ath_tx_tasklet_q0123, dev);
+		break;
+	}
+
+	sc->sc_setdefantenna = ath_setdefantenna;
+	sc->sc_rc = ieee80211_rate_attach(sc, ratectl);
+	if (sc->sc_rc == NULL) {
+		error = EIO;
+		goto bad2;
+	}
+
+	init_timer(&sc->sc_cal_ch);
+	sc->sc_cal_ch.function = ath_calibrate;
+	sc->sc_cal_ch.data = (unsigned long) dev;
+
+#ifdef ATH_SUPERG_DYNTURBO
+	init_timer(&sc->sc_dturbo_switch_mode);
+	sc->sc_dturbo_switch_mode.function = ath_turbo_switch_mode;
+	sc->sc_dturbo_switch_mode.data = (unsigned long) dev;
+#endif
+
+	sc->sc_blinking = 0;
+	sc->sc_ledstate = 1;
+	sc->sc_ledon = 0;			/* low true */
+	sc->sc_ledidle = msecs_to_jiffies(2700);	/* 2.7 sec */
+	sc->sc_dfstesttime = ATH_DFS_TEST_RETURN_PERIOD;
+	init_timer(&sc->sc_ledtimer);
+	init_timer(&sc->sc_dfswaittimer);
+	init_timer(&sc->sc_dfstesttimer);
+	sc->sc_ledtimer.data = (unsigned long) sc;
+	if (sc->sc_softled) {
+		ath_hal_gpioCfgOutput(ah, sc->sc_ledpin);
+		ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon);
+	}
+
+	/* NB: ether_setup is done by bus-specific code */
+	dev->open = ath_init;
+	dev->stop = ath_stop;
+	dev->hard_start_xmit = ath_hardstart;
+	dev->tx_timeout = ath_tx_timeout;
+	dev->watchdog_timeo = 5 * HZ;			/* XXX */
+	dev->set_multicast_list = ath_mode_init;
+	dev->do_ioctl = ath_ioctl;
+	dev->get_stats = ath_getstats;
+	dev->set_mac_address = ath_set_mac_address;
+ 	dev->change_mtu = ath_change_mtu;
+	dev->tx_queue_len = ATH_TXBUF - 1;		/* 1 for mgmt frame */
+#ifdef USE_HEADERLEN_RESV
+	dev->hard_header_len += sizeof(struct ieee80211_qosframe) +
+				sizeof(struct llc) +
+				IEEE80211_ADDR_LEN +
+				IEEE80211_WEP_IVLEN +
+				IEEE80211_WEP_KIDLEN;
+#ifdef ATH_SUPERG_FF
+	dev->hard_header_len += ATH_FF_MAX_HDR;
+#endif
+#endif
+	ic->ic_dev = dev;
+	ic->ic_mgtstart = ath_mgtstart;
+	ic->ic_init = ath_init;
+	ic->ic_reset = ath_reset;
+	ic->ic_newassoc = ath_newassoc;
+	ic->ic_updateslot = ath_updateslot;
+
+	ic->ic_wme.wme_update = ath_wme_update;
+	ic->ic_uapsd_flush = ath_uapsd_flush;
+
+	/* XXX not right but it's not used anywhere important */
+	ic->ic_phytype = IEEE80211_T_OFDM;
+	ic->ic_opmode = IEEE80211_M_STA;
+	sc->sc_opmode = HAL_M_STA;
+	/* 
+	 * Set the Atheros Advanced Capabilities from station config before 
+	 * starting 802.11 state machine.  Currently, set only fast-frames 
+	 * capability.
+	 */
+	ic->ic_ath_cap = 0;
+	sc->sc_fftxqmin = ATH_FF_TXQMIN;
+#ifdef ATH_SUPERG_FF
+	ic->ic_ath_cap |= (ath_hal_fastframesupported(ah) ? IEEE80211_ATHC_FF : 0);
+#endif
+	ic->ic_ath_cap |= (ath_hal_burstsupported(ah) ? IEEE80211_ATHC_BURST : 0);
+
+#ifdef ATH_SUPERG_COMP
+	ic->ic_ath_cap |= (ath_hal_compressionsupported(ah) ? IEEE80211_ATHC_COMP : 0); 
+#endif
+
+#ifdef ATH_SUPERG_DYNTURBO
+	ic->ic_ath_cap |= (ath_hal_turboagsupported(ah) ? (IEEE80211_ATHC_TURBOP |
+							IEEE80211_ATHC_AR) : 0);
+#endif
+#ifdef ATH_SUPERG_XR
+	ic->ic_ath_cap |= (ath_hal_xrsupported(ah) ? IEEE80211_ATHC_XR : 0);
+#endif
+
+	ic->ic_caps |=
+		  IEEE80211_C_IBSS		/* ibss, nee adhoc, mode */
+		| IEEE80211_C_HOSTAP		/* hostap mode */
+		| IEEE80211_C_MONITOR		/* monitor mode */
+		| IEEE80211_C_AHDEMO		/* adhoc demo mode */
+		| 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 */
+		;
+	/*
+	 * Query the HAL to figure out h/w crypto support.
+	 */
+	if (ath_hal_ciphersupported(ah, HAL_CIPHER_WEP))
+		ic->ic_caps |= IEEE80211_C_WEP;
+	if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_OCB))
+		ic->ic_caps |= IEEE80211_C_AES;
+	if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_CCM))
+		ic->ic_caps |= IEEE80211_C_AES_CCM;
+	if (ath_hal_ciphersupported(ah, HAL_CIPHER_CKIP))
+		ic->ic_caps |= IEEE80211_C_CKIP;
+	if (ath_hal_ciphersupported(ah, HAL_CIPHER_TKIP)) {
+		ic->ic_caps |= IEEE80211_C_TKIP;
+		/*
+		 * Check if h/w does the MIC and/or whether the
+		 * separate key cache entries are required to
+		 * handle both tx+rx MIC keys.
+		 */
+		if (ath_hal_ciphersupported(ah, HAL_CIPHER_MIC)) {
+			ic->ic_caps |= IEEE80211_C_TKIPMIC;
+			/*
+			 * Check if h/w does MIC correctly when
+			 * WMM is turned on.
+			 */
+			if (ath_hal_wmetkipmic(ah))
+				ic->ic_caps |= IEEE80211_C_WME_TKIPMIC;
+		}
+
+		/*
+		 * 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);
+#if 0
+	sc->sc_mcastkey = ath_hal_getmcastkeysearch(ah);
+#endif
+	/*
+	 * 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
+	 * support a global cap.
+	 */
+	sc->sc_hastpc = ath_hal_hastpc(ah);
+	if (sc->sc_hastpc || ath_hal_hastxpowlimit(ah))
+		ic->ic_caps |= IEEE80211_C_TXPMGT;
+
+	/*
+	 * Default 11.h to start enabled.
+	 */
+	ic->ic_flags |= IEEE80211_F_DOTH;
+	
+	/*
+	 * Check for misc other capabilities.
+	 */
+	if (ath_hal_hasbursting(ah))
+		ic->ic_caps |= IEEE80211_C_BURST;
+	sc->sc_hasbmask = ath_hal_hasbssidmask(ah);
+	sc->sc_hastsfadd = ath_hal_hastsfadjust(ah);
+	/*
+	 * Indicate we need the 802.11 header padded to a
+	 * 32-bit boundary for 4-address and QoS frames.
+	 */
+	ic->ic_flags |= IEEE80211_F_DATAPAD;
+
+	/*
+	 * Query the HAL about antenna support
+	 * Enable rx fast diversity if HAL has support
+	 */
+	if (ath_hal_hasdiversity(ah)) {
+		sc->sc_hasdiversity = 1;
+		ath_hal_setdiversity(ah, AH_TRUE);
+		sc->sc_diversity = 1;
+	} else {
+		sc->sc_hasdiversity = 0;
+		sc->sc_diversity = 0;
+		ath_hal_setdiversity(ah, AH_FALSE);
+	}
+	sc->sc_defant = ath_hal_getdefantenna(ah);
+
+	/*
+	 * Not all chips have the VEOL support we want to
+	 * use with IBSS beacons; check here for it.
+	 */
+	sc->sc_hasveol = ath_hal_hasveol(ah);
+
+	/* get mac address from hardware */
+	ath_hal_getmac(ah, ic->ic_myaddr);
+	if (sc->sc_hasbmask) {
+		ath_hal_getbssidmask(ah, sc->sc_bssidmask);
+		ATH_SET_VAP_BSSID_MASK(sc->sc_bssidmask);
+		ath_hal_setbssidmask(ah, sc->sc_bssidmask);
+	}
+	IEEE80211_ADDR_COPY(dev->dev_addr, ic->ic_myaddr);
+
+	/* call MI attach routine. */
+	ieee80211_ifattach(ic);
+	/* override default methods */
+	ic->ic_node_alloc = ath_node_alloc;
+	sc->sc_node_free = ic->ic_node_free;
+	ic->ic_node_free = ath_node_free;
+	ic->ic_node_getrssi = ath_node_getrssi;
+#ifdef ATH_SUPERG_XR
+	ic->ic_node_move_data = ath_node_move_data;
+#endif
+	sc->sc_node_cleanup = ic->ic_node_cleanup;
+	ic->ic_node_cleanup = ath_node_cleanup;
+	sc->sc_recv_mgmt = ic->ic_recv_mgmt;
+	ic->ic_recv_mgmt = ath_recv_mgmt;
+
+	ic->ic_vap_create = ath_vap_create;
+	ic->ic_vap_delete = ath_vap_delete;
+
+	ic->ic_scan_start = ath_scan_start;
+	ic->ic_scan_end = ath_scan_end;
+	ic->ic_set_channel = ath_set_channel;
+
+	ic->ic_set_coverageclass = ath_set_coverageclass;
+	ic->ic_mhz2ieee = ath_mhz2ieee;
+
+	if (register_netdev(dev)) {
+		printk(KERN_ERR "%s: unable to register device\n", dev->name);
+		goto bad3;
+	}
+	/*
+	 * Attach dynamic MIB vars and announce support
+	 * now that we have a device name with unit number.
+	 */
+	ath_dynamic_sysctl_register(sc);
+	ieee80211_announce(ic);
+	ath_announce(dev);
+#ifdef ATH_TX99_DIAG
+	printk("%s: TX99 support enabled\n", dev->name);
+#endif
+	sc->sc_invalid = 0;
+
+	if (autocreate) {
+		if (!strcmp(autocreate, "none"))
+			autocreatemode = -1;
+		else if (!strcmp(autocreate, "sta"))
+			autocreatemode = IEEE80211_M_STA;
+		else if (!strcmp(autocreate, "ap"))
+			autocreatemode = IEEE80211_M_HOSTAP;
+		else if (!strcmp(autocreate, "adhoc"))
+			autocreatemode = IEEE80211_M_IBSS;
+		else if (!strcmp(autocreate, "ahdemo"))
+			autocreatemode = IEEE80211_M_AHDEMO;
+		else if (!strcmp(autocreate, "wds"))
+			autocreatemode = IEEE80211_M_WDS;
+		else if (!strcmp(autocreate, "monitor"))
+			autocreatemode = IEEE80211_M_MONITOR;
+		else {
+			printk(KERN_INFO "Unknown autocreate mode: %s\n",
+				autocreate);
+			autocreatemode = -1;
+		}
+	}
+	
+	if (autocreatemode != -1) {
+		rtnl_lock();
+		error = ieee80211_create_vap(ic, "ath%d", dev,
+				autocreatemode, IEEE80211_CLONE_BSSID);
+		rtnl_unlock();
+		if (error)
+			printk(KERN_ERR "%s: autocreation of VAP failed: %d\n",
+				dev->name, error);
+	}
+
+	return 0;
+bad3:
+	ieee80211_ifdetach(ic);
+	ieee80211_rate_detach(sc->sc_rc);
+bad2:
+	ath_tx_cleanup(sc);
+	ath_desc_free(sc);
+bad:
+	if (ah)
+		ath_hal_detach(ah);
+	ATH_TXBUF_LOCK_DESTROY(sc);
+	ATH_LOCK_DESTROY(sc);
+	sc->sc_invalid = 1;
+
+	return error;
+}
+
+int
+ath_detach(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ath_hal *ah = sc->sc_ah;
+
+	HAL_INT tmp;
+	DPRINTF(sc, ATH_DEBUG_ANY, "%s: flags %x\n", __func__, dev->flags);
+	ath_stop(dev);
+
+	ath_hal_setpower(sc->sc_ah, HAL_PM_AWAKE);
+	/* Flush the radar task if it's scheduled */
+	if (sc->sc_rtasksched == 1)
+		flush_scheduled_work();
+
+	sc->sc_invalid = 1;
+
+	/*
+	 * NB: the order of these is important:
+	 * o call the 802.11 layer before detaching the HAL to
+	 *   ensure callbacks into the driver to delete global
+	 *   key cache entries can be handled
+	 * o reclaim the tx queue data structures after calling
+	 *   the 802.11 layer as we'll get called back to reclaim
+	 *   node state and potentially want to use them
+	 * o to cleanup the tx queues the HAL is called, so detach
+	 *   it last
+	 * Other than that, it's straightforward...
+	 */
+	ieee80211_ifdetach(&sc->sc_ic);
+
+	ath_hal_intrset(ah, 0);		/* disable further intr's */
+	ath_hal_getisr(ah, &tmp);	/* clear ISR */
+	if(dev->irq) {
+		free_irq(dev->irq, dev);
+		dev->irq = 0;
+	}
+#ifdef ATH_TX99_DIAG
+	if (sc->sc_tx99 != NULL)
+		sc->sc_tx99->detach(sc->sc_tx99);
+#endif
+	ieee80211_rate_detach(sc->sc_rc);
+	ath_desc_free(sc);
+	ath_tx_cleanup(sc);
+	ath_hal_detach(ah);
+
+	ath_dynamic_sysctl_unregister(sc);
+	ATH_LOCK_DESTROY(sc);
+	dev->stop = NULL; /* prevent calling ath_stop again */
+	unregister_netdev(dev);
+	return 0;
+}
+
+static struct ieee80211vap *
+ath_vap_create(struct ieee80211com *ic, const char *name, int unit,
+	int opmode, int flags, struct net_device *mdev)
+{
+	struct ath_softc *sc = ic->ic_dev->priv;
+	struct ath_hal *ah = sc->sc_ah;
+	struct net_device *dev;
+	struct ath_vap *avp;
+	struct ieee80211vap *vap;
+	int ic_opmode;
+
+	if (ic->ic_dev->flags & IFF_RUNNING) {
+		/* needs to disable hardware too */
+		ath_hal_intrset(ah, 0);		/* disable interrupts */
+		ath_draintxq(sc);		/* stop xmit side */
+		ath_stoprecv(sc);		/* stop recv side */
+	}
+	/* XXX ic unlocked and race against add */
+	switch (opmode) {
+	case IEEE80211_M_STA:	/* ap+sta for repeater application */
+		if (sc->sc_nstavaps != 0)  /* only one sta regardless */
+			return NULL;
+		if ((sc->sc_nvaps != 0) && (!(flags & IEEE80211_NO_STABEACONS)))
+			return NULL;   /* If using station beacons, must first up */
+		if (flags & IEEE80211_NO_STABEACONS) {
+			sc->sc_nostabeacons = 1;
+			ic_opmode = IEEE80211_M_HOSTAP;	/* Run with chip in AP mode */
+		} else 
+			ic_opmode = opmode;
+		break;
+	case IEEE80211_M_IBSS:
+		if (sc->sc_nvaps != 0)		/* only one */
+			return NULL;
+		ic_opmode = opmode;
+		break;
+	case IEEE80211_M_AHDEMO:
+	case IEEE80211_M_MONITOR:
+		if (sc->sc_nvaps != 0 && ic->ic_opmode != opmode) {
+			/* preserve existing mode */
+			ic_opmode = ic->ic_opmode;
+		} else
+			ic_opmode = opmode;
+		break;
+	case IEEE80211_M_HOSTAP:
+	case IEEE80211_M_WDS:
+		/* permit multiple ap's and/or wds links */
+		/* XXX sta+ap for repeater/bridge application */
+		if ((sc->sc_nvaps != 0) && (ic->ic_opmode == IEEE80211_M_STA))
+			return NULL;
+		/* XXX not right, beacon buffer is allocated on RUN trans */
+		if (opmode == IEEE80211_M_HOSTAP && STAILQ_EMPTY(&sc->sc_bbuf))
+			return NULL;
+		/*
+		 * XXX Not sure if this is correct when operating only
+		 * with WDS links.
+		 */
+		ic_opmode = IEEE80211_M_HOSTAP;
+
+		break;
+	default:
+		return NULL;
+	}
+
+	if (sc->sc_nvaps >= ATH_BCBUF) {
+		printk(KERN_WARNING "too many virtual ap's (already got %d)\n", sc->sc_nvaps);
+		return NULL;
+	}
+
+	dev = alloc_etherdev(sizeof(struct ath_vap) + sc->sc_rc->arc_vap_space);
+	if (dev == NULL) {
+		/* XXX msg */
+		return NULL;
+	}
+	
+	avp = dev->priv;
+	ieee80211_vap_setup(ic, dev, name, unit, opmode, flags);
+	/* override with driver methods */
+	vap = &avp->av_vap;
+	avp->av_newstate = vap->iv_newstate;
+	vap->iv_newstate = ath_newstate;
+	vap->iv_key_alloc = ath_key_alloc;
+	vap->iv_key_delete = ath_key_delete;
+	vap->iv_key_set = ath_key_set;
+	vap->iv_key_update_begin = ath_key_update_begin;
+	vap->iv_key_update_end = ath_key_update_end;
+#ifdef ATH_SUPERG_COMP
+	vap->iv_comp_set = ath_comp_set;
+#endif
+
+	/* Let rate control register proc entries for the VAP */
+	if (sc->sc_rc->ops->dynamic_proc_register)
+		sc->sc_rc->ops->dynamic_proc_register(vap);
+
+	/*
+	 * Change the interface type for monitor mode.
+	 */
+	if (opmode == IEEE80211_M_MONITOR)
+		dev->type = ARPHRD_IEEE80211_PRISM;
+	if ((flags & IEEE80211_CLONE_BSSID) &&
+	    sc->sc_nvaps != 0 && opmode != IEEE80211_M_WDS && sc->sc_hasbmask) {
+		struct ieee80211vap *v;
+		int id_mask, id;
+		
+		/*
+		 * Hardware supports the bssid mask and a unique
+		 * bssid was requested.  Assign a new mac address
+		 * and expand our bssid mask to cover the active
+		 * virtual ap's with distinct addresses.
+		 */
+		
+		/* do a full search to mark all the allocated VAPs */
+		id_mask = 0;
+		TAILQ_FOREACH(v, &ic->ic_vaps, iv_next)
+			id_mask |= (1 << ATH_GET_VAP_ID(v->iv_myaddr));
+		
+		for (id = 0; id < ATH_BCBUF; id++) {
+			/* get the first available slot */
+			if ((id_mask & (1 << id)) == 0) {
+				ATH_SET_VAP_BSSID(vap->iv_myaddr, id);
+				break;
+			}
+		}
+	}
+	avp->av_bslot = -1;
+	STAILQ_INIT(&avp->av_mcastq.axq_q);
+	ATH_TXQ_LOCK_INIT(&avp->av_mcastq);
+	if (opmode == IEEE80211_M_HOSTAP || opmode == IEEE80211_M_IBSS) {
+		/*
+		 * Allocate beacon state for hostap/ibss.  We know
+		 * a buffer is available because of the check above.
+		 */
+		avp->av_bcbuf = STAILQ_FIRST(&sc->sc_bbuf);
+		STAILQ_REMOVE_HEAD(&sc->sc_bbuf, bf_list);
+		if (opmode == IEEE80211_M_HOSTAP || !sc->sc_hasveol) {
+			int slot;
+			/*
+			 * Assign the VAP to a beacon xmit slot.  As
+			 * above, this cannot fail to find one.
+			 */
+			avp->av_bslot = 0;
+			for (slot = 0; slot < ATH_BCBUF; slot++)
+				if (sc->sc_bslot[slot] == NULL) {
+					/*
+					 * XXX hack, space out slots to better
+					 * deal with misses
+					 */
+					if (slot + 1 < ATH_BCBUF &&
+					    sc->sc_bslot[slot+1] == NULL) {
+						avp->av_bslot = slot + 1;
+						break;
+					}
+					avp->av_bslot = slot;
+					/* NB: keep looking for a double slot */
+				}
+			KASSERT(sc->sc_bslot[avp->av_bslot] == NULL,
+				("beacon slot %u not empty?", avp->av_bslot));
+			sc->sc_bslot[avp->av_bslot] = vap;
+			sc->sc_nbcnvaps++;
+		}
+		if ((opmode == IEEE80211_M_HOSTAP) && (sc->sc_hastsfadd)) {
+			/*
+			 * Multiple VAPs are to transmit beacons and we
+			 * have h/w support for TSF adjusting; enable use
+			 * of staggered beacons.
+			 */
+			/* XXX check for beacon interval too small */
+			sc->sc_stagbeacons = 1;
+		}
+	}
+	if (sc->sc_hastsfadd)
+		ath_hal_settsfadjust(sc->sc_ah, sc->sc_stagbeacons);
+	SET_NETDEV_DEV(dev, ATH_GET_NETDEV_DEV(mdev));
+	/* complete setup */
+	(void) ieee80211_vap_attach(vap,
+		ieee80211_media_change, ieee80211_media_status);
+
+	ic->ic_opmode = ic_opmode;
+	
+	if (opmode != IEEE80211_M_WDS)
+		sc->sc_nvaps++;
+		
+	if (opmode == IEEE80211_M_STA)
+		sc->sc_nstavaps++;
+	else if (opmode == IEEE80211_M_MONITOR)
+		sc->sc_nmonvaps++;
+	/*
+	 * Adhoc demo mode is a pseudo mode; to the HAL it's
+	 * just ibss mode and the driver doesn't use management
+	 * frames.  Other modes carry over directly to the HAL.
+	 */
+	if (ic->ic_opmode == IEEE80211_M_AHDEMO)
+		sc->sc_opmode = HAL_M_IBSS;
+	else
+		sc->sc_opmode = (HAL_OPMODE) ic->ic_opmode;	/* NB: compatible */
+
+#ifdef ATH_SUPERG_XR
+	if ( vap->iv_flags & IEEE80211_F_XR ) {
+		if (ath_descdma_setup(sc, &sc->sc_grppolldma, &sc->sc_grppollbuf,
+			"grppoll", (sc->sc_xrpollcount+1) * HAL_ANTENNA_MAX_MODE, 1) != 0)
+			printk("%s:grppoll Buf allocation failed \n",__func__);
+		if (!sc->sc_xrtxq)
+			sc->sc_xrtxq = ath_txq_setup(sc, HAL_TX_QUEUE_DATA, HAL_XR_DATA);
+		if (sc->sc_hasdiversity) {
+			/* Save current diversity state if user destroys XR VAP */
+			sc->sc_olddiversity = sc->sc_diversity;
+			ath_hal_setdiversity(sc->sc_ah, 0);
+			sc->sc_diversity = 0;
+		}
+	}
+#endif
+	if (ic->ic_dev->flags & IFF_RUNNING) {
+		/* restart hardware */
+		if (ath_startrecv(sc) != 0)	/* restart recv */
+			printk("%s: %s: unable to start recv logic\n",
+				dev->name, __func__);
+		if (sc->sc_beacons)
+			ath_beacon_config(sc, NULL);	/* restart beacons */
+		ath_hal_intrset(ah, sc->sc_imask);
+	}
+
+	return vap;
+}
+
+static void
+ath_vap_delete(struct ieee80211vap *vap)
+{
+	struct net_device *dev = vap->iv_ic->ic_dev;
+	struct ath_softc *sc = dev->priv;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ath_vap *avp = ATH_VAP(vap);
+	int decrease = 1;
+	int i;
+	KASSERT(vap->iv_state == IEEE80211_S_INIT, ("VAP not stopped"));
+
+	if (dev->flags & IFF_RUNNING) {
+		/*
+		 * Quiesce the hardware while we remove the VAP.  In
+		 * particular we need to reclaim all references to the
+		 * VAP state by any frames pending on the tx queues.
+		 *
+		 * XXX can we do this w/o affecting other VAPs?
+		 */
+		ath_hal_intrset(ah, 0);		/* disable interrupts */
+		ath_draintxq(sc);		/* stop xmit side */
+		ath_stoprecv(sc);		/* stop recv side */
+	}
+
+	/*
+	 * Reclaim any pending mcast bufs on the VAP.
+	 */
+	ath_tx_draintxq(sc, &avp->av_mcastq);
+	ATH_TXQ_LOCK_DESTROY(&avp->av_mcastq);
+
+	/*
+	 * Reclaim beacon state.  Note this must be done before
+	 * VAP instance is reclaimed as we may have a reference
+	 * to it in the buffer for the beacon frame.
+	 */
+	if (avp->av_bcbuf != NULL) {
+		if (avp->av_bslot != -1) {
+			sc->sc_bslot[avp->av_bslot] = NULL;
+			sc->sc_nbcnvaps--;
+		}
+		ath_beacon_return(sc, avp->av_bcbuf);
+		avp->av_bcbuf = NULL;
+		if (sc->sc_nbcnvaps == 0)
+			sc->sc_stagbeacons = 0;
+	}
+	if (vap->iv_opmode == IEEE80211_M_STA) {
+		sc->sc_nstavaps--;
+		if (sc->sc_nostabeacons)
+			sc->sc_nostabeacons = 0;
+	} else if (vap->iv_opmode == IEEE80211_M_MONITOR) {
+		sc->sc_nmonvaps--;
+	} else if (vap->iv_opmode == IEEE80211_M_WDS) {
+		decrease = 0;
+	}
+	ieee80211_vap_detach(vap);
+	/* NB: memory is reclaimed through dev->destructor callback */
+	if (decrease)
+		sc->sc_nvaps--;
+
+#ifdef ATH_SUPERG_XR 
+	/*
+	 * If it's an XR VAP, free the memory allocated explicitly.
+	 * Since the XR VAP is not registered, OS cannot free the memory.
+	 */
+	if (vap->iv_flags & IEEE80211_F_XR) {
+		ath_grppoll_stop(vap);
+		ath_descdma_cleanup(sc, &sc->sc_grppolldma, &sc->sc_grppollbuf, BUS_DMA_FROMDEVICE);
+		memset(&sc->sc_grppollbuf, 0, sizeof(sc->sc_grppollbuf));
+		memset(&sc->sc_grppolldma, 0, sizeof(sc->sc_grppolldma));
+		if (vap->iv_xrvap)
+			vap->iv_xrvap->iv_xrvap = NULL;
+		kfree(vap->iv_dev);
+		ath_tx_cleanupq(sc,sc->sc_xrtxq);
+		sc->sc_xrtxq = NULL;
+		if (sc->sc_hasdiversity) {
+			/* Restore diversity setting to old diversity setting */
+			ath_hal_setdiversity(ah, sc->sc_olddiversity);
+			sc->sc_diversity = sc->sc_olddiversity;
+		}
+	}
+#endif
+
+	for (i = 0; i < IEEE80211_APPIE_NUM_OF_FRAME; i++) {
+		if (vap->app_ie[i].ie != NULL) {
+			FREE(vap->app_ie[i].ie, M_DEVBUF);
+			vap->app_ie[i].ie = NULL;
+			vap->app_ie[i].length = 0;
+		}
+	}
+
+	if (dev->flags & IFF_RUNNING) {
+		/*
+		 * Restart rx+tx machines if device is still running.
+		 */
+		if (ath_startrecv(sc) != 0)	/* restart recv */
+			printk("%s: %s: unable to start recv logic\n",
+				dev->name, __func__);
+		if (sc->sc_beacons)
+			ath_beacon_config(sc, NULL);	/* restart beacons */
+		ath_hal_intrset(ah, sc->sc_imask);
+	}
+}
+
+void
+ath_suspend(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, "%s: flags %x\n", __func__, dev->flags);
+	ath_stop(dev);
+}
+
+void
+ath_resume(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, "%s: flags %x\n", __func__, dev->flags);
+	ath_init(dev);
+}
+
+static void
+ath_uapsd_processtriggers(struct ath_softc *sc)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	struct ath_buf *bf;
+	struct ath_desc *ds;
+	struct sk_buff *skb;
+	struct ieee80211_node *ni;
+	struct ath_node *an;
+	struct ieee80211_qosframe *qwh;
+	struct ath_txq *uapsd_xmit_q = sc->sc_uapsdq;
+	struct ieee80211com *ic = &sc->sc_ic;
+	int ac, retval;
+	u_int8_t tid;
+	u_int16_t frame_seq;
+	u_int64_t tsf;
+#define	PA2DESC(_sc, _pa) \
+	((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
+		((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
+
+	/* XXXAPSD: build in check against max triggers we could see
+	 *          based on ic->ic_uapsdmaxtriggers.
+	 */
+
+	tsf = ath_hal_gettsf64(ah);
+	ATH_RXBUF_LOCK(sc);
+	if (sc->sc_rxbufcur == NULL)
+		sc->sc_rxbufcur = STAILQ_FIRST(&sc->sc_rxbuf);
+	for (bf = sc->sc_rxbufcur; bf; bf = STAILQ_NEXT(bf, bf_list)) {
+		ds = bf->bf_desc;
+		if (ds->ds_link == bf->bf_daddr) {
+			/* NB: never process the self-linked entry at the end */
+			break;
+		}
+		if (bf->bf_status & ATH_BUFSTATUS_DONE) {
+			/* 
+			 * already processed this buffer (shouldn't occur if
+			 * we change code to always process descriptors in
+			 * rx intr handler - as opposed to sometimes processing
+			 * in the rx tasklet).
+			 */
+			continue;
+		}
+		skb = bf->bf_skb;
+		if (skb == NULL) {		/* XXX ??? can this happen */
+			printk("%s: no skbuff\n", __func__);
+			continue;
+		}
+
+		/*
+		 * XXXAPSD: consider new HAL call that does only the subset
+		 *          of ath_hal_rxprocdesc we require for trigger search.
+		 */
+
+		/* 
+		 * NB: descriptor memory doesn't need to be sync'd
+		 *     due to the way it was allocated. 
+		 */
+
+		/*
+		 * Must provide the virtual address of the current
+		 * descriptor, the physical address, and the virtual
+		 * address of the next descriptor in the h/w chain.
+		 * This allows the HAL to look ahead to see if the
+		 * hardware is done with a descriptor by checking the
+		 * done bit in the following descriptor and the address
+		 * of the current descriptor the DMA engine is working
+		 * on.  All this is necessary because of our use of
+		 * a self-linked list to avoid rx overruns.
+		 */
+		retval = ath_hal_rxprocdesc(ah, ds, bf->bf_daddr, PA2DESC(sc, ds->ds_link), tsf);
+		if (HAL_EINPROGRESS == retval)
+			break;
+
+		/* XXX: we do not support frames spanning multiple descriptors */
+		bf->bf_status |= ATH_BUFSTATUS_DONE;
+
+		/* errors? */
+		if (ds->ds_rxstat.rs_status)
+			continue;
+
+		/* prepare wireless header for examination */
+		bus_dma_sync_single(sc->sc_bdev, bf->bf_skbaddr, 
+							sizeof(struct ieee80211_qosframe), 
+							BUS_DMA_FROMDEVICE);
+		qwh = (struct ieee80211_qosframe *) skb->data;
+
+		/* find the node. it MUST be in the keycache. */
+		if (ds->ds_rxstat.rs_keyix == HAL_RXKEYIX_INVALID ||
+		    (ni = sc->sc_keyixmap[ds->ds_rxstat.rs_keyix]) == NULL) {
+			/* 
+			 * XXX: this can occur if WEP mode is used for non-Atheros clients
+			 *      (since we do not know which of the 4 WEP keys will be used
+			 *      at association time, so cannot setup a key-cache entry.
+			 *      The Atheros client can convey this in the Atheros IE.)
+			 *
+			 * TODO: The fix is to use the hash lookup on the node here.
+			 */
+#if 0
+			/*
+			 * This print is very chatty, so removing for now.
+			 */
+			DPRINTF(sc, ATH_DEBUG_UAPSD, "%s: U-APSD node (%s) has invalid keycache entry\n",
+				__func__, ether_sprintf(qwh->i_addr2));
+#endif
+			continue;
+		}
+		
+		if (!(ni->ni_flags & IEEE80211_NODE_UAPSD))
+			continue;
+		
+		/*
+		 * Must deal with change of state here, since otherwise there would
+		 * be a race (on two quick frames from STA) between this code and the
+		 * tasklet where we would:
+		 *   - miss a trigger on entry to PS if we're already trigger hunting
+		 *   - generate spurious SP on exit (due to frame following exit frame)
+		 */
+		if (((qwh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
+		     (ni->ni_flags & IEEE80211_NODE_PWR_MGT))) {
+			/*
+			 * NB: do not require lock here since this runs at intr
+			 * "proper" time and cannot be interrupted by rx tasklet
+			 * (code there has lock). May want to place a macro here
+			 * (that does nothing) to make this more clear.
+			 */
+			ni->ni_flags |= IEEE80211_NODE_PS_CHANGED;
+			ni->ni_pschangeseq = *(__le16 *)(&qwh->i_seq[0]);
+			ni->ni_flags &= ~IEEE80211_NODE_UAPSD_SP;
+			ni->ni_flags ^= IEEE80211_NODE_PWR_MGT;
+			if (qwh->i_fc[1] & IEEE80211_FC1_PWR_MGT) {
+				ni->ni_flags |= IEEE80211_NODE_UAPSD_TRIG;
+				ic->ic_uapsdmaxtriggers++;
+				WME_UAPSD_NODE_TRIGSEQINIT(ni);
+				DPRINTF(sc, ATH_DEBUG_UAPSD,
+					"%s: Node (%s) became U-APSD triggerable (%d)\n", 
+					__func__, ether_sprintf(qwh->i_addr2),
+					ic->ic_uapsdmaxtriggers);
+			} else {
+				ni->ni_flags &= ~IEEE80211_NODE_UAPSD_TRIG;
+				ic->ic_uapsdmaxtriggers--;
+				DPRINTF(sc, ATH_DEBUG_UAPSD,
+					"%s: Node (%s) no longer U-APSD triggerable (%d)\n", 
+					__func__, ether_sprintf(qwh->i_addr2),
+					ic->ic_uapsdmaxtriggers);
+				/* 
+				 * XXX: rapidly thrashing sta could get 
+				 * out-of-order frames due this flush placing
+				 * frames on backlogged regular AC queue and
+				 * re-entry to PS having fresh arrivals onto
+				 * faster UPSD delivery queue. if this is a
+				 * big problem we may need to drop these.
+				 */
+				ath_uapsd_flush(ni);
+			}
+			
+			continue;
+		}
+
+		if (ic->ic_uapsdmaxtriggers == 0)
+			continue;
+		
+		/* make sure the frame is QoS data/null */
+		/* NB: with current sub-type definitions, the 
+		 * IEEE80211_FC0_SUBTYPE_QOS check, below, covers the 
+		 * QoS null case too.
+		 */
+		if (((qwh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) ||
+		     !(qwh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS))
+			continue;
+		
+		/*
+		 * To be a trigger:
+		 *   - node is in triggerable state
+		 *   - QoS data/null frame with triggerable AC
+		 */
+		tid = qwh->i_qos[0] & IEEE80211_QOS_TID;
+		ac = TID_TO_WME_AC(tid);
+		if (!WME_UAPSD_AC_CAN_TRIGGER(ac, ni))
+			continue;
+		
+		DPRINTF(sc, ATH_DEBUG_UAPSD, 
+			"%s: U-APSD trigger detected for node (%s) on AC %d\n",
+			__func__, ether_sprintf(ni->ni_macaddr), ac);
+		if (ni->ni_flags & IEEE80211_NODE_UAPSD_SP) {
+			/* have trigger, but SP in progress, so ignore */
+			DPRINTF(sc, ATH_DEBUG_UAPSD,
+				"%s:   SP already in progress - ignoring\n",
+				__func__);
+			continue;
+		}
+
+		/*
+		 * Detect duplicate triggers and drop if so.
+		 */
+		frame_seq = le16toh(*(__le16 *)qwh->i_seq);
+		if ((qwh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+		    frame_seq == ni->ni_uapsd_trigseq[ac]) {
+			DPRINTF(sc, ATH_DEBUG_UAPSD, "%s: dropped dup trigger, ac %d, seq %d\n",
+				__func__, ac, frame_seq);
+			continue;
+		}
+
+		an = ATH_NODE(ni);
+
+		/* start the SP */
+		ATH_NODE_UAPSD_LOCK(an);
+		ni->ni_stats.ns_uapsd_triggers++;
+		ni->ni_flags |= IEEE80211_NODE_UAPSD_SP;
+		ni->ni_uapsd_trigseq[ac] = frame_seq;
+		ATH_NODE_UAPSD_UNLOCK(an);
+
+		ATH_TXQ_LOCK(uapsd_xmit_q);
+		if (STAILQ_EMPTY(&an->an_uapsd_q)) {
+			DPRINTF(sc, ATH_DEBUG_UAPSD,
+				"%s: Queue empty, generating QoS NULL to send\n",
+				__func__);
+			/* 
+			 * Empty queue, so need to send QoS null on this ac. Make a
+			 * call that will dump a QoS null onto the node's queue, then
+			 * we can proceed as normal.
+			 */
+			ieee80211_send_qosnulldata(ni, ac);
+		}
+
+		if (STAILQ_FIRST(&an->an_uapsd_q)) {
+			struct ath_buf *last_buf = STAILQ_LAST(&an->an_uapsd_q, ath_buf, bf_list);
+			struct ath_desc *last_desc = last_buf->bf_desc;
+			struct ieee80211_qosframe *qwhl = (struct ieee80211_qosframe *)last_buf->bf_skb->data;
+			/* 
+			 * NB: flip the bit to cause intr on the EOSP desc,
+			 * which is the last one
+			 */
+			ath_hal_txreqintrdesc(sc->sc_ah, last_desc);
+			qwhl->i_qos[0] |= IEEE80211_QOS_EOSP;
+
+			if (IEEE80211_VAP_EOSPDROP_ENABLED(ni->ni_vap)) {
+				/* simulate lost EOSP */
+				qwhl->i_addr1[0] |= 0x40;
+			}
+			
+			/* more data bit only for EOSP frame */
+			if (an->an_uapsd_overflowqdepth)
+				qwhl->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+			else if (IEEE80211_NODE_UAPSD_USETIM(ni))
+				ni->ni_vap->iv_set_tim(ni, 0);
+
+			ni->ni_stats.ns_tx_uapsd += an->an_uapsd_qdepth;
+
+			bus_dma_sync_single(sc->sc_bdev, last_buf->bf_skbaddr,
+				sizeof(*qwhl), BUS_DMA_TODEVICE);
+			
+			if (uapsd_xmit_q->axq_link) {
+#ifdef AH_NEED_DESC_SWAP
+				*uapsd_xmit_q->axq_link = cpu_to_le32(STAILQ_FIRST(&an->an_uapsd_q)->bf_daddr);
+#else
+				*uapsd_xmit_q->axq_link = STAILQ_FIRST(&an->an_uapsd_q)->bf_daddr;
+#endif
+			}
+			/* below leaves an_uapsd_q NULL */
+			STAILQ_CONCAT(&uapsd_xmit_q->axq_q, &an->an_uapsd_q);
+			uapsd_xmit_q->axq_link = &last_desc->ds_link;
+			ath_hal_puttxbuf(sc->sc_ah, 
+				uapsd_xmit_q->axq_qnum, 
+				(STAILQ_FIRST(&uapsd_xmit_q->axq_q))->bf_daddr);
+			ath_hal_txstart(sc->sc_ah, uapsd_xmit_q->axq_qnum);
+		}
+		an->an_uapsd_qdepth = 0;
+
+		ATH_TXQ_UNLOCK(uapsd_xmit_q);
+	}
+	sc->sc_rxbufcur = bf;
+	ATH_RXBUF_UNLOCK(sc);
+#undef PA2DESC
+}
+
+/*
+ * Interrupt handler.  Most of the actual processing is deferred.
+ */
+irqreturn_t
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
+ath_intr(int irq, void *dev_id)
+#else
+ath_intr(int irq, void *dev_id, struct pt_regs *regs)
+#endif
+{
+	struct net_device *dev = dev_id;
+	struct ath_softc *sc = dev->priv;
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_INT status;
+	int needmark;
+
+	if (sc->sc_invalid) {
+		/*
+		 * The hardware is not ready/present, don't touch anything.
+		 * Note this can happen early on if the IRQ is shared.
+		 */
+		return IRQ_NONE;
+	}
+	if (!ath_hal_intrpend(ah))		/* shared irq, not for us */
+		return IRQ_NONE;
+	if ((dev->flags & (IFF_RUNNING | IFF_UP)) != (IFF_RUNNING | IFF_UP)) {
+		DPRINTF(sc, ATH_DEBUG_INTR, "%s: flags 0x%x\n",
+			__func__, dev->flags);
+		ath_hal_getisr(ah, &status);	/* clear ISR */
+		ath_hal_intrset(ah, 0);		/* disable further intr's */
+		return IRQ_HANDLED;
+	}
+	needmark = 0;
+	/*
+	 * Figure out the reason(s) for the interrupt.  Note
+	 * that the HAL returns a pseudo-ISR that may include
+	 * bits we haven't explicitly enabled so we mask the
+	 * value to ensure we only process bits we requested.
+	 */
+	ath_hal_getisr(ah, &status);		/* NB: clears ISR too */
+	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) {
+		sc->sc_stats.ast_hardware++;
+		ath_hal_intrset(ah, 0);		/* disable intr's until reset */
+		ATH_SCHEDULE_TQUEUE(&sc->sc_fataltq, &needmark);
+	} else if (status & HAL_INT_RXORN) {
+		sc->sc_stats.ast_rxorn++;
+		ath_hal_intrset(ah, 0);		/* disable intr's until reset */
+		ATH_SCHEDULE_TQUEUE(&sc->sc_rxorntq, &needmark);
+	} else {
+		if (status & HAL_INT_SWBA) {
+			/*
+			 * Software beacon alert--time to send a beacon.
+			 * Handle beacon transmission directly; deferring
+			 * this is too slow to meet timing constraints
+			 * under load.
+			 */
+			ath_beacon_send(sc, &needmark);
+		}
+		if (status & HAL_INT_RXEOL) {
+			/*
+			 * NB: the hardware should re-read the link when
+			 *     RXE bit is written, but it doesn't work at
+			 *     least on older hardware revs.
+			 */
+			sc->sc_stats.ast_rxeol++;
+		}
+		if (status & HAL_INT_TXURN) {
+			sc->sc_stats.ast_txurn++;
+			/* bump tx trigger level */
+			ath_hal_updatetxtriglevel(ah, AH_TRUE);
+		}
+		if (status & HAL_INT_RX) {
+			ath_uapsd_processtriggers(sc);
+			/* Get the noise floor data in interrupt context as we can't get it
+			 * per frame, so we need to get it as soon as possible (i.e. the tasklet
+			 * might take too long to fire */
+			ath_hal_process_noisefloor(ah);
+			sc->sc_channoise = ath_hal_get_channel_noise(ah, &(sc->sc_curchan));
+			ATH_SCHEDULE_TQUEUE(&sc->sc_rxtq, &needmark);
+		}
+		if (status & HAL_INT_TX) {
+#ifdef ATH_SUPERG_DYNTURBO
+			/*
+			 * Check if the beacon queue caused the interrupt 
+			 * when a dynamic turbo switch
+			 * is pending so we can initiate the change. 
+			 * XXX must wait for all VAPs' beacons
+			 */
+
+			if (sc->sc_dturbo_switch) {
+				u_int32_t txqs = (1 << sc->sc_bhalq);
+				ath_hal_gettxintrtxqs(ah, &txqs);
+				if(txqs & (1 << sc->sc_bhalq)) {
+					sc->sc_dturbo_switch = 0;
+					/*
+					 * Hack: defer switch for 10ms to permit slow
+					 * clients time to track us.  This especially
+					 * noticeable with Windows clients.
+					 */
+					mod_timer(&sc->sc_dturbo_switch_mode,
+							  jiffies + msecs_to_jiffies(10));
+				}
+			} 
+#endif
+			ATH_SCHEDULE_TQUEUE(&sc->sc_txtq, &needmark);
+		}
+		if (status & HAL_INT_BMISS) {
+			sc->sc_stats.ast_bmiss++;
+			ATH_SCHEDULE_TQUEUE(&sc->sc_bmisstq, &needmark);
+		}
+		if (status & HAL_INT_MIB) {
+			sc->sc_stats.ast_mib++;
+			/*
+			 * Disable interrupts until we service the MIB
+			 * interrupt; otherwise it will continue to fire.
+			 */
+			ath_hal_intrset(ah, 0);
+			/*
+			 * Let the HAL handle the event.  We assume it will
+			 * clear whatever condition caused the interrupt.
+			 */
+			ath_hal_mibevent(ah, &sc->sc_halstats);
+			ath_hal_intrset(ah, sc->sc_imask);
+		}
+	}
+	if (needmark)
+		mark_bh(IMMEDIATE_BH);
+	return IRQ_HANDLED;
+}
+
+static void
+ath_radar_task(struct work_struct *thr)
+{
+	struct ath_softc *sc = container_of(thr, struct ath_softc, sc_radartask);
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ieee80211_channel ichan;
+	HAL_CHANNEL hchan;
+
+	sc->sc_rtasksched = 0;
+	if (ath_hal_procdfs(ah, &hchan)) {
+		/*
+		 * DFS was found, initiate channel change
+		 */
+		ichan.ic_ieee = ath_hal_mhz2ieee(ah, hchan.channel, hchan.channelFlags);
+		ichan.ic_freq = hchan.channel;
+		ichan.ic_flags = hchan.channelFlags;
+
+		if ((sc->sc_curchan.channel == hchan.channel) &&
+		    (sc->sc_curchan.channelFlags == hchan.channel)) {
+			if (hchan.privFlags & CHANNEL_INTERFERENCE)
+				sc->sc_curchan.privFlags |= CHANNEL_INTERFERENCE;
+		}
+		ieee80211_mark_dfs(ic, &ichan);
+		if (((ic->ic_flags_ext & IEEE80211_FEXT_MARKDFS) == 0) &&
+		    (ic->ic_opmode == IEEE80211_M_HOSTAP)) {
+			sc->sc_dfstest_ieeechan = ic->ic_curchan->ic_ieee;
+			sc->sc_dfstesttimer.function = ath_dfs_test_return;
+			sc->sc_dfstesttimer.expires = jiffies + (sc->sc_dfstesttime * HZ);
+			sc->sc_dfstesttimer.data = (unsigned long)sc;
+			if (sc->sc_dfstest == 0) {
+				sc->sc_dfstest = 1;
+				add_timer(&sc->sc_dfstesttimer);
+			}
+		}
+	}
+}
+
+static void
+ath_dfs_test_return(unsigned long data)
+{
+	struct ath_softc *sc = (struct ath_softc *)data; 
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	sc->sc_dfstest = 0;
+	ieee80211_dfs_test_return(ic, sc->sc_dfstest_ieeechan);
+}
+
+static void
+ath_fatal_tasklet(TQUEUE_ARG data)
+{
+	struct net_device *dev = (struct net_device *)data;
+
+	printk("%s: hardware error; resetting\n", dev->name);
+	ath_reset(dev);
+}
+
+static void
+ath_rxorn_tasklet(TQUEUE_ARG data)
+{
+	struct net_device *dev = (struct net_device *)data;
+
+	printk("%s: rx FIFO overrun; resetting\n", dev->name);
+	ath_reset(dev);
+}
+
+static void
+ath_bmiss_tasklet(TQUEUE_ARG data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct ath_softc *sc = dev->priv;
+
+	if (time_before(jiffies, sc->sc_ic.ic_bmiss_guard)) {
+		/* Beacon miss interrupt occured too short after last beacon
+		 * timer configuration. Ignore it as it could be spurious. */
+		DPRINTF(sc, ATH_DEBUG_ANY, "%s: ignored\n", __func__);
+	} else {
+		DPRINTF(sc, ATH_DEBUG_ANY, "%s\n", __func__);
+		ieee80211_beacon_miss(&sc->sc_ic);
+	}
+}
+
+static u_int
+ath_chan2flags(struct ieee80211_channel *chan)
+{
+	u_int flags;
+	static const u_int modeflags[] = {
+		0,		/* IEEE80211_MODE_AUTO    */
+		CHANNEL_A,	/* IEEE80211_MODE_11A     */
+		CHANNEL_B,	/* IEEE80211_MODE_11B     */
+		CHANNEL_PUREG,	/* IEEE80211_MODE_11G     */
+		0,		/* IEEE80211_MODE_FH      */
+		CHANNEL_108A,	/* IEEE80211_MODE_TURBO_A */
+		CHANNEL_108G,	/* IEEE80211_MODE_TURBO_G */
+	};
+
+	flags = modeflags[ieee80211_chan2mode(chan)];
+
+	if (IEEE80211_IS_CHAN_HALF(chan))
+		flags |= CHANNEL_HALF;
+	else if (IEEE80211_IS_CHAN_QUARTER(chan))
+		flags |= CHANNEL_QUARTER;
+
+	return flags;
+}
+
+/*
+ * Context: process context
+ */
+
+static int
+ath_init(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_STATUS status;
+	int error = 0;
+
+	ATH_LOCK(sc);
+
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: mode %d\n", __func__, ic->ic_opmode);
+
+	/*
+	 * Stop anything previously setup.  This is safe
+	 * whether this is the first time through or not.
+	 */
+	ath_stop_locked(dev);
+
+#ifdef ATH_CAP_TPC
+	ath_hal_setcapability(sc->sc_ah, HAL_CAP_TPC, 0, 1, NULL);
+#endif
+
+	/* Whether we should enable h/w TKIP MIC */
+	if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+		ath_hal_setcapability(sc->sc_ah, HAL_CAP_TKIP_MIC, 0, 0, NULL);
+	else {
+		if (((ic->ic_caps & IEEE80211_C_WME_TKIPMIC) == 0) &&
+		    (ic->ic_flags & IEEE80211_F_WME))
+			ath_hal_setcapability(sc->sc_ah, HAL_CAP_TKIP_MIC, 0, 0, NULL);
+		else
+			ath_hal_setcapability(sc->sc_ah, HAL_CAP_TKIP_MIC, 0, 1, NULL);
+	}
+		
+	/*
+	 * Flush the skb's allocated for receive in case the rx
+	 * buffer size changes.  This could be optimized but for
+	 * now we do it each time under the assumption it does
+	 * not happen often.
+	 */
+	ath_flushrecv(sc);
+
+	/*
+	 * The basic interface to setting the hardware in a good
+	 * state is ``reset''.  On return the hardware is known to
+	 * be powered up and with interrupts disabled.  This must
+	 * 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_curchan);
+	if (!ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, AH_FALSE, &status)) {
+		printk("%s: unable to reset hardware: '%s' (HAL status %u) "
+			"(freq %u flags 0x%x)\n", dev->name,
+			ath_get_hal_status_desc(status), status,
+			sc->sc_curchan.channel, sc->sc_curchan.channelFlags);
+		error = -EIO;
+		goto done;
+	}
+
+	if (sc->sc_softled)
+		ath_hal_gpioCfgOutput(ah, sc->sc_ledpin);
+	/*
+	 * This is needed only to setup initial state
+	 * but it's best done after a reset.
+	 */
+	ath_update_txpow(sc);
+
+	/* Set the default RX antenna; it may get lost on reset. */
+	ath_setdefantenna(sc, sc->sc_defant);
+
+	/*
+	 * Setup the hardware after reset: the key cache
+	 * is filled as needed and the receive engine is
+	 * set going.  Frame transmit is handled entirely
+	 * in the frame output path; there's nothing to do
+	 * here except setup the interrupt mask.
+	 */
+#if 0
+	ath_initkeytable(sc);		/* XXX still needed? */
+#endif
+	if (ath_startrecv(sc) != 0) {
+		printk("%s: unable to start recv logic\n", dev->name);
+		error = -EIO;
+		goto done;
+	}
+	/* Enable interrupts. */
+	sc->sc_imask = HAL_INT_RX | HAL_INT_TX
+		  | HAL_INT_RXEOL | HAL_INT_RXORN
+		  | HAL_INT_FATAL | HAL_INT_GLOBAL;
+	/*
+	 * Enable MIB interrupts when there are hardware phy counters.
+	 * Note we only do this (at the moment) for station mode.
+	 */
+	if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA)
+		sc->sc_imask |= HAL_INT_MIB;
+	ath_hal_intrset(ah, sc->sc_imask);
+
+	/*
+	 * The hardware should be ready to go now so it's safe
+	 * to kick the 802.11 state machine as it's likely to
+	 * immediately call back to us to send mgmt frames.
+	 */
+	ath_chan_change(sc, ic->ic_curchan);
+	ath_set_ack_bitrate(sc, sc->sc_ackrate);
+	dev->flags |= IFF_RUNNING;		/* we are ready to go */
+	ieee80211_start_running(ic);		/* start all VAPs */
+#ifdef ATH_TX99_DIAG
+	if (sc->sc_tx99 != NULL)
+		sc->sc_tx99->start(sc->sc_tx99);
+#endif
+done:
+	ATH_UNLOCK(sc);
+	return error;
+}
+
+/* Caller must lock ATH_LOCK 
+ *
+ * Context: softIRQ
+ */ 
+static int
+ath_stop_locked(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+
+	DPRINTF(sc, ATH_DEBUG_RESET, "%s: invalid %u flags 0x%x\n",
+		__func__, sc->sc_invalid, dev->flags);
+
+	if (dev->flags & IFF_RUNNING) {
+		/*
+		 * Shutdown the hardware and driver:
+		 *    stop output from above
+		 *    reset 802.11 state machine
+		 *	(sends station deassoc/deauth frames)
+		 *    turn off timers
+		 *    disable interrupts
+		 *    clear transmit machinery
+		 *    clear receive machinery
+		 *    turn off the radio
+		 *    reclaim beacon resources
+		 *
+		 * Note that some of this work is not possible if the
+		 * hardware is gone (invalid).
+		 */
+#ifdef ATH_TX99_DIAG
+		if (sc->sc_tx99 != NULL)
+			sc->sc_tx99->stop(sc->sc_tx99);
+#endif
+		netif_stop_queue(dev);		/* XXX re-enabled by ath_newstate */
+		dev->flags &= ~IFF_RUNNING;	/* NB: avoid recursion */
+		ieee80211_stop_running(ic);	/* stop all VAPs */
+		if (!sc->sc_invalid) {
+			ath_hal_intrset(ah, 0);
+			if (sc->sc_softled) {
+				del_timer(&sc->sc_ledtimer);
+				ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon);
+				sc->sc_blinking = 0;
+				sc->sc_ledstate = 1;
+			}
+		}
+		ath_draintxq(sc);
+		if (!sc->sc_invalid) {
+			ath_stoprecv(sc);
+			ath_hal_phydisable(ah);
+		} else
+			sc->sc_rxlink = NULL;
+		ath_beacon_free(sc);		/* XXX needed? */
+	} else
+		ieee80211_stop_running(ic);	/* stop other VAPs */
+
+	if (sc->sc_softled)
+		ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon);
+	
+	return 0;
+}
+
+/*
+ * Stop the device, grabbing the top-level lock to protect
+ * against concurrent entry through ath_init (which can happen
+ * if another thread does a system call and the thread doing the
+ * stop is preempted).
+ */
+static int
+ath_stop(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	int error;
+
+	ATH_LOCK(sc);
+
+	if (!sc->sc_invalid)
+		ath_hal_setpower(sc->sc_ah, HAL_PM_AWAKE);
+
+	error = ath_stop_locked(dev);
+#if 0
+	if (error == 0 && !sc->sc_invalid) {
+		/*
+		 * Set the chip in full sleep mode.  Note that we are
+		 * careful to do this only when bringing the interface
+		 * completely to a stop.  When the chip is in this state
+		 * it must be carefully woken up or references to
+		 * registers in the PCI clock domain may freeze the bus
+		 * (and system).  This varies by chip and is mostly an
+		 * issue with newer parts that go to sleep more quickly.
+		 */
+		ath_hal_setpower(sc->sc_ah, HAL_PM_FULL_SLEEP);
+	}
+#endif
+	ATH_UNLOCK(sc);
+
+	return error;
+}
+
+static int 
+ar_device(int devid)
+{
+	switch (devid) {
+	case AR5210_DEFAULT:
+	case AR5210_PROD:
+	case AR5210_AP:
+		return 5210;
+	case AR5211_DEFAULT:
+	case AR5311_DEVID:
+	case AR5211_LEGACY:
+	case AR5211_FPGA11B:
+		return 5211;
+	case AR5212_DEFAULT:
+	case AR5212_DEVID:
+	case AR5212_FPGA:
+	case AR5212_DEVID_IBM:
+	case AR5212_AR5312_REV2:
+	case AR5212_AR5312_REV7:
+	case AR5212_AR2313_REV8:
+	case AR5212_AR2315_REV6:
+	case AR5212_AR2315_REV7:
+	case AR5212_AR2317_REV1:
+	case AR5212_DEVID_0014:
+	case AR5212_DEVID_0015:
+	case AR5212_DEVID_0016:
+	case AR5212_DEVID_0017:
+	case AR5212_DEVID_0018:
+	case AR5212_DEVID_0019:
+	case AR5212_AR2413:
+	case AR5212_AR5413:
+	case AR5212_AR5424:
+	case AR5212_DEVID_FF19:
+		return 5212;
+	case AR5213_SREV_1_0:
+	case AR5213_SREV_REG:
+	case AR_SUBVENDOR_ID_NOG:
+	case AR_SUBVENDOR_ID_NEW_A:
+		return 5213;
+	default: 
+		return 0; /* unknown */
+	}
+}
+
+
+static int 
+ath_set_ack_bitrate(struct ath_softc *sc, int high) 
+{
+	struct ath_hal *ah = sc->sc_ah;
+	if (ar_device(sc->devid) == 5212 || ar_device(sc->devid) == 5213) {
+		/* set ack to be sent at low bit-rate */
+		/* registers taken from the OpenBSD 5212 HAL */
+#define AR5K_AR5212_STA_ID1                     0x8004
+#define AR5K_AR5212_STA_ID1_ACKCTS_6MB          0x01000000
+#define AR5K_AR5212_STA_ID1_BASE_RATE_11B       0x02000000
+		u_int32_t v = AR5K_AR5212_STA_ID1_BASE_RATE_11B | AR5K_AR5212_STA_ID1_ACKCTS_6MB;
+		if (high) {
+			OS_REG_WRITE(ah, AR5K_AR5212_STA_ID1, OS_REG_READ(ah, AR5K_AR5212_STA_ID1) & ~v);
+		} else {
+			OS_REG_WRITE(ah, AR5K_AR5212_STA_ID1, OS_REG_READ(ah, AR5K_AR5212_STA_ID1) | v);
+		}
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * Reset the hardware w/o losing operational state.  This is
+ * basically a more efficient way of doing ath_stop, ath_init,
+ * followed by state transitions to the current 802.11
+ * operational state.  Used to recover from errors rx overrun
+ * and to reset the hardware when rf gain settings must be reset.
+ */
+static int
+ath_reset(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	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(c);
+
+	ath_hal_intrset(ah, 0);		/* disable interrupts */
+	ath_draintxq(sc);		/* stop xmit side */
+	ath_stoprecv(sc);		/* stop recv side */
+	/* NB: indicate channel change so we do a full reset */
+	if (!ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, AH_TRUE, &status))
+		printk("%s: %s: unable to reset hardware: '%s' (HAL status %u)\n",
+			dev->name, __func__, ath_get_hal_status_desc(status), status);
+	ath_update_txpow(sc);		/* update tx power state */
+	if (ath_startrecv(sc) != 0)	/* restart recv */
+		printk("%s: %s: unable to start recv logic\n",
+			dev->name, __func__);
+	if (sc->sc_softled)
+		ath_hal_gpioCfgOutput(ah, sc->sc_ledpin);
+
+	/*
+	 * 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 (sc->sc_beacons)
+		ath_beacon_config(sc, NULL);	/* restart beacons */
+	ath_hal_intrset(ah, sc->sc_imask);
+	ath_set_ack_bitrate(sc, sc->sc_ackrate);
+	netif_wake_queue(dev);		/* restart xmit */
+#ifdef ATH_SUPERG_XR
+	/*
+	 * restart the group polls.
+	 */
+	if (sc->sc_xrgrppoll) {
+		struct ieee80211vap *vap;
+		TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+			if (vap && (vap->iv_flags & IEEE80211_F_XR))
+				break;
+		ath_grppoll_stop(vap);
+		ath_grppoll_start(vap, sc->sc_xrpollcount);
+	}
+#endif
+	return 0;
+}
+
+
+/* Swap transmit descriptor.
+ * if AH_NEED_DESC_SWAP flag is not defined this becomes a "null"
+ * function.
+ */
+static __inline void
+ath_desc_swap(struct ath_desc *ds)
+{
+#ifdef AH_NEED_DESC_SWAP
+	ds->ds_link = cpu_to_le32(ds->ds_link);
+	ds->ds_data = cpu_to_le32(ds->ds_data);
+	ds->ds_ctl0 = cpu_to_le32(ds->ds_ctl0);
+	ds->ds_ctl1 = cpu_to_le32(ds->ds_ctl1);
+	ds->ds_hw[0] = cpu_to_le32(ds->ds_hw[0]);
+	ds->ds_hw[1] = cpu_to_le32(ds->ds_hw[1]);
+#endif
+}
+
+/*
+ * Insert a buffer on a txq 
+ * 
+ */
+static __inline void
+ath_tx_txqaddbuf(struct ath_softc *sc, struct ieee80211_node *ni, 
+	struct ath_txq *txq, struct ath_buf *bf, 
+	struct ath_desc *lastds, int framelen)
+{
+	struct ath_hal *ah = sc->sc_ah;
+
+	/*
+	 * Insert the frame on the outbound list and
+	 * pass it on to the hardware.
+	 */
+	ATH_TXQ_LOCK(txq);
+	if (ni && ni->ni_vap && txq == &ATH_VAP(ni->ni_vap)->av_mcastq) {
+		/*
+		 * The CAB queue is started from the SWBA handler since
+		 * frames only go out on DTIM and to avoid possible races.
+		 */
+		ath_hal_intrset(ah, sc->sc_imask & ~HAL_INT_SWBA);
+		ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
+		DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: txq depth = %d\n", __func__, txq->axq_depth);
+		if (txq->axq_link != NULL) {
+#ifdef AH_NEED_DESC_SWAP
+			*txq->axq_link = cpu_to_le32(bf->bf_daddr);
+#else
+			*txq->axq_link = bf->bf_daddr;
+#endif
+			DPRINTF(sc, ATH_DEBUG_XMIT, "%s: link[%u](%p)=%llx (%p)\n",
+				__func__,
+				txq->axq_qnum, txq->axq_link,
+				ito64(bf->bf_daddr), bf->bf_desc);
+		}
+		txq->axq_link = &lastds->ds_link;
+		ath_hal_intrset(ah, sc->sc_imask);
+	} else {
+		ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
+		DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: txq depth = %d\n", __func__, txq->axq_depth);
+		if (txq->axq_link == NULL) {
+			ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
+			DPRINTF(sc, ATH_DEBUG_XMIT, "%s: TXDP[%u] = %llx (%p)\n",
+				__func__,
+				txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc);
+		} else {
+#ifdef AH_NEED_DESC_SWAP
+			*txq->axq_link = cpu_to_le32(bf->bf_daddr);
+#else
+			*txq->axq_link = bf->bf_daddr;
+#endif
+			DPRINTF(sc, ATH_DEBUG_XMIT, "%s: link[%u] (%p)=%llx (%p)\n",
+				__func__,
+				txq->axq_qnum, txq->axq_link,
+				ito64(bf->bf_daddr), bf->bf_desc);
+		}
+		txq->axq_link = &lastds->ds_link;
+		ath_hal_txstart(ah, txq->axq_qnum);
+		sc->sc_dev->trans_start = jiffies;
+	}
+	ATH_TXQ_UNLOCK(txq);
+
+	sc->sc_devstats.tx_packets++;
+	sc->sc_devstats.tx_bytes += framelen;
+}
+
+static int 
+dot11_to_ratecode(struct ath_softc *sc, const HAL_RATE_TABLE *rt, int dot11)
+{
+	int index = sc->sc_rixmap[dot11 & IEEE80211_RATE_VAL];
+	if (index >= 0 && index < rt->rateCount)
+		return rt->info[index].rateCode;
+	
+	return rt->info[sc->sc_minrateix].rateCode;
+}
+
+
+static int 
+ath_tx_startraw(struct net_device *dev, struct ath_buf *bf, struct sk_buff *skb) 
+{
+	struct ath_softc *sc = dev->priv;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211_phy_params *ph = (struct ieee80211_phy_params *) (skb->cb + sizeof(struct ieee80211_cb));
+	const HAL_RATE_TABLE *rt;
+	int pktlen;
+	int hdrlen;
+	HAL_PKT_TYPE atype;
+	u_int flags;
+	int keyix;
+	int try0;
+	int power;
+	u_int8_t antenna, txrate;
+	struct ath_txq *txq=NULL;
+	struct ath_desc *ds=NULL;
+	struct ieee80211_frame *wh; 
+	
+	wh = (struct ieee80211_frame *) skb->data;
+	try0 = ph->try0;
+	rt = sc->sc_currates;
+	txrate = dot11_to_ratecode(sc, rt, ph->rate0);
+	power = ph->power > 60 ? 60 : ph->power;
+	hdrlen = ieee80211_anyhdrsize(wh);
+	pktlen = skb->len + IEEE80211_CRC_LEN;
+	
+	keyix = HAL_TXKEYIX_INVALID;
+	flags = HAL_TXDESC_INTREQ | HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */
+	
+	bf->bf_skbaddr = bus_map_single(sc->sc_bdev,
+					skb->data, pktlen, BUS_DMA_TODEVICE);
+	DPRINTF(sc, ATH_DEBUG_XMIT, "%s: skb %p [data %p len %u] skbaddr %llx\n",
+		__func__, skb, skb->data, skb->len, ito64(bf->bf_skbaddr));
+	
+	
+	bf->bf_skb = skb;
+	bf->bf_node = NULL;
+	
+#ifdef ATH_SUPERG_FF
+	bf->bf_numdesc = 1;
+#endif
+	
+	/* setup descriptors */
+	ds = bf->bf_desc;
+	rt = sc->sc_currates;
+	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+	
+	
+	if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+		flags |= HAL_TXDESC_NOACK;	/* no ack on broad/multicast */
+		sc->sc_stats.ast_tx_noack++;
+		try0 = 1;
+	}
+	atype = HAL_PKT_TYPE_NORMAL;		/* default */
+	txq = sc->sc_ac2q[skb->priority & 0x3];
+	
+	
+	flags |= HAL_TXDESC_INTREQ;
+	antenna = sc->sc_txantenna;
+	
+	/* XXX check return value? */
+	ath_hal_setuptxdesc(ah, ds
+			    , pktlen	/* packet length */
+			    , hdrlen	/* header length */
+			    , atype	/* Atheros packet type */
+			    , power	/* txpower */
+			    , txrate, try0 /* series 0 rate/tries */
+			    , keyix	/* key cache index */
+			    , antenna	/* antenna mode */
+			    , flags	/* flags */
+			    , 0		/* rts/cts rate */
+			    , 0		/* rts/cts duration */
+			    , 0		/* comp icv len */
+			    , 0		/* comp iv len */
+			    , ATH_COMP_PROC_NO_COMP_NO_CCS /* comp scheme */
+			   );
+
+	if (ph->try1) {
+		ath_hal_setupxtxdesc(sc->sc_ah, ds
+			, dot11_to_ratecode(sc, rt, ph->rate1), ph->try1 /* series 1 */
+			, dot11_to_ratecode(sc, rt, ph->rate2), ph->try2 /* series 2 */
+			, dot11_to_ratecode(sc, rt, ph->rate3), ph->try3 /* series 3 */
+			);	
+	}
+	bf->bf_flags = flags;			/* record for post-processing */
+
+	ds->ds_link = 0;
+	ds->ds_data = bf->bf_skbaddr;
+	
+	ath_hal_filltxdesc(ah, ds
+			   , skb->len	/* segment length */
+			   , AH_TRUE	/* first segment */
+			   , AH_TRUE	/* last segment */
+			   , ds		/* first descriptor */
+			   );
+	
+	/* NB: The desc swap function becomes void, 
+	 * if descriptor swapping is not enabled
+	 */
+	ath_desc_swap(ds);
+	
+	DPRINTF(sc, ATH_DEBUG_XMIT, "%s: Q%d: %08x %08x %08x %08x %08x %08x\n",
+		__func__, M_FLAG_GET(skb, M_UAPSD) ? 0 : txq->axq_qnum, ds->ds_link, ds->ds_data,
+		ds->ds_ctl0, ds->ds_ctl1, ds->ds_hw[0], ds->ds_hw[1]);
+		
+	ath_tx_txqaddbuf(sc, NULL, txq, bf, ds, pktlen);
+	return 0;
+}
+
+#ifdef ATH_SUPERG_FF
+/*
+ * Flush FF staging queue.
+ */
+static int
+ath_ff_neverflushtestdone(struct ath_txq *txq, struct ath_buf *bf)
+{
+	return 0;
+}
+
+static int
+ath_ff_ageflushtestdone(struct ath_txq *txq, struct ath_buf *bf)
+{
+	if ( (txq->axq_totalqueued - bf->bf_queueage) < ATH_FF_STAGEQAGEMAX )
+		return 1;
+
+	return 0;
+}
+
+/* Caller must not hold ATH_TXQ_LOCK and ATH_TXBUF_LOCK
+ *
+ * Context: softIRQ
+ */
+static void
+ath_ffstageq_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_ff = NULL;
+	struct ieee80211_node *ni = NULL;
+	int pktlen;
+	int framecnt;
+
+	for (;;) {
+		ATH_TXQ_LOCK(txq);
+
+		bf_ff = TAILQ_LAST(&txq->axq_stageq, axq_headtype);
+		if ((!bf_ff) || ath_ff_flushdonetest(txq, bf_ff))
+		{
+			ATH_TXQ_UNLOCK(txq);
+			break;
+		}
+
+		ni = bf_ff->bf_node;
+		KASSERT(ATH_NODE(ni)->an_tx_ffbuf[bf_ff->bf_skb->priority],
+			("no bf_ff on staging queue %p", bf_ff));
+		ATH_NODE(ni)->an_tx_ffbuf[bf_ff->bf_skb->priority] = NULL;
+		TAILQ_REMOVE(&txq->axq_stageq, bf_ff, bf_stagelist);
+
+		ATH_TXQ_UNLOCK(txq);
+
+		/* encap and xmit */
+		bf_ff->bf_skb = ieee80211_encap(ni, bf_ff->bf_skb, &framecnt);
+		if (bf_ff->bf_skb == 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_ff->bf_skb->len;	/* NB: don't reference skb below */
+		if (ath_tx_start(sc->sc_dev, ni, bf_ff, bf_ff->bf_skb, 0) == 0)
+			continue;
+	bad:
+		ieee80211_free_node(ni);
+		if (bf_ff->bf_skb != NULL) {
+			dev_kfree_skb(bf_ff->bf_skb);
+			bf_ff->bf_skb = NULL;
+		}
+		bf_ff->bf_node = NULL;
+
+		ATH_TXBUF_LOCK_IRQ(sc);
+		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf_ff, bf_list);
+		ATH_TXBUF_UNLOCK_IRQ(sc);
+	}
+}
+#endif
+
+#define ATH_HARDSTART_GET_TX_BUF_WITH_LOCK				\
+	ATH_TXBUF_LOCK_IRQ(sc);						\
+	bf = STAILQ_FIRST(&sc->sc_txbuf);				\
+	if (bf != NULL) {						\
+		STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list);		\
+		STAILQ_INSERT_TAIL(&bf_head, bf, bf_list);              \
+	}                                                               \
+	/* XXX use a counter and leave at least one for mgmt frames */	\
+	if (STAILQ_EMPTY(&sc->sc_txbuf)) {				\
+		DPRINTF(sc, ATH_DEBUG_XMIT,				\
+			"%s: stop queue\n", __func__);			\
+		sc->sc_stats.ast_tx_qstop++;				\
+		netif_stop_queue(dev); 					\
+		sc->sc_devstopped = 1;					\
+		ATH_SCHEDULE_TQUEUE(&sc->sc_txtq, NULL); 		\
+	}								\
+	ATH_TXBUF_UNLOCK_IRQ(sc);					\
+	if (bf == NULL) {		/* NB: should not happen */	\
+		DPRINTF(sc,ATH_DEBUG_XMIT,				\
+			"%s: discard, no xmit buf\n", __func__);	\
+		sc->sc_stats.ast_tx_nobuf++;				\
+	}
+
+/*
+ * Transmit a data packet.  On failure caller is
+ * assumed to reclaim the resources.
+ *
+ * Context: process context with BH's disabled
+ */
+static int
+ath_hardstart(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211_node *ni = NULL;
+	struct ath_buf *bf = NULL;
+	struct ieee80211_cb *cb = (struct ieee80211_cb *) skb->cb;
+	struct ether_header *eh;
+	STAILQ_HEAD(tmp_bf_head, ath_buf) bf_head;
+	struct ath_buf *tbf, *tempbf;
+	struct sk_buff *tskb;
+	int framecnt;
+	int requeue = 0;
+#ifdef ATH_SUPERG_FF
+	int pktlen;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_node *an;
+	struct ath_txq *txq = NULL;
+	int ff_flush;
+	struct ieee80211vap *vap;
+#endif
+
+	if ((dev->flags & IFF_RUNNING) == 0 || sc->sc_invalid) {
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+			"%s: discard, invalid %d flags %x\n",
+			__func__, sc->sc_invalid, dev->flags);
+		sc->sc_stats.ast_tx_invalid++;
+		return -ENETDOWN;
+	}
+
+	STAILQ_INIT(&bf_head);
+
+	if (cb->flags & M_RAW) {
+		ATH_HARDSTART_GET_TX_BUF_WITH_LOCK;
+		if (bf == NULL)
+			goto hardstart_fail;
+		ath_tx_startraw(dev, bf,skb);
+		return NETDEV_TX_OK;
+	}
+
+	eh = (struct ether_header *) skb->data;
+	ni = cb->ni;		/* NB: always passed down by 802.11 layer */
+	if (ni == NULL) {
+		/* NB: this happens if someone marks the underlying device up */
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+			"%s: discard, no node in cb\n", __func__);
+		goto hardstart_fail;
+	}
+#ifdef ATH_SUPERG_FF
+	vap = ni->ni_vap;
+
+	if (M_FLAG_GET(skb, M_UAPSD)) {
+		/* bypass FF handling */
+		ATH_HARDSTART_GET_TX_BUF_WITH_LOCK;
+		if (bf == NULL)
+			goto hardstart_fail;
+		goto ff_bypass;
+	}
+
+	/*
+	 * Fast frames check.
+	 */
+	ATH_FF_MAGIC_CLR(skb);
+	an = ATH_NODE(ni);
+
+	txq = sc->sc_ac2q[skb->priority];
+
+	if (txq->axq_depth > TAIL_DROP_COUNT) {
+		sc->sc_stats.ast_tx_discard++;
+		/* queue is full, let the kernel backlog the skb */
+		requeue = 1;
+		goto hardstart_fail;
+	}
+
+	/* NB: use this lock to protect an->an_ff_txbuf in athff_can_aggregate()
+	 *     call too.
+	 */
+	ATH_TXQ_LOCK(txq);
+	if (athff_can_aggregate(sc, eh, an, skb, vap->iv_fragthreshold, &ff_flush)) {
+
+		if (an->an_tx_ffbuf[skb->priority]) { /* i.e., frame on the staging queue */
+			bf = an->an_tx_ffbuf[skb->priority];
+
+			/* get (and remove) the frame from staging queue */
+			TAILQ_REMOVE(&txq->axq_stageq, bf, bf_stagelist);
+			an->an_tx_ffbuf[skb->priority] = NULL;
+
+			ATH_TXQ_UNLOCK(txq);
+
+			/*
+			 * chain skbs and add FF magic
+			 *
+			 * NB: the arriving skb should not be on a list (skb->list),
+			 *     so "re-using" the skb next field should be OK.
+			 */
+			bf->bf_skb->next = skb;
+			skb->next = NULL;
+			skb = bf->bf_skb;
+			ATH_FF_MAGIC_PUT(skb);
+
+			/* decrement extra node reference made when an_tx_ffbuf[] was set */
+			//ieee80211_free_node(ni); /* XXX where was it set ? */
+
+			DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF,
+				"%s: aggregating fast-frame\n", __func__);
+		} else {
+			/* NB: careful grabbing the TX_BUF lock since still holding the txq lock.
+			 *     this could be avoided by always obtaining the txbuf earlier,
+			 *     but the "if" portion of this "if/else" clause would then need
+			 *     to give the buffer back.
+			 */
+			ATH_HARDSTART_GET_TX_BUF_WITH_LOCK;
+			if (bf == NULL) {
+				ATH_TXQ_UNLOCK(txq);
+				goto hardstart_fail;
+			}
+			DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF,
+				"%s: adding to fast-frame stage Q\n", __func__);
+
+			bf->bf_skb = skb;
+			bf->bf_node = ni;
+			bf->bf_queueage = txq->axq_totalqueued;
+			an->an_tx_ffbuf[skb->priority] = bf;
+
+			TAILQ_INSERT_HEAD(&txq->axq_stageq, bf, bf_stagelist);
+
+			ATH_TXQ_UNLOCK(txq);
+
+			return NETDEV_TX_OK;
+		}
+	} else {
+		if (ff_flush) {
+			struct ath_buf *bf_ff = an->an_tx_ffbuf[skb->priority];
+
+			TAILQ_REMOVE(&txq->axq_stageq, bf_ff, bf_stagelist);
+			an->an_tx_ffbuf[skb->priority] = NULL;
+
+			ATH_TXQ_UNLOCK(txq);
+
+			/* encap and xmit */
+			bf_ff->bf_skb = ieee80211_encap(ni, bf_ff->bf_skb, &framecnt);
+
+			if (bf_ff->bf_skb == NULL) {
+				DPRINTF(sc, ATH_DEBUG_XMIT,
+					"%s: discard, ff flush encap failure\n",
+					__func__);
+				sc->sc_stats.ast_tx_encap++;
+				goto ff_flushbad;
+			}
+			pktlen = bf_ff->bf_skb->len;	/* NB: don't reference skb below */
+			/* NB: ath_tx_start() will use ATH_TXBUF_LOCK_BH(). The _BH
+			 *     portion is not needed here since we're running at
+			 *     interrupt time, but should be harmless.
+			 */
+			if (ath_tx_start(dev, ni, bf_ff, bf_ff->bf_skb, 0))
+				goto ff_flushbad;
+			goto ff_flushdone;
+		ff_flushbad:
+			DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF,
+				"%s: ff stageq flush failure\n", __func__);
+			ieee80211_free_node(ni);
+			if (bf_ff->bf_skb) {
+				dev_kfree_skb(bf_ff->bf_skb);
+				bf_ff->bf_skb = NULL;
+			}
+			bf_ff->bf_node = NULL;
+
+			ATH_TXBUF_LOCK(sc);
+			STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf_ff, bf_list);
+			ATH_TXBUF_UNLOCK(sc);
+			goto ff_flushdone;
+		}
+		/*
+		 * XXX: out-of-order condition only occurs for AP mode and multicast.
+		 *      But, there may be no valid way to get this condition.
+		 */
+		else if (an->an_tx_ffbuf[skb->priority]) {
+			DPRINTF(sc, ATH_DEBUG_XMIT | ATH_DEBUG_FF,
+				"%s: Out-Of-Order fast-frame\n", __func__);
+			ATH_TXQ_UNLOCK(txq);
+		} else
+			ATH_TXQ_UNLOCK(txq);
+
+	ff_flushdone:
+		ATH_HARDSTART_GET_TX_BUF_WITH_LOCK;
+		if (bf == NULL)
+			goto hardstart_fail;
+	}
+
+ff_bypass:
+
+#else /* ATH_SUPERG_FF */
+
+	ATH_HARDSTART_GET_TX_BUF_WITH_LOCK;
+
+#endif /* ATH_SUPERG_FF */
+
+	/*
+	 * Encapsulate the packet for transmission.
+	 */
+	skb = ieee80211_encap(ni, skb, &framecnt);
+	if (skb == NULL) {
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+			"%s: discard, encapsulation failure\n", __func__);
+		sc->sc_stats.ast_tx_encap++;
+		goto hardstart_fail;
+	}
+
+	if (framecnt > 1) {
+		int bfcnt;
+
+		/*
+		**  Allocate 1 ath_buf for each frame given 1 was 
+		**  already alloc'd
+		*/
+		ATH_TXBUF_LOCK(sc);
+		for (bfcnt = 1; bfcnt < framecnt; ++bfcnt) {
+			if ((tbf = STAILQ_FIRST(&sc->sc_txbuf)) != NULL) {
+				STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list);
+				STAILQ_INSERT_TAIL(&bf_head, tbf, bf_list);
+			}
+			else
+				break;
+			
+			ieee80211_ref_node(ni);
+		}
+
+		if (bfcnt != framecnt) {
+			if (!STAILQ_EMPTY(&bf_head)) {
+				/*
+				**  Failed to alloc enough ath_bufs;
+				**  return to sc_txbuf list
+				*/
+				STAILQ_FOREACH_SAFE(tbf, &bf_head, bf_list, tempbf) {
+					STAILQ_INSERT_TAIL(&sc->sc_txbuf, tbf, bf_list);
+				}
+			}
+			ATH_TXBUF_UNLOCK(sc);
+			STAILQ_INIT(&bf_head);
+			goto hardstart_fail;
+		}
+		ATH_TXBUF_UNLOCK(sc);
+
+		while ((bf = STAILQ_FIRST(&bf_head)) != NULL && skb != NULL) {
+			int nextfraglen = 0;
+
+			STAILQ_REMOVE_HEAD(&bf_head, bf_list);
+			tskb = skb->next;
+			skb->next = NULL;
+			if (tskb)
+				nextfraglen = tskb->len;
+
+			if (ath_tx_start(dev, ni, bf, skb, nextfraglen) != 0) {
+				STAILQ_INSERT_TAIL(&bf_head, bf, bf_list);
+				skb->next = tskb;
+				goto hardstart_fail;
+			}
+			skb = tskb;
+		}
+	} else {
+		if (ath_tx_start(dev, ni, bf, skb, 0) != 0) {
+			STAILQ_INSERT_TAIL(&bf_head, bf, bf_list);
+			goto hardstart_fail;
+		}
+	}
+
+#ifdef ATH_SUPERG_FF
+	/*
+	 * flush out stale FF from staging Q for applicable operational modes.
+	 */
+	/* XXX: ADHOC mode too? */
+	if (txq && ic->ic_opmode == IEEE80211_M_HOSTAP)
+		ath_ffstageq_flush(sc, txq, ath_ff_ageflushtestdone);
+#endif
+
+	return NETDEV_TX_OK;
+
+hardstart_fail:
+	if (!STAILQ_EMPTY(&bf_head)) {
+		ATH_TXBUF_LOCK(sc);
+		STAILQ_FOREACH_SAFE(tbf, &bf_head, bf_list, tempbf) {
+			tbf->bf_skb = NULL;
+			tbf->bf_node = NULL;
+			
+			if (ni != NULL) 
+				ieee80211_free_node(ni);
+
+			STAILQ_INSERT_TAIL(&sc->sc_txbuf, tbf, bf_list);
+		}
+		ATH_TXBUF_UNLOCK(sc);
+	}
+	
+	/* let the kernel requeue the skb (don't free it!) */
+	if (requeue)
+		return NETDEV_TX_BUSY;
+
+	/* free sk_buffs */
+	while (skb) {
+		tskb = skb->next;
+		skb->next = NULL;
+		dev_kfree_skb(skb);
+		skb = tskb;
+	}
+	return NETDEV_TX_OK;
+}
+#undef ATH_HARDSTART_GET_TX_BUF_WITH_LOCK
+
+/*
+ * Transmit a management frame.  On failure we reclaim the skbuff.
+ * Note that management frames come directly from the 802.11 layer
+ * and do not honor the send queue flow control.  Need to investigate
+ * using priority queuing so management frames can bypass data.
+ *
+ * Context: hwIRQ and softIRQ
+ */
+static int
+ath_mgtstart(struct ieee80211com *ic, struct sk_buff *skb)
+{
+	struct net_device *dev = ic->ic_dev;
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211_node *ni = NULL;
+	struct ath_buf *bf = NULL;
+	struct ieee80211_cb *cb;
+	int error;
+
+	if ((dev->flags & IFF_RUNNING) == 0 || sc->sc_invalid) {
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+			"%s: discard, invalid %d flags %x\n",
+			__func__, sc->sc_invalid, dev->flags);
+		sc->sc_stats.ast_tx_invalid++;
+		error = -ENETDOWN;
+		goto bad;
+	}
+	/*
+	 * Grab a TX buffer and associated resources.
+	 */
+	ATH_TXBUF_LOCK_IRQ(sc);
+	bf = STAILQ_FIRST(&sc->sc_txbuf);
+	if (bf != NULL)
+		STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list);
+	if (STAILQ_EMPTY(&sc->sc_txbuf)) {
+		DPRINTF(sc, ATH_DEBUG_XMIT, "%s: stop queue\n", __func__);
+		sc->sc_stats.ast_tx_qstop++;
+		netif_stop_queue(dev);
+		sc->sc_devstopped=1;
+		ATH_SCHEDULE_TQUEUE(&sc->sc_txtq, NULL);
+	}
+	ATH_TXBUF_UNLOCK_IRQ(sc);
+	if (bf == NULL) {
+		printk("ath_mgtstart: discard, no xmit buf\n");
+		sc->sc_stats.ast_tx_nobufmgt++;
+		error = -ENOBUFS;
+		goto bad;
+	}
+
+	/*
+	 * NB: the referenced node pointer is in the
+	 * control block of the sk_buff.  This is
+	 * placed there by ieee80211_mgmt_output because
+	 * we need to hold the reference with the frame.
+	 */
+	cb = (struct ieee80211_cb *)skb->cb;
+	ni = cb->ni;
+	error = ath_tx_start(dev, ni, bf, skb, 0);
+	if (error == 0) {
+		sc->sc_stats.ast_tx_mgmt++;
+		return 0;
+	}
+	/* fall thru... */
+bad:
+	if (ni != NULL)
+		ieee80211_free_node(ni);
+	if (bf != NULL) {
+		bf->bf_skb = NULL;
+		bf->bf_node = NULL;
+
+		ATH_TXBUF_LOCK_IRQ(sc);
+		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+		ATH_TXBUF_UNLOCK_IRQ(sc);
+	}
+	dev_kfree_skb_any(skb);
+	skb = NULL;
+	return error;
+}
+
+#ifdef AR_DEBUG
+static void
+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[] = {
+		"WEP",
+		"AES-OCB",
+		"AES-CCM",
+		"CKIP",
+		"TKIP",
+		"CLR",
+	};
+	int i, n;
+
+	printk("%s: [%02u] %-7s ", tag, ix, ciphers[hk->kv_type]);
+	for (i = 0, n = hk->kv_len; i < n; i++)
+		printk("%02x", hk->kv_val[i]);
+	printk(" mac %s", ether_sprintf(mac));
+	if (hk->kv_type == HAL_CIPHER_TKIP) {
+		printk(" %s ", sc->sc_splitmic ? "mic" : "rxmic");
+		for (i = 0; i < sizeof(hk->kv_mic); i++)
+			printk("%02x", hk->kv_mic[i]);
+#if HAL_ABI_VERSION > 0x06052200
+		if (!sc->sc_splitmic) {
+			printk(" txmic ");
+			for (i = 0; i < sizeof(hk->kv_txmic); i++)
+				printk("%02x", hk->kv_txmic[i]);
+		}
+#endif
+	}
+	printk("\n");
+}
+#endif
+
+/*
+ * Set a TKIP key into the hardware.  This handles the
+ * potential distribution of key state to multiple key
+ * cache slots for TKIP.
+ */
+static int
+ath_keyset_tkip(struct ath_softc *sc, const struct ieee80211_key *k,
+	HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+#define	IEEE80211_KEY_XR	(IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)
+	static const u_int8_t zerobssid[IEEE80211_ADDR_LEN];
+	struct ath_hal *ah = sc->sc_ah;
+
+	KASSERT(k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP,
+		("got a non-TKIP key, cipher %u", k->wk_cipher->ic_cipher));
+	if ((k->wk_flags & IEEE80211_KEY_XR) == 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.
+		 */
+		memcpy(hk->kv_mic, k->wk_flags & IEEE80211_KEY_XMIT ?
+			k->wk_txmic : k->wk_rxmic, sizeof(hk->kv_mic));
+		KEYPRINTF(sc, k->wk_keyix, hk, mac);
+		return ath_hal_keyset(ah, k->wk_keyix, hk, mac);
+	}
+	return 0;
+#undef IEEE80211_KEY_XR
+}
+
+/*
+ * Set a net80211 key into the hardware.  This handles the
+ * potential distribution of key state to multiple key
+ * cache slots for TKIP with hardware MIC support.
+ */
+static int
+ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k,
+	const u_int8_t mac0[IEEE80211_ADDR_LEN],
+	struct ieee80211_node *bss)
+{
+#define	N(a)	((int)(sizeof(a)/sizeof(a[0])))
+	static const u_int8_t ciphermap[] = {
+		HAL_CIPHER_WEP,		/* IEEE80211_CIPHER_WEP */
+		HAL_CIPHER_TKIP,	/* IEEE80211_CIPHER_TKIP */
+		HAL_CIPHER_AES_OCB,	/* IEEE80211_CIPHER_AES_OCB */
+		HAL_CIPHER_AES_CCM,	/* IEEE80211_CIPHER_AES_CCM */
+		(u_int8_t) -1,		/* 4 is not allocated */
+		HAL_CIPHER_CKIP,	/* IEEE80211_CIPHER_CKIP */
+		HAL_CIPHER_CLR,		/* IEEE80211_CIPHER_NONE */
+	};
+	struct ath_hal *ah = sc->sc_ah;
+	const struct ieee80211_cipher *cip = k->wk_cipher;
+	u_int8_t gmac[IEEE80211_ADDR_LEN];
+	const u_int8_t *mac;
+	HAL_KEYVAL hk;
+
+	memset(&hk, 0, sizeof(hk));
+	/*
+	 * Software crypto uses a "clear key" so non-crypto
+	 * state kept in the key cache are maintained and
+	 * so that rx frames have an entry to match.
+	 */
+	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
+		KASSERT(cip->ic_cipher < N(ciphermap),
+			("invalid cipher type %u", cip->ic_cipher));
+		hk.kv_type = ciphermap[cip->ic_cipher];
+		hk.kv_len = k->wk_keylen;
+		memcpy(hk.kv_val, k->wk_key, k->wk_keylen);
+	} else
+		hk.kv_type = HAL_CIPHER_CLR;
+
+	if ((k->wk_flags & IEEE80211_KEY_GROUP) && sc->sc_mcastkey) {
+		/*
+		 * Group keys on hardware that supports multicast frame
+		 * key search use a mac that is the sender's address with
+		 * the high bit set instead of the app-specified address.
+		 */
+		IEEE80211_ADDR_COPY(gmac, bss->ni_macaddr);
+		gmac[0] |= 0x80;
+		mac = gmac;
+	} else
+		mac = mac0;
+
+	if (hk.kv_type == HAL_CIPHER_TKIP &&
+	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
+		return ath_keyset_tkip(sc, k, &hk, mac);
+	} else {
+		KEYPRINTF(sc, k->wk_keyix, &hk, mac);
+		return ath_hal_keyset(ah, k->wk_keyix, &hk, mac);
+	}
+#undef N
+}
+
+/*
+ * 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_2pair(struct ath_softc *sc)
+{
+#define	N(a)	((int)(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;
+			}
+			/* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */
+			if (isset(sc->sc_keymap, keyix + 32) ||
+			    isset(sc->sc_keymap, keyix + 64) ||
+			    isset(sc->sc_keymap, keyix + 32 + 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);
+			setbit(sc->sc_keymap, keyix + 32);
+			setbit(sc->sc_keymap, keyix + 32 + 64);
+			DPRINTF(sc, ATH_DEBUG_KEYCACHE,
+				"%s: key pair %u,%u %u,%u\n",
+				__func__, keyix, keyix + 64,
+				keyix + 32, keyix + 32 + 64);
+			return keyix;
+		}
+	}
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__);
+	return IEEE80211_KEYIX_NONE;
+#undef N
+}
+
+/*
+ * 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)
+{
+#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);
+			return keyix;
+		}
+	}
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__);
+	return IEEE80211_KEYIX_NONE;
+#undef N
+}
+
+/*
+ * Allocate a single key cache slot.
+ */
+static u_int16_t
+key_alloc_single(struct ath_softc *sc)
+{
+#define	N(a)	((int)(sizeof(a)/sizeof(a[0])))
+	u_int i, keyix;
+
+	/* XXX try i,i+32,i+64,i+32+64 to minimize key pair conflicts */
+	for (i = 0; i < N(sc->sc_keymap); i++) {
+		u_int8_t b = sc->sc_keymap[i];
+		if (b != 0xff) {
+			/*
+			 * One or more slots are free.
+			 */
+			keyix = i * NBBY;
+			while (b & 1)
+				keyix++, b >>= 1;
+			setbit(sc->sc_keymap, keyix);
+			DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: key %u\n",
+				__func__, keyix);
+			return keyix;
+		}
+	}
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of space\n", __func__);
+	return IEEE80211_KEYIX_NONE;
+#undef N
+}
+
+/*
+ * Allocate one or more key cache slots for a unicast key.  The
+ * key itself is needed only to identify the cipher.  For hardware
+ * TKIP with split cipher+MIC keys we allocate two key cache slot
+ * pairs so that we can setup separate TX and RX MIC keys.  Note
+ * that the MIC key for a TKIP key at slot i is assumed by the
+ * hardware to be at slot i+64.  This limits TKIP keys to the first
+ * 64 entries.
+ */
+static int
+ath_key_alloc(struct ieee80211vap *vap, const struct ieee80211_key *k)
+{
+	struct net_device *dev = vap->iv_ic->ic_dev;
+	struct ath_softc *sc = dev->priv;
+
+	/*
+	 * Group key allocation must be handled specially for
+	 * parts that do not support multicast key cache search
+	 * functionality.  For those parts the key id must match
+	 * the h/w key index so lookups find the right key.  On
+	 * parts w/ the key search facility we install the sender's
+	 * mac address (with the high bit set) and let the hardware
+	 * find the key w/o using the key id.  This is preferred as
+	 * it permits us to support multiple users for adhoc and/or
+	 * multi-station operation.
+	 */
+	if ((k->wk_flags & IEEE80211_KEY_GROUP) && !sc->sc_mcastkey) {
+		u_int keyix;
+
+		if (!(&vap->iv_nw_keys[0] <= k &&
+		    k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) {
+			/* should not happen */
+			DPRINTF(sc, ATH_DEBUG_KEYCACHE,
+				"%s: bogus group key\n", __func__);
+			return IEEE80211_KEYIX_NONE;
+		}
+		keyix = k - vap->iv_nw_keys;
+		/*
+		 * XXX we pre-allocate the global keys so
+		 * have no way to check if they've already been allocated.
+		 */
+		return keyix;
+	}
+	/*
+	 * We allocate two pair for TKIP when using the h/w to do
+	 * the MIC.  For everything else, including software crypto,
+	 * we allocate a single entry.  Note that s/w crypto requires
+	 * a pass-through slot on the 5211 and 5212.  The 5210 does
+	 * not support pass-through cache entries and we map all
+	 * those requests to slot 0.
+	 *
+	 * Allocate 1 pair of keys for WEP case. Make sure the key
+	 * is not a shared-key.
+	 */
+	if (k->wk_flags & IEEE80211_KEY_SWCRYPT)
+		return key_alloc_single(sc);
+	else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP &&
+		(k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
+		if (sc->sc_splitmic)
+			return key_alloc_2pair(sc);
+		else
+			return key_alloc_pair(sc);
+	} else
+		return key_alloc_single(sc);
+}
+
+/*
+ * Delete an entry in the key cache allocated by ath_key_alloc.
+ */
+static int
+ath_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k,
+				struct ieee80211_node *ninfo)
+{
+	struct net_device *dev = vap->iv_ic->ic_dev;
+	struct ath_softc *sc = dev->priv;
+	struct ath_hal *ah = sc->sc_ah;
+	const struct ieee80211_cipher *cip = k->wk_cipher;
+	struct ieee80211_node *ni;
+	u_int keyix = k->wk_keyix;
+	int rxkeyoff = 0;
+
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: delete key %u\n", __func__, keyix);
+
+	ath_hal_keyreset(ah, keyix);
+	/*
+	 * Check the key->node map and flush any ref.
+	 */
+	ni = sc->sc_keyixmap[keyix];
+	if (ni != NULL) {
+		ieee80211_free_node(ni);
+		sc->sc_keyixmap[keyix] = NULL;
+	}
+	/*
+	 * Handle split tx/rx keying required for TKIP with h/w MIC.
+	 */
+	if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
+	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic) {
+		ath_hal_keyreset(ah, keyix + 32);	/* RX key */
+		ni = sc->sc_keyixmap[keyix + 32];
+		if (ni != NULL) {			/* as above... */
+			ieee80211_free_node(ni);
+			sc->sc_keyixmap[keyix + 32] = NULL;
+		}
+	}
+
+	/* Remove receive key entry if one exists for static WEP case */
+	if (ninfo != NULL) {
+		rxkeyoff = ninfo->ni_rxkeyoff;
+		if (rxkeyoff != 0) {
+			ninfo->ni_rxkeyoff = 0;
+			ath_hal_keyreset(ah, keyix + rxkeyoff);
+			ni = sc->sc_keyixmap[keyix + rxkeyoff];
+			if (ni != NULL) {	/* as above... */
+				ieee80211_free_node(ni);
+				sc->sc_keyixmap[keyix + rxkeyoff] = NULL;
+			}
+		}
+	}
+
+	if (keyix >= IEEE80211_WEP_NKID) {
+		/*
+		 * Don't touch keymap entries for global keys so
+		 * they are never considered for dynamic allocation.
+		 */
+		clrbit(sc->sc_keymap, keyix);
+		if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
+		    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
+			clrbit(sc->sc_keymap, keyix + 64);	/* TX 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);
+			}
+		}
+
+		if (rxkeyoff != 0)
+			clrbit(sc->sc_keymap, keyix + rxkeyoff);/*RX Key */
+	}
+	return 1;
+}
+
+/*
+ * Set the key cache contents for the specified key.  Key cache
+ * slot(s) must already have been allocated by ath_key_alloc.
+ */
+static int
+ath_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k,
+	const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	struct net_device *dev = vap->iv_ic->ic_dev;
+	struct ath_softc *sc = dev->priv;
+
+	return ath_keyset(sc, k, mac, vap->iv_bss);
+}
+
+/*
+ * Block/unblock tx+rx processing while a key change is done.
+ * We assume the caller serializes key management operations
+ * so we only need to worry about synchronization with other
+ * uses that originate in the driver.
+ */
+static void
+ath_key_update_begin(struct ieee80211vap *vap)
+{
+	struct net_device *dev = vap->iv_ic->ic_dev;
+	struct ath_softc *sc = dev->priv;
+
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__);
+	/*
+	 * When called from the rx tasklet we cannot use
+	 * tasklet_disable because it will block waiting
+	 * for us to complete execution.
+	 *
+	 * XXX Using in_softirq is not right since we might
+	 * be called from other soft irq contexts than
+	 * ath_rx_tasklet.
+	 */
+	if (!in_softirq())
+		tasklet_disable(&sc->sc_rxtq);
+	netif_stop_queue(dev);
+}
+
+static void
+ath_key_update_end(struct ieee80211vap *vap)
+{
+	struct net_device *dev = vap->iv_ic->ic_dev;
+	struct ath_softc *sc = dev->priv;
+
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__);
+	netif_start_queue(dev);
+	if (!in_softirq())		/* NB: see above */
+		tasklet_enable(&sc->sc_rxtq);
+}
+
+/*
+ * Calculate the receive filter according to the
+ * operating mode and state:
+ *
+ * o always accept unicast, broadcast, and multicast traffic
+ * o maintain current state of phy error reception (the HAL
+ *   may enable phy error frames for noise immunity work)
+ * o probe request frames are accepted only when operating in
+ *   hostap, adhoc, or monitor modes
+ * o enable promiscuous mode according to the interface state
+ * o accept beacons:
+ *   - when operating in adhoc mode so the 802.11 layer creates
+ *     node table entries for peers,
+ *   - when operating in station mode for collecting rssi data when
+ *     the station is otherwise quiet, or
+ *   - when operating as a repeater so we see repeater-sta beacons
+ *   - when scanning
+ */
+static u_int32_t
+ath_calcrxfilter(struct ath_softc *sc)
+{
+#define	RX_FILTER_PRESERVE	(HAL_RX_FILTER_PHYERR | HAL_RX_FILTER_PHYRADAR)
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct net_device *dev = ic->ic_dev;
+	struct ath_hal *ah = sc->sc_ah;
+	u_int32_t rfilt;
+
+	rfilt = (ath_hal_getrxfilter(ah) & RX_FILTER_PRESERVE) |
+		 HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST |
+		 HAL_RX_FILTER_MCAST;
+	if (ic->ic_opmode != IEEE80211_M_STA)
+		rfilt |= HAL_RX_FILTER_PROBEREQ;
+	if (ic->ic_opmode != IEEE80211_M_HOSTAP && (dev->flags & IFF_PROMISC))
+		rfilt |= HAL_RX_FILTER_PROM;
+	if (ic->ic_opmode == IEEE80211_M_STA ||
+	    sc->sc_opmode == HAL_M_IBSS ||	/* NB: AHDEMO too */
+	    (sc->sc_nostabeacons) || sc->sc_scanning)
+		rfilt |= HAL_RX_FILTER_BEACON;
+	if (sc->sc_nmonvaps > 0) 
+		rfilt |= (HAL_RX_FILTER_CONTROL | HAL_RX_FILTER_BEACON | 
+			  HAL_RX_FILTER_PROBEREQ | HAL_RX_FILTER_PROM);
+	return rfilt;
+#undef RX_FILTER_PRESERVE
+}
+
+/*
+ * Merge multicast addresses from all VAPs to form the
+ * hardware filter.  Ideally we should only inspect our
+ * own list and the 802.11 layer would merge for us but
+ * that's a bit difficult so for now we put the onus on
+ * the driver.
+ */
+static void
+ath_merge_mcast(struct ath_softc *sc, u_int32_t mfilt[2])
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ieee80211vap *vap;
+	struct dev_mc_list *mc;
+	u_int32_t val;
+	u_int8_t pos;
+
+	mfilt[0] = mfilt[1] = 0;
+	/* XXX locking */
+	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+		struct net_device *dev = vap->iv_dev;
+		for (mc = dev->mc_list; mc; mc = mc->next) {
+			/* calculate XOR of eight 6-bit values */
+			val = LE_READ_4(mc->dmi_addr + 0);
+			pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
+			val = LE_READ_4(mc->dmi_addr + 3);
+			pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
+			pos &= 0x3f;
+			mfilt[pos / 32] |= (1 << (pos % 32));
+		}
+	}
+}
+
+static void
+ath_mode_init(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ath_hal *ah = sc->sc_ah;
+	u_int32_t rfilt, mfilt[2];
+
+	/* configure rx filter */
+	rfilt = ath_calcrxfilter(sc);
+	ath_hal_setrxfilter(ah, rfilt);
+
+	/* configure bssid mask */
+	if (sc->sc_hasbmask)
+		ath_hal_setbssidmask(ah, sc->sc_bssidmask);
+
+	/* configure operational mode */
+	ath_hal_setopmode(ah);
+
+	/* calculate and install multicast filter */
+	if ((dev->flags & IFF_ALLMULTI) == 0)
+		ath_merge_mcast(sc, mfilt);
+	else
+		mfilt[0] = mfilt[1] = ~0;
+	ath_hal_setmcastfilter(ah, mfilt[0], mfilt[1]);
+	DPRINTF(sc, ATH_DEBUG_STATE,
+	     "%s: RX filter 0x%x, MC filter %08x:%08x\n",
+	     __func__, rfilt, mfilt[0], mfilt[1]);
+}
+
+/*
+ * Set the slot time based on the current setting.
+ */
+static void
+ath_setslottime(struct ath_softc *sc)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+
+	if (sc->sc_slottimeconf > 0) /* manual override */
+		ath_hal_setslottime(ah, sc->sc_slottimeconf);
+	else if (ic->ic_flags & IEEE80211_F_SHSLOT)
+		ath_hal_setslottime(ah, HAL_SLOT_TIME_9);
+	else
+		ath_hal_setslottime(ah, HAL_SLOT_TIME_20);
+	sc->sc_updateslot = OK;
+}
+
+/*
+ * Callback from the 802.11 layer to update the
+ * slot time based on the current setting.
+ */
+static void
+ath_updateslot(struct net_device *dev)
+{
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	/*
+	 * When not coordinating the BSS, change the hardware
+	 * immediately.  For other operation we defer the change
+	 * until beacon updates have propagated to the stations.
+	 */
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+		sc->sc_updateslot = UPDATE;
+	else if (dev->flags & IFF_RUNNING)
+		ath_setslottime(sc);
+}
+
+#ifdef ATH_SUPERG_DYNTURBO
+/*
+ * Dynamic turbo support.
+ * XXX much of this could be moved up to the net80211 layer.
+ */
+
+/*
+ * Configure dynamic turbo state on beacon setup.
+ */
+static void
+ath_beacon_dturbo_config(struct ieee80211vap *vap, u_int32_t intval)
+{
+#define	IS_CAPABLE(vap) \
+	(vap->iv_bss && (vap->iv_bss->ni_ath_flags & (IEEE80211_ATHC_TURBOP )) == \
+		(IEEE80211_ATHC_TURBOP))
+	struct ieee80211com *ic = vap->iv_ic;
+	struct ath_softc *sc = ic->ic_dev->priv;
+
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP && IS_CAPABLE(vap)) {
+
+		/* Dynamic Turbo is supported on this channel. */
+		sc->sc_dturbo = 1;
+		sc->sc_dturbo_tcount = 0;
+		sc->sc_dturbo_switch = 0;
+		sc->sc_ignore_ar = 0;
+
+		/* Set the initial ATHC_BOOST capability. */
+		if (ic->ic_bsschan->ic_flags & CHANNEL_TURBO)
+			ic->ic_ath_cap |=  IEEE80211_ATHC_BOOST;
+		else
+			ic->ic_ath_cap &= ~IEEE80211_ATHC_BOOST;
+
+		/*
+		 * Calculate time & bandwidth thresholds
+		 *
+		 * sc_dturbo_base_tmin  :  ~70 seconds
+		 * sc_dturbo_turbo_tmax : ~120 seconds
+		 *
+		 * NB: scale calculated values to account for staggered
+		 *     beacon handling
+		 */
+		sc->sc_dturbo_base_tmin  = 70  * 1024 / ic->ic_lintval;
+		sc->sc_dturbo_turbo_tmax = 120 * 1024 / ic->ic_lintval;
+		sc->sc_dturbo_turbo_tmin = 5 * 1024 / ic->ic_lintval;
+		/* convert the thresholds from BW/sec to BW/beacon period */
+		sc->sc_dturbo_bw_base    = ATH_TURBO_DN_THRESH/(1024/ic->ic_lintval);  
+		sc->sc_dturbo_bw_turbo   = ATH_TURBO_UP_THRESH/(1024/ic->ic_lintval); 
+		/* time in hold state in number of beacon */
+		sc->sc_dturbo_hold_max   = (ATH_TURBO_PERIOD_HOLD * 1024)/ic->ic_lintval;
+	} else {
+		sc->sc_dturbo = 0;
+		ic->ic_ath_cap &= ~IEEE80211_ATHC_BOOST;
+	}
+#undef IS_CAPABLE
+}
+
+/*
+ * Update dynamic turbo state at SWBA.  We assume we care
+ * called only if dynamic turbo has been enabled (sc_turbo).
+ */
+static void
+ath_beacon_dturbo_update(struct ieee80211vap *vap, int *needmark,u_int8_t dtim)
+{
+	struct ieee80211com *ic = vap->iv_ic;
+	struct ath_softc *sc = ic->ic_dev->priv;
+	u_int32_t bss_traffic;
+
+	/* TBD: Age out CHANNEL_INTERFERENCE */
+	if (sc->sc_ignore_ar) {
+		/* 
+		 * Ignore AR for this beacon; a dynamic turbo
+		 * switch just happened and the information
+		 * is invalid.  Notify AR support of the channel
+		 * change.
+		 */
+		sc->sc_ignore_ar = 0;
+		ath_hal_ar_enable(sc->sc_ah);
+	}
+	sc->sc_dturbo_tcount++;
+	/*
+	 * Calculate BSS traffic over the previous interval.
+	 */
+	bss_traffic = (sc->sc_devstats.tx_bytes + sc->sc_devstats.rx_bytes)
+		    - sc->sc_dturbo_bytes;
+	sc->sc_dturbo_bytes = sc->sc_devstats.tx_bytes
+			    + sc->sc_devstats.rx_bytes;
+	if (ic->ic_ath_cap & IEEE80211_ATHC_BOOST) {
+ 		/* 
+  		* before switching to base mode,
+  		* make sure that the conditions( low rssi, low bw) to switch mode 
+  		* hold for some time and time in turbo exceeds minimum turbo time.
+  		*/
+ 
+		if (sc->sc_dturbo_tcount >= sc->sc_dturbo_turbo_tmin && 
+		   sc->sc_dturbo_hold ==0 &&
+		   (bss_traffic < sc->sc_dturbo_bw_base || !sc->sc_rate_recn_state)) {
+			sc->sc_dturbo_hold = 1;
+		} else {
+			if (sc->sc_dturbo_hold &&
+			   bss_traffic >= sc->sc_dturbo_bw_turbo && sc->sc_rate_recn_state) {
+				/* out of hold state */
+				sc->sc_dturbo_hold = 0;
+				sc->sc_dturbo_hold_count = sc->sc_dturbo_hold_max;
+			}
+		}
+		if (sc->sc_dturbo_hold && sc->sc_dturbo_hold_count)
+			sc->sc_dturbo_hold_count--;
+		/*
+		 * Current Mode: Turbo (i.e. BOOST)
+		 *
+		 * Transition to base occurs when one of the following
+		 * is true:
+		 *    1. its a DTIM beacon. 
+		 *    2. Maximum time in BOOST has elapsed (120 secs).
+		 *    3. Channel is marked with interference
+		 *    4. Average BSS traffic falls below 4Mbps 
+		 *    5. RSSI cannot support at least 18 Mbps rate 
+		 * XXX do bw checks at true beacon interval?
+		 */
+		if (dtim && 
+			(sc->sc_dturbo_tcount >= sc->sc_dturbo_turbo_tmax ||
+			 ((vap->iv_bss->ni_ath_flags & IEEE80211_ATHC_AR) && 
+			  (sc->sc_curchan.privFlags & CHANNEL_INTERFERENCE) &&
+			  IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) || 
+			 !sc->sc_dturbo_hold_count)) {
+			DPRINTF(sc, ATH_DEBUG_TURBO, "%s: Leaving turbo\n",
+					sc->sc_dev->name);
+			ic->ic_ath_cap &= ~IEEE80211_ATHC_BOOST;
+			vap->iv_bss->ni_ath_flags &= ~IEEE80211_ATHC_BOOST;
+			sc->sc_dturbo_tcount = 0;
+			sc->sc_dturbo_switch = 1;
+		}
+	} else {
+		/*
+		 * Current Mode: BASE
+		 *
+		 * Transition to Turbo (i.e. BOOST) when all of the
+		 * following are true:
+		 *
+		 * 1. its a DTIM beacon. 
+		 * 2. Dwell time at base has exceeded minimum (70 secs)
+		 * 3. Only DT-capable stations are associated
+		 * 4. Channel is marked interference-free.
+		 * 5. BSS data traffic averages at least 6Mbps 
+		 * 6. RSSI is good enough to support 36Mbps 
+		 * XXX do bw+rssi checks at true beacon interval?
+		 */
+		if (dtim && 
+			(sc->sc_dturbo_tcount >= sc->sc_dturbo_base_tmin &&
+			 (ic->ic_dt_sta_assoc != 0 &&
+			  ic->ic_sta_assoc == ic->ic_dt_sta_assoc) &&
+			 ((vap->iv_bss->ni_ath_flags & IEEE80211_ATHC_AR) == 0 || 
+			  (sc->sc_curchan.privFlags & CHANNEL_INTERFERENCE) == 0) &&
+			 bss_traffic >= sc->sc_dturbo_bw_turbo && 
+			 sc->sc_rate_recn_state)) {
+			DPRINTF(sc, ATH_DEBUG_TURBO, "%s: Entering turbo\n",
+					sc->sc_dev->name);
+			ic->ic_ath_cap |= IEEE80211_ATHC_BOOST;
+			vap->iv_bss->ni_ath_flags |= IEEE80211_ATHC_BOOST;
+			sc->sc_dturbo_tcount = 0;
+			sc->sc_dturbo_switch = 1;
+			sc->sc_dturbo_hold = 0;
+			sc->sc_dturbo_hold_count = sc->sc_dturbo_hold_max;
+		}
+	}
+}
+
+
+static int 
+ath_check_beacon_done(struct ath_softc *sc)
+{
+	struct ieee80211vap *vap=NULL;
+	struct ath_vap *avp;
+	struct ath_buf *bf;
+	struct sk_buff *skb;
+	struct ath_desc *ds;
+	struct ath_hal *ah = sc->sc_ah;
+	int slot;
+
+	/*
+	 * check if the last beacon went out with the mode change flag set.
+	 */
+	for (slot = 0; slot < ATH_BCBUF; slot++) {
+		if(sc->sc_bslot[slot]) { 
+			vap = sc->sc_bslot[slot];
+			break;
+		}
+	}
+	if (!vap)
+		 return 0;
+	avp = ATH_VAP(vap);
+	bf = avp->av_bcbuf;
+	skb = bf->bf_skb;
+	ds = bf->bf_desc;
+
+	return (ath_hal_txprocdesc(ah, ds) != HAL_EINPROGRESS);
+
+}
+
+/*
+ * Effect a turbo mode switch when operating in dynamic
+ * turbo mode. wait for beacon to go out before switching.
+ */
+static void
+ath_turbo_switch_mode(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct ath_softc *sc = dev->priv;
+	struct ieee80211com *ic = &sc->sc_ic;
+	int newflags;
+
+	KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP,
+		("unexpected operating mode %d", ic->ic_opmode));
+
+	DPRINTF(sc, ATH_DEBUG_STATE, "%s: dynamic turbo switch to %s mode\n",
+		dev->name,
+		ic->ic_ath_cap & IEEE80211_ATHC_BOOST ? "turbo" : "base");
+
+	if (!ath_check_beacon_done(sc)) {
+		/* 
+		 * beacon did not go out. reschedule tasklet.
+		 */
+		mod_timer(&sc->sc_dturbo_switch_mode, jiffies + msecs_to_jiffies(2));
+		return;
+	}
+
+	/* TBD: DTIM adjustments, delay CAB queue tx until after transmit */
+	newflags = ic->ic_bsschan->ic_flags;
+	if (ic->ic_ath_cap & IEEE80211_ATHC_BOOST) {
+		if (IEEE80211_IS_CHAN_2GHZ(ic->ic_bsschan)) {
+			/*
+			 * Ignore AR next beacon. the AR detection
+			 * code detects the traffic in normal channel
+			 * from stations during transition delays
+			 * between AP and station.
+			 */
+			sc->sc_ignore_ar = 1;
+			ath_hal_ar_disable(sc->sc_ah);
+		}
+		newflags |= IEEE80211_CHAN_TURBO;
+	} else
+		newflags &= ~IEEE80211_CHAN_TURBO;
+	ieee80211_dturbo_switch(ic, newflags);
+	/* XXX ieee80211_reset_erp? */
+}
+#endif /* ATH_SUPERG_DYNTURBO */
+
+/*
+ * Setup a h/w transmit queue for beacons.
+ */
+static int
+ath_beaconq_setup(struct ath_hal *ah)
+{
+	HAL_TXQ_INFO qi;
+
+	memset(&qi, 0, sizeof(qi));
+	qi.tqi_aifs = 1;
+	qi.tqi_cwmin = 0;
+	qi.tqi_cwmax = 0;
+#ifdef ATH_SUPERG_DYNTURBO
+	qi.tqi_qflags = HAL_TXQ_TXDESCINT_ENABLE;
+#endif
+	/* NB: don't enable any interrupts */
+	return ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, &qi);
+}
+
+/*
+ * Configure IFS parameter for the beacon queue.
+ */
+static int
+ath_beaconq_config(struct ath_softc *sc)
+{
+#define	ATH_EXPONENT_TO_VALUE(v)	((1<<v)-1)
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_TXQ_INFO qi;
+
+	ath_hal_gettxqueueprops(ah, sc->sc_bhalq, &qi);
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+		/*
+		 * Always burst out beacon and CAB traffic.
+		 */
+		qi.tqi_aifs = 1;
+		qi.tqi_cwmin = 0;
+		qi.tqi_cwmax = 0;
+	} else {
+		struct wmeParams *wmep =
+			&ic->ic_wme.wme_chanParams.cap_wmeParams[WME_AC_BE];
+		/*
+		 * Adhoc mode; important thing is to use 2x cwmin.
+		 */
+		qi.tqi_aifs = wmep->wmep_aifsn;
+		qi.tqi_cwmin = 2 * ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin);
+		qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax);
+	}
+
+	if (!ath_hal_settxqueueprops(ah, sc->sc_bhalq, &qi)) {
+		printk("%s: unable to update h/w beacon queue parameters\n",
+			sc->sc_dev->name);
+		return 0;
+	} else {
+		ath_hal_resettxqueue(ah, sc->sc_bhalq);	/* push to h/w */
+		return 1;
+	}
+#undef ATH_EXPONENT_TO_VALUE
+}
+
+/*
+ * Allocate and setup an initial beacon frame.
+ *
+ * Context: softIRQ
+ */
+static int
+ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni)
+{
+	struct ath_vap *avp = ATH_VAP(ni->ni_vap);
+	struct ieee80211_frame *wh;
+	struct ath_buf *bf;
+	struct sk_buff *skb;
+
+	/*
+	 * release the previous beacon's skb if it already exists.
+	 */
+	bf = avp->av_bcbuf;
+	if (bf->bf_skb != NULL) {
+		bus_unmap_single(sc->sc_bdev,
+		    bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE);
+		dev_kfree_skb(bf->bf_skb);
+		bf->bf_skb = NULL;
+	}
+	if (bf->bf_node != NULL) {
+		ieee80211_free_node(bf->bf_node);
+		bf->bf_node = NULL;
+	}
+
+	/*
+	 * NB: the beacon data buffer must be 32-bit aligned;
+	 * we assume the mbuf routines will return us something
+	 * with this alignment (perhaps should assert).
+	 */
+	skb = ieee80211_beacon_alloc(ni, &avp->av_boff);
+	if (skb == NULL) {
+		DPRINTF(sc, ATH_DEBUG_BEACON, "%s: cannot get sk_buff\n",
+			__func__);
+		sc->sc_stats.ast_be_nobuf++;
+		return -ENOMEM;
+	}
+
+	/*
+	 * Calculate a TSF adjustment factor required for
+	 * staggered beacons.  Note that we assume the format
+	 * of the beacon frame leaves the tstamp field immediately
+	 * following the header.
+	 */
+	if (sc->sc_stagbeacons && avp->av_bslot > 0) {
+		uint64_t tuadjust;
+		__le64 tsfadjust;
+		/*
+		 * The beacon interval is in TU's; the TSF in usecs.
+		 * We figure out how many TU's to add to align the
+		 * timestamp then convert to TSF units and handle
+		 * byte swapping before writing it in the frame.
+		 * The hardware will then add this each time a beacon
+		 * frame is sent.  Note that we align VAPs 1..N
+		 * and leave VAP 0 untouched.  This means VAP 0
+		 * has a timestamp in one beacon interval while the
+		 * others get a timestamp aligned to the next interval.
+		 */
+		tuadjust = (ni->ni_intval * (ATH_BCBUF - avp->av_bslot)) / ATH_BCBUF;
+		tsfadjust = cpu_to_le64(tuadjust << 10);	/* TU->TSF */
+
+		DPRINTF(sc, ATH_DEBUG_BEACON,
+			"%s: %s beacons, bslot %d intval %u tsfadjust(Kus) %llu\n",
+			__func__, sc->sc_stagbeacons ? "stagger" : "burst",
+			avp->av_bslot, ni->ni_intval, (long long) tuadjust);
+
+		wh = (struct ieee80211_frame *) skb->data;
+		memcpy(&wh[1], &tsfadjust, sizeof(tsfadjust));
+	}
+
+	bf->bf_node = ieee80211_ref_node(ni);
+	bf->bf_skbaddr = bus_map_single(sc->sc_bdev,
+		skb->data, skb->len, BUS_DMA_TODEVICE);
+	bf->bf_skb = skb;
+
+	return 0;
+}
+
+/*
+ * Setup the beacon frame for transmit.
+ */
+static void
+ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf)
+{
+#define	USE_SHPREAMBLE(_ic) \
+	(((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\
+		== IEEE80211_F_SHPREAMBLE)
+	struct ieee80211_node *ni = bf->bf_node;
+	struct ieee80211com *ic = ni->ni_ic;
+	struct sk_buff *skb = bf->bf_skb;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ath_desc *ds;
+	int flags;
+	int antenna = sc->sc_txantenna;
+	const HAL_RATE_TABLE *rt;
+	u_int8_t rix, rate;
+	int ctsrate = 0;
+	int ctsduration = 0;
+
+	DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: m %p len %u\n",
+		__