[svn-inject] Installing original source of madwifi (0.9.4+r4100.20090929) upstream/0.9.4+r4100.20090929
authorgregor herrmann <gregoa@debian.org>
Mon, 20 Dec 2010 18:12:44 +0000 (18:12 -0000)
committergregor herrmann <gregoa@debian.org>
Mon, 20 Dec 2010 18:12:44 +0000 (18:12 -0000)
207 files changed:
BuildCaps.inc [new file with mode: 0644]
COPYRIGHT [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile [new file with mode: 0644]
Makefile.inc [new file with mode: 0644]
README [new file with mode: 0644]
SNAPSHOT [new file with mode: 0644]
THANKS [new file with mode: 0644]
ath/Makefile [new file with mode: 0644]
ath/Makefile.kernel [new file with mode: 0644]
ath/if_ath.c [new file with mode: 0644]
ath/if_ath_ahb.c [new file with mode: 0644]
ath/if_ath_ahb.h [new file with mode: 0644]
ath/if_ath_pci.c [new file with mode: 0644]
ath/if_ath_pci.h [new file with mode: 0644]
ath/if_athioctl.h [new file with mode: 0644]
ath/if_athvar.h [new file with mode: 0644]
ath_hal/Makefile [new file with mode: 0644]
ath_hal/Makefile.kernel [new file with mode: 0644]
ath_hal/ah_os.c [new file with mode: 0644]
ath_hal/ah_os.h [new file with mode: 0644]
ath_hal/ah_osdep.h [new file with mode: 0644]
ath_hal/ah_target.inc [new file with mode: 0644]
ath_hal/opt_ah.h [new file with mode: 0644]
ath_hal/uudecode.c [new file with mode: 0644]
ath_rate/Makefile [new file with mode: 0644]
ath_rate/amrr/Makefile [new file with mode: 0644]
ath_rate/amrr/Makefile.kernel [new file with mode: 0644]
ath_rate/amrr/amrr.c [new file with mode: 0644]
ath_rate/amrr/amrr.h [new file with mode: 0644]
ath_rate/minstrel/Makefile [new file with mode: 0644]
ath_rate/minstrel/Makefile.kernel [new file with mode: 0644]
ath_rate/minstrel/minstrel.c [new file with mode: 0644]
ath_rate/minstrel/minstrel.h [new file with mode: 0644]
ath_rate/minstrel/minstrel.txt [new file with mode: 0644]
ath_rate/onoe/Makefile [new file with mode: 0644]
ath_rate/onoe/Makefile.kernel [new file with mode: 0644]
ath_rate/onoe/onoe.c [new file with mode: 0644]
ath_rate/onoe/onoe.h [new file with mode: 0644]
ath_rate/sample/Makefile [new file with mode: 0644]
ath_rate/sample/Makefile.kernel [new file with mode: 0644]
ath_rate/sample/sample.c [new file with mode: 0644]
ath_rate/sample/sample.h [new file with mode: 0644]
contrib/madwifi.spec [new file with mode: 0644]
contrib/madwifi.spec.in [new file with mode: 0644]
hal/COPYRIGHT [new file with mode: 0644]
hal/README [new file with mode: 0644]
hal/ah.h [new file with mode: 0644]
hal/ah_desc.h [new file with mode: 0644]
hal/ah_devid.h [new file with mode: 0644]
hal/ah_soc.h [new file with mode: 0644]
hal/public/alpha-elf.hal.o.uu [new file with mode: 0644]
hal/public/alpha-elf.inc [new file with mode: 0644]
hal/public/alpha-elf.opt_ah.h [new file with mode: 0644]
hal/public/ap30.hal.o.uu [new file with mode: 0644]
hal/public/ap30.inc [new file with mode: 0644]
hal/public/ap30.opt_ah.h [new file with mode: 0644]
hal/public/ap43.hal.o.uu [new file with mode: 0644]
hal/public/ap43.inc [new file with mode: 0644]
hal/public/ap43.opt_ah.h [new file with mode: 0644]
hal/public/ap51.hal.o.uu [new file with mode: 0644]
hal/public/ap51.inc [new file with mode: 0644]
hal/public/ap51.opt_ah.h [new file with mode: 0644]
hal/public/ap61.hal.o.uu [new file with mode: 0644]
hal/public/ap61.inc [new file with mode: 0644]
hal/public/ap61.opt_ah.h [new file with mode: 0644]
hal/public/arm9-le-thumb-elf.hal.o.uu [new file with mode: 0644]
hal/public/arm9-le-thumb-elf.inc [new file with mode: 0644]
hal/public/arm9-le-thumb-elf.opt_ah.h [new file with mode: 0644]
hal/public/armv4-be-elf.hal.o.uu [new file with mode: 0644]
hal/public/armv4-be-elf.inc [new file with mode: 0644]
hal/public/armv4-be-elf.opt_ah.h [new file with mode: 0644]
hal/public/armv4-le-elf.hal.o.uu [new file with mode: 0644]
hal/public/armv4-le-elf.inc [new file with mode: 0644]
hal/public/armv4-le-elf.opt_ah.h [new file with mode: 0644]
hal/public/i386-elf.hal.o.uu [new file with mode: 0644]
hal/public/i386-elf.inc [new file with mode: 0644]
hal/public/i386-elf.opt_ah.h [new file with mode: 0644]
hal/public/mips-be-elf.hal.o.uu [new file with mode: 0644]
hal/public/mips-be-elf.inc [new file with mode: 0644]
hal/public/mips-be-elf.opt_ah.h [new file with mode: 0644]
hal/public/mips-le-elf.hal.o.uu [new file with mode: 0644]
hal/public/mips-le-elf.inc [new file with mode: 0644]
hal/public/mips-le-elf.opt_ah.h [new file with mode: 0644]
hal/public/mips1-be-elf.hal.o.uu [new file with mode: 0644]
hal/public/mips1-be-elf.inc [new file with mode: 0644]
hal/public/mips1-be-elf.opt_ah.h [new file with mode: 0644]
hal/public/mips1-le-elf.hal.o.uu [new file with mode: 0644]
hal/public/mips1-le-elf.inc [new file with mode: 0644]
hal/public/mips1-le-elf.opt_ah.h [new file with mode: 0644]
hal/public/mipsisa32-be-elf.hal.o.uu [new file with mode: 0644]
hal/public/mipsisa32-be-elf.inc [new file with mode: 0644]
hal/public/mipsisa32-be-elf.opt_ah.h [new file with mode: 0644]
hal/public/mipsisa32-le-elf.hal.o.uu [new file with mode: 0644]
hal/public/mipsisa32-le-elf.inc [new file with mode: 0644]
hal/public/mipsisa32-le-elf.opt_ah.h [new file with mode: 0644]
hal/public/powerpc-be-eabi.hal.o.uu [new file with mode: 0644]
hal/public/powerpc-be-eabi.inc [new file with mode: 0644]
hal/public/powerpc-be-eabi.opt_ah.h [new file with mode: 0644]
hal/public/powerpc-be-elf.hal.o.uu [new file with mode: 0644]
hal/public/powerpc-be-elf.inc [new file with mode: 0644]
hal/public/powerpc-be-elf.opt_ah.h [new file with mode: 0644]
hal/public/powerpc-le-eabi.hal.o.uu [new file with mode: 0644]
hal/public/powerpc-le-eabi.inc [new file with mode: 0644]
hal/public/powerpc-le-eabi.opt_ah.h [new file with mode: 0644]
hal/public/sh4-le-elf.hal.o.uu [new file with mode: 0644]
hal/public/sh4-le-elf.inc [new file with mode: 0644]
hal/public/sh4-le-elf.opt_ah.h [new file with mode: 0644]
hal/public/sparc-be-elf.hal.o.uu [new file with mode: 0644]
hal/public/sparc-be-elf.inc [new file with mode: 0644]
hal/public/sparc-be-elf.opt_ah.h [new file with mode: 0644]
hal/public/sparc64-be-elf.hal.o.uu [new file with mode: 0644]
hal/public/sparc64-be-elf.inc [new file with mode: 0644]
hal/public/sparc64-be-elf.opt_ah.h [new file with mode: 0644]
hal/public/wackelf.c [new file with mode: 0644]
hal/public/x86_64-elf.hal.o.uu [new file with mode: 0644]
hal/public/x86_64-elf.inc [new file with mode: 0644]
hal/public/x86_64-elf.opt_ah.h [new file with mode: 0644]
hal/public/xscale-be-elf.hal.o.uu [new file with mode: 0644]
hal/public/xscale-be-elf.inc [new file with mode: 0644]
hal/public/xscale-be-elf.opt_ah.h [new file with mode: 0644]
hal/public/xscale-le-elf.hal.o.uu [new file with mode: 0644]
hal/public/xscale-le-elf.inc [new file with mode: 0644]
hal/public/xscale-le-elf.opt_ah.h [new file with mode: 0644]
hal/version.h [new file with mode: 0644]
include/compat.h [new file with mode: 0644]
include/sys/queue.h [new file with mode: 0644]
kernelversion.c [new file with mode: 0644]
net80211/Makefile [new file with mode: 0644]
net80211/Makefile.kernel [new file with mode: 0644]
net80211/_ieee80211.h [new file with mode: 0644]
net80211/ieee80211.c [new file with mode: 0644]
net80211/ieee80211.h [new file with mode: 0644]
net80211/ieee80211_acl.c [new file with mode: 0644]
net80211/ieee80211_beacon.c [new file with mode: 0644]
net80211/ieee80211_crypto.c [new file with mode: 0644]
net80211/ieee80211_crypto.h [new file with mode: 0644]
net80211/ieee80211_crypto_ccmp.c [new file with mode: 0644]
net80211/ieee80211_crypto_none.c [new file with mode: 0644]
net80211/ieee80211_crypto_tkip.c [new file with mode: 0644]
net80211/ieee80211_crypto_wep.c [new file with mode: 0644]
net80211/ieee80211_input.c [new file with mode: 0644]
net80211/ieee80211_ioctl.h [new file with mode: 0644]
net80211/ieee80211_linux.c [new file with mode: 0644]
net80211/ieee80211_linux.h [new file with mode: 0644]
net80211/ieee80211_monitor.c [new file with mode: 0644]
net80211/ieee80211_monitor.h [new file with mode: 0644]
net80211/ieee80211_node.c [new file with mode: 0644]
net80211/ieee80211_node.h [new file with mode: 0644]
net80211/ieee80211_output.c [new file with mode: 0644]
net80211/ieee80211_power.c [new file with mode: 0644]
net80211/ieee80211_power.h [new file with mode: 0644]
net80211/ieee80211_proto.c [new file with mode: 0644]
net80211/ieee80211_proto.h [new file with mode: 0644]
net80211/ieee80211_radiotap.h [new file with mode: 0644]
net80211/ieee80211_rate.c [new file with mode: 0644]
net80211/ieee80211_rate.h [new file with mode: 0644]
net80211/ieee80211_scan.c [new file with mode: 0644]
net80211/ieee80211_scan.h [new file with mode: 0644]
net80211/ieee80211_scan_ap.c [new file with mode: 0644]
net80211/ieee80211_scan_sta.c [new file with mode: 0644]
net80211/ieee80211_var.h [new file with mode: 0644]
net80211/ieee80211_wireless.c [new file with mode: 0644]
net80211/ieee80211_xauth.c [new file with mode: 0644]
net80211/if_athproto.h [new file with mode: 0644]
net80211/if_ethersubr.h [new file with mode: 0644]
net80211/if_llc.h [new file with mode: 0644]
net80211/if_media.c [new file with mode: 0644]
net80211/if_media.h [new file with mode: 0644]
patches/2.4/Config.in [new file with mode: 0644]
patches/2.4/Configure.help.patch [new file with mode: 0644]
patches/2.4/Makefile [new file with mode: 0644]
patches/2.6/Kconfig [new file with mode: 0644]
patches/2.6/Makefile [new file with mode: 0644]
patches/README [new file with mode: 0644]
patches/install.sh [new file with mode: 0755]
regression/Makefile [new file with mode: 0644]
regression/ccmp/Makefile [new file with mode: 0644]
regression/ccmp/test_ccmp.c [new file with mode: 0644]
regression/tkip/Makefile [new file with mode: 0644]
regression/tkip/test_tkip.c [new file with mode: 0644]
regression/wep/Makefile [new file with mode: 0644]
regression/wep/test_wep.c [new file with mode: 0644]
release.h [new file with mode: 0644]
scripts/find-madwifi-modules.sh [new file with mode: 0755]
scripts/get_arch.mk [new file with mode: 0644]
scripts/hal_unmangle.objcopy [new file with mode: 0644]
scripts/madwifi-unload [new file with mode: 0755]
scripts/make-release.bash [new file with mode: 0755]
tools/80211debug.c [new file with mode: 0644]
tools/80211stats.c [new file with mode: 0644]
tools/Makefile [new file with mode: 0644]
tools/athchans.c [new file with mode: 0644]
tools/athctrl.c [new file with mode: 0644]
tools/athdebug.c [new file with mode: 0644]
tools/athkey.c [new file with mode: 0644]
tools/athstats.c [new file with mode: 0644]
tools/man/80211debug.8 [new file with mode: 0644]
tools/man/80211stats.8 [new file with mode: 0644]
tools/man/athchans.8 [new file with mode: 0644]
tools/man/athctrl.8 [new file with mode: 0644]
tools/man/athdebug.8 [new file with mode: 0644]
tools/man/athkey.8 [new file with mode: 0644]
tools/man/athstats.8 [new file with mode: 0644]
tools/man/wlanconfig.8 [new file with mode: 0644]
tools/wireless_copy.h [new file with mode: 0644]
tools/wlanconfig.c [new file with mode: 0644]

diff --git a/BuildCaps.inc b/BuildCaps.inc
new file mode 100644 (file)
index 0000000..976e15c
--- /dev/null
@@ -0,0 +1,53 @@
+#
+# defaults
+#
+ifndef ATH_CAP_SUPERG_FF
+export ATH_CAP_SUPERG_FF=1
+endif
+
+ifndef ATH_CAP_DYNTURBO
+export ATH_CAP_DYNTURBO=1
+endif
+
+ifndef ATH_CAP_SUPERG_COMP
+export ATH_CAP_SUPERG_COMP=0
+endif
+
+ifndef ATH_CAP_XR
+export ATH_CAP_XR=1
+endif
+
+ifndef ATH_CAP_TPC
+export ATH_CAP_TPC=0
+endif
+
+ifndef ATH_CAP_TX99
+export ATH_CAP_TX99=0
+endif
+
+#
+# directives
+#
+ifneq ($(strip $(ATH_CAP_SUPERG_FF)),0)
+COPTS+= -DATH_SUPERG_FF=1
+endif
+
+ifneq ($(strip $(ATH_CAP_DYNTURBO)),0)
+COPTS+= -DATH_SUPERG_DYNTURBO=1 -DATH_TURBO_SCAN=1
+endif
+
+ifneq ($(strip $(ATH_CAP_SUPERG_COMP)),0)
+COPTS+= -DATH_SUPERG_COMP=1
+endif
+
+ifneq ($(strip $(ATH_CAP_XR)),0)
+COPTS+= -DATH_SUPERG_XR=1
+endif
+
+ifneq ($(strip $(ATH_CAP_TPC)),0)
+COPTS+= -DATH_CAP_TPC=1
+endif
+
+ifneq ($(strip $(ATH_CAP_TX99)),0)
+COPTS+= -DATH_TX99_DIAG=1
+endif
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644 (file)
index 0000000..0aa94e9
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,40 @@
+All files contained in this distribution are covered by the following
+copyright unless explicitly identified otherwise.
+
+/*-
+ * 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: COPYRIGHT,v 1.4 2004/01/13 18:05:41 samleffler Exp $
+ */
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..cdb7f25
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,202 @@
+MADWIFI: Multimode Atheros Driver for WiFi on Linux
+===================================================
+
+* Copyright (c) 2002-2005 Sam Leffler.  All rights reserved.
+
+Read the file COPYRIGHT for the complete copyright.
+
+
+Requirements
+------------
+
+- Configured kernel sources of the target kernel.  Some Linux
+  distributions provide headers, makefiles and configuration data - it
+  should suffice.
+
+- Wireless Extensions support (14 or later, 17 preferred) - option
+  CONFIG_NET_RADIO in kernel .config file.
+
+- Sysctl support - option CONFIG_SYSCTL in kernel .config file.
+
+- Crypto API support - option CONFIG_CRYPTO in kernel .config file (AES
+  support is used if present, otherwise the AES-CCMP cipher module falls
+  back to a private implementation).
+
+- gcc of same version that was used to compile the kernel.  At least
+  make sure that the first two version numbers or the compiler are the
+  same (e.g. it's OK to use gcc 3.4.6 to compile MadWifi if the kernel
+  was compiled by gcc 3.4.2).  Ignoring this rule will cause "Invalid
+  module format" errors during module load.
+
+Linux 2.4.x kernels starting with 2.4.22 and 2.6 kernels should work
+without problems.  Due to quick pace of Linux development, there is no
+way compatibility with the future 2.6 kernels can be ensured.  However,
+the latest 2.6 kernel at the time of the release should be expected to
+work.
+
+Automatic module loading support (CONFIG_KMOD) is recommended; otherwise, 
+care will have to be taken to manually load needed modules.
+
+Building the driver
+-------------------
+
+The driver is built using the Linux kernel build mechanism.  This means
+you must have some part of the kernel source distribution installed on
+the machine where you want to build the driver.  In particular, the
+kernel include files, makefiles, build scripts and configuration must be
+available.
+
+This will be present if you built your kernel from source.  Otherwise
+you may need to install an additional kernel development package from
+your distribution that would match your kernel.  For example, the
+development package for the default kernel is called linux-headers on
+Debian and kernel-devel on Fedora Core.  Installing a package with full
+kernel sources should not be generally necessary.
+
+Note: in the following examples "$" stands for your system prompt;
+you're not expected to type that as part of the actual command.  "#"
+stands for the command prompt when the commands must be executed by
+root.
+
+Most people can just type:
+
+  $ make
+
+in the top-level MadWifi source directory to build all the modules for
+the currently running system.
+
+You MUST do a "make clean" before compiling for a different version of
+Linux, e.g. building for 2.6 after building for 2.4.
+
+If you want to compile MadWifi for a different kernel, you need to
+specify the location of the kernel build tree, e.g.:
+
+  $ make KERNELPATH=/usr/src/linux-2.6.3
+
+Note that you can also specify this path by setting an environment
+variable; e.g.
+
+  $ export KERNELPATH=/usr/src/linux-2.6.3
+  $ make
+
+If the kernel was built outside the source directory, KERNELPATH should
+point to the output directory where .config is located, not to the
+sources.
+
+MadWifi currently provides four different rate control algorithms,
+ONOE, AMRR, SAMPLE and MINSTREL.  SAMPLE and MINSTREL are both very
+advanced, but MINSTREL is quite new.  Consequently, SAMPLE is used by
+default.  In order to make MadWifi use e.g. AMRR instead, you have to
+specify that as the module parameter e.g.
+
+  # modprobe ath_pci ratectl=amrr
+
+NOTE: Changing the rate control is only required (and recommended) for
+      users who want to setup an access point using MadWifi in difficult
+      (e.g. lossy) environments and who know what they are doing.
+
+This distribution includes support for a variety of target platforms.
+Because of the binary nature of the HAL not all platforms are supported
+(the list grows as time permits).  The supported target platforms can be
+found with:
+
+  $ ls hal/public/*.inc
+
+A target specifies the CPU architecture, byte order (unless implied by
+the CPU), and the ABI/file format.  For most popular platforms, the
+build system will find the appropriate files.  When cross-compiling or
+compiling for less common platforms, the target platform may need to be
+specified using the TARGET variable, e.g:
+
+  $ make TARGET=armv4-le-elf
+
+Consult the contents of the .inc file to find out what the target
+platform is and what toolchain was used to build the HAL object module. 
+Beware of mixing toolchains; some target platforms require that the HAL
+and driver be built with the same toolchain (i.e. compiler, assembler,
+and linker) and the same compiler flags.  If you get warnings about
+incompatible compiler flags, chances are that you are compiling for a
+wrong target or using an incompatible compiler.
+
+
+Cross-compiling
+---------------
+
+The build system is designed to support cross-compiling without any
+modification to the distribution files.  It should be sufficient to
+specify any parameters on the make command line.
+
+In most cases, only KERNELPATH and CROSS_COMPILE need to be defined. 
+CROSS_COMPILE is the prefix for cross-compiling tools.  For instance, if
+the cross compiler is called arm-linux-gcc, set CROSS_COMPILE to
+"arm-linux-":
+
+  $ make KERNELPATH=/usr/src/linux-arm CROSS_COMPILE=arm-linux-
+
+The build system determines ARCH and TARGET based on the .config file in
+the Linux build tree.  TARGET still may need to be provided on the
+command line some uncommon systems.  If ARCH is determined incorrectly,
+please report it.
+
+
+Loading the modules
+-------------------
+
+Building the software will generate numerous loadable modules:
+
+  ath_pci              Atheros driver for PCI/Cardbus devices
+  ath_hal              Atheros HAL
+  wlan                 802.11 support layer
+  wlan_wep             WEP cipher support
+  wlan_tkip            TKIP cipher support
+  wlan_ccmp            AES-CCMP cipher support
+  wlan_xauth           external authenticator
+  wlan_acl             MAC ACL support for AP operation
+  wlan_scan_ap         AP scanning support
+  wlan_scan_sta                station scanning support
+  ath_rate_onoe                ONOE rate control
+  ath_rate_amrr                AMRR rate control
+  ath_rate_sample      SAMPLE rate control
+
+The ath_pci module must be loaded either manually or by the system, e.g.
+through the hotplug or card manager support.  The remaining modules are
+loaded automatically as needed, so after doing a "make install" you only
+need to run following:
+
+  # modprobe ath_pci
+
+For automatic module loading you may need to modify your system's
+configuration files so the necessary modules are loaded when an Atheros
+device is recognized.  The exact procedure varies from system to system.
+
+There are module parameters available to fit your needs, e.g. you can
+set the countrycode manually if your card's EEPROM does not contain the
+correct one for your location.  See
+http://www.unicode.org/onlinedat/countries.html to find your code.
+
+To activate German frequencies you would specify:
+
+  # modprobe ath_pci countrycode=276
+
+To see all available module parameters type:
+
+  $ modinfo ath_pci
+
+
+Integrating into the kernel sources
+-----------------------------------
+
+It is also possible to patch Linux kernel sources to integrate MadWifi
+directly into the kernel tree.  This allows building MadWifi as part of
+the kernel.  This could be useful for embedded systems that don't
+support loadable modules.  Please refer to patches/README for details.
+
+
+Further information
+-------------------
+
+Further information on how to work with the driver can be found in the
+file README.  In addition, the project's wiki has a lot of valuable
+information:
+
+http://madwifi-project.org/
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..6d0bcfd
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,186 @@
+#
+# 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: //depot/sw/linuxsrc/src/802_11/madwifi/madwifi/Makefile $
+#
+
+#
+# Makefile for the HAL-based Atheros driver.
+#
+
+obj := $(firstword $(obj) $(SUBDIRS) .)
+TOP = $(obj)
+
+ifneq (svnversion.h,$(MAKECMDGOALS))
+include $(TOP)/Makefile.inc
+endif
+
+obj-y := ath/ ath_hal/ ath_rate/ net80211/
+
+ifdef CRYPTO_TESTING
+obj-y += regression/
+endif
+
+all: modules tools
+
+modules: configcheck svnversion.h
+ifdef LINUX24
+       for i in $(obj-y); do \
+               $(MAKE) -C $$i || exit 1; \
+       done
+else
+       $(MAKE) -C $(KERNELPATH) SUBDIRS=$(shell pwd) modules
+endif
+
+.PHONY: svnversion.h
+svnversion.h:
+       @if [ -d .svn ]; then \
+               ver=`svnversion -nc . | sed -e 's/^[^:]*://;s/[A-Za-z]//'`; \
+               echo "#define SVNVERSION \"svn r$$ver\"" > $@.tmp; \
+       elif [ -s SNAPSHOT ]; then \
+               ver=`sed -e '/^Revision: */!d;s///;q' SNAPSHOT`; \
+               echo "#define SVNVERSION \"svn r$$ver\"" > $@.tmp; \
+       else \
+               touch $@.tmp; \
+       fi; \
+       diff $@ $@.tmp >/dev/null 2>&1 || cp -f $@.tmp $@; rm -f $@.tmp
+
+# conflicts with the 'tools' subdirectory
+.PHONY: tools
+tools:
+       $(MAKE) -C $(TOOLS) all || exit 1
+
+install: install-modules install-tools
+
+install-modules:
+       @# check if there are modules left from an old installation
+       @# might cause make to abort the build
+       sh scripts/find-madwifi-modules.sh -r $(KERNELRELEASE) $(DESTDIR)
+
+       for i in $(obj-y); do \
+               $(MAKE) -C $$i install || exit 1; \
+       done
+ifeq ($(DESTDIR),)
+       (export KMODPATH=$(KMODPATH); /sbin/depmod -ae $(KERNELRELEASE))
+endif
+
+install-tools:
+       $(MAKE) -C $(TOOLS) install || exit 1
+
+uninstall:
+       sh scripts/find-madwifi-modules.sh $(KERNELRELEASE) $(DESTDIR)
+       $(MAKE) -C $(TOOLS) uninstall
+
+clean:
+       for i in $(obj-y); do \
+               $(MAKE) -C $$i clean; \
+       done
+       -$(MAKE) -C $(TOOLS) clean
+       rm -rf .tmp_versions
+       rm -f *.symvers svnversion.h
+
+info:
+       @echo "The following settings will be used for compilation:"
+       @echo "TARGET       : $(TARGET)"
+       @echo "ARCH         : $(ARCH)"
+       @echo "BUS          : $(BUS)"
+       @echo "KERNELRELEASE: $(KERNELRELEASE)"
+       @echo "KERNELPATH   : $(KERNELPATH)"
+       @echo "KERNELCONF   : $(KERNELCONF)"
+       @echo "KMODPATH     : $(KMODPATH)"
+       @echo "KMODSUF      : $(KMODSUF)"
+
+sanitycheck:
+       @echo -n "Checking requirements... "
+       
+       @# check if specified rate control is available
+       @if [ ! -d $(ATH_RATE) ]; then \
+           echo "FAILED"; \
+           echo "Selected rate control $(ATH_RATE) not available."; \
+           exit 1; \
+       fi
+       
+       @echo "ok."
+
+.PHONY: release
+release:
+       sh scripts/make-release.bash
+
+.PHONY: unload
+unload:
+       bash scripts/madwifi-unload
+
+configcheck: sanitycheck
+       @echo -n "Checking kernel configuration... "
+       
+       @# check version of kernel
+       @echo $(KERNELRELEASE) | grep -q -i '^[2-9]\.[4-9]\.' || { \
+           echo "FAILED"; \
+           echo "Only kernel versions 2.4.x and above are supported."; \
+           echo "You have $(KERNELRELEASE)."; \
+           exit 1; \
+       }
+       
+       @# check kernel configuration
+       @if [ -z "$(CONFIG_SYSCTL)" ]; then \
+           echo "FAILED"; \
+           echo "Please enable sysctl support."; \
+           exit 1; \
+       fi
+       
+ifeq ($(strip $(BUS)),PCI)
+       @# check PCI support
+       @if [ -z "$(CONFIG_PCI)" ]; then \
+           echo "FAILED"; \
+           echo "Please enable PCI support."; \
+           exit 1; \
+       fi
+endif
+       
+       @# check wireless extensions support is enabled
+       @if [ -z "$(CONFIG_NET_RADIO)$(CONFIG_WIRELESS_EXT)" ]; then \
+           echo "FAILED"; \
+           echo "Please enable wireless extensions."; \
+           exit 1; \
+       fi
+       
+       @# check crypto support is enabled
+       @if [ -z "$(CONFIG_CRYPTO)" ]; then \
+           echo "FAILED"; \
+           echo "Please enable crypto API."; \
+           exit 1; \
+       fi
+       
+       @echo "ok."
diff --git a/Makefile.inc b/Makefile.inc
new file mode 100644 (file)
index 0000000..15a5e9b
--- /dev/null
@@ -0,0 +1,204 @@
+#
+# 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: //depot/sw/linuxsrc/src/802_11/madwifi/madwifi/Makefile.inc#11 $
+#
+
+# other locales sometimes cause trouble
+export LC_ALL = POSIX
+
+# BUS defines the bus type to which the wireless devices is attached.
+# Currently, the valid BUS types are PCI and AHB.  If BUS is not
+# defined, then, we assume the bus type is PCI
+ifndef BUS
+BUS=           PCI
+endif
+
+# DESTDIR is used as path prefix during installation.
+DESTDIR ?=
+
+# KERNELPATH is the path to the Linux kernel build tree.  Unless a
+# separate build directory was used for the kernel build, it's the same
+# as the kernel source tree.  KERNELPATH is used to access the kernel
+# configuration, include files and the build system.  To build for
+# another kernel, set KERNELPATH manually, for example with:
+# make KERNELPATH=/path/to/kernel/source
+
+# The default KERNELPATH points to the directory where the currently
+# running kernel was compiled.  Note that the configuration and the
+# version of the kernel tree might have changed since then.
+ifeq ($(wildcard $(KERNELPATH)),)
+KERNELPATH = /lib/modules/$(shell uname -r)/build
+# sanity check: does KERNELPATH exist?
+ifeq ($(shell cd $(KERNELPATH) && pwd),)
+$(error $(KERNELPATH) is missing, please set KERNELPATH)
+endif
+export KERNELPATH
+endif
+
+# KERNELRELEASE is the target kernel's version.  It's always taken from
+# the kernel build tree.  Kernel Makefile doesn't always know the exact
+# kernel version (especially for vendor stock kernels), so we get it
+# from <linux/version.h> instead. But simply grepping it from version.h
+# doesn't work, since some distributions have multiple UTS_RELEASE
+# in that file.
+# This trick has been inspired by the lm_sensors project.
+ifndef KERNELRELEASE
+KERNELRELEASE := $(shell $(CC) -I $(KERNELPATH)/include -E $(TOP)/kernelversion.c | grep uts_release | cut -f2 -d'"')
+ifeq (,$(KERNELRELEASE))
+$(error Cannot detect kernel version - please check compiler and KERNELPATH)
+endif
+endif
+
+# KERNELCONF is the name of the file that holds the configuration
+# of the target kernel.
+KERNELCONF ?=  $(KERNELPATH)/.config
+
+# sanity check: does KERNELCONF exist?
+ifeq ($(wildcard $(KERNELCONF)),)
+$(error KERNELCONF: $(KERNELCONF) does not exist.)
+endif
+include $(KERNELCONF)
+
+# Determine architecture of the kernel.
+include $(TOP)/scripts/get_arch.mk
+export ARCH
+
+# Determine TARGET
+include $(TOP)/ath_hal/ah_target.inc
+export TARGET
+
+# KMODPATH nominates the directory where the modules will be
+# installed to
+KMODPATH :=    /lib/modules/$(KERNELRELEASE)/net
+
+# Recognize 2.4.x kernels to support the old build system
+ifeq ($(filter-out 2.4%,$(KERNELRELEASE)),)
+LINUX24 = y
+endif
+
+ifndef LINUX24
+KMODSUF                := ko
+else
+export-objs    = $(foreach m, $(obj-m), $($(m:.o=-objs)))
+list-multi     = $(obj-m)
+KMODSUF                := o
+endif
+
+NM=            nm
+
+#
+# Path to the HAL source code.
+#
+ifeq ($(HAL),)
+HAL=   $(TOP)/hal
+endif
+#
+# Path to HAL/OS interface code
+#
+ATH_HAL= $(TOP)/ath_hal
+#
+# Path to the 802.11 include files.
+#
+WLAN=  $(TOP)/net80211
+#
+# Path to the Atheros device driver.
+#
+ATH=   $(TOP)/ath
+#
+# Path to the rate control algorithms.
+#
+ATH_RATE=      $(TOP)/ath_rate
+#
+# Path to the userspace utilities. 
+# 
+TOOLS=  $(TOP)/tools 
+
+WARNINGS = -Werror -Wall
+COPTS+= $(WARNINGS)
+INCS=  -include $(TOP)/include/compat.h -I$(TOP)/include
+
+# TARGET defines the target platform architecture. It must match one of
+# the target platforms supported by the HAL. The default target is the
+# host machine architecture. You can override TARGET on the make command
+# line or in the environment. See hal/linux/*.inc for the list of
+# supported targets.
+ifeq (,$(wildcard $(HAL)/public/$(TARGET).inc))
+TARGETS=$(basename $(notdir $(wildcard $(HAL)/public/*.inc)))
+$(error TARGET $(TARGET) is invalid, valid targets are: $(TARGETS))
+endif
+
+# Don't let HAL mess with the toolchain - save toolchain variables
+save_CC := $(CC)
+save_LD := $(LD)
+save_STRIP := $(STRIP)
+save_OBJCOPY := $(OBJCOPY)
+save_NM := $(NM)
+
+include $(HAL)/public/$(TARGET).inc
+
+# Restore toolchain variables
+CC := $(save_CC)
+LD := $(save_LD)
+STRIP := $(save_STRIP)
+OBJCOPY := $(save_OBJCOPY)
+NM := $(save_NM)
+
+include $(TOP)/BuildCaps.inc
+
+# Ensure correct endianess
+LDFLAGS += $(LDOPTS)
+
+# Filter out compiler options that are not supported by all compilers
+# and that are not needed to ensure compatible architecture and calling
+# conventions.
+COPTS := $(filter-out -mshort-load-bytes,$(COPTS))
+
+
+# The following variables will affect developers only, and are used in the
+# make-release.bash script.
+#
+# RELEASE_TMP is used as a temporary store for the files needed to create the
+# release tarball. Packaging will take place in TMPDIR/madwifi-release.
+# Defaults to /tmp.
+RELEASE_TMP=/tmp
+
+# RELEASE_STORE is the (local) directory where the release tarball should be
+# stored in. Defaults to the parent directory of the working copy used to
+# create the release.
+RELEASE_STORE=..
+
+# expose these settings to make-release.bash
+export RELEASE_TMP RELEASE_STORE
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..42c55b4
--- /dev/null
+++ b/README
@@ -0,0 +1,377 @@
+MADWIFI: Multimode Atheros Driver for WiFi on Linux
+===================================================
+
+* Copyright (c) 2002-2005 Sam Leffler.  All rights reserved.
+
+Read the file COPYRIGHT for the complete copyright.
+
+
+WARNING: THIS IS A BETA DISTRIBUTION.  THIS SOFTWARE HAS KNOWN PROBLEMS
+WARNING: AND LIMITATIONS THAT WILL BE CORRECTED BEFORE A PRODUCTION
+WARNING: RELEASE.  DON'T BLAME US IF THE SOFTWARE EATS YOUR SYSTEM,
+WARNING: DESTROYS YOUR DISK OR MAKES YOUR CORN-FLAKES SOGGY.
+WARNING: USE AT YOUR OWN RISK!
+
+
+Introduction
+------------
+
+This software contains a Linux kernel driver for Atheros-based Wireless
+LAN devices.  The driver supports station, AP, ad-hoc, and monitor modes
+of operation.  The Atheros driver depends on a device-independent
+implementation of the 802.11 protocols that originated in the BSD
+community (NetBSD in particular).  
+
+The driver functions as a normal network device and uses the Wireless
+Extensions API.  As such normal Linux tools can and should be used with
+it.  Where the wireless extensions are lacking private ioctls have been
+added.
+
+There is only one driver included here; it supports PCI, MiniPCI and
+Cardbus devices - USB devices are currently not supported by this
+driver!  The driver can be built as a module or linked directly into the
+kernel.  Note however that the net80211 layer is device-independent;
+there is no reason it cannot be used with any 802.11 device (in fact
+this is the case on BSD systems).
+
+This software is broken into multiple modules.  The Atheros-specific
+device support is found in the ath_pci module; it should be loaded when
+an Atheros wireless device is recognized.  The ath_pci module requires
+an additional device specific module, ath_hal, which is described more
+below.  In addition the driver requires the wlan module which contains
+the 802.11 state machine, protocol support, and other device-independent
+support needed by any 802.11 device.  This code is derived from work
+that first appeared in NetBSD and then FreeBSD.  The wlan module may
+also force the loading of additional modules for crypto support
+(wlan_wep, wlan_tkip, wlan_ccmp, etc.), for MAC-based ACL support
+(wlan_acl), and for 802.1x authenticator support (wlan_auth,
+wlan_radius).  The latter modules are only used when operating as an AP. 
+The crypto modules are loaded when keys of that type are created.
+
+The ath_hal module contains the Atheros Hardware Access Layer (HAL).
+This code manages much of the chip-specific operation of the driver. 
+The HAL is provided in a binary-only form in order to comply with FCC
+regulations.  In particular, a radio transmitter can only be operated at
+power levels and on frequency channels for which it is approved.  The
+FCC requires that a software-defined radio cannot be configured by the
+user to operate outside the approved power levels and frequency
+channels.  This makes it difficult to open-source code that enforces
+limits on the power levels, frequency channels and other parameters of
+the radio transmitter.  See
+http://ftp.fcc.gov/Bureaus/Engineering_Technology/Orders/2001/fcc01264.pdf
+for the specific FCC regulation.  Because the module is provided in a
+binary-only form it is marked "Proprietary"; this means when you load it
+you will see messages that your system is now "tainted".
+
+A detailed discussion of the pros and cons of this design can be found
+at http://madwifi-project.org/wiki/HAL
+
+If you wish to use this driver on a platform for which an ath_hal module
+is not already provided please contact the author.  Note that this is
+only necessary for new _architectures_; the HAL is not tied to any
+specific version of Linux - in fact the identical HAL binary code is
+used unchanged with other operating systems.
+
+
+Atheros Hardware
+----------------
+
+There are currently three "programming generations" of Atheros 802.11
+wireless devices (some of these have multiple hardware implementations
+but otherwise appear identical to users):
+
+  5210 supports 11a only
+  5211 supports both 11a and 11b
+  5212 supports 11a, 11b, and 11g
+
+These parts have been incorporated in a variety of retail products
+including Cardbus cards from D-Link, Linksys, Netgear, Orinoco, Proxim,
+and 3Com; and mini-pci cards from some of these same vendors.  In
+addition, many laptop vendors use Atheros mini-pci cards for their
+built-in wireless support.
+
+For an up-to-date list of cards based on Atheros parts visit:
+http://customerproducts.atheros.com/customerproducts
+
+A list of products that have been reported to be supported by MadWifi
+can be found here:
+http://madwifi-project.org/wiki/Compatibility
+
+In general, if a device is identified as ``11a only'', it is almost
+certain to contain an Atheros 5210 part in it.  Most retail a+b products
+use the 5211.  Many a+b+g combo products use the 5212 though other
+vendors have started to offer dual-band support.  When in doubt, check
+the PCI vendor ID with a tool like lspci, the Atheros vendor ID is
+0x168c; e.g.
+
+  00:13.0 Ethernet controller: Unknown device 168c:0012 (rev 01)
+
+but beware that some vendors use alternate vendor IDs (e.g 3Com, IBM).
+The file hal/ah_devid.h has a list of known PCI IDs.
+
+
+Building the driver
+-------------------
+
+The procedure to build the driver is described in the file INSTALL.
+
+
+Using the driver
+----------------
+
+The driver should support any Atheros-based Cardbus or PCI device.  This
+version of the driver is managed and controlled by the usual Linux tools
+(ifconfig, iwconfig, iwpriv) plus the wlanconfig tool, which is included
+with the driver in the tools directory and gets installed on your system
+with make install.
+
+First, run "modprobe ath_pci" or the equivalent using "insmod".  When
+the driver is successfully loaded it creates two devices, named "wifi0"
+and "ath0".  The output from iwconfig should look like this:
+
+    lo      no wireless extensions.
+    
+    wifi0   no wireless extensions.
+    
+    ath0    IEEE 802.11b  ESSID:""
+            Mode:Managed  Channel:0  Access Point: Not-Associated
+           Bit Rate:0 kb/s   Tx-Power:50 dBm   Sensitivity=0/3
+           Retry:off   RTS thr:off   Fragment thr:off
+           Power Management:off
+           Link Quality=0/94  Signal level=-95 dBm  Noise level=-95 dBm
+           Rx invalid nwid:0  Rx invalid crypt:0  Rx invalid frag:0
+           Tx excessive retries:0  Invalid misc:0   Missed beacon:0
+
+
+This driver uses wifi%d only as a placeholder for the physical device, 
+and will create one wifi device for each wireless NIC in the system.  
+These wifi devices will reject ifconfig and iwconfig commands.  The wifi
+interface indicates the existence of a physical MadWifi device, but it
+is not of any functional interest other than as the starting point for
+VAP creation via wlanconfig (see Virtual AP section below).
+
+By default, an ath%d Managed mode interface is also created.  This
+device is a "virtual ap" (VAP) of the wifi%d physical device, and is
+configurable by the standard networking tools  - ifconfig, iwconfig,
+iwpriv. 
+
+The autocreation function can be manipulated to create any one of the
+other supported device types automatically by using the autocreate=mode
+option when the ath_pci module is first loaded.  The following example
+will cause ath%d to be in Master mode:
+
+  modprobe ath_pci autocreate=ap
+
+Autocreation can be disabled:
+
+  modprobe ath_pci autocreate=none
+
+Please see the following link for more information:
+http://madwifi-project.org/wiki/UserDocs/autocreate
+
+
+Virtual APs (VAPs) and wlanconfig
+---------------------------------
+
+An interesting feature of MadWifi is Virtual AP (VAP) mode, which allows
+the operation of multiple concurrent (virtual) access points, and
+concurrent interfaces running in both AP and station mode.  To 
+manipulate VAP's, MadWifi comes with a tool called wlanconfig which is
+used to create and destroy VAPS with various different modes.
+
+The following examples assume that the "autocreate=none" option has been
+parsed to the module at load time.  This allows fine control over
+management of VAP's, as the creation of a Managed mode station should be
+delayed until all other required VAP's are first created, as only one
+sta mode VAP can exist per physical device.
+
+To create an access point, use:
+
+  wlanconfig ath0 create wlandev wifi0 wlanmode ap
+
+To create an access point and a station, use:
+
+  wlanconfig ath0 create wlandev wifi0 wlanmode ap
+  wlanconfig ath1 create wlandev wifi0 wlanmode sta nosbeacon
+
+To create APs that share a single MAC address, use the -bssid flag when
+creating the VAPs:
+
+  wlanconfig ath0 create wlandev wifi0 wlanmode ap -bssid
+  wlanconfig ath1 create wlandev wifi0 wlanmode ap -bssid
+
+Finally, to destroy a VAP, issue the command:
+
+  wlanconfig ath0 destroy
+
+For more information about Virtual AP's, please refer to the users-guide
+document distributed with the MadWifi source code.
+
+For more information about wlanconfig, see its manpage, it is installed
+when you run "make install".
+
+
+Operating Mode
+--------------
+
+If you have a multi-mode card, use one of the following commands to lock
+the operating mode to one of 11a, 11b, or 11g:
+
+  iwpriv ath0 mode 1           lock operation to 11a only
+  iwpriv ath0 mode 2           lock operation to 11b only
+  iwpriv ath0 mode 3           lock operation to 11g only
+  iwpriv ath0 mode 0           autoselect from 11a/b/g (default)
+
+
+Debugging
+---------
+
+There are some debugging mechanisms for the curious/masochistic:
+
+ sysctl -w dev.ath.debug=0xXXX    enable console msgs from the driver
+ sysctl -w net.wlan0.debug=0xYYY  enable console msgs from the wlan module
+
+The values specified for 0XXX and 0xYYY are bit masks that enable
+debugging in various parts of each module.  For the wlan module these
+values are found in the file net80211/ieee80211_var.h (search for MSG_). 
+For the ath driver look in ath/if_ath.c (search for ATH_DEBUG).  Beware
+that enabling some debugging msgs can affect the operation of the
+software by slowing it down too much.
+
+A more comfortable way to manipulate the debug settings is to make use
+of athdebug and 80211debug tools.  Call them with the parameter "-h" to
+learn how they are used, or refer to the appropriate man pages.
+
+In addition the programs tools/athstats and tools/80211stats can be very
+useful in understanding what is going on.  In particular, something like
+
+  athstats 1
+
+will give a running display of the most interesting statistics sampled
+every 1 second.  Running athstats without any options will display a
+summary of all non-zero statistics from the time the driver was loaded.
+By default the ath0 device is used; to override this use the -i option.
+
+A wiki page describes common MadWifi debugging methods here:
+http://madwifi-project.org/wiki/DevDocs/AthDebug
+
+
+Security/Crypto Support
+-----------------------
+
+All Atheros devices implement fixed/shared key WEP in hardware.  Newer
+Atheros hardware is capable of much more (e.g. AES, TKIP and Michael).
+When hardware support is not available for a cipher the net80211 layer
+will automatically do the work in software.
+
+WPA/802.11i station operation (aka supplicant) is supported using Jouni
+Malinen's wpa_supplicant program.  This can be obtained from:
+
+http://hostap.epitest.fi/wpa_supplicant/
+
+wpa_supplicant also supports a wide range of 802.1x EAP methods, either
+together with WPA/WPA2 or without; consult the wpa_supplicant
+documentation for an up to date list.
+
+MadWifi supports the use of the Wireless Extensions ioctl's equal to or
+greater than WE18 (linux 2.6.15).  When using wpa_supplicant with a
+recent linux kernel, it is preferred to use the 'wext' driver backend,
+rather than the private MadWifi ioctl's.  This means that '-D wext'
+option should be used with wpa_supplicant when the linux kernel version
+is 2.6.15 or above.
+
+NOTE: the in-kernel authenticator is being replaced; to use it you need
+      to follow the directions in net80211/Makefile.
+
+When operating as an AP, you can use fixed/shared key ciphers and/or
+802.1x authentication.  The authentication mode is specified using
+iwpriv:
+
+  iwpriv ath0 authmode 1               # open authentication
+  iwpriv ath0 authmode 2               # shared key authentication
+  iwpriv ath0 authmode 3               # 802.1x authentication
+
+To use the 802.1x authenticator you must install and configure the
+hostapd program from the same place you got wpa_supplicant from. 
+Consult the hostapd documentation for further information.
+
+
+Live Monitoring and Writing Raw 802.11 Packets
+----------------------------------------------
+
+The driver can be used in a live "monitor" mode, by creating a monitor
+VAP and sending packets to it.  All packets sent to a monitor mode VAP
+will bypass any state machine.
+
+To create a monitor VAP, use:
+
+  wlanconfig ath1 create wlandev wifi0 wlanmode monitor
+  ifconfig ath1 up
+
+Finally, you can choose to receive packets on ath1 in several different
+packet formats:
+
+  echo '801' > /proc/sys/net/ath1/dev_type     # only 802.11 headers
+  echo '802' > /proc/sys/net/ath1/dev_type     # prism2 headers
+  echo '803' > /proc/sys/net/ath1/dev_type     # radiotap headers
+  echo '804' > /proc/sys/net/ath1/dev_type     # atheros descriptors
+
+
+Known Problems
+--------------
+
+[All these problems are to be fixed in future revisions.]
+
+1. Ad-hoc mode is broken; symptoms are intermittent operation.
+
+Other issues might be mentioned in our ticket tracker:
+http://madwifi-project.org/report/1
+
+
+Getting Support
+---------------
+
+User support is provided via the madwifi-users mailing list, which can
+be reached at:
+
+  madwifi-users@lists.sourceforge.net
+
+Contact this mailing list if you need help in getting your installation
+up and running.  We suggest that you subscribe to the list before
+sending your request (see below).
+
+We also offer an IRC channel that might be a better help in urgent
+cases.  Learn more about the different ways to get support by visiting:
+
+  http://madwifi-project.org/wiki/Support
+
+When sending a support request or problem report be sure to include the
+version of the driver and the part identification the driver prints to
+the console when the module is loaded.  For example:
+
+  ath_hal: 0.8.2.0
+  wlan: 0.7.0.0
+  ath_pci: 0.8.2.0
+  PCI: Found IRQ 11 for device 00:13.0
+  ath0: 11a rates: 6Mbps 9Mbps 12Mbps 18Mbps 24Mbps 36Mbps 48Mbps 54Mbps
+  ath0: 11b rates: 1Mbps 2Mbps 5.5Mbps 11Mbps
+  ath0: 802.11 address: 00:05:5d:6f:99:5f
+  ath0: Atheros 5211: mem=0xfebf0000, irq=11
+
+This says the HAL module is version 0.8.2, the wlan module is version
+0.7, the driver is version 0.8.2 and the hardware uses an Atheros 5211
+chip (which supports 11a and 11b modes).
+
+We will try to respond in a timely manner but understand this software
+is provided as-is, without any promise of support.
+
+
+Feedback and Contributions
+--------------------------
+
+Reports about reproducible bugs, feature requests and patches should be
+submitted in the form of a trouble ticket:
+
+  http://madwifi-project.org/wiki/TicketSubmissionGuidelines
+        
+Fixes and enhancements are encouraged. 
diff --git a/SNAPSHOT b/SNAPSHOT
new file mode 100644 (file)
index 0000000..aa6dbfd
--- /dev/null
+++ b/SNAPSHOT
@@ -0,0 +1,8 @@
+Path: madwifi/branches/madwifi-0.9.4
+URL: http://svn.madwifi-project.org/madwifi/branches/madwifi-0.9.4
+Repository Root: http://svn.madwifi-project.org
+Repository UUID: 0192ed92-7a03-0410-a25b-9323aeb14dbd
+Revision: 4100
+Last Changed Author: proski
+Last Changed Rev: 4100
+Last Changed Date: 2009-09-29 01:07:12 +0200 (Tue, 29 Sep 2009)
diff --git a/THANKS b/THANKS
new file mode 100644 (file)
index 0000000..4b1d4b5
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,167 @@
+Acknowledgments
+================
+This work could not have been done without the support of Atheros
+and in particular the efforts of Greg Chesson.  Comments from David
+Miller were helpful in cleaning up some of the roughest edges in
+early versions of the driver.
+
+Many people have contributed fixes and improvements to this software,
+including (in no particular order):
+
+Joerg Albert
+Mathieu Lacage
+Henry Qian
+giova+mwfdev@fagl
+Vivien Chappelier
+Eric Lammerts
+Mark Rakes
+Stephen Walton
+Tong Chia
+Stephane Laroche
+Michael Renzmann
+Dale Whitfield
+Joachim Gleissner
+Georg Lukas
+Paul Stewart
+Alexander Wirtz
+Guy Erb
+Tom Marshall
+D. Stolte
+Kevin Yu
+Kristian Hoffmann
+Nick Moss
+Bindu Therthala
+William S. Kish
+Nick Petroni
+Carl Thompson
+Thorsten von Eicken
+Jouni Malinen
+Satish Balay
+Richard Dawe
+Bruno Randolf
+Sebastian Weitzel
+Rajendra Singh
+Tai-hwa Liang
+Divy Le Ray
+John Bicket
+Takayuki Kaiso
+Owen Stampflee
+Kel Modderman
+Matt Foster
+Patrick Pichon
+Pavel Roskin
+Stijn Tintel
+jonty at electrichamster dot net
+Aaron Dummer
+Dustin McIntire
+Frank Schaeckermann
+Antonio Ospite
+Jon Anderson
+Wang Wenjuan
+Bell Kin
+Ilia Baldine
+David Overton
+Andreas Schultz
+David Fort
+Sven Schnelle
+Rudger van Brenk
+John Byrne
+Jae-don Shin
+Stefan Becker
+Mark Glines
+Matt Brown
+Steffen Pfendtner
+Jick Nan
+Charles Bovy
+Ian M. Rawley
+Beat Meier
+Alexander Warg
+Jiri Fojtasek
+Joshua Wright
+Dan Williams
+Aurelien Jarno
+Daniel Wu
+Paolo Cristiani
+"stiabhan"
+Elias Oltmanns
+Imre Kaloz
+Brian Eaton
+"paolo"
+Daniel J Blueman
+Digger Vermont
+Felix Fietkau
+Colubris Networks
+Matthew W. S. Bell
+Ivan S. Subrov
+Jochem Berndsen
+Nicholas J Kreucher
+Terry Todd
+Dave Platt
+Scott Raynel
+Christian Buennig
+Joe Parks
+Pavel Novak
+Wade Mealing
+Tony Espy
+Joerg Albert
+Oliver Stampfli
+Till Wollenberg
+Mats Hojlund
+Michal Wrobel
+Tim Harvey
+Andrew Lunn
+Chris Hessing
+Zilvinas Valinskas
+Pramod Babu Gummaraj
+Chris Pearson
+Helge Deller
+Pavol Gono
+Laurent Butti
+Julien Tinnes
+Tjalling Hattink
+Jiri Fojtasek
+Brian Braunstein
+Frank Zdarsky
+Andrew Lunn
+Michael Plourde
+Md Sohail Ahmad
+Derek J Smithies
+
+Apologies to anyone whose name was unintentionally left off.
+Please let us know if you think your name should be mentioned here!
+
+
+We also would like to thank the following individuals and companies
+(listed in alphabetic order) who supported us with donations:
+
+* Compex Systems Pte Ltd, Singapore - http://www.compex.com.sg
+  two donations with a total of 7 MiniPCI cards
+
+* Linux-Consulting, USA - http://www.linux-consulting.com
+  madwifi.org domain name, DNS server maintenance
+
+* Rusty Mellinger, USA
+  donation of a MiniPCI card with AR5211
+
+* Netgate, USA - http://www.netgate.com
+  1U server (P3/850, 1GB RAM, 2x200GB HDD)
+
+  A special thanks goes to Will Herrick, who invested a lot of
+  personal time and money to pick up the server and ship it to
+  Germany!
+
+* Suzo, Slovakia - http://www.suzo.sk
+  1 Wistron DCMA-81 MiniPCI card
+  
+* true global communications GmbH, Germany - http://www.tgc.de
+  Free server and bandwidth for madwifi-project.org
+
+* Voyage, Hong Kong - htt://www.voyage.hk
+  1 Senao NMP-8602 Plus MiniPCI card, 2 pigtails and 2 rubber
+  antenna
+
+To learn more about donations to our project visit:
+http://madwifi-project.org/wiki/Donations
+
+If you want to support the project this way, please contact us by
+e-mail at donation@madwifi-project.org.
diff --git a/ath/Makefile b/ath/Makefile
new file mode 100644 (file)
index 0000000..c017850
--- /dev/null
@@ -0,0 +1,78 @@
+#
+# 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: //depot/sw/linuxsrc/src/802_11/madwifi/madwifi/ath/Makefile#12 $
+#
+
+#
+# Makefile for the Atheros WLAN driver.
+#
+
+obj := $(firstword $(obj) $(SUBDIRS) .)
+TOP = $(obj)/..
+
+ifeq ($(strip $(BUS)),AHB)
+BUSNAME=ahb
+COPTS+= -DATH_AHB
+else
+BUSNAME=pci
+COPTS+= -DATH_PCI
+endif
+
+include $(TOP)/Makefile.inc
+
+obj-m          += ath_$(BUSNAME).o
+ath_$(BUSNAME)-objs    := if_ath.o if_ath_$(BUSNAME).o
+
+INCS += -I$(TOP) -I$(ATH_HAL) -I$(HAL) -I$(WLAN)
+
+EXTRA_CFLAGS+= $(INCS) $(COPTS) -DOPT_AH_H=\"$(HAL)/public/$(TARGET).opt_ah.h\"
+
+-include $(TOPDIR)/Rules.make
+
+all:
+       $(MAKE) -C $(KERNELPATH) SUBDIRS=$(shell pwd) modules
+
+install:
+       test -d $(DESTDIR)/$(KMODPATH) || mkdir -p $(DESTDIR)/$(KMODPATH)
+       install -m 0644 ath_$(BUSNAME).$(KMODSUF) $(DESTDIR)/$(KMODPATH)
+
+clean:
+       rm -f *~ *.o *.ko *.mod.c .*.cmd
+       rm -f .depend .version .*.o.flags .*.o.d
+       rm -rf .tmp_versions
+
+ath_$(BUSNAME).o: $(ath_$(BUSNAME)-objs)
+       $(LD) $(LDOPTS) -o ath_$(BUSNAME).$(KMODSUF) -r $(ath_$(BUSNAME)-objs)
diff --git a/ath/Makefile.kernel b/ath/Makefile.kernel
new file mode 100644 (file)
index 0000000..12d6803
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Makefile for the Atheros WLAN driver.
+#
+
+obj ?= .
+src ?= .
+srctree ?= .
+
+TOP = $(srctree)/$(src)/..
+
+include $(TOP)/BuildCaps.inc
+include $(TOP)/ath_hal/ah_target.inc
+
+ATH_HAL=$(TOP)/ath_hal
+HAL=   $(TOP)/hal
+WLAN=  $(TOP)/net80211
+COMPAT=        $(TOP)/include
+
+#
+# Select bus-specific code.  Note that this defaults to PCI.
+#
+ifeq ($(strip $(BUS)),AHB)
+BUSNAME=ahb
+EXTRA_CFLAGS+= -DATH_AHB
+else
+BUSNAME=pci
+EXTRA_CFLAGS+= -DATH_PCI
+endif
+
+INCS = -include $(COMPAT)/compat.h -I$(COMPAT)
+INCS += -I$(TOP) -I$(ATH_HAL) -I$(HAL) -I$(WLAN)
+
+EXTRA_CFLAGS += $(INCS) $(COPTS) -DOPT_AH_H=\"$(HAL)/public/$(TARGET).opt_ah.h\"
+
+ifneq ($(VERSION).$(PATCHLEVEL),2.4)
+obj-$(CONFIG_ATHEROS)  += ath_$(BUSNAME).o
+ath_$(BUSNAME)-objs    := if_ath.o if_ath_$(BUSNAME).o
+endif
+
+ifeq ($(VERSION).$(PATCHLEVEL),2.4)
+obj-$(CONFIG_ATHEROS) := if_ath.o if_ath_$(BUSNAME).o
+
+O_TARGET := ath_$(BUSNAME).o
+include $(TOPDIR)/Rules.make
+
+export-objs    := if_ath_$(BUSNAME).o
+list-multi     := ath_$(BUSNAME).o
+endif
diff --git a/ath/if_ath.c b/ath/if_ath.c
new file mode 100644 (file)
index 0000000..76532aa
--- /dev/null
@@ -0,0 +1,9963 @@
+/*-
+ * 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 4005 2009-04-15 12:04:43Z proski $
+ */
+
+/*
+ * 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 int ath_maxvaps = ATH_MAXVAPS_DEFAULT;  /* set default maximum vaps */
+static char *autocreate = NULL;
+static char *ratectl = DEF_RATE_CTL;
+static int rfkill = -1;
+static int countrycode = -1;
+static int maxvaps = -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(maxvaps, "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(maxvaps, 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(maxvaps, "Maximum VAPs");
+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|minstrel|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_maxvaps-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)
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
+static const struct net_device_ops ath_netdev_ops = {
+       .ndo_open               = ath_init,
+       .ndo_stop               = ath_stop,
+       .ndo_start_xmit         = ath_hardstart,
+       .ndo_tx_timeout         = ath_tx_timeout,
+       .ndo_set_multicast_list = ath_mode_init,
+       .ndo_do_ioctl           = ath_ioctl,
+       .ndo_get_stats          = ath_getstats,
+       .ndo_set_mac_address    = ath_set_mac_address,
+       .ndo_change_mtu         = ath_change_mtu,
+};
+#endif
+
+int
+ath_attach(u_int16_t devid, struct net_device *dev, HAL_BUS_TAG tag)
+{
+       struct ath_softc *sc = netdev_priv(dev);
+       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);
+
+       /* Allocate space for dynamically determined maximum VAP count */
+       sc->sc_bslot = kmalloc(ath_maxvaps * sizeof(struct ieee80211vap), GFP_KERNEL);
+       memset(sc->sc_bslot, 0, ath_maxvaps * sizeof(struct ieee80211vap));
+
+       /*
+        * 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 (maxvaps != -1) {
+               ath_maxvaps = maxvaps;
+               if (ath_maxvaps < ATH_MAXVAPS_MIN)
+                       ath_maxvaps = ATH_MAXVAPS_MIN;
+               if (ath_maxvaps > ATH_MAXVAPS_MAX)
+                       ath_maxvaps = ATH_MAXVAPS_MAX;
+       }
+       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 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
+       dev->open = ath_init;
+       dev->stop = ath_stop;
+       dev->hard_start_xmit = ath_hardstart;
+       dev->tx_timeout = ath_tx_timeout;
+       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;
+#else
+       dev->netdev_ops = &ath_netdev_ops;
+#endif
+       dev->watchdog_timeo = 5 * HZ;
+       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);
+
+       /* Interference Mitigation causes problems with recevive sensitivity
+        * for OFDM rates when we are in non-STA modes. We will turn this
+        * capability off in non-STA VAPs
+        */
+       sc->sc_hasintmit = ath_hal_hasintmit(ah);
+       sc->sc_useintmit = 1;
+
+       /* 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 = netdev_priv(dev);
+       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);
+       kfree(sc->sc_bslot);
+       sc->sc_bslot = NULL;
+
+       ath_dynamic_sysctl_unregister(sc);
+       ATH_LOCK_DESTROY(sc);
+       dev->flags &= ~IFF_RUNNING; /* mark as stopped */
+       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 = netdev_priv(ic->ic_dev);
+       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_maxvaps) {
+               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 = netdev_priv(dev);
+       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_RADIOTAP;
+       if ((flags & IEEE80211_CLONE_BSSID) &&
+           sc->sc_nvaps != 0 && opmode != IEEE80211_M_WDS && sc->sc_hasbmask) {
+               struct ieee80211vap *v;
+               uint64_t id_mask;
+               unsigned int 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_maxvaps; 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_maxvaps; slot++)
+                               if (sc->sc_bslot[slot] == NULL) {
+                                       /*
+                                        * XXX hack, space out slots to better
+                                        * deal with misses
+                                        */
+                                       if (slot + 1 < ath_maxvaps &&
+                                           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 */
+                       if (ath_maxvaps > 4) {
+                               DPRINTF(sc, ATH_DEBUG_BEACON,
+                                       "Staggered beacons are not possible "
+                                       "with maxvaps set to %d.\n",
+                                       ath_maxvaps);
+                               sc->sc_stagbeacons = 0;
+                       } else {
+                               sc->sc_stagbeacons = 1;
+                       }
+               }
+               DPRINTF(sc, ATH_DEBUG_BEACON, "sc->stagbeacons %sabled\n",
+                       (sc->sc_stagbeacons ? "en" : "dis"));
+       }
+       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 = netdev_priv(dev);
+       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 = netdev_priv(dev);
+
+       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 = netdev_priv(dev);
+
+       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 = netdev_priv(dev);
+       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 = netdev_priv(dev);
+
+       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 = netdev_priv(dev);
+       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);
+
+       /* Turn off Interference Mitigation in non-STA modes */
+       if ((sc->sc_opmode != HAL_M_STA) && sc->sc_hasintmit && !sc->sc_useintmit) {
+               DPRINTF(sc, ATH_DEBUG_RESET,
+                       "%s: disabling interference mitigation (ANI)\n", __func__);
+               ath_hal_setintmit(ah, 0);
+       }
+
+       /*
+        * 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 = netdev_priv(dev);
+       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 = netdev_priv(dev);
+       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 = netdev_priv(dev);
+       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);
+
+       /* Turn off Interference Mitigation in non-STA modes */
+       if ((sc->sc_opmode != HAL_M_STA) && sc->sc_hasintmit && !sc->sc_useintmit) {
+               DPRINTF(sc, ATH_DEBUG_RESET,
+                       "%s: disabling interference mitigation (ANI)\n", __func__);
+               ath_hal_setintmit(ah, 0);
+       }
+
+       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 = netdev_priv(dev);
+       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 = netdev_priv(dev);
+       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;
+       struct ieee80211vap *vap;
+       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;
+#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;
+       }
+
+       vap = ni->ni_vap;
+
+#ifdef ATH_SUPERG_FF
+       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 = netdev_priv(dev);
+       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 = netdev_priv(dev);
+
+       /*
+        * 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) {
+               int i;
+               u_int keyix = IEEE80211_KEYIX_NONE;
+
+               for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+                       if (k == &vap->iv_nw_keys[i]) {
+                               keyix = i;
+                               break;
+                       }
+               }
+               if (keyix == IEEE80211_KEYIX_NONE) {
+                       /* should not happen */
+                       DPRINTF(sc, ATH_DEBUG_KEYCACHE,
+                               "%s: bogus group key\n", __func__);
+                       return IEEE80211_KEYIX_NONE;
+               }
+
+               /*
+                * 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 = netdev_priv(dev);
+       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 = netdev_priv(dev);
+
+       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 = netdev_priv(dev);
+
+       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 = netdev_priv(dev);
+
+       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 = netdev_priv(dev);
+       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 = netdev_priv(dev);
+       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 = netdev_priv(ic->ic_dev);
+
+       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 = netdev_priv(ic->ic_dev);
+       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_maxvaps; 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 = netdev_priv(dev);
+       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_maxvaps - avp->av_bslot)) / ath_maxvaps;
+               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",
+               __func__, skb, skb->len);
+
+       /* setup descriptors */
+       ds = bf->bf_desc;
+
+       flags = HAL_TXDESC_NOACK;
+#ifdef ATH_SUPERG_DYNTURBO
+       if (sc->sc_dturbo_switch)
+               flags |= HAL_TXDESC_INTREQ;
+#endif
+
+       if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) {
+               ds->ds_link = bf->bf_daddr;     /* self-linked */
+               flags |= HAL_TXDESC_VEOL;
+               /*
+                * Let hardware handle antenna switching if txantenna is not set
+                */
+       } else {
+               ds->ds_link = 0;
+               /*
+                * Switch antenna every beacon if txantenna is not set
+                * Should only switch every beacon period, not for every
+                * SWBA's
+                * XXX assumes two antenna
+                */
+               if (antenna == 0) {
+                       if (sc->sc_stagbeacons)
+                               antenna = ((sc->sc_stats.ast_be_xmit / sc->sc_nbcnvaps) & 1 ? 2 : 1);
+                       else
+                               antenna = (sc->sc_stats.ast_be_xmit & 1 ? 2 : 1);
+               }
+       }
+
+       ds->ds_data = bf->bf_skbaddr;
+       /*
+        * Calculate rate code.
+        * XXX everything at min xmit rate
+        */
+       rix = sc->sc_minrateix;
+       rt = sc->sc_currates;
+       rate = rt->info[rix].rateCode;
+       if (USE_SHPREAMBLE(ic))
+               rate |= rt->info[rix].shortPreamble;
+#ifdef ATH_SUPERG_XR
+       if (bf->bf_node->ni_vap->iv_flags & IEEE80211_F_XR) {
+               u_int8_t cix;
+               int pktlen;
+               pktlen = skb->len + IEEE80211_CRC_LEN;
+               cix = rt->info[sc->sc_protrix].controlRate;
+               /* for XR VAP use different RTSCTS rates and calculate duration */
+               ctsrate = rt->info[cix].rateCode;
+               if (USE_SHPREAMBLE(ic))
+                       ctsrate |= rt->info[cix].shortPreamble;
+               flags |= HAL_TXDESC_CTSENA;
+               rt = sc->sc_xr_rates;
+               ctsduration = ath_hal_computetxtime(ah,rt, pktlen,
+                       IEEE80211_XR_DEFAULT_RATE_INDEX, AH_FALSE);
+               rate = rt->info[IEEE80211_XR_DEFAULT_RATE_INDEX].rateCode;
+       }
+#endif
+       ath_hal_setuptxdesc(ah, ds
+               , skb->len + IEEE80211_CRC_LEN  /* frame length */
+               , sizeof(struct ieee80211_frame)        /* header length */
+               , HAL_PKT_TYPE_BEACON           /* Atheros packet type */
+               , ni->ni_txpower                /* txpower XXX */
+               , rate, 1                       /* series 0 rate/tries */
+               , HAL_TXKEYIX_INVALID           /* no encryption */
+               , antenna                       /* antenna mode */
+               , flags                         /* no ack, veol for beacons */
+               , ctsrate                       /* rts/cts rate */
+               , ctsduration                   /* rts/cts duration */
+               , 0                             /* comp icv len */
+               , 0                             /* comp iv len */
+               , ATH_COMP_PROC_NO_COMP_NO_CCS  /* comp scheme */
+       );
+
+       /* NB: beacon's BufLen must be a multiple of 4 bytes */
+       ath_hal_filltxdesc(ah, ds
+               , roundup(skb->len, 4)  /* buffer 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);
+#undef USE_SHPREAMBLE
+}
+
+/*
+ * Generate beacon frame and queue cab data for a VAP.
+ */
+static struct ath_buf *
+ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap, int *needmark)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       struct ath_buf *bf;
+       struct ieee80211_node *ni;
+       struct ath_vap *avp;
+       struct sk_buff *skb;
+       int ncabq;
+       unsigned int curlen;
+
+       if (vap->iv_state != IEEE80211_S_RUN) {
+               DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: skip VAP in %s state\n",
+                       __func__, ieee80211_state_name[vap->iv_state]);
+               return NULL;
+       }
+#ifdef ATH_SUPERG_XR
+       if (vap->iv_flags & IEEE80211_F_XR) {
+               vap->iv_xrbcnwait++;
+               /* wait for XR_BEACON_FACTOR times before sending the beacon */
+               if (vap->iv_xrbcnwait < IEEE80211_XR_BEACON_FACTOR)
+                       return NULL;
+               vap->iv_xrbcnwait = 0;
+       }
+#endif
+       avp = ATH_VAP(vap);
+       if (avp->av_bcbuf == NULL) {
+               DPRINTF(sc, ATH_DEBUG_ANY, "%s: avp=%p av_bcbuf=%p\n",
+                        __func__, avp, avp->av_bcbuf);
+               return NULL;
+       }
+       bf = avp->av_bcbuf;
+       ni = bf->bf_node;
+
+#ifdef ATH_SUPERG_DYNTURBO
+       /* 
+        * If we are using dynamic turbo, update the
+        * capability info and arrange for a mode change
+        * if needed.
+        */
+       if (sc->sc_dturbo) {
+               u_int8_t dtim;
+               dtim = ((avp->av_boff.bo_tim[2] == 1) ||
+                       (avp->av_boff.bo_tim[3] == 1));
+               ath_beacon_dturbo_update(vap, needmark, dtim);
+       }
+#endif
+       /*
+        * Update dynamic beacon contents.  If this returns
+        * non-zero then we need to remap the memory because
+        * the beacon frame changed size (probably because
+        * of the TIM bitmap).
+        */
+       skb = bf->bf_skb;
+       curlen = skb->len;
+       ncabq = avp->av_mcastq.axq_depth;
+       if (ieee80211_beacon_update(ni, &avp->av_boff, skb, ncabq)) {
+               bus_unmap_single(sc->sc_bdev,
+                       bf->bf_skbaddr, curlen, BUS_DMA_TODEVICE);
+               bf->bf_skbaddr = bus_map_single(sc->sc_bdev,
+                       skb->data, skb->len, BUS_DMA_TODEVICE);
+       }
+
+       /*
+        * if the CABQ traffic from previous DTIM is pending and the current
+        * beacon is also a DTIM. 
+        *  1) if there is only one VAP let the cab traffic continue. 
+        *  2) if there are more than one VAP and we are using staggered
+        *     beacons, then drain the cabq by dropping all the frames in
+        *     the cabq so that the current VAP's cab traffic can be scheduled.
+        * XXX: Need to handle the last MORE_DATA bit here.
+        */
+       if (ncabq && (avp->av_boff.bo_tim[4] & 1) && sc->sc_cabq->axq_depth) {
+               if (sc->sc_nvaps > 1 && sc->sc_stagbeacons) {
+                       ath_tx_draintxq(sc, sc->sc_cabq);
+                       DPRINTF(sc, ATH_DEBUG_BEACON,
+                               "%s: flush previous cabq traffic\n", __func__);
+               }
+       }
+
+       /*
+        * Construct tx descriptor.
+        */
+       ath_beacon_setup(sc, bf);
+
+       bus_dma_sync_single(sc->sc_bdev,
+               bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE);
+
+       /*
+        * Enable the CAB queue before the beacon queue to
+        * ensure cab frames are triggered by this beacon.
+        */
+       if (avp->av_boff.bo_tim[4] & 1) {       /* NB: only at DTIM */
+               struct ath_txq *cabq = sc->sc_cabq;
+               struct ath_buf *bfmcast;
+               /*
+                * Move everything from the VAP's mcast queue 
+                * to the hardware cab queue.
+                */
+               ATH_TXQ_LOCK(&avp->av_mcastq);
+               ATH_TXQ_LOCK(cabq);
+               bfmcast = STAILQ_FIRST(&avp->av_mcastq.axq_q);
+               /* link the descriptors */
+               if (cabq->axq_link == NULL)
+                       ath_hal_puttxbuf(ah, cabq->axq_qnum, bfmcast->bf_daddr);
+               else {
+#ifdef AH_NEED_DESC_SWAP
+                       *cabq->axq_link = cpu_to_le32(bfmcast->bf_daddr);
+#else
+                       *cabq->axq_link = bfmcast->bf_daddr;
+#endif
+               }
+
+               /* Set the MORE_DATA bit for each packet except the last one */
+               STAILQ_FOREACH(bfmcast, &avp->av_mcastq.axq_q, bf_list) {
+                       if (bfmcast != STAILQ_LAST(&avp->av_mcastq.axq_q, ath_buf, bf_list))
+                               ((struct ieee80211_frame *)bfmcast->bf_skb->data)->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+               }
+
+               /* append the private VAP mcast list to the cabq */
+               ATH_TXQ_MOVE_MCASTQ(&avp->av_mcastq, cabq);
+               /* NB: gated by beacon so safe to start here */
+               ath_hal_txstart(ah, cabq->axq_qnum);
+               ATH_TXQ_UNLOCK(cabq);
+               ATH_TXQ_UNLOCK(&avp->av_mcastq);
+       }
+
+       return bf;
+}
+
+/*
+ * Transmit one or more beacon frames at SWBA.  Dynamic
+ * updates to the frame contents are done as needed and
+ * the slot time is also adjusted based on current state.
+ */
+static void
+ath_beacon_send(struct ath_softc *sc, int *needmark)
+{
+#define        TSF_TO_TU(_h,_l) \
+       ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10))
+       struct ath_hal *ah = sc->sc_ah;
+       struct ieee80211vap *vap;
+       struct ath_buf *bf;
+       int slot;
+       u_int32_t bfaddr;
+
+       /*
+        * Check if the previous beacon has gone out.  If
+        * not don't try to post another, skip this period
+        * and wait for the next.  Missed beacons indicate
+        * a problem and should not occur.  If we miss too
+        * many consecutive beacons reset the device.
+        */
+       if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) {
+               sc->sc_bmisscount++;
+               /* XXX: 802.11h needs the chanchange IE countdown decremented.
+                *      We should consider adding a net80211 call to indicate
+                *      a beacon miss so appropriate action could be taken
+                *      (in that layer).
+                */
+               DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
+                       "%s: missed %u consecutive beacons\n",
+                       __func__, sc->sc_bmisscount);
+               if (sc->sc_bmisscount > BSTUCK_THRESH)
+                       ATH_SCHEDULE_TQUEUE(&sc->sc_bstucktq, needmark);
+               return;
+       }
+       if (sc->sc_bmisscount != 0) {
+               DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
+                       "%s: resume beacon xmit after %u misses\n",
+                       __func__, sc->sc_bmisscount);
+               sc->sc_bmisscount = 0;
+       }
+
+       /*
+        * Generate beacon frames.  If we are sending frames
+        * staggered then calculate the slot for this frame based
+        * on the tsf to safeguard against missing an swba.
+        * Otherwise we are bursting all frames together and need
+        * to generate a frame for each VAP that is up and running.
+        */
+       if (sc->sc_stagbeacons) {               /* staggered beacons */
+               struct ieee80211com *ic = &sc->sc_ic;
+               u_int64_t tsf;
+               u_int32_t tsftu;
+
+               tsf = ath_hal_gettsf64(ah);
+               tsftu = TSF_TO_TU(tsf >> 32, tsf);
+               slot = ((tsftu % ic->ic_lintval) * ath_maxvaps) / ic->ic_lintval;
+               vap = sc->sc_bslot[(slot + 1) % ath_maxvaps];
+               DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
+                       "%s: slot %d [tsf %llu tsftu %u intval %u] vap %p\n",
+                       __func__, slot, (long long) tsf, tsftu, ic->ic_lintval, vap);
+               bfaddr = 0;
+               if (vap != NULL) {
+                       bf = ath_beacon_generate(sc, vap, needmark);
+                       if (bf != NULL)
+                               bfaddr = bf->bf_daddr;
+               }
+       } else {                                /* burst'd beacons */
+               u_int32_t *bflink;
+
+               bflink = &bfaddr;
+               /* XXX rotate/randomize order? */
+               for (slot = 0; slot < ath_maxvaps; slot++) {
+                       vap = sc->sc_bslot[slot];
+                       if (vap != NULL) {
+                               bf = ath_beacon_generate(sc, vap, needmark);
+                               if (bf != NULL) {
+#ifdef AH_NEED_DESC_SWAP
+                                       if (bflink != &bfaddr)
+                                               *bflink = cpu_to_le32(bf->bf_daddr);
+                                       else
+                                               *bflink = bf->bf_daddr;
+#else
+                                       *bflink = bf->bf_daddr;
+#endif
+                                       bflink = &bf->bf_desc->ds_link;
+                               }
+                       }
+               }
+               *bflink = 0;                    /* link of last frame */
+       }
+
+       /*
+        * Handle slot time change when a non-ERP station joins/leaves
+        * an 11g network.  The 802.11 layer notifies us via callback,
+        * we mark updateslot, then wait one beacon before effecting
+        * the change.  This gives associated stations at least one
+        * beacon interval to note the state change.
+        *
+        * NB: The slot time change state machine is clocked according
+        *     to whether we are bursting or staggering beacons.  We
+        *     recognize the request to update and record the current
+        *     slot then don't transition until that slot is reached
+        *     again.  If we miss a beacon for that slot then we'll be
+        *     slow to transition but we'll be sure at least one beacon
+        *     interval has passed.  When bursting slot is always left
+        *     set to ath_maxvaps so this check is a no-op.
+        */
+       /* XXX locking */
+       if (sc->sc_updateslot == UPDATE) {
+               sc->sc_updateslot = COMMIT;     /* commit next beacon */
+               sc->sc_slotupdate = slot;
+       } else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot)
+               ath_setslottime(sc);            /* commit change to hardware */
+
+       if ((!sc->sc_stagbeacons || slot == 0) && (!sc->sc_diversity)) {
+               int otherant;
+               /*
+                * Check recent per-antenna transmit statistics and flip
+                * the default rx antenna if noticeably more frames went out
+                * on the non-default antenna.  Only do this if rx diversity
+                * is off.
+                * XXX assumes 2 antennae
+                */
+               otherant = sc->sc_defant & 1 ? 2 : 1;
+               if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + ATH_ANTENNA_DIFF) {
+                       DPRINTF(sc, ATH_DEBUG_BEACON,
+                               "%s: flip defant to %u, %u > %u\n",
+                               __func__, otherant, sc->sc_ant_tx[otherant],
+                               sc->sc_ant_tx[sc->sc_defant]);
+                       ath_setdefantenna(sc, otherant);
+               }
+               sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0;
+       }
+
+       if (bfaddr != 0) {
+               /*
+                * Stop any current DMA and put the new frame(s) on the queue.
+                * This should never fail since we check above that no frames
+                * are still pending on the queue.
+                */
+               if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) {
+                       DPRINTF(sc, ATH_DEBUG_ANY,
+                               "%s: beacon queue %u did not stop?\n",
+                               __func__, sc->sc_bhalq);
+                       /* NB: the HAL still stops DMA, so proceed */
+               }
+               /* NB: cabq traffic should already be queued and primed */
+               ath_hal_puttxbuf(ah, sc->sc_bhalq, bfaddr);
+               ath_hal_txstart(ah, sc->sc_bhalq);
+
+               sc->sc_stats.ast_be_xmit++;             /* XXX per-VAP? */
+       }
+#undef TSF_TO_TU
+}
+
+/*
+ * Reset the hardware after detecting beacons have stopped.
+ */
+static void
+ath_bstuck_tasklet(TQUEUE_ARG data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct ath_softc *sc = netdev_priv(dev);
+       /*
+        * XXX:if the bmisscount is cleared while the 
+        *     tasklet execution is pending, the following
+        *     check will be true, in which case return 
+        *     without resetting the driver.
+        */
+       if (sc->sc_bmisscount <= BSTUCK_THRESH) 
+               return;
+       printk("%s: stuck beacon; resetting (bmiss count %u)\n",
+               dev->name, sc->sc_bmisscount);
+       ath_reset(dev);
+}
+
+/*
+ * Startup beacon transmission for adhoc mode when
+ * they are sent entirely by the hardware using the
+ * self-linked descriptor + veol trick.
+ */
+static void
+ath_beacon_start_adhoc(struct ath_softc *sc, struct ieee80211vap *vap)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       struct ath_buf *bf;
+       struct ieee80211_node *ni;
+       struct ath_vap *avp;
+       struct sk_buff *skb;
+
+       avp = ATH_VAP(vap);
+       if (avp->av_bcbuf == NULL) {
+               DPRINTF(sc, ATH_DEBUG_ANY, "%s: avp=%p av_bcbuf=%p\n",
+                        __func__, avp, avp != NULL ? avp->av_bcbuf : NULL);
+               return;
+       }
+       bf = avp->av_bcbuf;
+       ni = bf->bf_node;
+
+       /*
+        * Update dynamic beacon contents.  If this returns
+        * non-zero then we need to remap the memory because
+        * the beacon frame changed size (probably because
+        * of the TIM bitmap).
+        */
+       skb = bf->bf_skb;
+       if (ieee80211_beacon_update(ni, &avp->av_boff, skb, 0)) {
+               bus_unmap_single(sc->sc_bdev,
+                       bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE);
+               bf->bf_skbaddr = bus_map_single(sc->sc_bdev,
+                       skb->data, skb->len, BUS_DMA_TODEVICE);
+       }
+
+       /*
+        * Construct tx descriptor.
+        */
+       ath_beacon_setup(sc, bf);
+
+       bus_dma_sync_single(sc->sc_bdev,
+               bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE);
+
+       /* NB: caller is known to have already stopped tx DMA */
+       ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr);
+       ath_hal_txstart(ah, sc->sc_bhalq);
+       DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: TXDP%u = %llx (%p)\n", __func__,
+               sc->sc_bhalq, ito64(bf->bf_daddr), bf->bf_desc);
+}
+
+/*
+ * Reclaim beacon resources and return buffer to the pool.
+ */
+static void
+ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf)
+{
+       if (bf->bf_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;
+       }
+       STAILQ_INSERT_TAIL(&sc->sc_bbuf, bf, bf_list);
+}
+
+/*
+ * Reclaim all beacon resources.
+ */
+static void
+ath_beacon_free(struct ath_softc *sc)
+{
+       struct ath_buf *bf;
+
+       STAILQ_FOREACH(bf, &sc->sc_bbuf, bf_list) {
+               if (bf->bf_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;
+               }
+       }
+}
+
+/*
+ * Configure the beacon and sleep timers.
+ *
+ * When operating as an AP this resets the TSF and sets
+ * up the hardware to notify us when we need to issue beacons.
+ *
+ * When operating in station mode this sets up the beacon
+ * timers according to the timestamp of the last received
+ * beacon and the current TSF, configures PCF and DTIM
+ * handling, programs the sleep registers so the hardware
+ * will wake up in time to receive beacons, and configures
+ * the beacon miss handling so we'll receive a BMISS
+ * interrupt when we stop seeing beacons from the AP
+ * we've associated with.
+ */
+static void
+ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap)
+{
+#define        TSF_TO_TU(_h,_l) \
+       ((((u_int32_t)(_h)) << 22) | (((u_int32_t)(_l)) >> 10))
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ath_hal *ah = sc->sc_ah;
+       struct ieee80211_node *ni;
+       u_int32_t nexttbtt, intval;
+
+       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? */
+       if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+               /*
+                * 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. 
+                */
+               /* NB: the beacon interval is kept internally in TU's */
+               intval = ic->ic_lintval & HAL_BEACON_PERIOD;
+               if (sc->sc_stagbeacons)
+                       intval /= ath_maxvaps;  /* for staggered beacons */
+               if ((sc->sc_nostabeacons) &&
+                   (vap->iv_opmode == IEEE80211_M_HOSTAP))
+                       nexttbtt = 0;
+       } else
+               intval = ni->ni_intval & HAL_BEACON_PERIOD;
+       if (nexttbtt == 0)              /* e.g. for ap mode */
+               nexttbtt = intval;
+       else if (intval)                /* NB: can be 0 for monitor mode */
+               nexttbtt = roundup(nexttbtt, intval);
+       DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u intval %u (%u)\n",
+               __func__, nexttbtt, intval, ni->ni_intval);
+       if (ic->ic_opmode == IEEE80211_M_STA && !(sc->sc_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).
+                */
+               dtimperiod = vap->iv_dtim_period;
+               if (dtimperiod <= 0)            /* NB: 0 if not known */
+                       dtimperiod = 1;
+               dtimcount = vap->iv_dtim_count;
+               if (dtimcount >= dtimperiod)    /* NB: sanity check */
+                       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;
+               do {
+                       nexttbtt += intval;
+                       if (--dtimcount < 0) {
+                               dtimcount = dtimperiod - 1;
+                               if (--cfpcount < 0)
+                                       cfpcount = cfpperiod - 1;
+                       }
+               } while (nexttbtt < tsftu);
+#undef FUDGE
+               memset(&bs, 0, sizeof(bs));
+               bs.bs_intval = intval;
+               bs.bs_nexttbtt = nexttbtt;
+               bs.bs_dtimperiod = dtimperiod * intval;
+               bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount * intval;
+               bs.bs_cfpperiod = cfpperiod * bs.bs_dtimperiod;
+               bs.bs_cfpnext = bs.bs_nextdtim + cfpcount * bs.bs_dtimperiod;
+               bs.bs_cfpmaxduration = 0;
+#if 0
+               /*
+                * The 802.11 layer records the offset to the DTIM
+                * bitmap while receiving beacons; use it here to
+                * enable h/w detection of our AID being marked in
+                * the bitmap vector (to indicate frames for us are
+                * pending at the AP).
+                * XXX do DTIM handling in s/w to WAR old h/w bugs
+                * XXX enable based on h/w rev for newer chips
+                */
+               bs.bs_timoffset = ni->ni_timoff;
+#endif
+               /*
+                * Calculate the number of consecutive beacons to miss
+                * before taking a BMISS interrupt.  The configuration
+                * is specified in TU so we only need calculate based
+                * on the beacon interval.  Note that we clamp the
+                * result to at most 10 beacons.
+                */
+               bs.bs_bmissthreshold = howmany(ic->ic_bmisstimeout, intval);
+               if (bs.bs_bmissthreshold > 10)
+                       bs.bs_bmissthreshold = 10;
+               else if (bs.bs_bmissthreshold < 2)
+                       bs.bs_bmissthreshold = 2;
+
+               /*
+                * Calculate sleep duration.  The configuration is
+                * given in ms.  We ensure a multiple of the beacon
+                * period is used.  Also, if the sleep duration is
+                * greater than the DTIM period then it makes senses
+                * to make it a multiple of that.
+                *
+                * XXX fixed at 100ms
+                */
+               bs.bs_sleepduration =
+                       roundup(IEEE80211_MS_TO_TU(100), bs.bs_intval);
+               if (bs.bs_sleepduration > bs.bs_dtimperiod)
+                       bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod);
+
+               DPRINTF(sc, ATH_DEBUG_BEACON, 
+                       "%s: tsf %llu tsf:tu %u intval %u nexttbtt %u dtim %u nextdtim %u bmiss %u sleep %u cfp:period %u maxdur %u next %u timoffset %u\n"
+                       , __func__
+                       , (long long) tsf, tsftu
+                       , bs.bs_intval
+                       , bs.bs_nexttbtt
+                       , bs.bs_dtimperiod
+                       , bs.bs_nextdtim
+                       , bs.bs_bmissthreshold
+                       , bs.bs_sleepduration
+                       , bs.bs_cfpperiod
+                       , bs.bs_cfpmaxduration
+                       , bs.bs_cfpnext
+                       , bs.bs_timoffset
+               );
+
+               ic->ic_bmiss_guard = jiffies +
+                       IEEE80211_TU_TO_JIFFIES(bs.bs_intval * bs.bs_bmissthreshold);
+
+               ath_hal_intrset(ah, 0);
+               ath_hal_beacontimers(ah, &bs);
+               sc->sc_imask |= HAL_INT_BMISS;
+               ath_hal_intrset(ah, sc->sc_imask);
+       } else {
+               ath_hal_intrset(ah, 0);
+               if (nexttbtt == intval)
+                       intval |= HAL_BEACON_RESET_TSF;
+               if (ic->ic_opmode == IEEE80211_M_IBSS) {
+                       /*
+                        * In IBSS mode enable the beacon timers but only
+                        * enable SWBA interrupts if we need to manually
+                        * prepare beacon frames.  Otherwise we use a
+                        * self-linked tx descriptor and let the hardware
+                        * deal with things.
+                        */
+                       intval |= HAL_BEACON_ENA;
+                       if (!sc->sc_hasveol)
+                               sc->sc_imask |= HAL_INT_SWBA;
+                       ath_beaconq_config(sc);
+               } else if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+                       /*
+                        * In AP mode we enable the beacon timers and
+                        * SWBA interrupts to prepare beacon frames.
+                        */
+                       intval |= HAL_BEACON_ENA;
+                       sc->sc_imask |= HAL_INT_SWBA;   /* beacon prepare */
+                       ath_beaconq_config(sc);
+               }
+#ifdef ATH_SUPERG_DYNTURBO
+               ath_beacon_dturbo_config(vap, intval & 
+                               ~(HAL_BEACON_RESET_TSF | HAL_BEACON_ENA));
+#endif
+               ath_hal_beaconinit(ah, nexttbtt, intval);
+               sc->sc_bmisscount = 0;
+               ath_hal_intrset(ah, sc->sc_imask);
+               /*
+                * When using a self-linked beacon descriptor in
+                * ibss mode load it once here.
+                */
+               if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol)
+                       ath_beacon_start_adhoc(sc, vap);
+       }
+       sc->sc_syncbeacon = 0;
+#undef TSF_TO_TU
+}
+
+static int
+ath_descdma_setup(struct ath_softc *sc,
+       struct ath_descdma *dd, ath_bufhead *head,
+       const char *name, int nbuf, int ndesc)
+{
+#define        DS2PHYS(_dd, _ds) \
+       ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
+       struct ath_desc *ds;
+       struct ath_buf *bf;
+       int i, bsize, error;
+
+       DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA: %u buffers %u desc/buf\n",
+               __func__, name, nbuf, ndesc);
+
+       dd->dd_name = name;
+       dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc;
+
+       /* allocate descriptors */
+       dd->dd_desc = bus_alloc_consistent(sc->sc_bdev,
+               dd->dd_desc_len, &dd->dd_desc_paddr);
+       if (dd->dd_desc == NULL) {
+               error = -ENOMEM;
+               goto fail;
+       }
+       ds = dd->dd_desc;
+       DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA map: %p (%lu) -> %llx (%lu)\n",
+               __func__, dd->dd_name, ds, (u_long) dd->dd_desc_len,
+               ito64(dd->dd_desc_paddr), /*XXX*/ (u_long) dd->dd_desc_len);
+
+       /* allocate buffers */
+       bsize = sizeof(struct ath_buf) * nbuf;
+       bf = kmalloc(bsize, GFP_KERNEL);
+       if (bf == NULL) {
+               error = -ENOMEM;                /* XXX different code */
+               goto fail2;
+       }
+       memset(bf, 0, bsize);
+       dd->dd_bufptr = bf;
+
+       STAILQ_INIT(head);
+       for (i = 0; i < nbuf; i++, bf++, ds += ndesc) {
+               bf->bf_desc = ds;
+               bf->bf_daddr = DS2PHYS(dd, ds);
+               STAILQ_INSERT_TAIL(head, bf, bf_list);
+       }
+       return 0;
+fail2:
+       bus_free_consistent(sc->sc_bdev, dd->dd_desc_len,
+               dd->dd_desc, dd->dd_desc_paddr);
+fail:
+       memset(dd, 0, sizeof(*dd));
+       return error;
+#undef DS2PHYS
+}
+
+static void
+ath_descdma_cleanup(struct ath_softc *sc,
+       struct ath_descdma *dd, ath_bufhead *head, int dir)
+{
+       struct ath_buf *bf;
+       struct ieee80211_node *ni;
+
+       STAILQ_FOREACH(bf, head, bf_list) {
+               if (bf->bf_skb != NULL) {
+                       /* XXX skb->len is not good enough for rxbuf */
+                       if (dd == &sc->sc_rxdma)
+                               bus_unmap_single(sc->sc_bdev,
+                                       bf->bf_skbaddr, sc->sc_rxbufsize, dir);
+                       else
+                               bus_unmap_single(sc->sc_bdev,
+                                       bf->bf_skbaddr, bf->bf_skb->len, dir);
+                       dev_kfree_skb(bf->bf_skb);
+                       bf->bf_skb = NULL;
+               }
+               ni = bf->bf_node;
+               bf->bf_node = NULL;
+               if (ni != NULL) {
+                       /*
+                        * Reclaim node reference.
+                        */
+                       ieee80211_free_node(ni);
+               }
+       }
+
+       /* Free memory associated with descriptors */
+       bus_free_consistent(sc->sc_bdev, dd->dd_desc_len,
+               dd->dd_desc, dd->dd_desc_paddr);
+
+       STAILQ_INIT(head);
+       kfree(dd->dd_bufptr);
+       memset(dd, 0, sizeof(*dd));
+}
+
+static int
+ath_desc_alloc(struct ath_softc *sc)
+{
+       int error;
+
+       error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf,
+                       "rx", ATH_RXBUF, 1);
+       if (error != 0)
+               return error;
+
+       error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf,
+                       "tx", ATH_TXBUF, ATH_TXDESC);
+       if (error != 0) {
+               ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf,
+                       BUS_DMA_FROMDEVICE);
+               return error;
+       }
+
+       /* XXX allocate beacon state together with VAP */
+       error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf,
+                       "beacon", ath_maxvaps, 1);
+       if (error != 0) {
+               ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf,
+                       BUS_DMA_TODEVICE);
+               ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf,
+                       BUS_DMA_FROMDEVICE);
+               return error;
+       }
+       return 0;
+}
+
+static void
+ath_desc_free(struct ath_softc *sc)
+{
+       if (sc->sc_bdma.dd_desc_len != 0)
+               ath_descdma_cleanup(sc, &sc->sc_bdma, &sc->sc_bbuf,
+                       BUS_DMA_TODEVICE);
+       if (sc->sc_txdma.dd_desc_len != 0)
+               ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf,
+                       BUS_DMA_TODEVICE);
+       if (sc->sc_rxdma.dd_desc_len != 0)
+               ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf,
+                       BUS_DMA_FROMDEVICE);
+}
+
+static struct ieee80211_node *
+ath_node_alloc(struct ieee80211_node_table *nt,struct ieee80211vap *vap)
+{
+       struct ath_softc *sc = netdev_priv(nt->nt_ic->ic_dev);
+       const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space;
+       struct ath_node *an;
+
+       an = kmalloc(space, GFP_ATOMIC);
+       if (an == NULL)
+               return NULL;
+       memset(an, 0, space);
+       an->an_decomp_index = INVALID_DECOMP_INDEX;
+       an->an_avgrssi = ATH_RSSI_DUMMY_MARKER;
+       an->an_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
+       an->an_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER;
+       an->an_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
+       /*
+        * ath_rate_node_init needs a VAP pointer in node
+        * to decide which mgt rate to use
+        */
+       an->an_node.ni_vap = vap;
+       sc->sc_rc->ops->node_init(sc, an);
+
+       /* U-APSD init */
+       STAILQ_INIT(&an->an_uapsd_q);
+       an->an_uapsd_qdepth = 0;
+       STAILQ_INIT(&an->an_uapsd_overflowq);
+       an->an_uapsd_overflowqdepth = 0;
+       ATH_NODE_UAPSD_LOCK_INIT(an);
+
+       DPRINTF(sc, ATH_DEBUG_NODE, "%s: an %p\n", __func__, an);
+       return &an->an_node;
+}
+
+static void
+ath_node_cleanup(struct ieee80211_node *ni)
+{
+       struct ieee80211com *ic = ni->ni_ic;
+       struct ath_softc *sc = netdev_priv(ni->ni_ic->ic_dev);
+       struct ath_node *an = ATH_NODE(ni);
+       struct ath_buf *bf;
+       
+       /*
+        * U-APSD cleanup
+        */
+       ATH_NODE_UAPSD_LOCK_IRQ(an);
+       if (ni->ni_flags & IEEE80211_NODE_UAPSD_TRIG) {
+               ni->ni_flags &= ~IEEE80211_NODE_UAPSD_TRIG;
+               ic->ic_uapsdmaxtriggers--;
+               ni->ni_flags &= ~IEEE80211_NODE_UAPSD_SP;
+       }
+       ATH_NODE_UAPSD_UNLOCK_IRQ(an);
+       while (an->an_uapsd_qdepth) {
+               bf = STAILQ_FIRST(&an->an_uapsd_q);
+               STAILQ_REMOVE_HEAD(&an->an_uapsd_q, bf_list);
+               bf->bf_desc->ds_link = 0;
+
+               dev_kfree_skb_any(bf->bf_skb);
+               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);
+               ieee80211_free_node(ni);
+
+               an->an_uapsd_qdepth--;
+       }
+
+       while (an->an_uapsd_overflowqdepth) {
+               bf = STAILQ_FIRST(&an->an_uapsd_overflowq);
+               STAILQ_REMOVE_HEAD(&an->an_uapsd_overflowq, bf_list);
+               bf->bf_desc->ds_link = 0;
+
+               dev_kfree_skb_any(bf->bf_skb);
+               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);
+               ieee80211_free_node(ni);
+
+               an->an_uapsd_overflowqdepth--;
+       }
+
+       ATH_NODE_UAPSD_LOCK_IRQ(an);
+       sc->sc_node_cleanup(ni);
+       ATH_NODE_UAPSD_UNLOCK_IRQ(an);
+}
+
+static void
+ath_node_free(struct ieee80211_node *ni)
+{
+       struct ath_softc *sc = netdev_priv(ni->ni_ic->ic_dev);
+
+       sc->sc_rc->ops->node_cleanup(sc, ATH_NODE(ni));
+       sc->sc_node_free(ni);
+#ifdef ATH_SUPERG_XR
+       ath_grppoll_period_update(sc);
+#endif
+}
+
+static u_int8_t
+ath_node_getrssi(const struct ieee80211_node *ni)
+{
+#define        HAL_EP_RND(x, mul) \
+       ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
+       u_int32_t avgrssi = ATH_NODE_CONST(ni)->an_avgrssi;
+       int32_t rssi;
+
+       /*
+        * When only one frame is received there will be no state in
+        * avgrssi so fallback on the value recorded by the 802.11 layer.
+        */
+       if (avgrssi != ATH_RSSI_DUMMY_MARKER)
+               rssi = HAL_EP_RND(avgrssi, HAL_RSSI_EP_MULTIPLIER);
+       else
+               rssi = ni->ni_rssi;
+       /* NB: theoretically we shouldn't need this, but be paranoid */
+       return rssi < 0 ? 0 : rssi > 127 ? 127 : rssi;
+#undef HAL_EP_RND
+}
+
+
+#ifdef ATH_SUPERG_XR
+/*
+ * Stops the txqs and moves data between XR and Normal queues.
+ * Also adjusts the rate info in the descriptors.
+ */
+
+static u_int8_t
+ath_node_move_data(const struct ieee80211_node *ni)
+{
+#ifdef NOT_YET
+       struct ath_txq *txq = NULL; 
+       struct ieee80211com *ic = ni->ni_ic;
+       struct ath_softc *sc = netdev_priv(ic->ic_dev);
+       struct ath_buf *bf, *prev, *bf_tmp, *bf_tmp1;
+       struct ath_hal *ah = sc->sc_ah;
+       struct sk_buff *skb = NULL;
+       struct ath_desc *ds;
+       HAL_STATUS status;
+       int index;
+
+       if (ni->ni_vap->iv_flags & IEEE80211_F_XR) {
+               struct ath_txq tmp_q; 
+               memset(&tmp_q, 0, sizeof(tmp_q));
+               STAILQ_INIT(&tmp_q.axq_q);
+               /*
+                * move data from Normal txqs to XR queue.
+                */
+               printk("move data from NORMAL to XR\n");
+               /*
+                * collect all the data towards the node
+                * in to the tmp_q.
+                */
+               index = WME_AC_VO;
+               while (index >= WME_AC_BE && txq != sc->sc_ac2q[index]) { 
+                       txq = sc->sc_ac2q[index]; 
+                       ATH_TXQ_LOCK(txq);
+                       ath_hal_stoptxdma(ah, txq->axq_qnum);
+                       bf = prev = STAILQ_FIRST(&txq->axq_q);
+                       /*
+                        * skip all the buffers that are done
+                        * until the first one that is in progress
+                        */
+                       while (bf) {
+#ifdef ATH_SUPERG_FF
+                               ds = &bf->bf_desc[bf->bf_numdesc - 1];
+#else
+                               ds = bf->bf_desc;               /* NB: last descriptor */
+#endif
+                               status = ath_hal_txprocdesc(ah, ds);
+                               if (status == HAL_EINPROGRESS)
+                                       break; 
+                               prev = bf;
+                               bf = STAILQ_NEXT(bf,bf_list);
+                       }
+                       /* 
+                        * save the pointer to the last buf that's
+                        * done
+                        */
+                       if (prev == bf)
+                               bf_tmp = NULL;  
+                       else
+                               bf_tmp=prev;
+                       while (bf) {
+                               if (ni == bf->bf_node) {
+                                       if (prev == bf) {
+                                               ATH_TXQ_REMOVE_HEAD(txq, bf_list);
+                                               STAILQ_INSERT_TAIL(&tmp_q.axq_q, bf, bf_list);
+                                               bf = STAILQ_FIRST(&txq->axq_q);
+                                               prev = bf;
+                                       } else {
+                                               STAILQ_REMOVE_AFTER(&(txq->axq_q), prev, bf_list);
+                                               txq->axq_depth--;
+                                               STAILQ_INSERT_TAIL(&tmp_q.axq_q, bf, bf_list);
+                                               bf = STAILQ_NEXT(prev, bf_list);
+                                               /*
+                                                * after deleting the node
+                                                * link the descriptors
+                                                */
+#ifdef ATH_SUPERG_FF
+                                               ds = &prev->bf_desc[prev->bf_numdesc - 1];
+#else
+                                               ds = prev->bf_desc;     /* NB: last descriptor */
+#endif
+#ifdef AH_NEED_DESC_SWAP
+                                               ds->ds_link = cpu_to_le32(bf->bf_daddr);
+#else
+                                               ds->ds_link = bf->bf_daddr;
+#endif
+                                       }
+                               } else {
+                                       prev = bf;
+                                       bf = STAILQ_NEXT(bf, bf_list);
+                               }
+                       }
+                       /*
+                        * if the last buf was deleted.
+                        * set the pointer to the last descriptor.
+                        */
+                       bf = STAILQ_FIRST(&txq->axq_q);
+                       if (bf) {
+                               if (prev) {
+                                       bf = STAILQ_NEXT(prev, bf_list);
+                                       if (!bf) { /* prev is the last one on the list */
+#ifdef ATH_SUPERG_FF
+                                               ds = &prev->bf_desc[prev->bf_numdesc - 1];
+#else
+                                               ds = prev->bf_desc;     /* NB: last descriptor */
+#endif
+                                               status = ath_hal_txprocdesc(ah, ds);
+                                               if (status == HAL_EINPROGRESS) 
+                                                       txq->axq_link = &ds->ds_link;
+                                               else
+                                                       txq->axq_link = NULL;   
+                                       }
+                               } 
+                       } else
+                               txq->axq_link = NULL;
+
+                       ATH_TXQ_UNLOCK(txq);
+                       /*
+                        * restart the DMA from the first 
+                        * buffer that was not DMA'd.
+                        */
+                       if (bf_tmp)
+                               bf = STAILQ_NEXT(bf_tmp, bf_list);
+                       else
+                               bf = STAILQ_FIRST(&txq->axq_q);
+                       if (bf) {       
+                               ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
+                               ath_hal_txstart(ah, txq->axq_qnum);
+                       }
+               }
+               /* 
+                * queue them on to the XR txqueue. 
+                * can not directly put them on to the XR txq. since the
+                * skb data size may be greater than the XR fragmentation
+                * threshold size.
+                */
+               bf  = STAILQ_FIRST(&tmp_q.axq_q);
+               index = 0;
+               while (bf) {
+                       skb = bf->bf_skb;
+                       bf->bf_skb = NULL;
+                       bf->bf_node = NULL;
+                       ATH_TXBUF_LOCK(sc);
+                       STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+                       ATH_TXBUF_UNLOCK(sc);
+                       ath_hardstart(skb,sc->sc_dev);
+                       ATH_TXQ_REMOVE_HEAD(&tmp_q, bf_list);
+                       bf = STAILQ_FIRST(&tmp_q.axq_q);
+                       index++;
+               }
+               printk("moved %d buffers from NORMAL to XR\n", index);
+       } else {
+               struct ath_txq wme_tmp_qs[WME_AC_VO+1]; 
+               struct ath_txq *wmeq = NULL, *prevq; 
+               struct ieee80211_frame *wh;
+               struct ath_desc *ds = NULL;
+               int count = 0;
+
+               /*
+                * move data from XR txq to Normal txqs.
+                */
+               printk("move buffers from XR to NORMAL\n");
+               memset(&wme_tmp_qs, 0, sizeof(wme_tmp_qs));
+               for (index = 0; index <= WME_AC_VO; index++)
+                       STAILQ_INIT(&wme_tmp_qs[index].axq_q);
+               txq = sc->sc_xrtxq; 
+               ATH_TXQ_LOCK(txq);
+               ath_hal_stoptxdma(ah, txq->axq_qnum);
+               bf = prev = STAILQ_FIRST(&txq->axq_q);
+               /*
+                * skip all the buffers that are done
+                * until the first one that is in progress
+                */
+               while (bf) {
+#ifdef ATH_SUPERG_FF
+                       ds = &bf->bf_desc[bf->bf_numdesc - 1];
+#else
+                       ds = bf->bf_desc;               /* NB: last descriptor */
+#endif
+                       status = ath_hal_txprocdesc(ah, ds);
+                       if (status == HAL_EINPROGRESS)
+                               break;
+                       prev= bf;
+                       bf = STAILQ_NEXT(bf,bf_list);
+               }
+               /* 
+                * save the pointer to the last buf that's
+                * done
+                */
+               if (prev == bf)
+                       bf_tmp1 = NULL;  
+               else
+                       bf_tmp1 = prev;
+               /*
+                * collect all the data in to four temp SW queues.
+                */
+               while (bf) {
+                       if (ni == bf->bf_node) {
+                               if (prev == bf) {
+                                       STAILQ_REMOVE_HEAD(&txq->axq_q,bf_list);
+                                       bf_tmp=bf;
+                                       bf = STAILQ_FIRST(&txq->axq_q);
+                                       prev = bf;
+                               } else {
+                                       STAILQ_REMOVE_AFTER(&(txq->axq_q),prev,bf_list);
+                                       bf_tmp=bf;
+                                       bf = STAILQ_NEXT(prev,bf_list);
+                               }
+                               count++;
+                               skb = bf_tmp->bf_skb;
+                               wh = (struct ieee80211_frame *) skb->data;
+                               if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
+                                       /* XXX validate skb->priority, remove mask */
+                                       wmeq = &wme_tmp_qs[skb->priority & 0x3];
+                               } else
+                                       wmeq = &wme_tmp_qs[WME_AC_BE];
+                               STAILQ_INSERT_TAIL(&wmeq->axq_q, bf_tmp, bf_list);
+                               ds = bf_tmp->bf_desc;
+                               /* 
+                                * link the descriptors
+                                */
+                               if (wmeq->axq_link != NULL) {
+#ifdef AH_NEED_DESC_SWAP
+                                       *wmeq->axq_link = cpu_to_le32(bf_tmp->bf_daddr);
+#else
+                                       *wmeq->axq_link = bf_tmp->bf_daddr;
+#endif
+                                       DPRINTF(sc, ATH_DEBUG_XMIT, "%s: link[%u](%p)=%p (%p)\n",
+                                                       __func__,
+                                                       wmeq->axq_qnum, wmeq->axq_link,
+                                                       (caddr_t)bf_tmp->bf_daddr, bf_tmp->bf_desc);
+                               }
+                               wmeq->axq_link = &ds->ds_link;
+                               /* 
+                                * update the rate information  
+                                */
+                       } else {
+                               prev = bf;
+                               bf = STAILQ_NEXT(bf, bf_list);
+                       }
+               }
+               /*
+                * reset the axq_link pointer to the last descriptor.
+                */
+               bf = STAILQ_FIRST(&txq->axq_q);
+               if (bf) {
+                       if (prev) {
+                               bf = STAILQ_NEXT(prev, bf_list);
+                               if (!bf) { /* prev is the last one on the list */
+#ifdef ATH_SUPERG_FF
+                                       ds = &prev->bf_desc[prev->bf_numdesc - 1];
+#else
+                                       ds = prev->bf_desc;     /* NB: last descriptor */
+#endif
+                                       status = ath_hal_txprocdesc(ah, ds);
+                                       if (status == HAL_EINPROGRESS) 
+                                               txq->axq_link = &ds->ds_link;
+                                       else
+                                               txq->axq_link = NULL;
+                               } 
+                       } 
+               } else {
+                       /*
+                        * if the list is empty reset the pointer.
+                        */
+                       txq->axq_link = NULL;
+               }
+               ATH_TXQ_UNLOCK(txq);
+               /*
+                * restart the DMA from the first 
+                * buffer that was not DMA'd.
+                */
+               if (bf_tmp1)
+                       bf = STAILQ_NEXT(bf_tmp1,bf_list);
+               else
+                       bf = STAILQ_FIRST(&txq->axq_q);
+               if (bf) {       
+                       ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
+                       ath_hal_txstart(ah, txq->axq_qnum);
+               }
+
+               /* 
+                * move (concant) the lists from the temp sw queues in to
+                * WME queues.
+                */
+               index = WME_AC_VO;
+               txq = NULL;
+               while (index >= WME_AC_BE ) { 
+                       prevq = txq;
+                       txq = sc->sc_ac2q[index];
+                       if (txq != prevq) {
+                               ATH_TXQ_LOCK(txq);
+                               ath_hal_stoptxdma(ah, txq->axq_qnum);
+                       }
+                       
+                       wmeq = &wme_tmp_qs[index];
+                       bf = STAILQ_FIRST(&wmeq->axq_q);
+                       if (bf) {
+                               ATH_TXQ_MOVE_Q(wmeq,txq);
+                               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
+                               } 
+                       }
+                       if (index == WME_AC_BE || txq != prevq) {
+                               /* 
+                                * find the first buffer to be DMA'd.
+                                */
+                               bf = STAILQ_FIRST(&txq->axq_q);
+                               while (bf) {
+#ifdef ATH_SUPERG_FF
+                                       ds = &bf->bf_desc[bf->bf_numdesc - 1];
+#else
+                                       ds = bf->bf_desc;       /* NB: last descriptor */
+#endif
+                                       status = ath_hal_txprocdesc(ah, ds);
+                                       if (status == HAL_EINPROGRESS)
+                                               break; 
+                                       bf = STAILQ_NEXT(bf,bf_list);
+                               }
+                               if (bf) {
+                                       ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
+                                       ath_hal_txstart(ah, txq->axq_qnum);
+                               }
+                               ATH_TXQ_UNLOCK(txq);
+                       }
+                       index--;
+               }
+               printk("moved %d buffers from XR to NORMAL\n", count);
+       }
+#endif
+       return 0;
+}
+#endif
+
+static struct sk_buff *
+ath_alloc_skb(u_int size, u_int align)
+{
+       struct sk_buff *skb;
+       u_int off;
+
+       skb = dev_alloc_skb(size + align - 1);
+       if (skb != NULL) {
+               off = ((unsigned long) skb->data) % align;
+               if (off != 0)
+                       skb_reserve(skb, align - off);
+       }
+       return skb;
+}
+
+static int
+ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       struct sk_buff *skb;
+       struct ath_desc *ds;
+
+       skb = bf->bf_skb;
+       if (skb == NULL) {
+               if (sc->sc_nmonvaps > 0) {
+                       u_int off;
+                       int extra = A_MAX(sizeof(struct ath_rx_radiotap_header), 
+                                         A_MAX(sizeof(wlan_ng_prism2_header), ATHDESC_HEADER_SIZE));
+                                               
+                       /*
+                        * Allocate buffer for monitor mode with space for the
+                        * wlan-ng style physical layer header at the start.
+                        */
+                       skb = dev_alloc_skb(sc->sc_rxbufsize + extra + sc->sc_cachelsz - 1);
+                       if (skb == NULL) {
+                               DPRINTF(sc, ATH_DEBUG_ANY,
+                                       "%s: skbuff alloc of size %u failed\n",
+                                       __func__,
+                                       sc->sc_rxbufsize + extra + sc->sc_cachelsz - 1);
+                               sc->sc_stats.ast_rx_nobuf++;
+                               return -ENOMEM;
+                       }
+                       /*
+                        * Reserve space for the Prism header.
+                        */
+                       skb_reserve(skb, sizeof(wlan_ng_prism2_header));
+                       /*
+                        * Align to cache line.
+                        */
+                       off = ((unsigned long) skb->data) % sc->sc_cachelsz;
+                       if (off != 0)
+                               skb_reserve(skb, sc->sc_cachelsz - off);
+               } else {
+                       /*
+                        * Cache-line-align.  This is important (for the
+                        * 5210 at least) as not doing so causes bogus data
+                        * in rx'd frames.
+                        */
+                       skb = ath_alloc_skb(sc->sc_rxbufsize, sc->sc_cachelsz);
+                       if (skb == NULL) {
+                               DPRINTF(sc, ATH_DEBUG_ANY,
+                                       "%s: skbuff alloc of size %u failed\n",
+                                       __func__, sc->sc_rxbufsize);
+                               sc->sc_stats.ast_rx_nobuf++;
+                               return -ENOMEM;
+                       }
+               }
+               skb->dev = sc->sc_dev;
+               bf->bf_skb = skb;
+               bf->bf_skbaddr = bus_map_single(sc->sc_bdev,
+                       skb->data, sc->sc_rxbufsize, BUS_DMA_FROMDEVICE);
+       }
+
+       /*
+        * Setup descriptors.  For receive we always terminate
+        * the descriptor list with a self-linked entry so we'll
+        * not get overrun under high load (as can happen with a
+        * 5212 when ANI processing enables PHY error frames).
+        *
+        * To ensure the last descriptor is self-linked we create
+        * each descriptor as self-linked and add it to the end.  As
+        * each additional descriptor is added the previous self-linked
+        * entry is ``fixed'' naturally.  This should be safe even
+        * if DMA is happening.  When processing RX interrupts we
+        * never remove/process the last, self-linked, entry on the
+        * descriptor list.  This ensures the hardware always has
+        * someplace to write a new frame.
+        */
+       ds = bf->bf_desc;
+       ds->ds_link = bf->bf_daddr;             /* link to self */
+       ds->ds_data = bf->bf_skbaddr;
+       ds->ds_vdata = (void *) skb->data;      /* virt addr of buffer */
+       ath_hal_setuprxdesc(ah, ds
+               , skb_tailroom(skb)             /* buffer size */
+               , 0
+       );
+       if (sc->sc_rxlink != NULL)
+               *sc->sc_rxlink = bf->bf_daddr;
+       sc->sc_rxlink = &ds->ds_link;
+       return 0;
+}
+
+/*
+ * Extend 15-bit time stamp from rx descriptor to
+ * a full 64-bit TSF using the current h/w TSF.
+ */
+static __inline u_int64_t
+ath_extend_tsf(struct ath_hal *ah, u_int32_t rstamp)
+{
+       u_int64_t tsf;
+
+       tsf = ath_hal_gettsf64(ah);
+       if ((tsf & 0x7fff) < rstamp)
+               tsf -= 0x8000;
+       return ((tsf &~ 0x7fff) | rstamp);
+}
+
+/*
+ * Add a prism2 header to a received frame and
+ * dispatch it to capture tools like kismet.
+ */
+static void
+ath_rx_capture(struct net_device *dev, struct ath_desc *ds, struct sk_buff *skb)
+{
+       struct ath_softc *sc = netdev_priv(dev);
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ieee80211_frame *wh = (struct ieee80211_frame *) skb->data;
+       unsigned int headersize = ieee80211_anyhdrsize(wh);
+       int padbytes = roundup(headersize, 4) - headersize;
+       u_int64_t tsf;
+
+       /* Pass up tsf clock in mactime
+        * Rx descriptor has the low 15 bits of the tsf at
+        * the time the frame was received.  Use the current
+        * tsf to extend this to 64 bits.
+        */
+       tsf = ath_extend_tsf(sc->sc_ah, ds->ds_rxstat.rs_tstamp);
+
+       KASSERT(ic->ic_flags & IEEE80211_F_DATAPAD,
+               ("data padding not enabled?"));
+
+       if (padbytes > 0) {
+               /* Remove hw pad bytes */
+               struct sk_buff *skb1 = skb_copy(skb, GFP_ATOMIC);
+               memmove(skb1->data + padbytes, skb1->data, headersize);
+               skb_pull(skb1, padbytes);
+               ieee80211_input_monitor(ic, skb1, ds, 0, tsf, sc);
+               dev_kfree_skb(skb1);
+       } else {
+               ieee80211_input_monitor(ic, skb, ds, 0, tsf, sc);
+       }
+}
+
+
+static void
+ath_tx_capture(struct net_device *dev, struct ath_desc *ds, struct sk_buff *skb)
+{
+       struct ath_softc *sc = netdev_priv(dev);
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ieee80211_frame *wh;
+       int extra = A_MAX(sizeof(struct ath_tx_radiotap_header), 
+                         A_MAX(sizeof(wlan_ng_prism2_header), ATHDESC_HEADER_SIZE));
+       u_int64_t tsf;
+       u_int32_t tstamp;
+       unsigned int headersize;
+       int padbytes;
+       
+       /* Pass up tsf clock in mactime
+        * TX descriptor contains the transmit time in TU's,
+        * (bits 25-10 of the TSF).
+        */
+       tsf = ath_hal_gettsf64(sc->sc_ah);
+       tstamp = ds->ds_txstat.ts_tstamp << 10;
+       
+       if ((tsf & 0x3ffffff) < tstamp)
+               tsf -= 0x4000000;
+       tsf = ((tsf &~ 0x3ffffff) | tstamp);
+
+       /*                                                                      
+        * release the owner of this skb since we're basically                  
+        * recycling it                                                         
+        */
+       if (atomic_read(&skb->users) != 1) {
+               struct sk_buff *skb2 = skb;
+               skb = skb_copy(skb, GFP_ATOMIC);
+               if (skb == NULL) {
+                       printk("%s:%d %s\n", __FILE__, __LINE__, __func__);
+                       dev_kfree_skb(skb2);
+                       return;
+               }
+               dev_kfree_skb(skb2);
+       } else
+               skb_orphan(skb);
+
+       wh = (struct ieee80211_frame *) skb->data;
+       headersize = ieee80211_anyhdrsize(wh);
+       padbytes = roundup(headersize, 4) - headersize;
+       if (padbytes > 0) {
+               /* Unlike in rx_capture, we're freeing the skb at the end
+                * anyway, so we don't need to worry about using a copy */
+               memmove(skb->data + padbytes, skb->data, headersize);
+               skb_pull(skb, padbytes);
+       }
+       
+       if (skb_headroom(skb) < extra &&
+           pskb_expand_head(skb, extra, 0, GFP_ATOMIC)) {
+               printk("%s:%d %s\n", __FILE__, __LINE__, __func__);
+               goto done;
+       }
+       ieee80211_input_monitor(ic, skb, ds, 1, tsf, sc);
+ done:
+       dev_kfree_skb(skb);
+}
+
+/*
+ * Intercept management frames to collect beacon rssi data
+ * and to do ibss merges.
+ */
+static void
+ath_recv_mgmt(struct ieee80211_node *ni, struct sk_buff *skb,
+       int subtype, int rssi, u_int32_t rstamp)
+{
+       struct ath_softc *sc = netdev_priv(ni->ni_ic->ic_dev);
+       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).
+        */
+       sc->sc_recv_mgmt(ni, skb, subtype, rssi, rstamp);
+       switch (subtype) {
+       case IEEE80211_FC0_SUBTYPE_BEACON:
+               /* 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.
+                        */
+                       vap->iv_flags_ext &= ~IEEE80211_FEXT_APPIE_UPDATE;
+                       ath_beacon_config(sc, vap);
+               }
+               /* fall thru... */
+       case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+               if (vap->iv_opmode == IEEE80211_M_IBSS &&
+                   vap->iv_state == IEEE80211_S_RUN) {
+                       u_int64_t tsf = ath_extend_tsf(sc->sc_ah, rstamp);
+                       /*
+                        * 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);
+                       }
+               }
+               break;
+       }
+}
+
+static void
+ath_setdefantenna(struct ath_softc *sc, u_int antenna)
+{
+       struct ath_hal *ah = sc->sc_ah;
+
+       /* XXX block beacon interrupts */
+       ath_hal_setdefantenna(ah, antenna);
+       if (sc->sc_defant != antenna)
+               sc->sc_stats.ast_ant_defswitch++;
+       sc->sc_defant = antenna;
+       sc->sc_rxotherant = 0;
+}
+
+static void
+ath_rx_tasklet(TQUEUE_ARG data)
+{
+#define        PA2DESC(_sc, _pa) \
+       ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
+               ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
+       struct net_device *dev = (struct net_device *)data;
+       struct ath_buf *bf;
+       struct ath_softc *sc = netdev_priv(dev);
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ath_hal *ah = sc->sc_ah;
+       struct ath_desc *ds;
+       struct sk_buff *skb;
+       struct ieee80211_node *ni;
+       int len, type;
+       u_int phyerr;
+
+       /* Let the 802.11 layer know about the new noise floor */
+       ic->ic_channoise = sc->sc_channoise;
+       
+       DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s\n", __func__);
+       do {
+               bf = STAILQ_FIRST(&sc->sc_rxbuf);
+               if (bf == NULL) {               /* XXX ??? can this happen */
+                       printk("%s: no buffer (%s)\n", dev->name, __func__);
+                       break;
+               }
+
+               /*
+                * Descriptors are now processed at in the first-level
+                * interrupt handler to support U-APSD trigger search.
+                * This must also be done even when U-APSD is not active to support
+                * other error handling that requires immediate attention.
+                * We check bf_status to find out if the bf's descriptors have 
+                * been processed by the HAL.
+                */
+               if (!(bf->bf_status & ATH_BUFSTATUS_DONE))
+                       break;
+               
+               ds = bf->bf_desc;
+               if (ds->ds_link == bf->bf_daddr) {
+                       /* NB: never process the self-linked entry at the end */
+                       break;
+               }
+               skb = bf->bf_skb;
+               if (skb == NULL) {              /* XXX ??? can this happen */
+                       printk("%s: no skbuff (%s)\n", dev->name, __func__);
+                       continue;
+               }
+
+#ifdef AR_DEBUG
+               if (sc->sc_debug & ATH_DEBUG_RECV_DESC)
+                       ath_printrxbuf(bf, 1);
+#endif
+
+               if (ds->ds_rxstat.rs_more) {
+                       /*
+                        * Frame spans multiple descriptors; this
+                        * cannot happen yet as we don't support
+                        * jumbograms.  If not in monitor mode,
+&nb