- Port ath(4) driver from FreeBSD, which supports various Atheros chip based
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Thu, 13 Jul 2006 09:15:22 +0000 (09:15 +0000)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Thu, 13 Jul 2006 09:15:22 +0000 (09:15 +0000)
  WiFi NIC
- Hook ath(4) into module building
- Add ath(4) and ath_hal(4) manpage
  With-help-from: swildner

Thank Sam Leffler and many other people for their greate work, also thank
Sam Leffler for his kind explaining about some caveats concerning ath_hal(4).

An early version of this driver, which works under DragonFly, was submitted by:
Andrew Atrens <atrens@nortel.com>
and was later modified to work on DragonFly 1.4.x and resubmitted by:
Adrian Michael Nida <nida@musc.edu>

Thank them for their submission.

Obtained-from: FreeBSD (mainly sam@freebsd.org)
Tested-by: many
Approved-by: dillon
23 files changed:
share/man/man4/Makefile
share/man/man4/ath.4 [new file with mode: 0644]
share/man/man4/ath_hal.4 [new file with mode: 0644]
sys/dev/netif/Makefile
sys/dev/netif/ath/Makefile [new file with mode: 0644]
sys/dev/netif/ath/Makefile.inc [new file with mode: 0644]
sys/dev/netif/ath/ath/Makefile [new file with mode: 0644]
sys/dev/netif/ath/ath/if_ath.c [new file with mode: 0644]
sys/dev/netif/ath/ath/if_ath_pci.c [new file with mode: 0644]
sys/dev/netif/ath/ath/if_athioctl.h [new file with mode: 0644]
sys/dev/netif/ath/ath/if_athrate.h [new file with mode: 0644]
sys/dev/netif/ath/ath/if_athvar.h [new file with mode: 0644]
sys/dev/netif/ath/hal/Makefile [new file with mode: 0644]
sys/dev/netif/ath/hal/ah_osdep.c.patch [new file with mode: 0644]
sys/dev/netif/ath/rate_amrr/Makefile [new file with mode: 0644]
sys/dev/netif/ath/rate_amrr/amrr.c [new file with mode: 0644]
sys/dev/netif/ath/rate_amrr/amrr.h [new file with mode: 0644]
sys/dev/netif/ath/rate_onoe/Makefile [new file with mode: 0644]
sys/dev/netif/ath/rate_onoe/onoe.c [new file with mode: 0644]
sys/dev/netif/ath/rate_onoe/onoe.h [new file with mode: 0644]
sys/dev/netif/ath/rate_sample/Makefile [new file with mode: 0644]
sys/dev/netif/ath/rate_sample/sample.c [new file with mode: 0644]
sys/dev/netif/ath/rate_sample/sample.h [new file with mode: 0644]

index e7079c1..cf4c066 100644 (file)
@@ -1,6 +1,6 @@
 #      @(#)Makefile    8.1 (Berkeley) 6/18/93
 # $FreeBSD: src/share/man/man4/Makefile,v 1.83.2.66 2003/06/04 17:10:30 sam Exp $
-# $DragonFly: src/share/man/man4/Makefile,v 1.40 2006/07/06 16:17:20 swildner Exp $
+# $DragonFly: src/share/man/man4/Makefile,v 1.41 2006/07/13 09:15:21 sephe Exp $
 
 MAN=   aac.4 \
        acpi.4 \
@@ -19,6 +19,8 @@ MAN=  aac.4 \
        an.4 \
        ata.4 \
        atapicam.4 \
+       ath.4 \
+       ath_hal.4 \
        atkbd.4 \
        atkbdc.4 \
        aue.4 \
diff --git a/share/man/man4/ath.4 b/share/man/man4/ath.4
new file mode 100644 (file)
index 0000000..687dee9
--- /dev/null
@@ -0,0 +1,293 @@
+.\"-
+.\" Copyright (c) 2002-2004 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.
+.\"
+.\" NO WARRANTY
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+.\" AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+.\" THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+.\" OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+.\" IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+.\" THE POSSIBILITY OF SUCH DAMAGES.
+.\"
+.\" $FreeBSD: /repoman/r/ncvs/src/share/man/man4/ath.4,v 1.40 2006/05/12 17:58:11 keramida Exp $
+.\" $DragonFly: src/share/man/man4/ath.4,v 1.1 2006/07/13 09:15:21 sephe Exp $
+.\"
+.Dd July 13, 2006
+.Dt ATH 4
+.Os
+.Sh NAME
+.Nm ath
+.Nd "Atheros IEEE 802.11 wireless network driver"
+.Sh SYNOPSIS
+To load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+if_ath_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for wireless network adapters based on
+the Atheros AR5210, AR5211, and AR5212 programming APIs.
+These APIs are used by a wide variety of chips; all chips with
+a PCI and/or CardBus interface are supported.
+Chip-specific support is provided by the Atheros Hardware Access Layer
+(HAL), that is packaged separately.
+.Pp
+Supported features include 802.11 and 802.3 frames, power management, BSS,
+IBSS, and host-based access point operation modes.
+All host/device interaction is via DMA.
+.Pp
+The
+.Nm
+driver encapsulates all IP and ARP traffic as 802.11 frames, however
+it can receive either 802.11 or 802.3 frames.
+Transmit speed and operating mode is selectable
+and depends on the specific chipset.
+AR5210-based devices support 802.11a operation with transmit speeds
+of 6 Mbps, 9 Mbps, 12 Mbps, 18 Mbps, 24 Mbps, 36 Mbps, 48 Mbps, and 54 Mbps.
+AR5211-based devices support 802.11a and 802.11b operation with transmit
+speeds as above for 802.11a operation and
+1Mbps, 2Mbps, 5.5 Mbps and 11Mbps for 802.11b operation.
+AR5212-based devices support 802.11a, 802.11b, and 802.11g operation
+with transmit speeds appropriate to each.
+Most chips also support an Atheros Turbo Mode (TM) that operates in
+the 5Ghz frequency range with 2x the transmit speeds.
+Some chips also support Turbo mode in the 2.4Ghz range with 802.11g
+though this support is not presently available due to regulatory requirements.
+(Note that Turbo modes are, however,
+only interoperable with other Atheros-based devices.)
+The actual transmit speed used is dependent on signal quality and the
+.Dq "rate control"
+algorithm employed by the driver.
+All chips support WEP encryption.
+The AR5212 has hardware support for the AES-CCM, TKIP, and Michael cryptographic
+operations required for WPA.
+To enable encryption, use
+.Xr ifconfig 8
+as shown below.
+.Pp
+By default, the
+.Nm
+driver configures the card for BSS operation (aka infrastructure
+mode).
+This mode requires the use of an access point (base station).
+.Pp
+The
+.Nm
+driver also supports the standard IBSS point-to-point mode
+where stations can communicate amongst themselves without the
+aid of an access point.
+.Pp
+The driver may also be configured to operate in hostap mode.
+In this mode a host may function as an access point (base station).
+Access points are different than operating in IBSS mode.
+They operate in BSS mode.
+They allow for easier roaming and bridge all Ethernet traffic such
+that machines connected via an access point appear to be on the local
+Ethernet segment.
+.Pp
+For more information on configuring this device, see
+.Xr ifconfig 8 .
+.Pp
+Devices supported by the
+.Nm
+driver come in either Cardbus or mini-PCI packages.
+Wireless cards in Cardbus slots may be inserted and ejected on the fly.
+.Sh HARDWARE
+The
+.Nm
+driver supports all Atheros Cardbus or PCI cards,
+except those that are based on the AR5005VL chipset.
+.Pp
+A list of cards that are supported can be found at
+.Pp
+.Pa http://customerproducts.atheros.com/customerproducts .
+.Sh EXAMPLES
+Join an existing BSS network (ie: connect to an access point):
+.Pp
+.Dl "ifconfig ath0 inet 192.168.0.20 netmask 0xffffff00"
+.Pp
+Join a specific BSS network with network name
+.Dq Li my_net :
+.Pp
+.Dl "ifconfig ath0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net"
+.Pp
+Join a specific BSS network with WEP encryption:
+.Bd -literal -offset indent
+ifconfig ath0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
+       wepmode on wepkey 0x8736639624
+.Ed
+.Pp
+Join/create an 802.11b IBSS network with network name
+.Dq Li my_net :
+.Bd -literal -offset indent
+ifconfig ath0 inet 192.168.0.22 netmask 0xffffff00 ssid my_net \e
+       mode 11b mediaopt adhoc
+.Ed
+.Pp
+.\"Create an 802.11g host-based access point:
+.\".Bd -literal -offset indent
+.\"ifconfig ath0 inet 192.168.0.10 netmask 0xffffff00 ssid my_ap \e
+.\"    mode 11g mediaopt hostap
+.\".Ed
+.\".Pp
+.\"Create an 802.11a host-based access point with WEP enabled:
+.\".Bd -literal -offset indent
+.\"ifconfig ath0 inet 192.168.0.10 netmask 0xffffff00 ssid my_ap \e
+.\"    wepmode on wepkey 0x1234567890 mode 11a mediaopt hostap
+.\".Ed
+.\".Pp
+.\"Create a host-based wireless bridge to fxp0:
+.\".Bd -literal -offset indent
+.\"ifconfig ath0 inet up ssid my_ap media DS/11Mbps mediaopt hostap
+.\"sysctl net.inet.ip.check_interface=0
+.\"ifconfig bridge0 create
+.\"ifconfig bridge0 addm ath0 addm fxp0 up
+.\".Ed
+.\".Pp
+.\"This will give you the same functionality as an access point.
+.Sh DIAGNOSTICS
+.Bl -diag
+.It "ath%d: unable to attach hardware; HAL status %u"
+The Atheros Hardware Access Layer was unable to configure the hardware
+as requested.
+The status code is explained in the HAL include file
+.Pa sys/contrib/dev/ath/ah.h .
+.It "ath%d: failed to allocate descriptors: %d"
+The driver was unable to allocate contiguous memory for the transmit
+and receive descriptors.
+This usually indicates system memory is scarce and/or fragmented.
+.It "ath%d: unable to setup a data xmit queue!"
+The request to the HAL to set up the transmit queue for normal
+data frames failed.
+This should not happen.
+.It "ath%d: unable to setup a beacon xmit queue!"
+The request to the HAL to set up the transmit queue for 802.11 beacon frames
+frames failed.
+This should not happen.
+.It "ath%d: 802.11 address: %s"
+The MAC address programmed in the EEPROM is displayed.
+.It "ath%d: hardware error; resetting"
+An unrecoverable error in the hardware occurred.
+Errors of this sort include unrecoverable DMA errors.
+The driver will reset the hardware and continue.
+.It "ath%d: rx FIFO overrun; resetting"
+The receive FIFO in the hardware overflowed before the data could be
+transferred to the host.
+This typically occurs because the hardware ran short of receive
+descriptors and had no place to transfer received data.
+The driver will reset the hardware and continue.
+.It "ath%d: unable to reset hardware; hal status %u"
+The Atheros Hardware Access Layer was unable to reset the hardware
+as requested.
+The status code is explained in the HAL include file
+.Pa sys/contrib/dev/ath/ah.h .
+This should not happen.
+.It "ath%d: unable to start recv logic"
+The driver was unable to restart frame reception.
+This should not happen.
+.It "ath%d: device timeout"
+A frame dispatched to the hardware for transmission did not complete in time.
+The driver will reset the hardware and continue.
+This should not happen.
+.It "ath%d: bogus xmit rate 0x%x"
+An invalid transmit rate was specified for an outgoing frame.
+The frame is discarded.
+This should not happen.
+.It "ath%d: ath_chan_set: unable to reset channel %u (%u Mhz)"
+The Atheros Hardware Access Layer was unable to reset the hardware
+when switching channels during scanning.
+This should not happen.
+.It "ath%d: unable to allocate channel table"
+The driver was unable to allocate memory for the table used to hold
+the set of available channels.
+.It "ath%d: unable to collect channel list from hal"
+A problem occurred while querying the HAL to find the set of available
+channels for the device.
+This should not happen.
+.It "ath%d: failed to enable memory mapping"
+The driver was unable to enable memory-mapped I/O to the PCI device registers.
+This should not happen.
+.It "ath%d: failed to enable bus mastering"
+The driver was unable to enable the device as a PCI bus master for doing DMA.
+This should not happen.
+.It "ath%d: cannot map register space"
+The driver was unable to map the device registers into the host address space.
+This should not happen.
+.It "ath%d: could not map interrupt"
+The driver was unable to allocate an IRQ for the device interrupt.
+This should not happen.
+.It "ath%d: could not establish interrupt"
+The driver was unable to install the device interrupt handler.
+This should not happen.
+.El
+.Sh SEE ALSO
+.Xr an 4 ,
+.Xr arp 4 ,
+.Xr ath_hal 4 ,
+.Xr netintro 4 ,
+.Xr wi 4 ,
+.Xr wlan 4 ,
+.Xr ifconfig 8 ,
+.Xr wicontrol 8
+.Sh HISTORY
+The
+.Nm
+device driver first appeared in
+.Fx 5.2
+and was imported into
+.Dx 1.5 .
+.Sh CAVEATS
+Different regulatory domains have different default channels for adhoc
+mode.
+See
+.Xr ifconfig 8
+for information on how to change the channel.
+See
+.Xr wicontrol 8
+for information on different regulatory domains.
+Different regulatory domains may not be able to communicate with each
+other with 802.11a as different regulatory domains do not necessarily
+have overlapping channels.
+.Pp
+Revision A1 of the D-LINK DWL-G520 and DWL-G650 are based on an
+Intersil PrismGT chip and are not supported by this driver.
+.Sh BUGS
+There is no software retransmit; only hardware retransmit is used.
+.Pp
+The driver does not fully enable power-save operation of the chip;
+consequently power use is suboptimal.
+.Pp
+The driver honors the regulatory domain programmed into the EEPROM of a
+device and does not support overriding this setting.
+This is done to insure compliance with local regulatory agencies when
+operating as an access point.
+Unfortunately this also means that devices purchased for use in one locale
+may not be usable in another.
+Changes are planned to remove this restriction when operating in station mode.
+.Pp
+WPA is not supported for 5210 parts.
+.Pp
+Atheros' SuperG functionality is not supported.
diff --git a/share/man/man4/ath_hal.4 b/share/man/man4/ath_hal.4
new file mode 100644 (file)
index 0000000..1b4ced0
--- /dev/null
@@ -0,0 +1,130 @@
+.\"-
+.\" Copyright (c) 2002, 2003 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.
+.\"
+.\" NO WARRANTY
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+.\" AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+.\" THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+.\" OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+.\" IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+.\" THE POSSIBILITY OF SUCH DAMAGES.
+.\"
+.\" $FreeBSD: /repoman/r/ncvs/src/share/man/man4/ath_hal.4,v 1.11 2005/01/15 12:49:11 ru Exp $
+.\" $DragonFly: src/share/man/man4/ath_hal.4,v 1.1 2006/07/13 09:15:21 sephe Exp $
+.\"
+.Dd July 13, 2006
+.Dt ATH_HAL 4
+.Os
+.Sh NAME
+.Nm ath_hal
+.Nd "Atheros Hardware Access Layer (HAL)"
+.Sh SYNOPSIS
+To load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+ath_hal_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+module provides hardware support for wireless network adapters based on
+the Atheros AR5210, AR5211, and AR5212 chips.
+This module is required by the
+.Xr ath 4
+driver.
+.Pp
+Devices supported by the
+.Nm
+module come in either Cardbus or mini-PCI packages.
+.Sh HARDWARE
+The following cards are among those supported by the
+.Nm
+module:
+.Pp
+.Bl -column -compact "Samsung SWL-5200N" "AR5212" "Cardbus" "a/b/g"
+.It Em "Card   Chip    Bus     Standard"
+.It "Aztech WL830PC    AR5212  CardBus b/g"
+.It "D-Link DWL-A650   AR5210  CardBus a"
+.It "D-Link DWL-AB650  AR5211  CardBus a/b"
+.It "D-Link DWL-A520   AR5210  PCI     a"
+.It "D-Link DWL-AG520  AR5212  PCI     a/b/g"
+.It "D-Link DWL-AG650  AR5212  CardBus a/b/g"
+.It "D-Link DWL-G520B  AR5212  PCI     b/g"
+.It "D-Link DWL-G650B  AR5212  CardBus b/g"
+.It "Elecom LD-WL54AG  AR5212  Cardbus a/b/g"
+.It "Elecom LD-WL54    AR5211  Cardbus a"
+.It "Fujitsu E5454     AR5212  Cardbus a/b/g"
+.It "Fujitsu FMV-JW481 AR5212  Cardbus a/b/g"
+.It "Fujitsu E5454     AR5212  Cardbus a/b/g"
+.It "HP NC4000 AR5212  PCI     a/b/g"
+.It "I/O Data WN-AB    AR5212  CardBus a/b"
+.It "I/O Data WN-AG    AR5212  CardBus a/b/g"
+.It "I/O Data WN-A54   AR5212  CardBus a"
+.It "Linksys WMP55AG   AR5212  PCI     a/b/g"
+.It "Linksys WPC51AB   AR5211  CardBus a/b"
+.It "Linksys WPC55AG   AR5212  CardBus a/b/g"
+.It "NEC PA-WL/54AG    AR5212  CardBus a/b/g"
+.It "Netgear WAG311    AR5212  PCI     a/b/g"
+.It "Netgear WAB501    AR5211  CardBus a/b"
+.It "Netgear WAG511    AR5212  CardBus a/b/g"
+.It "Netgear WG311     AR5212  PCI     b/g"
+.It "Netgear WG511T    AR5212  CardBus b/g"
+.It "Orinoco 8480      AR5212  CardBus a/b/g"
+.It "Orinoco 8470WD    AR5212  CardBus a/b/g"
+.It "Proxim Skyline 4030       AR5210  CardBus a"
+.It "Proxim Skyline 4032       AR5210  PCI     a"
+.It "Samsung SWL-5200N AR5212  CardBus a/b/g"
+.It "SMC SMC2735W      AR5210  CardBus a"
+.It "Sony PCWA-C700    AR5212  Cardbus a/b"
+.It "Sony PCWA-C300S   AR5212  Cardbus b/g"
+.It "Sony PCWA-C500    AR5210  Cardbus a"
+.It "3Com 3CRPAG175    AR5212  CardBus a/b/g"
+.El
+.Pp
+An up to date list can be found at
+.Pp
+.Pa http://customerproducts.atheros.com/customerproducts .
+.Sh SEE ALSO
+.Xr ath 4
+.Sh HISTORY
+The
+.Nm
+module first appeared in
+.Fx 5.2
+and was imported into
+.Dx 1.5 .
+.Sh CAVEATS
+The
+.Nm
+module is constructed from a binary component and
+operating system-dependent source code.
+Redistribution and use in source and binary forms, without
+modification, are permitted provided that the conditions
+set forth in
+.Pa sys/contrib/dev/ath/COPYRIGHT
+are observed.
+.Sh BUGS
+See
+.Xr ath 4
+for known bugs.
index d459f48..aef4d8b 100644 (file)
@@ -1,8 +1,8 @@
-# $DragonFly: src/sys/dev/netif/Makefile,v 1.19 2006/05/20 09:13:09 sephe Exp $
+# $DragonFly: src/sys/dev/netif/Makefile,v 1.20 2006/07/13 09:15:22 sephe Exp $
 #
 
-SUBDIR= an acx ar aue axe bfe bge cue dc ed em ep fwe fxp gx ipw iwi kue lge \
-       lnc mii_layer my nge nv pcn ral ray re rl rue sbni sbsh sf sis sk \
+SUBDIR= an acx ar ath aue axe bfe bge cue dc ed em ep fwe fxp gx ipw iwi kue \
+       lge lnc mii_layer my nge nv pcn ral ray re rl rue sbni sbsh sf sis sk \
        sr ste ti tl tx txp vge vr vx wb wi xe xl
 
 .include <bsd.subdir.mk>
diff --git a/sys/dev/netif/ath/Makefile b/sys/dev/netif/ath/Makefile
new file mode 100644 (file)
index 0000000..cc63b07
--- /dev/null
@@ -0,0 +1,10 @@
+# $DragonFly: src/sys/dev/netif/ath/Makefile,v 1.1 2006/07/13 09:15:22 sephe Exp $
+
+SUBDIR = hal ath
+
+# Choose the desired rate below
+SUBDIR += rate_sample
+#SUBDIR        += rate_amrr
+#SUBDIR        += rate_onoe 
+
+.include <bsd.subdir.mk>
diff --git a/sys/dev/netif/ath/Makefile.inc b/sys/dev/netif/ath/Makefile.inc
new file mode 100644 (file)
index 0000000..5350fe0
--- /dev/null
@@ -0,0 +1,5 @@
+# $DragonFly: src/sys/dev/netif/ath/Makefile.inc,v 1.1 2006/07/13 09:15:22 sephe Exp $
+
+HAL    = ${.CURDIR}/../../../../contrib/dev/ath
+CONTRIBDIR = ${HAL}/dragonfly
+CFLAGS += -I${CONTRIBDIR}
diff --git a/sys/dev/netif/ath/ath/Makefile b/sys/dev/netif/ath/ath/Makefile
new file mode 100644 (file)
index 0000000..1de32a5
--- /dev/null
@@ -0,0 +1,46 @@
+#
+# Copyright (c) 2002, 2003 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.
+#
+# $FreeBSD: src/sys/modules/ath/Makefile,v 1.1.10.1 2006/04/04 23:55:33 sam Exp $
+# $DragonFly: src/sys/dev/netif/ath/ath/Makefile,v 1.1 2006/07/13 09:15:22 sephe Exp $
+#
+
+KMOD   = if_ath
+SRCS   = if_ath.c if_ath_pci.c
+SRCS   += device_if.h bus_if.h pci_if.h opt_ath.h
+
+KMODDEPS = ath_hal ath_rate
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/netif/ath/ath/if_ath.c b/sys/dev/netif/ath/ath/if_ath.c
new file mode 100644 (file)
index 0000000..81fbfaf
--- /dev/null
@@ -0,0 +1,5580 @@
+/*
+ * 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.
+ *
+ * $FreeBSD: src/sys/dev/ath/if_ath.c,v 1.94.2.17 2006/04/19 16:14:47 sam Exp $
+ * $DragonFly: src/sys/dev/netif/ath/ath/if_ath.c,v 1.1 2006/07/13 09:15:22 sephe Exp $
+ */
+
+/*
+ * Driver for the Atheros Wireless LAN controller.
+ *
+ * This software is derived from work of Atsushi Onoe; his contribution
+ * is greatly appreciated.
+ */
+
+#include "opt_ath.h"
+
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/sysctl.h>
+#include <sys/mbuf.h>   
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/errno.h>
+#include <sys/callout.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kthread.h>
+#include <sys/serialize.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_llc.h>
+#include <net/ifq_var.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+#define ATH_DEBUG
+
+#include <dev/netif/ath/ath/if_athvar.h>
+#include <contrib/dev/ath/ah_desc.h>
+#include <contrib/dev/ath/ah_devid.h>          /* XXX for softled */
+
+/* unaligned little endian access */
+#define LE_READ_2(p)                                                   \
+       ((uint16_t)                                                     \
+        ((((uint8_t *)(p))[0]      ) | (((uint8_t *)(p))[1] <<  8)))
+#define LE_READ_4(p)                                                   \
+       ((uint32_t)                                                     \
+        ((((uint8_t *)(p))[0]      ) | (((uint8_t *)(p))[1] <<  8) |   \
+         (((uint8_t *)(p))[2] << 16) | (((uint8_t *)(p))[3] << 24)))
+
+enum {
+       ATH_LED_TX,
+       ATH_LED_RX,
+       ATH_LED_POLL,
+};
+
+static void    ath_init(void *);
+static void    ath_stop_no_pwchg(struct ifnet *);
+static void    ath_stop(struct ifnet *);
+static void    ath_start(struct ifnet *);
+static int     ath_reset(struct ifnet *);
+static int     ath_media_change(struct ifnet *);
+static void    ath_watchdog(struct ifnet *);
+static int     ath_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *);
+static void    ath_fatal_proc(struct ath_softc *);
+static void    ath_rxorn_proc(struct ath_softc *);
+static void    ath_bmiss_proc(struct ath_softc *);
+static void    ath_radar_proc(struct ath_softc *);
+static int     ath_key_alloc(struct ieee80211com *,
+                       const struct ieee80211_key *,
+                       ieee80211_keyix *, ieee80211_keyix *);
+static int     ath_key_delete(struct ieee80211com *,
+                       const struct ieee80211_key *);
+static int     ath_key_set(struct ieee80211com *, const struct ieee80211_key *,
+                       const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void    ath_key_update_begin(struct ieee80211com *);
+static void    ath_key_update_end(struct ieee80211com *);
+static void    ath_mode_init(struct ath_softc *);
+static void    ath_setslottime(struct ath_softc *);
+static void    ath_updateslot(struct ifnet *);
+static int     ath_beaconq_setup(struct ath_hal *);
+static int     ath_beacon_alloc(struct ath_softc *, struct ieee80211_node *);
+static void    ath_beacon_setup(struct ath_softc *, struct ath_buf *);
+static void    ath_beacon_proc(struct ath_softc *);
+static void    ath_bstuck_proc(struct ath_softc *);
+static void    ath_beacon_free(struct ath_softc *);
+static void    ath_beacon_config(struct ath_softc *);
+static void    ath_descdma_cleanup(struct ath_softc *sc,
+                       struct ath_descdma *, ath_bufhead *);
+static int     ath_desc_alloc(struct ath_softc *);
+static void    ath_desc_free(struct ath_softc *);
+static struct ieee80211_node *ath_node_alloc(struct ieee80211_node_table *);
+static void    ath_node_free(struct ieee80211_node *);
+static uint8_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 ieee80211com *ic, struct mbuf *m,
+                       struct ieee80211_node *ni,
+                       int subtype, int rssi, uint32_t rstamp);
+static void    ath_setdefantenna(struct ath_softc *, u_int);
+static void    ath_rx_proc(struct ath_softc *);
+static struct ath_txq *ath_txq_setup(struct ath_softc*, int qtype, int subtype);
+static int     ath_tx_setup(struct ath_softc *, int, int);
+static int     ath_wme_update(struct ieee80211com *);
+static void    ath_tx_cleanupq(struct ath_softc *, struct ath_txq *);
+static void    ath_tx_cleanup(struct ath_softc *);
+static int     ath_tx_start(struct ath_softc *, struct ieee80211_node *,
+                            struct ath_buf *, struct mbuf *);
+static void    ath_tx_proc_q0(struct ath_softc *);
+static void    ath_tx_proc_q0123(struct ath_softc *);
+static void    ath_tx_proc(struct ath_softc *);
+static int     ath_chan_set(struct ath_softc *, struct ieee80211_channel *);
+static void    ath_draintxq(struct ath_softc *);
+static void    ath_stoprecv(struct ath_softc *);
+static int     ath_startrecv(struct ath_softc *);
+static void    ath_chan_change(struct ath_softc *, struct ieee80211_channel *);
+static void    ath_next_scan(void *);
+static void    ath_calibrate(void *);
+static int     ath_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static void    ath_setup_stationkey(struct ieee80211_node *);
+static void    ath_newassoc(struct ieee80211_node *, int);
+static int     ath_getchannels(struct ath_softc *, u_int cc,
+                       HAL_BOOL outdoor, HAL_BOOL xchanmode);
+static void    ath_led_event(struct ath_softc *, int);
+static void    ath_update_txpow(struct ath_softc *);
+
+static int     ath_rate_setup(struct ath_softc *, u_int mode);
+static void    ath_setcurmode(struct ath_softc *, enum ieee80211_phymode);
+
+static void    ath_sysctlattach(struct ath_softc *);
+static void    ath_bpfattach(struct ath_softc *);
+static void    ath_announce(struct ath_softc *);
+
+static void    ath_dma_map_mbuf(void *, bus_dma_segment_t *, int, bus_size_t,
+                                int);
+
+SYSCTL_DECL(_hw_ath);
+
+/* XXX validate sysctl values */
+static int ath_dwelltime = 200;                /* 5 channels/second */
+SYSCTL_INT(_hw_ath, OID_AUTO, dwell, CTLFLAG_RW, &ath_dwelltime,
+           0, "channel dwell time (ms) for AP/station scanning");
+static int ath_calinterval = 30;               /* calibrate every 30 secs */
+SYSCTL_INT(_hw_ath, OID_AUTO, calibrate, CTLFLAG_RW, &ath_calinterval,
+           0, "chip calibration interval (secs)");
+static int ath_outdoor = AH_TRUE;              /* outdoor operation */
+SYSCTL_INT(_hw_ath, OID_AUTO, outdoor, CTLFLAG_RD, &ath_outdoor,
+           0, "outdoor operation");
+TUNABLE_INT("hw.ath.outdoor", &ath_outdoor);
+static int ath_xchanmode = AH_TRUE;            /* extended channel use */
+SYSCTL_INT(_hw_ath, OID_AUTO, xchanmode, CTLFLAG_RD, &ath_xchanmode,
+           0, "extended channel mode");
+TUNABLE_INT("hw.ath.xchanmode", &ath_xchanmode);
+static int ath_countrycode = CTRY_DEFAULT;     /* country code */
+SYSCTL_INT(_hw_ath, OID_AUTO, countrycode, CTLFLAG_RD, &ath_countrycode,
+           0, "country code");
+TUNABLE_INT("hw.ath.countrycode", &ath_countrycode);
+static int ath_regdomain = 0;                  /* regulatory domain */
+SYSCTL_INT(_hw_ath, OID_AUTO, regdomain, CTLFLAG_RD, &ath_regdomain,
+           0, "regulatory domain");
+
+static int ath_rxbuf = ATH_RXBUF;              /* # rx buffers to allocate */
+SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RD, &ath_rxbuf,
+           0, "rx buffers allocated");
+TUNABLE_INT("hw.ath.rxbuf", &ath_rxbuf);
+static int ath_txbuf = ATH_TXBUF;              /* # tx buffers to allocate */
+SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RD, &ath_txbuf,
+           0, "tx buffers allocated");
+TUNABLE_INT("hw.ath.txbuf", &ath_txbuf);
+
+#ifdef ATH_DEBUG
+static int ath_debug = 0;
+SYSCTL_INT(_hw_ath, OID_AUTO, debug, CTLFLAG_RW, &ath_debug,
+           0, "control debugging printfs");
+TUNABLE_INT("hw.ath.debug", &ath_debug);
+enum {
+       ATH_DEBUG_XMIT          = 0x00000001,   /* basic xmit operation */
+       ATH_DEBUG_XMIT_DESC     = 0x00000002,   /* xmit descriptors */
+       ATH_DEBUG_RECV          = 0x00000004,   /* basic recv operation */
+       ATH_DEBUG_RECV_DESC     = 0x00000008,   /* recv descriptors */
+       ATH_DEBUG_RATE          = 0x00000010,   /* rate control */
+       ATH_DEBUG_RESET         = 0x00000020,   /* reset processing */
+       ATH_DEBUG_MODE          = 0x00000040,   /* mode init/setup */
+       ATH_DEBUG_BEACON        = 0x00000080,   /* beacon handling */
+       ATH_DEBUG_WATCHDOG      = 0x00000100,   /* watchdog timeout */
+       ATH_DEBUG_INTR          = 0x00001000,   /* ISR */
+       ATH_DEBUG_TX_PROC       = 0x00002000,   /* tx ISR proc */
+       ATH_DEBUG_RX_PROC       = 0x00004000,   /* rx ISR proc */
+       ATH_DEBUG_BEACON_PROC   = 0x00008000,   /* beacon ISR proc */
+       ATH_DEBUG_CALIBRATE     = 0x00010000,   /* periodic calibration */
+       ATH_DEBUG_KEYCACHE      = 0x00020000,   /* key cache management */
+       ATH_DEBUG_STATE         = 0x00040000,   /* 802.11 state transitions */
+       ATH_DEBUG_NODE          = 0x00080000,   /* node management */
+       ATH_DEBUG_LED           = 0x00100000,   /* led management */
+       ATH_DEBUG_FF            = 0x00200000,   /* fast frames */
+       ATH_DEBUG_DFS           = 0x00400000,   /* DFS processing */
+       ATH_DEBUG_FATAL         = 0x80000000,   /* fatal errors */
+       ATH_DEBUG_ANY           = 0xffffffff
+};
+#define        IFF_DUMPPKTS(sc, m) \
+       ((sc->sc_debug & (m)) || \
+        (sc->sc_ic.ic_if.if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2))
+#define        DPRINTF(sc, m, fmt, ...) do {                           \
+       if (sc->sc_debug & (m))                                 \
+               printf(fmt, __VA_ARGS__);                       \
+} while (0)
+#define        KEYPRINTF(sc, ix, hk, mac) do {                         \
+       if (sc->sc_debug & ATH_DEBUG_KEYCACHE)                  \
+               ath_keyprint(__func__, ix, hk, mac);            \
+} while (0)
+static void ath_printrxbuf(struct ath_buf *bf, u_int ix, int);
+static void ath_printtxbuf(struct ath_buf *bf, u_int qnum, u_int ix, int done);
+#else
+#define        IFF_DUMPPKTS(sc, m) \
+       ((sc->sc_ic.ic_if.if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2))
+#define        DPRINTF(sc, m, fmt, ...) do {                           \
+       (void) sc;                                              \
+} while (0)
+#define        KEYPRINTF(sc, k, ix, mac) do {                          \
+       (void) sc;                                              \
+} while (0)
+#endif
+
+MALLOC_DEFINE(M_ATHDEV, "athdev", "ath driver dma buffers");
+
+int
+ath_attach(uint16_t devid, struct ath_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+       struct ath_hal *ah = NULL;
+       HAL_STATUS status;
+       int error = 0, i;
+
+       DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid);
+
+       /* set these up early for if_printf use */
+       if_initname(ifp, device_get_name(sc->sc_dev),
+                   device_get_unit(sc->sc_dev));
+
+       /*
+        * Mark device invalid so any interrupts (shared or otherwise)
+        * that arrive before the HAL is setup are discarded.
+        */
+       sc->sc_invalid = 1;
+
+       /*
+        * Arrange interrupt line.
+        */
+       sc->sc_irq_rid = 0;
+       sc->sc_irq = bus_alloc_resource_any(sc->sc_dev, SYS_RES_IRQ,
+                                           &sc->sc_irq_rid,
+                                           RF_SHAREABLE | RF_ACTIVE);
+       if (sc->sc_irq == NULL) {
+               if_printf(ifp, "could not map interrupt\n");
+               return ENXIO;
+       }
+
+       sysctl_ctx_init(&sc->sc_sysctl_ctx);
+       sc->sc_sysctl_tree = SYSCTL_ADD_NODE(&sc->sc_sysctl_ctx,
+                                            SYSCTL_STATIC_CHILDREN(_hw),
+                                            OID_AUTO,
+                                            device_get_nameunit(sc->sc_dev),
+                                            CTLFLAG_RD, 0, "");
+       if (sc->sc_sysctl_tree == NULL) {
+               if_printf(ifp, "could not add sysctl node\n");
+               error = ENXIO;
+               goto fail;
+       }
+
+       ah = ath_hal_attach(devid, sc, sc->sc_st, sc->sc_sh, &status);
+       if (ah == NULL) {
+               if_printf(ifp, "unable to attach hardware; HAL status %u\n",
+                       status);
+               error = ENXIO;
+               goto fail;
+       }
+       sc->sc_ah = ah;
+
+       if (ah->ah_abi != HAL_ABI_VERSION) {
+               if_printf(ifp, "HAL ABI mismatch detected "
+                       "(HAL:0x%x != driver:0x%x)\n",
+                       ah->ah_abi, HAL_ABI_VERSION);
+               error = ENXIO;
+               goto fail;
+       }
+       sc->sc_invalid = 0;     /* ready to go, enable interrupt handling */
+
+       /*
+        * 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) {
+               if_printf(ifp, "Warning, using only %u of %u key cache slots\n",
+                       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);
+       /*
+        * Mark key cache slots associated with global keys
+        * as in use.  If we knew TKIP was not to be used we
+        * could leave the +32, +64, and +32+64 slots free.
+        * XXX only for splitmic.
+        */
+       for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+               setbit(sc->sc_keymap, i);
+               setbit(sc->sc_keymap, i+32);
+               setbit(sc->sc_keymap, i+64);
+               setbit(sc->sc_keymap, i+32+64);
+       }
+
+       /*
+        * Collect the channel list using the default country
+        * code and including outdoor channels.  The 802.11 layer
+        * is resposible for filtering this list based on settings
+        * like the phy mode.
+        */
+       error = ath_getchannels(sc, ath_countrycode,
+                               ath_outdoor, ath_xchanmode);
+       if (error)
+               goto fail;
+
+       /*
+        * Setup rate tables for all potential media types.
+        */
+       ath_rate_setup(sc, IEEE80211_MODE_11A);
+       ath_rate_setup(sc, IEEE80211_MODE_11B);
+       ath_rate_setup(sc, IEEE80211_MODE_11G);
+       ath_rate_setup(sc, IEEE80211_MODE_TURBO_A);
+       ath_rate_setup(sc, IEEE80211_MODE_TURBO_G);
+
+       /* 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) {
+               if_printf(ifp, "failed to allocate descriptors: %d\n", error);
+               goto fail;
+       }
+
+       callout_init(&sc->sc_scan_ch);
+       callout_init(&sc->sc_cal_ch);
+       callout_init(&sc->sc_dfs_ch);
+
+       /*
+        * Allocate hardware transmit queues: one queue for
+        * beacon frames and one data queue for each QoS
+        * priority.  Note that the hal handles reseting
+        * these queues at the needed time.
+        *
+        * XXX PS-Poll
+        */
+       sc->sc_bhalq = ath_beaconq_setup(ah);
+       if (sc->sc_bhalq == (u_int)-1) {
+               if_printf(ifp, "unable to setup a beacon xmit queue!\n");
+               error = EIO;
+               goto fail;
+       }
+
+       sc->sc_cabq = ath_txq_setup(sc, HAL_TX_QUEUE_CAB, 0);
+       if (sc->sc_cabq == NULL) {
+               if_printf(ifp, "unable to setup CAB xmit queue!\n");
+               error = EIO;
+               goto fail;
+       }
+
+       /* NB: insure BK queue is the lowest priority h/w queue */
+       if (!ath_tx_setup(sc, WME_AC_BK, HAL_WME_AC_BK)) {
+               if_printf(ifp, "unable to setup xmit queue for %s traffic!\n",
+                       ieee80211_wme_acnames[WME_AC_BK]);
+               error = EIO;
+               goto fail;
+       }
+       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];
+       }
+
+       /* 
+        * Special case certain configurations.  Note the
+        * CAB queue is handled by these specially so don't
+        * include them when checking the txq setup mask.
+        */
+       switch (sc->sc_txqsetup &~ (1<<sc->sc_cabq->axq_qnum)) {
+       case 0x01:
+               sc->sc_tx_proc = ath_tx_proc_q0;
+               if_printf(ifp, "tx_proc_q0\n");
+               break;
+       case 0x0f:
+               sc->sc_tx_proc = ath_tx_proc_q0123;
+               if_printf(ifp, "tx_proc_q0123\n");
+               break;
+       default:
+               sc->sc_tx_proc = ath_tx_proc;
+               if_printf(ifp, "tx_proc\n");
+               break;
+       }
+
+       /*
+        * Setup rate control.  Some rate control modules
+        * call back to change the anntena state so expose
+        * the necessary entry points.
+        * XXX maybe belongs in struct ath_ratectrl?
+        */
+       sc->sc_setdefantenna = ath_setdefantenna;
+       sc->sc_rc = ath_rate_attach(sc);
+       if (sc->sc_rc == NULL) {
+               error = EIO;
+               goto fail;
+       }
+
+       sc->sc_blinking = 0;
+       sc->sc_ledstate = 1;
+       sc->sc_ledon = 0;                       /* low true */
+       sc->sc_ledidle = (2700 * hz) / 1000;    /* 2.7sec */
+       callout_init(&sc->sc_ledtimer);
+
+       /*
+        * Auto-enable soft led processing for IBM cards and for
+        * 5211 minipci cards.  Users can also manually enable/disable
+        * support with a sysctl.
+        */
+       sc->sc_softled = (devid == AR5212_DEVID_IBM || devid == AR5211_DEVID);
+       if (sc->sc_softled) {
+               ath_hal_gpioCfgOutput(ah, sc->sc_ledpin);
+               ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon);
+       }
+
+       ifp->if_softc = sc;
+       ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
+       ifp->if_start = ath_start;
+       ifp->if_watchdog = ath_watchdog;
+       ifp->if_ioctl = ath_ioctl;
+       ifp->if_init = ath_init;
+       ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN);
+       ifq_set_ready(&ifp->if_snd);
+
+       ic->ic_reset = ath_reset;
+       ic->ic_newassoc = ath_newassoc;
+       ic->ic_updateslot = ath_updateslot;
+       ic->ic_wme.wme_update = ath_wme_update;
+       /* XXX not right but it's not used anywhere important */
+       ic->ic_phytype = IEEE80211_T_OFDM;
+       ic->ic_opmode = IEEE80211_M_STA;
+       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 */
+               ;
+       /*
+        * 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;
+               if (ath_hal_tkipsplit(ah))
+                       sc->sc_splitmic = 1;
+       }
+       sc->sc_hasclrkey = ath_hal_ciphersupported(ah, HAL_CIPHER_CLR);
+       sc->sc_mcastkey = ath_hal_getmcastkeysearch(ah);
+       /*
+        * 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.
+        */
+       if (ath_hal_hastpc(ah) || ath_hal_hastxpowlimit(ah))
+               ic->ic_caps |= IEEE80211_C_TXPMGT;
+
+       /*
+        * Mark WME capability only if we have sufficient
+        * hardware queues to do proper priority scheduling.
+        */
+       if (sc->sc_ac2q[WME_AC_BE] != sc->sc_ac2q[WME_AC_BK])
+               ic->ic_caps |= IEEE80211_C_WME;
+       /*
+        * Check for misc other capabilities.
+        */
+       if (ath_hal_hasbursting(ah))
+               ic->ic_caps |= IEEE80211_C_BURST;
+
+       /*
+        * 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.
+        */
+       sc->sc_defant = ath_hal_getdefantenna(ah);
+
+       /*
+        * Not all chips have the VEOL support we want to
+        * use with IBSS beacons; check here for it.
+        */
+       sc->sc_hasveol = ath_hal_hasveol(ah);
+
+       /* get mac address from hardware */
+       ath_hal_getmac(ah, ic->ic_myaddr);
+
+       /* call MI attach routine. */
+       ieee80211_ifattach(ic);
+       sc->sc_opmode = ic->ic_opmode;
+       /* 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;
+       sc->sc_recv_mgmt = ic->ic_recv_mgmt;
+       ic->ic_recv_mgmt = ath_recv_mgmt;
+       sc->sc_newstate = ic->ic_newstate;
+       ic->ic_newstate = ath_newstate;
+       ic->ic_crypto.cs_max_keyix = sc->sc_keymax;
+       ic->ic_crypto.cs_key_alloc = ath_key_alloc;
+       ic->ic_crypto.cs_key_delete = ath_key_delete;
+       ic->ic_crypto.cs_key_set = ath_key_set;
+       ic->ic_crypto.cs_key_update_begin = ath_key_update_begin;
+       ic->ic_crypto.cs_key_update_end = ath_key_update_end;
+       /* complete initialization */
+       ieee80211_media_init(ic, ath_media_change, ieee80211_media_status);
+
+       ath_bpfattach(sc);
+       /*
+        * Setup dynamic sysctl's now that country code and
+        * regdomain are available from the hal.
+        */
+       ath_sysctlattach(sc);
+
+       error = bus_setup_intr(sc->sc_dev, sc->sc_irq, INTR_MPSAFE, ath_intr,
+                              sc, &sc->sc_ih, ifp->if_serializer);
+       if (error) {
+               if_printf(ifp, "could not establish interrupt\n");
+               bpfdetach(ifp);
+               ieee80211_ifdetach(ic);
+               goto fail;
+       }
+
+       if (bootverbose)
+               ieee80211_announce(ic);
+       ath_announce(sc);
+
+       return 0;
+fail:
+       ath_detach(sc);
+       return error;
+}
+
+int
+ath_detach(struct ath_softc *sc)
+{
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+       DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n",
+               __func__, ifp->if_flags);
+
+       /* 
+        * NB: the order of these is important:
+        * o call the 802.11 layer before detaching the hal to
+        *   insure 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...
+        */
+
+       if (device_is_attached(sc->sc_dev)) {
+               lwkt_serialize_enter(ifp->if_serializer);
+
+               ath_rate_stop(sc->sc_rc);
+
+               /*
+                * It seems power changing in ath_stop() will freeze
+                * ath_hal_releasetxqueue(), which is called by
+                * ath_tx_cleanup() below.
+                */
+#if 1
+               ath_stop_no_pwchg(ifp);
+#else
+               ath_stop(ifp);
+#endif
+               bus_teardown_intr(sc->sc_dev, sc->sc_irq, sc->sc_ih);
+
+               lwkt_serialize_exit(ifp->if_serializer);
+
+               bpfdetach(ifp);
+               ieee80211_ifdetach(&sc->sc_ic);
+       }
+
+       if (sc->sc_rc != NULL)
+               ath_rate_detach(sc->sc_rc);
+
+       ath_desc_free(sc);
+
+       ath_tx_cleanup(sc);
+
+       if (sc->sc_ah)
+               ath_hal_detach(sc->sc_ah);
+
+       if (sc->sc_irq != NULL) {
+               bus_release_resource(sc->sc_dev, SYS_RES_IRQ, sc->sc_irq_rid,
+                                    sc->sc_irq);
+       }
+
+       if (sc->sc_sysctl_tree != NULL)
+               sysctl_ctx_free(&sc->sc_sysctl_ctx);
+
+       return 0;
+}
+
+void
+ath_suspend(struct ath_softc *sc)
+{
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n",
+               __func__, ifp->if_flags);
+       ath_stop(ifp);
+
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+void
+ath_resume(struct ath_softc *sc)
+{
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n",
+               __func__, ifp->if_flags);
+
+       if (ifp->if_flags & IFF_UP) {
+               ath_init(sc);
+               if (ifp->if_flags & IFF_RUNNING)
+                       ath_start(ifp);
+       }
+       if (sc->sc_softled) {
+               ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin);
+               ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon);
+       }
+
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+void
+ath_shutdown(struct ath_softc *sc)
+{
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n",
+               __func__, ifp->if_flags);
+       ath_stop(ifp);
+
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+/*
+ * Interrupt handler.  Most of the actual processing is deferred.
+ */
+void
+ath_intr(void *arg)
+{
+       struct ath_softc *sc = arg;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       struct ath_hal *ah = sc->sc_ah;
+       HAL_INT status;
+
+       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.
+                */
+               DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid; ignored\n", __func__);
+               return;
+       }
+       if (!ath_hal_intrpend(ah))              /* shared irq, not for us */
+               return;
+       if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) !=
+           (IFF_UP | IFF_RUNNING)) {
+               DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n",
+                       __func__, ifp->if_flags);
+               ath_hal_getisr(ah, &status);    /* clear ISR */
+               ath_hal_intrset(ah, 0);         /* disable further intr's */
+               return;
+       }
+       /*
+        * 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 insure 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) {
+               /*
+                * Fatal errors are unrecoverable.  Typically
+                * these are caused by DMA errors.  Unfortunately
+                * the exact reason is not (presently) returned
+                * by the hal.
+                */
+               sc->sc_stats.ast_hardware++;
+               ath_hal_intrset(ah, 0);         /* disable intr's until reset */
+               ath_fatal_proc(sc);
+       } else if (status & HAL_INT_RXORN) {
+               sc->sc_stats.ast_rxorn++;
+               ath_hal_intrset(ah, 0);         /* disable intr's until reset */
+               ath_rxorn_proc(sc);
+       } 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_proc(sc);
+               }
+               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++;
+                       sc->sc_rxlink = NULL;
+               }
+               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_rx_proc(sc);
+               if (status & HAL_INT_TX)
+                       sc->sc_tx_proc(sc);
+               if (status & HAL_INT_BMISS) {
+                       sc->sc_stats.ast_bmiss++;
+                       ath_bmiss_proc(sc);
+               }
+               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);
+               }
+       }
+}
+
+static void
+ath_fatal_proc(struct ath_softc *sc)
+{
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+       if_printf(ifp, "hardware error; resetting\n");
+       ath_reset(ifp);
+}
+
+static void
+ath_rxorn_proc(struct ath_softc *sc)
+{
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+       if_printf(ifp, "rx FIFO overrun; resetting\n");
+       ath_reset(ifp);
+}
+
+static void
+ath_bmiss_proc(struct ath_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+
+       DPRINTF(sc, ATH_DEBUG_ANY, "%s\n", __func__);
+       KASSERT(ic->ic_opmode == IEEE80211_M_STA,
+               ("unexpect operating mode %u", ic->ic_opmode));
+       if (ic->ic_state == IEEE80211_S_RUN) {
+               uint64_t lastrx = sc->sc_lastrx;
+               uint64_t tsf = ath_hal_gettsf64(sc->sc_ah);
+               u_int bmisstimeout =
+                       ic->ic_bmissthreshold * ic->ic_bss->ni_intval * 1024;
+
+               DPRINTF(sc, ATH_DEBUG_BEACON,
+                   "%s: tsf %llu lastrx %lld (%llu) bmiss %u\n",
+                   __func__, (unsigned long long) tsf,
+                   (unsigned long long)(tsf - lastrx),
+                   (unsigned long long) lastrx, bmisstimeout);
+               /*
+                * Workaround phantom bmiss interrupts by sanity-checking
+                * the time of our last rx'd frame.  If it is within the
+                * beacon miss interval then ignore the interrupt.  If it's
+                * truly a bmiss we'll get another interrupt soon and that'll
+                * be dispatched up for processing.
+                */
+               if (tsf - lastrx > bmisstimeout)
+                       ieee80211_beacon_miss(ic);
+               else
+                       sc->sc_stats.ast_bmiss_phantom++;
+       }
+}
+
+static void
+ath_radar_proc(struct ath_softc *sc)
+{
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       struct ath_hal *ah = sc->sc_ah;
+       HAL_CHANNEL hchan;
+
+       if (ath_hal_procdfs(ah, &hchan)) {
+               if_printf(ifp, "radar detected on channel %u/0x%x/0x%x\n",
+                       hchan.channel, hchan.channelFlags, hchan.privFlags);
+               /*
+                * Initiate channel change.
+                */
+               /* XXX not yet */
+       }
+}
+
+static u_int
+ath_chan2flags(struct ieee80211com *ic, struct ieee80211_channel *chan)
+{
+#define        N(a)    (sizeof(a) / sizeof(a[0]))
+       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_ST,             /* IEEE80211_MODE_TURBO_A */
+               CHANNEL_108G            /* IEEE80211_MODE_TURBO_G */
+       };
+       enum ieee80211_phymode mode = ieee80211_chan2mode(ic, chan);
+
+       KASSERT(mode < N(modeflags), ("unexpected phy mode %u", mode));
+       KASSERT(modeflags[mode] != 0, ("mode %u undefined", mode));
+       return modeflags[mode];
+#undef N
+}
+
+/* XXX error cleanup */
+static void
+ath_init(void *arg)
+{
+       struct ath_softc *sc = arg;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+       struct ath_hal *ah = sc->sc_ah;
+       HAL_STATUS status;
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+
+       DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags 0x%x\n",
+               __func__, ifp->if_flags);
+
+       /*
+        * Stop anything previously setup.  This is safe
+        * whether this is the first time through or not.
+        */
+       ath_stop_no_pwchg(ifp);
+
+       /*
+        * 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->ic_curchan);
+       if (!ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, AH_FALSE,
+                          &status)) {
+               if_printf(ifp, "unable to reset hardware; hal status %u\n",
+                       status);
+               return;
+       }
+
+       /*
+        * This is needed only to setup initial state
+        * but it's best done after a reset.
+        */
+       ath_update_txpow(sc);
+       /*
+        * Likewise this is set during reset so update
+        * state cached in the driver.
+        */
+       sc->sc_diversity = ath_hal_getdiversity(ah);
+       sc->sc_calinterval = 1;
+       sc->sc_caltries = 0;
+
+       /*
+        * 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 (ath_startrecv(sc) != 0) {
+               if_printf(ifp, "unable to start recv logic\n");
+               return;
+       }
+
+       /*
+        * 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);
+
+       ifp->if_flags |= IFF_RUNNING;
+       ic->ic_state = IEEE80211_S_INIT;
+
+       /*
+        * 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);
+       if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+               if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
+                       ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+       } else {
+               ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+       }
+}
+
+static void
+ath_stop_no_pwchg(struct ifnet *ifp)
+{
+       struct ath_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ath_hal *ah = sc->sc_ah;
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+
+       DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid %u if_flags 0x%x\n",
+               __func__, sc->sc_invalid, ifp->if_flags);
+
+       if (ifp->if_flags & IFF_RUNNING) {
+               /*
+                * Shutdown the hardware and driver:
+                *    reset 802.11 state machine
+                *    turn off timers
+                *    disable interrupts
+                *    turn off the radio
+                *    clear transmit machinery
+                *    clear receive machinery
+                *    drain and release tx queues
+                *    reclaim beacon resources
+                *    power down hardware
+                *
+                * Note that some of this work is not possible if the
+                * hardware is gone (invalid).
+                */
+               ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+               ifp->if_flags &= ~IFF_RUNNING;
+               ifp->if_timer = 0;
+               if (!sc->sc_invalid) {
+                       if (sc->sc_softled) {
+                               callout_stop(&sc->sc_ledtimer);
+                               ath_hal_gpioset(ah, sc->sc_ledpin,
+                                       !sc->sc_ledon);
+                               sc->sc_blinking = 0;
+                       }
+                       ath_hal_intrset(ah, 0);
+               }
+               ath_draintxq(sc);
+               if (!sc->sc_invalid) {
+                       ath_stoprecv(sc);
+                       ath_hal_phydisable(ah);
+               } else
+                       sc->sc_rxlink = NULL;
+#ifdef foo
+               ifq_purge(&ifp->if_snd);
+#endif
+               ath_beacon_free(sc);
+       }
+}
+
+static void
+ath_stop(struct ifnet *ifp)
+{
+       struct ath_softc *sc = ifp->if_softc;
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+
+       ath_stop_no_pwchg(ifp);
+       if (!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);
+       }
+}
+
+/*
+ * 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 various errors and
+ * to reset or reload hardware state.
+ */
+static int
+ath_reset(struct ifnet *ifp)
+{
+       struct ath_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ath_hal *ah = sc->sc_ah;
+       struct ieee80211_channel *c;
+       HAL_STATUS status;
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+
+       /*
+        * Convert to a HAL channel description with the flags
+        * constrained to reflect the current operating mode.
+        */
+       c = ic->ic_curchan;
+       sc->sc_curchan.channel = c->ic_freq;
+       sc->sc_curchan.channelFlags = ath_chan2flags(ic, c);
+
+       ath_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)) {
+               if_printf(ifp, "%s: unable to reset hardware; hal status %u\n",
+                       __func__, status);
+       }
+       ath_update_txpow(sc);           /* update tx power state */
+       sc->sc_diversity = ath_hal_getdiversity(ah);
+       sc->sc_calinterval = 1;
+       sc->sc_caltries = 0;
+       /*
+        * We may be doing a reset in response to an ioctl
+        * that changes the channel so update any state that
+        * might change as a result.
+        */
+       ath_chan_change(sc, c);
+       if (ath_startrecv(sc) != 0)     /* restart recv */
+               if_printf(ifp, "%s: unable to start recv logic\n", __func__);
+       if (ic->ic_state == IEEE80211_S_RUN)
+               ath_beacon_config(sc);  /* restart beacons */
+       ath_hal_intrset(ah, sc->sc_imask);
+
+       ath_start(ifp);                 /* restart xmit */
+       return 0;
+}
+
+static void
+ath_start(struct ifnet *ifp)
+{
+       struct ath_softc *sc = ifp->if_softc;
+       struct ath_hal *ah = sc->sc_ah;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ieee80211_node *ni;
+       struct ath_buf *bf;
+       struct mbuf *m;
+       struct ieee80211_frame *wh;
+       struct ether_header *eh;
+
+       if ((ifp->if_flags & IFF_RUNNING) == 0 || sc->sc_invalid)
+               return;
+
+       for (;;) {
+               /*
+                * Grab a TX buffer and associated resources.
+                */
+               bf = STAILQ_FIRST(&sc->sc_txbuf);
+               if (bf != NULL)
+                       STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list);
+               if (bf == NULL) {
+                       DPRINTF(sc, ATH_DEBUG_XMIT, "%s: out of xmit buffers\n",
+                               __func__);
+                       sc->sc_stats.ast_tx_qstop++;
+                       ifp->if_flags |= IFF_OACTIVE;
+                       break;
+               }
+               /*
+                * Poll the management queue for frames; they
+                * have priority over normal data frames.
+                */
+               IF_DEQUEUE(&ic->ic_mgtq, m);
+               if (m == NULL) {
+                       /*
+                        * No data frames go out unless we're associated.
+                        */
+                       if (ic->ic_state != IEEE80211_S_RUN) {
+                               DPRINTF(sc, ATH_DEBUG_XMIT,
+                                   "%s: discard data packet, state %s\n",
+                                   __func__,
+                                   ieee80211_state_name[ic->ic_state]);
+                               sc->sc_stats.ast_tx_discard++;
+                               STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+                               break;
+                       }
+                       m = ifq_dequeue(&ifp->if_snd, NULL);
+                       if (m == NULL) {
+                               STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+                               break;
+                       }
+                       /* 
+                        * Find the node for the destination so we can do
+                        * things like power save and fast frames aggregation.
+                        */
+                       if (m->m_len < sizeof(struct ether_header) &&
+                           (m = m_pullup(m, sizeof(struct ether_header))) == NULL) {
+                               ic->ic_stats.is_tx_nobuf++;     /* XXX */
+                               ni = NULL;
+                               goto bad;
+                       }
+                       eh = mtod(m, struct ether_header *);
+                       ni = ieee80211_find_txnode(ic, eh->ether_dhost);
+                       if (ni == NULL) {
+                               /* NB: ieee80211_find_txnode does stat+msg */
+                               m_freem(m);
+                               goto bad;
+                       }
+                       if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
+                           (m->m_flags & M_PWR_SAV) == 0) {
+                               /*
+                                * Station in power save mode; pass the frame
+                                * to the 802.11 layer and continue.  We'll get
+                                * the frame back when the time is right.
+                                */
+                               ieee80211_pwrsave(ic, ni, m);
+                               goto reclaim;
+                       }
+                       /* calculate priority so we can find the tx queue */
+                       if (ieee80211_classify(ic, m, ni)) {
+                               DPRINTF(sc, ATH_DEBUG_XMIT,
+                                       "%s: discard, classification failure\n",
+                                       __func__);
+                               m_freem(m);
+                               goto bad;
+                       }
+                       ifp->if_opackets++;
+                       BPF_MTAP(ifp, m);
+                       /*
+                        * Encapsulate the packet in prep for transmission.
+                        */
+                       m = ieee80211_encap(ic, m, ni);
+                       if (m == NULL) {
+                               DPRINTF(sc, ATH_DEBUG_XMIT,
+                                       "%s: encapsulation failure\n",
+                                       __func__);
+                               sc->sc_stats.ast_tx_encap++;
+                               goto bad;
+                       }
+               } else {
+                       /*
+                        * Hack!  The referenced node pointer is in the
+                        * rcvif field of the packet header.  This is
+                        * placed there by ieee80211_mgmt_output because
+                        * we need to hold the reference with the frame
+                        * and there's no other way (other than packet
+                        * tags which we consider too expensive to use)
+                        * to pass it along.
+                        */
+                       ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+                       m->m_pkthdr.rcvif = NULL;
+
+                       wh = mtod(m, struct ieee80211_frame *);
+                       if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+                           IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
+                               /* fill time stamp */
+                               uint64_t tsf;
+                               uint32_t *tstamp;
+
+                               tsf = ath_hal_gettsf64(ah);
+                               /* XXX: adjust 100us delay to xmit */
+                               tsf += 100;
+                               tstamp = (uint32_t *)&wh[1];
+                               tstamp[0] = htole32(tsf & 0xffffffff);
+                               tstamp[1] = htole32(tsf >> 32);
+                       }
+                       sc->sc_stats.ast_tx_mgmt++;
+               }
+
+               if (ath_tx_start(sc, ni, bf, m)) {
+bad:
+                       ifp->if_oerrors++;
+reclaim:
+                       STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+                       if (ni != NULL)
+                               ieee80211_free_node(ni);
+                       continue;
+               }
+
+               sc->sc_tx_timer = 5;
+               ifp->if_timer = 1;
+       }
+}
+
+static int
+ath_media_change(struct ifnet *ifp)
+{
+#define        IS_UP(ifp) \
+       ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_RUNNING | IFF_UP))
+       int error;
+
+       error = ieee80211_media_change(ifp);
+       if (error == ENETRESET) {
+               struct ath_softc *sc = ifp->if_softc;
+               struct ieee80211com *ic = &sc->sc_ic;
+
+               if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
+                       /* 
+                        * Adhoc demo mode is just ibss mode w/o beacons
+                        * (mostly).  The hal knows nothing about it;
+                        * tell it we're operating in ibss mode.
+                        */
+                       sc->sc_opmode = HAL_M_IBSS;
+               } else
+                       sc->sc_opmode = ic->ic_opmode;
+               if (IS_UP(ifp))
+                       ath_init(ifp->if_softc);        /* XXX lose error */
+               error = 0;
+       }
+       return error;
+#undef IS_UP
+}
+
+#ifdef ATH_DEBUG
+static void
+ath_keyprint(const char *tag, u_int ix,
+       const HAL_KEYVAL *hk, const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+       static const char *ciphers[] = {
+               "WEP",
+               "AES-OCB",
+               "AES-CCM",
+               "CKIP",
+               "TKIP",
+               "CLR",
+       };
+       int i, n;
+
+       printf("%s: [%02u] %-7s ", tag, ix, ciphers[hk->kv_type]);
+       for (i = 0, n = hk->kv_len; i < n; i++)
+               printf("%02x", hk->kv_val[i]);
+       printf(" mac %6D", mac, ":");
+       if (hk->kv_type == HAL_CIPHER_TKIP) {
+               printf(" mic ");
+               for (i = 0; i < sizeof(hk->kv_mic); i++)
+                       printf("%02x", hk->kv_mic[i]);
+       }
+       printf("\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 uint8_t mac[IEEE80211_ADDR_LEN])
+{
+#define        IEEE80211_KEY_XR        (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)
+       static const uint8_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));
+       KASSERT(sc->sc_splitmic, ("key cache !split"));
+       if ((k->wk_flags & IEEE80211_KEY_XR) == IEEE80211_KEY_XR) {
+               /*
+                * TX key goes at first index, RX key at the rx index.
+                * The hal handles the MIC keys at index+64.
+                */
+               memcpy(hk->kv_mic, k->wk_txmic, sizeof(hk->kv_mic));
+               KEYPRINTF(sc, k->wk_keyix, hk, zerobssid);
+               if (!ath_hal_keyset(ah, k->wk_keyix, hk, zerobssid))
+                       return 0;
+
+               memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic));
+               KEYPRINTF(sc, k->wk_keyix+32, hk, mac);
+               /* XXX delete tx key on failure? */
+               return ath_hal_keyset(ah, k->wk_keyix+32, hk, mac);
+       } else if (k->wk_flags & IEEE80211_KEY_XR) {
+               /*
+                * 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 uint8_t mac0[IEEE80211_ADDR_LEN],
+       struct ieee80211_node *bss)
+{
+#define        N(a)    (sizeof(a)/sizeof(a[0]))
+       static const uint8_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 */
+               (uint8_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;
+       uint8_t gmac[IEEE80211_ADDR_LEN];
+       const uint8_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 &&
+           sc->sc_splitmic) {
+               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 uint16_t
+key_alloc_2pair(struct ath_softc *sc,
+       ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
+{
+#define        N(a)    (sizeof(a)/sizeof(a[0]))
+       u_int i, keyix;
+
+       KASSERT(sc->sc_splitmic, ("key cache !split"));
+       /* XXX could optimize */
+       for (i = 0; i < N(sc->sc_keymap)/4; i++) {
+               uint8_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);
+                       *txkeyix = keyix;
+                       *rxkeyix = keyix+32;
+                       return 1;
+               }
+       }
+       DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__);
+       return 0;
+#undef N
+}
+
+/*
+ * Allocate a single key cache slot.
+ */
+static int
+key_alloc_single(struct ath_softc *sc,
+       ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
+{
+#define        N(a)    (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++) {
+               uint8_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);
+                       *txkeyix = *rxkeyix = keyix;
+                       return 1;
+               }
+       }
+       DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of space\n", __func__);
+       return 0;
+#undef N
+}
+
+/*
+ * Allocate one or more key cache slots for a uniacst 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 ieee80211com *ic, const struct ieee80211_key *k,
+       ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
+{
+       struct ath_softc *sc = ic->ic_ifp->if_softc;
+
+       /*
+        * 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) {
+               if (!(&ic->ic_nw_keys[0] <= k &&
+                     k < &ic->ic_nw_keys[IEEE80211_WEP_NKID])) {
+                       /* should not happen */
+                       DPRINTF(sc, ATH_DEBUG_KEYCACHE,
+                               "%s: bogus group key\n", __func__);
+                       return 0;
+               }
+               /*
+                * XXX we pre-allocate the global keys so
+                * have no way to check if they've already been allocated.
+                */
+               *keyix = *rxkeyix = k - ic->ic_nw_keys;
+               return 1;
+       }
+
+       /*
+        * 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.
+        */
+       if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
+               return key_alloc_single(sc, keyix, rxkeyix);
+       } else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP &&
+           (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic) {
+               return key_alloc_2pair(sc, keyix, rxkeyix);
+       } else {
+               return key_alloc_single(sc, keyix, rxkeyix);
+       }
+}
+
+/*
+ * Delete an entry in the key cache allocated by ath_key_alloc.
+ */
+static int
+ath_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
+{
+       struct ath_softc *sc = ic->ic_ifp->if_softc;
+       struct ath_hal *ah = sc->sc_ah;
+       const struct ieee80211_cipher *cip = k->wk_cipher;
+       u_int keyix = k->wk_keyix;
+
+       DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: delete key %u\n", __func__, keyix);
+
+       ath_hal_keyreset(ah, keyix);
+       /*
+        * 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 */
+       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 &&
+                   sc->sc_splitmic) {
+                       clrbit(sc->sc_keymap, keyix+64);        /* TX key MIC */
+                       clrbit(sc->sc_keymap, keyix+32);        /* RX key */
+                       clrbit(sc->sc_keymap, keyix+32+64);     /* RX key MIC */
+               }
+       }
+       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 ieee80211com *ic, const struct ieee80211_key *k,
+       const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+       struct ath_softc *sc = ic->ic_ifp->if_softc;
+
+       return ath_keyset(sc, k, mac, ic->ic_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 ieee80211com *ic)
+{
+       struct ifnet *ifp = ic->ic_ifp;
+       struct ath_softc *sc = ifp->if_softc;
+
+       DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__);
+#if 0
+       tasklet_disable(&sc->sc_rxtq);
+       IF_LOCK(&ifp->if_snd);          /* NB: doesn't block mgmt frames */
+#endif
+}
+
+static void
+ath_key_update_end(struct ieee80211com *ic)
+{
+       struct ifnet *ifp = ic->ic_ifp;
+       struct ath_softc *sc = ifp->if_softc;
+
+       DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__);
+#if 0
+       IF_UNLOCK(&ifp->if_snd);
+       tasklet_enable(&sc->sc_rxtq);
+#endif
+}
+
+/*
+ * 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 scanning
+ */
+static uint32_t
+ath_calcrxfilter(struct ath_softc *sc, enum ieee80211_state state)
+{
+#define        RX_FILTER_PRESERVE      (HAL_RX_FILTER_PHYERR | HAL_RX_FILTER_PHYRADAR)
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ath_hal *ah = sc->sc_ah;
+       struct ifnet *ifp = &ic->ic_if;
+       uint32_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 &&
+           (ifp->if_flags & IFF_PROMISC))
+               rfilt |= HAL_RX_FILTER_PROM;
+       if (ic->ic_opmode == IEEE80211_M_STA ||
+           ic->ic_opmode == IEEE80211_M_IBSS ||
+           state == IEEE80211_S_SCAN)
+               rfilt |= HAL_RX_FILTER_BEACON;
+       return rfilt;
+#undef RX_FILTER_PRESERVE
+}
+
+static void
+ath_mode_init(struct ath_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ath_hal *ah = sc->sc_ah;
+       struct ifnet *ifp = &ic->ic_if;
+       uint32_t rfilt, mfilt[2], val;
+       uint8_t pos;
+       struct ifmultiaddr *ifma;
+
+       /* configure rx filter */
+       rfilt = ath_calcrxfilter(sc, ic->ic_state);
+       ath_hal_setrxfilter(ah, rfilt);
+
+       /* configure operational mode */
+       ath_hal_setopmode(ah);
+
+       /*
+        * Handle any link-level address change.  Note that we only
+        * need to force ic_myaddr; any other addresses are handled
+        * as a byproduct of the ifnet code marking the interface
+        * down then up.
+        *
+        * XXX should get from lladdr instead of arpcom but that's more work
+        */
+       IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
+       ath_hal_setmac(ah, ic->ic_myaddr);
+
+       /* calculate and install multicast filter */
+       if ((ifp->if_flags & IFF_ALLMULTI) == 0) {
+               mfilt[0] = mfilt[1] = 0;
+               LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+                       caddr_t dl;
+
+                       /* calculate XOR of eight 6bit values */
+                       dl = LLADDR((struct sockaddr_dl *)ifma->ifma_addr);
+                       val = LE_READ_4(dl + 0);
+                       pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
+                       val = LE_READ_4(dl + 3);
+                       pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
+                       pos &= 0x3f;
+                       mfilt[pos / 32] |= (1 << (pos % 32));
+               }
+       } else {
+               mfilt[0] = mfilt[1] = ~0;
+       }
+       ath_hal_setmcastfilter(ah, mfilt[0], mfilt[1]);
+       DPRINTF(sc, ATH_DEBUG_MODE, "%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 (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 ifnet *ifp)
+{
+       struct ath_softc *sc = ifp->if_softc;
+       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
+               ath_setslottime(sc);
+}
+
+/*
+ * Setup a h/w transmit queue for beacons.
+ */
+static int
+ath_beaconq_setup(struct ath_hal *ah)
+{
+       HAL_TXQ_INFO qi;
+
+       memset(&qi, 0, sizeof(qi));
+       qi.tqi_aifs = HAL_TXQ_USEDEFAULT;
+       qi.tqi_cwmin = HAL_TXQ_USEDEFAULT;
+       qi.tqi_cwmax = HAL_TXQ_USEDEFAULT;
+       /* NB: for dynamic turbo, don't enable any other interrupts */
+       qi.tqi_qflags = HAL_TXQ_TXDESCINT_ENABLE;
+       return ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, &qi);
+}
+
+/*
+ * Setup the transmit queue parameters for the beacon queue.
+ */
+static int
+ath_beaconq_config(struct ath_softc *sc)
+{
+#define        ATH_EXPONENT_TO_VALUE(v)        ((1<<(v))-1)
+       struct ieee80211com *ic = &sc->sc_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 = ATH_BEACON_AIFS_DEFAULT;
+               qi.tqi_cwmin = ATH_BEACON_CWMIN_DEFAULT;
+               qi.tqi_cwmax = ATH_BEACON_CWMAX_DEFAULT;
+       } else {
+               struct wmeParams *wmep =
+                       &ic->ic_wme.wme_chanParams.cap_wmeParams[WME_AC_BE];
+               /*
+                * Adhoc mode; important thing is to use 2x cwmin.
+                */
+               qi.tqi_aifs = wmep->wmep_aifsn;
+               qi.tqi_cwmin = 2*ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin);
+               qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax);
+       }
+
+       if (!ath_hal_settxqueueprops(ah, sc->sc_bhalq, &qi)) {
+               device_printf(sc->sc_dev, "unable to update parameters for "
+                       "beacon hardware queue!\n");
+               return 0;
+       } else {
+               ath_hal_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */
+               return 1;
+       }
+#undef ATH_EXPONENT_TO_VALUE
+}
+
+/*
+ * Allocate and setup an initial beacon frame.
+ */
+static int
+ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni)
+{
+       struct ieee80211com *ic = ni->ni_ic;
+       struct ath_buf *bf;
+       struct mbuf *m;
+       int error;
+
+       bf = STAILQ_FIRST(&sc->sc_bbuf);
+       if (bf == NULL) {
+               DPRINTF(sc, ATH_DEBUG_BEACON, "%s: no dma buffers\n", __func__);
+               sc->sc_stats.ast_be_nombuf++;   /* XXX */
+               return ENOMEM;                  /* XXX */
+       }
+       /*
+        * NB: the beacon data buffer must be 32-bit aligned;
+        * we assume the mbuf routines will return us something
+        * with this alignment (perhaps should assert).
+        */
+       m = ieee80211_beacon_alloc(ic, ni, &sc->sc_boff);
+       if (m == NULL) {
+               DPRINTF(sc, ATH_DEBUG_BEACON, "%s: cannot get mbuf\n",
+                       __func__);
+               sc->sc_stats.ast_be_nombuf++;
+               return ENOMEM;
+       }
+
+       error = bus_dmamap_load_mbuf(sc->sc_dmat, bf->bf_dmamap, m,
+                                    ath_dma_map_mbuf, bf, BUS_DMA_NOWAIT);
+       if (error == 0) {
+               bf->bf_m = m;
+               bf->bf_node = ieee80211_ref_node(ni);
+       } else {
+               m_freem(m);
+       }
+       return error;
+}
+
+/*
+ * Setup the beacon frame for transmit.
+ */
+static void
+ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf)
+{
+#define        USE_SHPREAMBLE(_ic) \
+       (((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\
+               == IEEE80211_F_SHPREAMBLE)
+       struct ieee80211_node *ni = bf->bf_node;
+       struct ieee80211com *ic = ni->ni_ic;
+       struct mbuf *m = bf->bf_m;
+       struct ath_hal *ah = sc->sc_ah;
+       struct ath_desc *ds;
+       int flags, antenna;
+       const HAL_RATE_TABLE *rt;
+       uint8_t rix, rate;
+
+       DPRINTF(sc, ATH_DEBUG_BEACON, "%s: m %p len %u\n",
+               __func__, m, m->m_len);
+
+       /* setup descriptors */
+       ds = bf->bf_desc;
+
+       flags = HAL_TXDESC_NOACK;
+       if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) {
+               ds->ds_link = bf->bf_daddr;     /* self-linked */
+               flags |= HAL_TXDESC_VEOL;
+               /*
+                * Let hardware handle antenna switching.
+                */
+               antenna = sc->sc_txantenna;
+       } else {
+               ds->ds_link = 0;
+               /*
+                * Switch antenna every 4 beacons.
+                * XXX assumes two antenna
+                */
+               antenna = (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1);
+       }
+
+       KASSERT(bf->bf_nseg == 1,
+               ("multi-segment beacon frame; nseg %u", bf->bf_nseg));
+       ds->ds_data = bf->bf_segs[0].ds_addr;
+       /*
+        * Calculate rate code.
+        * XXX everything at min xmit rate
+        */
+       rix = sc->sc_minrateix;
+       rt = sc->sc_currates;
+       rate = rt->info[rix].rateCode;
+       if (USE_SHPREAMBLE(ic))
+               rate |= rt->info[rix].shortPreamble;
+       ath_hal_setuptxdesc(ah, ds
+               , m->m_len + IEEE80211_CRC_LEN  /* frame length */
+               , sizeof(struct ieee80211_frame)/* header length */
+               , HAL_PKT_TYPE_BEACON           /* Atheros packet type */
+               , ni->ni_txpower                /* txpower XXX */
+               , rate, 1                       /* series 0 rate/tries */
+               , HAL_TXKEYIX_INVALID           /* no encryption */
+               , antenna                       /* antenna mode */
+               , flags                         /* no ack, veol for beacons */
+               , 0                             /* rts/cts rate */
+               , 0                             /* rts/cts duration */
+       );
+       /* NB: beacon's BufLen must be a multiple of 4 bytes */
+       ath_hal_filltxdesc(ah, ds
+               , roundup(m->m_len, 4)          /* buffer length */
+               , AH_TRUE                       /* first segment */
+               , AH_TRUE                       /* last segment */
+               , ds                            /* first descriptor */
+       );
+#undef USE_SHPREAMBLE
+}
+
+/*
+ * Transmit a beacon frame at SWBA.  Dynamic updates to the
+ * frame contents are done as needed and the slot time is
+ * also adjusted based on current state.
+ */
+static void
+ath_beacon_proc(struct ath_softc *sc)
+{
+       struct ath_buf *bf = STAILQ_FIRST(&sc->sc_bbuf);
+       struct ieee80211_node *ni = bf->bf_node;
+       struct ieee80211com *ic = ni->ni_ic;
+       struct ath_hal *ah = sc->sc_ah;
+       struct mbuf *m;
+       int ncabq, error, otherant;
+
+       if (ic->ic_opmode == IEEE80211_M_STA ||
+           ic->ic_opmode == IEEE80211_M_MONITOR ||
+           bf == NULL || bf->bf_m == NULL) {
+               DPRINTF(sc, ATH_DEBUG_ANY, "%s: ic_flags=%x bf=%p bf_m=%p\n",
+                       __func__, ic->ic_flags, bf, bf ? bf->bf_m : NULL);
+               return;
+       }
+       /*
+        * Check if the previous beacon has gone out.  If
+        * not don't don't try to post another, skip this
+        * period and wait for the next.  Missed beacons
+        * indicate a problem and should not occur.  If we
+        * miss too many consecutive beacons reset the device.
+        */
+       if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) {
+               sc->sc_bmisscount++;
+               DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
+                       "%s: missed %u consecutive beacons\n",
+                       __func__, sc->sc_bmisscount);
+               if (sc->sc_bmisscount > 3)              /* NB: 3 is a guess */
+                       ath_bstuck_proc(sc);
+               return;
+       }
+       if (sc->sc_bmisscount != 0) {
+               DPRINTF(sc, ATH_DEBUG_BEACON,
+                       "%s: resume beacon xmit after %u misses\n",
+                       __func__, sc->sc_bmisscount);
+               sc->sc_bmisscount = 0;
+       }
+
+       /*
+        * 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).
+        */
+       m = bf->bf_m;
+       ncabq = sc->sc_cabq->axq_depth;
+       if (ieee80211_beacon_update(ic, bf->bf_node, &sc->sc_boff, m, ncabq)) {
+               /* XXX too conservative? */
+               bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+               error = bus_dmamap_load_mbuf(sc->sc_dmat, bf->bf_dmamap, m,
+                                            ath_dma_map_mbuf, bf,
+                                            BUS_DMA_NOWAIT);
+               if (error != 0) {
+                       if_printf(ic->ic_ifp,
+                           "%s: bus_dmamap_load_mbuf failed, error %u\n",
+                           __func__, error);
+                       return;
+               }
+       }
+
+       /*
+        * Handle slot time change when a non-ERP station joins/leaves
+        * an 11g network.  The 802.11 layer notifies us via callback,
+        * we mark updateslot, then wait one beacon before effecting
+        * the change.  This gives associated stations at least one
+        * beacon interval to note the state change.
+        */
+       /* XXX locking */
+       if (sc->sc_updateslot == UPDATE)
+               sc->sc_updateslot = COMMIT;     /* commit next beacon */
+       else if (sc->sc_updateslot == COMMIT)
+               ath_setslottime(sc);            /* commit change to h/w */
+
+       /*
+        * Check recent per-antenna transmit statistics and flip
+        * the default antenna if noticeably more frames went out
+        * on the non-default antenna.
+        * XXX assumes 2 anntenae
+        */
+       otherant = sc->sc_defant & 1 ? 2 : 1;
+       if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2)
+               ath_setdefantenna(sc, otherant);
+       sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0;
+
+       /*
+        * Construct tx descriptor.
+        */
+       ath_beacon_setup(sc, bf);
+
+       /*
+        * Stop any current dma and put the new frame on the queue.
+        * This should never fail since we check above that no frames
+        * are still pending on the queue.
+        */
+       if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) {
+               DPRINTF(sc, ATH_DEBUG_ANY,
+                       "%s: beacon queue %u did not stop?\n",
+                       __func__, sc->sc_bhalq);
+       }
+       bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
+
+       /*
+        * Enable the CAB queue before the beacon queue to
+        * insure cab frames are triggered by this beacon.
+        */
+       if (sc->sc_boff.bo_tim[4] & 1)  /* NB: only at DTIM */
+               ath_hal_txstart(ah, sc->sc_cabq->axq_qnum);
+       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] = %p (%p)\n", __func__,
+               sc->sc_bhalq, (caddr_t)bf->bf_daddr, bf->bf_desc);
+
+       sc->sc_stats.ast_be_xmit++;
+}
+
+/*
+ * Reset the hardware after detecting beacons have stopped.
+ */
+static void
+ath_bstuck_proc(struct ath_softc *sc)
+{
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+       if_printf(ifp, "stuck beacon; resetting (bmiss count %u)\n",
+                 sc->sc_bmisscount);
+       ath_reset(ifp);
+}
+
+/*
+ * Reclaim beacon resources.
+ */
+static void
+ath_beacon_free(struct ath_softc *sc)
+{
+       struct ath_buf *bf;
+
+       ASSERT_SERIALIZED(sc->sc_ic.ic_if.if_serializer);
+
+       STAILQ_FOREACH(bf, &sc->sc_bbuf, bf_list) {
+               if (bf->bf_m != NULL) {
+                       bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+                       m_freem(bf->bf_m);
+                       bf->bf_m = NULL;
+               }
+               if (bf->bf_node != NULL) {
+                       ieee80211_free_node(bf->bf_node);
+                       bf->bf_node = NULL;
+               }
+       }
+}
+
+/*
+ * Configure the beacon and sleep timers.
+ *
+ * When operating as an AP this resets the TSF and sets
+ * up the hardware to notify us when we need to issue beacons.
+ *
+ * When operating in station mode this sets up the beacon
+ * timers according to the timestamp of the last received
+ * beacon and the current TSF, configures PCF and DTIM
+ * handling, programs the sleep registers so the hardware
+ * will wakeup in time to receive beacons, and configures
+ * the beacon miss handling so we'll receive a BMISS
+ * interrupt when we stop seeing beacons from the AP
+ * we've associated with.
+ */
+static void
+ath_beacon_config(struct ath_softc *sc)
+{
+#define        TSF_TO_TU(_h,_l) \
+       ((((uint32_t)(_h)) << 22) | (((uint32_t)(_l)) >> 10))
+#define        FUDGE   2
+       struct ath_hal *ah = sc->sc_ah;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ieee80211_node *ni = ic->ic_bss;
+       uint32_t nexttbtt, intval, tsftu;
+       uint64_t tsf;
+
+       /* 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));
+       /* NB: the beacon interval is kept internally in TU's */
+       intval = ni->ni_intval & HAL_BEACON_PERIOD;
+       if (nexttbtt == 0)              /* e.g. for ap mode */
+               nexttbtt = intval;
+       else if (intval)                /* NB: can be 0 for monitor mode */
+               nexttbtt = roundup(nexttbtt, intval);
+       DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u intval %u (%u)\n",
+               __func__, nexttbtt, intval, ni->ni_intval);
+       if (ic->ic_opmode == IEEE80211_M_STA) {
+               HAL_BEACON_STATE bs;
+               int dtimperiod, dtimcount;
+               int cfpperiod, cfpcount;
+
+               /*
+                * Setup dtim and cfp parameters according to
+                * last beacon we received (which may be none).
+                */
+               dtimperiod = ni->ni_dtim_period;
+               if (dtimperiod <= 0)            /* NB: 0 if not known */
+                       dtimperiod = 1;
+               dtimcount = ni->ni_dtim_count;
+               if (dtimcount >= dtimperiod)    /* NB: sanity check */
+                       dtimcount = 0;          /* XXX? */
+               cfpperiod = 1;                  /* NB: no PCF support yet */
+               cfpcount = 0;
+               /*
+                * Pull nexttbtt forward to reflect the current
+                * TSF and calculate dtim+cfp state for the result.
+                */
+               tsf = ath_hal_gettsf64(ah);
+               tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
+               do {
+                       nexttbtt += intval;
+                       if (--dtimcount < 0) {
+                               dtimcount = dtimperiod - 1;
+                               if (--cfpcount < 0)
+                                       cfpcount = cfpperiod - 1;
+                       }
+               } while (nexttbtt < tsftu);
+               memset(&bs, 0, sizeof(bs));
+               bs.bs_intval = intval;
+               bs.bs_nexttbtt = nexttbtt;
+               bs.bs_dtimperiod = dtimperiod*intval;
+               bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
+               bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
+               bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
+               bs.bs_cfpmaxduration = 0;
+#if 0
+               /*
+                * The 802.11 layer records the offset to the DTIM
+                * bitmap while receiving beacons; use it here to
+                * enable h/w detection of our AID being marked in
+                * the bitmap vector (to indicate frames for us are
+                * pending at the AP).
+                * XXX do DTIM handling in s/w to WAR old h/w bugs
+                * XXX enable based on h/w rev for newer chips
+                */
+               bs.bs_timoffset = ni->ni_timoff;
+#endif
+               /*
+                * Calculate the number of consecutive beacons to miss
+                * before taking a BMISS interrupt.  The configuration
+                * is specified in ms, so we need to convert that to
+                * TU's and then calculate based on the beacon interval.
+                * Note that we clamp the result to at most 10 beacons.
+                */
+               bs.bs_bmissthreshold = ic->ic_bmissthreshold;
+               if (bs.bs_bmissthreshold > 10)
+                       bs.bs_bmissthreshold = 10;
+               else if (bs.bs_bmissthreshold <= 0)
+                       bs.bs_bmissthreshold = 1;
+
+               /*
+                * Calculate sleep duration.  The configuration is
+                * given in ms.  We insure a multiple of the beacon
+                * period is used.  Also, if the sleep duration is
+                * greater than the DTIM period then it makes senses
+                * to make it a multiple of that.
+                *
+                * XXX fixed at 100ms
+                */
+               bs.bs_sleepduration =
+                       roundup(IEEE80211_MS_TO_TU(100), bs.bs_intval);
+               if (bs.bs_sleepduration > bs.bs_dtimperiod)
+                       bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod);
+
+               DPRINTF(sc, ATH_DEBUG_BEACON, 
+                       "%s: tsf %ju tsf:tu %u intval %u nexttbtt %u dtim %u nextdtim %u bmiss %u sleep %u cfp:period %u maxdur %u next %u timoffset %u\n"
+                       , __func__
+                       , tsf, tsftu
+                       , bs.bs_intval
+                       , bs.bs_nexttbtt
+                       , bs.bs_dtimperiod
+                       , bs.bs_nextdtim
+                       , bs.bs_bmissthreshold
+                       , bs.bs_sleepduration
+                       , bs.bs_cfpperiod
+                       , bs.bs_cfpmaxduration
+                       , bs.bs_cfpnext
+                       , bs.bs_timoffset
+               );
+               ath_hal_intrset(ah, 0);
+               ath_hal_beacontimers(ah, &bs);
+               sc->sc_imask |= HAL_INT_BMISS;
+               ath_hal_intrset(ah, sc->sc_imask);
+       } else {
+               ath_hal_intrset(ah, 0);
+               if (nexttbtt == intval)
+                       intval |= HAL_BEACON_RESET_TSF;
+               if (ic->ic_opmode == IEEE80211_M_IBSS) {
+                       /*
+                        * In IBSS mode enable the beacon timers but only
+                        * enable SWBA interrupts if we need to manually
+                        * prepare beacon frames.  Otherwise we use a
+                        * self-linked tx descriptor and let the hardware
+                        * deal with things.
+                        */
+                       intval |= HAL_BEACON_ENA;
+                       if (!sc->sc_hasveol)
+                               sc->sc_imask |= HAL_INT_SWBA;
+                       if ((intval & HAL_BEACON_RESET_TSF) == 0) {
+                               /*
+                                * Pull nexttbtt forward to reflect
+                                * the current TSF.
+                                */
+                               tsf = ath_hal_gettsf64(ah);
+                               tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
+                               do {
+                                       nexttbtt += intval;
+                               } while (nexttbtt < tsftu);
+                       }
+                       ath_beaconq_config(sc);
+               } else if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+                       /*
+                        * 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);
+               }
+               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_proc(sc);
+       }
+       sc->sc_syncbeacon = 0;
+#undef FUDGE
+#undef TSF_TO_TU
+}
+
+static void
+ath_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+       bus_addr_t *paddr = (bus_addr_t*) arg;
+       KASSERT(error == 0, ("error %u on bus_dma callback", error));
+       *paddr = segs->ds_addr;
+}
+
+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 ifnet *ifp = &sc->sc_ic.ic_if;
+       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;
+       STAILQ_INIT(head);
+
+       /*
+        * Setup DMA descriptor area.
+        */
+       error = bus_dma_tag_create(NULL,        /* parent */
+                      PAGE_SIZE, 0,            /* alignment, bounds */
+                      BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
+                      BUS_SPACE_MAXADDR,       /* highaddr */
+                      NULL, NULL,              /* filter, filterarg */
+                      dd->dd_desc_len,         /* maxsize */
+                      1,                       /* nsegments */
+                      BUS_SPACE_MAXADDR,       /* maxsegsize */
+                      BUS_DMA_ALLOCNOW,        /* flags */
+                      &dd->dd_dmat);
+       if (error) {
+               if_printf(ifp, "cannot allocate %s DMA tag\n", dd->dd_name);
+               return error;
+       }
+
+       /* allocate descriptors */
+       error = bus_dmamap_create(dd->dd_dmat, BUS_DMA_WAITOK, &dd->dd_dmamap);
+       if (error) {
+               if_printf(ifp, "unable to create dmamap for %s descriptors, "
+                       "error %u\n", dd->dd_name, error);
+               return error;
+       }
+
+       error = bus_dmamem_alloc(dd->dd_dmat, (void **)&dd->dd_desc,
+                                BUS_DMA_WAITOK, &dd->dd_dmamap);
+       if (error) {
+               if_printf(ifp, "unable to alloc memory for %u %s descriptors, "
+                       "error %u\n", nbuf * ndesc, dd->dd_name, error);
+               return error;
+       }
+
+       error = bus_dmamap_load(dd->dd_dmat, dd->dd_dmamap,
+                               dd->dd_desc, dd->dd_desc_len,
+                               ath_load_cb, &dd->dd_desc_paddr,
+                               BUS_DMA_WAITOK);
+       if (error) {
+               if_printf(ifp, "unable to map %s descriptors, error %u\n",
+                       dd->dd_name, error);
+
+               bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap);
+               dd->dd_desc = NULL;
+               return error;
+       }
+
+       ds = dd->dd_desc;
+       DPRINTF(sc, ATH_DEBUG_RESET, "%s: %s DMA map: %p (%lu) -> %p (%lu)\n",
+           __func__, dd->dd_name, ds, (u_long) dd->dd_desc_len,
+           (caddr_t) dd->dd_desc_paddr, /*XXX*/ (u_long) dd->dd_desc_len);
+
+       /* allocate rx buffers */
+       bsize = sizeof(struct ath_buf) * nbuf;
+       bf = malloc(bsize, M_ATHDEV, M_WAITOK | M_ZERO);
+       dd->dd_bufptr = bf;
+
+       for (i = 0; i < nbuf; i++, bf++, ds += ndesc) {
+               bf->bf_desc = ds;
+               bf->bf_daddr = DS2PHYS(dd, ds);
+               error = bus_dmamap_create(sc->sc_dmat, BUS_DMA_WAITOK,
+                                         &bf->bf_dmamap);
+               if (error) {
+                       if_printf(ifp, "unable to create dmamap for %s "
+                               "buffer %u, error %u\n", dd->dd_name, i, error);
+                       ath_descdma_cleanup(sc, dd, head);
+                       return error;
+               }
+               STAILQ_INSERT_TAIL(head, bf, bf_list);
+       }
+       return 0;
+
+#if 0
+fail3:
+       bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap);
+fail2:
+       bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap);
+fail1:
+       bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap);
+fail0:
+       bus_dma_tag_destroy(dd->dd_dmat);
+       memset(dd, 0, sizeof(*dd));
+       return error;
+#endif
+#undef DS2PHYS
+}
+
+static void
+ath_descdma_cleanup(struct ath_softc *sc,
+       struct ath_descdma *dd, ath_bufhead *head)
+{
+       struct ath_buf *bf;
+       struct ieee80211_node *ni;
+
+       if (dd->dd_desc != NULL) {
+               bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap);
+               bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap);
+               dd->dd_desc = NULL;
+       }
+
+       if (dd->dd_dmamap != NULL) {
+               bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap);
+               dd->dd_dmamap = NULL;
+       }
+
+       if (dd->dd_dmat != NULL) {
+               bus_dma_tag_destroy(dd->dd_dmat);
+               dd->dd_dmat = NULL;
+       }
+
+       STAILQ_FOREACH(bf, head, bf_list) {
+               if (bf->bf_m) {
+                       m_freem(bf->bf_m);
+                       bf->bf_m = NULL;
+               }
+               if (bf->bf_dmamap != NULL) {
+                       bus_dmamap_destroy(sc->sc_dmat, bf->bf_dmamap);
+                       bf->bf_dmamap = NULL;
+               }
+               ni = bf->bf_node;
+               bf->bf_node = NULL;
+               if (ni != NULL) {
+                       /*
+                        * Reclaim node reference.
+                        */
+                       ieee80211_free_node(ni);
+               }
+       }
+       STAILQ_INIT(head);
+
+       if (dd->dd_bufptr != NULL)
+               free(dd->dd_bufptr, M_ATHDEV);
+       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)
+               return error;
+
+       error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf,
+                                 "tx", ath_txbuf, ATH_TXDESC);
+       if (error)
+               return error;
+
+       error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf,
+                                 "beacon", 1, 1);
+       if (error)
+               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);
+               sc->sc_bdma.dd_desc_len = 0;
+       }
+       if (sc->sc_txdma.dd_desc_len != 0) {
+               ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf);
+               sc->sc_txdma.dd_desc_len = 0;
+       }
+       if (sc->sc_rxdma.dd_desc_len != 0) {
+               ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
+               sc->sc_rxdma.dd_desc_len = 0;
+       }
+}
+
+static struct ieee80211_node *
+ath_node_alloc(struct ieee80211_node_table *nt)
+{
+       struct ieee80211com *ic = nt->nt_ic;
+       struct ath_softc *sc = ic->ic_ifp->if_softc;
+       const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space;
+       struct ath_node *an;
+
+       an = malloc(space, M_80211_NODE, M_NOWAIT|M_ZERO);
+       if (an == NULL) {
+               /* XXX stat+msg */
+               return NULL;
+       }
+       an->an_avgrssi = ATH_RSSI_DUMMY_MARKER;
+       ath_rate_node_init(sc, an);
+
+       DPRINTF(sc, ATH_DEBUG_NODE, "%s: an %p\n", __func__, an);
+       return &an->an_node;
+}
+
+static void
+ath_node_free(struct ieee80211_node *ni)
+{
+       struct ieee80211com *ic = ni->ni_ic;
+        struct ath_softc *sc = ic->ic_ifp->if_softc;
+
+       DPRINTF(sc, ATH_DEBUG_NODE, "%s: ni %p\n", __func__, ni);
+
+       ath_rate_node_cleanup(sc, ATH_NODE(ni));
+       sc->sc_node_free(ni);
+}
+
+static uint8_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))
+       uint32_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
+}
+
+static int
+ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       int error;
+       struct mbuf *m;
+       struct ath_desc *ds;
+
+       m = bf->bf_m;
+       if (m == NULL) {
+               /*
+                * NB: by assigning a page to the rx dma buffer we
+                * implicitly satisfy the Atheros requirement that
+                * this buffer be cache-line-aligned and sized to be
+                * multiple of the cache line size.  Not doing this
+                * causes weird stuff to happen (for the 5210 at least).
+                */
+               m = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR);
+               if (m == NULL) {
+                       DPRINTF(sc, ATH_DEBUG_ANY,
+                               "%s: no mbuf/cluster\n", __func__);
+                       sc->sc_stats.ast_rx_nombuf++;
+                       return ENOMEM;
+               }
+               m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
+
+               error = bus_dmamap_load_mbuf(sc->sc_dmat, bf->bf_dmamap, m,
+                                            ath_dma_map_mbuf, bf,
+                                            BUS_DMA_NOWAIT);
+               if (error != 0) {
+                       DPRINTF(sc, ATH_DEBUG_ANY,
+                           "%s: bus_dmamap_load_mbuf failed; error %d\n",
+                           __func__, error);
+                       sc->sc_stats.ast_rx_busdma++;
+                       m_freem(m);
+                       return error;
+               }
+               KASSERT(bf->bf_nseg == 1,
+                       ("multi-segment packet; nseg %u", bf->bf_nseg));
+               bf->bf_m = m;
+       }
+       bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREREAD);
+
+       /*
+        * Setup descriptors.  For receive we always terminate
+        * the descriptor list with a self-linked entry so we'll
+        * not get overrun under high load (as can happen with a
+        * 5212 when ANI processing enables PHY error frames).
+        *
+        * To insure the last descriptor is self-linked we create
+        * each descriptor as self-linked and add it to the end.  As
+        * each additional descriptor is added the previous self-linked
+        * entry is ``fixed'' naturally.  This should be safe even
+        * if DMA is happening.  When processing RX interrupts we
+        * never remove/process the last, self-linked, entry on the
+        * descriptor list.  This insures the hardware always has
+        * someplace to write a new frame.
+        */
+       ds = bf->bf_desc;
+       ds->ds_link = bf->bf_daddr;     /* link to self */
+       ds->ds_data = bf->bf_segs[0].ds_addr;
+       ds->ds_vdata = mtod(m, void *); /* for radar */
+       ath_hal_setuprxdesc(ah, ds
+               , m->m_len              /* buffer size */
+               , 0
+       );
+
+       if (sc->sc_rxlink != NULL)
+               *sc->sc_rxlink = bf->bf_daddr;
+       sc->sc_rxlink = &ds->ds_link;
+       return 0;
+}
+
+/*
+ * Extend 15-bit time stamp from rx descriptor to
+ * a full 64-bit TSF using the specified TSF.
+ */
+static __inline uint64_t
+ath_extend_tsf(uint32_t rstamp, uint64_t tsf)
+{
+       if ((tsf & 0x7fff) < rstamp)
+               tsf -= 0x8000;
+       return ((tsf &~ 0x7fff) | rstamp);
+}
+
+/*
+ * Intercept management frames to collect beacon rssi data
+ * and to do ibss merges.
+ */
+static void
+ath_recv_mgmt(struct ieee80211com *ic, struct mbuf *m,
+       struct ieee80211_node *ni,
+       int subtype, int rssi, uint32_t rstamp)
+{
+       struct ath_softc *sc = ic->ic_ifp->if_softc;
+
+       /*
+        * Call up first so subsequent work can use information
+        * potentially stored in the node (e.g. for ibss merge).
+        */
+       sc->sc_recv_mgmt(ic, m, ni, subtype, rssi, rstamp);
+       switch (subtype) {
+       case IEEE80211_FC0_SUBTYPE_BEACON:
+               /* update rssi statistics for use by the hal */
+               ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi);
+               if (sc->sc_syncbeacon &&
+                   ni == ic->ic_bss && ic->ic_state == IEEE80211_S_RUN) {
+                       /*
+                        * Resync beacon timers using the tsf of the beacon
+                        * frame we just received.
+                        */
+                       ath_beacon_config(sc);
+               }
+               /* fall thru... */
+       case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+               if (ic->ic_opmode == IEEE80211_M_IBSS &&
+                   ic->ic_state == IEEE80211_S_RUN) {
+                       uint64_t tsf = ath_extend_tsf(rstamp,
+                               ath_hal_gettsf64(sc->sc_ah));
+                       /*
+                        * Handle ibss merge as needed; check the tsf on the
+                        * frame before attempting the merge.  The 802.11 spec
+                        * says the station should change it's bssid to match
+                        * the oldest station with the same ssid, where oldest
+                        * is determined by the tsf.  Note that hardware
+                        * reconfiguration happens through callback to
+                        * ath_newstate as the state machine will go from
+                        * RUN -> RUN when this happens.
+                        */
+                       if (le64toh(ni->ni_tstamp.tsf) >= tsf) {
+                               DPRINTF(sc, ATH_DEBUG_STATE,
+                                   "ibss merge, rstamp %u tsf %ju "
+                                   "tstamp %ju\n", rstamp, (uintmax_t)tsf,
+                                   (uintmax_t)ni->ni_tstamp.tsf);
+                               (void) ieee80211_ibss_merge(ni);
+                       }
+               }
+               break;
+       }
+}
+
+/*
+ * Set the default antenna.
+ */
+static void
+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 int
+ath_rx_tap(struct ath_softc *sc, struct mbuf *m,
+       const struct ath_desc *ds, uint64_t tsf, int16_t nf)
+{
+       uint8_t rix;
+
+       KASSERT(sc->sc_drvbpf != NULL, ("no tap"));
+
+       /*
+        * Discard anything shorter than an ack or cts.
+        */
+       if (m->m_pkthdr.len < IEEE80211_ACK_LEN) {
+               DPRINTF(sc, ATH_DEBUG_RECV, "%s: runt packet %d\n",
+                       __func__, m->m_pkthdr.len);
+               sc->sc_stats.ast_rx_tooshort++;
+               return 0;
+       }
+       sc->sc_rx_th.wr_tsf = htole64(
+               ath_extend_tsf(ds->ds_rxstat.rs_tstamp, tsf));
+       rix = ds->ds_rxstat.rs_rate;
+       sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].rxflags;
+       if (ds->ds_rxstat.rs_status & HAL_RXERR_CRC)
+               sc->sc_rx_th.wr_flags |= IEEE80211_RADIOTAP_F_BADFCS;
+       /* XXX propagate other error flags from descriptor */
+       sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate;
+       sc->sc_rx_th.wr_antsignal = ds->ds_rxstat.rs_rssi + nf;
+       sc->sc_rx_th.wr_antnoise = nf;
+       sc->sc_rx_th.wr_antenna = ds->ds_rxstat.rs_antenna;
+
+       bpf_ptap(sc->sc_drvbpf, m, &sc->sc_rx_th, sc->sc_rx_th_len);
+
+       return 1;
+}
+
+static void
+ath_rx_proc(struct ath_softc *sc)
+{
+#define        PA2DESC(_sc, _pa) \
+       ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
+               ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
+       struct ath_buf *bf;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+       struct ath_hal *ah = sc->sc_ah;
+       struct ath_desc *ds;
+       struct mbuf *m;
+       struct ieee80211_node *ni;
+       struct ath_node *an;
+       int len, type, ngood;
+       u_int phyerr;
+       HAL_STATUS status;
+       int16_t nf;
+       uint64_t tsf;
+
+       ngood = 0;
+       nf = ath_hal_getchannoise(ah, &sc->sc_curchan);
+       tsf = ath_hal_gettsf64(ah);
+       do {
+               bf = STAILQ_FIRST(&sc->sc_rxbuf);
+               if (bf == NULL) {               /* NB: shouldn't happen */
+                       if_printf(ifp, "%s: no buffer!\n", __func__);
+                       break;
+               }
+               m = bf->bf_m;
+               if (m == NULL) {                /* NB: shouldn't happen */
+                       /*
+                        * If mbuf allocation failed previously there
+                        * will be no mbuf; try again to re-populate it.
+                        */ 
+                       /* XXX make debug msg */
+                       if_printf(ifp, "%s: no mbuf!\n", __func__);
+                       STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list);
+                       goto rx_next;
+               }
+               ds = bf->bf_desc;
+               if (ds->ds_link == bf->bf_daddr) {
+                       /* NB: never process the self-linked entry at the end */
+                       break;
+               }
+               /* XXX sync descriptor memory */
+               /*
+                * Must provide the virtual address of the current
+                * descriptor, the physical address, and the virtual
+                * address of the next descriptor in the h/w chain.
+                * This allows the HAL to look ahead to see if the
+                * hardware is done with a descriptor by checking the
+                * done bit in the following descriptor and the address
+                * of the current descriptor the DMA engine is working
+                * on.  All this is necessary because of our use of
+                * a self-linked list to avoid rx overruns.
+                */
+               status = ath_hal_rxprocdesc(ah, ds,
+                               bf->bf_daddr, PA2DESC(sc, ds->ds_link));
+#ifdef ATH_DEBUG
+               if (sc->sc_debug & ATH_DEBUG_RECV_DESC)
+                       ath_printrxbuf(bf, 0, status == HAL_OK); 
+#endif
+               if (status == HAL_EINPROGRESS)
+                       break;
+               STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list);
+               if (ds->ds_rxstat.rs_more) {
+                       /*
+                        * Frame spans multiple descriptors; this
+                        * cannot happen yet as we don't support
+                        * jumbograms.  If not in monitor mode,
+                        * discard the frame.
+                        */
+                       if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+                               sc->sc_stats.ast_rx_toobig++;
+                               goto rx_next;
+                       }
+                       /* fall thru for monitor mode handling... */
+               } else if (ds->ds_rxstat.rs_status != 0) {
+                       if (ds->ds_rxstat.rs_status & HAL_RXERR_CRC)
+                               sc->sc_stats.ast_rx_crcerr++;
+                       if (ds->ds_rxstat.rs_status & HAL_RXERR_FIFO)
+                               sc->sc_stats.ast_rx_fifoerr++;
+                       if (ds->ds_rxstat.rs_status & HAL_RXERR_PHY) {
+                               sc->sc_stats.ast_rx_phyerr++;
+                               phyerr = ds->ds_rxstat.rs_phyerr & 0x1f;
+                               sc->sc_stats.ast_rx_phy[phyerr]++;
+                               goto rx_next;
+                       }
+                       if (ds->ds_rxstat.rs_status & HAL_RXERR_DECRYPT) {
+                               /*
+                                * Decrypt error.  If the error occurred
+                                * because there was no hardware key, then
+                                * let the frame through so the upper layers
+                                * can process it.  This is necessary for 5210
+                                * parts which have no way to setup a ``clear''
+                                * key cache entry.
+                                *
+                                * XXX do key cache faulting
+                                */
+                               if (ds->ds_rxstat.rs_keyix == HAL_RXKEYIX_INVALID)
+                                       goto rx_accept;
+                               sc->sc_stats.ast_rx_badcrypt++;
+                       }
+                       if (ds->ds_rxstat.rs_status & HAL_RXERR_MIC) {
+                               sc->sc_stats.ast_rx_badmic++;
+                               /*
+                                * Do minimal work required to hand off
+                                * the 802.11 header for notifcation.
+                                */
+                               /* XXX frag's and qos frames */
+                               len = ds->ds_rxstat.rs_datalen;
+                               if (len >= sizeof (struct ieee80211_frame)) {
+                                       bus_dmamap_sync(sc->sc_dmat,
+                                           bf->bf_dmamap,
+                                           BUS_DMASYNC_POSTREAD);
+                                       ieee80211_notify_michael_failure(ic,
+                                           mtod(m, struct ieee80211_frame *),
+                                           sc->sc_splitmic ?
+                                               ds->ds_rxstat.rs_keyix-32 :
+                                               ds->ds_rxstat.rs_keyix
+                                       );
+                               }
+                       }
+                       ifp->if_ierrors++;
+                       /*
+                        * When a tap is present pass error frames
+                        * that have been requested.  By default we
+                        * pass decrypt+mic errors but others may be
+                        * interesting (e.g. crc).
+                        */
+                       if (sc->sc_drvbpf != NULL &&
+                           (ds->ds_rxstat.rs_status & sc->sc_monpass)) {
+                               bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
+                                   BUS_DMASYNC_POSTREAD);
+                               /* NB: bpf needs the mbuf length setup */
+                               len = ds->ds_rxstat.rs_datalen;
+                               m->m_pkthdr.len = m->m_len = len;
+                               (void) ath_rx_tap(sc, m, ds, tsf, nf);
+                       }
+                       /* XXX pass MIC errors up for s/w reclaculation */
+                       goto rx_next;
+               }
+rx_accept:
+               /*
+                * Sync and unmap the frame.  At this point we're
+                * committed to passing the mbuf somewhere so clear
+                * bf_m; this means a new sk_buff must be allocated
+                * when the rx descriptor is setup again to receive
+                * another frame.
+                */
+               bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
+                   BUS_DMASYNC_POSTREAD);
+               bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+               bf->bf_m = NULL;
+
+               m->m_pkthdr.rcvif = ifp;
+               len = ds->ds_rxstat.rs_datalen;
+               m->m_pkthdr.len = m->m_len = len;
+
+               sc->sc_stats.ast_ant_rx[ds->ds_rxstat.rs_antenna]++;
+
+               if (sc->sc_drvbpf != NULL && !ath_rx_tap(sc, m, ds, tsf, nf)) {
+                       m_freem(m);             /* XXX reclaim */
+                       goto rx_next;
+               }
+
+               /*
+                * From this point on we assume the frame is at least
+                * as large as ieee80211_frame_min; verify that.
+                */
+               if (len < IEEE80211_MIN_LEN) {
+                       DPRINTF(sc, ATH_DEBUG_RECV, "%s: short packet %d\n",
+                               __func__, len);
+                       sc->sc_stats.ast_rx_tooshort++;
+                       m_freem(m);
+                       goto rx_next;
+               }
+
+               if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) {
+                       ieee80211_dump_pkt(mtod(m, caddr_t), len,
+                                  sc->sc_hwmap[ds->ds_rxstat.rs_rate].ieeerate,
+                                  ds->ds_rxstat.rs_rssi);
+               }
+
+               m_adj(m, -IEEE80211_CRC_LEN);
+
+               /*
+                * Locate the node for sender, track state, and then
+                * pass the (referenced) node up to the 802.11 layer
+                * for its use.
+                */
+               ni = ieee80211_find_rxnode_withkey(ic,
+                       mtod(m, const struct ieee80211_frame_min *),
+                       ds->ds_rxstat.rs_keyix == HAL_RXKEYIX_INVALID ?
+                               IEEE80211_KEYIX_NONE : ds->ds_rxstat.rs_keyix);
+               /*
+                * Track rx rssi and do any rx antenna management.
+                */
+               an = ATH_NODE(ni);
+               ATH_RSSI_LPF(an->an_avgrssi, ds->ds_rxstat.rs_rssi);
+               ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, ds->ds_rxstat.rs_rssi);
+               /*
+                * Send frame up for processing.
+                */
+               type = ieee80211_input(ic, m, ni,
+                       ds->ds_rxstat.rs_rssi, ds->ds_rxstat.rs_tstamp);
+               ieee80211_free_node(ni);
+               if (sc->sc_diversity) {
+                       /*
+                        * When using fast diversity, change the default rx
+                        * antenna if diversity chooses the other antenna 3
+                        * times in a row.
+                        */
+                       if (sc->sc_defant != ds->ds_rxstat.rs_antenna) {
+                               if (++sc->sc_rxotherant >= 3)
+                                       ath_setdefantenna(sc,
+                                               ds->ds_rxstat.rs_antenna);
+                       } else
+                               sc->sc_rxotherant = 0;
+               }
+               if (sc->sc_softled) {
+                       /*
+                        * Blink for any data frame.  Otherwise do a
+                        * heartbeat-style blink when idle.  The latter
+                        * is mainly for station mode where we depend on
+                        * periodic beacon frames to trigger the poll event.
+                        */
+                       if (type == IEEE80211_FC0_TYPE_DATA) {
+                               sc->sc_rxrate = ds->ds_rxstat.rs_rate;
+                               ath_led_event(sc, ATH_LED_RX);
+                       } else if (ticks - sc->sc_ledevent >= sc->sc_ledidle)
+                               ath_led_event(sc, ATH_LED_POLL);
+               }
+               /*
+                * Arrange to update the last rx timestamp only for
+                * frames from our ap when operating in station mode.
+                * This assumes the rx key is always setup when associated.
+                */
+               if (ic->ic_opmode == IEEE80211_M_STA &&
+                   ds->ds_rxstat.rs_keyix != HAL_RXKEYIX_INVALID)
+                       ngood++;
+rx_next:
+               STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
+       } while (ath_rxbuf_init(sc, bf) == 0);
+
+       /* rx signal state monitoring */
+       ath_hal_rxmonitor(ah, &sc->sc_halstats, &sc->sc_curchan);
+       if (ath_hal_radar_event(ah))
+               ath_radar_proc(sc);
+       if (ngood)
+               sc->sc_lastrx = tsf;
+#undef PA2DESC
+}
+
+/*
+ * Setup a h/w transmit queue.
+ */
+static struct ath_txq *
+ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
+{
+#define        N(a)    (sizeof(a)/sizeof(a[0]))
+       struct ath_hal *ah = sc->sc_ah;
+       HAL_TXQ_INFO qi;
+       int qnum;
+
+       memset(&qi, 0, sizeof(qi));
+       qi.tqi_subtype = subtype;
+       qi.tqi_aifs = HAL_TXQ_USEDEFAULT;
+       qi.tqi_cwmin = HAL_TXQ_USEDEFAULT;
+       qi.tqi_cwmax = HAL_TXQ_USEDEFAULT;
+       /*
+        * Enable interrupts only for EOL and DESC conditions.
+        * We mark tx descriptors to receive a DESC interrupt
+        * when a tx queue gets deep; otherwise waiting for the
+        * EOL to reap descriptors.  Note that this is done to
+        * reduce interrupt load and this only defers reaping
+        * descriptors, never transmitting frames.  Aside from
+        * reducing interrupts this also permits more concurrency.
+        * The only potential downside is if the tx queue backs
+        * up in which case the top half of the kernel may backup
+        * due to a lack of tx descriptors.
+        */
+       qi.tqi_qflags = HAL_TXQ_TXEOLINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE;
+       qnum = ath_hal_setuptxqueue(ah, qtype, &qi);
+       if (qnum == -1) {
+               /*
+                * NB: don't print a message, this happens
+                * normally on parts with too few tx queues
+                */
+               return NULL;
+       }
+       if (qnum >= N(sc->sc_txq)) {
+               device_printf(sc->sc_dev,
+                       "hal qnum %u out of range, max %zu!\n",
+                       qnum, N(sc->sc_txq));
+               ath_hal_releasetxqueue(ah, qnum);
+               return NULL;
+       }
+       if (!IS_ATH_TXQ_SETUP(sc, qnum)) {
+               struct ath_txq *txq = &sc->sc_txq[qnum];
+
+               txq->axq_qnum = qnum;
+               txq->axq_depth = 0;
+               txq->axq_intrcnt = 0;
+               txq->axq_link = NULL;
+               STAILQ_INIT(&txq->axq_q);
+               ATH_TXQ_SETUP(sc, qnum);
+       }
+       return &sc->sc_txq[qnum];
+#undef N
+}
+
+/*
+ * Setup a hardware data transmit queue for the specified
+ * access control.  The hal may not support all requested
+ * queues in which case it will return a reference to a
+ * previously setup queue.  We record the mapping from ac's
+ * to h/w queues for use by ath_tx_start and also track
+ * the set of h/w queues being used to optimize work in the
+ * transmit interrupt handler and related routines.
+ */
+static int
+ath_tx_setup(struct ath_softc *sc, int ac, int haltype)
+{
+#define        N(a)    (sizeof(a)/sizeof(a[0]))
+       struct ath_txq *txq;
+
+       if (ac >= N(sc->sc_ac2q)) {
+               device_printf(sc->sc_dev, "AC %u out of range, max %zu!\n",
+                       ac, N(sc->sc_ac2q));
+               return 0;
+       }
+       txq = ath_txq_setup(sc, HAL_TX_QUEUE_DATA, haltype);
+       if (txq != NULL) {
+               sc->sc_ac2q[ac] = txq;
+               return 1;
+       } else {
+               return 0;
+       }
+#undef N
+}
+
+/*
+ * Update WME parameters for a transmit queue.
+ */
+static int
+ath_txq_update(struct ath_softc *sc, int ac)
+{
+#define        ATH_EXPONENT_TO_VALUE(v)        ((1<<v)-1)
+#define        ATH_TXOP_TO_US(v)               (v<<5)
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ath_txq *txq = sc->sc_ac2q[ac];
+       struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac];
+       struct ath_hal *ah = sc->sc_ah;
+       HAL_TXQ_INFO qi;
+
+       ath_hal_gettxqueueprops(ah, txq->axq_qnum, &qi);
+       qi.tqi_aifs = wmep->wmep_aifsn;
+       qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin);
+       qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax);      
+       qi.tqi_burstTime = ATH_TXOP_TO_US(wmep->wmep_txopLimit);
+
+       if (!ath_hal_settxqueueprops(ah, txq->axq_qnum, &qi)) {
+               device_printf(sc->sc_dev, "unable to update hardware queue "
+                       "parameters for %s traffic!\n",
+                       ieee80211_wme_acnames[ac]);
+               return 0;
+       } else {
+               ath_hal_resettxqueue(ah, txq->axq_qnum); /* push to h/w */
+               return 1;
+       }
+#undef ATH_TXOP_TO_US
+#undef ATH_EXPONENT_TO_VALUE
+}
+
+/*
+ * Callback from the 802.11 layer to update WME parameters.
+ */
+static int
+ath_wme_update(struct ieee80211com *ic)
+{
+       struct ath_softc *sc = ic->ic_ifp->if_softc;
+
+       return !ath_txq_update(sc, WME_AC_BE) ||
+           !ath_txq_update(sc, WME_AC_BK) ||
+           !ath_txq_update(sc, WME_AC_VI) ||
+           !ath_txq_update(sc, WME_AC_VO) ? EIO : 0;
+}
+
+/*
+ * Reclaim resources for a setup queue.
+ */
+static void
+ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq)
+{
+       ath_hal_releasetxqueue(sc->sc_ah, txq->axq_qnum);
+       sc->sc_txqsetup &= ~(1<<txq->axq_qnum);
+}
+
+/*
+ * Reclaim all tx queue resources.
+ */
+static void
+ath_tx_cleanup(struct ath_softc *sc)
+{
+       int i;
+
+       for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
+               if (IS_ATH_TXQ_SETUP(sc, i))
+                       ath_tx_cleanupq(sc, &sc->sc_txq[i]);
+}
+
+/*
+ * Defragment an mbuf chain, returning at most maxfrags separate
+ * mbufs+clusters.  If this is not possible NULL is returned and
+ * the original mbuf chain is left in it's present (potentially
+ * modified) state.  We use two techniques: collapsing consecutive
+ * mbufs and replacing consecutive mbufs by a cluster.
+ */
+static struct mbuf *
+ath_defrag(struct mbuf *m0, int how, int maxfrags)
+{
+       struct mbuf *m, *n, *n2, **prev;
+       u_int curfrags;
+
+       /*
+        * Calculate the current number of frags.
+        */
+       curfrags = 0;
+       for (m = m0; m != NULL; m = m->m_next)
+               curfrags++;
+       /*
+        * First, try to collapse mbufs.  Note that we always collapse
+        * towards the front so we don't need to deal with moving the
+        * pkthdr.  This may be suboptimal if the first mbuf has much
+        * less data than the following.
+        */
+       m = m0;
+again:
+       for (;;) {
+               n = m->m_next;
+               if (n == NULL)
+                       break;
+               if (n->m_len < M_TRAILINGSPACE(m)) {
+                       bcopy(mtod(n, void *), mtod(m, char *) + m->m_len,
+                             n->m_len);
+                       m->m_len += n->m_len;
+                       m->m_next = n->m_next;
+                       m_free(n);
+                       if (--curfrags <= maxfrags)
+                               return m0;
+               } else
+                       m = n;
+       }
+       KASSERT(maxfrags > 1,
+               ("maxfrags %u, but normal collapse failed", maxfrags));
+       /*
+        * Collapse consecutive mbufs to a cluster.
+        */
+       prev = &m0->m_next;             /* NB: not the first mbuf */
+       while ((n = *prev) != NULL) {
+               if ((n2 = n->m_next) != NULL &&
+                   n->m_len + n2->m_len < MCLBYTES) {
+                       m = m_getcl(how, MT_DATA, 0);
+                       if (m == NULL)
+                               goto bad;
+                       bcopy(mtod(n, void *), mtod(m, void *), n->m_len);
+                       bcopy(mtod(n2, void *), mtod(m, char *) + n->m_len,
+                               n2->m_len);
+                       m->m_len = n->m_len + n2->m_len;
+                       m->m_next = n2->m_next;
+                       *prev = m;
+                       m_free(n);
+                       m_free(n2);
+                       if (--curfrags <= maxfrags)     /* +1 cl -2 mbufs */
+                               return m0;
+                       /*
+                        * Still not there, try the normal collapse
+                        * again before we allocate another cluster.
+                        */
+                       goto again;
+               }
+               prev = &n->m_next;
+       }
+       /*
+        * No place where we can collapse to a cluster; punt.
+        * This can occur if, for example, you request 2 frags
+        * but the packet requires that both be clusters (we
+        * never reallocate the first mbuf to avoid moving the
+        * packet header).
+        */
+bad:
+       return NULL;
+}
+
+/*
+ * Return h/w rate index for an IEEE rate (w/o basic rate bit).
+ */
+static int
+ath_tx_findrix(const HAL_RATE_TABLE *rt, int rate)
+{
+       int i;
+
+       for (i = 0; i < rt->rateCount; i++)
+               if ((rt->info[i].dot11Rate & IEEE80211_RATE_VAL) == rate)
+                       return i;
+       return 0;               /* NB: lowest rate */
+}
+
+static int
+ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni,
+            struct ath_buf *bf, struct mbuf *m0)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ath_hal *ah = sc->sc_ah;
+       struct ifnet *ifp = &ic->ic_if;
+       const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams;
+       int i, error, iswep, ismcast, ismrr;
+       int keyix, hdrlen, pktlen, try0;
+       uint8_t rix, txrate, ctsrate;
+       uint8_t cix = 0xff;             /* NB: silence compiler */
+       struct ath_desc *ds, *ds0;
+       struct ath_txq *txq;
+       struct ieee80211_frame *wh;
+       u_int subtype, flags, ctsduration;
+       HAL_PKT_TYPE atype;
+       const HAL_RATE_TABLE *rt;
+       HAL_BOOL shortPreamble;
+       struct ath_node *an;
+       struct mbuf *m;
+       u_int pri;
+
+       wh = mtod(m0, struct ieee80211_frame *);
+       iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
+       ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
+       hdrlen = ieee80211_anyhdrsize(wh);
+       /*
+        * Packet length must not include any
+        * pad bytes; deduct them here.
+        */
+       pktlen = m0->m_pkthdr.len - (hdrlen & 3);
+
+       if (iswep) {
+               const struct ieee80211_cipher *cip;
+               struct ieee80211_key *k;
+
+               /*
+                * Construct the 802.11 header+trailer for an encrypted
+                * frame. The only reason this can fail is because of an
+                * unknown or unsupported cipher/key type.
+                */
+               k = ieee80211_crypto_encap(ic, ni, m0);
+               if (k == NULL) {
+                       /*
+                        * This can happen when the key is yanked after the
+                        * frame was queued.  Just discard the frame; the
+                        * 802.11 layer counts failures and provides
+                        * debugging/diagnostics.
+                        */
+                       m_freem(m0);
+                       return EIO;
+               }
+               /*
+                * Adjust the packet + header lengths for the crypto
+                * additions and calculate the h/w key index.  When
+                * a s/w mic is done the frame will have had any mic
+                * added to it prior to entry so m0->m_pkthdr.len above will
+                * account for it. Otherwise we need to add it to the
+                * packet length.
+                */
+               cip = k->wk_cipher;
+               hdrlen += cip->ic_header;
+               pktlen += cip->ic_header + cip->ic_trailer;
+               if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0)
+                       pktlen += cip->ic_miclen;
+               keyix = k->wk_keyix;
+
+               /* packet header may have moved, reset our local pointer */
+               wh = mtod(m0, struct ieee80211_frame *);
+       } else if (ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) {
+               /*
+                * Use station key cache slot, if assigned.
+                */
+               keyix = ni->ni_ucastkey.wk_keyix;
+               if (keyix == IEEE80211_KEYIX_NONE)
+                       keyix = HAL_TXKEYIX_INVALID;
+       } else
+               keyix = HAL_TXKEYIX_INVALID;
+
+       pktlen += IEEE80211_CRC_LEN;
+
+       /*
+        * Load the DMA map so any coalescing is done.  This
+        * also calculates the number of descriptors we need.
+        */
+       error = bus_dmamap_load_mbuf(sc->sc_dmat, bf->bf_dmamap, m0,
+                                    ath_dma_map_mbuf, bf, BUS_DMA_NOWAIT);
+       if (error == EFBIG) {
+               /* XXX packet requires too many descriptors */
+               bf->bf_nseg = ATH_TXDESC+1;
+       } else if (error != 0) {
+               sc->sc_stats.ast_tx_busdma++;
+               m_freem(m0);
+               return error;
+       }
+       /*
+        * Discard null packets and check for packets that
+        * require too many TX descriptors.  We try to convert
+        * the latter to a cluster.
+        */
+       if (bf->bf_nseg > ATH_TXDESC) {         /* too many desc's, linearize */
+               sc->sc_stats.ast_tx_linear++;
+               m = ath_defrag(m0, MB_DONTWAIT, ATH_TXDESC);
+               if (m == NULL) {
+                       m_freem(m0);
+                       sc->sc_stats.ast_tx_nombuf++;
+                       return ENOMEM;
+               }
+               m0 = m;
+               error = bus_dmamap_load_mbuf(sc->sc_dmat, bf->bf_dmamap, m0,
+                                            ath_dma_map_mbuf, bf,
+                                            BUS_DMA_NOWAIT);
+               if (error != 0) {
+                       sc->sc_stats.ast_tx_busdma++;
+                       m_freem(m0);
+                       return error;
+               }
+               KASSERT(bf->bf_nseg <= ATH_TXDESC,
+                   ("too many segments after defrag; nseg %u", bf->bf_nseg));
+       } else if (bf->bf_nseg == 0) {          /* null packet, discard */
+               sc->sc_stats.ast_tx_nodata++;
+               m_freem(m0);
+               return EIO;
+       }
+       DPRINTF(sc, ATH_DEBUG_XMIT, "%s: m %p len %u\n", __func__, m0, pktlen);
+       bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
+       bf->bf_m = m0;
+       bf->bf_node = ni;                       /* NB: held reference */
+
+       /* setup descriptors */
+       ds = bf->bf_desc;
+       rt = sc->sc_currates;
+       KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+
+       /*
+        * NB: the 802.11 layer marks whether or not we should
+        * use short preamble based on the current mode and
+        * negotiated parameters.
+        */
+       if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+           (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
+               shortPreamble = AH_TRUE;
+               sc->sc_stats.ast_tx_shortpre++;
+       } else {
+               shortPreamble = AH_FALSE;
+       }
+
+       an = ATH_NODE(ni);
+       flags = HAL_TXDESC_CLRDMASK;            /* XXX needed for crypto errs */
+       ismrr = 0;                              /* default no multi-rate retry*/
+       /*
+        * Calculate Atheros packet type from IEEE80211 packet header,
+        * setup for rate calculations, and select h/w transmit queue.
+        */
+       switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
+       case IEEE80211_FC0_TYPE_MGT:
+               subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+               if (subtype == IEEE80211_FC0_SUBTYPE_BEACON)
+                       atype = HAL_PKT_TYPE_BEACON;
+               else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+                       atype = HAL_PKT_TYPE_PROBE_RESP;
+               else if (subtype == IEEE80211_FC0_SUBTYPE_ATIM)
+                       atype = HAL_PKT_TYPE_ATIM;
+               else
+                       atype = HAL_PKT_TYPE_NORMAL;    /* XXX */
+               rix = sc->sc_minrateix;
+               txrate = rt->info[rix].rateCode;
+               if (shortPreamble)
+                       txrate |= rt->info[rix].shortPreamble;
+               try0 = ATH_TXMGTTRY;
+               /* NB: force all management frames to highest queue */
+               if (ni->ni_flags & IEEE80211_NODE_QOS) {
+                       /* NB: force all management frames to highest queue */
+                       pri = WME_AC_VO;
+               } else
+                       pri = WME_AC_BE;
+               flags |= HAL_TXDESC_INTREQ;     /* force interrupt */
+               break;
+       case IEEE80211_FC0_TYPE_CTL:
+               atype = HAL_PKT_TYPE_PSPOLL;    /* stop setting of duration */
+               rix = sc->sc_minrateix;
+               txrate = rt->info[rix].rateCode;
+               if (shortPreamble)
+                       txrate |= rt->info[rix].shortPreamble;
+               try0 = ATH_TXMGTTRY;
+               /* NB: force all ctl frames to highest queue */
+               if (ni->ni_flags & IEEE80211_NODE_QOS) {
+                       /* NB: force all ctl frames to highest queue */
+                       pri = WME_AC_VO;
+               } else
+                       pri = WME_AC_BE;
+               flags |= HAL_TXDESC_INTREQ;     /* force interrupt */
+               break;
+       case IEEE80211_FC0_TYPE_DATA:
+               atype = HAL_PKT_TYPE_NORMAL;            /* default */
+               /*
+                * Data frames: multicast frames go out at a fixed rate,
+                * otherwise consult the rate control module for the
+                * rate to use.
+                */
+               if (ismcast) {
+                       /*
+                        * Check mcast rate setting in case it's changed.
+                        * XXX move out of fastpath
+                        */
+                       if (ic->ic_mcast_rate != sc->sc_mcastrate) {
+                               sc->sc_mcastrix =
+                                       ath_tx_findrix(rt, ic->ic_mcast_rate);
+                               sc->sc_mcastrate = ic->ic_mcast_rate;
+                       }
+                       rix = sc->sc_mcastrix;
+                       txrate = rt->info[rix].rateCode;
+                       if (shortPreamble)
+                               txrate |= rt->info[rix].shortPreamble;
+                       try0 = 1;
+               } else {
+                       ath_rate_findrate(sc, an, shortPreamble, pktlen,
+                               &rix, &try0, &txrate);
+                       sc->sc_txrate = txrate;         /* for LED blinking */
+                       if (try0 != ATH_TXMAXTRY)
+                               ismrr = 1;
+               }
+               pri = M_WME_GETAC(m0);
+               if (cap->cap_wmeParams[pri].wmep_noackPolicy)
+                       flags |= HAL_TXDESC_NOACK;
+               break;
+       default:
+               if_printf(ifp, "bogus frame type 0x%x (%s)\n",
+                       wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__);
+               /* XXX statistic */
+               m_freem(m0);
+               return EIO;
+       }
+       txq = sc->sc_ac2q[pri];
+
+       /*
+        * When servicing one or more stations in power-save mode
+        * multicast frames must be buffered until after the beacon.
+        * We use the CAB queue for that.
+        */
+       if (ismcast && ic->ic_ps_sta) {
+               txq = sc->sc_cabq;
+               /* XXX? more bit in 802.11 frame header */
+       }
+
+       /*
+        * Calculate miscellaneous flags.
+        */
+       if (ismcast) {
+               flags |= HAL_TXDESC_NOACK;      /* no ack on broad/multicast */
+       } else if (pktlen > ic->ic_rtsthreshold) {
+               flags |= HAL_TXDESC_RTSENA;     /* RTS based on frame length */
+               cix = rt->info[rix].controlRate;
+               sc->sc_stats.ast_tx_rts++;
+       }
+       if (flags & HAL_TXDESC_NOACK)           /* NB: avoid double counting */
+               sc->sc_stats.ast_tx_noack++;
+
+       /*
+        * If 802.11g protection is enabled, determine whether
+        * to use RTS/CTS or just CTS.  Note that this is only
+        * done for OFDM unicast frames.
+        */
+       if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+           rt->info[rix].phy == IEEE80211_T_OFDM &&
+           (flags & HAL_TXDESC_NOACK) == 0) {
+               /* XXX fragments must use CCK rates w/ protection */
+               if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+                       flags |= HAL_TXDESC_RTSENA;
+               else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
+                       flags |= HAL_TXDESC_CTSENA;
+               cix = rt->info[sc->sc_protrix].controlRate;
+               sc->sc_stats.ast_tx_protect++;
+       }
+
+       /*
+        * Calculate duration.  This logically belongs in the 802.11
+        * layer but it lacks sufficient information to calculate it.
+        */
+       if ((flags & HAL_TXDESC_NOACK) == 0 &&
+           (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) {
+               uint16_t dur;
+               /*
+                * XXX not right with fragmentation.
+                */
+               if (shortPreamble)
+                       dur = rt->info[rix].spAckDuration;
+               else
+                       dur = rt->info[rix].lpAckDuration;
+               *(uint16_t *)wh->i_dur = htole16(dur);
+       }
+
+       /*
+        * Calculate RTS/CTS rate and duration if needed.
+        */
+       ctsduration = 0;
+       if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) {
+               /*
+                * CTS transmit rate is derived from the transmit rate
+                * by looking in the h/w rate table.  We must also factor
+                * in whether or not a short preamble is to be used.
+                */
+               /* NB: cix is set above where RTS/CTS is enabled */
+               KASSERT(cix != 0xff, ("cix not setup"));
+               ctsrate = rt->info[cix].rateCode;
+               /*
+                * Compute the transmit duration based on the frame
+                * size and the size of an ACK frame.  We call into the
+                * HAL to do the computation since it depends on the
+                * characteristics of the actual PHY being used.
+                *
+                * NB: CTS is assumed the same size as an ACK so we can
+                *     use the precalculated ACK durations.
+                */
+               if (shortPreamble) {
+                       ctsrate |= rt->info[cix].shortPreamble;
+                       if (flags & HAL_TXDESC_RTSENA)          /* SIFS + CTS */
+                               ctsduration += rt->info[cix].spAckDuration;
+                       ctsduration += ath_hal_computetxtime(ah,
+                               rt, pktlen, rix, AH_TRUE);
+                       if ((flags & HAL_TXDESC_NOACK) == 0)    /* SIFS + ACK */
+                               ctsduration += rt->info[rix].spAckDuration;
+               } else {
+                       if (flags & HAL_TXDESC_RTSENA)          /* SIFS + CTS */
+                               ctsduration += rt->info[cix].lpAckDuration;
+                       ctsduration += ath_hal_computetxtime(ah,
+                               rt, pktlen, rix, AH_FALSE);
+                       if ((flags & HAL_TXDESC_NOACK) == 0)    /* SIFS + ACK */
+                               ctsduration += rt->info[rix].lpAckDuration;
+               }
+               /*
+                * Must disable multi-rate retry when using RTS/CTS.
+                */
+               ismrr = 0;
+               try0 = ATH_TXMGTTRY;            /* XXX */
+       } else
+               ctsrate = 0;
+
+       if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT))
+               ieee80211_dump_pkt(mtod(m0, caddr_t), m0->m_len,
+                       sc->sc_hwmap[txrate].ieeerate, -1);
+
+       if (ic->ic_rawbpf)
+               bpf_mtap(ic->ic_rawbpf, m0);
+       if (sc->sc_drvbpf) {
+               uint64_t tsf = ath_hal_gettsf64(ah);
+
+               sc->sc_tx_th.wt_tsf = htole64(tsf);
+               sc->sc_tx_th.wt_flags = sc->sc_hwmap[txrate].txflags;
+               if (iswep)
+                       sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP;
+               sc->sc_tx_th.wt_rate = sc->sc_hwmap[txrate].ieeerate;
+               sc->sc_tx_th.wt_txpower = ni->ni_txpower;
+               sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
+
+               bpf_ptap(sc->sc_drvbpf, m0, &sc->sc_tx_th, sc->sc_tx_th_len);
+       }
+
+       /* 
+        * Determine if a tx interrupt should be generated for
+        * this descriptor.  We take a tx interrupt to reap
+        * descriptors when the h/w hits an EOL condition or
+        * when the descriptor is specifically marked to generate
+        * an interrupt.  We periodically mark descriptors in this
+        * way to insure timely replenishing of the supply needed
+        * for sending frames.  Defering interrupts reduces system
+        * load and potentially allows more concurrent work to be
+        * done but if done to aggressively can cause senders to
+        * backup.
+        *
+        * NB: use >= to deal with sc_txintrperiod changing
+        *     dynamically through sysctl.
+        */
+       if (flags & HAL_TXDESC_INTREQ) {
+               txq->axq_intrcnt = 0;
+       } else if (++txq->axq_intrcnt >= sc->sc_txintrperiod) {
+               flags |= HAL_TXDESC_INTREQ;
+               txq->axq_intrcnt = 0;
+       }
+
+       /*
+        * Formulate first tx descriptor with tx controls.
+        */
+       /* XXX check return value? */
+       ath_hal_setuptxdesc(ah, ds
+               , pktlen                /* packet length */
+               , hdrlen                /* header length */
+               , atype                 /* Atheros packet type */
+               , ni->ni_txpower        /* txpower */
+               , txrate, try0          /* series 0 rate/tries */
+               , keyix                 /* key cache index */
+               , sc->sc_txantenna      /* antenna mode */
+               , flags                 /* flags */
+               , ctsrate               /* rts/cts rate */
+               , ctsduration           /* rts/cts duration */
+       );
+       bf->bf_flags = flags;
+       /*
+        * Setup the multi-rate retry state only when we're
+        * going to use it.  This assumes ath_hal_setuptxdesc
+        * initializes the descriptors (so we don't have to)
+        * when the hardware supports multi-rate retry and
+        * we don't use it.
+        */
+       if (ismrr)
+               ath_rate_setupxtxdesc(sc, an, ds, shortPreamble, rix);
+
+       /*
+        * Fillin the remainder of the descriptor info.
+        */
+       ds0 = ds;
+       for (i = 0; i < bf->bf_nseg; i++, ds++) {
+               ds->ds_data = bf->bf_segs[i].ds_addr;
+               if (i == bf->bf_nseg - 1)
+                       ds->ds_link = 0;
+               else
+                       ds->ds_link = bf->bf_daddr + sizeof(*ds) * (i + 1);
+               ath_hal_filltxdesc(ah, ds
+                       , bf->bf_segs[i].ds_len /* segment length */
+                       , i == 0                /* first segment */
+                       , i == bf->bf_nseg - 1  /* last segment */
+                       , ds0                   /* first descriptor */
+               );
+               DPRINTF(sc, ATH_DEBUG_XMIT,
+                       "%s: %d: %08x %08x %08x %08x %08x %08x\n",
+                       __func__, i, ds->ds_link, ds->ds_data,
+                       ds->ds_ctl0, ds->ds_ctl1, ds->ds_hw[0], ds->ds_hw[1]);
+       }
+       /*
+        * Insert the frame on the outbound list and
+        * pass it on to the hardware.
+        */
+       ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
+       if (txq->axq_link == NULL) {
+               ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
+               DPRINTF(sc, ATH_DEBUG_XMIT,
+                       "%s: TXDP[%u] = %p (%p) depth %d\n", __func__,
+                       txq->axq_qnum, (caddr_t)bf->bf_daddr, bf->bf_desc,
+                       txq->axq_depth);
+       } else {
+               *txq->axq_link = bf->bf_daddr;
+               DPRINTF(sc, ATH_DEBUG_XMIT,
+                       "%s: link[%u](%p)=%p (%p) depth %d\n", __func__,
+                       txq->axq_qnum, txq->axq_link,
+                       (caddr_t)bf->bf_daddr, bf->bf_desc, txq->axq_depth);
+       }
+       txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
+       /*
+        * The CAB queue is started from the SWBA handler since
+        * frames only go out on DTIM and to avoid possible races.
+        */
+       if (txq != sc->sc_cabq)
+               ath_hal_txstart(ah, txq->axq_qnum);
+
+       return 0;
+}
+
+/*
+ * Process completed xmit descriptors from the specified queue.
+ */
+static int
+ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ath_buf *bf;
+       struct ath_desc *ds, *ds0;
+       struct ieee80211_node *ni;
+       struct ath_node *an;
+       int sr, lr, pri, nacked;
+       HAL_STATUS status;
+
+       DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: tx queue %u head %p link %p\n",
+               __func__, txq->axq_qnum,
+               (caddr_t)(uintptr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum),
+               txq->axq_link);
+       nacked = 0;
+       for (;;) {
+               txq->axq_intrcnt = 0;   /* reset periodic desc intr count */
+               bf = STAILQ_FIRST(&txq->axq_q);
+               if (bf == NULL)
+                       break;
+               ds0 = &bf->bf_desc[0];
+               ds = &bf->bf_desc[bf->bf_nseg - 1];
+               status = ath_hal_txprocdesc(ah, ds);
+#ifdef ATH_DEBUG
+               if (sc->sc_debug & ATH_DEBUG_XMIT_DESC)
+                       ath_printtxbuf(bf, txq->axq_qnum, 0, status == HAL_OK);
+#endif
+               if (status == HAL_EINPROGRESS)
+                       break;
+               ATH_TXQ_REMOVE_HEAD(txq, bf_list);
+               if (txq->axq_depth == 0)
+                       txq->axq_link = NULL;
+
+               ni = bf->bf_node;
+               if (ni != NULL) {
+                       an = ATH_NODE(ni);
+                       if (ds->ds_txstat.ts_status == 0) {
+                               uint8_t txant = ds->ds_txstat.ts_antenna;
+                               sc->sc_stats.ast_ant_tx[txant]++;
+                               sc->sc_ant_tx[txant]++;
+                               if (ds->ds_txstat.ts_rate & HAL_TXSTAT_ALTRATE)
+                                       sc->sc_stats.ast_tx_altrate++;
+                               sc->sc_stats.ast_tx_rssi =
+                                       ds->ds_txstat.ts_rssi;
+                               ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi,
+                                       ds->ds_txstat.ts_rssi);
+                               pri = M_WME_GETAC(bf->bf_m);
+                               if (pri >= WME_AC_VO)
+                                       ic->ic_wme.wme_hipri_traffic++;
+                               ni->ni_inact = ni->ni_inact_reload;
+                       } else {
+                               if (ds->ds_txstat.ts_status & HAL_TXERR_XRETRY)
+                                       sc->sc_stats.ast_tx_xretries++;
+                               if (ds->ds_txstat.ts_status & HAL_TXERR_FIFO)
+                                       sc->sc_stats.ast_tx_fifoerr++;
+                               if (ds->ds_txstat.ts_status & HAL_TXERR_FILT)
+                                       sc->sc_stats.ast_tx_filtered++;
+                       }
+                       sr = ds->ds_txstat.ts_shortretry;
+                       lr = ds->ds_txstat.ts_longretry;
+                       sc->sc_stats.ast_tx_shortretry += sr;
+                       sc->sc_stats.ast_tx_longretry += lr;
+                       /*
+                        * Hand the descriptor to the rate control algorithm.
+                        */
+                       if ((ds->ds_txstat.ts_status & HAL_TXERR_FILT) == 0 &&
+                           (bf->bf_flags & HAL_TXDESC_NOACK) == 0) {
+                               /*
+                                * If frame was ack'd update the last rx time
+                                * used to workaround phantom bmiss interrupts.
+                                */
+                               if (ds->ds_txstat.ts_status == 0)
+                                       nacked++;
+                               ath_rate_tx_complete(sc, an, ds, ds0);
+                       }
+                       /*
+                        * Reclaim reference to node.
+                        *
+                        * NB: the node may be reclaimed here if, for example
+                        *     this is a DEAUTH message that was sent and the
+                        *     node was timed out due to inactivity.
+                        */
+                       ieee80211_free_node(ni);
+               }
+               bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
+                   BUS_DMASYNC_POSTWRITE);
+               bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+               m_freem(bf->bf_m);
+               bf->bf_m = NULL;
+               bf->bf_node = NULL;
+
+               STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+       }
+       return nacked;
+}
+
+static __inline int
+txqactive(struct ath_hal *ah, int qnum)
+{
+       uint32_t txqs = 1<<qnum;
+       ath_hal_gettxintrtxqs(ah, &txqs);
+       return (txqs & (1<<qnum));
+}
+
+/*
+ * Deferred processing of transmit interrupt; special-cased
+ * for a single hardware transmit queue (e.g. 5210 and 5211).
+ */
+static void
+ath_tx_proc_q0(struct ath_softc *sc)
+{
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+       if (txqactive(sc->sc_ah, 0) && ath_tx_processq(sc, &sc->sc_txq[0]))
+               sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah);
+       if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum))
+               ath_tx_processq(sc, sc->sc_cabq);
+       ifp->if_flags &= ~IFF_OACTIVE;
+       sc->sc_tx_timer = 0;
+
+       if (sc->sc_softled)
+               ath_led_event(sc, ATH_LED_TX);
+
+       ath_start(ifp);
+}
+
+/*
+ * Deferred processing of transmit interrupt; special-cased
+ * for four hardware queues, 0-3 (e.g. 5212 w/ WME support).
+ */
+static void
+ath_tx_proc_q0123(struct ath_softc *sc)
+{
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       int nacked;
+
+       /*
+        * Process each active queue.
+        */
+       nacked = 0;
+       if (txqactive(sc->sc_ah, 0))
+               nacked += ath_tx_processq(sc, &sc->sc_txq[0]);
+       if (txqactive(sc->sc_ah, 1))
+               nacked += ath_tx_processq(sc, &sc->sc_txq[1]);
+       if (txqactive(sc->sc_ah, 2))
+               nacked += ath_tx_processq(sc, &sc->sc_txq[2]);
+       if (txqactive(sc->sc_ah, 3))
+               nacked += ath_tx_processq(sc, &sc->sc_txq[3]);
+       if (txqactive(sc->sc_ah, sc->sc_cabq->axq_qnum))
+               ath_tx_processq(sc, sc->sc_cabq);
+       if (nacked)
+               sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah);
+
+       ifp->if_flags &= ~IFF_OACTIVE;
+       sc->sc_tx_timer = 0;
+
+       if (sc->sc_softled)
+               ath_led_event(sc, ATH_LED_TX);
+
+       ath_start(ifp);
+}
+
+/*
+ * Deferred processing of transmit interrupt.
+ */
+static void
+ath_tx_proc(struct ath_softc *sc)
+{
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       int i, nacked;
+
+       /*
+        * Process each active queue.
+        */
+       nacked = 0;
+       for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
+               if (IS_ATH_TXQ_SETUP(sc, i) && txqactive(sc->sc_ah, i))
+                       nacked += ath_tx_processq(sc, &sc->sc_txq[i]);
+       if (nacked)
+               sc->sc_lastrx = ath_hal_gettsf64(sc->sc_ah);
+
+       ifp->if_flags &= ~IFF_OACTIVE;
+       sc->sc_tx_timer = 0;
+
+       if (sc->sc_softled)
+               ath_led_event(sc, ATH_LED_TX);
+
+       ath_start(ifp);
+}
+
+static void
+ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq)
+{
+#ifdef ATH_DEBUG
+       struct ath_hal *ah = sc->sc_ah;
+#endif
+       struct ieee80211_node *ni;
+       struct ath_buf *bf;
+       u_int ix;
+
+       /*
+        * NB: this assumes output has been stopped and
+        *     we do not need to block ath_tx_tasklet
+        */
+       for (ix = 0;; ix++) {
+               bf = STAILQ_FIRST(&txq->axq_q);
+               if (bf == NULL) {
+                       txq->axq_link = NULL;
+                       break;
+               }
+               ATH_TXQ_REMOVE_HEAD(txq, bf_list);
+#ifdef ATH_DEBUG
+               if (sc->sc_debug & ATH_DEBUG_RESET)
+                       ath_printtxbuf(bf, txq->axq_qnum, ix,
+                               ath_hal_txprocdesc(ah, bf->bf_desc) == HAL_OK);
+#endif /* ATH_DEBUG */
+               bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+               m_freem(bf->bf_m);
+               bf->bf_m = NULL;
+               ni = bf->bf_node;
+               bf->bf_node = NULL;
+               if (ni != NULL) {
+                       /*
+                        * Reclaim node reference.
+                        */
+                       ieee80211_free_node(ni);
+               }
+               STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+       }
+}
+
+static void
+ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq)
+{
+       struct ath_hal *ah = sc->sc_ah;
+
+       ath_hal_stoptxdma(ah, txq->axq_qnum);
+       DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n",
+           __func__, txq->axq_qnum,
+           (caddr_t)(uintptr_t)ath_hal_gettxbuf(ah, txq->axq_qnum),
+           txq->axq_link);
+}
+
+/*
+ * Drain the transmit queues and reclaim resources.
+ */
+static void
+ath_draintxq(struct ath_softc *sc)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       int i;
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+
+       /* XXX return value */
+       if (!sc->sc_invalid) {
+               /* don't touch the hardware if marked invalid */
+               ath_hal_stoptxdma(ah, sc->sc_bhalq);
+               DPRINTF(sc, ATH_DEBUG_RESET,
+                   "%s: beacon queue %p\n", __func__,
+                   (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, sc->sc_bhalq));
+               for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
+                       if (IS_ATH_TXQ_SETUP(sc, i))
+                               ath_tx_stopdma(sc, &sc->sc_txq[i]);
+       }
+       for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
+               if (IS_ATH_TXQ_SETUP(sc, i))
+                       ath_tx_draintxq(sc, &sc->sc_txq[i]);
+       ifp->if_flags &= ~IFF_OACTIVE;
+       sc->sc_tx_timer = 0;
+}
+
+/*
+ * Disable the receive h/w in preparation for a reset.
+ */
+static void
+ath_stoprecv(struct ath_softc *sc)
+{
+#define        PA2DESC(_sc, _pa) \
+       ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
+               ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
+       struct ath_hal *ah = sc->sc_ah;
+
+       ASSERT_SERIALIZED(sc->sc_ic.ic_if.if_serializer);
+
+       ath_hal_stoppcurecv(ah);        /* disable PCU */
+       ath_hal_setrxfilter(ah, 0);     /* clear recv filter */
+       ath_hal_stopdmarecv(ah);        /* disable DMA engine */
+       DELAY(3000);                    /* 3ms is long enough for 1 frame */
+#ifdef ATH_DEBUG
+       if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) {
+               struct ath_buf *bf;
+               u_int ix;
+
+               printf("%s: rx queue %p, link %p\n", __func__,
+                       (caddr_t)(uintptr_t) ath_hal_getrxbuf(ah),
+                       sc->sc_rxlink);
+               ix = 0;
+               STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
+                       struct ath_desc *ds = bf->bf_desc;
+                       HAL_STATUS status;
+                               
+                       status = ath_hal_rxprocdesc(ah, ds, bf->bf_daddr,
+                                                   PA2DESC(sc, ds->ds_link));
+                       if (status == HAL_OK ||
+                           (sc->sc_debug & ATH_DEBUG_FATAL))
+                               ath_printrxbuf(bf, ix, status == HAL_OK);
+                       ix++;
+               }
+       }
+#endif
+       sc->sc_rxlink = NULL;           /* just in case */
+#undef PA2DESC
+}
+
+/*
+ * Enable the receive h/w following a reset.
+ */
+static int
+ath_startrecv(struct ath_softc *sc)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       struct ath_buf *bf;
+
+       ASSERT_SERIALIZED(sc->sc_ic.ic_if.if_serializer);
+
+       sc->sc_rxlink = NULL;
+       STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
+               int error = ath_rxbuf_init(sc, bf);
+               if (error != 0) {
+                       DPRINTF(sc, ATH_DEBUG_RECV,
+                               "%s: ath_rxbuf_init failed %d\n",
+                               __func__, error);
+                       return error;
+               }
+       }
+
+       bf = STAILQ_FIRST(&sc->sc_rxbuf);
+       ath_hal_putrxbuf(ah, bf->bf_daddr);
+       ath_hal_rxena(ah);              /* enable recv descriptors */
+       ath_mode_init(sc);              /* set filters, etc. */
+       ath_hal_startpcurecv(ah);       /* re-enable PCU/DMA engine */
+       return 0;
+}
+
+/* 
+ * Update internal state after a channel change.
+ */
+static void
+ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       enum ieee80211_phymode mode;
+       uint16_t flags;
+
+       /*
+        * Change channels and update the h/w rate map
+        * if we're switching; e.g. 11a to 11b/g.
+        */
+       mode = ieee80211_chan2mode(ic, chan);
+       if (mode != sc->sc_curmode)
+               ath_setcurmode(sc, mode);
+       /*
+        * Update BPF state.  NB: ethereal et. al. don't handle
+        * merged flags well so pick a unique mode for their use.
+        */
+       if (IEEE80211_IS_CHAN_A(chan))
+               flags = IEEE80211_CHAN_A;
+       /* XXX 11g schizophrenia */
+       else if (IEEE80211_IS_CHAN_G(chan) ||
+           IEEE80211_IS_CHAN_PUREG(chan))
+               flags = IEEE80211_CHAN_G;
+       else
+               flags = IEEE80211_CHAN_B;
+       if (IEEE80211_IS_CHAN_T(chan))
+               flags |= IEEE80211_CHAN_TURBO;
+       sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq =
+               htole16(chan->ic_freq);
+       sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags =
+               htole16(flags);
+}
+
+/*
+ * Poll for a channel clear indication; this is required
+ * for channels requiring DFS and not previously visited
+ * and/or with a recent radar detection.
+ */
+static void
+ath_dfswait(void *arg)
+{
+       struct ath_softc *sc = arg;
+       struct ath_hal *ah = sc->sc_ah;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       HAL_CHANNEL hchan;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       ath_hal_radar_wait(ah, &hchan);
+       DPRINTF(sc, ATH_DEBUG_DFS, "%s: radar_wait %u/%x/%x\n",
+           __func__, hchan.channel, hchan.channelFlags, hchan.privFlags);
+
+       if (hchan.privFlags & CHANNEL_INTERFERENCE) {
+               if_printf(ifp, "channel %u/0x%x/0x%x has interference\n",
+                   hchan.channel, hchan.channelFlags, hchan.privFlags);
+               goto back;
+       }
+       if ((hchan.privFlags & CHANNEL_DFS) == 0) {
+               /* XXX should not happen */
+               goto back;
+       }
+       if (hchan.privFlags & CHANNEL_DFS_CLEAR) {
+               sc->sc_curchan.privFlags |= CHANNEL_DFS_CLEAR;
+               ifp->if_flags &= ~IFF_OACTIVE;
+               if_printf(ifp, "channel %u/0x%x/0x%x marked clear\n",
+                   hchan.channel, hchan.channelFlags, hchan.privFlags);
+       } else {
+               callout_reset(&sc->sc_dfs_ch, 2 * hz, ath_dfswait, sc);
+       }
+
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+/*
+ * Set/change channels.  If the channel is really being changed,
+ * it's done by reseting the chip.  To accomplish this we must
+ * first cleanup any pending DMA, then restart stuff after a la
+ * ath_init.
+ */
+static int
+ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+       HAL_CHANNEL hchan;
+
+       /*
+        * Convert to a HAL channel description with
+        * the flags constrained to reflect the current
+        * operating mode.
+        */
+       hchan.channel = chan->ic_freq;
+       hchan.channelFlags = ath_chan2flags(ic, chan);
+
+       DPRINTF(sc, ATH_DEBUG_RESET,
+           "%s: %u (%u MHz, hal flags 0x%x) -> %u (%u MHz, hal flags 0x%x)\n",
+           __func__,
+           ath_hal_mhz2ieee(ah, sc->sc_curchan.channel,
+               sc->sc_curchan.channelFlags),
+               sc->sc_curchan.channel, sc->sc_curchan.channelFlags,
+           ath_hal_mhz2ieee(ah, hchan.channel, hchan.channelFlags),
+               hchan.channel, hchan.channelFlags);
+       if (hchan.channel != sc->sc_curchan.channel ||
+           hchan.channelFlags != sc->sc_curchan.channelFlags) {
+               HAL_STATUS status;
+
+               /*
+                * To switch channels clear any pending DMA operations;
+                * wait long enough for the RX fifo to drain, reset the
+                * hardware at the new frequency, and then re-enable
+                * the relevant bits of the h/w.
+                */
+               ath_hal_intrset(ah, 0);         /* disable interrupts */
+               ath_draintxq(sc);               /* clear pending tx frames */
+               ath_stoprecv(sc);               /* turn off frame recv */
+               if (!ath_hal_reset(ah, sc->sc_opmode, &hchan, AH_TRUE, &status)) {
+                       if_printf(ifp, "%s: unable to reset "
+                           "channel %u (%u Mhz, flags 0x%x hal flags 0x%x)\n",
+                           __func__, ieee80211_chan2ieee(ic, chan),
+                           chan->ic_freq, chan->ic_flags, hchan.channelFlags);
+                       return EIO;
+               }
+               sc->sc_curchan = hchan;
+               ath_update_txpow(sc);           /* update tx power state */
+               sc->sc_diversity = ath_hal_getdiversity(ah);
+               sc->sc_calinterval = 1;
+               sc->sc_caltries = 0;
+
+               /*
+                * Re-enable rx framework.
+                */
+               if (ath_startrecv(sc) != 0) {
+                       if_printf(ic->ic_ifp,
+                               "%s: unable to restart recv logic\n", __func__);
+                       return EIO;
+               }
+
+               /*
+                * Change channels and update the h/w rate map
+                * if we're switching; e.g. 11a to 11b/g.
+                */
+               ic->ic_ibss_chan = chan;
+               ath_chan_change(sc, chan);
+
+               /*
+                * Handle DFS required waiting period to determine
+                * if channel is clear of radar traffic.
+                */
+               if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+#define        DFS_AND_NOT_CLEAR(_c) \
+       (((_c)->privFlags & (CHANNEL_DFS | CHANNEL_DFS_CLEAR)) == CHANNEL_DFS)
+                       if (DFS_AND_NOT_CLEAR(&sc->sc_curchan)) {
+                               if_printf(ifp,
+                                       "wait for DFS clear channel signal\n");
+                               /* XXX stop sndq */
+                               ifp->if_flags |= IFF_OACTIVE;
+                               callout_reset(&sc->sc_dfs_ch,
+                                       2 * hz, ath_dfswait, sc);
+                       } else {
+                               callout_stop(&sc->sc_dfs_ch);
+                       }
+#undef DFS_NOT_CLEAR
+               }
+
+               /*
+                * Re-enable interrupts.
+                */
+               ath_hal_intrset(ah, sc->sc_imask);
+       }
+       return 0;
+}
+
+static void
+ath_next_scan(void *arg)
+{
+       struct ath_softc *sc = arg;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       if (ic->ic_state == IEEE80211_S_SCAN)
+               ieee80211_next_scan(ic);
+
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+/*
+ * Periodically recalibrate the PHY to account
+ * for temperature/environment changes.
+ */
+static void
+ath_calibrate(void *arg)
+{
+       struct ath_softc *sc = arg;
+       struct ath_hal *ah = sc->sc_ah;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       HAL_BOOL iqCalDone;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       sc->sc_stats.ast_per_cal++;
+
+       if (ath_hal_getrfgain(ah) == HAL_RFGAIN_NEED_CHANGE) {
+               /*
+                * Rfgain is out of bounds, reset the chip
+                * to load new gain values.
+                */
+               DPRINTF(sc, ATH_DEBUG_CALIBRATE,
+                       "%s: rfgain change\n", __func__);
+               sc->sc_stats.ast_per_rfgain++;
+               ath_reset(&sc->sc_ic.ic_if);
+       }
+       if (!ath_hal_calibrate(ah, &sc->sc_curchan, &iqCalDone)) {
+               DPRINTF(sc, ATH_DEBUG_ANY,
+                       "%s: calibration of channel %u failed\n",
+                       __func__, sc->sc_curchan.channel);
+               sc->sc_stats.ast_per_calfail++;
+       }
+       /*
+        * Calibrate noise floor data again in case of change.
+        */
+       ath_hal_process_noisefloor(ah);
+       /*
+        * Poll more frequently when the IQ calibration is in
+        * progress to speedup loading the final settings. 
+        * We temper this aggressive polling with an exponential
+        * back off after 4 tries up to ath_calinterval.
+        */
+       if (iqCalDone || sc->sc_calinterval >= ath_calinterval) {
+               sc->sc_caltries = 0;
+               sc->sc_calinterval = ath_calinterval;
+       } else if (sc->sc_caltries > 4) {
+               sc->sc_caltries = 0;
+               sc->sc_calinterval <<= 1;
+               if (sc->sc_calinterval > ath_calinterval)
+                       sc->sc_calinterval = ath_calinterval;
+       }
+       KASSERT(0 < sc->sc_calinterval && sc->sc_calinterval <= ath_calinterval,
+               ("bad calibration interval %u", sc->sc_calinterval));
+
+       DPRINTF(sc, ATH_DEBUG_CALIBRATE,
+               "%s: next +%u (%siqCalDone tries %u)\n", __func__,
+               sc->sc_calinterval, iqCalDone ? "" : "!", sc->sc_caltries);
+       sc->sc_caltries++;
+       callout_reset(&sc->sc_cal_ch, sc->sc_calinterval * hz,
+               ath_calibrate, sc);
+
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+static int
+ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+{
+       struct ifnet *ifp = ic->ic_ifp;
+       struct ath_softc *sc = ifp->if_softc;
+       struct ath_hal *ah = sc->sc_ah;
+       struct ieee80211_node *ni;
+       int i, error;
+       const uint8_t *bssid;
+       uint32_t rfilt;
+       static const HAL_LED_STATE leds[] = {
+           HAL_LED_INIT,       /* IEEE80211_S_INIT */
+           HAL_LED_SCAN,       /* IEEE80211_S_SCAN */
+           HAL_LED_AUTH,       /* IEEE80211_S_AUTH */
+           HAL_LED_ASSOC,      /* IEEE80211_S_ASSOC */
+           HAL_LED_RUN,        /* IEEE80211_S_RUN */
+       };
+
+       DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__,
+               ieee80211_state_name[ic->ic_state],
+               ieee80211_state_name[nstate]);
+
+       callout_stop(&sc->sc_scan_ch);
+       callout_stop(&sc->sc_cal_ch);
+       callout_stop(&sc->sc_dfs_ch);
+       ath_hal_setledstate(ah, leds[nstate]);  /* set LED */
+
+       if (nstate == IEEE80211_S_INIT) {
+               sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
+               /*
+                * NB: disable interrupts so we don't rx frames.
+                */
+               ath_hal_intrset(ah, sc->sc_imask &~ HAL_INT_GLOBAL);
+               /*
+                * Notify the rate control algorithm.
+                */
+               ath_rate_newstate(sc, nstate);
+               goto done;
+       }
+       ni = ic->ic_bss;
+       error = ath_chan_set(sc, ic->ic_curchan);
+       if (error != 0)
+               goto bad;
+       rfilt = ath_calcrxfilter(sc, nstate);
+       if (nstate == IEEE80211_S_SCAN)
+               bssid = ifp->if_broadcastaddr;
+       else
+               bssid = ni->ni_bssid;
+       ath_hal_setrxfilter(ah, rfilt);
+       DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %6D\n",
+                __func__, rfilt, bssid, ":");
+
+       if (nstate == IEEE80211_S_RUN && ic->ic_opmode == IEEE80211_M_STA)
+               ath_hal_setassocid(ah, bssid, ni->ni_associd);
+       else
+               ath_hal_setassocid(ah, bssid, 0);
+       if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+               for (i = 0; i < IEEE80211_WEP_NKID; i++)
+                       if (ath_hal_keyisvalid(ah, i))
+                               ath_hal_keysetmac(ah, i, bssid);
+       }
+
+       /*
+        * Notify the rate control algorithm so rates
+        * are setup should ath_beacon_alloc be called.
+        */
+       ath_rate_newstate(sc, nstate);
+
+       if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+               /* nothing to do */;
+       } else if (nstate == IEEE80211_S_RUN) {
+               DPRINTF(sc, ATH_DEBUG_STATE,
+                       "%s(RUN): ic_flags=0x%08x iv=%d bssid=%6D "
+                       "capinfo=0x%04x chan=%d\n"
+                        , __func__
+                        , ic->ic_flags
+                        , ni->ni_intval
+                        , ni->ni_bssid, ":"
+                        , ni->ni_capinfo
+                        , ieee80211_chan2ieee(ic, ic->ic_curchan));
+
+               switch (ic->ic_opmode) {
+               case IEEE80211_M_HOSTAP:
+               case IEEE80211_M_IBSS:
+                       /*
+                        * Allocate and setup the beacon frame.
+                        *
+                        * Stop any previous beacon DMA.  This may be
+                        * necessary, for example, when an ibss merge
+                        * causes reconfiguration; there will be a state
+                        * transition from RUN->RUN that means we may
+                        * be called with beacon transmission active.
+                        */
+                       ath_hal_stoptxdma(ah, sc->sc_bhalq);
+                       ath_beacon_free(sc);
+                       error = ath_beacon_alloc(sc, ni);
+                       if (error != 0)
+                               goto bad;
+                       /*
+                        * If joining an adhoc network defer beacon timer
+                        * configuration to the next beacon frame so we
+                        * have a current TSF to use.  Otherwise we're
+                        * starting an ibss/bss so there's no need to delay.
+                        */
+                       if (ic->ic_opmode == IEEE80211_M_IBSS &&
+                           ic->ic_bss->ni_tstamp.tsf != 0)
+                               sc->sc_syncbeacon = 1;
+                       else
+                               ath_beacon_config(sc);
+                       break;
+               case IEEE80211_M_STA:
+                       /*
+                        * Allocate a key cache slot to the station.
+                        */
+                       if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0 &&
+                           sc->sc_hasclrkey &&
+                           ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE)
+                               ath_setup_stationkey(ni);
+                       /*
+                        * Defer beacon timer configuration to the next
+                        * beacon frame so we have a current TSF to use
+                        * (any TSF collected when scanning is likely old).
+                        */
+                       sc->sc_syncbeacon = 1;
+                       break;
+               default:
+                       break;
+               }
+
+               /*
+                * Let the hal process statistics collected during a
+                * scan so it can provide calibrated noise floor data.
+                */
+               ath_hal_process_noisefloor(ah);
+               /*
+                * Reset rssi stats; maybe not the best place...
+                */
+               sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
+               sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER;
+               sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
+       } else {
+               ath_hal_intrset(ah,
+                       sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS));
+               sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
+       }
+done:
+       /*
+        * Invoke the parent method to complete the work.
+        */
+       error = sc->sc_newstate(ic, nstate, arg);
+       /*
+        * Finally, start any timers.
+        */
+       if (nstate == IEEE80211_S_RUN) {
+               /* start periodic recalibration timer */
+               callout_reset(&sc->sc_cal_ch, sc->sc_calinterval * hz,
+                       ath_calibrate, sc);
+       } else if (nstate == IEEE80211_S_SCAN) {
+               /* start ap/neighbor scan timer */
+               callout_reset(&sc->sc_scan_ch, (ath_dwelltime * hz) / 1000,
+                       ath_next_scan, sc);
+       }
+bad:
+       return error;
+}
+
+/*
+ * Allocate a key cache slot to the station so we can
+ * setup a mapping from key index to node. The key cache
+ * slot is needed for managing antenna state and for
+ * compression when stations do not use crypto.  We do
+ * it uniliaterally here; if crypto is employed this slot
+ * will be reassigned.
+ */
+static void
+ath_setup_stationkey(struct ieee80211_node *ni)
+{
+       struct ieee80211com *ic = ni->ni_ic;
+       struct ath_softc *sc = ic->ic_ifp->if_softc;
+       ieee80211_keyix keyix, rxkeyix;
+
+       if (!ath_key_alloc(ic, &ni->ni_ucastkey, &keyix, &rxkeyix)) {
+               /*
+                * Key cache is full; we'll fall back to doing
+                * the more expensive lookup in software.  Note
+                * this also means no h/w compression.
+                */
+               /* XXX msg+statistic */
+       } else {
+               /* XXX locking? */
+               ni->ni_ucastkey.wk_keyix = keyix;
+               ni->ni_ucastkey.wk_rxkeyix = rxkeyix;
+               /* NB: this will create a pass-thru key entry */
+               ath_keyset(sc, &ni->ni_ucastkey, ni->ni_macaddr, ic->ic_bss);
+       }
+}
+
+/*
+ * Setup driver-specific state for a newly associated node.
+ * Note that we're called also on a re-associate, the isnew
+ * param tells us if this is the first time or not.
+ */
+static void
+ath_newassoc(struct ieee80211_node *ni, int isnew)
+{
+       struct ieee80211com *ic = ni->ni_ic;
+       struct ath_softc *sc = ic->ic_ifp->if_softc;
+
+       ath_rate_newassoc(sc, ATH_NODE(ni), isnew);
+       if (isnew &&
+           (ic->ic_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey) {
+               KASSERT(ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE,
+                   ("new assoc with a unicast key already setup (keyix %u)",
+                   ni->ni_ucastkey.wk_keyix));
+               ath_setup_stationkey(ni);
+       }
+}
+
+static int
+ath_getchannels(struct ath_softc *sc, u_int cc,
+       HAL_BOOL outdoor, HAL_BOOL xchanmode)
+{
+#define        COMPAT  (CHANNEL_ALL_NOTURBO|CHANNEL_PASSIVE)
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+       struct ath_hal *ah = sc->sc_ah;
+       HAL_CHANNEL *chans;
+       int i, ix, nchan;
+
+       chans = malloc(IEEE80211_CHAN_MAX * sizeof(HAL_CHANNEL), M_TEMP,
+                      M_WAITOK);
+
+       if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan,
+           NULL, 0, NULL,
+           cc, HAL_MODE_ALL, outdoor, xchanmode)) {
+               uint32_t rd;
+
+               ath_hal_getregdomain(ah, &rd);
+               if_printf(ifp, "unable to collect channel list from hal; "
+                       "regdomain likely %u country code %u\n", rd, cc);
+               free(chans, M_TEMP);
+               return EINVAL;
+       }
+
+       /*
+        * Convert HAL channels to ieee80211 ones and insert
+        * them in the table according to their channel number.
+        */
+       for (i = 0; i < nchan; i++) {
+               HAL_CHANNEL *c = &chans[i];
+               uint16_t flags;
+
+               ix = ath_hal_mhz2ieee(ah, c->channel, c->channelFlags);
+               if (ix > IEEE80211_CHAN_MAX) {
+                       if_printf(ifp, "bad hal channel %d (%u/%x) ignored\n",
+                               ix, c->channel, c->channelFlags);
+                       continue;
+               }
+               if (ix < 0) {
+                       /* XXX can't handle stuff <2400 right now */
+                       if (bootverbose)
+                               if_printf(ifp, "hal channel %d (%u/%x) "
+                                   "cannot be handled; ignored\n",
+                                   ix, c->channel, c->channelFlags);
+                       continue;
+               }
+               /*
+                * Calculate net80211 flags; most are compatible
+                * but some need massaging.  Note the static turbo
+                * conversion can be removed once net80211 is updated
+                * to understand static vs. dynamic turbo.
+                */
+               flags = c->channelFlags & COMPAT;
+               if (c->channelFlags & CHANNEL_STURBO)
+                       flags |= IEEE80211_CHAN_TURBO;
+               if (ic->ic_channels[ix].ic_freq == 0) {
+                       ic->ic_channels[ix].ic_freq = c->channel;
+                       ic->ic_channels[ix].ic_flags = flags;
+               } else {
+                       /* channels overlap; e.g. 11g and 11b */
+                       ic->ic_channels[ix].ic_flags |= flags;
+               }
+       }
+       free(chans, M_TEMP);
+       return 0;
+#undef COMPAT
+}
+
+static void
+ath_led_done(void *arg)
+{
+       struct ath_softc *sc = arg;
+
+       sc->sc_blinking = 0;
+}
+
+/*
+ * Turn the LED off: flip the pin and then set a timer so no
+ * update will happen for the specified duration.
+ */
+static void
+ath_led_off(void *arg)
+{
+       struct ath_softc *sc = arg;
+
+       ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon);
+       callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, ath_led_done, sc);
+}
+
+/*
+ * Blink the LED according to the specified on/off times.
+ */
+static void
+ath_led_blink(struct ath_softc *sc, int on, int off)
+{
+       DPRINTF(sc, ATH_DEBUG_LED, "%s: on %u off %u\n", __func__, on, off);
+       ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, sc->sc_ledon);
+       sc->sc_blinking = 1;
+       sc->sc_ledoff = off;
+       callout_reset(&sc->sc_ledtimer, on, ath_led_off, sc);
+}
+
+static void
+ath_led_event(struct ath_softc *sc, int event)
+{
+
+       sc->sc_ledevent = ticks;        /* time of last event */
+       if (sc->sc_blinking)            /* don't interrupt active blink */
+               return;
+       switch (event) {
+       case ATH_LED_POLL:
+               ath_led_blink(sc, sc->sc_hwmap[0].ledon,
+                       sc->sc_hwmap[0].ledoff);
+               break;
+       case ATH_LED_TX:
+               ath_led_blink(sc, sc->sc_hwmap[sc->sc_txrate].ledon,
+                       sc->sc_hwmap[sc->sc_txrate].ledoff);
+               break;
+       case ATH_LED_RX:
+               ath_led_blink(sc, sc->sc_hwmap[sc->sc_rxrate].ledon,
+                       sc->sc_hwmap[sc->sc_rxrate].ledoff);
+               break;
+       }
+}
+
+static void
+ath_update_txpow(struct ath_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ath_hal *ah = sc->sc_ah;
+       uint32_t txpow;
+
+       if (sc->sc_curtxpow != ic->ic_txpowlimit) {
+               ath_hal_settxpowlimit(ah, ic->ic_txpowlimit);
+               /* read back in case value is clamped */
+               ath_hal_gettxpowlimit(ah, &txpow);
+               ic->ic_txpowlimit = sc->sc_curtxpow = txpow;
+       }
+       /* 
+        * Fetch max tx power level for status requests.
+        */
+       ath_hal_getmaxtxpow(sc->sc_ah, &txpow);
+       ic->ic_bss->ni_txpower = txpow;
+}
+
+static void
+rate_setup(struct ath_softc *sc,
+       const HAL_RATE_TABLE *rt, struct ieee80211_rateset *rs)
+{
+       int i, maxrates;
+
+       if (rt->rateCount > IEEE80211_RATE_MAXSIZE) {
+               DPRINTF(sc, ATH_DEBUG_ANY,
+                       "%s: rate table too small (%u > %u)\n",
+                      __func__, rt->rateCount, IEEE80211_RATE_MAXSIZE);
+               maxrates = IEEE80211_RATE_MAXSIZE;
+       } else
+               maxrates = rt->rateCount;
+       for (i = 0; i < maxrates; i++)
+               rs->rs_rates[i] = rt->info[i].dot11Rate;
+       rs->rs_nrates = maxrates;
+}
+
+static int
+ath_rate_setup(struct ath_softc *sc, u_int mode)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       struct ieee80211com *ic = &sc->sc_ic;
+       const HAL_RATE_TABLE *rt;
+
+       switch (mode) {
+       case IEEE80211_MODE_11A:
+               rt = ath_hal_getratetable(ah, HAL_MODE_11A);
+               break;
+       case IEEE80211_MODE_11B:
+               rt = ath_hal_getratetable(ah, HAL_MODE_11B);
+               break;
+       case IEEE80211_MODE_11G:
+               rt = ath_hal_getratetable(ah, HAL_MODE_11G);
+               break;
+       case IEEE80211_MODE_TURBO_A:
+               /* XXX until static/dynamic turbo is fixed */
+               rt = ath_hal_getratetable(ah, HAL_MODE_TURBO);
+               break;
+       case IEEE80211_MODE_TURBO_G:
+               rt = ath_hal_getratetable(ah, HAL_MODE_108G);
+               break;
+       default:
+               DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid mode %u\n",
+                       __func__, mode);
+               return 0;
+       }
+       sc->sc_rates[mode] = rt;
+       if (rt != NULL) {
+               rate_setup(sc, rt, &ic->ic_sup_rates[mode]);
+               return 1;
+       } else
+               return 0;
+}
+
+static void
+ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode)
+{
+#define        N(a)    (sizeof(a)/sizeof(a[0]))
+       /* NB: on/off times from the Atheros NDIS driver, w/ permission */
+       static const struct {
+               u_int           rate;           /* tx/rx 802.11 rate */
+               uint16_t        timeOn;         /* LED on time (ms) */
+               uint16_t        timeOff;        /* LED off time (ms) */
+       } blinkrates[] = {
+               { 108,  40,  10 },
+               {  96,  44,  11 },
+               {  72,  50,  13 },
+               {  48,  57,  14 },
+               {  36,  67,  16 },
+               {  24,  80,  20 },
+               {  22, 100,  25 },
+               {  18, 133,  34 },
+               {  12, 160,  40 },
+               {  10, 200,  50 },
+               {   6, 240,  58 },
+               {   4, 267,  66 },
+               {   2, 400, 100 },
+               {   0, 500, 130 },
+       };
+       const HAL_RATE_TABLE *rt;
+       int i, j;
+
+       memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap));
+       rt = sc->sc_rates[mode];
+       KASSERT(rt != NULL, ("no h/w rate set for phy mode %u", mode));
+       for (i = 0; i < rt->rateCount; i++)
+               sc->sc_rixmap[rt->info[i].dot11Rate & IEEE80211_RATE_VAL] = i;
+       memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap));
+       for (i = 0; i < 32; i++) {
+               uint8_t ix = rt->rateCodeToIndex[i];
+               if (ix == 0xff) {
+                       sc->sc_hwmap[i].ledon = (500 * hz) / 1000;
+                       sc->sc_hwmap[i].ledoff = (130 * hz) / 1000;
+                       continue;
+               }
+               sc->sc_hwmap[i].ieeerate =
+                       rt->info[ix].dot11Rate & IEEE80211_RATE_VAL;
+               sc->sc_hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD;
+               if (rt->info[ix].shortPreamble ||
+                   rt->info[ix].phy == IEEE80211_T_OFDM)
+                       sc->sc_hwmap[i].txflags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+               /* NB: receive frames include FCS */
+               sc->sc_hwmap[i].rxflags = sc->sc_hwmap[i].txflags |
+                       IEEE80211_RADIOTAP_F_FCS;
+               /* setup blink rate table to avoid per-packet lookup */
+               for (j = 0; j < N(blinkrates)-1; j++)
+                       if (blinkrates[j].rate == sc->sc_hwmap[i].ieeerate)
+                               break;
+               /* NB: this uses the last entry if the rate isn't found */
+               /* XXX beware of overlow */
+               sc->sc_hwmap[i].ledon = (blinkrates[j].timeOn * hz) / 1000;
+               sc->sc_hwmap[i].ledoff = (blinkrates[j].timeOff * hz) / 1000;
+       }
+       sc->sc_currates = rt;
+       sc->sc_curmode = mode;
+       /*
+        * All protection frames are transmited at 2Mb/s for
+        * 11g, otherwise at 1Mb/s.
+        */
+       if (mode == IEEE80211_MODE_11G)
+               sc->sc_protrix = ath_tx_findrix(rt, 2 * 2);
+       else
+               sc->sc_protrix = ath_tx_findrix(rt, 2 * 1);
+       /* rate index used to send management frames */
+       sc->sc_minrateix = 0;
+       /*
+        * Setup multicast rate state.
+        */
+       /* XXX layering violation */
+       sc->sc_mcastrix = ath_tx_findrix(rt, sc->sc_ic.ic_mcast_rate);
+       sc->sc_mcastrate = sc->sc_ic.ic_mcast_rate;
+       /* NB: caller is responsible for reseting rate control state */
+#undef N
+}
+
+#ifdef ATH_DEBUG
+static void
+ath_printrxbuf(struct ath_buf *bf, u_int ix, int done)
+{
+       struct ath_desc *ds;
+       int i;
+
+       for (i = 0, ds = bf->bf_desc; i < bf->bf_nseg; i++, ds++) {
+               printf("R[%2u] (DS.V:%p DS.P:%p) L:%08x D:%08x%s\n"
+                      "      %08x %08x %08x %08x\n",
+                   ix, ds, (struct ath_desc *)bf->bf_daddr + i,
+                   ds->ds_link, ds->ds_data,
+                   !done ? "" : (ds->ds_rxstat.rs_status == 0) ? " *" : " !",
+                   ds->ds_ctl0, ds->ds_ctl1,
+                   ds->ds_hw[0], ds->ds_hw[1]);
+       }
+}
+
+static void
+ath_printtxbuf(struct ath_buf *bf, u_int qnum, u_int ix, int done)
+{
+       struct ath_desc *ds;
+       int i;
+
+       printf("Q%u[%3u]", qnum, ix);
+       for (i = 0, ds = bf->bf_desc; i < bf->bf_nseg; i++, ds++) {
+               printf(" (DS.V:%p DS.P:%p) L:%08x D:%08x F:04%x%s\n"
+                      "        %08x %08x %08x %08x %08x %08x\n",
+                   ds, (struct ath_desc *)bf->bf_daddr + i,
+                   ds->ds_link, ds->ds_data, bf->bf_flags,
+                   !done ? "" : (ds->ds_txstat.ts_status == 0) ? " *" : " !",
+                   ds->ds_ctl0, ds->ds_ctl1,
+                   ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3]);
+       }
+}
+#endif /* ATH_DEBUG */
+
+static void
+ath_watchdog(struct ifnet *ifp)
+{
+       struct ath_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+
+       ifp->if_timer = 0;
+       if ((ifp->if_flags & IFF_RUNNING) == 0 || sc->sc_invalid)
+               return;
+       if (sc->sc_tx_timer) {
+               if (--sc->sc_tx_timer == 0) {
+                       if_printf(ifp, "device timeout\n");
+                       ath_reset(ifp);
+                       ifp->if_oerrors++;
+                       sc->sc_stats.ast_watchdog++;
+               } else
+                       ifp->if_timer = 1;
+       }
+       ieee80211_watchdog(ic);
+}
+
+#ifdef ATH_DIAGAPI
+/*
+ * Diagnostic interface to the HAL.  This is used by various
+ * tools to do things like retrieve register contents for
+ * debugging.  The mechanism is intentionally opaque so that
+ * it can change frequently w/o concern for compatiblity.
+ */
+static int
+ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       u_int id = ad->ad_id & ATH_DIAG_ID;
+       void *indata = NULL;
+       void *outdata = NULL;
+       uint32_t insize = ad->ad_in_size;
+       uint32_t outsize = ad->ad_out_size;
+       int error = 0;
+
+       if (ad->ad_id & ATH_DIAG_IN) {
+               /*
+                * Copy in data.
+                */
+               indata = malloc(insize, M_TEMP, M_NOWAIT);
+               if (indata == NULL) {
+                       error = ENOMEM;
+                       goto bad;
+               }
+               error = copyin(ad->ad_in_data, indata, insize);
+               if (error)
+                       goto bad;
+       }
+       if (ad->ad_id & ATH_DIAG_DYN) {
+               /*
+                * Allocate a buffer for the results (otherwise the HAL
+                * returns a pointer to a buffer where we can read the
+                * results).  Note that we depend on the HAL leaving this
+                * pointer for us to use below in reclaiming the buffer;
+                * may want to be more defensive.
+                */
+               outdata = malloc(outsize, M_TEMP, M_NOWAIT);
+               if (outdata == NULL) {
+                       error = ENOMEM;
+                       goto bad;
+               }
+       }
+       if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) {
+               if (outsize < ad->ad_out_size)
+                       ad->ad_out_size = outsize;
+               if (outdata != NULL)
+                       error = copyout(outdata, ad->ad_out_data,
+                                       ad->ad_out_size);
+       } else {
+               error = EINVAL;
+       }
+bad:
+       if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
+               free(indata, M_TEMP);
+       if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
+               free(outdata, M_TEMP);
+       return error;
+}
+#endif /* ATH_DIAGAPI */
+
+static int
+ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
+{
+#define        IS_RUNNING(ifp) \
+       ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING))
+       struct ath_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifreq *ifr = (struct ifreq *)data;
+       int error = 0;
+
+       ASSERT_SERIALIZED(ifp->if_serializer);
+
+       switch (cmd) {
+       case SIOCSIFFLAGS:
+               if (IS_RUNNING(ifp)) {
+                       /*
+                        * To avoid rescanning another access point,
+                        * do not call ath_init() here.  Instead,
+                        * only reflect promisc mode settings.
+                        */
+                       ath_mode_init(sc);
+               } else if (ifp->if_flags & IFF_UP) {
+                       /*
+                        * Beware of being called during attach/detach
+                        * to reset promiscuous mode.  In that case we
+                        * will still be marked UP but not RUNNING.
+                        * However trying to re-init the interface
+                        * is the wrong thing to do as we've already
+                        * torn down much of our state.  There's
+                        * probably a better way to deal with this.
+                        */
+                       if (!sc->sc_invalid && ic->ic_bss != NULL)
+                               ath_init(sc);   /* XXX lose error */
+               } else
+                       ath_stop_no_pwchg(ifp);
+               break;
+       case SIOCADDMULTI:
+       case SIOCDELMULTI:
+               /*
+                * The upper layer has already installed/removed
+                * the multicast address(es), just recalculate the
+                * multicast filter for the card.
+                */
+               if (ifp->if_flags & IFF_RUNNING)
+                       ath_mode_init(sc);
+               break;
+       case SIOCGATHSTATS:
+               /* NB: embed these numbers to get a consistent view */
+               sc->sc_stats.ast_tx_packets = ifp->if_opackets;
+               sc->sc_stats.ast_rx_packets = ifp->if_ipackets;
+               sc->sc_stats.ast_rx_rssi = ieee80211_getrssi(ic);
+               /*
+                * NB: Drop the softc lock in case of a page fault;
+                * we'll accept any potential inconsisentcy in the
+                * statistics.  The alternative is to copy the data
+                * to a local structure.
+                */
+               return copyout(&sc->sc_stats,
+                               ifr->ifr_data, sizeof (sc->sc_stats));
+#ifdef ATH_DIAGAPI
+       case SIOCGATHDIAG:
+               error = ath_ioctl_diag(sc, (struct ath_diag *)ifr);
+               break;
+#endif
+       default:
+               error = ieee80211_ioctl(ic, cmd, data, cr);
+               if (error == ENETRESET) {
+                       if (IS_RUNNING(ifp) && 
+                           ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
+                               ath_init(sc);   /* XXX lose error */
+                       error = 0;
+               }
+               if (error == ERESTART)
+                       error = IS_RUNNING(ifp) ? ath_reset(ifp) : 0;
+               break;
+       }
+       return error;
+#undef IS_RUNNING
+}
+
+static int
+ath_sysctl_slottime(SYSCTL_HANDLER_ARGS)
+{
+       struct ath_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       u_int slottime;
+       int error;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       slottime = ath_hal_getslottime(sc->sc_ah);
+       error = sysctl_handle_int(oidp, &slottime, 0, req);
+       if (error || !req->newptr)
+               goto back;
+       error = !ath_hal_setslottime(sc->sc_ah, slottime) ? EINVAL : 0;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+ath_sysctl_acktimeout(SYSCTL_HANDLER_ARGS)
+{
+       struct ath_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       u_int acktimeout;
+       int error;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       acktimeout = ath_hal_getacktimeout(sc->sc_ah);
+       error = sysctl_handle_int(oidp, &acktimeout, 0, req);
+       if (error || !req->newptr)
+               goto back;
+       error = !ath_hal_setacktimeout(sc->sc_ah, acktimeout) ? EINVAL : 0;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+ath_sysctl_ctstimeout(SYSCTL_HANDLER_ARGS)
+{
+       struct ath_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       u_int ctstimeout;
+       int error;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       ctstimeout = ath_hal_getctstimeout(sc->sc_ah);
+       error = sysctl_handle_int(oidp, &ctstimeout, 0, req);
+       if (error || !req->newptr)
+               goto back;
+       error = !ath_hal_setctstimeout(sc->sc_ah, ctstimeout) ? EINVAL : 0;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+ath_sysctl_softled(SYSCTL_HANDLER_ARGS)
+{
+       struct ath_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       int softled;
+       int error;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       softled = sc->sc_softled;
+       error = sysctl_handle_int(oidp, &softled, 0, req);
+       if (error || !req->newptr)
+               goto back;
+       softled = (softled != 0);
+       if (softled != sc->sc_softled) {
+               if (softled) {
+                       /* NB: handle any sc_ledpin change */
+                       ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin);
+                       ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin,
+                               !sc->sc_ledon);
+               }
+               sc->sc_softled = softled;
+       }
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+ath_sysctl_rxantenna(SYSCTL_HANDLER_ARGS)
+{
+       struct ath_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       u_int defantenna;
+       int error;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       defantenna = ath_hal_getdefantenna(sc->sc_ah);
+       error = sysctl_handle_int(oidp, &defantenna, 0, req);
+       if (!error && req->newptr)
+               ath_hal_setdefantenna(sc->sc_ah, defantenna);
+
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+ath_sysctl_diversity(SYSCTL_HANDLER_ARGS)
+{
+       struct ath_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       u_int diversity;
+       int error;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       diversity = ath_hal_getdiversity(sc->sc_ah);
+       error = sysctl_handle_int(oidp, &diversity, 0, req);
+       if (error || !req->newptr)
+               goto back;
+       if (!ath_hal_setdiversity(sc->sc_ah, diversity)) {
+               error = EINVAL;
+               goto back;
+       }
+       sc->sc_diversity = diversity;
+       error = 0;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+ath_sysctl_diag(SYSCTL_HANDLER_ARGS)
+{
+       struct ath_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       uint32_t diag;
+       int error;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       if (!ath_hal_getdiag(sc->sc_ah, &diag)) {
+               error = EINVAL;
+               goto back;
+       }
+       error = sysctl_handle_int(oidp, &diag, 0, req);
+       if (error || !req->newptr)
+               goto back;
+       error = !ath_hal_setdiag(sc->sc_ah, diag) ? EINVAL : 0;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+ath_sysctl_tpscale(SYSCTL_HANDLER_ARGS)
+{
+       struct ath_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       uint32_t scale;
+       int error;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       ath_hal_gettpscale(sc->sc_ah, &scale);
+       error = sysctl_handle_int(oidp, &scale, 0, req);
+       if (error || !req->newptr)
+               goto back;
+       error = !ath_hal_settpscale(sc->sc_ah, scale) ? EINVAL : ath_reset(ifp);
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+ath_sysctl_tpc(SYSCTL_HANDLER_ARGS)
+{
+       struct ath_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       u_int tpc;
+       int error;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       tpc = ath_hal_gettpc(sc->sc_ah);
+       error = sysctl_handle_int(oidp, &tpc, 0, req);
+       if (error || !req->newptr)
+               goto back;
+       error = !ath_hal_settpc(sc->sc_ah, tpc) ? EINVAL : 0;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+ath_sysctl_rfkill(SYSCTL_HANDLER_ARGS)
+{
+       struct ath_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       struct ath_hal *ah = sc->sc_ah;
+       u_int rfkill;
+       int error;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       rfkill = ath_hal_getrfkill(ah);
+       error = sysctl_handle_int(oidp, &rfkill, 0, req);
+       if (error || !req->newptr)
+               goto back;
+
+       error = 0;
+
+       if (rfkill == ath_hal_getrfkill(ah))    /* unchanged */
+               goto back;
+
+       if (!ath_hal_setrfkill(ah, rfkill) || ath_reset(&sc->sc_ic.ic_if) != 0)
+               error = EINVAL;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+ath_sysctl_rfsilent(SYSCTL_HANDLER_ARGS)
+{
+       struct ath_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       u_int rfsilent;
+       int error;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       ath_hal_getrfsilent(sc->sc_ah, &rfsilent);
+       error = sysctl_handle_int(oidp, &rfsilent, 0, req);
+       if (error || !req->newptr)
+               goto back;
+       if (!ath_hal_setrfsilent(sc->sc_ah, rfsilent)) {
+               error = EINVAL;
+               goto back;
+       }
+       sc->sc_rfsilentpin = rfsilent & 0x1c;
+       sc->sc_rfsilentpol = (rfsilent & 0x2) != 0;
+       error = 0;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+ath_sysctl_regdomain(SYSCTL_HANDLER_ARGS)
+{
+       struct ath_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       uint32_t rd;
+       int error;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       if (!ath_hal_getregdomain(sc->sc_ah, &rd)) {
+               error = EINVAL;
+               goto back;
+       }
+       error = sysctl_handle_int(oidp, &rd, 0, req);
+       if (error || !req->newptr)
+               goto back;
+       error = !ath_hal_setregdomain(sc->sc_ah, rd) ? EINVAL : 0;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+ath_sysctl_tpack(SYSCTL_HANDLER_ARGS)
+{
+       struct ath_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       uint32_t tpack;
+       int error;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       ath_hal_gettpack(sc->sc_ah, &tpack);
+       error = sysctl_handle_int(oidp, &tpack, 0, req);
+       if (error || !req->newptr)
+               goto back;
+       error = !ath_hal_settpack(sc->sc_ah, tpack) ? EINVAL : 0;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static int
+ath_sysctl_tpcts(SYSCTL_HANDLER_ARGS)
+{
+       struct ath_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       uint32_t tpcts;
+       int error;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       ath_hal_gettpcts(sc->sc_ah, &tpcts);
+       error = sysctl_handle_int(oidp, &tpcts, 0, req);
+       if (error || !req->newptr)
+               goto back;
+       error = !ath_hal_settpcts(sc->sc_ah, tpcts) ? EINVAL : 0;
+back:
+       lwkt_serialize_exit(ifp->if_serializer);
+       return error;
+}
+
+static void
+ath_sysctlattach(struct ath_softc *sc)
+{
+       struct sysctl_ctx_list *ctx = &sc->sc_sysctl_ctx;
+       struct sysctl_oid *tree = sc->sc_sysctl_tree;
+       struct ath_hal *ah = sc->sc_ah;
+
+       ath_hal_getcountrycode(sc->sc_ah, &sc->sc_countrycode);
+       SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+               "countrycode", CTLFLAG_RD, &sc->sc_countrycode, 0,
+               "EEPROM country code");
+       SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+               "regdomain", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+               ath_sysctl_regdomain, "I", "EEPROM regdomain code");
+#ifdef ATH_DEBUG
+       sc->sc_debug = ath_debug;
+       SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+               "debug", CTLFLAG_RW, &sc->sc_debug, 0,
+               "control debugging printfs");
+#endif
+       SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+               "slottime", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+               ath_sysctl_slottime, "I", "802.11 slot time (us)");
+       SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+               "acktimeout", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+               ath_sysctl_acktimeout, "I", "802.11 ACK timeout (us)");
+       SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+               "ctstimeout", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+               ath_sysctl_ctstimeout, "I", "802.11 CTS timeout (us)");
+       SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+               "softled", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+               ath_sysctl_softled, "I", "enable/disable software LED support");
+       SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+               "ledpin", CTLFLAG_RW, &sc->sc_ledpin, 0,
+               "GPIO pin connected to LED");
+       SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+               "ledon", CTLFLAG_RW, &sc->sc_ledon, 0,
+               "setting to turn LED on");
+       SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+               "ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0,
+               "idle time for inactivity LED (ticks)");
+       SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+               "txantenna", CTLFLAG_RW, &sc->sc_txantenna, 0,
+               "tx antenna (0=auto)");
+       SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+               "rxantenna", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+               ath_sysctl_rxantenna, "I", "default/rx antenna");
+       if (ath_hal_hasdiversity(ah))
+               SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+                       "diversity", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+                       ath_sysctl_diversity, "I", "antenna diversity");
+       sc->sc_txintrperiod = ATH_TXINTR_PERIOD;
+       SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+               "txintrperiod", CTLFLAG_RW, &sc->sc_txintrperiod, 0,
+               "tx descriptor batching");
+       SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+               "diag", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+               ath_sysctl_diag, "I", "h/w diagnostic control");
+       SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+               "tpscale", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+               ath_sysctl_tpscale, "I", "tx power scaling");
+       if (ath_hal_hastpc(ah)) {
+               SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+                       "tpc", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+                       ath_sysctl_tpc, "I", "enable/disable per-packet TPC");
+               SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+                       "tpack", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+                       ath_sysctl_tpack, "I", "tx power for ack frames");
+               SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+                       "tpcts", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+                       ath_sysctl_tpcts, "I", "tx power for cts frames");
+       }
+       if (ath_hal_hasrfsilent(ah)) {
+               SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+                       "rfsilent", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+                       ath_sysctl_rfsilent, "I", "h/w RF silent config");
+               SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+                       "rfkill", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+                       ath_sysctl_rfkill, "I", "enable/disable RF kill switch");
+       }
+       sc->sc_monpass = HAL_RXERR_DECRYPT | HAL_RXERR_MIC;
+       SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+               "monpass", CTLFLAG_RW, &sc->sc_monpass, 0,
+               "mask of error frames to pass when monitoring");
+}
+
+static void
+ath_bpfattach(struct ath_softc *sc)
+{
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+       bpfattach_dlt(ifp, DLT_IEEE802_11_RADIO,
+               sizeof(struct ieee80211_frame) + sizeof(sc->sc_tx_th),
+               &sc->sc_drvbpf);
+       /*
+        * Initialize constant fields.
+        * XXX make header lengths a multiple of 32-bits so subsequent
+        *     headers are properly aligned; this is a kludge to keep
+        *     certain applications happy.
+        *
+        * NB: the channel is setup each time we transition to the
+        *     RUN state to avoid filling it in for each frame.
+        */
+       sc->sc_tx_th_len = roundup(sizeof(sc->sc_tx_th), sizeof(uint32_t));
+       sc->sc_tx_th.wt_ihdr.it_len = htole16(sc->sc_tx_th_len);
+       sc->sc_tx_th.wt_ihdr.it_present = htole32(ATH_TX_RADIOTAP_PRESENT);
+
+       sc->sc_rx_th_len = roundup(sizeof(sc->sc_rx_th), sizeof(uint32_t));
+       sc->sc_rx_th.wr_ihdr.it_len = htole16(sc->sc_rx_th_len);
+       sc->sc_rx_th.wr_ihdr.it_present = htole32(ATH_RX_RADIOTAP_PRESENT);
+}
+
+/*
+ * Announce various information on device/driver attach.
+ */
+static void
+ath_announce(struct ath_softc *sc)
+{
+#define        HAL_MODE_DUALBAND       (HAL_MODE_11A|HAL_MODE_11B)
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       struct ath_hal *ah = sc->sc_ah;
+       u_int modes, cc;
+
+       if_printf(ifp, "mac %d.%d phy %d.%d",
+               ah->ah_macVersion, ah->ah_macRev,
+               ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf);
+       /*
+        * Print radio revision(s).  We check the wireless modes
+        * to avoid falsely printing revs for inoperable parts.
+        * Dual-band radio revs are returned in the 5Ghz rev number.
+        */
+       ath_hal_getcountrycode(ah, &cc);
+       modes = ath_hal_getwirelessmodes(ah, cc);
+       if ((modes & HAL_MODE_DUALBAND) == HAL_MODE_DUALBAND) {
+               if (ah->ah_analog5GhzRev && ah->ah_analog2GhzRev)
+                       printf(" 5ghz radio %d.%d 2ghz radio %d.%d",
+                               ah->ah_analog5GhzRev >> 4,
+                               ah->ah_analog5GhzRev & 0xf,
+                               ah->ah_analog2GhzRev >> 4,
+                               ah->ah_analog2GhzRev & 0xf);
+               else
+                       printf(" radio %d.%d", ah->ah_analog5GhzRev >> 4,
+                               ah->ah_analog5GhzRev & 0xf);
+       } else
+               printf(" radio %d.%d", ah->ah_analog5GhzRev >> 4,
+                       ah->ah_analog5GhzRev & 0xf);
+       printf("\n");
+       if (bootverbose) {
+               int i;
+               for (i = 0; i <= WME_AC_VO; i++) {
+                       struct ath_txq *txq = sc->sc_ac2q[i];
+                       if_printf(ifp, "Use hw queue %u for %s traffic\n",
+                               txq->axq_qnum, ieee80211_wme_acnames[i]);
+               }
+               if_printf(ifp, "Use hw queue %u for CAB traffic\n",
+                       sc->sc_cabq->axq_qnum);
+               if_printf(ifp, "Use hw queue %u for beacons\n", sc->sc_bhalq);
+       }
+       if (ath_rxbuf != ATH_RXBUF)
+               if_printf(ifp, "using %u rx buffers\n", ath_rxbuf);
+       if (ath_txbuf != ATH_TXBUF)
+               if_printf(ifp, "using %u tx buffers\n", ath_txbuf);
+#undef HAL_MODE_DUALBAND
+}
+
+static void
+ath_dma_map_mbuf(void *arg, bus_dma_segment_t *segs, int nseg,
+                bus_size_t mapsize, int error)
+{
+       struct ath_buf *bf = arg;
+
+       if (error)
+               return;
+
+       KASSERT(nseg <= ATH_MAX_SCATTER, ("too many DMA segments"));
+       bcopy(segs, bf->bf_segs, nseg * sizeof(bus_dma_segment_t));
+       bf->bf_nseg = nseg;
+}
diff --git a/sys/dev/netif/ath/ath/if_ath_pci.c b/sys/dev/netif/ath/ath/if_ath_pci.c
new file mode 100644 (file)
index 0000000..a4ea4e3
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * 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.
+ *
+ * $FreeBSD: src/sys/dev/ath/if_ath_pci.c,v 1.12 2005/03/05 19:06:12 imp Exp $
+ * $DragonFly: src/sys/dev/netif/ath/ath/if_ath_pci.c,v 1.1 2006/07/13 09:15:22 sephe Exp $
+ */
+
+/*
+ * PCI/Cardbus front-end for the Atheros Wireless LAN controller driver.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/module.h>
+#include <sys/kernel.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+
+#include <bus/pci/pcivar.h>
+#include <bus/pci/pcireg.h>
+
+#include <dev/netif/ath/ath/if_athvar.h>
+#include <contrib/dev/ath/ah.h>
+
+static int     ath_pci_probe(device_t);
+static int     ath_pci_attach(device_t);
+static int     ath_pci_detach(device_t);
+static int     ath_pci_shutdown(device_t);
+static int     ath_pci_suspend(device_t);
+static int     ath_pci_resume(device_t);
+
+static void    ath_pci_setup(device_t);
+
+/*
+ * PCI glue.
+ */
+
+struct ath_pci_softc {
+       struct ath_softc        sc_sc;
+       struct resource         *sc_sr;         /* memory resource */
+       int                     sc_srid;
+};
+
+#define        BS_BAR  0x10
+#define        PCIR_RETRY_TIMEOUT      0x41
+
+static int
+ath_pci_probe(device_t dev)
+{
+       const char* devname;
+
+       devname = ath_hal_probe(pci_get_vendor(dev), pci_get_device(dev));
+       if (devname != NULL) {
+               device_set_desc(dev, devname);
+               return 0;
+       }
+       return ENXIO;
+}
+
+static void
+ath_pci_setup(device_t dev)
+{
+       /*
+        * Enable memory mapping and bus mastering.
+        */
+       pci_enable_busmaster(dev);
+
+       /*
+        * Disable retry timeout to keep PCI Tx retries from
+        * interfering with C3 CPU state.
+        */
+       pci_write_config(dev, PCIR_RETRY_TIMEOUT, 0, 1);
+}
+
+static int
+ath_pci_attach(device_t dev)
+{
+       struct ath_pci_softc *psc = device_get_softc(dev);
+       struct ath_softc *sc = &psc->sc_sc;
+       int error;
+
+       sc->sc_dev = dev;
+
+       ath_pci_setup(dev);
+
+       /* 
+        * Setup memory-mapping of PCI registers.
+        */
+       psc->sc_srid = BS_BAR;
+       psc->sc_sr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &psc->sc_srid,
+                                           RF_ACTIVE);
+       if (psc->sc_sr == NULL) {
+               device_printf(dev, "cannot map register space\n");
+               return ENXIO;
+       }
+       sc->sc_st = rman_get_bustag(psc->sc_sr);
+       sc->sc_sh = rman_get_bushandle(psc->sc_sr);
+
+       /*
+        * Setup DMA descriptor area.
+        */
+       error = bus_dma_tag_create(NULL, 1, 0,
+                                  BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
+                                  NULL, NULL, 0x3ffff/* maxsize XXX */,
+                                  ATH_MAX_SCATTER, BUS_SPACE_MAXADDR,
+                                  BUS_DMA_ALLOCNOW, &sc->sc_dmat);
+       if (error) {
+               device_printf(dev, "cannot allocate DMA tag\n");
+               goto back;
+       }
+
+       error = ath_attach(pci_get_device(dev), sc);
+
+back:
+       if (error)
+               ath_pci_detach(dev);
+       return error;
+}
+
+static int
+ath_pci_detach(device_t dev)
+{
+       struct ath_pci_softc *psc = device_get_softc(dev);
+       struct ath_softc *sc = &psc->sc_sc;
+
+       /* XXX check if device was removed */
+       sc->sc_invalid = !bus_child_present(dev);
+
+       if (device_is_attached(dev))
+               ath_detach(sc);
+
+       bus_generic_detach(dev);
+
+       if (sc->sc_dmat != NULL)
+               bus_dma_tag_destroy(sc->sc_dmat);
+
+       if (psc->sc_sr != NULL) {
+               bus_release_resource(dev, SYS_RES_MEMORY, psc->sc_srid,
+                                    psc->sc_sr);
+       }
+       return 0;
+}
+
+static int
+ath_pci_shutdown(device_t dev)
+{
+       struct ath_pci_softc *psc = device_get_softc(dev);
+
+       ath_shutdown(&psc->sc_sc);
+       return (0);
+}
+
+static int
+ath_pci_suspend(device_t dev)
+{
+       struct ath_pci_softc *psc = device_get_softc(dev);
+
+       ath_suspend(&psc->sc_sc);
+       return (0);
+}
+
+static int
+ath_pci_resume(device_t dev)
+{
+       struct ath_pci_softc *psc = device_get_softc(dev);
+
+       ath_pci_setup(dev);
+       ath_resume(&psc->sc_sc);
+       return (0);
+}
+
+static device_method_t ath_pci_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_probe,         ath_pci_probe),
+       DEVMETHOD(device_attach,        ath_pci_attach),
+       DEVMETHOD(device_detach,        ath_pci_detach),
+       DEVMETHOD(device_shutdown,      ath_pci_shutdown),
+       DEVMETHOD(device_suspend,       ath_pci_suspend),
+       DEVMETHOD(device_resume,        ath_pci_resume),
+
+       { 0, 0 }
+};
+
+static driver_t ath_pci_driver = {
+       "ath",
+       ath_pci_methods,
+       sizeof (struct ath_pci_softc)
+};
+
+static devclass_t ath_devclass;
+
+DRIVER_MODULE(if_ath, pci, ath_pci_driver, ath_devclass, 0, 0);
+DRIVER_MODULE(if_ath, cardbus, ath_pci_driver, ath_devclass, 0, 0);
+
+MODULE_VERSION(if_ath, 1);
+
+MODULE_DEPEND(if_ath, ath_hal, 1, 1, 1);       /* Atheros HAL */
+MODULE_DEPEND(if_ath, ath_rate, 1, 1, 1);      /* rate control algorithm */
+MODULE_DEPEND(if_ath, wlan, 1, 1, 1);          /* 802.11 media layer */
diff --git a/sys/dev/netif/ath/ath/if_athioctl.h b/sys/dev/netif/ath/ath/if_athioctl.h
new file mode 100644 (file)
index 0000000..22ac372
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * 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.
+ *
+ * $FreeBSD: src/sys/dev/ath/if_athioctl.h,v 1.10.2.3 2006/02/24 19:51:11 sam Exp $
+ * $DragonFly: src/sys/dev/netif/ath/ath/if_athioctl.h,v 1.1 2006/07/13 09:15:22 sephe Exp $
+ */
+
+/*
+ * Ioctl-related defintions for the Atheros Wireless LAN controller driver.
+ */
+#ifndef _DEV_ATH_ATHIOCTL_H
+#define _DEV_ATH_ATHIOCTL_H
+
+struct ath_stats {
+       uint32_t        ast_watchdog;   /* device reset by watchdog */
+       uint32_t        ast_hardware;   /* fatal hardware error interrupts */
+       uint32_t        ast_bmiss;      /* beacon miss interrupts */
+       uint32_t        ast_bmiss_phantom;/* beacon miss interrupts */
+       uint32_t        ast_bstuck;     /* beacon stuck interrupts */
+       uint32_t        ast_rxorn;      /* rx overrun interrupts */
+       uint32_t        ast_rxeol;      /* rx eol interrupts */
+       uint32_t        ast_txurn;      /* tx underrun interrupts */
+       uint32_t        ast_mib;        /* mib interrupts */
+       uint32_t        ast_intrcoal;   /* interrupts coalesced */
+       uint32_t        ast_tx_packets; /* packet sent on the interface */
+       uint32_t        ast_tx_mgmt;    /* management frames transmitted */
+       uint32_t        ast_tx_discard; /* frames discarded prior to assoc */
+       uint32_t        ast_tx_qstop;   /* output stopped 'cuz no buffer */
+       uint32_t        ast_tx_encap;   /* tx encapsulation failed */
+       uint32_t        ast_tx_nonode;  /* tx failed 'cuz no node */
+       uint32_t        ast_tx_nombuf;  /* tx failed 'cuz no mbuf */
+       uint32_t        ast_tx_nomcl;   /* tx failed 'cuz no cluster */
+       uint32_t        ast_tx_linear;  /* tx linearized to cluster */
+       uint32_t        ast_tx_nodata;  /* tx discarded empty frame */
+       uint32_t        ast_tx_busdma;  /* tx failed for dma resrcs */
+       uint32_t        ast_tx_xretries;/* tx failed 'cuz too many retries */
+       uint32_t        ast_tx_fifoerr; /* tx failed 'cuz FIFO underrun */
+       uint32_t        ast_tx_filtered;/* tx failed 'cuz xmit filtered */
+       uint32_t        ast_tx_shortretry;/* tx on-chip retries (short) */
+       uint32_t        ast_tx_longretry;/* tx on-chip retries (long) */
+       uint32_t        ast_tx_badrate; /* tx failed 'cuz bogus xmit rate */
+       uint32_t        ast_tx_noack;   /* tx frames with no ack marked */
+       uint32_t        ast_tx_rts;     /* tx frames with rts enabled */
+       uint32_t        ast_tx_cts;     /* tx frames with cts enabled */
+       uint32_t        ast_tx_shortpre;/* tx frames with short preamble */
+       uint32_t        ast_tx_altrate; /* tx frames with alternate rate */
+       uint32_t        ast_tx_protect; /* tx frames with protection */
+       uint32_t        ast_unused1;
+       uint32_t        ast_unused2;
+       uint32_t        ast_rx_nombuf;  /* rx setup failed 'cuz no mbuf */
+       uint32_t        ast_rx_busdma;  /* rx setup failed for dma resrcs */
+       uint32_t        ast_rx_orn;     /* rx failed 'cuz of desc overrun */
+       uint32_t        ast_rx_crcerr;  /* rx failed 'cuz of bad CRC */
+       uint32_t        ast_rx_fifoerr; /* rx failed 'cuz of FIFO overrun */
+       uint32_t        ast_rx_badcrypt;/* rx failed 'cuz decryption */
+       uint32_t        ast_rx_badmic;  /* rx failed 'cuz MIC failure */
+       uint32_t        ast_rx_phyerr;  /* rx failed 'cuz of PHY err */
+       uint32_t        ast_rx_phy[32]; /* rx PHY error per-code counts */
+       uint32_t        ast_rx_tooshort;/* rx discarded 'cuz frame too short */
+       uint32_t        ast_rx_toobig;  /* rx discarded 'cuz frame too large */
+       uint32_t        ast_rx_packets; /* packet recv on the interface */
+       uint32_t        ast_rx_mgt;     /* management frames received */
+       uint32_t        ast_rx_ctl;     /* rx discarded 'cuz ctl frame */
+       int8_t          ast_tx_rssi;    /* tx rssi of last ack */
+       int8_t          ast_rx_rssi;    /* rx rssi from histogram */
+       uint32_t        ast_be_xmit;    /* beacons transmitted */
+       uint32_t        ast_be_nombuf;  /* beacon setup failed 'cuz no mbuf */
+       uint32_t        ast_per_cal;    /* periodic calibration calls */
+       uint32_t        ast_per_calfail;/* periodic calibration failed */
+       uint32_t        ast_per_rfgain; /* periodic calibration rfgain reset */
+       uint32_t        ast_rate_calls; /* rate control checks */
+       uint32_t        ast_rate_raise; /* rate control raised xmit rate */
+       uint32_t        ast_rate_drop;  /* rate control dropped xmit rate */
+       uint32_t        ast_ant_defswitch;/* rx/default antenna switches */
+       uint32_t        ast_ant_txswitch;/* tx antenna switches */
+       uint32_t        ast_ant_rx[8];  /* rx frames with antenna */
+       uint32_t        ast_ant_tx[8];  /* tx frames with antenna */
+       uint32_t        ast_pad[32];
+};
+
+#define        SIOCGATHSTATS   _IOWR('i', 137, struct ifreq)
+
+struct ath_diag {
+       char    ad_name[IFNAMSIZ];      /* if name, e.g. "ath0" */
+       uint16_t ad_id;
+#define        ATH_DIAG_DYN    0x8000          /* allocate buffer in caller */
+#define        ATH_DIAG_IN     0x4000          /* copy in parameters */
+#define        ATH_DIAG_OUT    0x0000          /* copy out results (always) */
+#define        ATH_DIAG_ID     0x0fff
+       uint16_t ad_in_size;            /* pack to fit, yech */
+       caddr_t ad_in_data;
+       caddr_t ad_out_data;
+       u_int   ad_out_size;
+
+};
+#define        SIOCGATHDIAG    _IOWR('i', 138, struct ath_diag)
+
+/*
+ * Radio capture format.
+ */
+#define ATH_RX_RADIOTAP_PRESENT (              \
+       (1 << IEEE80211_RADIOTAP_TSFT)          | \
+       (1 << IEEE80211_RADIOTAP_FLAGS)         | \
+       (1 << IEEE80211_RADIOTAP_RATE)          | \
+       (1 << IEEE80211_RADIOTAP_CHANNEL)       | \
+       (1 << IEEE80211_RADIOTAP_ANTENNA)       | \
+       (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \
+       (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE)  | \
+       0)
+
+struct ath_rx_radiotap_header {
+       struct ieee80211_radiotap_header wr_ihdr;
+       uint64_t        wr_tsf;
+       uint8_t         wr_flags;
+       uint8_t         wr_rate;
+       uint16_t        wr_chan_freq;
+       uint16_t        wr_chan_flags;
+       uint8_t         wr_antsignal;
+       uint8_t         wr_antnoise;
+       uint8_t         wr_antenna;
+};
+
+#define ATH_TX_RADIOTAP_PRESENT (              \
+       (1 << IEEE80211_RADIOTAP_TSFT)          | \
+       (1 << IEEE80211_RADIOTAP_FLAGS)         | \
+       (1 << IEEE80211_RADIOTAP_RATE)          | \
+       (1 << IEEE80211_RADIOTAP_CHANNEL)       | \
+       (1 << IEEE80211_RADIOTAP_DBM_TX_POWER)  | \
+       (1 << IEEE80211_RADIOTAP_ANTENNA)       | \
+       0)
+
+struct ath_tx_radiotap_header {
+       struct ieee80211_radiotap_header wt_ihdr;
+       uint64_t        wt_tsf;
+       uint8_t         wt_flags;
+       uint8_t         wt_rate;
+       uint16_t        wt_chan_freq;
+       uint16_t        wt_chan_flags;
+       uint8_t         wt_txpower;
+       uint8_t         wt_antenna;
+};
+
+#endif /* _DEV_ATH_ATHIOCTL_H */
diff --git a/sys/dev/netif/ath/ath/if_athrate.h b/sys/dev/netif/ath/ath/if_athrate.h
new file mode 100644 (file)
index 0000000..90aa24a
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004 Video54 Technologies, Inc.
+ * 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.
+ *
+ * $FreeBSD: src/sys/dev/ath/if_athrate.h,v 1.4 2005/04/02 18:54:30 sam Exp $
+ * $DragonFly: src/sys/dev/netif/ath/ath/if_athrate.h,v 1.1 2006/07/13 09:15:22 sephe Exp $
+ */
+#ifndef _ATH_RATECTRL_H_
+#define _ATH_RATECTRL_H_
+
+/*
+ * Interface definitions for transmit rate control modules for the
+ * Atheros driver.
+ *
+ * A rate control module is responsible for choosing the transmit rate
+ * for each data frame.  Management+control frames are always sent at
+ * a fixed rate.
+ *
+ * Only one module may be present at a time; the driver references
+ * rate control interfaces by symbol name.  If multiple modules are
+ * to be supported we'll need to switch to a registration-based scheme
+ * as is currently done, for example, for authentication modules.
+ *
+ * An instance of the rate control module is attached to each device
+ * at attach time and detached when the device is destroyed.  The module
+ * may associate data with each device and each node (station).  Both
+ * sets of storage are opaque except for the size of the per-node storage
+ * which must be provided when the module is attached.
+ *
+ * The rate control module is notified for each state transition and
+ * station association/reassociation.  Otherwise it is queried for a
+ * rate for each outgoing frame and provided status from each transmitted
+ * frame.  Any ancillary processing is the responsibility of the module
+ * (e.g. if periodic processing is required then the module should setup
+ * it's own timer).
+ *
+ * In addition to the transmit rate for each frame the module must also
+ * indicate the number of attempts to make at the specified rate.  If this
+ * number is != ATH_TXMAXTRY then an additional callback is made to setup
+ * additional transmit state.  The rate control code is assumed to write
+ * this additional data directly to the transmit descriptor.
+ */
+struct ath_softc;
+struct ath_node;
+struct ath_desc;
+
+struct ath_ratectrl {
+       size_t  arc_space;      /* space required for per-node state */
+};
+/*
+ * Attach/detach a rate control module.
+ */
+struct ath_ratectrl *ath_rate_attach(struct ath_softc *);
+void   ath_rate_stop(struct ath_ratectrl *);
+void   ath_rate_detach(struct ath_ratectrl *);
+
+
+/*
+ * State storage handling.
+ */
+/*
+ * Initialize per-node state already allocated for the specified
+ * node; this space can be assumed initialized to zero.
+ */
+void   ath_rate_node_init(struct ath_softc *, struct ath_node *);
+/*
+ * Cleanup any per-node state prior to the node being reclaimed.
+ */
+void   ath_rate_node_cleanup(struct ath_softc *, struct ath_node *);
+/*
+ * Update rate control state on station associate/reassociate 
+ * (when operating as an ap or for nodes discovered when operating
+ * in ibss mode).
+ */
+void   ath_rate_newassoc(struct ath_softc *, struct ath_node *,
+               int isNewAssociation);
+/*
+ * Update/reset rate control state for 802.11 state transitions.
+ * Important mostly as the analog to ath_rate_newassoc when operating
+ * in station mode.
+ */
+void   ath_rate_newstate(struct ath_softc *, enum ieee80211_state);
+
+/*
+ * Transmit handling.
+ */
+/*
+ * Return the transmit info for a data packet.  If multi-rate state
+ * is to be setup then try0 should contain a value other than ATH_TXMATRY
+ * and ath_rate_setupxtxdesc will be called after deciding if the frame
+ * can be transmitted with multi-rate retry.
+ */
+void   ath_rate_findrate(struct ath_softc *, struct ath_node *,
+               int shortPreamble, size_t frameLen,
+               uint8_t *rix, int *try0, uint8_t *txrate);
+/*
+ * Setup any extended (multi-rate) descriptor state for a data packet.
+ * The rate index returned by ath_rate_findrate is passed back in.
+ */
+void   ath_rate_setupxtxdesc(struct ath_softc *, struct ath_node *,
+               struct ath_desc *, int shortPreamble, uint8_t rix);
+/*
+ * Update rate control state for a packet associated with the
+ * supplied transmit descriptor.  The routine is invoked both
+ * for packets that were successfully sent and for those that
+ * failed (consult the descriptor for details).
+ */
+void   ath_rate_tx_complete(struct ath_softc *, struct ath_node *,
+               const struct ath_desc *last, const struct ath_desc *first);
+#endif /* _ATH_RATECTRL_H_ */
diff --git a/sys/dev/netif/ath/ath/if_athvar.h b/sys/dev/netif/ath/ath/if_athvar.h
new file mode 100644 (file)
index 0000000..d3a6f54
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * 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.
+ *
+ * $FreeBSD: src/sys/dev/ath/if_athvar.h,v 1.27.2.5 2006/02/24 19:51:11 sam Exp $
+ * $DragonFly: src/sys/dev/netif/ath/ath/if_athvar.h,v 1.1 2006/07/13 09:15:22 sephe Exp $
+ */
+
+/*
+ * Defintions for the Atheros Wireless LAN controller driver.
+ */
+#ifndef _DEV_ATH_ATHVAR_H
+#define _DEV_ATH_ATHVAR_H
+
+#include <contrib/dev/ath/ah.h>
+#include <netproto/802_11/ieee80211_radiotap.h>
+#include <dev/netif/ath/ath/if_athioctl.h>
+#include <dev/netif/ath/ath/if_athrate.h>
+
+#define        ATH_TIMEOUT             1000
+
+#ifndef ATH_RXBUF
+#define        ATH_RXBUF       40              /* number of RX buffers */
+#endif
+#ifndef ATH_TXBUF
+#define        ATH_TXBUF       100             /* number of TX buffers */
+#endif
+#define        ATH_TXDESC      10              /* number of descriptors per buffer */
+#define        ATH_TXMAXTRY    11              /* max number of transmit attempts */
+#define        ATH_TXMGTTRY    4               /* xmit attempts for mgt/ctl frames */
+#define        ATH_TXINTR_PERIOD 5             /* max number of batched tx descriptors */
+
+#define        ATH_BEACON_AIFS_DEFAULT  0      /* default aifs for ap beacon q */
+#define        ATH_BEACON_CWMIN_DEFAULT 0      /* default cwmin for ap beacon q */
+#define        ATH_BEACON_CWMAX_DEFAULT 0      /* default cwmax for ap beacon q */
+
+/*
+ * The key cache is used for h/w cipher state and also for
+ * tracking station state such as the current tx antenna.
+ * We also setup a mapping table between key cache slot indices
+ * and station state to short-circuit node lookups on rx.
+ * Different parts have different size key caches.  We handle
+ * up to ATH_KEYMAX entries (could dynamically allocate state).
+ */
+#define        ATH_KEYMAX      128             /* max key cache size we handle */
+#define        ATH_KEYBYTES    (ATH_KEYMAX/NBBY)       /* storage space in bytes */
+
+/* driver-specific node state */
+struct ath_node {
+       struct ieee80211_node an_node;  /* base class */
+       uint32_t        an_avgrssi;     /* average rssi over all rx frames */
+       /* variable-length rate control state follows */
+};
+#define        ATH_NODE(ni)    ((struct ath_node *)(ni))
+#define        ATH_NODE_CONST(ni)      ((const struct ath_node *)(ni))
+
+#define ATH_RSSI_LPF_LEN       10
+#define ATH_RSSI_DUMMY_MARKER  0x127
+#define ATH_EP_MUL(x, mul)     ((x) * (mul))
+#define ATH_RSSI_IN(x)         (ATH_EP_MUL((x), HAL_RSSI_EP_MULTIPLIER))
+#define ATH_LPF_RSSI(x, y, len) \
+    ((x != ATH_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y))
+#define ATH_RSSI_LPF(x, y) do {                                                \
+    if ((y) >= -20)                                                    \
+       x = ATH_LPF_RSSI((x), ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN);      \
+} while (0)
+
+struct ath_buf {
+       STAILQ_ENTRY(ath_buf)   bf_list;
+       int                     bf_nseg;
+       int                     bf_flags;       /* tx descriptor flags */
+       struct ath_desc         *bf_desc;       /* virtual addr of desc */
+       bus_addr_t              bf_daddr;       /* physical addr of desc */
+       bus_dmamap_t            bf_dmamap;      /* DMA map for mbuf chain */
+       struct mbuf             *bf_m;          /* mbuf for buf */
+       struct ieee80211_node   *bf_node;       /* pointer to the node */
+       bus_size_t              bf_mapsize;
+#define        ATH_MAX_SCATTER         ATH_TXDESC      /* max(tx,rx,beacon) desc's */
+       bus_dma_segment_t       bf_segs[ATH_MAX_SCATTER];
+};
+typedef STAILQ_HEAD(, ath_buf) ath_bufhead;
+
+/*
+ * DMA state for tx/rx descriptors.
+ */
+struct ath_descdma {
+       const char*             dd_name;
+       struct ath_desc         *dd_desc;       /* descriptors */
+       bus_addr_t              dd_desc_paddr;  /* physical addr of dd_desc */
+       bus_addr_t              dd_desc_len;    /* size of dd_desc */
+       bus_dma_segment_t       dd_dseg;
+       bus_dma_tag_t           dd_dmat;        /* bus DMA tag */
+       bus_dmamap_t            dd_dmamap;      /* DMA map for descriptors */
+       struct ath_buf          *dd_bufptr;     /* associated buffers */
+};
+
+/*
+ * Data transmit queue state.  One of these exists for each
+ * hardware transmit queue.  Packets sent to us from above
+ * are assigned to queues based on their priority.  Not all
+ * devices support a complete set of hardware transmit queues.
+ * For those devices the array sc_ac2q will map multiple
+ * priorities to fewer hardware queues (typically all to one
+ * hardware queue).
+ */
+struct ath_txq {
+       u_int                   axq_qnum;       /* hardware q number */
+       u_int                   axq_depth;      /* queue depth (stat only) */
+       u_int                   axq_intrcnt;    /* interrupt count */
+       uint32_t                *axq_link;      /* link ptr in last TX desc */
+       STAILQ_HEAD(, ath_buf)  axq_q;          /* transmit queue */
+       char                    axq_name[12];   /* e.g. "ath0_txq4" */
+};
+
+#define ATH_TXQ_INSERT_TAIL(_tq, _elm, _field) do { \
+       STAILQ_INSERT_TAIL(&(_tq)->axq_q, (_elm), _field); \
+       (_tq)->axq_depth++; \
+} while (0)
+#define ATH_TXQ_REMOVE_HEAD(_tq, _field) do { \
+       STAILQ_REMOVE_HEAD(&(_tq)->axq_q, _field); \
+       (_tq)->axq_depth--; \
+} while (0)
+
+struct ath_tx99;
+
+struct ath_softc {
+       struct ieee80211com     sc_ic;          /* IEEE 802.11 common */
+
+       struct resource         *sc_irq;        /* irq resource */
+       void                    *sc_ih;         /* interrupt handler */
+       int                     sc_irq_rid;
+
+       struct ath_stats        sc_stats;       /* interface statistics */
+       int                     sc_countrycode;
+       int                     sc_debug;
+       void                    (*sc_recv_mgmt)(struct ieee80211com *,
+                                       struct mbuf *,
+                                       struct ieee80211_node *,
+                                       int, int, uint32_t);
+       int                     (*sc_newstate)(struct ieee80211com *,
+                                       enum ieee80211_state, int);
+       void                    (*sc_node_free)(struct ieee80211_node *);
+       device_t                sc_dev;
+       bus_space_tag_t         sc_st;          /* bus space tag */
+       bus_space_handle_t      sc_sh;          /* bus space handle */
+       bus_dma_tag_t           sc_dmat;        /* bus DMA tag */
+       struct ath_hal          *sc_ah;         /* Atheros HAL */
+       struct ath_ratectrl     *sc_rc;         /* tx rate control support */
+       struct ath_tx99         *sc_tx99;       /* tx99 adjunct state */
+       void                    (*sc_setdefantenna)(struct ath_softc *, u_int);
+       void                    (*sc_tx_proc)(struct ath_softc *);
+       unsigned int            sc_invalid : 1, /* disable hardware accesses */
+                               sc_mrretry : 1, /* multi-rate retry support */
+                               sc_softled : 1, /* enable LED gpio status */
+                               sc_splitmic: 1, /* split TKIP MIC keys */
+                               sc_needmib : 1, /* enable MIB stats intr */
+                               sc_diversity : 1,/* enable rx diversity */
+                               sc_hasveol : 1, /* tx VEOL support */
+                               sc_ledstate: 1, /* LED on/off state */
+                               sc_blinking: 1, /* LED blink operation active */
+                               sc_mcastkey: 1, /* mcast key cache search */
+                               sc_syncbeacon:1,/* sync/resync beacon timers */
+                               sc_hasclrkey:1; /* CLR key supported */
+                                               /* rate tables */
+       const HAL_RATE_TABLE    *sc_rates[IEEE80211_MODE_MAX];
+       const HAL_RATE_TABLE    *sc_currates;   /* current rate table */
+       enum ieee80211_phymode  sc_curmode;     /* current phy mode */
+       HAL_OPMODE              sc_opmode;      /* current operating mode */
+       uint16_t                sc_curtxpow;    /* current tx power limit */
+       HAL_CHANNEL             sc_curchan;     /* current h/w channel */
+       uint8_t                 sc_rixmap[256]; /* IEEE to h/w rate table ix */
+       struct {
+               uint8_t         ieeerate;       /* IEEE rate */
+               uint8_t         rxflags;        /* radiotap rx flags */
+               uint8_t         txflags;        /* radiotap tx flags */
+               uint16_t        ledon;          /* softled on time */
+               uint16_t        ledoff;         /* softled off time */
+       } sc_hwmap[32];                         /* h/w rate ix mappings */
+       uint8_t                 sc_minrateix;   /* min h/w rate index */
+       uint8_t                 sc_mcastrix;    /* mcast h/w rate index */
+       uint8_t                 sc_protrix;     /* protection rate index */
+       u_int                   sc_mcastrate;   /* ieee rate for mcastrateix */
+       u_int                   sc_txantenna;   /* tx antenna (fixed or auto) */
+       HAL_INT                 sc_imask;       /* interrupt mask copy */
+       u_int                   sc_keymax;      /* size of key cache */
+       uint8_t                 sc_keymap[ATH_KEYBYTES];/* key use bit map */
+
+       u_int                   sc_ledpin;      /* GPIO pin for driving LED */
+       u_int                   sc_ledon;       /* pin setting for LED on */
+       u_int                   sc_ledidle;     /* idle polling interval */
+       int                     sc_ledevent;    /* time of last LED event */
+       uint8_t                 sc_rxrate;      /* current rx rate for LED */
+       uint8_t                 sc_txrate;      /* current tx rate for LED */
+       uint16_t                sc_ledoff;      /* off time for current blink */
+       struct callout          sc_ledtimer;    /* led off timer */
+
+       u_int                   sc_rfsilentpin; /* GPIO pin for rfkill int */
+       u_int                   sc_rfsilentpol; /* pin setting for rfkill on */
+
+       struct bpf_if           *sc_drvbpf;
+       union {
+               struct ath_tx_radiotap_header th;
+               uint8_t         pad[64];
+       } u_tx_rt;
+       int                     sc_tx_th_len;
+       union {
+               struct ath_rx_radiotap_header th;
+               uint8_t         pad[64];
+       } u_rx_rt;
+       int                     sc_rx_th_len;
+       u_int                   sc_monpass;     /* frames to pass in mon.mode */
+
+       struct ath_descdma      sc_rxdma;       /* RX descriptos */
+       ath_bufhead             sc_rxbuf;       /* receive buffer */
+       uint32_t                *sc_rxlink;     /* link ptr in last RX desc */
+       uint8_t                 sc_defant;      /* current default antenna */
+       uint8_t                 sc_rxotherant;  /* rx's on non-default antenna*/
+       uint64_t                sc_lastrx;      /* tsf at last rx'd frame */
+
+       struct ath_descdma      sc_txdma;       /* TX descriptors */
+       ath_bufhead             sc_txbuf;       /* transmit buffer */
+#if 0
+       struct mtx              sc_txbuflock;   /* txbuf lock */
+#endif
+       char                    sc_txname[12];  /* e.g. "ath0_buf" */
+       int                     sc_tx_timer;    /* transmit timeout */
+       u_int                   sc_txqsetup;    /* h/w queues setup */
+       u_int                   sc_txintrperiod;/* tx interrupt batching */
+       struct ath_txq          sc_txq[HAL_NUM_TX_QUEUES];
+       struct ath_txq          *sc_ac2q[5];    /* WME AC -> h/w q map */ 
+
+       struct ath_descdma      sc_bdma;        /* beacon descriptors */
+       ath_bufhead             sc_bbuf;        /* beacon buffers */
+       u_int                   sc_bhalq;       /* HAL q for outgoing beacons */
+       u_int                   sc_bmisscount;  /* missed beacon transmits */
+       uint32_t                sc_ant_tx[8];   /* recent tx frames/antenna */
+       struct ath_txq          *sc_cabq;       /* tx q for cab frames */
+       struct ieee80211_beacon_offsets sc_boff;/* dynamic update state */
+       enum {
+               OK,                             /* no change needed */
+               UPDATE,                         /* update pending */
+               COMMIT                          /* beacon sent, commit change */
+       } sc_updateslot;                        /* slot time update fsm */
+
+       struct callout          sc_cal_ch;      /* callout handle for cals */
+       int                     sc_calinterval; /* current polling interval */
+       int                     sc_caltries;    /* cals at current interval */
+       HAL_NODE_STATS          sc_halstats;    /* station-mode rssi stats */
+       struct callout          sc_scan_ch;     /* callout handle for scan */
+       struct callout          sc_dfs_ch;      /* callout handle for dfs */
+
+       struct sysctl_ctx_list  sc_sysctl_ctx;
+       struct sysctl_oid       *sc_sysctl_tree;
+};
+#define        sc_tx_th                u_tx_rt.th
+#define        sc_rx_th                u_rx_rt.th
+
+#define        IS_ATH_TXQ_SETUP(sc, i) ((sc)->sc_txqsetup & (1 << (i)))
+#define ATH_TXQ_SETUP(sc, i)   \
+       do { (sc)->sc_txqsetup |= (1 << (i)); } while (0)
+
+int    ath_attach(uint16_t, struct ath_softc *);
+int    ath_detach(struct ath_softc *);
+void   ath_resume(struct ath_softc *);
+void   ath_suspend(struct ath_softc *);
+void   ath_shutdown(struct ath_softc *);
+void   ath_intr(void *);
+
+/*
+ * HAL definitions to comply with local coding convention.
+ */
+#define        ath_hal_detach(_ah) \
+       ((*(_ah)->ah_detach)((_ah)))
+#define        ath_hal_reset(_ah, _opmode, _chan, _outdoor, _pstatus) \
+       ((*(_ah)->ah_reset)((_ah), (_opmode), (_chan), (_outdoor), (_pstatus)))
+#define        ath_hal_getratetable(_ah, _mode) \
+       ((*(_ah)->ah_getRateTable)((_ah), (_mode)))
+#define        ath_hal_getmac(_ah, _mac) \
+       ((*(_ah)->ah_getMacAddress)((_ah), (_mac)))
+#define        ath_hal_setmac(_ah, _mac) \
+       ((*(_ah)->ah_setMacAddress)((_ah), (_mac)))
+#define        ath_hal_intrset(_ah, _mask) \
+       ((*(_ah)->ah_setInterrupts)((_ah), (_mask)))
+#define        ath_hal_intrget(_ah) \
+       ((*(_ah)->ah_getInterrupts)((_ah)))
+#define        ath_hal_intrpend(_ah) \
+       ((*(_ah)->ah_isInterruptPending)((_ah)))
+#define        ath_hal_getisr(_ah, _pmask) \
+       ((*(_ah)->ah_getPendingInterrupts)((_ah), (_pmask)))
+#define        ath_hal_updatetxtriglevel(_ah, _inc) \
+       ((*(_ah)->ah_updateTxTrigLevel)((_ah), (_inc)))
+#define        ath_hal_setpower(_ah, _mode) \
+       ((*(_ah)->ah_setPowerMode)((_ah), (_mode), AH_TRUE))
+#define        ath_hal_keycachesize(_ah) \
+       ((*(_ah)->ah_getKeyCacheSize)((_ah)))
+#define        ath_hal_keyreset(_ah, _ix) \
+       ((*(_ah)->ah_resetKeyCacheEntry)((_ah), (_ix)))
+#define        ath_hal_keyset(_ah, _ix, _pk, _mac) \
+       ((*(_ah)->ah_setKeyCacheEntry)((_ah), (_ix), (_pk), (_mac), AH_FALSE))
+#define        ath_hal_keyisvalid(_ah, _ix) \
+       (((*(_ah)->ah_isKeyCacheEntryValid)((_ah), (_ix))))
+#define        ath_hal_keysetmac(_ah, _ix, _mac) \
+       ((*(_ah)->ah_setKeyCacheEntryMac)((_ah), (_ix), (_mac)))
+#define        ath_hal_getrxfilter(_ah) \
+       ((*(_ah)->ah_getRxFilter)((_ah)))
+#define        ath_hal_setrxfilter(_ah, _filter) \
+       ((*(_ah)->ah_setRxFilter)((_ah), (_filter)))
+#define        ath_hal_setmcastfilter(_ah, _mfilt0, _mfilt1) \
+       ((*(_ah)->ah_setMulticastFilter)((_ah), (_mfilt0), (_mfilt1)))
+#define        ath_hal_waitforbeacon(_ah, _bf) \
+       ((*(_ah)->ah_waitForBeaconDone)((_ah), (_bf)->bf_daddr))
+#define        ath_hal_putrxbuf(_ah, _bufaddr) \
+       ((*(_ah)->ah_setRxDP)((_ah), (_bufaddr)))
+#define        ath_hal_gettsf32(_ah) \
+       ((*(_ah)->ah_getTsf32)((_ah)))
+#define        ath_hal_gettsf64(_ah) \
+       ((*(_ah)->ah_getTsf64)((_ah)))
+#define        ath_hal_resettsf(_ah) \
+       ((*(_ah)->ah_resetTsf)((_ah)))
+#define        ath_hal_rxena(_ah) \
+       ((*(_ah)->ah_enableReceive)((_ah)))
+#define        ath_hal_puttxbuf(_ah, _q, _bufaddr) \
+       ((*(_ah)->ah_setTxDP)((_ah), (_q), (_bufaddr)))
+#define        ath_hal_gettxbuf(_ah, _q) \
+       ((*(_ah)->ah_getTxDP)((_ah), (_q)))
+#define        ath_hal_numtxpending(_ah, _q) \
+       ((*(_ah)->ah_numTxPending)((_ah), (_q)))
+#define        ath_hal_getrxbuf(_ah) \
+       ((*(_ah)->ah_getRxDP)((_ah)))
+#define        ath_hal_txstart(_ah, _q) \
+       ((*(_ah)->ah_startTxDma)((_ah), (_q)))
+#define        ath_hal_setchannel(_ah, _chan) \
+       ((*(_ah)->ah_setChannel)((_ah), (_chan)))
+#define        ath_hal_calibrate(_ah, _chan, _iqcal) \
+       ((*(_ah)->ah_perCalibration)((_ah), (_chan), (_iqcal)))
+#define        ath_hal_setledstate(_ah, _state) \
+       ((*(_ah)->ah_setLedState)((_ah), (_state)))
+#define        ath_hal_beaconinit(_ah, _nextb, _bperiod) \
+       ((*(_ah)->ah_beaconInit)((_ah), (_nextb), (_bperiod)))
+#define        ath_hal_beaconreset(_ah) \
+       ((*(_ah)->ah_resetStationBeaconTimers)((_ah)))
+#define        ath_hal_beacontimers(_ah, _bs) \
+       ((*(_ah)->ah_setStationBeaconTimers)((_ah), (_bs)))
+#define        ath_hal_setassocid(_ah, _bss, _associd) \
+       ((*(_ah)->ah_writeAssocid)((_ah), (_bss), (_associd)))
+#define        ath_hal_phydisable(_ah) \
+       ((*(_ah)->ah_phyDisable)((_ah)))
+#define        ath_hal_setopmode(_ah) \
+       ((*(_ah)->ah_setPCUConfig)((_ah)))
+#define        ath_hal_stoptxdma(_ah, _qnum) \
+       ((*(_ah)->ah_stopTxDma)((_ah), (_qnum)))
+#define        ath_hal_stoppcurecv(_ah) \
+       ((*(_ah)->ah_stopPcuReceive)((_ah)))
+#define        ath_hal_startpcurecv(_ah) \
+       ((*(_ah)->ah_startPcuReceive)((_ah)))
+#define        ath_hal_stopdmarecv(_ah) \
+       ((*(_ah)->ah_stopDmaReceive)((_ah)))
+#define        ath_hal_getdiagstate(_ah, _id, _indata, _insize, _outdata, _outsize) \
+       ((*(_ah)->ah_getDiagState)((_ah), (_id), \
+               (_indata), (_insize), (_outdata), (_outsize)))
+#define        ath_hal_setuptxqueue(_ah, _type, _irq) \
+       ((*(_ah)->ah_setupTxQueue)((_ah), (_type), (_irq)))
+#define        ath_hal_resettxqueue(_ah, _q) \
+       ((*(_ah)->ah_resetTxQueue)((_ah), (_q)))
+#define        ath_hal_releasetxqueue(_ah, _q) \
+       ((*(_ah)->ah_releaseTxQueue)((_ah), (_q)))
+#define        ath_hal_gettxqueueprops(_ah, _q, _qi) \
+       ((*(_ah)->ah_getTxQueueProps)((_ah), (_q), (_qi)))
+#define        ath_hal_settxqueueprops(_ah, _q, _qi) \
+       ((*(_ah)->ah_setTxQueueProps)((_ah), (_q), (_qi)))
+#define        ath_hal_getrfgain(_ah) \
+       ((*(_ah)->ah_getRfGain)((_ah)))
+#define        ath_hal_getdefantenna(_ah) \
+       ((*(_ah)->ah_getDefAntenna)((_ah)))
+#define        ath_hal_setdefantenna(_ah, _ant) \
+       ((*(_ah)->ah_setDefAntenna)((_ah), (_ant)))
+#define        ath_hal_rxmonitor(_ah, _arg, _chan) \
+       ((*(_ah)->ah_rxMonitor)((_ah), (_arg), (_chan)))
+#define        ath_hal_mibevent(_ah, _stats) \
+       ((*(_ah)->ah_procMibEvent)((_ah), (_stats)))
+#define        ath_hal_setslottime(_ah, _us) \
+       ((*(_ah)->ah_setSlotTime)((_ah), (_us)))
+#define        ath_hal_getslottime(_ah) \
+       ((*(_ah)->ah_getSlotTime)((_ah)))
+#define        ath_hal_setacktimeout(_ah, _us) \
+       ((*(_ah)->ah_setAckTimeout)((_ah), (_us)))
+#define        ath_hal_getacktimeout(_ah) \
+       ((*(_ah)->ah_getAckTimeout)((_ah)))
+#define        ath_hal_setctstimeout(_ah, _us) \
+       ((*(_ah)->ah_setCTSTimeout)((_ah), (_us)))
+#define        ath_hal_getctstimeout(_ah) \
+       ((*(_ah)->ah_getCTSTimeout)((_ah)))
+#define        ath_hal_getcapability(_ah, _cap, _param, _result) \
+       ((*(_ah)->ah_getCapability)((_ah), (_cap), (_param), (_result)))
+#define        ath_hal_setcapability(_ah, _cap, _param, _v, _status) \
+       ((*(_ah)->ah_setCapability)((_ah), (_cap), (_param), (_v), (_status)))
+#define        ath_hal_ciphersupported(_ah, _cipher) \
+       (ath_hal_getcapability(_ah, HAL_CAP_CIPHER, _cipher, NULL) == HAL_OK)
+#define        ath_hal_getregdomain(_ah, _prd) \
+       (ath_hal_getcapability(_ah, HAL_CAP_REG_DMN, 0, (_prd)) == HAL_OK)
+#define        ath_hal_setregdomain(_ah, _rd) \
+       ((*(_ah)->ah_setRegulatoryDomain)((_ah), (_rd), NULL))
+#define        ath_hal_getcountrycode(_ah, _pcc) \
+       (*(_pcc) = (_ah)->ah_countryCode)
+#define        ath_hal_tkipsplit(_ah) \
+       (ath_hal_getcapability(_ah, HAL_CAP_TKIP_SPLIT, 0, NULL) == HAL_OK)
+#define        ath_hal_hwphycounters(_ah) \
+       (ath_hal_getcapability(_ah, HAL_CAP_PHYCOUNTERS, 0, NULL) == HAL_OK)
+#define        ath_hal_hasdiversity(_ah) \
+       (ath_hal_getcapability(_ah, HAL_CAP_DIVERSITY, 0, NULL) == HAL_OK)
+#define        ath_hal_getdiversity(_ah) \
+       (ath_hal_getcapability(_ah, HAL_CAP_DIVERSITY, 1, NULL) == HAL_OK)
+#define        ath_hal_setdiversity(_ah, _v) \
+       ath_hal_setcapability(_ah, HAL_CAP_DIVERSITY, 1, _v, NULL)
+#define        ath_hal_getdiag(_ah, _pv) \
+       (ath_hal_getcapability(_ah, HAL_CAP_DIAG, 0, _pv) == HAL_OK)
+#define        ath_hal_setdiag(_ah, _v) \
+       ath_hal_setcapability(_ah, HAL_CAP_DIAG, 0, _v, NULL)
+#define        ath_hal_getnumtxqueues(_ah, _pv) \
+       (ath_hal_getcapability(_ah, HAL_CAP_NUM_TXQUEUES, 0, _pv) == HAL_OK)
+#define        ath_hal_hasveol(_ah) \
+       (ath_hal_getcapability(_ah, HAL_CAP_VEOL, 0, NULL) == HAL_OK)
+#define        ath_hal_hastxpowlimit(_ah) \
+       (ath_hal_getcapability(_ah, HAL_CAP_TXPOW, 0, NULL) == HAL_OK)
+#define        ath_hal_settxpowlimit(_ah, _pow) \
+       ((*(_ah)->ah_setTxPowerLimit)((_ah), (_pow)))
+#define        ath_hal_gettxpowlimit(_ah, _ppow) \
+       (ath_hal_getcapability(_ah, HAL_CAP_TXPOW, 1, _ppow) == HAL_OK)
+#define        ath_hal_getmaxtxpow(_ah, _ppow) \
+       (ath_hal_getcapability(_ah, HAL_CAP_TXPOW, 2, _ppow) == HAL_OK)
+#define        ath_hal_gettpscale(_ah, _scale) \
+       (ath_hal_getcapability(_ah, HAL_CAP_TXPOW, 3, _scale) == HAL_OK)
+#define        ath_hal_settpscale(_ah, _v) \
+       ath_hal_setcapability(_ah, HAL_CAP_TXPOW, 3, _v, NULL)
+#define        ath_hal_hastpc(_ah) \
+       (ath_hal_getcapability(_ah, HAL_CAP_TPC, 0, NULL) == HAL_OK)
+#define        ath_hal_gettpc(_ah) \
+       (ath_hal_getcapability(_ah, HAL_CAP_TPC, 1, NULL) == HAL_OK)
+#define        ath_hal_settpc(_ah, _v) \
+       ath_hal_setcapability(_ah, HAL_CAP_TPC, 1, _v, NULL)
+#define        ath_hal_hasbursting(_ah) \
+       (ath_hal_getcapability(_ah, HAL_CAP_BURST, 0, NULL) == HAL_OK)
+#ifdef notyet
+#define        ath_hal_hasmcastkeysearch(_ah) \
+       (ath_hal_getcapability(_ah, HAL_CAP_MCAST_KEYSRCH, 0, NULL) == HAL_OK)
+#define        ath_hal_getmcastkeysearch(_ah) \
+       (ath_hal_getcapability(_ah, HAL_CAP_MCAST_KEYSRCH, 1, NULL) == HAL_OK)
+#else
+#define        ath_hal_getmcastkeysearch(_ah)  0
+#endif
+#define        ath_hal_hasrfsilent(_ah) \
+       (ath_hal_getcapability(_ah, HAL_CAP_RFSILENT, 0, NULL) == HAL_OK)
+#define        ath_hal_getrfkill(_ah) \
+       (ath_hal_getcapability(_ah, HAL_CAP_RFSILENT, 1, NULL) == HAL_OK)
+#define        ath_hal_setrfkill(_ah, _onoff) \
+       ath_hal_setcapability(_ah, HAL_CAP_RFSILENT, 1, _onoff, NULL)
+#define        ath_hal_getrfsilent(_ah, _prfsilent) \
+       (ath_hal_getcapability(_ah, HAL_CAP_RFSILENT, 2, _prfsilent) == HAL_OK)
+#define        ath_hal_setrfsilent(_ah, _rfsilent) \
+       ath_hal_setcapability(_ah, HAL_CAP_RFSILENT, 2, _rfsilent, NULL)
+#define        ath_hal_gettpack(_ah, _ptpack) \
+       (ath_hal_getcapability(_ah, HAL_CAP_TPC_ACK, 0, _ptpack) == HAL_OK)
+#define        ath_hal_settpack(_ah, _tpack) \
+       ath_hal_setcapability(_ah, HAL_CAP_TPC_ACK, 0, _tpack, NULL)
+#define        ath_hal_gettpcts(_ah, _ptpcts) \
+       (ath_hal_getcapability(_ah, HAL_CAP_TPC_CTS, 0, _ptpcts) == HAL_OK)
+#define        ath_hal_settpcts(_ah, _tpcts) \
+       ath_hal_setcapability(_ah, HAL_CAP_TPC_CTS, 0, _tpcts, NULL)
+#if HAL_ABI_VERSION < 0x05120700
+#define        ath_hal_process_noisefloor(_ah)
+#define        ath_hal_getchannoise(_ah, _c)   (-96)
+#define        HAL_CAP_TPC_ACK 100
+#define        HAL_CAP_TPC_CTS 101
+#else
+#define        ath_hal_getchannoise(_ah, _c) \
+       ((*(_ah)->ah_getChanNoise)((_ah), (_c)))
+#endif
+#if HAL_ABI_VERSION < 0x05122200
+#define        HAL_TXQ_TXOKINT_ENABLE  TXQ_FLAG_TXOKINT_ENABLE
+#define        HAL_TXQ_TXERRINT_ENABLE TXQ_FLAG_TXERRINT_ENABLE
+#define        HAL_TXQ_TXDESCINT_ENABLE TXQ_FLAG_TXDESCINT_ENABLE
+#define        HAL_TXQ_TXEOLINT_ENABLE TXQ_FLAG_TXEOLINT_ENABLE
+#define        HAL_TXQ_TXURNINT_ENABLE TXQ_FLAG_TXURNINT_ENABLE
+#endif
+
+#define        ath_hal_setuprxdesc(_ah, _ds, _size, _intreq) \
+       ((*(_ah)->ah_setupRxDesc)((_ah), (_ds), (_size), (_intreq)))
+#define        ath_hal_rxprocdesc(_ah, _ds, _dspa, _dsnext) \
+       ((*(_ah)->ah_procRxDesc)((_ah), (_ds), (_dspa), (_dsnext), 0))
+#define        ath_hal_setuptxdesc(_ah, _ds, _plen, _hlen, _atype, _txpow, \
+               _txr0, _txtr0, _keyix, _ant, _flags, \
+               _rtsrate, _rtsdura) \
+       ((*(_ah)->ah_setupTxDesc)((_ah), (_ds), (_plen), (_hlen), (_atype), \
+               (_txpow), (_txr0), (_txtr0), (_keyix), (_ant), \
+               (_flags), (_rtsrate), (_rtsdura), 0, 0, 0))
+#define        ath_hal_setupxtxdesc(_ah, _ds, \
+               _txr1, _txtr1, _txr2, _txtr2, _txr3, _txtr3) \
+       ((*(_ah)->ah_setupXTxDesc)((_ah), (_ds), \
+               (_txr1), (_txtr1), (_txr2), (_txtr2), (_txr3), (_txtr3)))
+#define        ath_hal_filltxdesc(_ah, _ds, _l, _first, _last, _ds0) \
+       ((*(_ah)->ah_fillTxDesc)((_ah), (_ds), (_l), (_first), (_last), (_ds0)))
+#define        ath_hal_txprocdesc(_ah, _ds) \
+       ((*(_ah)->ah_procTxDesc)((_ah), (_ds)))
+#define        ath_hal_gettxintrtxqs(_ah, _txqs) \
+       ((*(_ah)->ah_getTxIntrQueue)((_ah), (_txqs)))
+
+#define ath_hal_gpioCfgOutput(_ah, _gpio) \
+        ((*(_ah)->ah_gpioCfgOutput)((_ah), (_gpio)))
+#define ath_hal_gpioset(_ah, _gpio, _b) \
+        ((*(_ah)->ah_gpioSet)((_ah), (_gpio), (_b)))
+#define ath_hal_gpioget(_ah, _gpio) \
+        ((*(_ah)->ah_gpioGet)((_ah), (_gpio)))
+#define ath_hal_gpiosetintr(_ah, _gpio, _b) \
+        ((*(_ah)->ah_gpioSetIntr)((_ah), (_gpio), (_b)))
+
+#define ath_hal_radar_event(_ah) \
+       ((*(_ah)->ah_radarHaveEvent)((_ah)))
+#define ath_hal_procdfs(_ah, _chan) \
+       ((*(_ah)->ah_processDfs)((_ah), (_chan)))
+#define ath_hal_checknol(_ah, _chan, _nchans) \
+       ((*(_ah)->ah_dfsNolCheck)((_ah), (_chan), (_nchans)))
+#define ath_hal_radar_wait(_ah, _chan) \
+       ((*(_ah)->ah_radarWait)((_ah), (_chan)))
+
+#endif /* _DEV_ATH_ATHVAR_H */
diff --git a/sys/dev/netif/ath/hal/Makefile b/sys/dev/netif/ath/hal/Makefile
new file mode 100644 (file)
index 0000000..df063a1
--- /dev/null
@@ -0,0 +1,64 @@
+#
+# Copyright (c) 2002, 2003 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.
+#
+# $FreeBSD: src/sys/modules/ath_hal/Makefile,v 1.4 2005/01/18 03:28:08 sam Exp $
+# $DragonFly: src/sys/dev/netif/ath/hal/Makefile,v 1.1 2006/07/13 09:15:22 sephe Exp $
+#
+
+#
+# Hardware Access Layer (HAL) for the Atheros Wireless NIC driver.
+#
+# This module contains the hardware-specific bits for the network
+# interface driver.  It is built as a separate module to simplify
+# maintenance and isolate the bits that are not (currently) distributed
+# in source form.
+#
+
+.include <bsd.init.mk>
+
+.PATH: ${HAL}/public
+
+KMOD   = ath_hal
+SRCS   = ah_osdep.c ah_osdep.c.patch
+SRCS   += bus_if.h device_if.h pci_if.h opt_ah.h
+OBJS   = hal.o
+
+hal.o: ${MACHINE_ARCH}-elf.hal.o.uu
+       uudecode -p < ${.ALLSRC} > ${.TARGET}
+
+opt_ah.h: ${MACHINE_ARCH}-elf.opt_ah.h
+       cp ${.ALLSRC} ${.TARGET}
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/netif/ath/hal/ah_osdep.c.patch b/sys/dev/netif/ath/hal/ah_osdep.c.patch
new file mode 100644 (file)
index 0000000..b426639
--- /dev/null
@@ -0,0 +1,63 @@
+# $DragonFly: src/sys/dev/netif/ath/hal/Attic/ah_osdep.c.patch,v 1.1 2006/07/13 09:15:22 sephe Exp $
+--- ah_osdep.c 2006-04-22 17:06:43.000000000 +0800
++++ ah_osdep.c 2006-04-22 22:31:56.000000000 +0800
+@@ -110,7 +110,7 @@ ath_hal_free(void* p)
+ }
+ void
+-ath_hal_vprintf(struct ath_hal *ah, const char* fmt, va_list ap)
++ath_hal_vprintf(struct ath_hal *ah, const char* fmt, __va_list ap)
+ {
+       vprintf(fmt, ap);
+ }
+@@ -118,16 +118,19 @@ ath_hal_vprintf(struct ath_hal *ah, cons
+ void
+ ath_hal_printf(struct ath_hal *ah, const char* fmt, ...)
+ {
+-      va_list ap;
+-      va_start(ap, fmt);
++      __va_list ap;
++      __va_start(ap, fmt);
+       ath_hal_vprintf(ah, fmt, ap);
+-      va_end(ap);
++      __va_end(ap);
+ }
+ const char*
+ ath_hal_ether_sprintf(const u_int8_t *mac)
+ {
+-      return ether_sprintf(mac);
++      static char etherbuf[18];
++
++      snprintf(etherbuf, sizeof(etherbuf), "%6D", mac, ":");
++      return etherbuf;
+ }
+ #ifdef AH_DEBUG
+@@ -136,9 +139,9 @@ HALDEBUG(struct ath_hal *ah, const char*
+ {
+       if (ath_hal_debug) {
+               __va_list ap;
+-              va_start(ap, fmt);
++              __va_start(ap, fmt);
+               ath_hal_vprintf(ah, fmt, ap);
+-              va_end(ap);
++              __va_end(ap);
+       }
+ }
+@@ -364,10 +367,10 @@ ath_hal_delay(int n)
+ u_int32_t
+ ath_hal_getuptime(struct ath_hal *ah)
+ {
+-      struct bintime bt;
+-      getbinuptime(&bt);
+-      return (bt.sec * 1000) +
+-              (((uint64_t)1000 * (uint32_t)(bt.frac >> 32)) >> 32);
++      struct timeval tv;
++
++      getmicrouptime(&tv);
++      return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+ }
+ void
diff --git a/sys/dev/netif/ath/rate_amrr/Makefile b/sys/dev/netif/ath/rate_amrr/Makefile
new file mode 100644 (file)
index 0000000..de2191b
--- /dev/null
@@ -0,0 +1,46 @@
+#
+# Copyright (c) 2004 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.
+#
+# $FreeBSD: src/sys/modules/ath_rate_amrr/Makefile,v 1.1 2004/12/08 17:38:37 sam Exp $
+# $DragonFly: src/sys/dev/netif/ath/rate_amrr/Makefile,v 1.1 2006/07/13 09:15:22 sephe Exp $
+#
+
+KMOD   = ath_rate
+SRCS   = amrr.c
+SRCS   += device_if.h bus_if.h pci_if.h
+
+KMODDEPS = ath_hal
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/netif/ath/rate_amrr/amrr.c b/sys/dev/netif/ath/rate_amrr/amrr.c
new file mode 100644 (file)
index 0000000..c943f71
--- /dev/null
@@ -0,0 +1,551 @@
+/*
+ * Copyright (c) 2004 INRIA
+ * 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.
+ *
+ * $FreeBSD: src/sys/dev/ath/ath_rate/amrr/amrr.c,v 1.8.2.3 2006/02/24 19:51:11 sam Exp $
+ * $DragonFly: src/sys/dev/netif/ath/rate_amrr/amrr.c,v 1.1 2006/07/13 09:15:22 sephe Exp $
+ */
+
+/*
+ * AMRR rate control. See:
+ * http://www-sop.inria.fr/rapports/sophia/RR-5208.html
+ * "IEEE 802.11 Rate Adaptation: A Practical Approach" by
+ *    Mathieu Lacage, Hossein Manshaei, Thierry Turletti
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/sysctl.h>
+#include <sys/module.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/errno.h>
+#include <sys/serialize.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+#include <dev/netif/ath/ath/if_athvar.h>
+#include <dev/netif/ath/rate_amrr/amrr.h>
+#include <contrib/dev/ath/ah_desc.h>
+
+#define        AMRR_DEBUG
+#ifdef AMRR_DEBUG
+#define        DPRINTF(sc, _fmt, ...) do {                                     \
+       if (sc->sc_debug & 0x10)                                        \
+               printf(_fmt, __VA_ARGS__);                              \
+} while (0)
+#else
+#define        DPRINTF(sc, _fmt, ...)
+#endif
+
+static int ath_rateinterval = 1000;            /* rate ctl interval (ms)  */
+static int ath_rate_max_success_threshold = 10;
+static int ath_rate_min_success_threshold = 1;
+
+static void    ath_ratectl(void *);
+static void    ath_rate_update(struct ath_softc *, struct ieee80211_node *,
+                       int rate);
+static void    ath_rate_ctl_start(struct ath_softc *, struct ieee80211_node *);
+static void    ath_rate_ctl(void *, struct ieee80211_node *);
+
+void
+ath_rate_node_init(struct ath_softc *sc, struct ath_node *an)
+{
+       /* NB: assumed to be zero'd by caller */
+       ath_rate_update(sc, &an->an_node, 0);
+}
+
+void
+ath_rate_node_cleanup(struct ath_softc *sc, struct ath_node *an)
+{
+}
+
+void
+ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,
+       int shortPreamble, size_t frameLen,
+       uint8_t *rix, int *try0, uint8_t *txrate)
+{
+       struct amrr_node *amn = ATH_NODE_AMRR(an);
+
+       *rix = amn->amn_tx_rix0;
+       *try0 = amn->amn_tx_try0;
+       if (shortPreamble)
+               *txrate = amn->amn_tx_rate0sp;
+       else
+               *txrate = amn->amn_tx_rate0;
+}
+
+void
+ath_rate_setupxtxdesc(struct ath_softc *sc, struct ath_node *an,
+       struct ath_desc *ds, int shortPreamble, uint8_t rix)
+{
+       struct amrr_node *amn = ATH_NODE_AMRR(an);
+
+       ath_hal_setupxtxdesc(sc->sc_ah, ds
+               , amn->amn_tx_rate1sp, amn->amn_tx_try1 /* series 1 */
+               , amn->amn_tx_rate2sp, amn->amn_tx_try2 /* series 2 */
+               , amn->amn_tx_rate3sp, amn->amn_tx_try3 /* series 3 */
+       );
+}
+
+void
+ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
+       const struct ath_desc *ds, const struct ath_desc *ds0)
+{
+       struct amrr_node *amn = ATH_NODE_AMRR(an);
+       int sr = ds->ds_txstat.ts_shortretry;
+       int lr = ds->ds_txstat.ts_longretry;
+       int retry_count = sr + lr;
+
+       amn->amn_tx_try0_cnt++;
+       if (retry_count == 1) {
+               amn->amn_tx_try1_cnt++;
+       } else if (retry_count == 2) {
+               amn->amn_tx_try1_cnt++;
+               amn->amn_tx_try2_cnt++;
+       } else if (retry_count == 3) {
+               amn->amn_tx_try1_cnt++;
+               amn->amn_tx_try2_cnt++;
+               amn->amn_tx_try3_cnt++;
+       } else if (retry_count > 3) {
+               amn->amn_tx_try1_cnt++;
+               amn->amn_tx_try2_cnt++;
+               amn->amn_tx_try3_cnt++;
+               amn->amn_tx_failure_cnt++;
+       }
+}
+
+void
+ath_rate_newassoc(struct ath_softc *sc, struct ath_node *an, int isnew)
+{
+       if (isnew)
+               ath_rate_ctl_start(sc, &an->an_node);
+}
+
+static void 
+node_reset (struct amrr_node *amn)
+{
+       amn->amn_tx_try0_cnt = 0;
+       amn->amn_tx_try1_cnt = 0;
+       amn->amn_tx_try2_cnt = 0;
+       amn->amn_tx_try3_cnt = 0;
+       amn->amn_tx_failure_cnt = 0;
+       amn->amn_success = 0;
+       amn->amn_recovery = 0;
+       amn->amn_success_threshold = ath_rate_min_success_threshold;
+}
+
+
+/**
+ * The code below assumes that we are dealing with hardware multi rate retry
+ * I have no idea what will happen if you try to use this module with another
+ * type of hardware. Your machine might catch fire or it might work with
+ * horrible performance...
+ */
+static void
+ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)
+{
+       struct ath_node *an = ATH_NODE(ni);
+       struct amrr_node *amn = ATH_NODE_AMRR(an);
+       const HAL_RATE_TABLE *rt = sc->sc_currates;
+       uint8_t rix;
+
+       KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+
+       DPRINTF(sc, "%s: set xmit rate for %6D to %dM\n",
+           __func__, ni->ni_macaddr, ":",
+           ni->ni_rates.rs_nrates > 0 ?
+               (ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL) / 2 : 0);
+
+       ni->ni_txrate = rate;
+       /*
+        * Before associating a node has no rate set setup
+        * so we can't calculate any transmit codes to use.
+        * This is ok since we should never be sending anything
+        * but management frames and those always go at the
+        * lowest hardware rate.
+        */
+       if (ni->ni_rates.rs_nrates > 0) {
+               amn->amn_tx_rix0 = sc->sc_rixmap[
+                                              ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL];
+               amn->amn_tx_rate0 = rt->info[amn->amn_tx_rix0].rateCode;
+               amn->amn_tx_rate0sp = amn->amn_tx_rate0 |
+                       rt->info[amn->amn_tx_rix0].shortPreamble;
+               if (sc->sc_mrretry) {
+                       amn->amn_tx_try0 = 1;
+                       amn->amn_tx_try1 = 1;
+                       amn->amn_tx_try2 = 1;
+                       amn->amn_tx_try3 = 1;
+                       if (--rate >= 0) {
+                               rix = sc->sc_rixmap[
+                                                   ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL];
+                               amn->amn_tx_rate1 = rt->info[rix].rateCode;
+                               amn->amn_tx_rate1sp = amn->amn_tx_rate1 |
+                                       rt->info[rix].shortPreamble;
+                       } else {
+                               amn->amn_tx_rate1 = amn->amn_tx_rate1sp = 0;
+                       }
+                       if (--rate >= 0) {
+                               rix = sc->sc_rixmap[
+                                                   ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL];
+                               amn->amn_tx_rate2 = rt->info[rix].rateCode;
+                               amn->amn_tx_rate2sp = amn->amn_tx_rate2 |
+                                       rt->info[rix].shortPreamble;
+                       } else {
+                               amn->amn_tx_rate2 = amn->amn_tx_rate2sp = 0;
+                       }
+                       if (rate > 0) {
+                               /* NB: only do this if we didn't already do it above */
+                               amn->amn_tx_rate3 = rt->info[0].rateCode;
+                               amn->amn_tx_rate3sp =
+                                       amn->amn_tx_rate3 | rt->info[0].shortPreamble;
+                       } else {
+                               amn->amn_tx_rate3 = amn->amn_tx_rate3sp = 0;
+                       }
+               } else {
+                       amn->amn_tx_try0 = ATH_TXMAXTRY;
+                       /* theorically, these statements are useless because
+                        *  the code which uses them tests for an_tx_try0 == ATH_TXMAXTRY
+                        */
+                       amn->amn_tx_try1 = 0;
+                       amn->amn_tx_try2 = 0;
+                       amn->amn_tx_try3 = 0;
+                       amn->amn_tx_rate1 = amn->amn_tx_rate1sp = 0;
+                       amn->amn_tx_rate2 = amn->amn_tx_rate2sp = 0;
+                    &