- Add ral(4) for Ralink RT2500/RT2501/RT2600 chip based wireless NIC
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Sat, 20 May 2006 09:13:09 +0000 (09:13 +0000)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Sat, 20 May 2006 09:13:09 +0000 (09:13 +0000)
- Add ral(4) to GENERIC and LINT
- Add man page for ral(4)
Reviewed-by: swildner
Thank Damien Bergamini for his work on this driver

For RT2500:
- Fix a ieee80211_node leakage
- Due to the inter-dependency nature of DONE/(ENCRYPT|DECRYPT) intr, reap desc
  rings twice if one of them comes.  This change gives me ~17.6% TX performance
  boost on my ASUS WL-107G (WPA is used here):
  Original way of TX/RX intr processing
  ------------------------------------------------------------
  Client connecting to sephe-test, TCP port 5001
  TCP window size: 32.5 KByte (default)
  ------------------------------------------------------------
  [  3] local 192.168.2.14 port 1063 connected with 192.168.2.254 port 5001
  [  3]  0.0- 5.0 sec  10.2 MBytes  17.1 Mbits/sec
  [  3]  5.0-10.0 sec  9.95 MBytes  16.7 Mbits/sec
  [  3] 10.0-15.0 sec  9.67 MBytes  16.2 Mbits/sec
  [  3] 15.0-20.0 sec  10.1 MBytes  17.0 Mbits/sec
  [  3] 20.0-25.0 sec  10.2 MBytes  17.1 Mbits/sec
  [  3] 25.0-30.0 sec  10.0 MBytes  16.8 Mbits/sec
  [  3] 30.0-35.0 sec  9.91 MBytes  16.6 Mbits/sec
  [  3] 35.0-40.0 sec  10.3 MBytes  17.2 Mbits/sec
  [  3] 40.0-45.0 sec  9.87 MBytes  16.6 Mbits/sec
  [  3] 45.0-50.0 sec  9.94 MBytes  16.7 Mbits/sec
  [  3] 50.0-55.0 sec  10.2 MBytes  17.2 Mbits/sec
  [  3] 55.0-60.0 sec  9.73 MBytes  16.3 Mbits/sec
  [  3]  0.0-60.0 sec    120 MBytes  16.8 Mbits/sec

  Adapted way of TX/RX intr processing
  ------------------------------------------------------------
  Client connecting to sephe-test, TCP port 5001
  TCP window size: 32.5 KByte (default)
  ------------------------------------------------------------
  [  3] local 192.168.2.14 port 1062 connected with 192.168.2.254 port 5001
  [  3]  0.0- 5.0 sec  11.8 MBytes  19.8 Mbits/sec
  [  3]  5.0-10.0 sec  11.5 MBytes  19.4 Mbits/sec
  [  3] 10.0-15.0 sec  11.1 MBytes  18.7 Mbits/sec
  [  3] 15.0-20.0 sec  12.0 MBytes  20.1 Mbits/sec
  [  3] 20.0-25.0 sec  12.6 MBytes  21.2 Mbits/sec
  [  3] 25.0-30.0 sec  11.7 MBytes  19.6 Mbits/sec
  [  3] 30.0-35.0 sec  12.3 MBytes  20.7 Mbits/sec
  [  3] 35.0-40.0 sec  11.9 MBytes  19.9 Mbits/sec
  [  3] 40.0-45.0 sec  11.9 MBytes  19.9 Mbits/sec
  [  3] 45.0-50.0 sec  12.2 MBytes  20.4 Mbits/sec
  [  3] 50.0-55.0 sec  12.1 MBytes  20.2 Mbits/sec
  [  3] 55.0-60.0 sec  12.3 MBytes  20.7 Mbits/sec
  [  3]  0.0-60.0 sec    143 MBytes  20.0 Mbits/sec

Obtained-from: FreeBSD

21 files changed:
share/man/man4/Makefile
share/man/man4/ral.4 [new file with mode: 0644]
sys/conf/files
sys/config/GENERIC
sys/config/LINT
sys/dev/netif/Makefile
sys/dev/netif/ral/Makefile [new file with mode: 0644]
sys/dev/netif/ral/if_ral_pci.c [new file with mode: 0644]
sys/dev/netif/ral/if_ralrate.c [new file with mode: 0644]
sys/dev/netif/ral/if_ralrate.h [new file with mode: 0644]
sys/dev/netif/ral/if_ralreg.h [new file with mode: 0644]
sys/dev/netif/ral/if_ralvar.h [new file with mode: 0644]
sys/dev/netif/ral/rt2560.c [new file with mode: 0644]
sys/dev/netif/ral/rt2560reg.h [new file with mode: 0644]
sys/dev/netif/ral/rt2560var.h [new file with mode: 0644]
sys/dev/netif/ral/rt2661.c [new file with mode: 0644]
sys/dev/netif/ral/rt2661_ucode.h [new file with mode: 0644]
sys/dev/netif/ral/rt2661reg.h [new file with mode: 0644]
sys/dev/netif/ral/rt2661var.h [new file with mode: 0644]
sys/i386/conf/GENERIC
sys/i386/conf/LINT

index 61b47db..f77e462 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.37 2006/05/20 07:15:17 sephe Exp $
+# $DragonFly: src/share/man/man4/Makefile,v 1.38 2006/05/20 09:13:09 sephe Exp $
 
 MAN=   aac.4 \
        acpi.4 \
@@ -172,6 +172,7 @@ MAN=        aac.4 \
        pt.4 \
        pty.4 \
        puc.4 \
+       ral.4 \
        rc.4 \
        re.4 \
        rl.4 \
diff --git a/share/man/man4/ral.4 b/share/man/man4/ral.4
new file mode 100644 (file)
index 0000000..3b6c255
--- /dev/null
@@ -0,0 +1,225 @@
+.\" Copyright (c) 2005, 2006
+.\"     Damien Bergamini <damien.bergamini@free.fr>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" $FreeBSD: src/share/man/man4/ral.4,v 1.10 2006/03/13 21:24:28 damien Exp $
+.\" $DragonFly: src/share/man/man4/ral.4,v 1.1 2006/05/20 09:13:09 sephe Exp $
+.\"
+.Dd May 20, 2006
+.Os
+.Dt RAL 4
+.Sh NAME
+.Nm ral
+.Nd "Ralink Technology IEEE 802.11 wireless network driver"
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device ral"
+.Cd "device wlan"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+if_ral_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver supports PCI/CardBus wireless adapters based on the Ralink Technology
+RT2500, RT2501, and RT2600 chipsets.
+.Pp
+The RT2500 chipset is the first generation of 802.11b/g adapters from Ralink.
+It consists of two integrated chips, a RT2560 MAC/BBP and a RT2525 radio
+transceiver.
+.Pp
+The RT2501 chipset is the second generation of 802.11b/g adapters from Ralink.
+It consists of two integrated chips, a RT2561 MAC/BBP and a RT2527 radio
+transceiver.
+This chipset provides support for the IEEE 802.11e standard with multiple
+hardware transmission queues and allows scatter/gather for efficient DMA
+operations.
+.Pp
+The RT2600 chipset consists of two integrated chips, a RT2661 MAC/BBP and a
+RT2529 radio transceiver.
+This chipset uses the MIMO (multiple-input multiple-output) technology with
+multiple antennas to extend the operating range of the adapter and to achieve
+higher throughput.
+MIMO is the basis of the forthcoming IEEE 802.11n standard.
+.Pp
+The transmit speed is user-selectable or can be adapted automatically by the
+driver depending on the received signal strength and on the number of hardware
+transmission retries.
+.Sh HARDWARE
+The
+.Nm
+driver supports the following adapters:
+.Pp
+.Bl -column -compact ".Li Atlantis Land A02-PCM-W54" "RT2561S" "CardBus"
+.It Em Card Ta Em MAC/BBP Ta Em Bus
+.It Li "A-Link WL54H" Ta RT2560 Ta PCI
+.It Li "A-Link WL54PC" Ta RT2560 Ta CardBus
+.It Li "AirLink101 AWLC5025" Ta RT2661 Ta CardBus
+.It Li "AirLink101 AWLH5025" Ta RT2661 Ta PCI
+.It Li "Amigo AWI-914W" Ta RT2560 Ta CardBus
+.It Li "Amigo AWI-922W" Ta RT2560 Ta mini-PCI
+.It Li "Amigo AWI-926W" Ta RT2560 Ta PCI
+.It Li "AMIT WL531C" Ta RT2560 Ta CardBus
+.It Li "AMIT WL531P" Ta RT2560 Ta PCI
+.It Li "AOpen AOI-831" Ta RT2560 Ta PCI
+.It Li "ASUS WL-107G" Ta RT2560 Ta CardBus
+.It Li "ASUS WL-130g" Ta RT2560 Ta PCI
+.It Li "Atlantis Land A02-PCI-W54" Ta RT2560 Ta PCI
+.It Li "Atlantis Land A02-PCM-W54" Ta RT2560 Ta CardBus
+.It Li "Belkin F5D7000 v3" Ta RT2560 Ta PCI
+.It Li "Belkin F5D7010 v2" Ta RT2560 Ta CardBus
+.It Li "Billionton MIWLGRL" Ta RT2560 Ta mini-PCI
+.It Li "Canyon CN-WF511" Ta RT2560 Ta PCI
+.It Li "Canyon CN-WF513" Ta RT2560 Ta CardBus
+.It Li "CC&C WL-2102" Ta RT2560 Ta CardBus
+.It Li "CNet CWC-854" Ta RT2560 Ta CardBus
+.It Li "CNet CWP-854" Ta RT2560 Ta PCI
+.It Li "Compex WL54G" Ta RT2560 Ta CardBus
+.It Li "Compex WLP54G" Ta RT2560 Ta PCI
+.It Li "Conceptronic C54RC" Ta RT2560 Ta CardBus
+.It Li "Conceptronic C54Ri" Ta RT2560 Ta PCI
+.It Li "Digitus DN-7001G-RA" Ta RT2560 Ta CardBus
+.It Li "Digitus DN-7006G-RA" Ta RT2560 Ta PCI
+.It Li "E-Tech WGPC02" Ta RT2560 Ta CardBus
+.It Li "E-Tech WGPI02" Ta RT2560 Ta PCI
+.It Li "Edimax EW-7108PCg" Ta RT2560 Ta CardBus
+.It Li "Edimax EW-7128g" Ta RT2560 Ta PCI
+.It Li "Eminent EM3036" Ta RT2560 Ta CardBus
+.It Li "Eminent EM3037" Ta RT2560 Ta PCI
+.It Li "Encore ENLWI-G-RLAM" Ta RT2560 Ta PCI
+.It Li "Encore ENPWI-G-RLAM" Ta RT2560 Ta CardBus
+.It Li "Fiberline WL-400P" Ta RT2560 Ta PCI
+.It Li "Fibreline WL-400X" Ta RT2560 Ta CardBus
+.It Li "Gigabyte GN-WI01GS" Ta RT2561S Ta mini-PCI
+.It Li "Gigabyte GN-WIKG" Ta RT2560 Ta mini-PCI
+.It Li "Gigabyte GN-WMKG" Ta RT2560 Ta CardBus
+.It Li "Gigabyte GN-WP01GS" Ta RT2561S Ta PCI
+.It Li "Gigabyte GN-WPKG" Ta RT2560 Ta PCI
+.It Li "Hawking HWC54GR" Ta RT2560 Ta CardBus
+.It Li "Hawking HWP54GR" Ta RT2560 Ta PCI
+.It Li "iNexQ CR054g-009 (R03)" Ta RT2560 Ta PCI
+.It Li "JAHT WN-4054P" Ta RT2560 Ta CardBus
+.It Li "JAHT WN-4054PCI" Ta RT2560 Ta PCI
+.It Li "LevelOne WNC-0301 v2" Ta RT2560 Ta PCI
+.It Li "LevelOne WPC-0301 v2" Ta RT2560 Ta CardBus
+.It Li "Linksys WMP54G v4" Ta RT2560 Ta PCI
+.It Li "Micronet SP906GK" Ta RT2560 Ta PCI
+.It Li "Micronet SP908GK V3" Ta RT2560 Ta CardBus
+.It Li "Minitar MN54GCB-R" Ta RT2560 Ta CardBus
+.It Li "Minitar MN54GPC-R" Ta RT2560 Ta PCI
+.It Li "MSI CB54G2" Ta RT2560 Ta CardBus
+.It Li "MSI MP54G2" Ta RT2560 Ta mini-PCI
+.It Li "MSI PC54G2" Ta RT2560 Ta PCI
+.It Li "OvisLink EVO-W54PCI" Ta RT2560 Ta PCI
+.It Li "PheeNet HWL-PCIG/RA" Ta RT2560 Ta PCI
+.It Li "Pro-Nets CB80211G" Ta RT2560 Ta CardBus
+.It Li "Pro-Nets PC80211G" Ta RT2560 Ta PCI
+.It Li "Repotec RP-WB7108" Ta RT2560 Ta CardBus
+.It Li "Repotec RP-WP0854" Ta RT2560 Ta PCI
+.It Li "SATech SN-54C" Ta RT2560 Ta CardBus
+.It Li "SATech SN-54P" Ta RT2560 Ta PCI
+.It Li "Sitecom WL-112" Ta RT2560 Ta CardBus
+.It Li "Sitecom WL-115" Ta RT2560 Ta PCI
+.It Li "SMC SMCWCB-GM" Ta RT2661 Ta CardBus
+.It Li "SMC SMCWPCI-GM" Ta RT2661 Ta PCI
+.It Li "SparkLAN WL-685R" Ta RT2560 Ta CardBus
+.It Li "Surecom EP-9321-g" Ta RT2560 Ta PCI
+.It Li "Surecom EP-9321-g1" Ta RT2560 Ta PCI
+.It Li "Surecom EP-9428-g" Ta RT2560 Ta CardBus
+.It Li "Sweex LC500050" Ta RT2560 Ta CardBus
+.It Li "Sweex LC700030" Ta RT2560 Ta PCI
+.It Li "TekComm NE-9321-g" Ta RT2560 Ta PCI
+.It Li "TekComm NE-9428-g" Ta RT2560 Ta CardBus
+.It Li "Unex CR054g-R02" Ta RT2560 Ta PCI
+.It Li "Unex MR054g-R02" Ta RT2560 Ta CardBus
+.It Li "Zinwell ZWX-G160" Ta RT2560 Ta CardBus
+.It Li "Zinwell ZWX-G360" Ta RT2560 Ta mini-PCI
+.It Li "Zinwell ZWX-G361" Ta RT2560 Ta PCI
+.It Li "Zonet ZEW1500" Ta RT2560 Ta CardBus
+.It Li "Zonet ZEW1600" Ta RT2560 Ta PCI
+.El
+.Pp
+An up to date list can be found at
+.Pa http://damien.bergamini.free.fr/ral/list.html .
+.Sh EXAMPLES
+Join an existing BSS network (i.e., connect to an access point):
+.Pp
+.Dl "ifconfig ral0 inet 192.168.0.20 netmask 0xffffff00"
+.Pp
+Join a specific BSS network with network name
+.Dq Li my_net :
+.Pp
+.Dl "ifconfig ral0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net"
+.Pp
+Join a specific BSS network with 40-bit WEP encryption:
+.Bd -literal -offset indent
+ifconfig ral0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
+    wepmode on wepkey 0x1234567890 weptxkey 1
+.Ed
+.Pp
+Join a specific BSS network with 104-bit WEP encryption:
+.Bd -literal -offset indent
+ifconfig ral0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \e
+    wepmode on wepkey 0x01020304050607080910111213 weptxkey 1
+.Ed
+.Sh DIAGNOSTICS
+.Bl -diag
+.It "ral%d: could not load 8051 microcode"
+An error occurred while attempting to upload the microcode to the onboard 8051
+microcontroller unit.
+.It "ral%d: timeout waiting for MCU to initialize"
+The onboard 8051 microcontroller unit failed to initialize in time.
+.It "ral%d: device timeout"
+A frame dispatched to the hardware for transmission did not complete in time.
+The driver will reset the hardware.
+This should not happen.
+.El
+.Sh SEE ALSO
+.Xr arp 4 ,
+.Xr cardbus 4 ,
+.Xr netintro 4 ,
+.Xr pci 4 ,
+.Xr wlan 4 ,
+.Xr ifconfig 8
+.Rs
+.%T "Ralink Technology"
+.%O http://www.ralinktech.com/
+.Re
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 3.7 .
+.Sh CAVEATS
+PCI
+.Nm
+adapters seem to require a PCI 2.2 compliant motherboard and will likely not
+work with PCI 2.1 only motherboard.
+.Pp
+The
+.Nm
+driver does not implement frame aggregation.
+.Sh AUTHORS
+The
+.Nm
+driver was written by
+.An Damien Bergamini Aq damien@FreeBSD.org .
index c40969d..de8d9a7 100644 (file)
@@ -1,5 +1,5 @@
 # $FreeBSD: src/sys/conf/files,v 1.340.2.137 2003/06/04 17:10:30 sam Exp $
-# $DragonFly: src/sys/conf/files,v 1.122 2006/05/20 07:15:17 sephe Exp $
+# $DragonFly: src/sys/conf/files,v 1.123 2006/05/20 09:13:09 sephe Exp $
 #
 # The long compile-with and dependency lines are required because of
 # limitations in config: backslash-newline doesn't work in strings, and
@@ -251,6 +251,10 @@ dev/netif/acx/if_acx.c     optional acx
 dev/netif/acx/acxcmd.c optional acx
 dev/netif/acx/acx100.c optional acx
 dev/netif/acx/acx111.c optional acx
+dev/netif/ral/if_ral_pci.c     optional ral
+dev/netif/ral/if_ralrate.c     optional ral
+dev/netif/ral/rt2560.c         optional ral
+dev/netif/ral/rt2661.c         optional ral
 dev/netif/lge/if_lge.c optional lge
 dev/disk/md/md.c               optional md
 dev/netif/mii_layer/mii.c              optional miibus
index f010601..79baf10 100644 (file)
@@ -4,7 +4,7 @@
 # Check the LINT configuration file in sys/i386/conf, for an
 # exhaustive list of options.
 #
-# $DragonFly: src/sys/config/GENERIC,v 1.35 2006/05/20 07:15:17 sephe Exp $
+# $DragonFly: src/sys/config/GENERIC,v 1.36 2006/05/20 09:13:09 sephe Exp $
 
 machine                i386
 cpu            I386_CPU
@@ -228,6 +228,7 @@ device              wlan_wep        # 802.11 WEP support
 # those parameters here.
 device         an
 #device                awi             # PRISM I IEEE 802.11b wireless NIC
+device         ral             # Ralink Technology 802.11 wireless NIC
 # WaveLAN/IEEE 802.11 wireless NICs.  Note: the WaveLAN/IEEE really
 # exists only as a PCMCIA device, so there is no ISA attachment needed
 # and resources will always be dynamically assigned by the pccard code.
index 256eb4c..a4af2fa 100644 (file)
@@ -3,7 +3,7 @@
 #      as much of the source tree as it can.
 #
 # $FreeBSD: src/sys/i386/conf/LINT,v 1.749.2.144 2003/06/04 17:56:59 sam Exp $
-# $DragonFly: src/sys/config/LINT,v 1.77 2006/05/20 07:15:17 sephe Exp $
+# $DragonFly: src/sys/config/LINT,v 1.78 2006/05/20 09:13:09 sephe Exp $
 #
 # NB: You probably don't want to try running a kernel built from this
 # file.  Instead, you should start from GENERIC, and add options from
@@ -1428,6 +1428,7 @@ device            acx             # TI ACX100/ACX111
 device wl0 at isa? port 0x300  # T1 speed ISA/radio lan
 device         xe              # Xircom PCMCIA
 device         ray             # Raytheon Raylink/Webgear Aviator
+device         ral             # Ralink Technology 802.11 wireless NIC
 
 device oltr0 at isa?
 
index ba8eb75..d459f48 100644 (file)
@@ -1,8 +1,8 @@
-# $DragonFly: src/sys/dev/netif/Makefile,v 1.18 2006/05/20 07:15:17 sephe Exp $
+# $DragonFly: src/sys/dev/netif/Makefile,v 1.19 2006/05/20 09:13:09 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 ray re rl rue sbni sbsh sf sis sk \
+       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/ral/Makefile b/sys/dev/netif/ral/Makefile
new file mode 100644 (file)
index 0000000..14cae36
--- /dev/null
@@ -0,0 +1,7 @@
+# $DragonFly: src/sys/dev/netif/ral/Makefile,v 1.1 2006/05/20 09:13:09 sephe Exp $
+KMOD   = if_ral
+
+SRCS   = if_ral_pci.c if_ralrate.c rt2560.c rt2661.c
+SRCS   += device_if.h bus_if.h pci_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/netif/ral/if_ral_pci.c b/sys/dev/netif/ral/if_ral_pci.c
new file mode 100644 (file)
index 0000000..4ec9915
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2005, 2006
+ *     Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD: src/sys/dev/ral/if_ral_pci.c,v 1.4 2006/03/05 23:27:51 silby Exp $
+ * $DragonFly: src/sys/dev/netif/ral/if_ral_pci.c,v 1.1 2006/05/20 09:13:09 sephe Exp $
+ */
+
+/*
+ * PCI/Cardbus front-end for the Ralink RT2560/RT2561/RT2561S/RT2661 driver.
+ */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+#include <netproto/802_11/ieee80211_radiotap.h>
+
+#include <bus/pci/pcireg.h>
+#include <bus/pci/pcivar.h>
+
+#include <dev/netif/ral/if_ralrate.h>
+#include <dev/netif/ral/rt2560var.h>
+#include <dev/netif/ral/rt2661var.h>
+
+struct ral_opns {
+       int     (*attach)(device_t, int);
+       int     (*detach)(void *);
+       void    (*shutdown)(void *);
+       void    (*suspend)(void *);
+       void    (*resume)(void *);
+};
+
+static const struct ral_opns ral_rt2560_opns = {
+       .attach         = rt2560_attach,
+       .detach         = rt2560_detach,
+       .shutdown       = rt2560_shutdown,
+       .suspend        = rt2560_suspend,
+       .resume         = rt2560_resume,
+};
+
+static const struct ral_opns ral_rt2661_opns = {
+       .attach         = rt2661_attach,
+       .detach         = rt2661_detach,
+       .shutdown       = rt2661_shutdown,
+       .suspend        = rt2661_suspend,
+       .resume         = rt2661_resume,
+};
+
+static const struct ral_pci_ident {
+       uint16_t                vendor;
+       uint16_t                device;
+       const char              *name;
+       const struct ral_opns   *opns;
+} ral_pci_ids[] = {
+       { 0x1814, 0x0201, "Ralink Technology RT2560", &ral_rt2560_opns },
+       { 0x1814, 0x0301, "Ralink Technology RT2561S", &ral_rt2661_opns },
+       { 0x1814, 0x0302, "Ralink Technology RT2561", &ral_rt2661_opns },
+       { 0x1814, 0x0401, "Ralink Technology RT2661", &ral_rt2661_opns },
+       { 0, 0, NULL, NULL }
+};
+
+struct ral_pci_softc {
+       /* XXX MUST be the first field */
+       union {
+               struct rt2560_softc sc_rt2560;
+               struct rt2661_softc sc_rt2661;
+       } u;
+
+       const struct ral_opns   *sc_opns;
+       int                     mem_rid;
+       struct resource         *mem;
+};
+
+static int ral_pci_probe(device_t);
+static int ral_pci_attach(device_t);
+static int ral_pci_detach(device_t);
+static int ral_pci_shutdown(device_t);
+static int ral_pci_suspend(device_t);
+static int ral_pci_resume(device_t);
+
+static device_method_t ral_pci_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_probe,         ral_pci_probe),
+       DEVMETHOD(device_attach,        ral_pci_attach),
+       DEVMETHOD(device_detach,        ral_pci_detach),
+       DEVMETHOD(device_shutdown,      ral_pci_shutdown),
+       DEVMETHOD(device_suspend,       ral_pci_suspend),
+       DEVMETHOD(device_resume,        ral_pci_resume),
+
+       { 0, 0 }
+};
+
+static driver_t ral_pci_driver = {
+       "ral",
+       ral_pci_methods,
+       sizeof (struct ral_pci_softc)
+};
+
+static devclass_t ral_devclass;
+
+DRIVER_MODULE(ral, pci, ral_pci_driver, ral_devclass, 0, 0);
+DRIVER_MODULE(ral, cardbus, ral_pci_driver, ral_devclass, 0, 0);
+
+MODULE_DEPEND(ral, wlan, 1, 1, 1);
+MODULE_DEPEND(ral, pci, 1, 1, 1);
+MODULE_DEPEND(ral, cardbus, 1, 1, 1);
+
+static int
+ral_pci_probe(device_t dev)
+{
+       const struct ral_pci_ident *ident;
+       uint16_t vid, did;
+
+       vid = pci_get_vendor(dev);
+       did = pci_get_device(dev);
+       for (ident = ral_pci_ids; ident->name != NULL; ident++) {
+               if (vid == ident->vendor && did == ident->device) {
+                       struct ral_pci_softc *psc = device_get_softc(dev);
+
+                       psc->sc_opns = ident->opns;
+                       device_set_desc(dev, ident->name);
+                       return 0;
+               }
+       }
+       return ENXIO;
+}
+
+/* Base Address Register */
+#define RAL_PCI_BAR0   0x10
+
+static int
+ral_pci_attach(device_t dev)
+{
+       struct ral_pci_softc *psc = device_get_softc(dev);
+       struct rt2560_softc *sc = &psc->u.sc_rt2560;
+       int error;
+
+       /* Assign `dev' earlier, so that we can do possible error clean up */
+       sc->sc_dev = dev;
+
+       if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
+               device_printf(dev, "chip is in D%d power mode "
+                   "-- setting to D0\n", pci_get_powerstate(dev));
+               pci_set_powerstate(dev, PCI_POWERSTATE_D0);
+       }
+
+       /* enable bus-mastering */
+       pci_enable_busmaster(dev);
+
+       psc->mem_rid = RAL_PCI_BAR0;
+       psc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &psc->mem_rid,
+                                         RF_ACTIVE);
+       if (psc->mem == NULL) {
+               device_printf(dev, "could not allocate memory resource\n");
+               return ENXIO;
+       }
+       sc->sc_st = rman_get_bustag(psc->mem);
+       sc->sc_sh = rman_get_bushandle(psc->mem);
+
+       error = psc->sc_opns->attach(dev, pci_get_device(dev));
+       if (error != 0)
+               ral_pci_detach(dev);
+
+       return error;
+}
+
+static int
+ral_pci_detach(device_t dev)
+{
+       struct ral_pci_softc *psc = device_get_softc(dev);
+
+       if (device_is_attached(dev))
+               psc->sc_opns->detach(psc);
+
+       bus_generic_detach(dev);
+
+       if (psc->mem != NULL) {
+               bus_release_resource(dev, SYS_RES_MEMORY, psc->mem_rid,
+                                    psc->mem);
+       }
+       return 0;
+}
+
+static int
+ral_pci_shutdown(device_t dev)
+{
+       struct ral_pci_softc *psc = device_get_softc(dev);
+
+       psc->sc_opns->shutdown(psc);
+       return 0;
+}
+
+static int
+ral_pci_suspend(device_t dev)
+{
+       struct ral_pci_softc *psc = device_get_softc(dev);
+
+       psc->sc_opns->suspend(psc);
+       return 0;
+}
+
+static int
+ral_pci_resume(device_t dev)
+{
+       struct ral_pci_softc *psc = device_get_softc(dev);
+
+       psc->sc_opns->resume(psc);
+       return 0;
+}
diff --git a/sys/dev/netif/ral/if_ralrate.c b/sys/dev/netif/ral/if_ralrate.c
new file mode 100644 (file)
index 0000000..3ae3b14
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2003, 2004 David Young.  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.
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution.
+ * 3. The name of David Young may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL David
+ * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE.
+ *
+ * $NetBSD: ieee80211_rssadapt.c,v 1.9 2005/02/26 22:45:09 perry Exp $
+ * $FreeBSD: src/sys/dev/ral/if_ralrate.c,v 1.1 2005/04/18 18:47:36 damien Exp $
+ * $DragonFly: src/sys/dev/netif/ral/Attic/if_ralrate.c,v 1.1 2006/05/20 09:13:09 sephe Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+
+#include <dev/netif/ral/if_ralrate.h>
+
+#ifdef interpolate
+#undef interpolate
+#endif
+#define interpolate(parm, old, new) ((parm##_old * (old) + \
+                                     (parm##_denom - parm##_old) * (new)) / \
+                                   parm##_denom)
+
+static struct ral_rssadapt_expavgctl master_expavgctl = {
+       rc_decay_denom : 16,
+       rc_decay_old : 15,
+       rc_thresh_denom : 8,
+       rc_thresh_old : 4,
+       rc_avgrssi_denom : 8,
+       rc_avgrssi_old : 4
+};
+
+int
+ral_rssadapt_choose(struct ral_rssadapt *ra, struct ieee80211_rateset *rs,
+    struct ieee80211_frame *wh, u_int len, const char *dvname, int do_not_adapt)
+{
+       u_int16_t (*thrs)[IEEE80211_RATE_SIZE];
+       int flags = 0, i, rateidx = 0, thridx, top;
+
+       if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
+               flags |= IEEE80211_RATE_BASIC;
+
+       for (i = 0, top = RAL_RSSADAPT_BKT0;
+            i < RAL_RSSADAPT_BKTS;
+            i++, top <<= RAL_RSSADAPT_BKTPOWER) {
+               thridx = i;
+               if (len <= top)
+                       break;
+       }
+
+       thrs = &ra->ra_rate_thresh[thridx];
+
+       i = rs->rs_nrates;
+       while (--i >= 0) {
+               rateidx = i;
+               if ((rs->rs_rates[i] & flags) != flags)
+                       continue;
+               if (do_not_adapt)
+                       break;
+               if ((*thrs)[i] < ra->ra_avg_rssi)
+                       break;
+       }
+
+       return rateidx;
+}
+
+void
+ral_rssadapt_updatestats(struct ral_rssadapt *ra)
+{
+       long interval;
+
+       ra->ra_pktrate =
+           (ra->ra_pktrate + 10 * (ra->ra_nfail + ra->ra_nok)) / 2;
+       ra->ra_nfail = ra->ra_nok = 0;
+
+       /* a node is eligible for its rate to be raised every 1/10 to 10
+        * seconds, more eligible in proportion to recent packet rates.
+        */
+       interval = MAX(100000, 10000000 / MAX(1, 10 * ra->ra_pktrate));
+       ra->ra_raise_interval.tv_sec = interval / (1000 * 1000);
+       ra->ra_raise_interval.tv_usec = interval % (1000 * 1000);
+}
+
+void
+ral_rssadapt_input(struct ieee80211com *ic, struct ieee80211_node *ni,
+    struct ral_rssadapt *ra, int rssi)
+{
+       ra->ra_avg_rssi = interpolate(master_expavgctl.rc_avgrssi,
+                                     ra->ra_avg_rssi, (rssi << 8));
+}
+
+/*
+ * Adapt the data rate to suit the conditions.  When a transmitted
+ * packet is dropped after RAL_RSSADAPT_RETRY_LIMIT retransmissions,
+ * raise the RSS threshold for transmitting packets of similar length at
+ * the same data rate.
+ */
+void
+ral_rssadapt_lower_rate(struct ieee80211com *ic, struct ieee80211_node *ni,
+    struct ral_rssadapt *ra, struct ral_rssdesc *id)
+{
+       struct ieee80211_rateset *rs = &ni->ni_rates;
+       u_int16_t last_thr;
+       u_int i, thridx, top;
+
+       ra->ra_nfail++;
+
+       if (id->id_rateidx >= rs->rs_nrates)
+               return;
+
+       for (i = 0, top = RAL_RSSADAPT_BKT0;
+            i < RAL_RSSADAPT_BKTS;
+            i++, top <<= RAL_RSSADAPT_BKTPOWER) {
+               thridx = i;
+               if (id->id_len <= top)
+                       break;
+       }
+
+       last_thr = ra->ra_rate_thresh[thridx][id->id_rateidx];
+       ra->ra_rate_thresh[thridx][id->id_rateidx] =
+           interpolate(master_expavgctl.rc_thresh, last_thr,
+                       (id->id_rssi << 8));
+}
+
+void
+ral_rssadapt_raise_rate(struct ieee80211com *ic, struct ral_rssadapt *ra,
+    struct ral_rssdesc *id)
+{
+       u_int16_t (*thrs)[IEEE80211_RATE_SIZE], newthr, oldthr;
+       struct ieee80211_node *ni = id->id_node;
+       struct ieee80211_rateset *rs = &ni->ni_rates;
+       int i, rate, top;
+
+       ra->ra_nok++;
+
+       if (!ratecheck(&ra->ra_last_raise, &ra->ra_raise_interval))
+               return;
+
+       for (i = 0, top = RAL_RSSADAPT_BKT0;
+            i < RAL_RSSADAPT_BKTS;
+            i++, top <<= RAL_RSSADAPT_BKTPOWER) {
+               thrs = &ra->ra_rate_thresh[i];
+               if (id->id_len <= top)
+                       break;
+       }
+
+       if (id->id_rateidx + 1 < rs->rs_nrates &&
+           (*thrs)[id->id_rateidx + 1] > (*thrs)[id->id_rateidx]) {
+               rate = (rs->rs_rates[id->id_rateidx + 1] & IEEE80211_RATE_VAL);
+
+               oldthr = (*thrs)[id->id_rateidx + 1];
+               if ((*thrs)[id->id_rateidx] == 0)
+                       newthr = ra->ra_avg_rssi;
+               else
+                       newthr = (*thrs)[id->id_rateidx];
+               (*thrs)[id->id_rateidx + 1] =
+                   interpolate(master_expavgctl.rc_decay, oldthr, newthr);
+       }
+}
diff --git a/sys/dev/netif/ral/if_ralrate.h b/sys/dev/netif/ral/if_ralrate.h
new file mode 100644 (file)
index 0000000..99182fc
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2003, 2004 David Young.  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.
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution.
+ * 3. The name of David Young may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL David
+ * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE.
+ *
+ * $NetBSD: ieee80211_rssadapt.h,v 1.4 2005/02/26 22:45:09 perry Exp $
+ * $FreeBSD: src/sys/dev/ral/if_ralrate.h,v 1.1 2005/04/18 18:47:36 damien Exp $
+ * $DragonFly: src/sys/dev/netif/ral/Attic/if_ralrate.h,v 1.1 2006/05/20 09:13:09 sephe Exp $
+ */
+
+/* Data-rate adaptation loosely based on "Link Adaptation Strategy
+ * for IEEE 802.11 WLAN via Received Signal Strength Measurement"
+ * by Javier del Prado Pavon and Sunghyun Choi.
+ */
+
+/* Buckets for frames 0-128 bytes long, 129-1024, 1025-maximum. */
+#define        RAL_RSSADAPT_BKTS               3
+#define RAL_RSSADAPT_BKT0              128
+#define        RAL_RSSADAPT_BKTPOWER   3       /* 2**_BKTPOWER */
+
+#define        ral_rssadapt_thresh_new \
+    (ral_rssadapt_thresh_denom - ral_rssadapt_thresh_old)
+#define        ral_rssadapt_decay_new \
+    (ral_rssadapt_decay_denom - ral_rssadapt_decay_old)
+#define        ral_rssadapt_avgrssi_new \
+    (ral_rssadapt_avgrssi_denom - ral_rssadapt_avgrssi_old)
+
+struct ral_rssadapt_expavgctl {
+       /* RSS threshold decay. */
+       u_int rc_decay_denom;
+       u_int rc_decay_old;
+       /* RSS threshold update. */
+       u_int rc_thresh_denom;
+       u_int rc_thresh_old;
+       /* RSS average update. */
+       u_int rc_avgrssi_denom;
+       u_int rc_avgrssi_old;
+};
+
+struct ral_rssadapt {
+       /* exponential average RSSI << 8 */
+       u_int16_t               ra_avg_rssi;
+       /* Tx failures in this update interval */
+       u_int32_t               ra_nfail;
+       /* Tx successes in this update interval */
+       u_int32_t               ra_nok;
+       /* exponential average packets/second */
+       u_int32_t               ra_pktrate;
+       /* RSSI threshold for each Tx rate */
+       u_int16_t               ra_rate_thresh[RAL_RSSADAPT_BKTS]
+                                             [IEEE80211_RATE_SIZE];
+       struct timeval          ra_last_raise;
+       struct timeval          ra_raise_interval;
+};
+
+/* Properties of a Tx packet, for link adaptation. */
+struct ral_rssdesc {
+       u_int                    id_len;        /* Tx packet length */
+       u_int                    id_rateidx;    /* index into ni->ni_rates */
+       struct ieee80211_node   *id_node;       /* destination STA MAC */
+       u_int8_t                 id_rssi;       /* destination STA avg RSS @
+                                                * Tx time
+                                                */
+};
+
+void   ral_rssadapt_updatestats(struct ral_rssadapt *);
+void   ral_rssadapt_input(struct ieee80211com *, struct ieee80211_node *,
+           struct ral_rssadapt *, int);
+void   ral_rssadapt_lower_rate(struct ieee80211com *,
+           struct ieee80211_node *, struct ral_rssadapt *,
+           struct ral_rssdesc *);
+void   ral_rssadapt_raise_rate(struct ieee80211com *,
+           struct ral_rssadapt *, struct ral_rssdesc *);
+int    ral_rssadapt_choose(struct ral_rssadapt *,
+           struct ieee80211_rateset *, struct ieee80211_frame *, u_int,
+           const char *, int);
diff --git a/sys/dev/netif/ral/if_ralreg.h b/sys/dev/netif/ral/if_ralreg.h
new file mode 100644 (file)
index 0000000..35f3a5a
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 2005, 2006
+ *     Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD: src/sys/dev/ral/if_ralreg.h,v 1.1.2.1 2006/01/29 15:21:46 damien Exp $
+ * $DragonFly: src/sys/dev/netif/ral/Attic/if_ralreg.h,v 1.1 2006/05/20 09:13:09 sephe Exp $
+ */
+
+#define RAL_TX_RING_COUNT      48
+#define RAL_ATIM_RING_COUNT    4
+#define RAL_PRIO_RING_COUNT    16
+#define RAL_BEACON_RING_COUNT  1
+#define RAL_RX_RING_COUNT      32
+
+#define RAL_TX_DESC_SIZE       (sizeof (struct ral_tx_desc))
+#define RAL_RX_DESC_SIZE       (sizeof (struct ral_rx_desc))
+
+#define RAL_MAX_SCATTER        1
+
+/*
+ * Control and status registers.
+ */
+#define RAL_CSR0       0x0000  /* ASIC version number */
+#define RAL_CSR1       0x0004  /* System control */
+#define RAL_CSR3       0x000c  /* STA MAC address 0 */
+#define RAL_CSR4       0x0010  /* STA MAC address 1 */
+#define RAL_CSR5       0x0014  /* BSSID 0 */
+#define RAL_CSR6       0x0018  /* BSSID 1 */
+#define RAL_CSR7       0x001c  /* Interrupt source */
+#define RAL_CSR8       0x0020  /* Interrupt mask */
+#define RAL_CSR9       0x0024  /* Maximum frame length */
+#define RAL_SECCSR0    0x0028  /* WEP control */
+#define RAL_CSR11      0x002c  /* Back-off control */
+#define RAL_CSR12      0x0030  /* Synchronization configuration 0 */
+#define RAL_CSR13      0x0034  /* Synchronization configuration 1 */
+#define RAL_CSR14      0x0038  /* Synchronization control */
+#define RAL_CSR15      0x003c  /* Synchronization status */
+#define RAL_CSR16      0x0040  /* TSF timer 0 */
+#define RAL_CSR17      0x0044  /* TSF timer 1 */
+#define RAL_CSR18      0x0048  /* IFS timer 0 */
+#define RAL_CSR19      0x004c  /* IFS timer 1 */
+#define RAL_CSR20      0x0050  /* WAKEUP timer */
+#define RAL_CSR21      0x0054  /* EEPROM control */
+#define RAL_CSR22      0x0058  /* CFP control */
+#define RAL_TXCSR0     0x0060  /* TX control */
+#define RAL_TXCSR1     0x0064  /* TX configuration */
+#define RAL_TXCSR2     0x0068  /* TX descriptor configuration */
+#define RAL_TXCSR3     0x006c  /* TX ring base address */
+#define RAL_TXCSR4     0x0070  /* TX ATIM ring base address */
+#define RAL_TXCSR5     0x0074  /* TX PRIO ring base address */
+#define RAL_TXCSR6     0x0078  /* Beacon base address */
+#define RAL_TXCSR7     0x007c  /* AutoResponder control */
+#define RAL_RXCSR0     0x0080  /* RX control */
+#define RAL_RXCSR1     0x0084  /* RX descriptor configuration */
+#define RAL_RXCSR2     0x0088  /* RX ring base address */
+#define RAL_PCICSR     0x008c  /* PCI control */
+#define RAL_RXCSR3     0x0090  /* BBP ID 0 */
+#define RAL_TXCSR9     0x0094  /* OFDM TX BBP */
+#define RAL_ARSP_PLCP_0        0x0098  /* Auto Responder PLCP address */
+#define RAL_ARSP_PLCP_1        0x009c  /* Auto Responder PLCP Basic Rate bit mask */
+#define RAL_CNT0       0x00a0  /* FCS error counter */
+#define RAL_CNT1       0x00ac  /* PLCP error counter */
+#define RAL_CNT2       0x00b0  /* Long error counter */
+#define RAL_CNT3       0x00b8  /* CCA false alarm counter */
+#define RAL_CNT4       0x00bc  /* RX FIFO Overflow counter */
+#define RAL_CNT5       0x00c0  /* Tx FIFO Underrun counter */
+#define RAL_PWRCSR0    0x00c4  /* Power mode configuration */
+#define RAL_PSCSR0     0x00c8  /* Power state transition time */
+#define RAL_PSCSR1     0x00cc  /* Power state transition time */
+#define RAL_PSCSR2     0x00d0  /* Power state transition time */
+#define RAL_PSCSR3     0x00d4  /* Power state transition time */
+#define RAL_PWRCSR1    0x00d8  /* Manual power control/status */
+#define RAL_TIMECSR    0x00dc  /* Timer control */
+#define RAL_MACCSR0    0x00e0  /* MAC configuration */
+#define RAL_MACCSR1    0x00e4  /* MAC configuration */
+#define RAL_RALINKCSR  0x00e8  /* Ralink RX auto-reset BBCR */
+#define RAL_BCNCSR     0x00ec  /* Beacon interval control */
+#define RAL_BBPCSR     0x00f0  /* BBP serial control */
+#define RAL_RFCSR      0x00f4  /* RF serial control */
+#define RAL_LEDCSR     0x00f8  /* LED control */
+#define RAL_SECCSR3    0x00fc  /* XXX not documented */
+#define RAL_DMACSR0    0x0100  /* Current RX ring address */
+#define RAL_DMACSR1    0x0104  /* Current Tx ring address */
+#define RAL_DMACSR2    0x0104  /* Current Priority ring address */
+#define RAL_DMACSR3    0x0104  /* Current ATIM ring address */
+#define RAL_TXACKCSR0  0x0110  /* XXX not documented */
+#define RAL_GPIOCSR    0x0120  /* */
+#define RAL_BBBPPCSR   0x0124  /* BBP Pin Control */
+#define RAL_FIFOCSR0   0x0128  /* TX FIFO pointer */
+#define RAL_FIFOCSR1   0x012c  /* RX FIFO pointer */
+#define RAL_BCNOCSR    0x0130  /* Beacon time offset */
+#define RAL_RLPWCSR    0x0134  /* RX_PE Low Width */
+#define RAL_TESTCSR    0x0138  /* Test Mode Select */
+#define RAL_PLCP1MCSR  0x013c  /* Signal/Service/Length of ACK/CTS @1M */
+#define RAL_PLCP2MCSR  0x0140  /* Signal/Service/Length of ACK/CTS @2M */
+#define RAL_PLCP5p5MCSR        0x0144  /* Signal/Service/Length of ACK/CTS @5.5M */
+#define RAL_PLCP11MCSR 0x0148  /* Signal/Service/Length of ACK/CTS @11M */
+#define RAL_ACKPCTCSR  0x014c  /* ACK/CTS padload consume time */
+#define RAL_ARTCSR1    0x0150  /* ACK/CTS padload consume time */
+#define RAL_ARTCSR2    0x0154  /* ACK/CTS padload consume time */
+#define RAL_SECCSR1    0x0158  /* WEP control */
+#define RAL_BBPCSR1    0x015c  /* BBP TX Configuration */
+
+
+/* possible flags for register RXCSR0 */
+#define RAL_DISABLE_RX         (1 << 0)
+#define RAL_DROP_CRC_ERROR     (1 << 1)
+#define RAL_DROP_PHY_ERROR     (1 << 2)
+#define RAL_DROP_CTL           (1 << 3)
+#define RAL_DROP_NOT_TO_ME     (1 << 4)
+#define RAL_DROP_TODS          (1 << 5)
+#define RAL_DROP_VERSION_ERROR (1 << 6)
+
+/* possible flags for register CSR1 */
+#define RAL_RESET_ASIC (1 << 0)
+#define RAL_RESET_BBP  (1 << 1)
+#define RAL_HOST_READY (1 << 2)
+
+/* possible flags for register CSR14 */
+#define RAL_ENABLE_TSF                 (1 << 0)
+#define RAL_ENABLE_TSF_SYNC(x)         (((x) & 0x3) << 1)
+#define RAL_ENABLE_TBCN                        (1 << 3)
+#define RAL_ENABLE_BEACON_GENERATOR    (1 << 6)
+
+/* possible flags for register CSR21 */
+#define RAL_EEPROM_C           (1 << 1)
+#define RAL_EEPROM_S           (1 << 2)
+#define RAL_EEPROM_D           (1 << 3)
+#define RAL_EEPROM_Q           (1 << 4)
+#define RAL_EEPROM_93C46       (1 << 5)
+
+#define RAL_EEPROM_SHIFT_D     3
+#define RAL_EEPROM_SHIFT_Q     4
+
+/* possible flags for register TXCSR0 */
+#define RAL_KICK_TX    (1 << 0)
+#define RAL_KICK_ATIM  (1 << 1)
+#define RAL_KICK_PRIO  (1 << 2)
+#define RAL_ABORT_TX   (1 << 3)
+
+/* possible flags for register SECCSR0 */
+#define RAL_KICK_DECRYPT       (1 << 0)
+
+/* possible flags for register SECCSR1 */
+#define RAL_KICK_ENCRYPT       (1 << 0)
+
+/* possible flags for register CSR7 */
+#define RAL_BEACON_EXPIRE      0x00000001
+#define RAL_WAKEUP_EXPIRE      0x00000002
+#define RAL_ATIM_EXPIRE                0x00000004
+#define RAL_TX_DONE            0x00000008
+#define RAL_ATIM_DONE          0x00000010
+#define RAL_PRIO_DONE          0x00000020
+#define RAL_RX_DONE            0x00000040
+#define RAL_DECRYPTION_DONE    0x00000080
+#define RAL_ENCRYPTION_DONE    0x00000100
+
+#define RAL_INTR_MASK                                                  \
+       (~(RAL_BEACON_EXPIRE | RAL_WAKEUP_EXPIRE | RAL_TX_DONE |        \
+          RAL_PRIO_DONE | RAL_RX_DONE | RAL_DECRYPTION_DONE |          \
+          RAL_ENCRYPTION_DONE))
+
+/* Tx descriptor */
+struct ral_tx_desc {
+       uint32_t        flags;
+#define RAL_TX_BUSY            (1 << 0)
+#define RAL_TX_VALID           (1 << 1)
+
+#define RAL_TX_RESULT_MASK     0x0000001c
+#define RAL_TX_SUCCESS         (0 << 2)
+#define RAL_TX_SUCCESS_RETRY   (1 << 2)
+#define RAL_TX_FAIL_RETRY      (2 << 2)
+#define RAL_TX_FAIL_INVALID    (3 << 2)
+#define RAL_TX_FAIL_OTHER      (4 << 2)
+
+#define RAL_TX_MORE_FRAG       (1 << 8)
+#define RAL_TX_ACK             (1 << 9)
+#define RAL_TX_TIMESTAMP       (1 << 10)
+#define RAL_TX_OFDM            (1 << 11)
+#define RAL_TX_CIPHER_BUSY     (1 << 12)
+
+#define RAL_TX_IFS_MASK                0x00006000
+#define RAL_TX_IFS_BACKOFF     (0 << 13)
+#define RAL_TX_IFS_SIFS                (1 << 13)
+#define RAL_TX_IFS_NEWBACKOFF  (2 << 13)
+#define RAL_TX_IFS_NONE                (3 << 13)
+
+#define RAL_TX_LONG_RETRY      (1 << 15)
+
+#define RAL_TX_CIPHER_MASK     0xe0000000
+#define RAL_TX_CIPHER_NONE     (0 << 29)
+#define RAL_TX_CIPHER_WEP40    (1 << 29)
+#define RAL_TX_CIPHER_WEP104   (2 << 29)
+#define RAL_TX_CIPHER_TKIP     (3 << 29)
+#define RAL_TX_CIPHER_AES      (4 << 29)
+
+       uint32_t        physaddr;
+       uint16_t        wme;
+#define RAL_LOGCWMAX(x)                (((x) & 0xf) << 12)
+#define RAL_LOGCWMIN(x)                (((x) & 0xf) << 8)
+#define RAL_AIFSN(x)           (((x) & 0x3) << 6)
+#define RAL_IVOFFSET(x)                (((x) & 0x3f))
+
+       uint16_t        reserved1;
+       uint8_t         plcp_signal;
+       uint8_t         plcp_service;
+#define RAL_PLCP_LENGEXT       0x80
+
+       uint8_t         plcp_length_lo;
+       uint8_t         plcp_length_hi;
+       uint32_t        iv;
+       uint32_t        eiv;
+       uint8_t         key[IEEE80211_KEYBUF_SIZE];
+       uint32_t        reserved2[2];
+} __packed;
+
+/* Rx descriptor */
+struct ral_rx_desc {
+       uint32_t        flags;
+#define RAL_RX_BUSY            (1 << 0)
+#define RAL_RX_CRC_ERROR       (1 << 5)
+#define RAL_RX_OFDM            (1 << 6)
+#define RAL_RX_PHY_ERROR       (1 << 7)
+#define RAL_RX_CIPHER_BUSY     (1 << 8)
+#define RAL_RX_ICV_ERROR       (1 << 9)
+
+#define RAL_RX_CIPHER_MASK     0xe0000000
+#define RAL_RX_CIPHER_NONE     (0 << 29)
+#define RAL_RX_CIPHER_WEP40    (1 << 29)
+#define RAL_RX_CIPHER_WEP104   (2 << 29)
+#define RAL_RX_CIPHER_TKIP     (3 << 29)
+#define RAL_RX_CIPHER_AES      (4 << 29)
+
+       uint32_t        physaddr;
+       uint8_t         rate;
+       uint8_t         rssi;
+       uint8_t         ta[IEEE80211_ADDR_LEN];
+       uint32_t        iv;
+       uint32_t        eiv;
+       uint8_t         key[IEEE80211_KEYBUF_SIZE];
+       uint32_t        reserved[2];
+} __packed;
+
+#define RAL_RF1        0
+#define RAL_RF2        2
+#define RAL_RF3        1
+#define RAL_RF4        3
+
+#define RAL_RF1_AUTOTUNE       0x08000
+#define RAL_RF3_AUTOTUNE       0x00040
+
+#define RAL_BBP_BUSY   (1 << 15)
+#define RAL_BBP_WRITE  (1 << 16)
+#define RAL_RF_20BIT   (20 << 24)
+#define RAL_RF_BUSY    (1 << 31)
+
+#define RAL_RF_2522    0x00
+#define RAL_RF_2523    0x01
+#define RAL_RF_2524    0x02
+#define RAL_RF_2525    0x03
+#define RAL_RF_2525E   0x04
+#define RAL_RF_2526    0x05
+/* dual-band RF */
+#define RAL_RF_5222    0x10
+
+#define RAL_BBP_VERSION        0
+#define RAL_BBP_TX     2
+#define RAL_BBP_RX     14
+
+#define RAL_BBP_ANTA           0x00
+#define RAL_BBP_DIVERSITY      0x01
+#define RAL_BBP_ANTB           0x02
+#define RAL_BBP_ANTMASK                0x03
+#define RAL_BBP_FLIPIQ         0x04
+
+#define RAL_LED_MODE_DEFAULT           0
+#define RAL_LED_MODE_TXRX_ACTIVITY     1
+#define RAL_LED_MODE_SINGLE            2
+#define RAL_LED_MODE_ASUS              3
+
+#define RAL_JAPAN_FILTER       0x8
+
+#define RAL_EEPROM_DELAY       1       /* minimum hold time (microsecond) */
+
+#define RAL_EEPROM_CONFIG0     16
+#define RAL_EEPROM_BBP_BASE    19
+#define RAL_EEPROM_TXPOWER     35
+
+/*
+ * control and status registers access macros
+ */
+#define RAL_READ(sc, reg)                                              \
+       bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg))
+
+#define RAL_WRITE(sc, reg, val)                                                \
+       bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val))
+
+/*
+ * EEPROM access macro
+ */
+#define RAL_EEPROM_CTL(sc, val) do {                                   \
+       RAL_WRITE((sc), RAL_CSR21, (val));                              \
+       DELAY(RAL_EEPROM_DELAY);                                        \
+} while (/* CONSTCOND */0)
diff --git a/sys/dev/netif/ral/if_ralvar.h b/sys/dev/netif/ral/if_ralvar.h
new file mode 100644 (file)
index 0000000..3b1a236
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2005, 2006
+ *     Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD: src/sys/dev/ral/if_ralvar.h,v 1.2.2.1 2006/01/29 15:21:46 damien Exp $
+ * $DragonFly: src/sys/dev/netif/ral/Attic/if_ralvar.h,v 1.1 2006/05/20 09:13:09 sephe Exp $
+ */
+
+struct ral_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_antenna;
+       uint8_t         wr_antsignal;
+};
+
+#define RAL_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_DB_ANTSIGNAL))
+
+struct ral_tx_radiotap_header {
+       struct ieee80211_radiotap_header wt_ihdr;
+       uint8_t         wt_flags;
+       uint8_t         wt_rate;
+       uint16_t        wt_chan_freq;
+       uint16_t        wt_chan_flags;
+       uint8_t         wt_antenna;
+};
+
+#define RAL_TX_RADIOTAP_PRESENT                                                \
+       ((1 << IEEE80211_RADIOTAP_FLAGS) |                              \
+        (1 << IEEE80211_RADIOTAP_RATE) |                               \
+        (1 << IEEE80211_RADIOTAP_CHANNEL) |                            \
+        (1 << IEEE80211_RADIOTAP_ANTENNA))
+
+struct ral_tx_data {
+       bus_dmamap_t                    map;
+       struct mbuf                     *m;
+       struct ieee80211_node           *ni;
+       struct ral_rssdesc              id;
+};
+
+struct ral_tx_ring {
+       bus_dma_tag_t           desc_dmat;
+       bus_dma_tag_t           data_dmat;
+       bus_dmamap_t            desc_map;
+       bus_addr_t              physaddr;
+       struct ral_tx_desc      *desc;
+       struct ral_tx_data      *data;
+       int                     count;
+       int                     queued;
+       int                     cur;
+       int                     next;
+       int                     cur_encrypt;
+       int                     next_encrypt;
+};
+
+struct ral_rx_data {
+       bus_dmamap_t    map;
+       struct mbuf     *m;
+       int             drop;
+};
+
+struct ral_rx_ring {
+       bus_dma_tag_t           desc_dmat;
+       bus_dma_tag_t           data_dmat;
+       bus_dmamap_t            desc_map;
+       bus_addr_t              physaddr;
+       struct ral_rx_desc      *desc;
+       struct ral_rx_data      *data;
+       int                     count;
+       int                     cur;
+       int                     next;
+       int                     cur_decrypt;
+};
+
+struct ral_node {
+       struct ieee80211_node   ni;
+       struct ral_rssadapt     rssadapt;
+};
+
+struct ral_softc {
+       struct ifnet                    *sc_ifp;
+       struct ieee80211com             sc_ic;
+       int                             (*sc_newstate)(struct ieee80211com *,
+                                           enum ieee80211_state, int);
+       device_t                        sc_dev;
+
+       struct mtx                      sc_mtx;
+
+       struct callout                  scan_ch;
+       struct callout                  rssadapt_ch;
+
+       int                             irq_rid;
+       int                             mem_rid;
+       struct resource                 *irq;
+       struct resource                 *mem;
+       bus_space_tag_t                 sc_st;
+       bus_space_handle_t              sc_sh;
+       void                            *sc_ih;
+
+       int                             sc_tx_timer;
+
+       uint32_t                        asic_rev;
+       uint32_t                        eeprom_rev;
+       uint8_t                         rf_rev;
+
+       struct ral_tx_ring              txq;
+       struct ral_tx_ring              prioq;
+       struct ral_tx_ring              atimq;
+       struct ral_tx_ring              bcnq;
+       struct ral_rx_ring              rxq;
+
+       struct ieee80211_beacon_offsets sc_bo;
+
+       uint32_t                        rf_regs[4];
+       uint8_t                         txpow[14];
+
+       struct {
+               uint8_t         reg;
+               uint8_t         val;
+       }                               bbp_prom[16];
+
+       int                             led_mode;
+       int                             hw_radio;
+       int                             rx_ant;
+       int                             tx_ant;
+       int                             nb_ant;
+
+       int                             dwelltime;
+
+       struct bpf_if                   *sc_drvbpf;
+
+       union {
+               struct ral_rx_radiotap_header th;
+               uint8_t pad[64];
+       }                               sc_rxtapu;
+#define sc_rxtap       sc_rxtapu.th
+       int                             sc_rxtap_len;
+
+       union {
+               struct ral_tx_radiotap_header th;
+               uint8_t pad[64];
+       }                               sc_txtapu;
+#define sc_txtap       sc_txtapu.th
+       int                             sc_txtap_len;
+};
+
+extern devclass_t ral_devclass;
+
+int    ral_attach(device_t);
+int    ral_detach(device_t);
+void   ral_shutdown(device_t);
+int    ral_alloc(device_t, int);
+void   ral_free(device_t);
+void   ral_stop(void *);
+
+#define RAL_LOCK(sc)   mtx_lock(&(sc)->sc_mtx)
+#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
diff --git a/sys/dev/netif/ral/rt2560.c b/sys/dev/netif/ral/rt2560.c
new file mode 100644 (file)
index 0000000..7553cc4
--- /dev/null
@@ -0,0 +1,2785 @@
+/*
+ * Copyright (c) 2005, 2006
+ *     Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD: src/sys/dev/ral/rt2560.c,v 1.3 2006/03/21 21:15:43 damien Exp $
+ * $DragonFly: src/sys/dev/netif/ral/rt2560.c,v 1.1 2006/05/20 09:13:09 sephe Exp $
+ */
+
+/*
+ * Ralink Technology RT2560 chipset driver
+ * http://www.ralinktech.com/
+ */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/serialize.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/clock.h>
+#include <sys/rman.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/ifq_var.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+#include <netproto/802_11/ieee80211_radiotap.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+
+#include <dev/netif/ral/if_ralrate.h>
+#include <dev/netif/ral/rt2560reg.h>
+#include <dev/netif/ral/rt2560var.h>
+
+#ifdef RAL_DEBUG
+#define DPRINTF(x)     do { if (ral_debug > 0) printf x; } while (0)
+#define DPRINTFN(n, x) do { if (ral_debug >= (n)) printf x; } while (0)
+extern int ral_debug;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n, x)
+#endif
+
+static void            rt2560_dma_map_addr(void *, bus_dma_segment_t *, int,
+                           int);
+static void            rt2560_dma_map_mbuf(void *, bus_dma_segment_t *, int,
+                                           bus_size_t, int);
+static int             rt2560_alloc_tx_ring(struct rt2560_softc *,
+                           struct rt2560_tx_ring *, int);
+static void            rt2560_reset_tx_ring(struct rt2560_softc *,
+                           struct rt2560_tx_ring *);
+static void            rt2560_free_tx_ring(struct rt2560_softc *,
+                           struct rt2560_tx_ring *);
+static int             rt2560_alloc_rx_ring(struct rt2560_softc *,
+                           struct rt2560_rx_ring *, int);
+static void            rt2560_reset_rx_ring(struct rt2560_softc *,
+                           struct rt2560_rx_ring *);
+static void            rt2560_free_rx_ring(struct rt2560_softc *,
+                           struct rt2560_rx_ring *);
+static struct          ieee80211_node *rt2560_node_alloc(
+                           struct ieee80211_node_table *);
+static int             rt2560_media_change(struct ifnet *);
+static void            rt2560_next_scan(void *);
+static void            rt2560_iter_func(void *, struct ieee80211_node *);
+static void            rt2560_update_rssadapt(void *);
+static int             rt2560_newstate(struct ieee80211com *,
+                           enum ieee80211_state, int);
+static uint16_t                rt2560_eeprom_read(struct rt2560_softc *, uint8_t);
+static void            rt2560_encryption_intr(struct rt2560_softc *);
+static void            rt2560_tx_intr(struct rt2560_softc *);
+static void            rt2560_prio_intr(struct rt2560_softc *);
+static void            rt2560_decryption_intr(struct rt2560_softc *);
+static void            rt2560_rx_intr(struct rt2560_softc *);
+static void            rt2560_beacon_expire(struct rt2560_softc *);
+static void            rt2560_wakeup_expire(struct rt2560_softc *);
+static uint8_t         rt2560_rxrate(struct rt2560_rx_desc *);
+static int             rt2560_ack_rate(struct ieee80211com *, int);
+static uint16_t                rt2560_txtime(int, int, uint32_t);
+static uint8_t         rt2560_plcp_signal(int);
+static void            rt2560_setup_tx_desc(struct rt2560_softc *,
+                           struct rt2560_tx_desc *, uint32_t, int, int, int,
+                           bus_addr_t);
+static int             rt2560_tx_bcn(struct rt2560_softc *, struct mbuf *,
+                           struct ieee80211_node *);
+static int             rt2560_tx_mgt(struct rt2560_softc *, struct mbuf *,
+                           struct ieee80211_node *);
+static struct          mbuf *rt2560_get_rts(struct rt2560_softc *,
+                           struct ieee80211_frame *, uint16_t);
+static int             rt2560_tx_data(struct rt2560_softc *, struct mbuf *,
+                           struct ieee80211_node *);
+static void            rt2560_start(struct ifnet *);
+static void            rt2560_watchdog(struct ifnet *);
+static int             rt2560_reset(struct ifnet *);
+static int             rt2560_ioctl(struct ifnet *, u_long, caddr_t,
+                                    struct ucred *);
+static void            rt2560_bbp_write(struct rt2560_softc *, uint8_t,
+                           uint8_t);
+static uint8_t         rt2560_bbp_read(struct rt2560_softc *, uint8_t);
+static void            rt2560_rf_write(struct rt2560_softc *, uint8_t,
+                           uint32_t);
+static void            rt2560_set_chan(struct rt2560_softc *,
+                           struct ieee80211_channel *);
+#if 0
+static void            rt2560_disable_rf_tune(struct rt2560_softc *);
+#endif
+static void            rt2560_enable_tsf_sync(struct rt2560_softc *);
+static void            rt2560_update_plcp(struct rt2560_softc *);
+static void            rt2560_update_slot(struct ifnet *);
+static void            rt2560_set_basicrates(struct rt2560_softc *);
+static void            rt2560_update_led(struct rt2560_softc *, int, int);
+static void            rt2560_set_bssid(struct rt2560_softc *, uint8_t *);
+static void            rt2560_set_macaddr(struct rt2560_softc *, uint8_t *);
+static void            rt2560_get_macaddr(struct rt2560_softc *, uint8_t *);
+static void            rt2560_update_promisc(struct rt2560_softc *);
+static const char      *rt2560_get_rf(int);
+static void            rt2560_read_eeprom(struct rt2560_softc *);
+static int             rt2560_bbp_init(struct rt2560_softc *);
+static void            rt2560_set_txantenna(struct rt2560_softc *, int);
+static void            rt2560_set_rxantenna(struct rt2560_softc *, int);
+static void            rt2560_init(void *);
+static void            rt2560_stop(void *);
+static void            rt2560_intr(void *);
+
+/*
+ * Supported rates for 802.11a/b/g modes (in 500Kbps unit).
+ */
+static const struct ieee80211_rateset rt2560_rateset_11a =
+       { 8, { 12, 18, 24, 36, 48, 72, 96, 108 } };
+
+static const struct ieee80211_rateset rt2560_rateset_11b =
+       { 4, { 2, 4, 11, 22 } };
+
+static const struct ieee80211_rateset rt2560_rateset_11g =
+       { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
+
+static const struct {
+       uint32_t        reg;
+       uint32_t        val;
+} rt2560_def_mac[] = {
+       RT2560_DEF_MAC
+};
+
+static const struct {
+       uint8_t reg;
+       uint8_t val;
+} rt2560_def_bbp[] = {
+       RT2560_DEF_BBP
+};
+
+static const uint32_t rt2560_rf2522_r2[]    = RT2560_RF2522_R2;
+static const uint32_t rt2560_rf2523_r2[]    = RT2560_RF2523_R2;
+static const uint32_t rt2560_rf2524_r2[]    = RT2560_RF2524_R2;
+static const uint32_t rt2560_rf2525_r2[]    = RT2560_RF2525_R2;
+static const uint32_t rt2560_rf2525_hi_r2[] = RT2560_RF2525_HI_R2;
+static const uint32_t rt2560_rf2525e_r2[]   = RT2560_RF2525E_R2;
+static const uint32_t rt2560_rf2526_r2[]    = RT2560_RF2526_R2;
+static const uint32_t rt2560_rf2526_hi_r2[] = RT2560_RF2526_HI_R2;
+
+static const struct {
+       uint8_t         chan;
+       uint32_t        r1, r2, r4;
+} rt2560_rf5222[] = {
+       RT2560_RF5222
+};
+
+int
+rt2560_attach(device_t dev, int id)
+{
+       struct rt2560_softc *sc = device_get_softc(dev);
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+       int error, i;
+
+       callout_init(&sc->scan_ch);
+       callout_init(&sc->rssadapt_ch);
+
+       sc->sc_irq_rid = 0;
+       sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irq_rid,
+                                           RF_ACTIVE | RF_SHAREABLE);
+       if (sc->sc_irq == NULL) {
+               device_printf(dev, "could not allocate interrupt resource\n");
+               return ENXIO;
+       }
+
+       /* retrieve RT2560 rev. no */
+       sc->asic_rev = RAL_READ(sc, RT2560_CSR0);
+
+       /* retrieve MAC address */
+       rt2560_get_macaddr(sc, ic->ic_myaddr);
+
+       /* retrieve RF rev. no and various other things from EEPROM */
+       rt2560_read_eeprom(sc);
+
+       device_printf(dev, "MAC/BBP RT2560 (rev 0x%02x), RF %s\n",
+           sc->asic_rev, rt2560_get_rf(sc->rf_rev));
+
+       /*
+        * Allocate Tx and Rx rings.
+        */
+       error = rt2560_alloc_tx_ring(sc, &sc->txq, RT2560_TX_RING_COUNT);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not allocate Tx ring\n");
+               goto fail;
+       }
+
+       error = rt2560_alloc_tx_ring(sc, &sc->atimq, RT2560_ATIM_RING_COUNT);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not allocate ATIM ring\n");
+               goto fail;
+       }
+
+       error = rt2560_alloc_tx_ring(sc, &sc->prioq, RT2560_PRIO_RING_COUNT);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not allocate Prio ring\n");
+               goto fail;
+       }
+
+       error = rt2560_alloc_tx_ring(sc, &sc->bcnq, RT2560_BEACON_RING_COUNT);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not allocate Beacon ring\n");
+               goto fail;
+       }
+
+       error = rt2560_alloc_rx_ring(sc, &sc->rxq, RT2560_RX_RING_COUNT);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not allocate Rx ring\n");
+               goto fail;
+       }
+
+       sysctl_ctx_init(&sc->sysctl_ctx);
+       sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+                                         SYSCTL_STATIC_CHILDREN(_hw),
+                                         OID_AUTO,
+                                         device_get_nameunit(dev),
+                                         CTLFLAG_RD, 0, "");
+       if (sc->sysctl_tree == NULL) {
+               device_printf(dev, "could not add sysctl node\n");
+               error = ENXIO;
+               goto fail;
+       }
+
+       ifp->if_softc = sc;
+       if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+       ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+       ifp->if_init = rt2560_init;
+       ifp->if_ioctl = rt2560_ioctl;
+       ifp->if_start = rt2560_start;
+       ifp->if_watchdog = rt2560_watchdog;
+       ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN);
+       ifq_set_ready(&ifp->if_snd);
+
+       ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+       ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
+       ic->ic_state = IEEE80211_S_INIT;
+
+       /* set device capabilities */
+       ic->ic_caps =
+           IEEE80211_C_IBSS |          /* IBSS mode supported */
+           IEEE80211_C_MONITOR |       /* monitor mode supported */
+           IEEE80211_C_HOSTAP |        /* HostAp mode supported */
+           IEEE80211_C_TXPMGT |        /* tx power management */
+           IEEE80211_C_SHPREAMBLE |    /* short preamble supported */
+           IEEE80211_C_SHSLOT |        /* short slot time supported */
+           IEEE80211_C_WEP |           /* WEP */
+           IEEE80211_C_WPA;            /* 802.11i */
+
+       if (sc->rf_rev == RT2560_RF_5222) {
+               /* set supported .11a rates */
+               ic->ic_sup_rates[IEEE80211_MODE_11A] = rt2560_rateset_11a;
+
+               /* set supported .11a channels */
+               for (i = 36; i <= 64; i += 4) {
+                       ic->ic_channels[i].ic_freq =
+                           ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
+                       ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
+               }
+               for (i = 100; i <= 140; i += 4) {
+                       ic->ic_channels[i].ic_freq =
+                           ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
+                       ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
+               }
+               for (i = 149; i <= 161; i += 4) {
+                       ic->ic_channels[i].ic_freq =
+                           ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
+                       ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
+               }
+       }
+
+       /* set supported .11b and .11g rates */
+       ic->ic_sup_rates[IEEE80211_MODE_11B] = rt2560_rateset_11b;
+       ic->ic_sup_rates[IEEE80211_MODE_11G] = rt2560_rateset_11g;
+
+       /* set supported .11b and .11g channels (1 through 14) */
+       for (i = 1; i <= 14; i++) {
+               ic->ic_channels[i].ic_freq =
+                   ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
+               ic->ic_channels[i].ic_flags =
+                   IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
+                   IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
+       }
+
+       ieee80211_ifattach(ic);
+       ic->ic_node_alloc = rt2560_node_alloc;
+       ic->ic_updateslot = rt2560_update_slot;
+       ic->ic_reset = rt2560_reset;
+       /* enable s/w bmiss handling in sta mode */
+       ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;
+
+       /* override state transition machine */
+       sc->sc_newstate = ic->ic_newstate;
+       ic->ic_newstate = rt2560_newstate;
+       ieee80211_media_init(ic, rt2560_media_change, ieee80211_media_status);
+
+       bpfattach_dlt(ifp, DLT_IEEE802_11_RADIO,
+           sizeof (struct ieee80211_frame) + 64, &sc->sc_drvbpf);
+
+       sc->sc_rxtap_len = sizeof sc->sc_rxtapu;
+       sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
+       sc->sc_rxtap.wr_ihdr.it_present = htole32(RT2560_RX_RADIOTAP_PRESENT);
+
+       sc->sc_txtap_len = sizeof sc->sc_txtapu;
+       sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
+       sc->sc_txtap.wt_ihdr.it_present = htole32(RT2560_TX_RADIOTAP_PRESENT);
+
+       /*
+        * Add a few sysctl knobs.
+        */
+       sc->dwelltime = 200;
+
+       SYSCTL_ADD_INT(&sc->sysctl_ctx,
+           SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+           "txantenna", CTLFLAG_RW, &sc->tx_ant, 0, "tx antenna (0=auto)");
+
+       SYSCTL_ADD_INT(&sc->sysctl_ctx,
+           SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
+           "rxantenna", CTLFLAG_RW, &sc->rx_ant, 0, "rx antenna (0=auto)");
+
+       SYSCTL_ADD_INT(&sc->sysctl_ctx,
+           SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "dwell",
+           CTLFLAG_RW, &sc->dwelltime, 0,
+           "channel dwell time (ms) for AP/station scanning");
+
+       error = bus_setup_intr(dev, sc->sc_irq, INTR_MPSAFE, rt2560_intr,
+                              sc, &sc->sc_ih, ifp->if_serializer);
+       if (error != 0) {
+               device_printf(dev, "could not set up interrupt\n");
+               bpfdetach(ifp);
+               ieee80211_ifdetach(ic);
+               goto fail;
+       }
+
+       if (bootverbose)
+               ieee80211_announce(ic);
+       return 0;
+fail:
+       rt2560_detach(sc);
+       return error;
+}
+
+int
+rt2560_detach(void *xsc)
+{
+       struct rt2560_softc *sc = xsc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = ic->ic_ifp;
+
+       if (device_is_attached(sc->sc_dev)) {
+               lwkt_serialize_enter(ifp->if_serializer);
+
+               callout_stop(&sc->scan_ch);
+               callout_stop(&sc->rssadapt_ch);
+
+               rt2560_stop(sc);
+               bus_teardown_intr(sc->sc_dev, sc->sc_irq, sc->sc_ih);
+
+               lwkt_serialize_exit(ifp->if_serializer);
+
+               bpfdetach(ifp);
+               ieee80211_ifdetach(ic);
+       }
+
+       rt2560_free_tx_ring(sc, &sc->txq);
+       rt2560_free_tx_ring(sc, &sc->atimq);
+       rt2560_free_tx_ring(sc, &sc->prioq);
+       rt2560_free_tx_ring(sc, &sc->bcnq);
+       rt2560_free_rx_ring(sc, &sc->rxq);
+
+       if (sc->sc_irq != NULL) {
+               bus_release_resource(sc->sc_dev, SYS_RES_IRQ, sc->sc_irq_rid,
+                                    sc->sc_irq);
+       }
+
+       if (sc->sysctl_tree != NULL)
+               sysctl_ctx_free(&sc->sysctl_ctx);
+
+       return 0;
+}
+
+void
+rt2560_shutdown(void *xsc)
+{
+       struct rt2560_softc *sc = xsc;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+       rt2560_stop(sc);
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+void
+rt2560_suspend(void *xsc)
+{
+       struct rt2560_softc *sc = xsc;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+       rt2560_stop(sc);
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+void
+rt2560_resume(void *xsc)
+{
+       struct rt2560_softc *sc = xsc;
+       struct ifnet *ifp = sc->sc_ic.ic_ifp;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+       if (ifp->if_flags & IFF_UP) {
+               ifp->if_init(ifp->if_softc);
+               if (ifp->if_flags & IFF_RUNNING)
+                       ifp->if_start(ifp);
+       }
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+static void
+rt2560_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+       if (error != 0)
+               return;
+
+       KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg));
+
+       *(bus_addr_t *)arg = segs[0].ds_addr;
+}
+
+static int
+rt2560_alloc_tx_ring(struct rt2560_softc *sc, struct rt2560_tx_ring *ring,
+    int count)
+{
+       int i, error;
+
+       ring->count = count;
+       ring->queued = 0;
+       ring->cur = ring->next = 0;
+       ring->cur_encrypt = ring->next_encrypt = 0;
+
+       error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT,
+           BUS_SPACE_MAXADDR, NULL, NULL, count * RT2560_TX_DESC_SIZE, 1,
+           count * RT2560_TX_DESC_SIZE, 0, &ring->desc_dmat);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not create desc DMA tag\n");
+               goto fail;
+       }
+
+       error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc,
+           BUS_DMA_WAITOK | BUS_DMA_ZERO, &ring->desc_map);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not allocate DMA memory\n");
+               goto fail;
+       }
+
+       error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc,
+                               count * RT2560_TX_DESC_SIZE,
+                               rt2560_dma_map_addr, &ring->physaddr, 0);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not load desc DMA map\n");
+
+               bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map);
+               ring->desc = NULL;
+               goto fail;
+       }
+
+       ring->data = malloc(count * sizeof (struct rt2560_tx_data), M_DEVBUF,
+           M_WAITOK | M_ZERO);
+
+       error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT,
+           BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, RT2560_MAX_SCATTER,
+           MCLBYTES, 0, &ring->data_dmat);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not create data DMA tag\n");
+               goto fail;
+       }
+
+       for (i = 0; i < count; i++) {
+               error = bus_dmamap_create(ring->data_dmat, 0,
+                   &ring->data[i].map);
+               if (error != 0) {
+                       device_printf(sc->sc_dev, "could not create DMA map\n");
+                       goto fail;
+               }
+       }
+       return 0;
+
+fail:  rt2560_free_tx_ring(sc, ring);
+       return error;
+}
+
+static void
+rt2560_reset_tx_ring(struct rt2560_softc *sc, struct rt2560_tx_ring *ring)
+{
+       struct rt2560_tx_desc *desc;
+       struct rt2560_tx_data *data;
+       int i;
+
+       for (i = 0; i < ring->count; i++) {
+               desc = &ring->desc[i];
+               data = &ring->data[i];
+
+               if (data->m != NULL) {
+                       bus_dmamap_sync(ring->data_dmat, data->map,
+                           BUS_DMASYNC_POSTWRITE);
+                       bus_dmamap_unload(ring->data_dmat, data->map);
+                       m_freem(data->m);
+                       data->m = NULL;
+               }
+
+               if (data->ni != NULL) {
+                       ieee80211_free_node(data->ni);
+                       data->ni = NULL;
+               }
+
+               desc->flags = 0;
+       }
+
+       bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE);
+
+       ring->queued = 0;
+       ring->cur = ring->next = 0;
+       ring->cur_encrypt = ring->next_encrypt = 0;
+}
+
+static void
+rt2560_free_tx_ring(struct rt2560_softc *sc, struct rt2560_tx_ring *ring)
+{
+       struct rt2560_tx_data *data;
+       int i;
+
+       if (ring->desc != NULL) {
+               bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
+                   BUS_DMASYNC_POSTWRITE);
+               bus_dmamap_unload(ring->desc_dmat, ring->desc_map);
+               bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map);
+               ring->desc = NULL;
+       }
+
+       if (ring->desc_dmat != NULL) {
+               bus_dma_tag_destroy(ring->desc_dmat);
+               ring->desc_dmat = NULL;
+       }
+
+       if (ring->data != NULL) {
+               for (i = 0; i < ring->count; i++) {
+                       data = &ring->data[i];
+
+                       if (data->m != NULL) {
+                               bus_dmamap_sync(ring->data_dmat, data->map,
+                                   BUS_DMASYNC_POSTWRITE);
+                               bus_dmamap_unload(ring->data_dmat, data->map);
+                               m_freem(data->m);
+                               data->m = NULL;
+                       }
+
+                       if (data->ni != NULL) {
+                               ieee80211_free_node(data->ni);
+                               data->ni = NULL;
+                       }
+
+                       if (data->map != NULL) {
+                               bus_dmamap_destroy(ring->data_dmat, data->map);
+                               data->map = NULL;
+                       }
+               }
+
+               free(ring->data, M_DEVBUF);
+               ring->data = NULL;
+       }
+
+       if (ring->data_dmat != NULL) {
+               bus_dma_tag_destroy(ring->data_dmat);
+               ring->data_dmat = NULL;
+       }
+}
+
+static int
+rt2560_alloc_rx_ring(struct rt2560_softc *sc, struct rt2560_rx_ring *ring,
+    int count)
+{
+       struct rt2560_rx_desc *desc;
+       struct rt2560_rx_data *data;
+       bus_addr_t physaddr;
+       int i, error;
+
+       ring->count = count;
+       ring->cur = ring->next = 0;
+       ring->cur_decrypt = 0;
+
+       error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT,
+           BUS_SPACE_MAXADDR, NULL, NULL, count * RT2560_RX_DESC_SIZE, 1,
+           count * RT2560_RX_DESC_SIZE, 0, &ring->desc_dmat);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not create desc DMA tag\n");
+               goto fail;
+       }
+
+       error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc,
+           BUS_DMA_WAITOK | BUS_DMA_ZERO, &ring->desc_map);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not allocate DMA memory\n");
+               goto fail;
+       }
+
+       error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc,
+                               count * RT2560_RX_DESC_SIZE,
+                               rt2560_dma_map_addr, &ring->physaddr, 0);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not load desc DMA map\n");
+
+               bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map);
+               ring->desc = NULL;
+               goto fail;
+       }
+
+       ring->data = malloc(count * sizeof (struct rt2560_rx_data), M_DEVBUF,
+           M_WAITOK | M_ZERO);
+
+       /*
+        * Pre-allocate Rx buffers and populate Rx ring.
+        */
+       error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT,
+           BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0,
+           &ring->data_dmat);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not create data DMA tag\n");
+               goto fail;
+       }
+
+       for (i = 0; i < count; i++) {
+               desc = &sc->rxq.desc[i];
+               data = &sc->rxq.data[i];
+
+               error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
+               if (error != 0) {
+                       device_printf(sc->sc_dev, "could not create DMA map\n");
+                       goto fail;
+               }
+
+               data->m = m_getcl(MB_WAIT, MT_DATA, M_PKTHDR);
+               if (data->m == NULL) {
+                       device_printf(sc->sc_dev,
+                           "could not allocate rx mbuf\n");
+                       error = ENOMEM;
+                       goto fail;
+               }
+
+               error = bus_dmamap_load(ring->data_dmat, data->map,
+                   mtod(data->m, void *), MCLBYTES, rt2560_dma_map_addr,
+                   &physaddr, 0);
+               if (error != 0) {
+                       device_printf(sc->sc_dev,
+                           "could not load rx buf DMA map");
+
+                       m_freem(data->m);
+                       data->m = NULL;
+                       goto fail;
+               }
+
+               desc->flags = htole32(RT2560_RX_BUSY);
+               desc->physaddr = htole32(physaddr);
+       }
+
+       bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE);
+
+       return 0;
+
+fail:  rt2560_free_rx_ring(sc, ring);
+       return error;
+}
+
+static void
+rt2560_reset_rx_ring(struct rt2560_softc *sc, struct rt2560_rx_ring *ring)
+{
+       int i;
+
+       for (i = 0; i < ring->count; i++) {
+               ring->desc[i].flags = htole32(RT2560_RX_BUSY);
+               ring->data[i].drop = 0;
+       }
+
+       bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE);
+
+       ring->cur = ring->next = 0;
+       ring->cur_decrypt = 0;
+}
+
+static void
+rt2560_free_rx_ring(struct rt2560_softc *sc, struct rt2560_rx_ring *ring)
+{
+       struct rt2560_rx_data *data;
+
+       if (ring->desc != NULL) {
+               bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
+                   BUS_DMASYNC_POSTWRITE);
+               bus_dmamap_unload(ring->desc_dmat, ring->desc_map);
+               bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map);
+               ring->desc = NULL;
+       }
+
+       if (ring->desc_dmat != NULL) {
+               bus_dma_tag_destroy(ring->desc_dmat);
+               ring->desc_dmat = NULL;
+       }
+
+       if (ring->data != NULL) {
+               int i;
+
+               for (i = 0; i < ring->count; i++) {
+                       data = &ring->data[i];
+
+                       if (data->m != NULL) {
+                               bus_dmamap_sync(ring->data_dmat, data->map,
+                                   BUS_DMASYNC_POSTREAD);
+                               bus_dmamap_unload(ring->data_dmat, data->map);
+                               m_freem(data->m);
+                               data->m = NULL;
+                       }
+
+                       if (data->map != NULL) {
+                               bus_dmamap_destroy(ring->data_dmat, data->map);
+                               data->map = NULL;
+                       }
+               }
+
+               free(ring->data, M_DEVBUF);
+               ring->data = NULL;
+       }
+
+       if (ring->data_dmat != NULL) {
+               bus_dma_tag_destroy(ring->data_dmat);
+               ring->data_dmat = NULL;
+       }
+}
+
+static struct ieee80211_node *
+rt2560_node_alloc(struct ieee80211_node_table *nt)
+{
+       struct rt2560_node *rn;
+
+       rn = malloc(sizeof(struct rt2560_node), M_80211_NODE,
+           M_NOWAIT | M_ZERO);
+
+       return (rn != NULL) ? &rn->ni : NULL;
+}
+
+static int
+rt2560_media_change(struct ifnet *ifp)
+{
+       struct rt2560_softc *sc = ifp->if_softc;
+       int error;
+
+       error = ieee80211_media_change(ifp);
+       if (error != ENETRESET)
+               return error;
+
+       if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING))
+               rt2560_init(sc);
+       return 0;
+}
+
+/*
+ * This function is called periodically (every 200ms) during scanning to
+ * switch from one channel to another.
+ */
+static void
+rt2560_next_scan(void *arg)
+{
+       struct rt2560_softc *sc = arg;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = ic->ic_ifp;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+       if (ic->ic_state == IEEE80211_S_SCAN)
+               ieee80211_next_scan(ic);
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+/*
+ * This function is called for each node present in the node station table.
+ */
+static void
+rt2560_iter_func(void *arg, struct ieee80211_node *ni)
+{
+       struct rt2560_node *rn = (struct rt2560_node *)ni;
+
+       ral_rssadapt_updatestats(&rn->rssadapt);
+}
+
+/*
+ * This function is called periodically (every 100ms) in RUN state to update
+ * the rate adaptation statistics.
+ */
+static void
+rt2560_update_rssadapt(void *arg)
+{
+       struct rt2560_softc *sc = arg;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = ic->ic_ifp;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       ieee80211_iterate_nodes(&ic->ic_sta, rt2560_iter_func, arg);
+       callout_reset(&sc->rssadapt_ch, hz / 10, rt2560_update_rssadapt, sc);
+
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+static int
+rt2560_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+{
+       struct rt2560_softc *sc = ic->ic_ifp->if_softc;
+       enum ieee80211_state ostate;
+       struct ieee80211_node *ni;
+       struct mbuf *m;
+       int error = 0;
+
+       ostate = ic->ic_state;
+       callout_stop(&sc->scan_ch);
+
+       switch (nstate) {
+       case IEEE80211_S_INIT:
+               callout_stop(&sc->rssadapt_ch);
+
+               if (ostate == IEEE80211_S_RUN) {
+                       /* abort TSF synchronization */
+                       RAL_WRITE(sc, RT2560_CSR14, 0);
+
+                       /* turn association led off */
+                       rt2560_update_led(sc, 0, 0);
+               }
+               break;
+
+       case IEEE80211_S_SCAN:
+               rt2560_set_chan(sc, ic->ic_curchan);
+               callout_reset(&sc->scan_ch, (sc->dwelltime * hz) / 1000,
+                   rt2560_next_scan, sc);
+               break;
+
+       case IEEE80211_S_AUTH:
+               rt2560_set_chan(sc, ic->ic_curchan);
+               break;
+
+       case IEEE80211_S_ASSOC:
+               rt2560_set_chan(sc, ic->ic_curchan);
+               break;
+
+       case IEEE80211_S_RUN:
+               rt2560_set_chan(sc, ic->ic_curchan);
+
+               ni = ic->ic_bss;
+
+               if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+                       rt2560_update_plcp(sc);
+                       rt2560_set_basicrates(sc);
+                       rt2560_set_bssid(sc, ni->ni_bssid);
+               }
+
+               if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+                   ic->ic_opmode == IEEE80211_M_IBSS) {
+                       m = ieee80211_beacon_alloc(ic, ni, &sc->sc_bo);
+                       if (m == NULL) {
+                               device_printf(sc->sc_dev,
+                                   "could not allocate beacon\n");
+                               error = ENOBUFS;
+                               break;
+                       }
+
+                       ieee80211_ref_node(ni);
+                       error = rt2560_tx_bcn(sc, m, ni);
+                       if (error != 0)
+                               break;
+               }
+
+               /* turn assocation led on */
+               rt2560_update_led(sc, 1, 0);
+
+               if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+                       callout_reset(&sc->rssadapt_ch, hz / 10,
+                           rt2560_update_rssadapt, sc);
+
+                       rt2560_enable_tsf_sync(sc);
+               }
+               break;
+       }
+
+       return (error != 0) ? error : sc->sc_newstate(ic, nstate, arg);
+}
+
+/*
+ * Read 16 bits at address 'addr' from the serial EEPROM (either 93C46 or
+ * 93C66).
+ */
+static uint16_t
+rt2560_eeprom_read(struct rt2560_softc *sc, uint8_t addr)
+{
+       uint32_t tmp;
+       uint16_t val;
+       int n;
+
+       /* clock C once before the first command */
+       RT2560_EEPROM_CTL(sc, 0);
+
+       RT2560_EEPROM_CTL(sc, RT2560_S);
+       RT2560_EEPROM_CTL(sc, RT2560_S | RT2560_C);
+       RT2560_EEPROM_CTL(sc, RT2560_S);
+
+       /* write start bit (1) */
+       RT2560_EEPROM_CTL(sc, RT2560_S | RT2560_D);
+       RT2560_EEPROM_CTL(sc, RT2560_S | RT2560_D | RT2560_C);
+
+       /* write READ opcode (10) */
+       RT2560_EEPROM_CTL(sc, RT2560_S | RT2560_D);
+       RT2560_EEPROM_CTL(sc, RT2560_S | RT2560_D | RT2560_C);
+       RT2560_EEPROM_CTL(sc, RT2560_S);
+       RT2560_EEPROM_CTL(sc, RT2560_S | RT2560_C);
+
+       /* write address (A5-A0 or A7-A0) */
+       n = (RAL_READ(sc, RT2560_CSR21) & RT2560_93C46) ? 5 : 7;
+       for (; n >= 0; n--) {
+               RT2560_EEPROM_CTL(sc, RT2560_S |
+                   (((addr >> n) & 1) << RT2560_SHIFT_D));
+               RT2560_EEPROM_CTL(sc, RT2560_S |
+                   (((addr >> n) & 1) << RT2560_SHIFT_D) | RT2560_C);
+       }
+
+       RT2560_EEPROM_CTL(sc, RT2560_S);
+
+       /* read data Q15-Q0 */
+       val = 0;
+       for (n = 15; n >= 0; n--) {
+               RT2560_EEPROM_CTL(sc, RT2560_S | RT2560_C);
+               tmp = RAL_READ(sc, RT2560_CSR21);
+               val |= ((tmp & RT2560_Q) >> RT2560_SHIFT_Q) << n;
+               RT2560_EEPROM_CTL(sc, RT2560_S);
+       }
+
+       RT2560_EEPROM_CTL(sc, 0);
+
+       /* clear Chip Select and clock C */
+       RT2560_EEPROM_CTL(sc, RT2560_S);
+       RT2560_EEPROM_CTL(sc, 0);
+       RT2560_EEPROM_CTL(sc, RT2560_C);
+
+       return val;
+}
+
+/*
+ * Some frames were processed by the hardware cipher engine and are ready for
+ * transmission.
+ */
+static void
+rt2560_encryption_intr(struct rt2560_softc *sc)
+{
+       struct rt2560_tx_desc *desc;
+       int hw;
+
+       /* retrieve last descriptor index processed by cipher engine */
+       hw = RAL_READ(sc, RT2560_SECCSR1) - sc->txq.physaddr;
+       hw /= RT2560_TX_DESC_SIZE;
+
+       bus_dmamap_sync(sc->txq.desc_dmat, sc->txq.desc_map,
+           BUS_DMASYNC_POSTREAD);
+
+       for (; sc->txq.next_encrypt != hw;) {
+               desc = &sc->txq.desc[sc->txq.next_encrypt];
+
+               if ((le32toh(desc->flags) & RT2560_TX_BUSY) ||
+                   (le32toh(desc->flags) & RT2560_TX_CIPHER_BUSY))
+                       break;
+
+               /* for TKIP, swap eiv field to fix a bug in ASIC */
+               if ((le32toh(desc->flags) & RT2560_TX_CIPHER_MASK) ==
+                   RT2560_TX_CIPHER_TKIP)
+                       desc->eiv = bswap32(desc->eiv);
+
+               /* mark the frame ready for transmission */
+               desc->flags |= htole32(RT2560_TX_BUSY | RT2560_TX_VALID);
+
+               DPRINTFN(15, ("encryption done idx=%u\n",
+                   sc->txq.next_encrypt));
+
+               sc->txq.next_encrypt =
+                   (sc->txq.next_encrypt + 1) % RT2560_TX_RING_COUNT;
+       }
+
+       bus_dmamap_sync(sc->txq.desc_dmat, sc->txq.desc_map,
+           BUS_DMASYNC_PREWRITE);
+
+       /* kick Tx */
+       RAL_WRITE(sc, RT2560_TXCSR0, RT2560_KICK_TX);
+}
+
+static void
+rt2560_tx_intr(struct rt2560_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = ic->ic_ifp;
+       struct rt2560_tx_desc *desc;
+       struct rt2560_tx_data *data;
+       struct rt2560_node *rn;
+
+       bus_dmamap_sync(sc->txq.desc_dmat, sc->txq.desc_map,
+           BUS_DMASYNC_POSTREAD);
+
+       for (;;) {
+               desc = &sc->txq.desc[sc->txq.next];
+               data = &sc->txq.data[sc->txq.next];
+
+               if ((le32toh(desc->flags) & RT2560_TX_BUSY) ||
+                   (le32toh(desc->flags) & RT2560_TX_CIPHER_BUSY) ||
+                   !(le32toh(desc->flags) & RT2560_TX_VALID))
+                       break;
+
+               rn = (struct rt2560_node *)data->ni;
+
+               switch (le32toh(desc->flags) & RT2560_TX_RESULT_MASK) {
+               case RT2560_TX_SUCCESS:
+                       DPRINTFN(10, ("data frame sent successfully\n"));
+                       if (data->id.id_node != NULL) {
+                               ral_rssadapt_raise_rate(ic, &rn->rssadapt,
+                                   &data->id);
+                       }
+                       ifp->if_opackets++;
+                       break;
+
+               case RT2560_TX_SUCCESS_RETRY:
+                       DPRINTFN(9, ("data frame sent after %u retries\n",
+                           (le32toh(desc->flags) >> 5) & 0x7));
+                       ifp->if_opackets++;
+                       break;
+
+               case RT2560_TX_FAIL_RETRY:
+                       DPRINTFN(9, ("sending data frame failed (too much "
+                           "retries)\n"));
+                       if (data->id.id_node != NULL) {
+                               ral_rssadapt_lower_rate(ic, data->ni,
+                                   &rn->rssadapt, &data->id);
+                       }
+                       ifp->if_oerrors++;
+                       break;
+
+               case RT2560_TX_FAIL_INVALID:
+               case RT2560_TX_FAIL_OTHER:
+               default:
+                       device_printf(sc->sc_dev, "sending data frame failed "
+                           "0x%08x\n", le32toh(desc->flags));
+                       ifp->if_oerrors++;
+               }
+
+               bus_dmamap_sync(sc->txq.data_dmat, data->map,
+                   BUS_DMASYNC_POSTWRITE);
+               bus_dmamap_unload(sc->txq.data_dmat, data->map);
+               m_freem(data->m);
+               data->m = NULL;
+               ieee80211_free_node(data->ni);
+               data->ni = NULL;
+
+               /* descriptor is no longer valid */
+               desc->flags &= ~htole32(RT2560_TX_VALID);
+
+               DPRINTFN(15, ("tx done idx=%u\n", sc->txq.next));
+
+               sc->txq.queued--;
+               sc->txq.next = (sc->txq.next + 1) % RT2560_TX_RING_COUNT;
+       }
+
+       bus_dmamap_sync(sc->txq.desc_dmat, sc->txq.desc_map,
+           BUS_DMASYNC_PREWRITE);
+
+       sc->sc_tx_timer = 0;
+       ifp->if_flags &= ~IFF_OACTIVE;
+       rt2560_start(ifp);
+}
+
+static void
+rt2560_prio_intr(struct rt2560_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = ic->ic_ifp;
+       struct rt2560_tx_desc *desc;
+       struct rt2560_tx_data *data;
+
+       bus_dmamap_sync(sc->prioq.desc_dmat, sc->prioq.desc_map,
+           BUS_DMASYNC_POSTREAD);
+
+       for (;;) {
+               desc = &sc->prioq.desc[sc->prioq.next];
+               data = &sc->prioq.data[sc->prioq.next];
+
+               if ((le32toh(desc->flags) & RT2560_TX_BUSY) ||
+                   !(le32toh(desc->flags) & RT2560_TX_VALID))
+                       break;
+
+               switch (le32toh(desc->flags) & RT2560_TX_RESULT_MASK) {
+               case RT2560_TX_SUCCESS:
+                       DPRINTFN(10, ("mgt frame sent successfully\n"));
+                       break;
+
+               case RT2560_TX_SUCCESS_RETRY:
+                       DPRINTFN(9, ("mgt frame sent after %u retries\n",
+                           (le32toh(desc->flags) >> 5) & 0x7));
+                       break;
+
+               case RT2560_TX_FAIL_RETRY:
+                       DPRINTFN(9, ("sending mgt frame failed (too much "
+                           "retries)\n"));
+                       break;
+
+               case RT2560_TX_FAIL_INVALID:
+               case RT2560_TX_FAIL_OTHER:
+               default:
+                       device_printf(sc->sc_dev, "sending mgt frame failed "
+                           "0x%08x\n", le32toh(desc->flags));
+               }
+
+               bus_dmamap_sync(sc->prioq.data_dmat, data->map,
+                   BUS_DMASYNC_POSTWRITE);
+               bus_dmamap_unload(sc->prioq.data_dmat, data->map);
+               m_freem(data->m);
+               data->m = NULL;
+               ieee80211_free_node(data->ni);
+               data->ni = NULL;
+
+               /* descriptor is no longer valid */
+               desc->flags &= ~htole32(RT2560_TX_VALID);
+
+               DPRINTFN(15, ("prio done idx=%u\n", sc->prioq.next));
+
+               sc->prioq.queued--;
+               sc->prioq.next = (sc->prioq.next + 1) % RT2560_PRIO_RING_COUNT;
+       }
+
+       bus_dmamap_sync(sc->prioq.desc_dmat, sc->prioq.desc_map,
+           BUS_DMASYNC_PREWRITE);
+
+       sc->sc_tx_timer = 0;
+       ifp->if_flags &= ~IFF_OACTIVE;
+       rt2560_start(ifp);
+}
+
+/*
+ * Some frames were processed by the hardware cipher engine and are ready for
+ * transmission to the IEEE802.11 layer.
+ */
+static void
+rt2560_decryption_intr(struct rt2560_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = ic->ic_ifp;
+       struct rt2560_rx_desc *desc;
+       struct rt2560_rx_data *data;
+       bus_addr_t physaddr;
+       struct ieee80211_frame *wh;
+       struct ieee80211_node *ni;
+       struct rt2560_node *rn;
+       struct mbuf *mnew, *m;
+       int hw, error;
+
+       /* retrieve last decriptor index processed by cipher engine */
+       hw = RAL_READ(sc, RT2560_SECCSR0) - sc->rxq.physaddr;
+       hw /= RT2560_RX_DESC_SIZE;
+
+       bus_dmamap_sync(sc->rxq.desc_dmat, sc->rxq.desc_map,
+           BUS_DMASYNC_POSTREAD);
+
+       for (; sc->rxq.cur_decrypt != hw;) {
+               desc = &sc->rxq.desc[sc->rxq.cur_decrypt];
+               data = &sc->rxq.data[sc->rxq.cur_decrypt];
+
+               if ((le32toh(desc->flags) & RT2560_RX_BUSY) ||
+                   (le32toh(desc->flags) & RT2560_RX_CIPHER_BUSY))
+                       break;
+
+               if (data->drop) {
+                       ifp->if_ierrors++;
+                       goto skip;
+               }
+
+               if ((le32toh(desc->flags) & RT2560_RX_CIPHER_MASK) != 0 &&
+                   (le32toh(desc->flags) & RT2560_RX_ICV_ERROR)) {
+                       ifp->if_ierrors++;
+                       goto skip;
+               }
+
+               /*
+                * Try to allocate a new mbuf for this ring element and load it
+                * before processing the current mbuf. If the ring element
+                * cannot be loaded, drop the received packet and reuse the old
+                * mbuf. In the unlikely case that the old mbuf can't be
+                * reloaded either, explicitly panic.
+                */
+               mnew = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR);
+               if (mnew == NULL) {
+                       ifp->if_ierrors++;
+                       goto skip;
+               }
+
+               bus_dmamap_sync(sc->rxq.data_dmat, data->map,
+                   BUS_DMASYNC_POSTREAD);
+               bus_dmamap_unload(sc->rxq.data_dmat, data->map);
+
+               error = bus_dmamap_load(sc->rxq.data_dmat, data->map,
+                   mtod(mnew, void *), MCLBYTES, rt2560_dma_map_addr,
+                   &physaddr, 0);
+               if (error != 0) {
+                       m_freem(mnew);
+
+                       /* try to reload the old mbuf */
+                       error = bus_dmamap_load(sc->rxq.data_dmat, data->map,
+                           mtod(data->m, void *), MCLBYTES,
+                           rt2560_dma_map_addr, &physaddr, 0);
+                       if (error != 0) {
+                               /* very unlikely that it will fail... */
+                               panic("%s: could not load old rx mbuf",
+                                   device_get_name(sc->sc_dev));
+                       }
+                       ifp->if_ierrors++;
+                       goto skip;
+               }
+
+               /*
+                * New mbuf successfully loaded, update Rx ring and continue
+                * processing.
+                */
+               m = data->m;
+               data->m = mnew;
+               desc->physaddr = htole32(physaddr);
+
+               /* finalize mbuf */
+               m->m_pkthdr.rcvif = ifp;
+               m->m_pkthdr.len = m->m_len =
+                   (le32toh(desc->flags) >> 16) & 0xfff;
+
+               if (sc->sc_drvbpf != NULL) {
+                       struct rt2560_rx_radiotap_header *tap = &sc->sc_rxtap;
+                       uint32_t tsf_lo, tsf_hi;
+
+                       /* get timestamp (low and high 32 bits) */
+                       tsf_hi = RAL_READ(sc, RT2560_CSR17);
+                       tsf_lo = RAL_READ(sc, RT2560_CSR16);
+
+                       tap->wr_tsf =
+                           htole64(((uint64_t)tsf_hi << 32) | tsf_lo);
+                       tap->wr_flags = 0;
+                       tap->wr_rate = rt2560_rxrate(desc);
+                       tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
+                       tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+                       tap->wr_antenna = sc->rx_ant;
+                       tap->wr_antsignal = desc->rssi;
+
+                       bpf_ptap(sc->sc_drvbpf, m, tap, sc->sc_rxtap_len);
+               }
+
+               wh = mtod(m, struct ieee80211_frame *);
+               ni = ieee80211_find_rxnode(ic,
+                   (struct ieee80211_frame_min *)wh);
+
+               /* send the frame to the 802.11 layer */
+               ieee80211_input(ic, m, ni, desc->rssi, 0);
+
+               /* give rssi to the rate adatation algorithm */
+               rn = (struct rt2560_node *)ni;
+               ral_rssadapt_input(ic, ni, &rn->rssadapt, desc->rssi);
+
+               /* node is no longer needed */
+               ieee80211_free_node(ni);
+
+skip:          desc->flags = htole32(RT2560_RX_BUSY);
+
+               DPRINTFN(15, ("decryption done idx=%u\n", sc->rxq.cur_decrypt));
+
+               sc->rxq.cur_decrypt =
+                   (sc->rxq.cur_decrypt + 1) % RT2560_RX_RING_COUNT;
+       }
+
+       bus_dmamap_sync(sc->rxq.desc_dmat, sc->rxq.desc_map,
+           BUS_DMASYNC_PREWRITE);
+}
+
+/*
+ * Some frames were received. Pass them to the hardware cipher engine before
+ * sending them to the 802.11 layer.
+ */
+static void
+rt2560_rx_intr(struct rt2560_softc *sc)
+{
+       struct rt2560_rx_desc *desc;
+       struct rt2560_rx_data *data;
+
+       bus_dmamap_sync(sc->rxq.desc_dmat, sc->rxq.desc_map,
+           BUS_DMASYNC_POSTREAD);
+
+       for (;;) {
+               desc = &sc->rxq.desc[sc->rxq.cur];
+               data = &sc->rxq.data[sc->rxq.cur];
+
+               if ((le32toh(desc->flags) & RT2560_RX_BUSY) ||
+                   (le32toh(desc->flags) & RT2560_RX_CIPHER_BUSY))
+                       break;
+
+               data->drop = 0;
+
+               if ((le32toh(desc->flags) & RT2560_RX_PHY_ERROR) ||
+                   (le32toh(desc->flags) & RT2560_RX_CRC_ERROR)) {
+                       /*
+                        * This should not happen since we did not request
+                        * to receive those frames when we filled RXCSR0.
+                        */
+                       DPRINTFN(5, ("PHY or CRC error flags 0x%08x\n",
+                           le32toh(desc->flags)));
+                       data->drop = 1;
+               }
+
+               if (((le32toh(desc->flags) >> 16) & 0xfff) > MCLBYTES) {
+                       DPRINTFN(5, ("bad length\n"));
+                       data->drop = 1;
+               }
+
+               /* mark the frame for decryption */
+               desc->flags |= htole32(RT2560_RX_CIPHER_BUSY);
+
+               DPRINTFN(15, ("rx done idx=%u\n", sc->rxq.cur));
+
+               sc->rxq.cur = (sc->rxq.cur + 1) % RT2560_RX_RING_COUNT;
+       }
+
+       bus_dmamap_sync(sc->rxq.desc_dmat, sc->rxq.desc_map,
+           BUS_DMASYNC_PREWRITE);
+
+       /* kick decrypt */
+       RAL_WRITE(sc, RT2560_SECCSR0, RT2560_KICK_DECRYPT);
+}
+
+/*
+ * This function is called periodically in IBSS mode when a new beacon must be
+ * sent out.
+ */
+static void
+rt2560_beacon_expire(struct rt2560_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct rt2560_tx_data *data;
+
+       if (ic->ic_opmode != IEEE80211_M_IBSS &&
+           ic->ic_opmode != IEEE80211_M_HOSTAP)
+               return;
+
+       data = &sc->bcnq.data[sc->bcnq.next];
+
+       bus_dmamap_sync(sc->bcnq.data_dmat, data->map, BUS_DMASYNC_POSTWRITE);
+       bus_dmamap_unload(sc->bcnq.data_dmat, data->map);
+
+       ieee80211_beacon_update(ic, data->ni, &sc->sc_bo, data->m, 1);
+
+       if (ic->ic_rawbpf != NULL)
+               bpf_mtap(ic->ic_rawbpf, data->m);
+
+       rt2560_tx_bcn(sc, data->m, data->ni);
+
+       DPRINTFN(15, ("beacon expired\n"));
+
+       sc->bcnq.next = (sc->bcnq.next + 1) % RT2560_BEACON_RING_COUNT;
+}
+
+/* ARGSUSED */
+static void
+rt2560_wakeup_expire(struct rt2560_softc *sc)
+{
+       DPRINTFN(2, ("wakeup expired\n"));
+}
+
+static void
+rt2560_intr(void *arg)
+{
+       struct rt2560_softc *sc = arg;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       uint32_t r;
+
+       /* disable interrupts */
+       RAL_WRITE(sc, RT2560_CSR8, 0xffffffff);
+
+       /* don't re-enable interrupts if we're shutting down */
+       if (!(ifp->if_flags & IFF_RUNNING))
+               return;
+
+       r = RAL_READ(sc, RT2560_CSR7);
+       RAL_WRITE(sc, RT2560_CSR7, r);
+
+       if (r & RT2560_BEACON_EXPIRE)
+               rt2560_beacon_expire(sc);
+
+       if (r & RT2560_WAKEUP_EXPIRE)
+               rt2560_wakeup_expire(sc);
+
+       if (r & RT2560_PRIO_DONE)
+               rt2560_prio_intr(sc);
+
+       if (r & (RT2560_TX_DONE | RT2560_ENCRYPTION_DONE)) {
+               int i;
+
+               for (i = 0; i < 2; ++i) {
+                       rt2560_tx_intr(sc);
+                       rt2560_encryption_intr(sc);
+               }
+       }
+
+       if (r & (RT2560_DECRYPTION_DONE | RT2560_RX_DONE)) {
+               int i;
+
+               for (i = 0; i < 2; ++i) {
+                       rt2560_decryption_intr(sc);
+                       rt2560_rx_intr(sc);
+               }
+       }
+
+       /* re-enable interrupts */
+       RAL_WRITE(sc, RT2560_CSR8, RT2560_INTR_MASK);
+}
+
+/* quickly determine if a given rate is CCK or OFDM */
+#define RAL_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
+
+#define RAL_ACK_SIZE   14      /* 10 + 4(FCS) */
+#define RAL_CTS_SIZE   14      /* 10 + 4(FCS) */
+
+#define RAL_SIFS               10      /* us */
+
+#define RT2560_TXRX_TURNAROUND 10      /* us */
+
+/*
+ * This function is only used by the Rx radiotap code.
+ */
+static uint8_t
+rt2560_rxrate(struct rt2560_rx_desc *desc)
+{
+       if (le32toh(desc->flags) & RT2560_RX_OFDM) {
+               /* reverse function of rt2560_plcp_signal */
+               switch (desc->rate) {
+               case 0xb:       return 12;
+               case 0xf:       return 18;
+               case 0xa:       return 24;
+               case 0xe:       return 36;
+               case 0x9:       return 48;
+               case 0xd:       return 72;
+               case 0x8:       return 96;
+               case 0xc:       return 108;
+               }
+       } else {
+               if (desc->rate == 10)
+                       return 2;
+               if (desc->rate == 20)
+                       return 4;
+               if (desc->rate == 55)
+                       return 11;
+               if (desc->rate == 110)
+                       return 22;
+       }
+       return 2;       /* should not get there */
+}
+
+/*
+ * Return the expected ack rate for a frame transmitted at rate `rate'.
+ * XXX: this should depend on the destination node basic rate set.
+ */
+static int
+rt2560_ack_rate(struct ieee80211com *ic, int rate)
+{
+       switch (rate) {
+       /* CCK rates */
+       case 2:
+               return 2;
+       case 4:
+       case 11:
+       case 22:
+               return (ic->ic_curmode == IEEE80211_MODE_11B) ? 4 : rate;
+
+       /* OFDM rates */
+       case 12:
+       case 18:
+               return 12;
+       case 24:
+       case 36:
+               return 24;
+       case 48:
+       case 72:
+       case 96:
+       case 108:
+               return 48;
+       }
+
+       /* default to 1Mbps */
+       return 2;
+}
+
+/*
+ * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'.
+ * The function automatically determines the operating mode depending on the
+ * given rate. `flags' indicates whether short preamble is in use or not.
+ */
+static uint16_t
+rt2560_txtime(int len, int rate, uint32_t flags)
+{
+       uint16_t txtime;
+
+       if (RAL_RATE_IS_OFDM(rate)) {
+               /* IEEE Std 802.11a-1999, pp. 37 */
+               txtime = (8 + 4 * len + 3 + rate - 1) / rate;
+               txtime = 16 + 4 + 4 * txtime + 6;
+       } else {
+               /* IEEE Std 802.11b-1999, pp. 28 */
+               txtime = (16 * len + rate - 1) / rate;
+               if (rate != 2 && (flags & IEEE80211_F_SHPREAMBLE))
+                       txtime +=  72 + 24;
+               else
+                       txtime += 144 + 48;
+       }
+
+       return txtime;
+}
+
+static uint8_t
+rt2560_plcp_signal(int rate)
+{
+       switch (rate) {
+       /* CCK rates (returned values are device-dependent) */
+       case 2:         return 0x0;
+       case 4:         return 0x1;
+       case 11:        return 0x2;
+       case 22:        return 0x3;
+
+       /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
+       case 12:        return 0xb;
+       case 18:        return 0xf;
+       case 24:        return 0xa;
+       case 36:        return 0xe;
+       case 48:        return 0x9;
+       case 72:        return 0xd;
+       case 96:        return 0x8;
+       case 108:       return 0xc;
+
+       /* unsupported rates (should not get there) */
+       default:        return 0xff;
+       }
+}
+
+static void
+rt2560_setup_tx_desc(struct rt2560_softc *sc, struct rt2560_tx_desc *desc,
+    uint32_t flags, int len, int rate, int encrypt, bus_addr_t physaddr)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       uint16_t plcp_length;
+       int remainder;
+
+       desc->flags = htole32(flags);
+       desc->flags |= htole32(len << 16);
+       desc->flags |= encrypt ? htole32(RT2560_TX_CIPHER_BUSY) :
+           htole32(RT2560_TX_BUSY | RT2560_TX_VALID);
+
+       desc->physaddr = htole32(physaddr);
+       desc->wme = htole16(
+           RT2560_AIFSN(2) |
+           RT2560_LOGCWMIN(3) |
+           RT2560_LOGCWMAX(8));
+
+       /* setup PLCP fields */
+       desc->plcp_signal  = rt2560_plcp_signal(rate);
+       desc->plcp_service = 4;
+
+       len += IEEE80211_CRC_LEN;
+       if (RAL_RATE_IS_OFDM(rate)) {
+               desc->flags |= htole32(RT2560_TX_OFDM);
+
+               plcp_length = len & 0xfff;
+               desc->plcp_length_hi = plcp_length >> 6;
+               desc->plcp_length_lo = plcp_length & 0x3f;
+       } else {
+               plcp_length = (16 * len + rate - 1) / rate;
+               if (rate == 22) {
+                       remainder = (16 * len) % 22;
+                       if (remainder != 0 && remainder < 7)
+                               desc->plcp_service |= RT2560_PLCP_LENGEXT;
+               }
+               desc->plcp_length_hi = plcp_length >> 8;
+               desc->plcp_length_lo = plcp_length & 0xff;
+
+               if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
+                       desc->plcp_signal |= 0x08;
+       }
+}
+
+static int
+rt2560_tx_bcn(struct rt2560_softc *sc, struct mbuf *m0,
+    struct ieee80211_node *ni)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct rt2560_tx_desc *desc;
+       struct rt2560_tx_data *data;
+       bus_addr_t paddr;
+       int rate, error;
+
+       desc = &sc->bcnq.desc[sc->bcnq.cur];
+       data = &sc->bcnq.data[sc->bcnq.cur];
+
+       rate = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? 12 : 2;
+
+       error = bus_dmamap_load_mbuf(sc->bcnq.data_dmat, data->map, m0,
+                                    rt2560_dma_map_mbuf, &paddr,
+                                    BUS_DMA_NOWAIT);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not map mbuf (error %d)\n",
+                   error);
+               m_freem(m0);
+               return error;
+       }
+
+       if (sc->sc_drvbpf != NULL) {
+               struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap;
+
+               tap->wt_flags = 0;
+               tap->wt_rate = rate;
+               tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+               tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+               tap->wt_antenna = sc->tx_ant;
+
+               bpf_ptap(sc->sc_drvbpf, m0, tap, sc->sc_txtap_len);
+       }
+
+       data->m = m0;
+       data->ni = ni;
+
+       rt2560_setup_tx_desc(sc, desc, RT2560_TX_IFS_NEWBACKOFF |
+           RT2560_TX_TIMESTAMP, m0->m_pkthdr.len, rate, 0, paddr);
+
+       DPRINTFN(10, ("sending beacon frame len=%u idx=%u rate=%u\n",
+           m0->m_pkthdr.len, sc->bcnq.cur, rate));
+
+       bus_dmamap_sync(sc->bcnq.data_dmat, data->map, BUS_DMASYNC_PREWRITE);
+       bus_dmamap_sync(sc->bcnq.desc_dmat, sc->bcnq.desc_map,
+           BUS_DMASYNC_PREWRITE);
+
+       sc->bcnq.cur = (sc->bcnq.cur + 1) % RT2560_BEACON_RING_COUNT;
+
+       return 0;
+}
+
+static int
+rt2560_tx_mgt(struct rt2560_softc *sc, struct mbuf *m0,
+    struct ieee80211_node *ni)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct rt2560_tx_desc *desc;
+       struct rt2560_tx_data *data;
+       struct ieee80211_frame *wh;
+       bus_addr_t paddr;
+       uint16_t dur;
+       uint32_t flags = 0;
+       int rate, error;
+
+       desc = &sc->prioq.desc[sc->prioq.cur];
+       data = &sc->prioq.data[sc->prioq.cur];
+
+       rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
+
+       error = bus_dmamap_load_mbuf(sc->prioq.data_dmat, data->map, m0,
+                                    rt2560_dma_map_mbuf, &paddr, 0);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not map mbuf (error %d)\n",
+                   error);
+               m_freem(m0);
+               return error;
+       }
+
+       if (sc->sc_drvbpf != NULL) {
+               struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap;
+
+               tap->wt_flags = 0;
+               tap->wt_rate = rate;
+               tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+               tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+               tap->wt_antenna = sc->tx_ant;
+
+               bpf_ptap(sc->sc_drvbpf, m0, tap, sc->sc_txtap_len);
+       }
+
+       data->m = m0;
+       data->ni = ni;
+
+       wh = mtod(m0, struct ieee80211_frame *);
+
+       if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+               flags |= RT2560_TX_ACK;
+
+               dur = rt2560_txtime(RAL_ACK_SIZE, rate, ic->ic_flags) +
+                     RAL_SIFS;
+               *(uint16_t *)wh->i_dur = htole16(dur);
+
+               /* tell hardware to add timestamp for probe responses */
+               if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
+                   IEEE80211_FC0_TYPE_MGT &&
+                   (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+                   IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+                       flags |= RT2560_TX_TIMESTAMP;
+       }
+
+       rt2560_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, rate, 0, paddr);
+
+       bus_dmamap_sync(sc->prioq.data_dmat, data->map, BUS_DMASYNC_PREWRITE);
+       bus_dmamap_sync(sc->prioq.desc_dmat, sc->prioq.desc_map,
+           BUS_DMASYNC_PREWRITE);
+
+       DPRINTFN(10, ("sending mgt frame len=%u idx=%u rate=%u\n",
+           m0->m_pkthdr.len, sc->prioq.cur, rate));
+
+       /* kick prio */
+       sc->prioq.queued++;
+       sc->prioq.cur = (sc->prioq.cur + 1) % RT2560_PRIO_RING_COUNT;
+       RAL_WRITE(sc, RT2560_TXCSR0, RT2560_KICK_PRIO);
+
+       return 0;
+}
+
+/*
+ * Build a RTS control frame.
+ */
+static struct mbuf *
+rt2560_get_rts(struct rt2560_softc *sc, struct ieee80211_frame *wh,
+    uint16_t dur)
+{
+       struct ieee80211_frame_rts *rts;
+       struct mbuf *m;
+
+       MGETHDR(m, MB_DONTWAIT, MT_DATA);
+       if (m == NULL) {
+               sc->sc_ic.ic_stats.is_tx_nobuf++;
+               device_printf(sc->sc_dev, "could not allocate RTS frame\n");
+               return NULL;
+       }
+
+       rts = mtod(m, struct ieee80211_frame_rts *);
+
+       rts->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL |
+           IEEE80211_FC0_SUBTYPE_RTS;
+       rts->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+       *(uint16_t *)rts->i_dur = htole16(dur);
+       IEEE80211_ADDR_COPY(rts->i_ra, wh->i_addr1);
+       IEEE80211_ADDR_COPY(rts->i_ta, wh->i_addr2);
+
+       m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_rts);
+
+       return m;
+}
+
+static int
+rt2560_tx_data(struct rt2560_softc *sc, struct mbuf *m0,
+    struct ieee80211_node *ni)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct rt2560_tx_desc *desc;
+       struct rt2560_tx_data *data;
+       struct rt2560_node *rn;
+       struct ieee80211_rateset *rs;
+       struct ieee80211_frame *wh;
+       struct ieee80211_key *k;
+       struct mbuf *mnew;
+       bus_addr_t paddr;
+       uint16_t dur;
+       uint32_t flags = 0;
+       int rate, error;
+
+       wh = mtod(m0, struct ieee80211_frame *);
+
+       if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
+               rs = &ic->ic_sup_rates[ic->ic_curmode];
+               rate = rs->rs_rates[ic->ic_fixed_rate];
+       } else {
+               rs = &ni->ni_rates;
+               rn = (struct rt2560_node *)ni;
+               ni->ni_txrate = ral_rssadapt_choose(&rn->rssadapt, rs, wh,
+                   m0->m_pkthdr.len, NULL, 0);
+               rate = rs->rs_rates[ni->ni_txrate];
+       }
+       rate &= IEEE80211_RATE_VAL;
+
+       if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+               k = ieee80211_crypto_encap(ic, ni, m0);
+               if (k == NULL) {
+                       m_freem(m0);
+                       return ENOBUFS;
+               }
+
+               /* packet header may have moved, reset our local pointer */
+               wh = mtod(m0, struct ieee80211_frame *);
+       }
+
+       /*
+        * IEEE Std 802.11-1999, pp 82: "A STA shall use an RTS/CTS exchange
+        * for directed frames only when the length of the MPDU is greater
+        * than the length threshold indicated by [...]" ic_rtsthreshold.
+        */
+       if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+           m0->m_pkthdr.len > ic->ic_rtsthreshold) {
+               struct mbuf *m;
+               uint16_t dur;
+               int rtsrate, ackrate;
+
+               rtsrate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
+               ackrate = rt2560_ack_rate(ic, rate);
+
+               dur = rt2560_txtime(m0->m_pkthdr.len + 4, rate, ic->ic_flags) +
+                     rt2560_txtime(RAL_CTS_SIZE, rtsrate, ic->ic_flags) +
+                     rt2560_txtime(RAL_ACK_SIZE, ackrate, ic->ic_flags) +
+                     3 * RAL_SIFS;
+
+               m = rt2560_get_rts(sc, wh, dur);
+
+               desc = &sc->txq.desc[sc->txq.cur_encrypt];
+               data = &sc->txq.data[sc->txq.cur_encrypt];
+
+               error = bus_dmamap_load_mbuf(sc->txq.data_dmat, data->map,
+                                            m, rt2560_dma_map_mbuf, &paddr, 0);
+               if (error != 0) {
+                       device_printf(sc->sc_dev,
+                           "could not map mbuf (error %d)\n", error);
+                       m_freem(m);
+                       m_freem(m0);
+                       return error;
+               }
+
+               /* avoid multiple free() of the same node for each fragment */
+               ieee80211_ref_node(ni);
+
+               data->m = m;
+               data->ni = ni;
+
+               /* RTS frames are not taken into account for rssadapt */
+               data->id.id_node = NULL;
+
+               rt2560_setup_tx_desc(sc, desc, RT2560_TX_ACK |
+                   RT2560_TX_MORE_FRAG, m->m_pkthdr.len, rtsrate, 1, paddr);
+
+               bus_dmamap_sync(sc->txq.data_dmat, data->map,
+                   BUS_DMASYNC_PREWRITE);
+
+               sc->txq.queued++;
+               sc->txq.cur_encrypt =
+                   (sc->txq.cur_encrypt + 1) % RT2560_TX_RING_COUNT;
+
+               /*
+                * IEEE Std 802.11-1999: when an RTS/CTS exchange is used, the
+                * asynchronous data frame shall be transmitted after the CTS
+                * frame and a SIFS period.
+                */
+               flags |= RT2560_TX_LONG_RETRY | RT2560_TX_IFS_SIFS;
+       }
+
+       data = &sc->txq.data[sc->txq.cur_encrypt];
+       desc = &sc->txq.desc[sc->txq.cur_encrypt];
+
+       error = bus_dmamap_load_mbuf(sc->txq.data_dmat, data->map, m0,
+                                    rt2560_dma_map_mbuf, &paddr, 0);
+       if (error != 0 && error != EFBIG) {
+               device_printf(sc->sc_dev, "could not map mbuf (error %d)\n",
+                   error);
+               m_freem(m0);
+               return error;
+       }
+       if (error != 0) {
+               mnew = m_defrag(m0, MB_DONTWAIT);
+               if (mnew == NULL) {
+                       device_printf(sc->sc_dev,
+                           "could not defragment mbuf\n");
+                       m_freem(m0);
+                       return ENOBUFS;
+               }
+               m0 = mnew;
+
+               error = bus_dmamap_load_mbuf(sc->txq.data_dmat, data->map,
+                                            m0, rt2560_dma_map_mbuf, &paddr,
+                                            0);
+               if (error != 0) {
+                       device_printf(sc->sc_dev,
+                           "could not map mbuf (error %d)\n", error);
+                       m_freem(m0);
+                       return error;
+               }
+
+               /* packet header may have moved, reset our local pointer */
+               wh = mtod(m0, struct ieee80211_frame *);
+       }
+
+       if (sc->sc_drvbpf != NULL) {
+               struct rt2560_tx_radiotap_header *tap = &sc->sc_txtap;
+
+               tap->wt_flags = 0;
+               tap->wt_rate = rate;
+               tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+               tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+               tap->wt_antenna = sc->tx_ant;
+
+               bpf_ptap(sc->sc_drvbpf, m0, tap, sc->sc_txtap_len);
+       }
+
+       data->m = m0;
+       data->ni = ni;
+
+       /* remember link conditions for rate adaptation algorithm */
+       if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
+               data->id.id_len = m0->m_pkthdr.len;
+               data->id.id_rateidx = ni->ni_txrate;
+               data->id.id_node = ni;
+               data->id.id_rssi = ni->ni_rssi;
+       } else
+               data->id.id_node = NULL;
+
+       if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+               flags |= RT2560_TX_ACK;
+
+               dur = rt2560_txtime(RAL_ACK_SIZE, rt2560_ack_rate(ic, rate),
+                   ic->ic_flags) + RAL_SIFS;
+               *(uint16_t *)wh->i_dur = htole16(dur);
+       }
+
+       rt2560_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, rate, 1, paddr);
+
+       bus_dmamap_sync(sc->txq.data_dmat, data->map, BUS_DMASYNC_PREWRITE);
+       bus_dmamap_sync(sc->txq.desc_dmat, sc->txq.desc_map,
+           BUS_DMASYNC_PREWRITE);
+
+       DPRINTFN(10, ("sending data frame len=%u idx=%u rate=%u\n",
+           m0->m_pkthdr.len, sc->txq.cur_encrypt, rate));
+
+       /* kick encrypt */
+       sc->txq.queued++;
+       sc->txq.cur_encrypt = (sc->txq.cur_encrypt + 1) % RT2560_TX_RING_COUNT;
+       RAL_WRITE(sc, RT2560_SECCSR1, RT2560_KICK_ENCRYPT);
+
+       return 0;
+}
+
+static void
+rt2560_start(struct ifnet *ifp)
+{
+       struct rt2560_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct mbuf *m0;
+       struct ether_header *eh;
+       struct ieee80211_node *ni;
+
+       /* prevent management frames from being sent if we're not ready */
+       if (!(ifp->if_flags & IFF_RUNNING))
+               return;
+
+       for (;;) {
+               IF_POLL(&ic->ic_mgtq, m0);
+               if (m0 != NULL) {
+                       if (sc->prioq.queued >= RT2560_PRIO_RING_COUNT) {
+                               ifp->if_flags |= IFF_OACTIVE;
+                               break;
+                       }
+                       IF_DEQUEUE(&ic->ic_mgtq, m0);
+
+                       ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
+                       m0->m_pkthdr.rcvif = NULL;
+
+                       if (ic->ic_rawbpf != NULL)
+                               bpf_mtap(ic->ic_rawbpf, m0);
+
+                       if (rt2560_tx_mgt(sc, m0, ni) != 0)
+                               break;
+
+               } else {
+                       if (ic->ic_state != IEEE80211_S_RUN)
+                               break;
+                       m0 = ifq_poll(&ifp->if_snd);
+                       if (m0 == NULL)
+                               break;
+                       if (sc->txq.queued >= RT2560_TX_RING_COUNT - 1) {
+                               ifp->if_flags |= IFF_OACTIVE;
+                               break;
+                       }
+                       m0 = ifq_dequeue(&ifp->if_snd, m0);
+
+                       if (m0->m_len < sizeof (struct ether_header) &&
+                           !(m0 = m_pullup(m0, sizeof (struct ether_header))))
+                               continue;
+
+                       eh = mtod(m0, struct ether_header *);
+                       ni = ieee80211_find_txnode(ic, eh->ether_dhost);
+                       if (ni == NULL) {
+                               m_freem(m0);
+                               continue;
+                       }
+                       BPF_MTAP(ifp, m0);
+
+                       m0 = ieee80211_encap(ic, m0, ni);
+                       if (m0 == NULL) {
+                               ieee80211_free_node(ni);
+                               continue;
+                       }
+
+                       if (ic->ic_rawbpf != NULL)
+                               bpf_mtap(ic->ic_rawbpf, m0);
+
+                       if (rt2560_tx_data(sc, m0, ni) != 0) {
+                               ieee80211_free_node(ni);
+                               ifp->if_oerrors++;
+                               break;
+                       }
+               }
+
+               sc->sc_tx_timer = 5;
+               ifp->if_timer = 1;
+       }
+}
+
+static void
+rt2560_watchdog(struct ifnet *ifp)
+{
+       struct rt2560_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+
+       ifp->if_timer = 0;
+
+       if (sc->sc_tx_timer > 0) {
+               if (--sc->sc_tx_timer == 0) {
+                       device_printf(sc->sc_dev, "device timeout\n");
+                       rt2560_init(sc);
+                       ifp->if_oerrors++;
+                       return;
+               }
+               ifp->if_timer = 1;
+       }
+
+       ieee80211_watchdog(ic);
+}
+
+/*
+ * This function allows for fast channel switching in monitor mode (used by
+ * net-mgmt/kismet). In IBSS mode, we must explicitly reset the interface to
+ * generate a new beacon frame.
+ */
+static int
+rt2560_reset(struct ifnet *ifp)
+{
+       struct rt2560_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+
+       if (ic->ic_opmode != IEEE80211_M_MONITOR)
+               return ENETRESET;
+
+       rt2560_set_chan(sc, ic->ic_curchan);
+
+       return 0;
+}
+
+static int
+rt2560_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
+{
+       struct rt2560_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       int error = 0;
+
+       switch (cmd) {
+       case SIOCSIFFLAGS:
+               if (ifp->if_flags & IFF_UP) {
+                       if (ifp->if_flags & IFF_RUNNING)
+                               rt2560_update_promisc(sc);
+                       else
+                               rt2560_init(sc);
+               } else {
+                       if (ifp->if_flags & IFF_RUNNING)
+                               rt2560_stop(sc);
+               }
+               break;
+
+       default:
+               error = ieee80211_ioctl(ic, cmd, data, cr);
+       }
+
+       if (error == ENETRESET) {
+               if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
+                   (IFF_UP | IFF_RUNNING) &&
+                   (ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
+                       rt2560_init(sc);
+               error = 0;
+       }
+
+       return error;
+}
+
+static void
+rt2560_bbp_write(struct rt2560_softc *sc, uint8_t reg, uint8_t val)
+{
+       uint32_t tmp;
+       int ntries;
+
+       for (ntries = 0; ntries < 100; ntries++) {
+               if (!(RAL_READ(sc, RT2560_BBPCSR) & RT2560_BBP_BUSY))
+                       break;
+               DELAY(1);
+       }
+       if (ntries == 100) {
+               device_printf(sc->sc_dev, "could not write to BBP\n");
+               return;
+       }
+
+       tmp = RT2560_BBP_WRITE | RT2560_BBP_BUSY | reg << 8 | val;
+       RAL_WRITE(sc, RT2560_BBPCSR, tmp);
+
+       DPRINTFN(15, ("BBP R%u <- 0x%02x\n", reg, val));
+}
+
+static uint8_t
+rt2560_bbp_read(struct rt2560_softc *sc, uint8_t reg)
+{
+       uint32_t val;
+       int ntries;
+
+       val = RT2560_BBP_BUSY | reg << 8;
+       RAL_WRITE(sc, RT2560_BBPCSR, val);
+
+       for (ntries = 0; ntries < 100; ntries++) {
+               val = RAL_READ(sc, RT2560_BBPCSR);
+               if (!(val & RT2560_BBP_BUSY))
+                       return val & 0xff;
+               DELAY(1);
+       }
+
+       device_printf(sc->sc_dev, "could not read from BBP\n");
+       return 0;
+}
+
+static void
+rt2560_rf_write(struct rt2560_softc *sc, uint8_t reg, uint32_t val)
+{
+       uint32_t tmp;
+       int ntries;
+
+       for (ntries = 0; ntries < 100; ntries++) {
+               if (!(RAL_READ(sc, RT2560_RFCSR) & RT2560_RF_BUSY))
+                       break;
+               DELAY(1);
+       }
+       if (ntries == 100) {
+               device_printf(sc->sc_dev, "could not write to RF\n");
+               return;
+       }
+
+       tmp = RT2560_RF_BUSY | RT2560_RF_20BIT | (val & 0xfffff) << 2 |
+           (reg & 0x3);
+       RAL_WRITE(sc, RT2560_RFCSR, tmp);
+
+       /* remember last written value in sc */
+       sc->rf_regs[reg] = val;
+
+       DPRINTFN(15, ("RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff));
+}
+
+static void
+rt2560_set_chan(struct rt2560_softc *sc, struct ieee80211_channel *c)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       uint8_t power, tmp;
+       u_int i, chan;
+
+       chan = ieee80211_chan2ieee(ic, c);
+       if (chan == 0 || chan == IEEE80211_CHAN_ANY)
+               return;
+
+       if (IEEE80211_IS_CHAN_2GHZ(c))
+               power = min(sc->txpow[chan - 1], 31);
+       else
+               power = 31;
+
+       /* adjust txpower using ifconfig settings */
+       power -= (100 - ic->ic_txpowlimit) / 8;
+
+       DPRINTFN(2, ("setting channel to %u, txpower to %u\n", chan, power));
+
+       switch (sc->rf_rev) {
+       case RT2560_RF_2522:
+               rt2560_rf_write(sc, RAL_RF1, 0x00814);
+               rt2560_rf_write(sc, RAL_RF2, rt2560_rf2522_r2[chan - 1]);
+               rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x00040);
+               break;
+
+       case RT2560_RF_2523:
+               rt2560_rf_write(sc, RAL_RF1, 0x08804);
+               rt2560_rf_write(sc, RAL_RF2, rt2560_rf2523_r2[chan - 1]);
+               rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x38044);
+               rt2560_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
+               break;
+
+       case RT2560_RF_2524:
+               rt2560_rf_write(sc, RAL_RF1, 0x0c808);
+               rt2560_rf_write(sc, RAL_RF2, rt2560_rf2524_r2[chan - 1]);
+               rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x00040);
+               rt2560_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
+               break;
+
+       case RT2560_RF_2525:
+               rt2560_rf_write(sc, RAL_RF1, 0x08808);
+               rt2560_rf_write(sc, RAL_RF2, rt2560_rf2525_hi_r2[chan - 1]);
+               rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
+               rt2560_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
+
+               rt2560_rf_write(sc, RAL_RF1, 0x08808);
+               rt2560_rf_write(sc, RAL_RF2, rt2560_rf2525_r2[chan - 1]);
+               rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
+               rt2560_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
+               break;
+
+       case RT2560_RF_2525E:
+               rt2560_rf_write(sc, RAL_RF1, 0x08808);
+               rt2560_rf_write(sc, RAL_RF2, rt2560_rf2525e_r2[chan - 1]);
+               rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
+               rt2560_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282);
+               break;
+
+       case RT2560_RF_2526:
+               rt2560_rf_write(sc, RAL_RF2, rt2560_rf2526_hi_r2[chan - 1]);
+               rt2560_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381);
+               rt2560_rf_write(sc, RAL_RF1, 0x08804);
+
+               rt2560_rf_write(sc, RAL_RF2, rt2560_rf2526_r2[chan - 1]);
+               rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
+               rt2560_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381);
+               break;
+
+       /* dual-band RF */
+       case RT2560_RF_5222:
+               for (i = 0; rt2560_rf5222[i].chan != chan; i++);
+
+               rt2560_rf_write(sc, RAL_RF1, rt2560_rf5222[i].r1);
+               rt2560_rf_write(sc, RAL_RF2, rt2560_rf5222[i].r2);
+               rt2560_rf_write(sc, RAL_RF3, power << 7 | 0x00040);
+               rt2560_rf_write(sc, RAL_RF4, rt2560_rf5222[i].r4);
+               break;
+       }
+
+       if (ic->ic_state != IEEE80211_S_SCAN) {
+               /* set Japan filter bit for channel 14 */
+               tmp = rt2560_bbp_read(sc, 70);
+
+               tmp &= ~RT2560_JAPAN_FILTER;
+               if (chan == 14)
+                       tmp |= RT2560_JAPAN_FILTER;
+
+               rt2560_bbp_write(sc, 70, tmp);
+
+               /* clear CRC errors */
+               RAL_READ(sc, RT2560_CNT0);
+       }
+}
+
+#if 0
+/*
+ * Disable RF auto-tuning.
+ */
+static void
+rt2560_disable_rf_tune(struct rt2560_softc *sc)
+{
+       uint32_t tmp;
+
+       if (sc->rf_rev != RT2560_RF_2523) {
+               tmp = sc->rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE;
+               rt2560_rf_write(sc, RAL_RF1, tmp);
+       }
+
+       tmp = sc->rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE;
+       rt2560_rf_write(sc, RAL_RF3, tmp);
+
+       DPRINTFN(2, ("disabling RF autotune\n"));
+}
+#endif
+
+/*
+ * Refer to IEEE Std 802.11-1999 pp. 123 for more information on TSF
+ * synchronization.
+ */
+static void
+rt2560_enable_tsf_sync(struct rt2560_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       uint16_t logcwmin, preload;
+       uint32_t tmp;
+
+       /* first, disable TSF synchronization */
+       RAL_WRITE(sc, RT2560_CSR14, 0);
+
+       tmp = 16 * ic->ic_bss->ni_intval;
+       RAL_WRITE(sc, RT2560_CSR12, tmp);
+
+       RAL_WRITE(sc, RT2560_CSR13, 0);
+
+       logcwmin = 5;
+       preload = (ic->ic_opmode == IEEE80211_M_STA) ? 384 : 1024;
+       tmp = logcwmin << 16 | preload;
+       RAL_WRITE(sc, RT2560_BCNOCSR, tmp);
+
+       /* finally, enable TSF synchronization */
+       tmp = RT2560_ENABLE_TSF | RT2560_ENABLE_TBCN;
+       if (ic->ic_opmode == IEEE80211_M_STA)
+               tmp |= RT2560_ENABLE_TSF_SYNC(1);
+       else
+               tmp |= RT2560_ENABLE_TSF_SYNC(2) |
+                      RT2560_ENABLE_BEACON_GENERATOR;
+       RAL_WRITE(sc, RT2560_CSR14, tmp);
+
+       DPRINTF(("enabling TSF synchronization\n"));
+}
+
+static void
+rt2560_update_plcp(struct rt2560_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+
+       /* no short preamble for 1Mbps */
+       RAL_WRITE(sc, RT2560_PLCP1MCSR, 0x00700400);
+
+       if (!(ic->ic_flags & IEEE80211_F_SHPREAMBLE)) {
+               /* values taken from the reference driver */
+               RAL_WRITE(sc, RT2560_PLCP2MCSR,   0x00380401);
+               RAL_WRITE(sc, RT2560_PLCP5p5MCSR, 0x00150402);
+               RAL_WRITE(sc, RT2560_PLCP11MCSR,  0x000b8403);
+       } else {
+               /* same values as above or'ed 0x8 */
+               RAL_WRITE(sc, RT2560_PLCP2MCSR,   0x00380409);
+               RAL_WRITE(sc, RT2560_PLCP5p5MCSR, 0x0015040a);
+               RAL_WRITE(sc, RT2560_PLCP11MCSR,  0x000b840b);
+       }
+
+       DPRINTF(("updating PLCP for %s preamble\n",
+           (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? "short" : "long"));
+}
+
+/*
+ * This function can be called by ieee80211_set_shortslottime(). Refer to
+ * IEEE Std 802.11-1999 pp. 85 to know how these values are computed.
+ */
+static void
+rt2560_update_slot(struct ifnet *ifp)
+{
+       struct rt2560_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       uint8_t slottime;
+       uint16_t tx_sifs, tx_pifs, tx_difs, eifs;
+       uint32_t tmp;
+
+       slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20;
+
+       /* update the MAC slot boundaries */
+       tx_sifs = RAL_SIFS - RT2560_TXRX_TURNAROUND;
+       tx_pifs = tx_sifs + slottime;
+       tx_difs = tx_sifs + 2 * slottime;
+       eifs = (ic->ic_curmode == IEEE80211_MODE_11B) ? 364 : 60;
+
+       tmp = RAL_READ(sc, RT2560_CSR11);
+       tmp = (tmp & ~0x1f00) | slottime << 8;
+       RAL_WRITE(sc, RT2560_CSR11, tmp);
+
+       tmp = tx_pifs << 16 | tx_sifs;
+       RAL_WRITE(sc, RT2560_CSR18, tmp);
+
+       tmp = eifs << 16 | tx_difs;
+       RAL_WRITE(sc, RT2560_CSR19, tmp);
+
+       DPRINTF(("setting slottime to %uus\n", slottime));
+}
+
+static void
+rt2560_set_basicrates(struct rt2560_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+
+       /* update basic rate set */
+       if (ic->ic_curmode == IEEE80211_MODE_11B) {
+               /* 11b basic rates: 1, 2Mbps */
+               RAL_WRITE(sc, RT2560_ARSP_PLCP_1, 0x3);
+       } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) {
+               /* 11a basic rates: 6, 12, 24Mbps */
+               RAL_WRITE(sc, RT2560_ARSP_PLCP_1, 0x150);
+       } else {
+               /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */
+               RAL_WRITE(sc, RT2560_ARSP_PLCP_1, 0x15f);
+       }
+}
+
+static void
+rt2560_update_led(struct rt2560_softc *sc, int led1, int led2)
+{
+       uint32_t tmp;
+
+       /* set ON period to 70ms and OFF period to 30ms */
+       tmp = led1 << 16 | led2 << 17 | 70 << 8 | 30;
+       RAL_WRITE(sc, RT2560_LEDCSR, tmp);
+}
+
+static void
+rt2560_set_bssid(struct rt2560_softc *sc, uint8_t *bssid)
+{
+       uint32_t tmp;
+
+       tmp = bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24;
+       RAL_WRITE(sc, RT2560_CSR5, tmp);
+
+       tmp = bssid[4] | bssid[5] << 8;
+       RAL_WRITE(sc, RT2560_CSR6, tmp);
+
+       DPRINTF(("setting BSSID to %6D\n", bssid, ":"));
+}
+
+static void
+rt2560_set_macaddr(struct rt2560_softc *sc, uint8_t *addr)
+{
+       uint32_t tmp;
+
+       tmp = addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24;
+       RAL_WRITE(sc, RT2560_CSR3, tmp);
+
+       tmp = addr[4] | addr[5] << 8;
+       RAL_WRITE(sc, RT2560_CSR4, tmp);
+
+       DPRINTF(("setting MAC address to %6D\n", addr, ":"));
+}
+
+static void
+rt2560_get_macaddr(struct rt2560_softc *sc, uint8_t *addr)
+{
+       uint32_t tmp;
+
+       tmp = RAL_READ(sc, RT2560_CSR3);
+       addr[0] = tmp & 0xff;
+       addr[1] = (tmp >>  8) & 0xff;
+       addr[2] = (tmp >> 16) & 0xff;
+       addr[3] = (tmp >> 24);
+
+       tmp = RAL_READ(sc, RT2560_CSR4);
+       addr[4] = tmp & 0xff;
+       addr[5] = (tmp >> 8) & 0xff;
+}
+
+static void
+rt2560_update_promisc(struct rt2560_softc *sc)
+{
+       struct ifnet *ifp = sc->sc_ic.ic_ifp;
+       uint32_t tmp;
+
+       tmp = RAL_READ(sc, RT2560_RXCSR0);
+
+       tmp &= ~RT2560_DROP_NOT_TO_ME;
+       if (!(ifp->if_flags & IFF_PROMISC))
+               tmp |= RT2560_DROP_NOT_TO_ME;
+
+       RAL_WRITE(sc, RT2560_RXCSR0, tmp);
+
+       DPRINTF(("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ?
+           "entering" : "leaving"));
+}
+
+static const char *
+rt2560_get_rf(int rev)
+{
+       switch (rev) {
+       case RT2560_RF_2522:    return "RT2522";
+       case RT2560_RF_2523:    return "RT2523";
+       case RT2560_RF_2524:    return "RT2524";
+       case RT2560_RF_2525:    return "RT2525";
+       case RT2560_RF_2525E:   return "RT2525e";
+       case RT2560_RF_2526:    return "RT2526";
+       case RT2560_RF_5222:    return "RT5222";
+       default:                return "unknown";
+       }
+}
+
+static void
+rt2560_read_eeprom(struct rt2560_softc *sc)
+{
+       uint16_t val;
+       int i;
+
+       val = rt2560_eeprom_read(sc, RT2560_EEPROM_CONFIG0);
+       sc->rf_rev =   (val >> 11) & 0x7;
+       sc->hw_radio = (val >> 10) & 0x1;
+       sc->led_mode = (val >> 6)  & 0x7;
+       sc->rx_ant =   (val >> 4)  & 0x3;
+       sc->tx_ant =   (val >> 2)  & 0x3;
+       sc->nb_ant =   val & 0x3;
+
+       /* read default values for BBP registers */
+       for (i = 0; i < 16; i++) {
+               val = rt2560_eeprom_read(sc, RT2560_EEPROM_BBP_BASE + i);
+               sc->bbp_prom[i].reg = val >> 8;
+               sc->bbp_prom[i].val = val & 0xff;
+       }
+
+       /* read Tx power for all b/g channels */
+       for (i = 0; i < 14 / 2; i++) {
+               val = rt2560_eeprom_read(sc, RT2560_EEPROM_TXPOWER + i);
+               sc->txpow[i * 2] = val >> 8;
+               sc->txpow[i * 2 + 1] = val & 0xff;
+       }
+}
+
+static int
+rt2560_bbp_init(struct rt2560_softc *sc)
+{
+#define N(a)   (sizeof (a) / sizeof ((a)[0]))
+       int i, ntries;
+
+       /* wait for BBP to be ready */
+       for (ntries = 0; ntries < 100; ntries++) {
+               if (rt2560_bbp_read(sc, RT2560_BBP_VERSION) != 0)
+                       break;
+               DELAY(1);
+       }
+       if (ntries == 100) {
+               device_printf(sc->sc_dev, "timeout waiting for BBP\n");
+               return EIO;
+       }
+
+       /* initialize BBP registers to default values */
+       for (i = 0; i < N(rt2560_def_bbp); i++) {
+               rt2560_bbp_write(sc, rt2560_def_bbp[i].reg,
+                   rt2560_def_bbp[i].val);
+       }
+#if 0
+       /* initialize BBP registers to values stored in EEPROM */
+       for (i = 0; i < 16; i++) {
+               if (sc->bbp_prom[i].reg == 0xff)
+                       continue;
+               rt2560_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val);
+       }
+#endif
+
+       return 0;
+#undef N
+}
+
+static void
+rt2560_set_txantenna(struct rt2560_softc *sc, int antenna)
+{
+       uint32_t tmp;
+       uint8_t tx;
+
+       tx = rt2560_bbp_read(sc, RT2560_BBP_TX) & ~RT2560_BBP_ANTMASK;
+       if (antenna == 1)
+               tx |= RT2560_BBP_ANTA;
+       else if (antenna == 2)
+               tx |= RT2560_BBP_ANTB;
+       else
+               tx |= RT2560_BBP_DIVERSITY;
+
+       /* need to force I/Q flip for RF 2525e, 2526 and 5222 */
+       if (sc->rf_rev == RT2560_RF_2525E || sc->rf_rev == RT2560_RF_2526 ||
+           sc->rf_rev == RT2560_RF_5222)
+               tx |= RT2560_BBP_FLIPIQ;
+
+       rt2560_bbp_write(sc, RT2560_BBP_TX, tx);
+
+       /* update values for CCK and OFDM in BBPCSR1 */
+       tmp = RAL_READ(sc, RT2560_BBPCSR1) & ~0x00070007;
+       tmp |= (tx & 0x7) << 16 | (tx & 0x7);
+       RAL_WRITE(sc, RT2560_BBPCSR1, tmp);
+}
+
+static void
+rt2560_set_rxantenna(struct rt2560_softc *sc, int antenna)
+{
+       uint8_t rx;
+
+       rx = rt2560_bbp_read(sc, RT2560_BBP_RX) & ~RT2560_BBP_ANTMASK;
+       if (antenna == 1)
+               rx |= RT2560_BBP_ANTA;
+       else if (antenna == 2)
+               rx |= RT2560_BBP_ANTB;
+       else
+               rx |= RT2560_BBP_DIVERSITY;
+
+       /* need to force no I/Q flip for RF 2525e and 2526 */
+       if (sc->rf_rev == RT2560_RF_2525E || sc->rf_rev == RT2560_RF_2526)
+               rx &= ~RT2560_BBP_FLIPIQ;
+
+       rt2560_bbp_write(sc, RT2560_BBP_RX, rx);
+}
+
+static void
+rt2560_init(void *priv)
+{
+#define N(a)   (sizeof (a) / sizeof ((a)[0]))
+       struct rt2560_softc *sc = priv;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = ic->ic_ifp;
+       uint32_t tmp;
+       int i;
+
+       rt2560_stop(sc);
+
+       /* setup tx rings */
+       tmp = RT2560_PRIO_RING_COUNT << 24 |
+             RT2560_ATIM_RING_COUNT << 16 |
+             RT2560_TX_RING_COUNT   <<  8 |
+             RT2560_TX_DESC_SIZE;
+
+       /* rings must be initialized in this exact order */
+       RAL_WRITE(sc, RT2560_TXCSR2, tmp);
+       RAL_WRITE(sc, RT2560_TXCSR3, sc->txq.physaddr);
+       RAL_WRITE(sc, RT2560_TXCSR5, sc->prioq.physaddr);
+       RAL_WRITE(sc, RT2560_TXCSR4, sc->atimq.physaddr);
+       RAL_WRITE(sc, RT2560_TXCSR6, sc->bcnq.physaddr);
+
+       /* setup rx ring */
+       tmp = RT2560_RX_RING_COUNT << 8 | RT2560_RX_DESC_SIZE;
+
+       RAL_WRITE(sc, RT2560_RXCSR1, tmp);
+       RAL_WRITE(sc, RT2560_RXCSR2, sc->rxq.physaddr);
+
+       /* initialize MAC registers to default values */
+       for (i = 0; i < N(rt2560_def_mac); i++)
+               RAL_WRITE(sc, rt2560_def_mac[i].reg, rt2560_def_mac[i].val);
+
+       IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
+       rt2560_set_macaddr(sc, ic->ic_myaddr);
+
+       /* set basic rate set (will be updated later) */
+       RAL_WRITE(sc, RT2560_ARSP_PLCP_1, 0x153);
+
+       rt2560_set_txantenna(sc, sc->tx_ant);
+       rt2560_set_rxantenna(sc, sc->rx_ant);
+       rt2560_update_slot(ifp);
+       rt2560_update_plcp(sc);
+       rt2560_update_led(sc, 0, 0);
+
+       RAL_WRITE(sc, RT2560_CSR1, RT2560_RESET_ASIC);
+       RAL_WRITE(sc, RT2560_CSR1, RT2560_HOST_READY);
+
+       if (rt2560_bbp_init(sc) != 0) {
+               rt2560_stop(sc);
+               return;
+       }
+
+       /* set default BSS channel */
+       rt2560_set_chan(sc, ic->ic_curchan);
+
+       /* kick Rx */
+       tmp = RT2560_DROP_PHY_ERROR | RT2560_DROP_CRC_ERROR;
+       if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+               tmp |= RT2560_DROP_CTL | RT2560_DROP_VERSION_ERROR;
+               if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+                       tmp |= RT2560_DROP_TODS;
+               if (!(ifp->if_flags & IFF_PROMISC))
+                       tmp |= RT2560_DROP_NOT_TO_ME;
+       }
+       RAL_WRITE(sc, RT2560_RXCSR0, tmp);
+
+       /* clear old FCS and Rx FIFO errors */
+       RAL_READ(sc, RT2560_CNT0);
+       RAL_READ(sc, RT2560_CNT4);
+
+       /* clear any pending interrupts */
+       RAL_WRITE(sc, RT2560_CSR7, 0xffffffff);
+
+       /* enable interrupts */
+       RAL_WRITE(sc, RT2560_CSR8, RT2560_INTR_MASK);
+
+       ifp->if_flags &= ~IFF_OACTIVE;
+       ifp->if_flags |= IFF_RUNNING;
+
+       /* XXX */
+       if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+               int i;
+
+               ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+               for (i = 0; i < IEEE80211_WEP_NKID; ++i) {
+                       struct ieee80211_key *wk = &ic->ic_nw_keys[i];
+
+                       if (wk->wk_keylen == 0)
+                               continue;
+                       if (wk->wk_flags & IEEE80211_KEY_XMIT)
+                               wk->wk_flags |= IEEE80211_KEY_SWCRYPT;
+               }
+       }
+
+       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);
+#undef N
+}
+
+void
+rt2560_stop(void *priv)
+{
+       struct rt2560_softc *sc = priv;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = ic->ic_ifp;
+
+       sc->sc_tx_timer = 0;
+       ifp->if_timer = 0;
+       ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+
+       ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+
+       /* abort Tx */
+       RAL_WRITE(sc, RT2560_TXCSR0, RT2560_ABORT_TX);
+
+       /* disable Rx */
+       RAL_WRITE(sc, RT2560_RXCSR0, RT2560_DISABLE_RX);
+
+       /* reset ASIC (imply reset BBP) */
+       RAL_WRITE(sc, RT2560_CSR1, RT2560_RESET_ASIC);
+       RAL_WRITE(sc, RT2560_CSR1, 0);
+
+       /* disable interrupts */
+       RAL_WRITE(sc, RT2560_CSR8, 0xffffffff);
+
+       /* reset Tx and Rx rings */
+       rt2560_reset_tx_ring(sc, &sc->txq);
+       rt2560_reset_tx_ring(sc, &sc->atimq);
+       rt2560_reset_tx_ring(sc, &sc->prioq);
+       rt2560_reset_tx_ring(sc, &sc->bcnq);
+       rt2560_reset_rx_ring(sc, &sc->rxq);
+}
+
+static void
+rt2560_dma_map_mbuf(void *arg, bus_dma_segment_t *seg, int nseg,
+                   bus_size_t map_size __unused, int error)
+{
+       if (error)
+               return;
+
+       KASSERT(nseg == 1, ("too many dma segments\n"));
+       *((bus_addr_t *)arg) = seg->ds_addr;
+}
diff --git a/sys/dev/netif/ral/rt2560reg.h b/sys/dev/netif/ral/rt2560reg.h
new file mode 100644 (file)
index 0000000..6b9bde5
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * Copyright (c) 2005, 2006
+ *     Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD: src/sys/dev/ral/rt2560reg.h,v 1.1 2006/03/05 20:36:56 damien Exp $
+ * $DragonFly: src/sys/dev/netif/ral/rt2560reg.h,v 1.1 2006/05/20 09:13:09 sephe Exp $
+ */
+
+#define RT2560_TX_RING_COUNT           48
+#define RT2560_ATIM_RING_COUNT         4
+#define RT2560_PRIO_RING_COUNT         16
+#define RT2560_BEACON_RING_COUNT       1
+#define RT2560_RX_RING_COUNT           32
+
+#define RT2560_TX_DESC_SIZE    (sizeof (struct rt2560_tx_desc))
+#define RT2560_RX_DESC_SIZE    (sizeof (struct rt2560_rx_desc))
+
+#define RT2560_MAX_SCATTER     1
+
+/*
+ * Control and status registers.
+ */
+#define RT2560_CSR0            0x0000  /* ASIC version number */
+#define RT2560_CSR1            0x0004  /* System control */
+#define RT2560_CSR3            0x000c  /* STA MAC address 0 */
+#define RT2560_CSR4            0x0010  /* STA MAC address 1 */
+#define RT2560_CSR5            0x0014  /* BSSID 0 */
+#define RT2560_CSR6            0x0018  /* BSSID 1 */
+#define RT2560_CSR7            0x001c  /* Interrupt source */
+#define RT2560_CSR8            0x0020  /* Interrupt mask */
+#define RT2560_CSR9            0x0024  /* Maximum frame length */
+#define RT2560_SECCSR0         0x0028  /* WEP control */
+#define RT2560_CSR11           0x002c  /* Back-off control */
+#define RT2560_CSR12           0x0030  /* Synchronization configuration 0 */
+#define RT2560_CSR13           0x0034  /* Synchronization configuration 1 */
+#define RT2560_CSR14           0x0038  /* Synchronization control */
+#define RT2560_CSR15           0x003c  /* Synchronization status */
+#define RT2560_CSR16           0x0040  /* TSF timer 0 */
+#define RT2560_CSR17           0x0044  /* TSF timer 1 */
+#define RT2560_CSR18           0x0048  /* IFS timer 0 */
+#define RT2560_CSR19           0x004c  /* IFS timer 1 */
+#define RT2560_CSR20           0x0050  /* WAKEUP timer */
+#define RT2560_CSR21           0x0054  /* EEPROM control */
+#define RT2560_CSR22           0x0058  /* CFP control */
+#define RT2560_TXCSR0          0x0060  /* TX control */
+#define RT2560_TXCSR1          0x0064  /* TX configuration */
+#define RT2560_TXCSR2          0x0068  /* TX descriptor configuration */
+#define RT2560_TXCSR3          0x006c  /* TX ring base address */
+#define RT2560_TXCSR4          0x0070  /* TX ATIM ring base address */
+#define RT2560_TXCSR5          0x0074  /* TX PRIO ring base address */
+#define RT2560_TXCSR6          0x0078  /* Beacon base address */
+#define RT2560_TXCSR7          0x007c  /* AutoResponder control */
+#define RT2560_RXCSR0          0x0080  /* RX control */
+#define RT2560_RXCSR1          0x0084  /* RX descriptor configuration */
+#define RT2560_RXCSR2          0x0088  /* RX ring base address */
+#define RT2560_PCICSR          0x008c  /* PCI control */
+#define RT2560_RXCSR3          0x0090  /* BBP ID 0 */
+#define RT2560_TXCSR9          0x0094  /* OFDM TX BBP */
+#define RT2560_ARSP_PLCP_0     0x0098  /* Auto Responder PLCP address */
+#define RT2560_ARSP_PLCP_1     0x009c  /* Auto Responder Basic Rate mask */
+#define RT2560_CNT0            0x00a0  /* FCS error counter */
+#define RT2560_CNT1            0x00ac  /* PLCP error counter */
+#define RT2560_CNT2            0x00b0  /* Long error counter */
+#define RT2560_CNT3            0x00b8  /* CCA false alarm counter */
+#define RT2560_CNT4            0x00bc  /* RX FIFO Overflow counter */
+#define RT2560_CNT5            0x00c0  /* Tx FIFO Underrun counter */
+#define RT2560_PWRCSR0         0x00c4  /* Power mode configuration */
+#define RT2560_PSCSR0          0x00c8  /* Power state transition time */
+#define RT2560_PSCSR1          0x00cc  /* Power state transition time */
+#define RT2560_PSCSR2          0x00d0  /* Power state transition time */
+#define RT2560_PSCSR3          0x00d4  /* Power state transition time */
+#define RT2560_PWRCSR1         0x00d8  /* Manual power control/status */
+#define RT2560_TIMECSR         0x00dc  /* Timer control */
+#define RT2560_MACCSR0         0x00e0  /* MAC configuration */
+#define RT2560_MACCSR1         0x00e4  /* MAC configuration */
+#define RT2560_RALINKCSR       0x00e8  /* Ralink RX auto-reset BBCR */
+#define RT2560_BCNCSR          0x00ec  /* Beacon interval control */
+#define RT2560_BBPCSR          0x00f0  /* BBP serial control */
+#define RT2560_RFCSR           0x00f4  /* RF serial control */
+#define RT2560_LEDCSR          0x00f8  /* LED control */
+#define RT2560_SECCSR3         0x00fc  /* XXX not documented */
+#define RT2560_DMACSR0         0x0100  /* Current RX ring address */
+#define RT2560_DMACSR1         0x0104  /* Current Tx ring address */
+#define RT2560_DMACSR2         0x0104  /* Current Priority ring address */
+#define RT2560_DMACSR3         0x0104  /* Current ATIM ring address */
+#define RT2560_TXACKCSR0       0x0110  /* XXX not documented */
+#define RT2560_GPIOCSR         0x0120  /* */
+#define RT2560_BBBPPCSR                0x0124  /* BBP Pin Control */
+#define RT2560_FIFOCSR0                0x0128  /* TX FIFO pointer */
+#define RT2560_FIFOCSR1                0x012c  /* RX FIFO pointer */
+#define RT2560_BCNOCSR         0x0130  /* Beacon time offset */
+#define RT2560_RLPWCSR         0x0134  /* RX_PE Low Width */
+#define RT2560_TESTCSR         0x0138  /* Test Mode Select */
+#define RT2560_PLCP1MCSR       0x013c  /* Signal/Service/Length of ACK @1M */
+#define RT2560_PLCP2MCSR       0x0140  /* Signal/Service/Length of ACK @2M */
+#define RT2560_PLCP5p5MCSR     0x0144  /* Signal/Service/Length of ACK @5.5M */
+#define RT2560_PLCP11MCSR      0x0148  /* Signal/Service/Length of ACK @11M */
+#define RT2560_ACKPCTCSR       0x014c  /* ACK/CTS padload consume time */
+#define RT2560_ARTCSR1         0x0150  /* ACK/CTS padload consume time */
+#define RT2560_ARTCSR2         0x0154  /* ACK/CTS padload consume time */
+#define RT2560_SECCSR1         0x0158  /* WEP control */
+#define RT2560_BBPCSR1         0x015c  /* BBP TX Configuration */
+
+
+/* possible flags for register RXCSR0 */
+#define RT2560_DISABLE_RX              (1 << 0)
+#define RT2560_DROP_CRC_ERROR          (1 << 1)
+#define RT2560_DROP_PHY_ERROR          (1 << 2)
+#define RT2560_DROP_CTL                        (1 << 3)
+#define RT2560_DROP_NOT_TO_ME          (1 << 4)
+#define RT2560_DROP_TODS               (1 << 5)
+#define RT2560_DROP_VERSION_ERROR      (1 << 6)
+
+/* possible flags for register CSR1 */
+#define RT2560_RESET_ASIC      (1 << 0)
+#define RT2560_RESET_BBP       (1 << 1)
+#define RT2560_HOST_READY      (1 << 2)
+
+/* possible flags for register CSR14 */
+#define RT2560_ENABLE_TSF              (1 << 0)
+#define RT2560_ENABLE_TSF_SYNC(x)      (((x) & 0x3) << 1)
+#define RT2560_ENABLE_TBCN             (1 << 3)
+#define RT2560_ENABLE_BEACON_GENERATOR (1 << 6)
+
+/* possible flags for register CSR21 */
+#define RT2560_C       (1 << 1)
+#define RT2560_S       (1 << 2)
+#define RT2560_D       (1 << 3)
+#define RT2560_Q       (1 << 4)
+#define RT2560_93C46   (1 << 5)
+
+#define RT2560_SHIFT_D 3
+#define RT2560_SHIFT_Q 4
+
+/* possible flags for register TXCSR0 */
+#define RT2560_KICK_TX         (1 << 0)
+#define RT2560_KICK_ATIM       (1 << 1)
+#define RT2560_KICK_PRIO       (1 << 2)
+#define RT2560_ABORT_TX                (1 << 3)
+
+/* possible flags for register SECCSR0 */
+#define RT2560_KICK_DECRYPT    (1 << 0)
+
+/* possible flags for register SECCSR1 */
+#define RT2560_KICK_ENCRYPT    (1 << 0)
+
+/* possible flags for register CSR7 */
+#define RT2560_BEACON_EXPIRE   0x00000001
+#define RT2560_WAKEUP_EXPIRE   0x00000002
+#define RT2560_ATIM_EXPIRE     0x00000004
+#define RT2560_TX_DONE         0x00000008
+#define RT2560_ATIM_DONE       0x00000010
+#define RT2560_PRIO_DONE       0x00000020
+#define RT2560_RX_DONE         0x00000040
+#define RT2560_DECRYPTION_DONE 0x00000080
+#define RT2560_ENCRYPTION_DONE 0x00000100
+
+#define RT2560_INTR_MASK                                                       \
+       (~(RT2560_BEACON_EXPIRE | RT2560_WAKEUP_EXPIRE | RT2560_TX_DONE |       \
+          RT2560_PRIO_DONE | RT2560_RX_DONE | RT2560_DECRYPTION_DONE |         \
+          RT2560_ENCRYPTION_DONE))
+
+/* Tx descriptor */
+struct rt2560_tx_desc {
+       uint32_t        flags;
+#define RT2560_TX_BUSY                 (1 << 0)
+#define RT2560_TX_VALID                        (1 << 1)
+
+#define RT2560_TX_RESULT_MASK          0x0000001c
+#define RT2560_TX_SUCCESS              (0 << 2)
+#define RT2560_TX_SUCCESS_RETRY                (1 << 2)
+#define RT2560_TX_FAIL_RETRY           (2 << 2)
+#define RT2560_TX_FAIL_INVALID         (3 << 2)
+#define RT2560_TX_FAIL_OTHER           (4 << 2)
+
+#define RT2560_TX_MORE_FRAG            (1 << 8)
+#define RT2560_TX_ACK                  (1 << 9)
+#define RT2560_TX_TIMESTAMP            (1 << 10)
+#define RT2560_TX_OFDM                 (1 << 11)
+#define RT2560_TX_CIPHER_BUSY          (1 << 12)
+
+#define RT2560_TX_IFS_MASK             0x00006000
+#define RT2560_TX_IFS_BACKOFF          (0 << 13)
+#define RT2560_TX_IFS_SIFS             (1 << 13)
+#define RT2560_TX_IFS_NEWBACKOFF       (2 << 13)
+#define RT2560_TX_IFS_NONE             (3 << 13)
+
+#define RT2560_TX_LONG_RETRY           (1 << 15)
+
+#define RT2560_TX_CIPHER_MASK          0xe0000000
+#define RT2560_TX_CIPHER_NONE          (0 << 29)
+#define RT2560_TX_CIPHER_WEP40         (1 << 29)
+#define RT2560_TX_CIPHER_WEP104                (2 << 29)
+#define RT2560_TX_CIPHER_TKIP          (3 << 29)
+#define RT2560_TX_CIPHER_AES           (4 << 29)
+
+       uint32_t        physaddr;
+       uint16_t        wme;
+#define RT2560_LOGCWMAX(x)     (((x) & 0xf) << 12)
+#define RT2560_LOGCWMIN(x)     (((x) & 0xf) << 8)
+#define RT2560_AIFSN(x)                (((x) & 0x3) << 6)
+#define RT2560_IVOFFSET(x)     (((x) & 0x3f))
+
+       uint16_t        reserved1;
+       uint8_t         plcp_signal;
+       uint8_t         plcp_service;
+#define RT2560_PLCP_LENGEXT    0x80
+
+       uint8_t         plcp_length_lo;
+       uint8_t         plcp_length_hi;
+       uint32_t        iv;
+       uint32_t        eiv;
+       uint8_t         key[IEEE80211_KEYBUF_SIZE];
+       uint32_t        reserved2[2];
+} __packed;
+
+/* Rx descriptor */
+struct rt2560_rx_desc {
+       uint32_t        flags;
+#define RT2560_RX_BUSY         (1 << 0)
+#define RT2560_RX_CRC_ERROR    (1 << 5)
+#define RT2560_RX_OFDM         (1 << 6)
+#define RT2560_RX_PHY_ERROR    (1 << 7)
+#define RT2560_RX_CIPHER_BUSY  (1 << 8)
+#define RT2560_RX_ICV_ERROR    (1 << 9)
+
+#define RT2560_RX_CIPHER_MASK  0xe0000000
+#define RT2560_RX_CIPHER_NONE  (0 << 29)
+#define RT2560_RX_CIPHER_WEP40 (1 << 29)
+#define RT2560_RX_CIPHER_WEP104        (2 << 29)
+#define RT2560_RX_CIPHER_TKIP  (3 << 29)
+#define RT2560_RX_CIPHER_AES   (4 << 29)
+
+       uint32_t        physaddr;
+       uint8_t         rate;
+       uint8_t         rssi;
+       uint8_t         ta[IEEE80211_ADDR_LEN];
+       uint32_t        iv;
+       uint32_t        eiv;
+       uint8_t         key[IEEE80211_KEYBUF_SIZE];
+       uint32_t        reserved[2];
+} __packed;
+
+#define RAL_RF1        0
+#define RAL_RF2        2
+#define RAL_RF3        1
+#define RAL_RF4        3
+
+#define RT2560_RF1_AUTOTUNE    0x08000
+#define RT2560_RF3_AUTOTUNE    0x00040
+
+#define RT2560_BBP_BUSY                (1 << 15)
+#define RT2560_BBP_WRITE       (1 << 16)
+#define RT2560_RF_20BIT                (20 << 24)
+#define RT2560_RF_BUSY         (1 << 31)
+
+#define RT2560_RF_2522 0x00
+#define RT2560_RF_2523 0x01
+#define RT2560_RF_2524 0x02
+#define RT2560_RF_2525 0x03
+#define RT2560_RF_2525E        0x04
+#define RT2560_RF_2526 0x05
+/* dual-band RF */
+#define RT2560_RF_5222 0x10
+
+#define RT2560_BBP_VERSION     0
+#define RT2560_BBP_TX          2
+#define RT2560_BBP_RX          14
+
+#define RT2560_BBP_ANTA                0x00
+#define RT2560_BBP_DIVERSITY   0x01
+#define RT2560_BBP_ANTB                0x02
+#define RT2560_BBP_ANTMASK     0x03
+#define RT2560_BBP_FLIPIQ      0x04
+
+#define RT2560_LED_MODE_DEFAULT                0
+#define RT2560_LED_MODE_TXRX_ACTIVITY  1
+#define RT2560_LED_MODE_SINGLE         2
+#define RT2560_LED_MODE_ASUS           3
+
+#define RT2560_JAPAN_FILTER    0x8
+
+#define RT2560_EEPROM_DELAY    1       /* minimum hold time (microsecond) */
+
+#define RT2560_EEPROM_CONFIG0  16
+#define RT2560_EEPROM_BBP_BASE 19
+#define RT2560_EEPROM_TXPOWER  35
+
+/*
+ * control and status registers access macros
+ */
+#define RAL_READ(sc, reg)                                              \
+       bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg))
+
+#define RAL_WRITE(sc, reg, val)                                                \
+       bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val))
+
+/*
+ * EEPROM access macro
+ */
+#define RT2560_EEPROM_CTL(sc, val) do {                                        \
+       RAL_WRITE((sc), RT2560_CSR21, (val));                           \
+       DELAY(RT2560_EEPROM_DELAY);                                     \
+} while (/* CONSTCOND */0)
+
+/*
+ * Default values for MAC registers; values taken from the reference driver.
+ */
+#define RT2560_DEF_MAC                         \
+       { RT2560_PSCSR0,      0x00020002 },     \
+       { RT2560_PSCSR1,      0x00000002 },     \
+       { RT2560_PSCSR2,      0x00020002 },     \
+       { RT2560_PSCSR3,      0x00000002 },     \
+       { RT2560_TIMECSR,     0x00003f21 },     \
+       { RT2560_CSR9,        0x00000780 },     \
+       { RT2560_CSR11,       0x07041483 },     \
+       { RT2560_CNT3,        0x00000000 },     \
+       { RT2560_TXCSR1,      0x07614562 },     \
+       { RT2560_ARSP_PLCP_0, 0x8c8d8b8a },     \
+       { RT2560_ACKPCTCSR,   0x7038140a },     \
+       { RT2560_ARTCSR1,     0x1d21252d },     \
+       { RT2560_ARTCSR2,     0x1919191d },     \
+       { RT2560_RXCSR0,      0xffffffff },     \
+       { RT2560_RXCSR3,      0xb3aab3af },     \
+       { RT2560_PCICSR,      0x000003b8 },     \
+       { RT2560_PWRCSR0,     0x3f3b3100 },     \
+       { RT2560_GPIOCSR,     0x0000ff00 },     \
+       { RT2560_TESTCSR,     0x000000f0 },     \
+       { RT2560_PWRCSR1,     0x000001ff },     \
+       { RT2560_MACCSR0,     0x00213223 },     \
+       { RT2560_MACCSR1,     0x00235518 },     \
+       { RT2560_RLPWCSR,     0x00000040 },     \
+       { RT2560_RALINKCSR,   0x9a009a11 },     \
+       { RT2560_CSR7,        0xffffffff },     \
+       { RT2560_BBPCSR1,     0x82188200 },     \
+       { RT2560_TXACKCSR0,   0x00000020 },     \
+       { RT2560_SECCSR3,     0x0000e78f }
+
+/*
+ * Default values for BBP registers; values taken from the reference driver.
+ */
+#define RT2560_DEF_BBP \
+       {  3, 0x02 },   \
+       {  4, 0x19 },   \
+       { 14, 0x1c },   \
+       { 15, 0x30 },   \
+       { 16, 0xac },   \
+       { 17, 0x48 },   \
+       { 18, 0x18 },   \
+       { 19, 0xff },   \
+       { 20, 0x1e },   \
+       { 21, 0x08 },   \
+       { 22, 0x08 },   \
+       { 23, 0x08 },   \
+       { 24, 0x80 },   \
+       { 25, 0x50 },   \
+       { 26, 0x08 },   \
+       { 27, 0x23 },   \
+       { 30, 0x10 },   \
+       { 31, 0x2b },   \
+       { 32, 0xb9 },   \
+       { 34, 0x12 },   \
+       { 35, 0x50 },   \
+       { 39, 0xc4 },   \
+       { 40, 0x02 },   \
+       { 41, 0x60 },   \
+       { 53, 0x10 },   \
+       { 54, 0x18 },   \
+       { 56, 0x08 },   \
+       { 57, 0x10 },   \
+       { 58, 0x08 },   \
+       { 61, 0x60 },   \
+       { 62, 0x10 },   \
+       { 75, 0xff }
+
+/*
+ * Default values for RF register R2 indexed by channel numbers; values taken
+ * from the reference driver.
+ */
+#define RT2560_RF2522_R2                                               \
+{                                                                      \
+       0x307f6, 0x307fb, 0x30800, 0x30805, 0x3080a, 0x3080f, 0x30814,  \
+       0x30819, 0x3081e, 0x30823, 0x30828, 0x3082d, 0x30832, 0x3083e   \
+}
+
+#define RT2560_RF2523_R2                                               \
+{                                                                      \
+       0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d,  \
+       0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346   \
+}
+
+#define RT2560_RF2524_R2                                               \
+{                                                                      \
+       0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d,  \
+       0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346   \
+}
+
+#define RT2560_RF2525_R2                                               \
+{                                                                      \
+       0x20327, 0x20328, 0x20329, 0x2032a, 0x2032b, 0x2032c, 0x2032d,  \
+       0x2032e, 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20346   \
+}
+
+#define RT2560_RF2525_HI_R2                                            \
+{                                                                      \
+       0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20344, 0x20345,  \
+       0x20346, 0x20347, 0x20348, 0x20349, 0x2034a, 0x2034b, 0x2034e   \
+}
+
+#define RT2560_RF2525E_R2                                              \
+{                                                                      \
+       0x2044d, 0x2044e, 0x2044f, 0x20460, 0x20461, 0x20462, 0x20463,  \
+       0x20464, 0x20465, 0x20466, 0x20467, 0x20468, 0x20469, 0x2046b   \
+}
+
+#define RT2560_RF2526_HI_R2                                            \
+{                                                                      \
+       0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d, 0x0022d,  \
+       0x0022e, 0x0022e, 0x0022f, 0x0022d, 0x00240, 0x00240, 0x00241   \
+}
+
+#define RT2560_RF2526_R2                                               \
+{                                                                      \
+       0x00226, 0x00227, 0x00227, 0x00228, 0x00228, 0x00229, 0x00229,  \
+       0x0022a, 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d   \
+}
+
+/*
+ * For dual-band RF, RF registers R1 and R4 also depend on channel number;
+ * values taken from the reference driver.
+ */
+#define RT2560_RF5222                          \
+       {   1, 0x08808, 0x0044d, 0x00282 },     \
+       {   2, 0x08808, 0x0044e, 0x00282 },     \
+       {   3, 0x08808, 0x0044f, 0x00282 },     \
+       {   4, 0x08808, 0x00460, 0x00282 },     \
+       {   5, 0x08808, 0x00461, 0x00282 },     \
+       {   6, 0x08808, 0x00462, 0x00282 },     \
+       {   7, 0x08808, 0x00463, 0x00282 },     \
+       {   8, 0x08808, 0x00464, 0x00282 },     \
+       {   9, 0x08808, 0x00465, 0x00282 },     \
+       {  10, 0x08808, 0x00466, 0x00282 },     \
+       {  11, 0x08808, 0x00467, 0x00282 },     \
+       {  12, 0x08808, 0x00468, 0x00282 },     \
+       {  13, 0x08808, 0x00469, 0x00282 },     \
+       {  14, 0x08808, 0x0046b, 0x00286 },     \
+                                               \
+       {  36, 0x08804, 0x06225, 0x00287 },     \
+       {  40, 0x08804, 0x06226, 0x00287 },     \
+       {  44, 0x08804, 0x06227, 0x00287 },     \
+       {  48, 0x08804, 0x06228, 0x00287 },     \
+       {  52, 0x08804, 0x06229, 0x00287 },     \
+       {  56, 0x08804, 0x0622a, 0x00287 },     \
+       {  60, 0x08804, 0x0622b, 0x00287 },     \
+       {  64, 0x08804, 0x0622c, 0x00287 },     \
+                                               \
+       { 100, 0x08804, 0x02200, 0x00283 },     \
+       { 104, 0x08804, 0x02201, 0x00283 },     \
+       { 108, 0x08804, 0x02202, 0x00283 },     \
+       { 112, 0x08804, 0x02203, 0x00283 },     \
+       { 116, 0x08804, 0x02204, 0x00283 },     \
+       { 120, 0x08804, 0x02205, 0x00283 },     \
+       { 124, 0x08804, 0x02206, 0x00283 },     \
+       { 128, 0x08804, 0x02207, 0x00283 },     \
+       { 132, 0x08804, 0x02208, 0x00283 },     \
+       { 136, 0x08804, 0x02209, 0x00283 },     \
+       { 140, 0x08804, 0x0220a, 0x00283 },     \
+                                               \
+       { 149, 0x08808, 0x02429, 0x00281 },     \
+       { 153, 0x08808, 0x0242b, 0x00281 },     \
+       { 157, 0x08808, 0x0242d, 0x00281 },     \
+       { 161, 0x08808, 0x0242f, 0x00281 }
diff --git a/sys/dev/netif/ral/rt2560var.h b/sys/dev/netif/ral/rt2560var.h
new file mode 100644 (file)
index 0000000..a9c8c3e
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2005, 2006
+ *     Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD: src/sys/dev/ral/rt2560var.h,v 1.1 2006/03/05 20:36:56 damien Exp $
+ * $DragonFly: src/sys/dev/netif/ral/rt2560var.h,v 1.1 2006/05/20 09:13:09 sephe Exp $
+ */
+
+struct rt2560_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_antenna;
+       uint8_t         wr_antsignal;
+};
+
+#define RT2560_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_DB_ANTSIGNAL))
+
+struct rt2560_tx_radiotap_header {
+       struct ieee80211_radiotap_header wt_ihdr;
+       uint8_t         wt_flags;
+       uint8_t         wt_rate;
+       uint16_t        wt_chan_freq;
+       uint16_t        wt_chan_flags;
+       uint8_t         wt_antenna;
+};
+
+#define RT2560_TX_RADIOTAP_PRESENT                                     \
+       ((1 << IEEE80211_RADIOTAP_FLAGS) |                              \
+        (1 << IEEE80211_RADIOTAP_RATE) |                               \
+        (1 << IEEE80211_RADIOTAP_CHANNEL) |                            \
+        (1 << IEEE80211_RADIOTAP_ANTENNA))
+
+struct rt2560_tx_data {
+       bus_dmamap_t                    map;
+       struct mbuf                     *m;
+       struct ieee80211_node           *ni;
+       struct ral_rssdesc              id;
+};
+
+struct rt2560_tx_ring {
+       bus_dma_tag_t           desc_dmat;
+       bus_dma_tag_t           data_dmat;
+       bus_dmamap_t            desc_map;
+       bus_addr_t              physaddr;
+       struct rt2560_tx_desc   *desc;
+       struct rt2560_tx_data   *data;
+       int                     count;
+       int                     queued;
+       int                     cur;
+       int                     next;
+       int                     cur_encrypt;
+       int                     next_encrypt;
+};
+
+struct rt2560_rx_data {
+       bus_dmamap_t    map;
+       struct mbuf     *m;
+       int             drop;
+};
+
+struct rt2560_rx_ring {
+       bus_dma_tag_t           desc_dmat;
+       bus_dma_tag_t           data_dmat;
+       bus_dmamap_t            desc_map;
+       bus_addr_t              physaddr;
+       struct rt2560_rx_desc   *desc;
+       struct rt2560_rx_data   *data;
+       int                     count;
+       int                     cur;
+       int                     next;
+       int                     cur_decrypt;
+};
+
+struct rt2560_node {
+       struct ieee80211_node   ni;
+       struct ral_rssadapt     rssadapt;
+};
+
+struct rt2560_softc {
+       /*
+        * NOTE: following four fields MUST be in the
+        * same order as in rt2661_softc
+        */
+       struct ieee80211com     sc_ic;
+       bus_space_tag_t         sc_st;
+       bus_space_handle_t      sc_sh;
+       device_t                sc_dev;
+
+       int                     sc_irq_rid;
+       struct resource         *sc_irq;
+       void                    *sc_ih;
+
+       int                     (*sc_newstate)(struct ieee80211com *,
+                                   enum ieee80211_state, int);
+
+       struct callout          scan_ch;
+       struct callout          rssadapt_ch;
+
+       int                     sc_tx_timer;
+
+       uint32_t                asic_rev;
+       uint32_t                eeprom_rev;
+       uint8_t                 rf_rev;
+
+       struct rt2560_tx_ring   txq;
+       struct rt2560_tx_ring   prioq;
+       struct rt2560_tx_ring   atimq;
+       struct rt2560_tx_ring   bcnq;
+       struct rt2560_rx_ring   rxq;
+
+       struct ieee80211_beacon_offsets sc_bo;
+
+       uint32_t                rf_regs[4];
+       uint8_t                 txpow[14];
+
+       struct {
+               uint8_t reg;
+               uint8_t val;
+       }                       bbp_prom[16];
+
+       int                     led_mode;
+       int                     hw_radio;
+       int                     rx_ant;
+       int                     tx_ant;
+       int                     nb_ant;
+
+       int                     dwelltime;
+
+       struct bpf_if           *sc_drvbpf;
+
+       union {
+               struct rt2560_rx_radiotap_header th;
+               uint8_t pad[64];
+       }                       sc_rxtapu;
+#define sc_rxtap       sc_rxtapu.th
+       int                     sc_rxtap_len;
+
+       union {
+               struct rt2560_tx_radiotap_header th;
+               uint8_t pad[64];
+       }                       sc_txtapu;
+#define sc_txtap       sc_txtapu.th
+       int                     sc_txtap_len;
+
+       struct sysctl_ctx_list  sysctl_ctx;
+       struct sysctl_oid       *sysctl_tree;
+};
+
+int    rt2560_attach(device_t, int);
+int    rt2560_detach(void *);
+void   rt2560_shutdown(void *);
+void   rt2560_suspend(void *);
+void   rt2560_resume(void *);
diff --git a/sys/dev/netif/ral/rt2661.c b/sys/dev/netif/ral/rt2661.c
new file mode 100644 (file)
index 0000000..34b9f26
--- /dev/null
@@ -0,0 +1,2948 @@
+/*
+ * Copyright (c) 2006
+ *     Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD: src/sys/dev/ral/rt2661.c,v 1.4 2006/03/21 21:15:43 damien Exp $
+ * $DragonFly: src/sys/dev/netif/ral/rt2661.c,v 1.1 2006/05/20 09:13:09 sephe Exp $
+ */
+
+/*
+ * Ralink Technology RT2561, RT2561S and RT2661 chipset driver
+ * http://www.ralinktech.com/
+ */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/clock.h>
+#include <sys/rman.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/ifq_var.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+#include <netproto/802_11/ieee80211_radiotap.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+
+#include <dev/netif/ral/if_ralrate.h>
+#include <dev/netif/ral/rt2661reg.h>
+#include <dev/netif/ral/rt2661var.h>
+#include <dev/netif/ral/rt2661_ucode.h>
+
+#ifdef RAL_DEBUG
+#define DPRINTF(x)     do { if (ral_debug > 0) printf x; } while (0)
+#define DPRINTFN(n, x) do { if (ral_debug >= (n)) printf x; } while (0)
+int ral_debug = 0;
+SYSCTL_INT(_debug, OID_AUTO, ral, CTLFLAG_RW, &ral_debug, 0, "ral debug level");
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n, x)
+#endif
+
+static void            rt2661_dma_map_addr(void *, bus_dma_segment_t *, int,
+                           int);
+static void            rt2661_dma_map_mbuf(void *, bus_dma_segment_t *, int,
+                                           bus_size_t, int);
+static int             rt2661_alloc_tx_ring(struct rt2661_softc *,
+                           struct rt2661_tx_ring *, int);
+static void            rt2661_reset_tx_ring(struct rt2661_softc *,
+                           struct rt2661_tx_ring *);
+static void            rt2661_free_tx_ring(struct rt2661_softc *,
+                           struct rt2661_tx_ring *);
+static int             rt2661_alloc_rx_ring(struct rt2661_softc *,
+                           struct rt2661_rx_ring *, int);
+static void            rt2661_reset_rx_ring(struct rt2661_softc *,
+                           struct rt2661_rx_ring *);
+static void            rt2661_free_rx_ring(struct rt2661_softc *,
+                           struct rt2661_rx_ring *);
+static struct          ieee80211_node *rt2661_node_alloc(
+                           struct ieee80211_node_table *);
+static int             rt2661_media_change(struct ifnet *);
+static void            rt2661_next_scan(void *);
+static int             rt2661_newstate(struct ieee80211com *,
+                           enum ieee80211_state, int);
+static uint16_t                rt2661_eeprom_read(struct rt2661_softc *, uint8_t);
+static void            rt2661_rx_intr(struct rt2661_softc *);
+static void            rt2661_tx_intr(struct rt2661_softc *);
+static void            rt2661_tx_dma_intr(struct rt2661_softc *,
+                           struct rt2661_tx_ring *);
+static void            rt2661_mcu_beacon_expire(struct rt2661_softc *);
+static void            rt2661_mcu_wakeup(struct rt2661_softc *);
+static void            rt2661_mcu_cmd_intr(struct rt2661_softc *);
+static int             rt2661_ack_rate(struct ieee80211com *, int);
+static uint16_t                rt2661_txtime(int, int, uint32_t);
+static uint8_t         rt2661_rxrate(struct rt2661_rx_desc *);
+static uint8_t         rt2661_plcp_signal(int);
+static void            rt2661_setup_tx_desc(struct rt2661_softc *,
+                           struct rt2661_tx_desc *, uint32_t, uint16_t, int,
+                           int, const bus_dma_segment_t *, int, int);
+static struct mbuf *   rt2661_get_rts(struct rt2661_softc *,
+                           struct ieee80211_frame *, uint16_t);
+static int             rt2661_tx_data(struct rt2661_softc *, struct mbuf *,
+                           struct ieee80211_node *, int);
+static int             rt2661_tx_mgt(struct rt2661_softc *, struct mbuf *,
+                           struct ieee80211_node *);
+static void            rt2661_start(struct ifnet *);
+static void            rt2661_watchdog(struct ifnet *);
+static int             rt2661_reset(struct ifnet *);
+static int             rt2661_ioctl(struct ifnet *, u_long, caddr_t,
+                                    struct ucred *);
+static void            rt2661_bbp_write(struct rt2661_softc *, uint8_t,
+                           uint8_t);
+static uint8_t         rt2661_bbp_read(struct rt2661_softc *, uint8_t);
+static void            rt2661_rf_write(struct rt2661_softc *, uint8_t,
+                           uint32_t);
+static int             rt2661_tx_cmd(struct rt2661_softc *, uint8_t,
+                           uint16_t);
+static void            rt2661_select_antenna(struct rt2661_softc *);
+static void            rt2661_enable_mrr(struct rt2661_softc *);
+static void            rt2661_set_txpreamble(struct rt2661_softc *);
+static void            rt2661_set_basicrates(struct rt2661_softc *,
+                           const struct ieee80211_rateset *);
+static void            rt2661_select_band(struct rt2661_softc *,
+                           struct ieee80211_channel *);
+static void            rt2661_set_chan(struct rt2661_softc *,
+                           struct ieee80211_channel *);
+static void            rt2661_set_bssid(struct rt2661_softc *,
+                           const uint8_t *);
+static void            rt2661_set_macaddr(struct rt2661_softc *,
+                          const uint8_t *);
+static void            rt2661_update_promisc(struct rt2661_softc *);
+static int             rt2661_wme_update(struct ieee80211com *) __unused;
+static void            rt2661_update_slot(struct ifnet *);
+static const char      *rt2661_get_rf(int);
+static void            rt2661_read_eeprom(struct rt2661_softc *);
+static int             rt2661_bbp_init(struct rt2661_softc *);
+static void            rt2661_init(void *);
+static void            rt2661_stop(void *);
+static void            rt2661_intr(void *);
+static int             rt2661_load_microcode(struct rt2661_softc *,
+                           const uint8_t *, int);
+#ifdef notyet
+static void            rt2661_rx_tune(struct rt2661_softc *);
+static void            rt2661_radar_start(struct rt2661_softc *);
+static int             rt2661_radar_stop(struct rt2661_softc *);
+#endif
+static int             rt2661_prepare_beacon(struct rt2661_softc *);
+static void            rt2661_enable_tsf_sync(struct rt2661_softc *);
+static int             rt2661_get_rssi(struct rt2661_softc *, uint8_t);
+
+/*
+ * Supported rates for 802.11a/b/g modes (in 500Kbps unit).
+ */
+static const struct ieee80211_rateset rt2661_rateset_11a =
+       { 8, { 12, 18, 24, 36, 48, 72, 96, 108 } };
+
+static const struct ieee80211_rateset rt2661_rateset_11b =
+       { 4, { 2, 4, 11, 22 } };
+
+static const struct ieee80211_rateset rt2661_rateset_11g =
+       { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
+
+static const struct {
+       uint32_t        reg;
+       uint32_t        val;
+} rt2661_def_mac[] = {
+       RT2661_DEF_MAC
+};
+
+static const struct {
+       uint8_t reg;
+       uint8_t val;
+} rt2661_def_bbp[] = {
+       RT2661_DEF_BBP
+};
+
+static const struct rfprog {
+       uint8_t         chan;
+       uint32_t        r1, r2, r3, r4;
+}  rt2661_rf5225_1[] = {
+       RT2661_RF5225_1
+}, rt2661_rf5225_2[] = {
+       RT2661_RF5225_2
+};
+
+struct rt2661_dmamap {
+       bus_dma_segment_t       segs[RT2661_MAX_SCATTER];
+       int                     nseg;
+};
+
+int
+rt2661_attach(device_t dev, int id)
+{
+       struct rt2661_softc *sc = device_get_softc(dev);
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+       uint32_t val;
+       const uint8_t *ucode = NULL;
+       int error, i, ac, ntries, size = 0;
+
+       callout_init(&sc->scan_ch);
+       callout_init(&sc->rssadapt_ch);
+
+       sc->sc_irq_rid = 0;
+       sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irq_rid,
+                                           RF_ACTIVE | RF_SHAREABLE);
+       if (sc->sc_irq == NULL) {
+               device_printf(dev, "could not allocate interrupt resource\n");
+               return ENXIO;
+       }
+
+       /* wait for NIC to initialize */
+       for (ntries = 0; ntries < 1000; ntries++) {
+               if ((val = RAL_READ(sc, RT2661_MAC_CSR0)) != 0)
+                       break;
+               DELAY(1000);
+       }
+       if (ntries == 1000) {
+               device_printf(sc->sc_dev,
+                   "timeout waiting for NIC to initialize\n");
+               error = EIO;
+               goto fail;
+       }
+
+       /* retrieve RF rev. no and various other things from EEPROM */
+       rt2661_read_eeprom(sc);
+
+       device_printf(dev, "MAC/BBP RT%X, RF %s\n", val,
+           rt2661_get_rf(sc->rf_rev));
+
+       /*
+        * Load 8051 microcode into NIC.
+        */
+       switch (id) {
+       case 0x0301:
+               ucode = rt2561s_ucode;
+               size = sizeof rt2561s_ucode;
+               break;
+       case 0x0302:
+               ucode = rt2561_ucode;
+               size = sizeof rt2561_ucode;
+               break;
+       case 0x0401:
+               ucode = rt2661_ucode;
+               size = sizeof rt2661_ucode;
+               break;
+       }
+
+       error = rt2661_load_microcode(sc, ucode, size);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not load 8051 microcode\n");
+               goto fail;
+       }
+
+       /*
+        * Allocate Tx and Rx rings.
+        */
+       for (ac = 0; ac < 4; ac++) {
+               error = rt2661_alloc_tx_ring(sc, &sc->txq[ac],
+                   RT2661_TX_RING_COUNT);
+               if (error != 0) {
+                       device_printf(sc->sc_dev,
+                           "could not allocate Tx ring %d\n", ac);
+                       goto fail;
+               }
+       }
+
+       error = rt2661_alloc_tx_ring(sc, &sc->mgtq, RT2661_MGT_RING_COUNT);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not allocate Mgt ring\n");
+               goto fail;
+       }
+
+       error = rt2661_alloc_rx_ring(sc, &sc->rxq, RT2661_RX_RING_COUNT);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not allocate Rx ring\n");
+               goto fail;
+       }
+
+       sysctl_ctx_init(&sc->sysctl_ctx);
+       sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+                                         SYSCTL_STATIC_CHILDREN(_hw),
+                                         OID_AUTO,
+                                         device_get_nameunit(dev),
+                                         CTLFLAG_RD, 0, "");
+       if (sc->sysctl_tree == NULL) {
+               device_printf(dev, "could not add sysctl node\n");
+               error = ENXIO;
+               goto fail;
+       }
+
+       ifp->if_softc = sc;
+       if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+       ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+       ifp->if_init = rt2661_init;
+       ifp->if_ioctl = rt2661_ioctl;
+       ifp->if_start = rt2661_start;
+       ifp->if_watchdog = rt2661_watchdog;
+       ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN);
+       ifq_set_ready(&ifp->if_snd);
+
+       ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+       ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
+       ic->ic_state = IEEE80211_S_INIT;
+
+       /* set device capabilities */
+       ic->ic_caps =
+           IEEE80211_C_IBSS |          /* IBSS mode supported */
+           IEEE80211_C_MONITOR |       /* monitor mode supported */
+           IEEE80211_C_HOSTAP |        /* HostAp mode supported */
+           IEEE80211_C_TXPMGT |        /* tx power management */
+           IEEE80211_C_SHPREAMBLE |    /* short preamble supported */
+           IEEE80211_C_SHSLOT |        /* short slot time supported */
+#ifdef notyet
+           IEEE80211_C_WME |           /* 802.11e */
+#endif
+           IEEE80211_C_WEP |           /* WEP */
+           IEEE80211_C_WPA;            /* 802.11i */
+
+       if (sc->rf_rev == RT2661_RF_5225 || sc->rf_rev == RT2661_RF_5325) {
+               /* set supported .11a rates */
+               ic->ic_sup_rates[IEEE80211_MODE_11A] = rt2661_rateset_11a;
+
+               /* set supported .11a channels */
+               for (i = 36; i <= 64; i += 4) {
+                       ic->ic_channels[i].ic_freq =
+                           ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
+                       ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
+               }
+               for (i = 100; i <= 140; i += 4) {
+                       ic->ic_channels[i].ic_freq =
+                           ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
+                       ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
+               }
+               for (i = 149; i <= 165; i += 4) {
+                       ic->ic_channels[i].ic_freq =
+                           ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
+                       ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
+               }
+       }
+
+       /* set supported .11b and .11g rates */
+       ic->ic_sup_rates[IEEE80211_MODE_11B] = rt2661_rateset_11b;
+       ic->ic_sup_rates[IEEE80211_MODE_11G] = rt2661_rateset_11g;
+
+       /* set supported .11b and .11g channels (1 through 14) */
+       for (i = 1; i <= 14; i++) {
+               ic->ic_channels[i].ic_freq =
+                   ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
+               ic->ic_channels[i].ic_flags =
+                   IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
+                   IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
+       }
+
+       ieee80211_ifattach(ic);
+       ic->ic_node_alloc = rt2661_node_alloc;
+/*     ic->ic_wme.wme_update = rt2661_wme_update;*/
+       ic->ic_updateslot = rt2661_update_slot;
+       ic->ic_reset = rt2661_reset;
+       /* enable s/w bmiss handling in sta mode */
+       ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;
+
+       /* override state transition machine */
+       sc->sc_newstate = ic->ic_newstate;
+       ic->ic_newstate = rt2661_newstate;
+       ieee80211_media_init(ic, rt2661_media_change, ieee80211_media_status);
+
+       bpfattach_dlt(ifp, DLT_IEEE802_11_RADIO,
+           sizeof (struct ieee80211_frame) + 64, &sc->sc_drvbpf);
+
+       sc->sc_rxtap_len = sizeof sc->sc_rxtapu;
+       sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
+       sc->sc_rxtap.wr_ihdr.it_present = htole32(RT2661_RX_RADIOTAP_PRESENT);
+
+       sc->sc_txtap_len = sizeof sc->sc_txtapu;
+       sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
+       sc->sc_txtap.wt_ihdr.it_present = htole32(RT2661_TX_RADIOTAP_PRESENT);
+
+       /*
+        * Add a few sysctl knobs.
+        */
+       sc->dwelltime = 200;
+
+       SYSCTL_ADD_INT(&sc->sysctl_ctx,
+           SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "dwell",
+           CTLFLAG_RW, &sc->dwelltime, 0,
+           "channel dwell time (ms) for AP/station scanning");
+
+       error = bus_setup_intr(dev, sc->sc_irq, INTR_MPSAFE, rt2661_intr,
+                              sc, &sc->sc_ih, ifp->if_serializer);
+       if (error != 0) {
+               device_printf(dev, "could not set up interrupt\n");
+               bpfdetach(ifp);
+               ieee80211_ifdetach(ic);
+               goto fail;
+       }
+
+       if (bootverbose)
+               ieee80211_announce(ic);
+       return 0;
+fail:
+       rt2661_detach(sc);
+       return error;
+}
+
+int
+rt2661_detach(void *xsc)
+{
+       struct rt2661_softc *sc = xsc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+
+       if (device_is_attached(sc->sc_dev)) {
+               lwkt_serialize_enter(ifp->if_serializer);
+
+               callout_stop(&sc->scan_ch);
+               callout_stop(&sc->rssadapt_ch);
+               rt2661_stop(sc);
+               bus_teardown_intr(sc->sc_dev, sc->sc_irq, sc->sc_ih);
+
+               lwkt_serialize_exit(ifp->if_serializer);
+
+               bpfdetach(ifp);
+               ieee80211_ifdetach(ic);
+       }
+
+       rt2661_free_tx_ring(sc, &sc->txq[0]);
+       rt2661_free_tx_ring(sc, &sc->txq[1]);
+       rt2661_free_tx_ring(sc, &sc->txq[2]);
+       rt2661_free_tx_ring(sc, &sc->txq[3]);
+       rt2661_free_tx_ring(sc, &sc->mgtq);
+       rt2661_free_rx_ring(sc, &sc->rxq);
+
+       if (sc->sc_irq != NULL) {
+               bus_release_resource(sc->sc_dev, SYS_RES_IRQ, sc->sc_irq_rid,
+                                    sc->sc_irq);
+       }
+
+       if (sc->sysctl_tree != NULL)
+               sysctl_ctx_free(&sc->sysctl_ctx);
+
+       return 0;
+}
+
+void
+rt2661_shutdown(void *xsc)
+{
+       struct rt2661_softc *sc = xsc;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+       rt2661_stop(sc);
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+void
+rt2661_suspend(void *xsc)
+{
+       struct rt2661_softc *sc = xsc;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+       rt2661_stop(sc);
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+void
+rt2661_resume(void *xsc)
+{
+       struct rt2661_softc *sc = xsc;
+       struct ifnet *ifp = sc->sc_ic.ic_ifp;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+       if (ifp->if_flags & IFF_UP) {
+               ifp->if_init(ifp->if_softc);
+               if (ifp->if_flags & IFF_RUNNING)
+                       ifp->if_start(ifp);
+       }
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+static void
+rt2661_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+       if (error != 0)
+               return;
+
+       KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg));
+
+       *(bus_addr_t *)arg = segs[0].ds_addr;
+}
+
+static int
+rt2661_alloc_tx_ring(struct rt2661_softc *sc, struct rt2661_tx_ring *ring,
+    int count)
+{
+       int i, error;
+
+       ring->count = count;
+       ring->queued = 0;
+       ring->cur = ring->next = ring->stat = 0;
+
+       error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT,
+           BUS_SPACE_MAXADDR, NULL, NULL, count * RT2661_TX_DESC_SIZE, 1,
+           count * RT2661_TX_DESC_SIZE, 0, &ring->desc_dmat);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not create desc DMA tag\n");
+               goto fail;
+       }
+
+       error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc,
+           BUS_DMA_WAITOK | BUS_DMA_ZERO, &ring->desc_map);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not allocate DMA memory\n");
+               goto fail;
+       }
+
+       error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc,
+           count * RT2661_TX_DESC_SIZE, rt2661_dma_map_addr, &ring->physaddr,
+           0);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not load desc DMA map\n");
+
+               bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map);
+               ring->desc = NULL;
+               goto fail;
+       }
+
+       ring->data = malloc(count * sizeof (struct rt2661_tx_data), M_DEVBUF,
+           M_WAITOK | M_ZERO);
+
+       error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT,
+           BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, RT2661_MAX_SCATTER,
+           MCLBYTES, 0, &ring->data_dmat);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not create data DMA tag\n");
+               goto fail;
+       }
+
+       for (i = 0; i < count; i++) {
+               error = bus_dmamap_create(ring->data_dmat, 0,
+                   &ring->data[i].map);
+               if (error != 0) {
+                       device_printf(sc->sc_dev, "could not create DMA map\n");
+                       goto fail;
+               }
+       }
+       return 0;
+
+fail:  rt2661_free_tx_ring(sc, ring);
+       return error;
+}
+
+static void
+rt2661_reset_tx_ring(struct rt2661_softc *sc, struct rt2661_tx_ring *ring)
+{
+       struct rt2661_tx_desc *desc;
+       struct rt2661_tx_data *data;
+       int i;
+
+       for (i = 0; i < ring->count; i++) {
+               desc = &ring->desc[i];
+               data = &ring->data[i];
+
+               if (data->m != NULL) {
+                       bus_dmamap_sync(ring->data_dmat, data->map,
+                           BUS_DMASYNC_POSTWRITE);
+                       bus_dmamap_unload(ring->data_dmat, data->map);
+                       m_freem(data->m);
+                       data->m = NULL;
+               }
+
+               if (data->ni != NULL) {
+                       ieee80211_free_node(data->ni);
+                       data->ni = NULL;
+               }
+
+               desc->flags = 0;
+       }
+
+       bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE);
+
+       ring->queued = 0;
+       ring->cur = ring->next = ring->stat = 0;
+}
+
+static void
+rt2661_free_tx_ring(struct rt2661_softc *sc, struct rt2661_tx_ring *ring)
+{
+       struct rt2661_tx_data *data;
+       int i;
+
+       if (ring->desc != NULL) {
+               bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
+                   BUS_DMASYNC_POSTWRITE);
+               bus_dmamap_unload(ring->desc_dmat, ring->desc_map);
+               bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map);
+               ring->desc = NULL;
+       }
+
+       if (ring->desc_dmat != NULL) {
+               bus_dma_tag_destroy(ring->desc_dmat);
+               ring->desc_dmat = NULL;
+       }
+
+       if (ring->data != NULL) {
+               for (i = 0; i < ring->count; i++) {
+                       data = &ring->data[i];
+
+                       if (data->m != NULL) {
+                               bus_dmamap_sync(ring->data_dmat, data->map,
+                                   BUS_DMASYNC_POSTWRITE);
+                               bus_dmamap_unload(ring->data_dmat, data->map);
+                               m_freem(data->m);
+                               data->m = NULL;
+                       }
+
+                       if (data->ni != NULL) {
+                               ieee80211_free_node(data->ni);
+                               data->ni = NULL;
+                       }
+
+                       if (data->map != NULL) {
+                               bus_dmamap_destroy(ring->data_dmat, data->map);
+                               data->map = NULL;
+                       }
+               }
+
+               free(ring->data, M_DEVBUF);
+               ring->data = NULL;
+       }
+
+       if (ring->data_dmat != NULL) {
+               bus_dma_tag_destroy(ring->data_dmat);
+               ring->data_dmat = NULL;
+       }
+}
+
+static int
+rt2661_alloc_rx_ring(struct rt2661_softc *sc, struct rt2661_rx_ring *ring,
+    int count)
+{
+       struct rt2661_rx_desc *desc;
+       struct rt2661_rx_data *data;
+       bus_addr_t physaddr;
+       int i, error;
+
+       ring->count = count;
+       ring->cur = ring->next = 0;
+
+       error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT,
+           BUS_SPACE_MAXADDR, NULL, NULL, count * RT2661_RX_DESC_SIZE, 1,
+           count * RT2661_RX_DESC_SIZE, 0, &ring->desc_dmat);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not create desc DMA tag\n");
+               goto fail;
+       }
+
+       error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->desc,
+           BUS_DMA_WAITOK | BUS_DMA_ZERO, &ring->desc_map);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not allocate DMA memory\n");
+               goto fail;
+       }
+
+       error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->desc,
+           count * RT2661_RX_DESC_SIZE, rt2661_dma_map_addr, &ring->physaddr,
+           0);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not load desc DMA map\n");
+
+               bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map);
+               ring->desc = NULL;
+               goto fail;
+       }
+
+       ring->data = malloc(count * sizeof (struct rt2661_rx_data), M_DEVBUF,
+           M_WAITOK | M_ZERO);
+
+       /*
+        * Pre-allocate Rx buffers and populate Rx ring.
+        */
+       error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT,
+           BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1, MCLBYTES, 0,
+           &ring->data_dmat);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not create data DMA tag\n");
+               goto fail;
+       }
+
+       for (i = 0; i < count; i++) {
+               desc = &sc->rxq.desc[i];
+               data = &sc->rxq.data[i];
+
+               error = bus_dmamap_create(ring->data_dmat, 0, &data->map);
+               if (error != 0) {
+                       device_printf(sc->sc_dev, "could not create DMA map\n");
+                       goto fail;
+               }
+
+               data->m = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR);
+               if (data->m == NULL) {
+                       device_printf(sc->sc_dev,
+                           "could not allocate rx mbuf\n");
+                       error = ENOMEM;
+                       goto fail;
+               }
+
+               error = bus_dmamap_load(ring->data_dmat, data->map,
+                   mtod(data->m, void *), MCLBYTES, rt2661_dma_map_addr,
+                   &physaddr, 0);
+               if (error != 0) {
+                       device_printf(sc->sc_dev,
+                           "could not load rx buf DMA map");
+
+                       m_freem(data->m);
+                       data->m = NULL;
+                       goto fail;
+               }
+
+               desc->flags = htole32(RT2661_RX_BUSY);
+               desc->physaddr = htole32(physaddr);
+       }
+
+       bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE);
+
+       return 0;
+
+fail:  rt2661_free_rx_ring(sc, ring);
+       return error;
+}
+
+static void
+rt2661_reset_rx_ring(struct rt2661_softc *sc, struct rt2661_rx_ring *ring)
+{
+       int i;
+
+       for (i = 0; i < ring->count; i++)
+               ring->desc[i].flags = htole32(RT2661_RX_BUSY);
+
+       bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE);
+
+       ring->cur = ring->next = 0;
+}
+
+static void
+rt2661_free_rx_ring(struct rt2661_softc *sc, struct rt2661_rx_ring *ring)
+{
+       struct rt2661_rx_data *data;
+       int i;
+
+       if (ring->desc != NULL) {
+               bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
+                   BUS_DMASYNC_POSTWRITE);
+               bus_dmamap_unload(ring->desc_dmat, ring->desc_map);
+               bus_dmamem_free(ring->desc_dmat, ring->desc, ring->desc_map);
+               ring->desc = NULL;
+       }
+
+       if (ring->desc_dmat != NULL) {
+               bus_dma_tag_destroy(ring->desc_dmat);
+               ring->desc_dmat = NULL;
+       }
+
+       if (ring->data != NULL) {
+               for (i = 0; i < ring->count; i++) {
+                       data = &ring->data[i];
+
+                       if (data->m != NULL) {
+                               bus_dmamap_sync(ring->data_dmat, data->map,
+                                   BUS_DMASYNC_POSTREAD);
+                               bus_dmamap_unload(ring->data_dmat, data->map);
+                               m_freem(data->m);
+                               data->m = NULL;
+                       }
+
+                       if (data->map != NULL) {
+                               bus_dmamap_destroy(ring->data_dmat, data->map);
+                               data->map = NULL;
+                       }
+               }
+
+               free(ring->data, M_DEVBUF);
+               ring->data = NULL;
+       }
+
+       if (ring->data_dmat != NULL) {
+               bus_dma_tag_destroy(ring->data_dmat);
+               ring->data_dmat = NULL;
+       }
+}
+
+static struct ieee80211_node *
+rt2661_node_alloc(struct ieee80211_node_table *nt)
+{
+       struct rt2661_node *rn;
+
+       rn = malloc(sizeof (struct rt2661_node), M_80211_NODE,
+           M_NOWAIT | M_ZERO);
+
+       return (rn != NULL) ? &rn->ni : NULL;
+}
+
+static int
+rt2661_media_change(struct ifnet *ifp)
+{
+       struct rt2661_softc *sc = ifp->if_softc;
+       int error;
+
+       error = ieee80211_media_change(ifp);
+       if (error != ENETRESET)
+               return error;
+
+       if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING))
+               rt2661_init(sc);
+       return 0;
+}
+
+/*
+ * This function is called periodically (every 200ms) during scanning to
+ * switch from one channel to another.
+ */
+static void
+rt2661_next_scan(void *arg)
+{
+       struct rt2661_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);
+}
+
+/*
+ * This function is called for each node present in the node station table.
+ */
+static void
+rt2661_iter_func(void *arg, struct ieee80211_node *ni)
+{
+       struct rt2661_node *rn = (struct rt2661_node *)ni;
+
+       ral_rssadapt_updatestats(&rn->rssadapt);
+}
+
+/*
+ * This function is called periodically (every 100ms) in RUN state to update
+ * the rate adaptation statistics.
+ */
+static void
+rt2661_update_rssadapt(void *arg)
+{
+       struct rt2661_softc *sc = arg;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       ieee80211_iterate_nodes(&ic->ic_sta, rt2661_iter_func, arg);
+       callout_reset(&sc->rssadapt_ch, hz / 10, rt2661_update_rssadapt, sc);
+
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+static int
+rt2661_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+{
+       struct rt2661_softc *sc = ic->ic_ifp->if_softc;
+       enum ieee80211_state ostate;
+       struct ieee80211_node *ni;
+       uint32_t tmp;
+       int error = 0;
+
+       ostate = ic->ic_state;
+       callout_stop(&sc->scan_ch);
+
+       switch (nstate) {
+       case IEEE80211_S_INIT:
+               callout_stop(&sc->rssadapt_ch);
+
+               if (ostate == IEEE80211_S_RUN) {
+                       /* abort TSF synchronization */
+                       tmp = RAL_READ(sc, RT2661_TXRX_CSR9);
+                       RAL_WRITE(sc, RT2661_TXRX_CSR9, tmp & ~0x00ffffff);
+               }
+               break;
+
+       case IEEE80211_S_SCAN:
+               rt2661_set_chan(sc, ic->ic_curchan);
+               callout_reset(&sc->scan_ch, (sc->dwelltime * hz) / 1000,
+                   rt2661_next_scan, sc);
+               break;
+
+       case IEEE80211_S_AUTH:
+       case IEEE80211_S_ASSOC:
+               rt2661_set_chan(sc, ic->ic_curchan);
+               break;
+
+       case IEEE80211_S_RUN:
+               rt2661_set_chan(sc, ic->ic_curchan);
+
+               ni = ic->ic_bss;
+
+               if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+                       rt2661_enable_mrr(sc);
+                       rt2661_set_txpreamble(sc);
+                       rt2661_set_basicrates(sc, &ni->ni_rates);
+                       rt2661_set_bssid(sc, ni->ni_bssid);
+               }
+
+               if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+                   ic->ic_opmode == IEEE80211_M_IBSS) {
+                       if ((error = rt2661_prepare_beacon(sc)) != 0)
+                               break;
+               }
+
+               if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+                       callout_reset(&sc->rssadapt_ch, hz / 10,
+                           rt2661_update_rssadapt, sc);
+                       rt2661_enable_tsf_sync(sc);
+               }
+               break;
+       }       
+
+       return (error != 0) ? error : sc->sc_newstate(ic, nstate, arg);
+}
+
+/*
+ * Read 16 bits at address 'addr' from the serial EEPROM (either 93C46 or
+ * 93C66).
+ */
+static uint16_t
+rt2661_eeprom_read(struct rt2661_softc *sc, uint8_t addr)
+{
+       uint32_t tmp;
+       uint16_t val;
+       int n;
+
+       /* clock C once before the first command */
+       RT2661_EEPROM_CTL(sc, 0);
+
+       RT2661_EEPROM_CTL(sc, RT2661_S);
+       RT2661_EEPROM_CTL(sc, RT2661_S | RT2661_C);
+       RT2661_EEPROM_CTL(sc, RT2661_S);
+
+       /* write start bit (1) */
+       RT2661_EEPROM_CTL(sc, RT2661_S | RT2661_D);
+       RT2661_EEPROM_CTL(sc, RT2661_S | RT2661_D | RT2661_C);
+
+       /* write READ opcode (10) */
+       RT2661_EEPROM_CTL(sc, RT2661_S | RT2661_D);
+       RT2661_EEPROM_CTL(sc, RT2661_S | RT2661_D | RT2661_C);
+       RT2661_EEPROM_CTL(sc, RT2661_S);
+       RT2661_EEPROM_CTL(sc, RT2661_S | RT2661_C);
+
+       /* write address (A5-A0 or A7-A0) */
+       n = (RAL_READ(sc, RT2661_E2PROM_CSR) & RT2661_93C46) ? 5 : 7;
+       for (; n >= 0; n--) {
+               RT2661_EEPROM_CTL(sc, RT2661_S |
+                   (((addr >> n) & 1) << RT2661_SHIFT_D));
+               RT2661_EEPROM_CTL(sc, RT2661_S |
+                   (((addr >> n) & 1) << RT2661_SHIFT_D) | RT2661_C);
+       }
+
+       RT2661_EEPROM_CTL(sc, RT2661_S);
+
+       /* read data Q15-Q0 */
+       val = 0;
+       for (n = 15; n >= 0; n--) {
+               RT2661_EEPROM_CTL(sc, RT2661_S | RT2661_C);
+               tmp = RAL_READ(sc, RT2661_E2PROM_CSR);
+               val |= ((tmp & RT2661_Q) >> RT2661_SHIFT_Q) << n;
+               RT2661_EEPROM_CTL(sc, RT2661_S);
+       }
+
+       RT2661_EEPROM_CTL(sc, 0);
+
+       /* clear Chip Select and clock C */
+       RT2661_EEPROM_CTL(sc, RT2661_S);
+       RT2661_EEPROM_CTL(sc, 0);
+       RT2661_EEPROM_CTL(sc, RT2661_C);
+
+       return val;
+}
+
+static void
+rt2661_tx_intr(struct rt2661_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = ic->ic_ifp;
+       struct rt2661_tx_ring *txq;
+       struct rt2661_tx_data *data;
+       struct rt2661_node *rn;
+       uint32_t val;
+       int qid, retrycnt;
+
+       for (;;) {
+               val = RAL_READ(sc, RT2661_STA_CSR4);
+               if (!(val & RT2661_TX_STAT_VALID))
+                       break;
+
+               /* retrieve the queue in which this frame was sent */
+               qid = RT2661_TX_QID(val);
+               txq = (qid <= 3) ? &sc->txq[qid] : &sc->mgtq;
+
+               /* retrieve rate control algorithm context */
+               data = &txq->data[txq->stat];
+               rn = (struct rt2661_node *)data->ni;
+
+               switch (RT2661_TX_RESULT(val)) {
+               case RT2661_TX_SUCCESS:
+                       retrycnt = RT2661_TX_RETRYCNT(val);
+
+                       DPRINTFN(10, ("data frame sent successfully after "
+                           "%d retries\n", retrycnt));
+                       if (retrycnt == 0 && data->id.id_node != NULL) {
+                               ral_rssadapt_raise_rate(ic, &rn->rssadapt,
+                                   &data->id);
+                       }
+                       ifp->if_opackets++;
+                       break;
+
+               case RT2661_TX_RETRY_FAIL:
+                       DPRINTFN(9, ("sending data frame failed (too much "
+                           "retries)\n"));
+                       if (data->id.id_node != NULL) {
+                               ral_rssadapt_lower_rate(ic, data->ni,
+                                   &rn->rssadapt, &data->id);
+                       }
+                       ifp->if_oerrors++;
+                       break;
+
+               default:
+                       /* other failure */
+                       device_printf(sc->sc_dev,
+                           "sending data frame failed 0x%08x\n", val);
+                       ifp->if_oerrors++;
+               }
+
+               ieee80211_free_node(data->ni);
+               data->ni = NULL;
+
+               DPRINTFN(15, ("tx done q=%d idx=%u\n", qid, txq->stat));
+
+               txq->queued--;
+               if (++txq->stat >= txq->count)  /* faster than % count */
+                       txq->stat = 0;
+       }
+
+       sc->sc_tx_timer = 0;
+       ifp->if_flags &= ~IFF_OACTIVE;
+       rt2661_start(ifp);
+}
+
+static void
+rt2661_tx_dma_intr(struct rt2661_softc *sc, struct rt2661_tx_ring *txq)
+{
+       struct rt2661_tx_desc *desc;
+       struct rt2661_tx_data *data;
+
+       bus_dmamap_sync(txq->desc_dmat, txq->desc_map, BUS_DMASYNC_POSTREAD);
+
+       for (;;) {
+               desc = &txq->desc[txq->next];
+               data = &txq->data[txq->next];
+
+               if ((le32toh(desc->flags) & RT2661_TX_BUSY) ||
+                   !(le32toh(desc->flags) & RT2661_TX_VALID))
+                       break;
+
+               bus_dmamap_sync(txq->data_dmat, data->map,
+                   BUS_DMASYNC_POSTWRITE);
+               bus_dmamap_unload(txq->data_dmat, data->map);
+               m_freem(data->m);
+               data->m = NULL;
+               /* node reference is released in rt2661_tx_intr() */
+
+               /* descriptor is no longer valid */
+               desc->flags &= ~htole32(RT2661_TX_VALID);
+
+               DPRINTFN(15, ("tx dma done q=%p idx=%u\n", txq, txq->next));
+
+               if (++txq->next >= txq->count)  /* faster than % count */
+                       txq->next = 0;
+       }
+
+       bus_dmamap_sync(txq->desc_dmat, txq->desc_map, BUS_DMASYNC_PREWRITE);
+}
+
+static void
+rt2661_rx_intr(struct rt2661_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = ic->ic_ifp;
+       struct rt2661_rx_desc *desc;
+       struct rt2661_rx_data *data;
+       bus_addr_t physaddr;
+       struct ieee80211_frame *wh;
+       struct ieee80211_node *ni;
+       struct rt2661_node *rn;
+       struct mbuf *mnew, *m;
+       int error;
+
+       bus_dmamap_sync(sc->rxq.desc_dmat, sc->rxq.desc_map,
+           BUS_DMASYNC_POSTREAD);
+
+       for (;;) {
+               desc = &sc->rxq.desc[sc->rxq.cur];
+               data = &sc->rxq.data[sc->rxq.cur];
+
+               if (le32toh(desc->flags) & RT2661_RX_BUSY)
+                       break;
+
+               if ((le32toh(desc->flags) & RT2661_RX_PHY_ERROR) ||
+                   (le32toh(desc->flags) & RT2661_RX_CRC_ERROR)) {
+                       /*
+                        * This should not happen since we did not request
+                        * to receive those frames when we filled TXRX_CSR0.
+                        */
+                       DPRINTFN(5, ("PHY or CRC error flags 0x%08x\n",
+                           le32toh(desc->flags)));
+                       ifp->if_ierrors++;
+                       goto skip;
+               }
+
+               if ((le32toh(desc->flags) & RT2661_RX_CIPHER_MASK) != 0) {
+                       ifp->if_ierrors++;
+                       goto skip;
+               }
+
+               /*
+                * Try to allocate a new mbuf for this ring element and load it
+                * before processing the current mbuf. If the ring element
+                * cannot be loaded, drop the received packet and reuse the old
+                * mbuf. In the unlikely case that the old mbuf can't be
+                * reloaded either, explicitly panic.
+                */
+               mnew = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR);
+               if (mnew == NULL) {
+                       ifp->if_ierrors++;
+                       goto skip;
+               }
+
+               bus_dmamap_sync(sc->rxq.data_dmat, data->map,
+                   BUS_DMASYNC_POSTREAD);
+               bus_dmamap_unload(sc->rxq.data_dmat, data->map);
+
+               error = bus_dmamap_load(sc->rxq.data_dmat, data->map,
+                   mtod(mnew, void *), MCLBYTES, rt2661_dma_map_addr,
+                   &physaddr, 0);
+               if (error != 0) {
+                       m_freem(mnew);
+
+                       /* try to reload the old mbuf */
+                       error = bus_dmamap_load(sc->rxq.data_dmat, data->map,
+                           mtod(data->m, void *), MCLBYTES,
+                           rt2661_dma_map_addr, &physaddr, 0);
+                       if (error != 0) {
+                               /* very unlikely that it will fail... */
+                               panic("%s: could not load old rx mbuf",
+                                   device_get_name(sc->sc_dev));
+                       }
+                       ifp->if_ierrors++;
+                       goto skip;
+               }
+
+               /*
+                * New mbuf successfully loaded, update Rx ring and continue
+                * processing.
+                */
+               m = data->m;
+               data->m = mnew;
+               desc->physaddr = htole32(physaddr);
+
+               /* finalize mbuf */
+               m->m_pkthdr.rcvif = ifp;
+               m->m_pkthdr.len = m->m_len =
+                   (le32toh(desc->flags) >> 16) & 0xfff;
+
+               if (sc->sc_drvbpf != NULL) {
+                       struct rt2661_rx_radiotap_header *tap = &sc->sc_rxtap;
+                       uint32_t tsf_lo, tsf_hi;
+
+                       /* get timestamp (low and high 32 bits) */
+                       tsf_hi = RAL_READ(sc, RT2661_TXRX_CSR13);
+                       tsf_lo = RAL_READ(sc, RT2661_TXRX_CSR12);
+
+                       tap->wr_tsf =
+                           htole64(((uint64_t)tsf_hi << 32) | tsf_lo);
+                       tap->wr_flags = 0;
+                       tap->wr_rate = rt2661_rxrate(desc);
+                       tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
+                       tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+                       tap->wr_antsignal = desc->rssi;
+
+                       bpf_ptap(sc->sc_drvbpf, m, tap, sc->sc_rxtap_len);
+               }
+
+               wh = mtod(m, struct ieee80211_frame *);
+               ni = ieee80211_find_rxnode(ic,
+                   (struct ieee80211_frame_min *)wh);
+
+               /* send the frame to the 802.11 layer */
+               ieee80211_input(ic, m, ni, desc->rssi, 0);
+
+               /* give rssi to the rate adatation algorithm */
+               rn = (struct rt2661_node *)ni;
+               ral_rssadapt_input(ic, ni, &rn->rssadapt,
+                   rt2661_get_rssi(sc, desc->rssi));
+
+               /* node is no longer needed */
+               ieee80211_free_node(ni);
+
+skip:          desc->flags |= htole32(RT2661_RX_BUSY);
+
+               DPRINTFN(15, ("rx intr idx=%u\n", sc->rxq.cur));
+
+               sc->rxq.cur = (sc->rxq.cur + 1) % RT2661_RX_RING_COUNT;
+       }
+
+       bus_dmamap_sync(sc->rxq.desc_dmat, sc->rxq.desc_map,
+           BUS_DMASYNC_PREWRITE);
+}
+
+/* ARGSUSED */
+static void
+rt2661_mcu_beacon_expire(struct rt2661_softc *sc)
+{
+       /* do nothing */
+}
+
+static void
+rt2661_mcu_wakeup(struct rt2661_softc *sc)
+{
+       RAL_WRITE(sc, RT2661_MAC_CSR11, 5 << 16);
+
+       RAL_WRITE(sc, RT2661_SOFT_RESET_CSR, 0x7);
+       RAL_WRITE(sc, RT2661_IO_CNTL_CSR, 0x18);
+       RAL_WRITE(sc, RT2661_PCI_USEC_CSR, 0x20);
+
+       /* send wakeup command to MCU */
+       rt2661_tx_cmd(sc, RT2661_MCU_CMD_WAKEUP, 0);
+}
+
+static void
+rt2661_mcu_cmd_intr(struct rt2661_softc *sc)
+{
+       RAL_READ(sc, RT2661_M2H_CMD_DONE_CSR);
+       RAL_WRITE(sc, RT2661_M2H_CMD_DONE_CSR, 0xffffffff);
+}
+
+static void
+rt2661_intr(void *arg)
+{
+       struct rt2661_softc *sc = arg;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       uint32_t r1, r2;
+
+       /* disable MAC and MCU interrupts */
+       RAL_WRITE(sc, RT2661_INT_MASK_CSR, 0xffffff7f);
+       RAL_WRITE(sc, RT2661_MCU_INT_MASK_CSR, 0xffffffff);
+
+       /* don't re-enable interrupts if we're shutting down */
+       if (!(ifp->if_flags & IFF_RUNNING))
+               return;
+
+       r1 = RAL_READ(sc, RT2661_INT_SOURCE_CSR);
+       RAL_WRITE(sc, RT2661_INT_SOURCE_CSR, r1);
+
+       r2 = RAL_READ(sc, RT2661_MCU_INT_SOURCE_CSR);
+       RAL_WRITE(sc, RT2661_MCU_INT_SOURCE_CSR, r2);
+
+       if (r1 & RT2661_MGT_DONE)
+               rt2661_tx_dma_intr(sc, &sc->mgtq);
+
+       if (r1 & RT2661_RX_DONE)
+               rt2661_rx_intr(sc);
+
+       if (r1 & RT2661_TX0_DMA_DONE)
+               rt2661_tx_dma_intr(sc, &sc->txq[0]);
+
+       if (r1 & RT2661_TX1_DMA_DONE)
+               rt2661_tx_dma_intr(sc, &sc->txq[1]);
+
+       if (r1 & RT2661_TX2_DMA_DONE)
+               rt2661_tx_dma_intr(sc, &sc->txq[2]);
+
+       if (r1 & RT2661_TX3_DMA_DONE)
+               rt2661_tx_dma_intr(sc, &sc->txq[3]);
+
+       if (r1 & RT2661_TX_DONE)
+               rt2661_tx_intr(sc);
+
+       if (r2 & RT2661_MCU_CMD_DONE)
+               rt2661_mcu_cmd_intr(sc);
+
+       if (r2 & RT2661_MCU_BEACON_EXPIRE)
+               rt2661_mcu_beacon_expire(sc);
+
+       if (r2 & RT2661_MCU_WAKEUP)
+               rt2661_mcu_wakeup(sc);
+
+       /* re-enable MAC and MCU interrupts */
+       RAL_WRITE(sc, RT2661_INT_MASK_CSR, 0x0000ff10);
+       RAL_WRITE(sc, RT2661_MCU_INT_MASK_CSR, 0);
+}
+
+/* quickly determine if a given rate is CCK or OFDM */
+#define RAL_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
+
+#define RAL_ACK_SIZE   14      /* 10 + 4(FCS) */
+#define RAL_CTS_SIZE   14      /* 10 + 4(FCS) */
+
+#define RAL_SIFS       10      /* us */
+
+/*
+ * This function is only used by the Rx radiotap code. It returns the rate at
+ * which a given frame was received.
+ */
+static uint8_t
+rt2661_rxrate(struct rt2661_rx_desc *desc)
+{
+       if (le32toh(desc->flags) & RT2661_RX_OFDM) {
+               /* reverse function of rt2661_plcp_signal */
+               switch (desc->rate & 0xf) {
+               case 0xb:       return 12;
+               case 0xf:       return 18;
+               case 0xa:       return 24;
+               case 0xe:       return 36;
+               case 0x9:       return 48;
+               case 0xd:       return 72;
+               case 0x8:       return 96;
+               case 0xc:       return 108;
+               }
+       } else {
+               if (desc->rate == 10)
+                       return 2;
+               if (desc->rate == 20)
+                       return 4;
+               if (desc->rate == 55)
+                       return 11;
+               if (desc->rate == 110)
+                       return 22;
+       }
+       return 2;       /* should not get there */
+}
+
+/*
+ * Return the expected ack rate for a frame transmitted at rate `rate'.
+ * XXX: this should depend on the destination node basic rate set.
+ */
+static int
+rt2661_ack_rate(struct ieee80211com *ic, int rate)
+{
+       switch (rate) {
+       /* CCK rates */
+       case 2:
+               return 2;
+       case 4:
+       case 11:
+       case 22:
+               return (ic->ic_curmode == IEEE80211_MODE_11B) ? 4 : rate;
+
+       /* OFDM rates */
+       case 12:
+       case 18:
+               return 12;
+       case 24:
+       case 36:
+               return 24;
+       case 48:
+       case 72:
+       case 96:
+       case 108:
+               return 48;
+       }
+
+       /* default to 1Mbps */
+       return 2;
+}
+
+/*
+ * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'.
+ * The function automatically determines the operating mode depending on the
+ * given rate. `flags' indicates whether short preamble is in use or not.
+ */
+static uint16_t
+rt2661_txtime(int len, int rate, uint32_t flags)
+{
+       uint16_t txtime;
+
+       if (RAL_RATE_IS_OFDM(rate)) {
+               /* IEEE Std 802.11a-1999, pp. 37 */
+               txtime = (8 + 4 * len + 3 + rate - 1) / rate;
+               txtime = 16 + 4 + 4 * txtime + 6;
+       } else {
+               /* IEEE Std 802.11b-1999, pp. 28 */
+               txtime = (16 * len + rate - 1) / rate;
+               if (rate != 2 && (flags & IEEE80211_F_SHPREAMBLE))
+                       txtime +=  72 + 24;
+               else
+                       txtime += 144 + 48;
+       }
+
+       return txtime;
+}
+
+static uint8_t
+rt2661_plcp_signal(int rate)
+{
+       switch (rate) {
+       /* CCK rates (returned values are device-dependent) */
+       case 2:         return 0x0;
+       case 4:         return 0x1;
+       case 11:        return 0x2;
+       case 22:        return 0x3;
+
+       /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
+       case 12:        return 0xb;
+       case 18:        return 0xf;
+       case 24:        return 0xa;
+       case 36:        return 0xe;
+       case 48:        return 0x9;
+       case 72:        return 0xd;
+       case 96:        return 0x8;
+       case 108:       return 0xc;
+
+       /* unsupported rates (should not get there) */
+       default:        return 0xff;
+       }
+}
+
+static void
+rt2661_setup_tx_desc(struct rt2661_softc *sc, struct rt2661_tx_desc *desc,
+    uint32_t flags, uint16_t xflags, int len, int rate,
+    const bus_dma_segment_t *segs, int nsegs, int ac)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       uint16_t plcp_length;
+       int i, remainder;
+
+       desc->flags = htole32(flags);
+       desc->flags |= htole32(len << 16);
+       desc->flags |= htole32(RT2661_TX_BUSY | RT2661_TX_VALID);
+
+       desc->xflags = htole16(xflags);
+       desc->xflags |= htole16(nsegs << 13);
+
+       desc->wme = htole16(
+           RT2661_QID(ac) |
+           RT2661_AIFSN(2) |
+           RT2661_LOGCWMIN(4) |
+           RT2661_LOGCWMAX(10));
+
+       /*
+        * Remember in which queue this frame was sent. This field is driver
+        * private data only. It will be made available by the NIC in STA_CSR4
+        * on Tx interrupts.
+        */
+       desc->qid = ac;
+
+       /* setup PLCP fields */
+       desc->plcp_signal  = rt2661_plcp_signal(rate);
+       desc->plcp_service = 4;
+
+       len += IEEE80211_CRC_LEN;
+       if (RAL_RATE_IS_OFDM(rate)) {
+               desc->flags |= htole32(RT2661_TX_OFDM);
+
+               plcp_length = len & 0xfff;
+               desc->plcp_length_hi = plcp_length >> 6;
+               desc->plcp_length_lo = plcp_length & 0x3f;
+       } else {
+               plcp_length = (16 * len + rate - 1) / rate;
+               if (rate == 22) {
+                       remainder = (16 * len) % 22;
+                       if (remainder != 0 && remainder < 7)
+                               desc->plcp_service |= RT2661_PLCP_LENGEXT;
+               }
+               desc->plcp_length_hi = plcp_length >> 8;
+               desc->plcp_length_lo = plcp_length & 0xff;
+
+               if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
+                       desc->plcp_signal |= 0x08;
+       }
+
+       /* RT2x61 supports scatter with up to 5 segments */
+       for (i = 0; i < nsegs; i++) {
+               desc->addr[i] = htole32(segs[i].ds_addr);
+               desc->len [i] = htole16(segs[i].ds_len);
+       }
+}
+
+static int
+rt2661_tx_mgt(struct rt2661_softc *sc, struct mbuf *m0,
+    struct ieee80211_node *ni)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct rt2661_tx_desc *desc;
+       struct rt2661_tx_data *data;
+       struct ieee80211_frame *wh;
+       struct rt2661_dmamap map;
+       uint16_t dur;
+       uint32_t flags = 0;     /* XXX HWSEQ */
+       int rate, error;
+
+       desc = &sc->mgtq.desc[sc->mgtq.cur];
+       data = &sc->mgtq.data[sc->mgtq.cur];
+
+       /* send mgt frames at the lowest available rate */
+       rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
+
+       error = bus_dmamap_load_mbuf(sc->mgtq.data_dmat, data->map, m0,
+                                    rt2661_dma_map_mbuf, &map, 0);
+       if (error != 0) {
+               device_printf(sc->sc_dev, "could not map mbuf (error %d)\n",
+                   error);
+               m_freem(m0);
+               return error;
+       }
+
+       if (sc->sc_drvbpf != NULL) {
+               struct rt2661_tx_radiotap_header *tap = &sc->sc_txtap;
+
+               tap->wt_flags = 0;
+               tap->wt_rate = rate;
+               tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+               tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+
+               bpf_ptap(sc->sc_drvbpf, m0, tap, sc->sc_txtap_len);
+       }
+
+       data->m = m0;
+       data->ni = ni;
+
+       wh = mtod(m0, struct ieee80211_frame *);
+
+       if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+               flags |= RT2661_TX_NEED_ACK;
+
+               dur = rt2661_txtime(RAL_ACK_SIZE, rate, ic->ic_flags) +
+                   RAL_SIFS;
+               *(uint16_t *)wh->i_dur = htole16(dur);
+
+               /* tell hardware to add timestamp in probe responses */
+               if ((wh->i_fc[0] &
+                   (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
+                   (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP))
+                       flags |= RT2661_TX_TIMESTAMP;
+       }
+
+       rt2661_setup_tx_desc(sc, desc, flags, 0 /* XXX HWSEQ */,
+           m0->m_pkthdr.len, rate, map.segs, map.nseg, RT2661_QID_MGT);
+
+       bus_dmamap_sync(sc->mgtq.data_dmat, data->map, BUS_DMASYNC_PREWRITE);
+       bus_dmamap_sync(sc->mgtq.desc_dmat, sc->mgtq.desc_map,
+           BUS_DMASYNC_PREWRITE);
+
+       DPRINTFN(10, ("sending mgt frame len=%u idx=%u rate=%u\n",
+           m0->m_pkthdr.len, sc->mgtq.cur, rate));
+
+       /* kick mgt */
+       sc->mgtq.queued++;
+       sc->mgtq.cur = (sc->mgtq.cur + 1) % RT2661_MGT_RING_COUNT;
+       RAL_WRITE(sc, RT2661_TX_CNTL_CSR, RT2661_KICK_MGT);
+
+       return 0;
+}
+
+/*
+ * Build a RTS control frame.
+ */
+static struct mbuf *
+rt2661_get_rts(struct rt2661_softc *sc, struct ieee80211_frame *wh,
+    uint16_t dur)
+{
+       struct ieee80211_frame_rts *rts;
+       struct mbuf *m;
+
+       MGETHDR(m, MB_DONTWAIT, MT_DATA);
+       if (m == NULL) {
+               sc->sc_ic.ic_stats.is_tx_nobuf++;
+               device_printf(sc->sc_dev, "could not allocate RTS frame\n");
+               return NULL;
+       }
+
+       rts = mtod(m, struct ieee80211_frame_rts *);
+
+       rts->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL |
+           IEEE80211_FC0_SUBTYPE_RTS;
+       rts->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+       *(uint16_t *)rts->i_dur = htole16(dur);
+       IEEE80211_ADDR_COPY(rts->i_ra, wh->i_addr1);
+       IEEE80211_ADDR_COPY(rts->i_ta, wh->i_addr2);
+
+       m->m_pkthdr.len = m->m_len = sizeof (struct ieee80211_frame_rts);
+
+       return m;
+}
+
+static int
+rt2661_tx_data(struct rt2661_softc *sc, struct mbuf *m0,
+    struct ieee80211_node *ni, int ac)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct rt2661_tx_ring *txq = &sc->txq[ac];
+       struct rt2661_tx_desc *desc;
+       struct rt2661_tx_data *data;
+       struct rt2661_node *rn;
+       struct ieee80211_rateset *rs;
+       struct ieee80211_frame *wh;
+       struct ieee80211_key *k;
+       const struct chanAccParams *cap;
+       struct mbuf *mnew;
+       struct rt2661_dmamap map;
+       uint16_t dur;
+       uint32_t flags = 0;
+       int error, rate, noack = 0;
+
+       wh = mtod(m0, struct ieee80211_frame *);
+
+       if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
+               rs = &ic->ic_sup_rates[ic->ic_curmode];
+               rate = rs->rs_rates[ic->ic_fixed_rate];
+       } else {
+               rs = &ni->ni_rates;
+               rn = (struct rt2661_node *)ni;
+               ni->ni_txrate = ral_rssadapt_choose(&rn->rssadapt, rs,
+                   wh, m0->m_pkthdr.len, NULL, 0);
+               rate = rs->rs_rates[ni->ni_txrate];
+       }
+       rate &= IEEE80211_RATE_VAL;
+
+       if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
+               cap = &ic->ic_wme.wme_chanParams;
+               noack = cap->cap_wmeParams[ac].wmep_noackPolicy;
+       }
+
+       if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+               k = ieee80211_crypto_encap(ic, ni, m0);
+               if (k == NULL) {
+                       m_freem(m0);
+                       return ENOBUFS;
+               }
+
+               /* packet header may have moved, reset our local pointer */
+               wh = mtod(m0, struct ieee80211_frame *);
+       }
+
+       /*
+        * IEEE Std 802.11-1999, pp 82: "A STA shall use an RTS/CTS exchange
+        * for directed frames only when the length of the MPDU is greater
+        * than the length threshold indicated by [...]" ic_rtsthreshold.
+        */
+       if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+           m0->m_pkthdr.len > ic->ic_rtsthreshold) {
+               struct mbuf *m;
+               uint16_t dur;
+               int rtsrate, ackrate;
+
+               rtsrate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
+               ackrate = rt2661_ack_rate(ic, rate);
+
+               dur = rt2661_txtime(m0->m_pkthdr.len + 4, rate, ic->ic_flags) +
+                     rt2661_txtime(RAL_CTS_SIZE, rtsrate, ic->ic_flags) +
+                     /* XXX: noack (QoS)? */
+                     rt2661_txtime(RAL_ACK_SIZE, ackrate, ic->ic_flags) +
+                     3 * RAL_SIFS;
+
+               m = rt2661_get_rts(sc, wh, dur);
+
+               desc = &txq->desc[txq->cur];
+               data = &txq->data[txq->cur];
+
+               error = bus_dmamap_load_mbuf(txq->data_dmat, data->map, m,
+                                            rt2661_dma_map_mbuf, &map, 0);
+               if (error != 0) {
+                       device_printf(sc->sc_dev,
+                           "could not map mbuf (error %d)\n", error);
+                       m_freem(m);
+                       m_freem(m0);
+                       return error;
+               }
+
+               /* avoid multiple free() of the same node for each fragment */
+               ieee80211_ref_node(ni);
+
+               data->m = m;
+               data->ni = ni;
+
+               /* RTS frames are not taken into account for rssadapt */
+               data->id.id_node = NULL;
+
+               rt2661_setup_tx_desc(sc, desc, RT2661_TX_NEED_ACK |
+                                    RT2661_TX_MORE_FRAG, 0, m->m_pkthdr.len,
+                                    rtsrate, map.segs, map.nseg, ac);
+
+               bus_dmamap_sync(txq->data_dmat, data->map,
+                   BUS_DMASYNC_PREWRITE);
+
+               txq->queued++;
+               txq->cur = (txq->cur + 1) % RT2661_TX_RING_COUNT;
+
+               /*
+                * IEEE Std 802.11-1999: when an RTS/CTS exchange is used, the
+                * asynchronous data frame shall be transmitted after the CTS
+                * frame and a SIFS period.
+                */
+               flags |= RT2661_TX_LONG_RETRY | RT2661_TX_IFS;
+       }
+
+       data = &txq->data[txq->cur];
+       desc = &txq->desc[txq->cur];
+
+       error = bus_dmamap_load_mbuf(txq->data_dmat, data->map, m0,
+                                    rt2661_dma_map_mbuf, &map, 0);
+       if (error != 0 && error != EFBIG) {
+               device_printf(sc->sc_dev, "could not map mbuf (error %d)\n",
+                   error);
+               m_freem(m0);
+               return error;
+       }
+       if (error != 0) {
+               mnew = m_defrag(m0, MB_DONTWAIT);
+               if (mnew == NULL) {
+                       device_printf(sc->sc_dev,
+                           "could not defragment mbuf\n");
+                       m_freem(m0);
+                       return ENOBUFS;
+               }
+               m0 = mnew;
+
+               error = bus_dmamap_load_mbuf(txq->data_dmat, data->map, m0,
+                                            rt2661_dma_map_mbuf, &map, 0);
+               if (error != 0) {
+                       device_printf(sc->sc_dev,
+                           "could not map mbuf (error %d)\n", error);
+                       m_freem(m0);
+                       return error;
+               }
+
+               /* packet header have moved, reset our local pointer */
+               wh = mtod(m0, struct ieee80211_frame *);
+       }
+
+       if (sc->sc_drvbpf != NULL) {
+               struct rt2661_tx_radiotap_header *tap = &sc->sc_txtap;
+
+               tap->wt_flags = 0;
+               tap->wt_rate = rate;
+               tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+               tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+
+               bpf_ptap(sc->sc_drvbpf, m0, tap, sc->sc_txtap_len);
+       }
+
+       data->m = m0;
+       data->ni = ni;
+
+       /* remember link conditions for rate adaptation algorithm */
+       if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
+               data->id.id_len = m0->m_pkthdr.len;
+               data->id.id_rateidx = ni->ni_txrate;
+               data->id.id_node = ni;
+               data->id.id_rssi = ni->ni_rssi;
+       } else
+               data->id.id_node = NULL;
+
+       if (!noack && !IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+               flags |= RT2661_TX_NEED_ACK;
+
+               dur = rt2661_txtime(RAL_ACK_SIZE, rt2661_ack_rate(ic, rate),
+                   ic->ic_flags) + RAL_SIFS;
+               *(uint16_t *)wh->i_dur = htole16(dur);
+       }
+
+       rt2661_setup_tx_desc(sc, desc, flags, 0, m0->m_pkthdr.len, rate,
+                            map.segs, map.nseg, ac);
+
+       bus_dmamap_sync(txq->data_dmat, data->map, BUS_DMASYNC_PREWRITE);
+       bus_dmamap_sync(txq->desc_dmat, txq->desc_map, BUS_DMASYNC_PREWRITE);
+
+       DPRINTFN(10, ("sending data frame len=%u idx=%u rate=%u\n",
+           m0->m_pkthdr.len, txq->cur, rate));
+
+       /* kick Tx */
+       txq->queued++;
+       txq->cur = (txq->cur + 1) % RT2661_TX_RING_COUNT;
+       RAL_WRITE(sc, RT2661_TX_CNTL_CSR, 1 << ac);
+
+       return 0;
+}
+
+static void
+rt2661_start(struct ifnet *ifp)
+{
+       struct rt2661_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct mbuf *m0;
+       struct ether_header *eh;
+       struct ieee80211_node *ni;
+       int ac;
+
+       /* prevent management frames from being sent if we're not ready */
+       if (!(ifp->if_flags & IFF_RUNNING))
+               return;
+
+       for (;;) {
+               IF_POLL(&ic->ic_mgtq, m0);
+               if (m0 != NULL) {
+                       if (sc->mgtq.queued >= RT2661_MGT_RING_COUNT) {
+                               ifp->if_flags |= IFF_OACTIVE;
+                               break;
+                       }
+                       IF_DEQUEUE(&ic->ic_mgtq, m0);
+
+                       ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
+                       m0->m_pkthdr.rcvif = NULL;
+
+                       if (ic->ic_rawbpf != NULL)
+                               bpf_mtap(ic->ic_rawbpf, m0);
+
+                       if (rt2661_tx_mgt(sc, m0, ni) != 0)
+                               break;
+
+               } else {
+                       if (ic->ic_state != IEEE80211_S_RUN)
+                               break;
+
+                       m0 = ifq_dequeue(&ifp->if_snd, NULL);
+                       if (m0 == NULL)
+                               break;
+
+                       if (m0->m_len < sizeof (struct ether_header) &&
+                           !(m0 = m_pullup(m0, sizeof (struct ether_header))))
+                               continue;
+
+                       eh = mtod(m0, struct ether_header *);
+                       ni = ieee80211_find_txnode(ic, eh->ether_dhost);
+                       if (ni == NULL) {
+                               m_freem(m0);
+                               ifp->if_oerrors++;
+                               continue;
+                       }
+
+                       /* classify mbuf so we can find which tx ring to use */
+                       if (ieee80211_classify(ic, m0, ni) != 0) {
+                               m_freem(m0);
+                               ieee80211_free_node(ni);
+                               ifp->if_oerrors++;
+                               continue;
+                       }
+
+                       /* no QoS encapsulation for EAPOL frames */
+                       ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ?
+                           M_WME_GETAC(m0) : WME_AC_BE;
+
+                       if (sc->txq[ac].queued >= RT2661_TX_RING_COUNT - 1) {
+                               /* there is no place left in this ring */
+                               ifp->if_flags |= IFF_OACTIVE;
+                               m_freem(m0);
+                               ieee80211_free_node(ni);
+                               break;
+                       }
+
+                       BPF_MTAP(ifp, m0);
+
+                       m0 = ieee80211_encap(ic, m0, ni);
+                       if (m0 == NULL) {
+                               ieee80211_free_node(ni);
+                               ifp->if_oerrors++;
+                               continue;
+                       }
+
+                       if (ic->ic_rawbpf != NULL)
+                               bpf_mtap(ic->ic_rawbpf, m0);
+
+                       if (rt2661_tx_data(sc, m0, ni, ac) != 0) {
+                               ieee80211_free_node(ni);
+                               ifp->if_oerrors++;
+                               break;
+                       }
+               }
+
+               sc->sc_tx_timer = 5;
+               ifp->if_timer = 1;
+       }
+}
+
+static void
+rt2661_watchdog(struct ifnet *ifp)
+{
+       struct rt2661_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+
+       ifp->if_timer = 0;
+
+       if (sc->sc_tx_timer > 0) {
+               if (--sc->sc_tx_timer == 0) {
+                       device_printf(sc->sc_dev, "device timeout\n");
+                       rt2661_init(sc);
+                       ifp->if_oerrors++;
+                       return;
+               }
+               ifp->if_timer = 1;
+       }
+
+       ieee80211_watchdog(ic);
+}
+
+/*
+ * This function allows for fast channel switching in monitor mode (used by
+ * net-mgmt/kismet). In IBSS mode, we must explicitly reset the interface to
+ * generate a new beacon frame.
+ */
+static int
+rt2661_reset(struct ifnet *ifp)
+{
+       struct rt2661_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+
+       if (ic->ic_opmode != IEEE80211_M_MONITOR)
+               return ENETRESET;
+
+       rt2661_set_chan(sc, ic->ic_curchan);
+
+       return 0;
+}
+
+static int
+rt2661_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
+{
+       struct rt2661_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       int error = 0;
+
+       switch (cmd) {
+       case SIOCSIFFLAGS:
+               if (ifp->if_flags & IFF_UP) {
+                       if (ifp->if_flags & IFF_RUNNING)
+                               rt2661_update_promisc(sc);
+                       else
+                               rt2661_init(sc);
+               } else {
+                       if (ifp->if_flags & IFF_RUNNING)
+                               rt2661_stop(sc);
+               }
+               break;
+
+       default:
+               error = ieee80211_ioctl(ic, cmd, data, cr);
+       }
+
+       if (error == ENETRESET) {
+               if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
+                   (IFF_UP | IFF_RUNNING) &&
+                   (ic->ic_roaming != IEEE80211_ROAMING_MANUAL))
+                       rt2661_init(sc);
+               error = 0;
+       }
+       return error;
+}
+
+static void
+rt2661_bbp_write(struct rt2661_softc *sc, uint8_t reg, uint8_t val)
+{
+       uint32_t tmp;
+       int ntries;
+
+       for (ntries = 0; ntries < 100; ntries++) {
+               if (!(RAL_READ(sc, RT2661_PHY_CSR3) & RT2661_BBP_BUSY))
+                       break;
+               DELAY(1);
+       }
+       if (ntries == 100) {
+               device_printf(sc->sc_dev, "could not write to BBP\n");
+               return;
+       }
+
+       tmp = RT2661_BBP_BUSY | (reg & 0x7f) << 8 | val;
+       RAL_WRITE(sc, RT2661_PHY_CSR3, tmp);
+
+       DPRINTFN(15, ("BBP R%u <- 0x%02x\n", reg, val));
+}
+
+static uint8_t
+rt2661_bbp_read(struct rt2661_softc *sc, uint8_t reg)
+{
+       uint32_t val;
+       int ntries;
+
+       for (ntries = 0; ntries < 100; ntries++) {
+               if (!(RAL_READ(sc, RT2661_PHY_CSR3) & RT2661_BBP_BUSY))
+                       break;
+               DELAY(1);
+       }
+       if (ntries == 100) {
+               device_printf(sc->sc_dev, "could not read from BBP\n");
+               return 0;
+       }
+
+       val = RT2661_BBP_BUSY | RT2661_BBP_READ | reg << 8;
+       RAL_WRITE(sc, RT2661_PHY_CSR3, val);
+
+       for (ntries = 0; ntries < 100; ntries++) {
+               val = RAL_READ(sc, RT2661_PHY_CSR3);
+               if (!(val & RT2661_BBP_BUSY))
+                       return val & 0xff;
+               DELAY(1);
+       }
+
+       device_printf(sc->sc_dev, "could not read from BBP\n");
+       return 0;
+}
+
+static void
+rt2661_rf_write(struct rt2661_softc *sc, uint8_t reg, uint32_t val)
+{
+       uint32_t tmp;
+       int ntries;
+
+       for (ntries = 0; ntries < 100; ntries++) {
+               if (!(RAL_READ(sc, RT2661_PHY_CSR4) & RT2661_RF_BUSY))
+                       break;
+               DELAY(1);
+       }
+       if (ntries == 100) {
+               device_printf(sc->sc_dev, "could not write to RF\n");
+               return;
+       }
+
+       tmp = RT2661_RF_BUSY | RT2661_RF_21BIT | (val & 0x1fffff) << 2 |
+           (reg & 3);
+       RAL_WRITE(sc, RT2661_PHY_CSR4, tmp);
+
+       /* remember last written value in sc */
+       sc->rf_regs[reg] = val;
+
+       DPRINTFN(15, ("RF R[%u] <- 0x%05x\n", reg & 3, val & 0x1fffff));
+}
+
+static int
+rt2661_tx_cmd(struct rt2661_softc *sc, uint8_t cmd, uint16_t arg)
+{
+       if (RAL_READ(sc, RT2661_H2M_MAILBOX_CSR) & RT2661_H2M_BUSY)
+               return EIO;     /* there is already a command pending */
+
+       RAL_WRITE(sc, RT2661_H2M_MAILBOX_CSR,
+           RT2661_H2M_BUSY | RT2661_TOKEN_NO_INTR << 16 | arg);
+
+       RAL_WRITE(sc, RT2661_HOST_CMD_CSR, RT2661_KICK_CMD | cmd);
+
+       return 0;
+}
+
+static void
+rt2661_select_antenna(struct rt2661_softc *sc)
+{
+       uint8_t bbp4, bbp77;
+       uint32_t tmp;
+
+       bbp4  = rt2661_bbp_read(sc,  4);
+       bbp77 = rt2661_bbp_read(sc, 77);
+
+       /* TBD */
+
+       /* make sure Rx is disabled before switching antenna */
+       tmp = RAL_READ(sc, RT2661_TXRX_CSR0);
+       RAL_WRITE(sc, RT2661_TXRX_CSR0, tmp | RT2661_DISABLE_RX);
+
+       rt2661_bbp_write(sc,  4, bbp4);
+       rt2661_bbp_write(sc, 77, bbp77);
+
+       /* restore Rx filter */
+       RAL_WRITE(sc, RT2661_TXRX_CSR0, tmp);
+}
+
+/*
+ * Enable multi-rate retries for frames sent at OFDM rates.
+ * In 802.11b/g mode, allow fallback to CCK rates.
+ */
+static void
+rt2661_enable_mrr(struct rt2661_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       uint32_t tmp;
+
+       tmp = RAL_READ(sc, RT2661_TXRX_CSR4);
+
+       tmp &= ~RT2661_MRR_CCK_FALLBACK;
+       if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan))
+               tmp |= RT2661_MRR_CCK_FALLBACK;
+       tmp |= RT2661_MRR_ENABLED;
+
+       RAL_WRITE(sc, RT2661_TXRX_CSR4, tmp);
+}
+
+static void
+rt2661_set_txpreamble(struct rt2661_softc *sc)
+{
+       uint32_t tmp;
+
+       tmp = RAL_READ(sc, RT2661_TXRX_CSR4);
+
+       tmp &= ~RT2661_SHORT_PREAMBLE;
+       if (sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE)
+               tmp |= RT2661_SHORT_PREAMBLE;
+
+       RAL_WRITE(sc, RT2661_TXRX_CSR4, tmp);
+}
+
+static void
+rt2661_set_basicrates(struct rt2661_softc *sc,
+    const struct ieee80211_rateset *rs)
+{
+#define RV(r)  ((r) & IEEE80211_RATE_VAL)
+       uint32_t mask = 0;
+       uint8_t rate;
+       int i, j;
+
+       for (i = 0; i < rs->rs_nrates; i++) {
+               rate = rs->rs_rates[i];
+
+               if (!(rate & IEEE80211_RATE_BASIC))
+                       continue;
+
+               /*
+                * Find h/w rate index.  We know it exists because the rate
+                * set has already been negotiated.
+                */
+               for (j = 0; rt2661_rateset_11g.rs_rates[j] != RV(rate); j++);
+
+               mask |= 1 << j;
+       }
+
+       RAL_WRITE(sc, RT2661_TXRX_CSR5, mask);
+
+       DPRINTF(("Setting basic rate mask to 0x%x\n", mask));
+#undef RV
+}
+
+/*
+ * Reprogram MAC/BBP to switch to a new band.  Values taken from the reference
+ * driver.
+ */
+static void
+rt2661_select_band(struct rt2661_softc *sc, struct ieee80211_channel *c)
+{
+       uint8_t bbp17, bbp35, bbp96, bbp97, bbp98, bbp104;
+       uint32_t tmp;
+
+       /* update all BBP registers that depend on the band */
+       bbp17 = 0x20; bbp96 = 0x48; bbp104 = 0x2c;
+       bbp35 = 0x50; bbp97 = 0x48; bbp98  = 0x48;
+       if (IEEE80211_IS_CHAN_5GHZ(c)) {
+               bbp17 += 0x08; bbp96 += 0x10; bbp104 += 0x0c;
+               bbp35 += 0x10; bbp97 += 0x10; bbp98  += 0x10;
+       }
+       if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) ||
+           (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) {
+               bbp17 += 0x10; bbp96 += 0x10; bbp104 += 0x10;
+       }
+
+       rt2661_bbp_write(sc,  17, bbp17);
+       rt2661_bbp_write(sc,  96, bbp96);
+       rt2661_bbp_write(sc, 104, bbp104);
+
+       if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) ||
+           (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) {
+               rt2661_bbp_write(sc, 75, 0x80);
+               rt2661_bbp_write(sc, 86, 0x80);
+               rt2661_bbp_write(sc, 88, 0x80);
+       }
+
+       rt2661_bbp_write(sc, 35, bbp35);
+       rt2661_bbp_write(sc, 97, bbp97);
+       rt2661_bbp_write(sc, 98, bbp98);
+
+       tmp = RAL_READ(sc, RT2661_PHY_CSR0);
+       tmp &= ~(RT2661_PA_PE_2GHZ | RT2661_PA_PE_5GHZ);
+       if (IEEE80211_IS_CHAN_2GHZ(c))
+               tmp |= RT2661_PA_PE_2GHZ;
+       else
+               tmp |= RT2661_PA_PE_5GHZ;
+       RAL_WRITE(sc, RT2661_PHY_CSR0, tmp);
+}
+
+static void
+rt2661_set_chan(struct rt2661_softc *sc, struct ieee80211_channel *c)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       const struct rfprog *rfprog;
+       uint8_t bbp3, bbp94 = RT2661_BBPR94_DEFAULT;
+       int8_t power;
+       u_int i, chan;
+
+       chan = ieee80211_chan2ieee(ic, c);
+       if (chan == 0 || chan == IEEE80211_CHAN_ANY)
+               return;
+
+       /* select the appropriate RF settings based on what EEPROM says */
+       rfprog = (sc->rfprog == 0) ? rt2661_rf5225_1 : rt2661_rf5225_2;
+
+       /* find the settings for this channel (we know it exists) */
+       for (i = 0; rfprog[i].chan != chan; i++);
+
+       power = sc->txpow[i];
+       if (power < 0) {
+               bbp94 += power;
+               power = 0;
+       } else if (power > 31) {
+               bbp94 += power - 31;
+               power = 31;
+       }
+
+       /*
+        * If we are switching from the 2GHz band to the 5GHz band or
+        * vice-versa, BBP registers need to be reprogrammed.
+        */
+       if (c->ic_flags != sc->sc_curchan->ic_flags) {
+               rt2661_select_band(sc, c);
+               rt2661_select_antenna(sc);
+       }
+       sc->sc_curchan = c;
+
+       rt2661_rf_write(sc, RAL_RF1, rfprog[i].r1);
+       rt2661_rf_write(sc, RAL_RF2, rfprog[i].r2);
+       rt2661_rf_write(sc, RAL_RF3, rfprog[i].r3 | power << 7);
+       rt2661_rf_write(sc, RAL_RF4, rfprog[i].r4 | sc->rffreq << 10);
+
+       DELAY(200);
+
+       rt2661_rf_write(sc, RAL_RF1, rfprog[i].r1);
+       rt2661_rf_write(sc, RAL_RF2, rfprog[i].r2);
+       rt2661_rf_write(sc, RAL_RF3, rfprog[i].r3 | power << 7 | 1);
+       rt2661_rf_write(sc, RAL_RF4, rfprog[i].r4 | sc->rffreq << 10);
+
+       DELAY(200);
+
+       rt2661_rf_write(sc, RAL_RF1, rfprog[i].r1);
+       rt2661_rf_write(sc, RAL_RF2, rfprog[i].r2);
+       rt2661_rf_write(sc, RAL_RF3, rfprog[i].r3 | power << 7);
+       rt2661_rf_write(sc, RAL_RF4, rfprog[i].r4 | sc->rffreq << 10);
+
+       /* enable smart mode for MIMO-capable RFs */
+       bbp3 = rt2661_bbp_read(sc, 3);
+
+       bbp3 &= ~RT2661_SMART_MODE;
+       if (sc->rf_rev == RT2661_RF_5325 || sc->rf_rev == RT2661_RF_2529)
+               bbp3 |= RT2661_SMART_MODE;
+
+       rt2661_bbp_write(sc, 3, bbp3);
+
+       if (bbp94 != RT2661_BBPR94_DEFAULT)
+               rt2661_bbp_write(sc, 94, bbp94);
+
+       /* 5GHz radio needs a 1ms delay here */
+       if (IEEE80211_IS_CHAN_5GHZ(c))
+               DELAY(1000);
+}
+
+static void
+rt2661_set_bssid(struct rt2661_softc *sc, const uint8_t *bssid)
+{
+       uint32_t tmp;
+
+       tmp = bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24;
+       RAL_WRITE(sc, RT2661_MAC_CSR4, tmp);
+
+       tmp = bssid[4] | bssid[5] << 8 | RT2661_ONE_BSSID << 16;
+       RAL_WRITE(sc, RT2661_MAC_CSR5, tmp);
+}
+
+static void
+rt2661_set_macaddr(struct rt2661_softc *sc, const uint8_t *addr)
+{
+       uint32_t tmp;
+
+       tmp = addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24;
+       RAL_WRITE(sc, RT2661_MAC_CSR2, tmp);
+
+       tmp = addr[4] | addr[5] << 8;
+       RAL_WRITE(sc, RT2661_MAC_CSR3, tmp);
+}
+
+static void
+rt2661_update_promisc(struct rt2661_softc *sc)
+{
+       struct ifnet *ifp = sc->sc_ic.ic_ifp;
+       uint32_t tmp;
+
+       tmp = RAL_READ(sc, RT2661_TXRX_CSR0);
+
+       tmp &= ~RT2661_DROP_NOT_TO_ME;
+       if (!(ifp->if_flags & IFF_PROMISC))
+               tmp |= RT2661_DROP_NOT_TO_ME;
+
+       RAL_WRITE(sc, RT2661_TXRX_CSR0, tmp);
+
+       DPRINTF(("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ?
+           "entering" : "leaving"));
+}
+
+/*
+ * Update QoS (802.11e) settings for each h/w Tx ring.
+ */
+static int
+rt2661_wme_update(struct ieee80211com *ic)
+{
+       struct rt2661_softc *sc = ic->ic_ifp->if_softc;
+       const struct wmeParams *wmep;
+
+       wmep = ic->ic_wme.wme_chanParams.cap_wmeParams;
+
+       /* XXX: not sure about shifts. */
+       /* XXX: the reference driver plays with AC_VI settings too. */
+
+       /* update TxOp */
+       RAL_WRITE(sc, RT2661_AC_TXOP_CSR0,
+           wmep[WME_AC_BE].wmep_txopLimit << 16 |
+           wmep[WME_AC_BK].wmep_txopLimit);
+       RAL_WRITE(sc, RT2661_AC_TXOP_CSR1,
+           wmep[WME_AC_VI].wmep_txopLimit << 16 |
+           wmep[WME_AC_VO].wmep_txopLimit);
+
+       /* update CWmin */
+       RAL_WRITE(sc, RT2661_CWMIN_CSR,
+           wmep[WME_AC_BE].wmep_logcwmin << 12 |
+           wmep[WME_AC_BK].wmep_logcwmin <<  8 |
+           wmep[WME_AC_VI].wmep_logcwmin <<  4 |
+           wmep[WME_AC_VO].wmep_logcwmin);
+
+       /* update CWmax */
+       RAL_WRITE(sc, RT2661_CWMAX_CSR,
+           wmep[WME_AC_BE].wmep_logcwmax << 12 |
+           wmep[WME_AC_BK].wmep_logcwmax <<  8 |
+           wmep[WME_AC_VI].wmep_logcwmax <<  4 |
+           wmep[WME_AC_VO].wmep_logcwmax);
+
+       /* update Aifsn */
+       RAL_WRITE(sc, RT2661_AIFSN_CSR,
+           wmep[WME_AC_BE].wmep_aifsn << 12 |
+           wmep[WME_AC_BK].wmep_aifsn <<  8 |
+           wmep[WME_AC_VI].wmep_aifsn <<  4 |
+           wmep[WME_AC_VO].wmep_aifsn);
+
+       return 0;
+}
+
+static void
+rt2661_update_slot(struct ifnet *ifp)
+{
+       struct rt2661_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       uint8_t slottime;
+       uint32_t tmp;
+
+       slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20;
+
+       tmp = RAL_READ(sc, RT2661_MAC_CSR9);
+       tmp = (tmp & ~0xff) | slottime;
+       RAL_WRITE(sc, RT2661_MAC_CSR9, tmp);
+}
+
+static const char *
+rt2661_get_rf(int rev)
+{
+       switch (rev) {
+       case RT2661_RF_5225:    return "RT5225";
+       case RT2661_RF_5325:    return "RT5325 (MIMO XR)";
+       case RT2661_RF_2527:    return "RT2527";
+       case RT2661_RF_2529:    return "RT2529 (MIMO XR)";
+       default:                return "unknown";
+       }
+}
+
+static void
+rt2661_read_eeprom(struct rt2661_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       uint16_t val;
+       int i;
+
+       /* read MAC address */
+       val = rt2661_eeprom_read(sc, RT2661_EEPROM_MAC01);
+       ic->ic_myaddr[0] = val & 0xff;
+       ic->ic_myaddr[1] = val >> 8;
+
+       val = rt2661_eeprom_read(sc, RT2661_EEPROM_MAC23);
+       ic->ic_myaddr[2] = val & 0xff;
+       ic->ic_myaddr[3] = val >> 8;
+
+       val = rt2661_eeprom_read(sc, RT2661_EEPROM_MAC45);
+       ic->ic_myaddr[4] = val & 0xff;
+       ic->ic_myaddr[5] = val >> 8;
+
+       val = rt2661_eeprom_read(sc, RT2661_EEPROM_ANTENNA);
+       /* XXX: test if different from 0xffff? */
+       sc->rf_rev   = (val >> 11) & 0x1f;
+       sc->hw_radio = (val >> 10) & 0x1;
+       sc->rx_ant   = (val >> 4)  & 0x3;
+       sc->tx_ant   = (val >> 2)  & 0x3;
+       sc->nb_ant   = val & 0x3;
+
+       DPRINTF(("RF revision=%d\n", sc->rf_rev));
+
+       val = rt2661_eeprom_read(sc, RT2661_EEPROM_CONFIG2);
+       sc->ext_5ghz_lna = (val >> 6) & 0x1;
+       sc->ext_2ghz_lna = (val >> 4) & 0x1;
+
+       DPRINTF(("External 2GHz LNA=%d\nExternal 5GHz LNA=%d\n",
+           sc->ext_2ghz_lna, sc->ext_5ghz_lna));
+
+       val = rt2661_eeprom_read(sc, RT2661_EEPROM_RSSI_2GHZ_OFFSET);
+       if ((val & 0xff) != 0xff)
+               sc->rssi_2ghz_corr = (int8_t)(val & 0xff);      /* signed */
+
+       val = rt2661_eeprom_read(sc, RT2661_EEPROM_RSSI_5GHZ_OFFSET);
+       if ((val & 0xff) != 0xff)
+               sc->rssi_5ghz_corr = (int8_t)(val & 0xff);      /* signed */
+
+       /* adjust RSSI correction for external low-noise amplifier */
+       if (sc->ext_2ghz_lna)
+               sc->rssi_2ghz_corr -= 14;
+       if (sc->ext_5ghz_lna)
+               sc->rssi_5ghz_corr -= 14;
+
+       DPRINTF(("RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n",
+           sc->rssi_2ghz_corr, sc->rssi_5ghz_corr));
+
+       val = rt2661_eeprom_read(sc, RT2661_EEPROM_FREQ_OFFSET);
+       if ((val >> 8) != 0xff)
+               sc->rfprog = (val >> 8) & 0x3;
+       if ((val & 0xff) != 0xff)
+               sc->rffreq = val & 0xff;
+
+       DPRINTF(("RF prog=%d\nRF freq=%d\n", sc->rfprog, sc->rffreq));
+
+       /* read Tx power for all a/b/g channels */
+       for (i = 0; i < 19; i++) {
+               val = rt2661_eeprom_read(sc, RT2661_EEPROM_TXPOWER + i);
+               sc->txpow[i * 2] = (int8_t)(val >> 8);          /* signed */
+               DPRINTF(("Channel=%d Tx power=%d\n",
+                   rt2661_rf5225_1[i * 2].chan, sc->txpow[i * 2]));
+               sc->txpow[i * 2 + 1] = (int8_t)(val & 0xff);    /* signed */
+               DPRINTF(("Channel=%d Tx power=%d\n",
+                   rt2661_rf5225_1[i * 2 + 1].chan, sc->txpow[i * 2 + 1]));
+       }
+
+       /* read vendor-specific BBP values */
+       for (i = 0; i < 16; i++) {
+               val = rt2661_eeprom_read(sc, RT2661_EEPROM_BBP_BASE + i);
+               if (val == 0 || val == 0xffff)
+                       continue;       /* skip invalid entries */
+               sc->bbp_prom[i].reg = val >> 8;
+               sc->bbp_prom[i].val = val & 0xff;
+               DPRINTF(("BBP R%d=%02x\n", sc->bbp_prom[i].reg,
+                   sc->bbp_prom[i].val));
+       }
+}
+
+static int
+rt2661_bbp_init(struct rt2661_softc *sc)
+{
+#define N(a)   (sizeof (a) / sizeof ((a)[0]))
+       int i, ntries;
+       uint8_t val;
+
+       /* wait for BBP to be ready */
+       for (ntries = 0; ntries < 100; ntries++) {
+               val = rt2661_bbp_read(sc, 0);
+               if (val != 0 && val != 0xff)
+                       break;
+               DELAY(100);
+       }
+       if (ntries == 100) {
+               device_printf(sc->sc_dev, "timeout waiting for BBP\n");
+               return EIO;
+       }
+
+       /* initialize BBP registers to default values */
+       for (i = 0; i < N(rt2661_def_bbp); i++) {
+               rt2661_bbp_write(sc, rt2661_def_bbp[i].reg,
+                   rt2661_def_bbp[i].val);
+       }
+
+       /* write vendor-specific BBP values (from EEPROM) */
+       for (i = 0; i < 16; i++) {
+               if (sc->bbp_prom[i].reg == 0)
+                       continue;
+               rt2661_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val);
+       }
+
+       return 0;
+#undef N
+}
+
+static void
+rt2661_init(void *priv)
+{
+#define N(a)   (sizeof (a) / sizeof ((a)[0]))
+       struct rt2661_softc *sc = priv;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = ic->ic_ifp;
+       uint32_t tmp, sta[3];
+       int i, ntries;
+
+       rt2661_stop(sc);
+
+       /* initialize Tx rings */
+       RAL_WRITE(sc, RT2661_AC1_BASE_CSR, sc->txq[1].physaddr);
+       RAL_WRITE(sc, RT2661_AC0_BASE_CSR, sc->txq[0].physaddr);
+       RAL_WRITE(sc, RT2661_AC2_BASE_CSR, sc->txq[2].physaddr);
+       RAL_WRITE(sc, RT2661_AC3_BASE_CSR, sc->txq[3].physaddr);
+
+       /* initialize Mgt ring */
+       RAL_WRITE(sc, RT2661_MGT_BASE_CSR, sc->mgtq.physaddr);
+
+       /* initialize Rx ring */
+       RAL_WRITE(sc, RT2661_RX_BASE_CSR, sc->rxq.physaddr);
+
+       /* initialize Tx rings sizes */
+       RAL_WRITE(sc, RT2661_TX_RING_CSR0,
+           RT2661_TX_RING_COUNT << 24 |
+           RT2661_TX_RING_COUNT << 16 |
+           RT2661_TX_RING_COUNT <<  8 |
+           RT2661_TX_RING_COUNT);
+
+       RAL_WRITE(sc, RT2661_TX_RING_CSR1,
+           RT2661_TX_DESC_WSIZE << 16 |
+           RT2661_TX_RING_COUNT <<  8 |        /* XXX: HCCA ring unused */
+           RT2661_MGT_RING_COUNT);
+
+       /* initialize Rx rings */
+       RAL_WRITE(sc, RT2661_RX_RING_CSR,
+           RT2661_RX_DESC_BACK  << 16 |
+           RT2661_RX_DESC_WSIZE <<  8 |
+           RT2661_RX_RING_COUNT);
+
+       /* XXX: some magic here */
+       RAL_WRITE(sc, RT2661_TX_DMA_DST_CSR, 0xaa);
+
+       /* load base addresses of all 5 Tx rings (4 data + 1 mgt) */
+       RAL_WRITE(sc, RT2661_LOAD_TX_RING_CSR, 0x1f);
+
+       /* load base address of Rx ring */
+       RAL_WRITE(sc, RT2661_RX_CNTL_CSR, 2);
+
+       /* initialize MAC registers to default values */
+       for (i = 0; i < N(rt2661_def_mac); i++)
+               RAL_WRITE(sc, rt2661_def_mac[i].reg, rt2661_def_mac[i].val);
+
+       IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
+       rt2661_set_macaddr(sc, ic->ic_myaddr);
+
+       /* set host ready */
+       RAL_WRITE(sc, RT2661_MAC_CSR1, 3);
+       RAL_WRITE(sc, RT2661_MAC_CSR1, 0);
+
+       /* wait for BBP/RF to wakeup */
+       for (ntries = 0; ntries < 1000; ntries++) {
+               if (RAL_READ(sc, RT2661_MAC_CSR12) & 8)
+                       break;
+               DELAY(1000);
+       }
+       if (ntries == 1000) {
+               printf("timeout waiting for BBP/RF to wakeup\n");
+               rt2661_stop(sc);
+               return;
+       }
+
+       if (rt2661_bbp_init(sc) != 0) {
+               rt2661_stop(sc);
+               return;
+       }
+
+       /* select default channel */
+       sc->sc_curchan = ic->ic_curchan;
+       rt2661_select_band(sc, sc->sc_curchan);
+       rt2661_select_antenna(sc);
+       rt2661_set_chan(sc, sc->sc_curchan);
+
+       /* update Rx filter */
+       tmp = RAL_READ(sc, RT2661_TXRX_CSR0) & 0xffff;
+
+       tmp |= RT2661_DROP_PHY_ERROR | RT2661_DROP_CRC_ERROR;
+       if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+               tmp |= RT2661_DROP_CTL | RT2661_DROP_VER_ERROR |
+                      RT2661_DROP_ACKCTS;
+               if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+                       tmp |= RT2661_DROP_TODS;
+               if (!(ifp->if_flags & IFF_PROMISC))
+                       tmp |= RT2661_DROP_NOT_TO_ME;
+       }
+
+       RAL_WRITE(sc, RT2661_TXRX_CSR0, tmp);
+
+       /* clear STA registers */
+       RAL_READ_REGION_4(sc, RT2661_STA_CSR0, sta, N(sta));
+
+       /* initialize ASIC */
+       RAL_WRITE(sc, RT2661_MAC_CSR1, 4);
+
+       /* clear any pending interrupt */
+       RAL_WRITE(sc, RT2661_INT_SOURCE_CSR, 0xffffffff);
+
+       /* enable interrupts */
+       RAL_WRITE(sc, RT2661_INT_MASK_CSR, 0x0000ff10);
+       RAL_WRITE(sc, RT2661_MCU_INT_MASK_CSR, 0);
+
+       /* kick Rx */
+       RAL_WRITE(sc, RT2661_RX_CNTL_CSR, 1);
+
+       ifp->if_flags &= ~IFF_OACTIVE;
+       ifp->if_flags |= IFF_RUNNING;
+
+       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);
+#undef N
+}
+
+void
+rt2661_stop(void *priv)
+{
+       struct rt2661_softc *sc = priv;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = ic->ic_ifp;
+       uint32_t tmp;
+
+       sc->sc_tx_timer = 0;
+       ifp->if_timer = 0;
+       ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+
+       ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+
+       /* abort Tx (for all 5 Tx rings) */
+       RAL_WRITE(sc, RT2661_TX_CNTL_CSR, 0x1f << 16);
+
+       /* disable Rx (value remains after reset!) */
+       tmp = RAL_READ(sc, RT2661_TXRX_CSR0);
+       RAL_WRITE(sc, RT2661_TXRX_CSR0, tmp | RT2661_DISABLE_RX);
+
+       /* reset ASIC */
+       RAL_WRITE(sc, RT2661_MAC_CSR1, 3);
+       RAL_WRITE(sc, RT2661_MAC_CSR1, 0);
+
+       /* disable interrupts */
+       RAL_WRITE(sc, RT2661_INT_MASK_CSR, 0xffffffff);
+       RAL_WRITE(sc, RT2661_MCU_INT_MASK_CSR, 0xffffffff);
+
+       /* clear any pending interrupt */
+       RAL_WRITE(sc, RT2661_INT_SOURCE_CSR, 0xffffffff);
+       RAL_WRITE(sc, RT2661_MCU_INT_SOURCE_CSR, 0xffffffff);
+
+       /* reset Tx and Rx rings */
+       rt2661_reset_tx_ring(sc, &sc->txq[0]);
+       rt2661_reset_tx_ring(sc, &sc->txq[1]);
+       rt2661_reset_tx_ring(sc, &sc->txq[2]);
+       rt2661_reset_tx_ring(sc, &sc->txq[3]);
+       rt2661_reset_tx_ring(sc, &sc->mgtq);
+       rt2661_reset_rx_ring(sc, &sc->rxq);
+}
+
+static int
+rt2661_load_microcode(struct rt2661_softc *sc, const uint8_t *ucode, int size)
+{
+       int ntries;
+
+       /* reset 8051 */
+       RAL_WRITE(sc, RT2661_MCU_CNTL_CSR, RT2661_MCU_RESET);
+
+       /* cancel any pending Host to MCU command */
+       RAL_WRITE(sc, RT2661_H2M_MAILBOX_CSR, 0);
+       RAL_WRITE(sc, RT2661_M2H_CMD_DONE_CSR, 0xffffffff);
+       RAL_WRITE(sc, RT2661_HOST_CMD_CSR, 0);
+
+       /* write 8051's microcode */
+       RAL_WRITE(sc, RT2661_MCU_CNTL_CSR, RT2661_MCU_RESET | RT2661_MCU_SEL);
+       RAL_WRITE_REGION_1(sc, RT2661_MCU_CODE_BASE, ucode, size);
+       RAL_WRITE(sc, RT2661_MCU_CNTL_CSR, RT2661_MCU_RESET);
+
+       /* kick 8051's ass */
+       RAL_WRITE(sc, RT2661_MCU_CNTL_CSR, 0);
+
+       /* wait for 8051 to initialize */
+       for (ntries = 0; ntries < 500; ntries++) {
+               if (RAL_READ(sc, RT2661_MCU_CNTL_CSR) & RT2661_MCU_READY)
+                       break;
+               DELAY(100);
+       }
+       if (ntries == 500) {
+               printf("timeout waiting for MCU to initialize\n");
+               return EIO;
+       }
+       return 0;
+}
+
+#ifdef notyet
+/*
+ * Dynamically tune Rx sensitivity (BBP register 17) based on average RSSI and
+ * false CCA count.  This function is called periodically (every seconds) when
+ * in the RUN state.  Values taken from the reference driver.
+ */
+static void
+rt2661_rx_tune(struct rt2661_softc *sc)
+{
+       uint8_t bbp17;
+       uint16_t cca;
+       int lo, hi, dbm;
+
+       /*
+        * Tuning range depends on operating band and on the presence of an
+        * external low-noise amplifier.
+        */
+       lo = 0x20;
+       if (IEEE80211_IS_CHAN_5GHZ(sc->sc_curchan))
+               lo += 0x08;
+       if ((IEEE80211_IS_CHAN_2GHZ(sc->sc_curchan) && sc->ext_2ghz_lna) ||
+           (IEEE80211_IS_CHAN_5GHZ(sc->sc_curchan) && sc->ext_5ghz_lna))
+               lo += 0x10;
+       hi = lo + 0x20;
+
+       /* retrieve false CCA count since last call (clear on read) */
+       cca = RAL_READ(sc, RT2661_STA_CSR1) & 0xffff;
+
+       if (dbm >= -35) {
+               bbp17 = 0x60;
+       } else if (dbm >= -58) {
+               bbp17 = hi;
+       } else if (dbm >= -66) {
+               bbp17 = lo + 0x10;
+       } else if (dbm >= -74) {
+               bbp17 = lo + 0x08;
+       } else {
+               /* RSSI < -74dBm, tune using false CCA count */
+
+               bbp17 = sc->bbp17; /* current value */
+
+               hi -= 2 * (-74 - dbm);
+               if (hi < lo)
+                       hi = lo;
+
+               if (bbp17 > hi) {
+                       bbp17 = hi;
+
+               } else if (cca > 512) {
+                       if (++bbp17 > hi)
+                               bbp17 = hi;
+               } else if (cca < 100) {
+                       if (--bbp17 < lo)
+                               bbp17 = lo;
+               }
+       }
+
+       if (bbp17 != sc->bbp17) {
+               rt2661_bbp_write(sc, 17, bbp17);
+               sc->bbp17 = bbp17;
+       }
+}
+
+/*
+ * Enter/Leave radar detection mode.
+ * This is for 802.11h additional regulatory domains.
+ */
+static void
+rt2661_radar_start(struct rt2661_softc *sc)
+{
+       uint32_t tmp;
+
+       /* disable Rx */
+       tmp = RAL_READ(sc, RT2661_TXRX_CSR0);
+       RAL_WRITE(sc, RT2661_TXRX_CSR0, tmp | RT2661_DISABLE_RX);
+
+       rt2661_bbp_write(sc, 82, 0x20);
+       rt2661_bbp_write(sc, 83, 0x00);
+       rt2661_bbp_write(sc, 84, 0x40);
+
+       /* save current BBP registers values */
+       sc->bbp18 = rt2661_bbp_read(sc, 18);
+       sc->bbp21 = rt2661_bbp_read(sc, 21);
+       sc->bbp22 = rt2661_bbp_read(sc, 22);
+       sc->bbp16 = rt2661_bbp_read(sc, 16);
+       sc->bbp17 = rt2661_bbp_read(sc, 17);
+       sc->bbp64 = rt2661_bbp_read(sc, 64);
+
+       rt2661_bbp_write(sc, 18, 0xff);
+       rt2661_bbp_write(sc, 21, 0x3f);
+       rt2661_bbp_write(sc, 22, 0x3f);
+       rt2661_bbp_write(sc, 16, 0xbd);
+       rt2661_bbp_write(sc, 17, sc->ext_5ghz_lna ? 0x44 : 0x34);
+       rt2661_bbp_write(sc, 64, 0x21);
+
+       /* restore Rx filter */
+       RAL_WRITE(sc, RT2661_TXRX_CSR0, tmp);
+}
+
+static int
+rt2661_radar_stop(struct rt2661_softc *sc)
+{
+       uint8_t bbp66;
+
+       /* read radar detection result */
+       bbp66 = rt2661_bbp_read(sc, 66);
+
+       /* restore BBP registers values */
+       rt2661_bbp_write(sc, 16, sc->bbp16);
+       rt2661_bbp_write(sc, 17, sc->bbp17);
+       rt2661_bbp_write(sc, 18, sc->bbp18);
+       rt2661_bbp_write(sc, 21, sc->bbp21);
+       rt2661_bbp_write(sc, 22, sc->bbp22);
+       rt2661_bbp_write(sc, 64, sc->bbp64);
+
+       return bbp66 == 1;
+}
+#endif
+
+static int
+rt2661_prepare_beacon(struct rt2661_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ieee80211_beacon_offsets bo;
+       struct rt2661_tx_desc desc;
+       struct mbuf *m0;
+       int rate;
+
+       m0 = ieee80211_beacon_alloc(ic, ic->ic_bss, &bo);
+       if (m0 == NULL) {
+               device_printf(sc->sc_dev, "could not allocate beacon frame\n");
+               return ENOBUFS;
+       }
+
+       /* send beacons at the lowest available rate */
+       rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan) ? 12 : 2;
+
+       rt2661_setup_tx_desc(sc, &desc, RT2661_TX_TIMESTAMP, RT2661_TX_HWSEQ,
+           m0->m_pkthdr.len, rate, NULL, 0, RT2661_QID_MGT);
+
+       /* copy the first 24 bytes of Tx descriptor into NIC memory */
+       RAL_WRITE_REGION_1(sc, RT2661_HW_BEACON_BASE0, (uint8_t *)&desc, 24);
+
+       /* copy beacon header and payload into NIC memory */
+       RAL_WRITE_REGION_1(sc, RT2661_HW_BEACON_BASE0 + 24,
+           mtod(m0, uint8_t *), m0->m_pkthdr.len);
+
+       m_freem(m0);
+       return 0;
+}
+
+/*
+ * Enable TSF synchronization and tell h/w to start sending beacons for IBSS
+ * and HostAP operating modes.
+ */
+static void
+rt2661_enable_tsf_sync(struct rt2661_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       uint32_t tmp;
+
+       if (ic->ic_opmode != IEEE80211_M_STA) {
+               /*
+                * Change default 16ms TBTT adjustment to 8ms.
+                * Must be done before enabling beacon generation.
+                */
+               RAL_WRITE(sc, RT2661_TXRX_CSR10, 1 << 12 | 8);
+       }
+
+       tmp = RAL_READ(sc, RT2661_TXRX_CSR9) & 0xff000000;
+
+       /* set beacon interval (in 1/16ms unit) */
+       tmp |= ic->ic_bss->ni_intval * 16;
+
+       tmp |= RT2661_TSF_TICKING | RT2661_ENABLE_TBTT;
+       if (ic->ic_opmode == IEEE80211_M_STA)
+               tmp |= RT2661_TSF_MODE(1);
+       else
+               tmp |= RT2661_TSF_MODE(2) | RT2661_GENERATE_BEACON;
+
+       RAL_WRITE(sc, RT2661_TXRX_CSR9, tmp);
+}
+
+/*
+ * Retrieve the "Received Signal Strength Indicator" from the raw values
+ * contained in Rx descriptors.  The computation depends on which band the
+ * frame was received.  Correction values taken from the reference driver.
+ */
+static int
+rt2661_get_rssi(struct rt2661_softc *sc, uint8_t raw)
+{
+       int lna, agc, rssi;
+
+       lna = (raw >> 5) & 0x3;
+       agc = raw & 0x1f;
+
+       rssi = 2 * agc;
+
+       if (IEEE80211_IS_CHAN_2GHZ(sc->sc_curchan)) {
+               rssi += sc->rssi_2ghz_corr;
+
+               if (lna == 1)
+                       rssi -= 64;
+               else if (lna == 2)
+                       rssi -= 74;
+               else if (lna == 3)
+                       rssi -= 90;
+       } else {
+               rssi += sc->rssi_5ghz_corr;
+
+               if (lna == 1)
+                       rssi -= 64;
+               else if (lna == 2)
+                       rssi -= 86;
+               else if (lna == 3)
+                       rssi -= 100;
+       }
+       return rssi;
+}
+
+static void
+rt2661_dma_map_mbuf(void *arg, bus_dma_segment_t *seg, int nseg,
+                   bus_size_t map_size __unused, int error)
+{
+       struct rt2661_dmamap *map = arg;
+
+       if (error)
+               return;
+
+       KASSERT(nseg <= RT2661_MAX_SCATTER, ("too many DMA segments"));
+
+       bcopy(seg, map->segs, nseg * sizeof(bus_dma_segment_t));
+       map->nseg = nseg;
+}
diff --git a/sys/dev/netif/ral/rt2661_ucode.h b/sys/dev/netif/ral/rt2661_ucode.h
new file mode 100644 (file)
index 0000000..ca9d546
--- /dev/null
@@ -0,0 +1,2269 @@
+/*
+ * Copyright (c) 2005-2006, Ralink Technology, Corp.
+ *     Paul Lin <paul_lin@ralinktech.com.tw>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ * 
+ * $OpenBSD: microcode.h,v 1.1 2006/01/09 20:03:40 damien Exp $
+ * $FreeBSD: src/sys/dev/ral/rt2661_ucode.h,v 1.1 2006/03/05 20:41:51 damien Exp $
+ * $DragonFly: src/sys/dev/netif/ral/rt2661_ucode.h,v 1.1 2006/05/20 09:13:09 sephe Exp $
+ */
+
+/*
+ * This file contains the loadable 8051 microcodes for the Ralink RT2561,
+ * RT2561S and RT2661 chipsets.
+ */
+
+static const uint8_t rt2561_ucode[] = {
+       0x02, 0x1c, 0x12, 0x02, 0x13, 0xcb, 0xc2, 0x8c, 0x22, 0x22, 0x00,
+       0x02, 0x16, 0x0f, 0xc2, 0xaf, 0xc2, 0x8d, 0x75, 0x8c, 0x94, 0x75,
+       0x8a, 0x93, 0xd2, 0xaf, 0x22, 0x02, 0x18, 0xda, 0x12, 0x1b, 0xe8,
+       0x40, 0x03, 0x02, 0x02, 0x1e, 0x90, 0x21, 0x02, 0xe0, 0xf5, 0x2d,
+       0x90, 0x00, 0x03, 0xe0, 0x12, 0x08, 0x25, 0x00, 0xb0, 0x00, 0x00,
+       0xce, 0x01, 0x00, 0x5e, 0x10, 0x00, 0x6f, 0x11, 0x00, 0xf2, 0x20,
+       0x01, 0x4d, 0x21, 0x01, 0x70, 0x22, 0x01, 0x84, 0x30, 0x01, 0x8f,
+       0x31, 0x01, 0xd5, 0x50, 0x01, 0x9f, 0x51, 0x01, 0xf2, 0x52, 0x02,
+       0x06, 0x60, 0x00, 0x00, 0x02, 0x14, 0x90, 0x00, 0x0a, 0xe0, 0x20,
+       0xe5, 0x03, 0x30, 0x07, 0x03, 0xd2, 0x08, 0x22, 0x12, 0x17, 0xa5,
+       0x22, 0x90, 0x21, 0x00, 0xe0, 0xf5, 0x11, 0xe5, 0x11, 0xc4, 0x33,
+       0x54, 0xe0, 0x24, 0x21, 0xf5, 0x82, 0xe4, 0x34, 0x21, 0xf5, 0x83,
+       0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x11, 0xc4, 0x33, 0x54, 0xe0, 0x24,
+       0x2c, 0xf5, 0x82, 0xe4, 0x34, 0x21, 0xf5, 0x83, 0xe5, 0x11, 0xf0,
+       0xc4, 0x33, 0x54, 0xe0, 0x24, 0x2d, 0xf5, 0x82, 0xe4, 0x34, 0x21,
+       0xf5, 0x83, 0xe5, 0x2d, 0xf0, 0xe4, 0x90, 0x21, 0x03, 0xf0, 0x22,
+       0x12, 0x11, 0x31, 0x90, 0x21, 0x00, 0xe0, 0xf5, 0x31, 0x60, 0x05,
+       0x12, 0x1b, 0x8a, 0x80, 0x03, 0x12, 0x1b, 0x3d, 0xe4, 0x90, 0x21,
+       0x03, 0xf0, 0xaf, 0x2d, 0x12, 0x1c, 0x62, 0x22, 0x75, 0x31, 0xff,
+       0x90, 0x01, 0x00, 0xe0, 0x54, 0xf7, 0xf0, 0x90, 0x01, 0x01, 0xe0,
+       0x54, 0xfe, 0xf0, 0x54, 0x3e, 0xf0, 0xe4, 0x90, 0x00, 0x0b, 0xf0,
+       0xf0, 0x90, 0x21, 0x03, 0xf0, 0xaf, 0x2d, 0x12, 0x1c, 0x62, 0x22,
+       0x7e, 0x2b, 0x7f, 0x80, 0x7d, 0x03, 0x12, 0x04, 0x0e, 0x90, 0x34,
+       0xcd, 0xe0, 0x20, 0xe3, 0xf9, 0x90, 0x21, 0x14, 0x12, 0x08, 0x01,
+       0x90, 0x34, 0xc0, 0x12, 0x08, 0x0d, 0x90, 0x21, 0x18, 0x12, 0x08,
+       0x01, 0x90, 0x34, 0xc8, 0x12, 0x08, 0x0d, 0x90, 0x21, 0x1c, 0x12,
+       0x08, 0x01, 0x90, 0x34, 0xc4, 0x12, 0x08, 0x0d, 0x90, 0x34, 0xcc,
+       0x74, 0x01, 0xf0, 0xa3, 0xe0, 0x44, 0x04, 0xf0, 0x90, 0x01, 0x01,
+       0xe0, 0x44, 0x01, 0xf0, 0x44, 0x40, 0xf0, 0x90, 0x00, 0x0b, 0xe0,
+       0x44, 0x10, 0xf0, 0xe4, 0x90, 0x21, 0x03, 0xf0, 0xaf, 0x2d, 0x12,
+       0x1c, 0x62, 0x22, 0x90, 0x01, 0x00, 0xe0, 0x54, 0xf7, 0xf0, 0x90,
+       0x01, 0x01, 0xe0, 0x54, 0xfe, 0xf0, 0x54, 0xbf, 0xf0, 0x90, 0x00,
+       0x0b, 0xe0, 0x54, 0xef, 0xf0, 0xe4, 0x90, 0x21, 0x03, 0xf0, 0xaf,
+       0x2d, 0x12, 0x1c, 0x62, 0x22, 0x7e, 0x2b, 0x7f, 0x80, 0x7d, 0x03,
+       0x12, 0x04, 0x0e, 0xe4, 0x90, 0x21, 0x03, 0xf0, 0xaf, 0x2d, 0x12,
+       0x1c, 0x62, 0x22, 0xd2, 0x05, 0x85, 0x2d, 0x23, 0xe4, 0x90, 0x21,
+       0x03, 0xf0, 0x22, 0x12, 0x1a, 0x74, 0xc2, 0x00, 0xe4, 0x90, 0x21,
+       0x03, 0xf0, 0xaf, 0x2d, 0x12, 0x1c, 0x62, 0x22, 0x85, 0x2d, 0x25,
+       0x90, 0x00, 0x0b, 0xe0, 0x54, 0xfb, 0xff, 0xf0, 0xe4, 0x90, 0x00,
+       0x07, 0xf0, 0x90, 0x00, 0x0a, 0x74, 0x04, 0xf0, 0xe4, 0x90, 0x00,
+       0x08, 0xf0, 0x90, 0x21, 0x00, 0xe0, 0x90, 0x00, 0x09, 0xf0, 0x90,
+       0x00, 0x07, 0x74, 0x71, 0xf0, 0xef, 0x44, 0x04, 0x90, 0x00, 0x0b,
+       0xf0, 0xe4, 0x90, 0x21, 0x03, 0xf0, 0x22, 0x90, 0x21, 0x00, 0xe0,
+       0xff, 0x54, 0x1f, 0xf5, 0x30, 0xa3, 0xe0, 0xf5, 0x27, 0x8f, 0x26,
+       0x12, 0x08, 0x90, 0xe4, 0x90, 0x21, 0x03, 0xf0, 0xaf, 0x2d, 0x12,
+       0x1c, 0x62, 0x22, 0x90, 0x21, 0x00, 0xe0, 0xf5, 0x2c, 0x12, 0x18,
+       0x13, 0xe4, 0x90, 0x21, 0x03, 0xf0, 0xaf, 0x2d, 0x12, 0x1c, 0x62,
+       0x22, 0x12, 0x19, 0x53, 0xe4, 0x90, 0x21, 0x03, 0xf0, 0xaf, 0x2d,
+       0x12, 0x1c, 0x62, 0x22, 0xe4, 0x90, 0x21, 0x03, 0xf0, 0xaf, 0x2d,
+       0x12, 0x1c, 0x62, 0x22, 0x8e, 0x15, 0x8f, 0x16, 0xca, 0xed, 0xca,
+       0xc9, 0xeb, 0xc9, 0x30, 0x0a, 0x04, 0x7f, 0x4a, 0x80, 0x02, 0x7f,
+       0x42, 0xcb, 0xef, 0xcb, 0xea, 0xc3, 0x94, 0x04, 0x50, 0x02, 0x80,
+       0x01, 0xc3, 0x40, 0x04, 0xcb, 0x44, 0x20, 0xcb, 0x85, 0x16, 0x82,
+       0x85, 0x15, 0x83, 0xeb, 0xf0, 0xa3, 0xe4, 0xf0, 0x85, 0x16, 0x82,
+       0x85, 0x15, 0x83, 0xa3, 0xa3, 0xe5, 0x1a, 0xf0, 0xe5, 0x19, 0x85,
+       0x16, 0x82, 0x85, 0x15, 0x83, 0xa3, 0xa3, 0xa3, 0xf0, 0xe5, 0x16,
+       0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x15, 0xf5, 0x83, 0x74, 0x0f,
+       0xf0, 0xe5, 0x16, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x35, 0x15, 0xf5,
+       0x83, 0xe4, 0xf0, 0xe5, 0x16, 0x24, 0x06, 0xf5, 0x82, 0xe4, 0x35,
+       0x15, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x16, 0x24, 0x07, 0xf5, 0x82,
+       0xe4, 0x35, 0x15, 0xf5, 0x83, 0x74, 0x10, 0xf0, 0xea, 0x90, 0x1a,
+       0x9c, 0x93, 0xfb, 0xea, 0x64, 0x01, 0x60, 0x08, 0xea, 0x64, 0x02,
+       0x60, 0x03, 0xba, 0x03, 0x04, 0xcb, 0x44, 0x08, 0xcb, 0xe5, 0x16,
+       0x24, 0x08, 0xf5, 0x82, 0xe4, 0x35, 0x15, 0xf5, 0x83, 0xeb, 0xf0,
+       0xe5, 0x16, 0x24, 0x15, 0xf5, 0x82, 0xe4, 0x35, 0x15, 0xf5, 0x83,
+       0x74, 0xff, 0xf0, 0xe5, 0x16, 0x24, 0x16, 0xf5, 0x82, 0xe4, 0x35,
+       0x15, 0xf5, 0x83, 0xe9, 0xf0, 0xe5, 0x16, 0x24, 0x09, 0xf5, 0x82,
+       0xe4, 0x35, 0x15, 0xf5, 0x83, 0x74, 0x04, 0xf0, 0x25, 0x1a, 0xf5,
+       0x1a, 0xe4, 0x35, 0x19, 0xf5, 0x19, 0xea, 0xc3, 0x94, 0x04, 0x40,
+       0x03, 0x02, 0x03, 0xd6, 0xea, 0x60, 0x03, 0xba, 0x01, 0x1f, 0xea,
+       0x24, 0x01, 0xfd, 0xe4, 0x33, 0xfc, 0xe5, 0x1a, 0xae, 0x19, 0x78,
+       0x03, 0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9, 0xff, 0x12, 0x07,
+       0x96, 0x8e, 0x19, 0x8f, 0x1a, 0x02, 0x03, 0xb6, 0xea, 0x24, 0xff,
+       0xfd, 0xe4, 0x34, 0xff, 0xfc, 0x7e, 0x00, 0x7f, 0x0b, 0x12, 0x07,
+       0x84, 0xcc, 0xee, 0xcc, 0xcd, 0xef, 0xcd, 0xe5, 0x1a, 0xc4, 0xf8,
+       0x54, 0x0f, 0xc8, 0x68, 0xff, 0xe5, 0x19, 0xc4, 0x54, 0xf0, 0x48,
+       0xfe, 0x12, 0x07, 0x96, 0x8c, 0x1b, 0x8d, 0x1c, 0xea, 0x24, 0xff,
+       0xfd, 0xe4, 0x34, 0xff, 0xfc, 0x7e, 0x00, 0x7f, 0x0b, 0x12, 0x07,
+       0x84, 0xcc, 0xee, 0xcc, 0xcd, 0xef, 0xcd, 0xe5, 0x1a, 0xc4, 0xf8,
+       0x54, 0x0f, 0xc8, 0x68, 0xff, 0xe5, 0x19, 0xc4, 0x54, 0xf0, 0x48,
+       0xfe, 0x12, 0x07, 0x96, 0x8e, 0x19, 0x8f, 0x1a, 0xe5, 0x1c, 0x45,
+       0x1b, 0x60, 0x08, 0x05, 0x1a, 0xe5, 0x1a, 0x70, 0x02, 0x05, 0x19,
+       0xea, 0x24, 0xff, 0xfd, 0xe4, 0x34, 0xff, 0xfc, 0x7e, 0x00, 0x7f,
+       0x03, 0x12, 0x07, 0x84, 0xd3, 0xe5, 0x1c, 0x9f, 0xe5, 0x1b, 0x9e,
+       0x50, 0x18, 0xe5, 0x1c, 0x45, 0x1b, 0x60, 0x12, 0xba, 0x03, 0x0f,
+       0xe5, 0x16, 0x24, 0x09, 0xf5, 0x82, 0xe4, 0x35, 0x15, 0xf5, 0x83,
+       0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x16, 0x24, 0x0a, 0xf5, 0x82, 0xe4,
+       0x35, 0x15, 0xf5, 0x83, 0xe5, 0x1a, 0xf0, 0xe5, 0x19, 0xff, 0xe5,
+       0x16, 0x24, 0x0b, 0xf5, 0x82, 0xe4, 0x35, 0x15, 0xf5, 0x83, 0xef,
+       0xf0, 0x80, 0x2d, 0xe5, 0x1a, 0x54, 0x3f, 0xff, 0xe5, 0x16, 0x24,
+       0x0a, 0xf5, 0x82, 0xe4, 0x35, 0x15, 0xf5, 0x83, 0xef, 0xf0, 0xe5,
+       0x1a, 0xae, 0x19, 0x78, 0x06, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8,
+       0xf9, 0xff, 0xe5, 0x16, 0x24, 0x0b, 0xf5, 0x82, 0xe4, 0x35, 0x15,
+       0xf5, 0x83, 0xef, 0xf0, 0x85, 0x16, 0x82, 0x85, 0x15, 0x83, 0xe0,
+       0x44, 0x01, 0xf0, 0x22, 0x8e, 0x12, 0x8f, 0x13, 0x8d, 0x14, 0xe5,
+       0x14, 0xa2, 0xe1, 0x92, 0x09, 0xe5, 0x34, 0x24, 0x19, 0xf5, 0x82,
+       0xe4, 0x35, 0x33, 0xf5, 0x83, 0xe0, 0xfd, 0xe5, 0x34, 0x24, 0x1a,
+       0xf5, 0x82, 0xe4, 0x35, 0x33, 0xf5, 0x83, 0xe0, 0xfb, 0xa2, 0x09,
+       0x92, 0x0a, 0x75, 0x19, 0x00, 0x75, 0x1a, 0x1a, 0x12, 0x02, 0x1f,
+       0x30, 0x09, 0x04, 0x7f, 0xc8, 0x80, 0x02, 0x7f, 0xe8, 0xe5, 0x13,
+       0x24, 0x18, 0xf5, 0x82, 0xe4, 0x35, 0x12, 0xf5, 0x83, 0xef, 0xf0,
+       0xe5, 0x31, 0x60, 0x04, 0x7f, 0x02, 0x80, 0x02, 0x7f, 0x01, 0xe5,
+       0x13, 0x24, 0x19, 0xf5, 0x82, 0xe4, 0x35, 0x12, 0xf5, 0x83, 0xef,
+       0xf0, 0xe5, 0x34, 0x24, 0x19, 0xf5, 0x82, 0xe4, 0x35, 0x33, 0xf5,
+       0x83, 0xe0, 0xff, 0x7d, 0x1a, 0x7c, 0x00, 0x12, 0x0e, 0x64, 0xe5,
+       0x13, 0x24, 0x1a, 0xf5, 0x82, 0xe4, 0x35, 0x12, 0xf5, 0x83, 0xef,
+       0xf0, 0xe5, 0x13, 0x24, 0x1b, 0xf5, 0x82, 0xe4, 0x35, 0x12, 0xf5,
+       0x83, 0xee, 0xf0, 0xe5, 0x31, 0x60, 0x60, 0xe5, 0x13, 0x24, 0x1c,
+       0xff, 0xe4, 0x35, 0x12, 0xfe, 0xe5, 0x34, 0x24, 0x12, 0xfd, 0xe4,
+       0x35, 0x33, 0xfc, 0x75, 0x1b, 0x11, 0x7b, 0x06, 0x12, 0x14, 0xab,
+       0xe5, 0x13, 0x24, 0x22, 0xff, 0xe4, 0x35, 0x12, 0xfe, 0x7c, 0x30,
+       0x7d, 0x10, 0x75, 0x1b, 0x11, 0x7b, 0x06, 0x12, 0x14, 0xab, 0xe5,
+       0x13, 0x24, 0x28, 0xff, 0xe4, 0x35, 0x12, 0xfe, 0x7c, 0x30, 0x7d,
+       0x08, 0x75, 0x1b, 0x11, 0x7b, 0x06, 0x12, 0x14, 0xab, 0xe5, 0x34,
+       0x24, 0x18, 0xf5, 0x82, 0xe4, 0x35, 0x33, 0xf5, 0x83, 0xe0, 0xff,
+       0xe5, 0x13, 0x24, 0x2d, 0xf5, 0x82, 0xe4, 0x35, 0x12, 0xf5, 0x83,
+       0xef, 0xf0, 0x80, 0x3f, 0xe5, 0x13, 0x24, 0x1c, 0xff, 0xe4, 0x35,
+       0x12, 0xfe, 0x7c, 0x30, 0x7d, 0x10, 0x75, 0x1b, 0x11, 0x7b, 0x06,
+       0x12, 0x14, 0xab, 0xe5, 0x13, 0x24, 0x22, 0xff, 0xe4, 0x35, 0x12,
+       0xfe, 0x7c, 0x30, 0x7d, 0x08, 0x75, 0x1b, 0x11, 0x7b, 0x06, 0x12,
+       0x14, 0xab, 0xe5, 0x13, 0x24, 0x28, 0xff, 0xe4, 0x35, 0x12, 0xfe,
+       0x7c, 0x30, 0x7d, 0x10, 0x75, 0x1b, 0x11, 0x7b, 0x06, 0x12, 0x14,
+       0xab, 0xe5, 0x13, 0x24, 0x2e, 0xf5, 0x82, 0xe4, 0x35, 0x12, 0xf5,
+       0x83, 0xe4, 0xf0, 0xe5, 0x13, 0x24, 0x2f, 0xf5, 0x82, 0xe4, 0x35,
+       0x12, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x34, 0x24, 0x11, 0xf5, 0x82,
+       0xe4, 0x35, 0x33, 0xf5, 0x83, 0xe0, 0xff, 0xc3, 0x13, 0xff, 0xe5,
+       0x13, 0x24, 0x30, 0xf5, 0x82, 0xe4, 0x35, 0x12, 0xf5, 0x83, 0xef,
+       0xf0, 0x30, 0x09, 0x41, 0xe5, 0x13, 0x24, 0x30, 0xf5, 0x82, 0xe4,
+       0x35, 0x12, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x31, 0x60, 0x04, 0x7e,
+       0x00, 0x80, 0x02, 0x7e, 0x10, 0xef, 0x4e, 0xf0, 0xe5, 0x31, 0x60,
+       0x06, 0x7e, 0x00, 0x7f, 0x00, 0x80, 0x0f, 0xe5, 0x14, 0x30, 0xe0,
+       0x06, 0x7e, 0x00, 0x7f, 0xff, 0x80, 0x04, 0x7e, 0x00, 0x7f, 0x00,
+       0xe5, 0x13, 0x24, 0x31, 0xf5, 0x82, 0xe4, 0x35, 0x12, 0xf5, 0x83,
+       0xef, 0xf0, 0x22, 0xe5, 0x13, 0x24, 0x30, 0xf5, 0x82, 0xe4, 0x35,
+       0x12, 0xf5, 0x83, 0xe0, 0x44, 0x40, 0xf0, 0xe5, 0x14, 0x30, 0xe0,
+       0x0f, 0xe5, 0x34, 0x24, 0x10, 0xf5, 0x82, 0xe4, 0x35, 0x33, 0xf5,
+       0x83, 0xe0, 0xff, 0x80, 0x02, 0x7f, 0x00, 0xe5, 0x13, 0x24, 0x31,
+       0xf5, 0x82, 0xe4, 0x35, 0x12, 0xf5, 0x83, 0xef, 0xf0, 0x22, 0xe5,
+       0x34, 0x24, 0x11, 0xf5, 0x82, 0xe4, 0x35, 0x33, 0xf5, 0x83, 0xe0,
+       0x30, 0xe7, 0x3b, 0xe5, 0x34, 0x24, 0x1c, 0xf5, 0x82, 0xe4, 0x35,
+       0x33, 0xf5, 0x83, 0xe0, 0x65, 0x2b, 0x70, 0x03, 0x75, 0x2b, 0xff,
+       0xe5, 0x34, 0x24, 0x1d, 0xf5, 0x82, 0xe4, 0x35, 0x33, 0xf5, 0x83,
+       0xe0, 0xff, 0x12, 0x1c, 0x62, 0x7e, 0x22, 0x7f, 0x10, 0x12, 0x18,
+       0x7c, 0x8e, 0x33, 0x8f, 0x34, 0x90, 0x22, 0x2e, 0xe0, 0xfe, 0xa3,
+       0xe0, 0x8e, 0x33, 0xf5, 0x34, 0xc3, 0x22, 0xd2, 0x0a, 0xe5, 0x34,
+       0x24, 0x1b, 0xf5, 0x82, 0xe4, 0x35, 0x33, 0xf5, 0x83, 0xe0, 0x70,
+       0x3a, 0x85, 0x34, 0x82, 0x85, 0x33, 0x83, 0xc0, 0x83, 0xc0, 0x82,
+       0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x85, 0x34, 0x82, 0x85, 0x33, 0x83,
+       0xa3, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xc3, 0xef, 0x9d, 0xff,
+       0xee, 0x9c, 0xfe, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0xa3, 0xef, 0xf0,
+       0xd3, 0x94, 0x00, 0xee, 0x64, 0x80, 0x94, 0x80, 0x50, 0x03, 0x02,
+       0x07, 0x27, 0x80, 0xc6, 0x85, 0x34, 0x82, 0x85, 0x33, 0x83, 0xe0,
+       0xfe, 0xa3, 0xe0, 0xc3, 0xee, 0x64, 0x80, 0x94, 0x80, 0x50, 0x03,
+       0x02, 0x07, 0x27, 0x12, 0x1c, 0x41, 0x85, 0x34, 0x82, 0x85, 0x33,
+       0x83, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xc3, 0x9f, 0xee, 0x64, 0x80,
+       0xf8, 0xec, 0x64, 0x80, 0x98, 0x40, 0x20, 0x85, 0x34, 0x82, 0x85,
+       0x33, 0x83, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xe0, 0xfe, 0xa3,
+       0xe0, 0xff, 0xed, 0x9f, 0xff, 0xec, 0x9e, 0xd0, 0x82, 0xd0, 0x83,
+       0xf0, 0xa3, 0xef, 0xf0, 0xc2, 0x0a, 0x85, 0x34, 0x82, 0x85, 0x33,
+       0x83, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xe5, 0x34, 0x24, 0x10, 0xf5,
+       0x82, 0xe4, 0x35, 0x33, 0xf5, 0x83, 0xe0, 0xfd, 0xc3, 0xef, 0x9d,
+       0xfd, 0xee, 0x94, 0x00, 0xfc, 0x12, 0x16, 0x5a, 0x50, 0x2c, 0x85,
+       0x34, 0x82, 0x85, 0x33, 0x83, 0xc0, 0x83, 0xc0, 0x82, 0xe0, 0xfe,
+       0xa3, 0xe0, 0xff, 0x85, 0x34, 0x82, 0x85, 0x33, 0x83, 0xa3, 0xa3,
+       0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xc3, 0xef, 0x9d, 0xff, 0xee, 0x9c,
+       0xd0, 0x82, 0xd0, 0x83, 0xf0, 0xa3, 0xef, 0xf0, 0xc2, 0x0a, 0x20,
+       0x0a, 0x03, 0x02, 0x06, 0x37, 0x7e, 0x22, 0x7f, 0x10, 0x12, 0x18,
+       0x7c, 0x8e, 0x33, 0x8f, 0x34, 0x8f, 0x82, 0x8e, 0x83, 0xe0, 0xfe,
+       0xa3, 0xe0, 0xd3, 0x94, 0x00, 0xee, 0x64, 0x80, 0x94, 0x80, 0x40,
+       0x0d, 0x7e, 0x22, 0x7f, 0x10, 0xad, 0x34, 0xac, 0x33, 0x12, 0x15,
+       0x0e, 0x80, 0x1a, 0x12, 0x1b, 0xab, 0x85, 0x34, 0x82, 0x85, 0x33,
+       0x83, 0xee, 0x8f, 0xf0, 0x12, 0x07, 0xeb, 0x7e, 0x22, 0x7f, 0x30,
+       0xad, 0x34, 0xac, 0x33, 0x12, 0x15, 0x0e, 0x90, 0x22, 0x2e, 0xe0,
+       0xfe, 0xa3, 0xe0, 0xff, 0x65, 0x34, 0x70, 0x03, 0xee, 0x65, 0x33,
+       0x70, 0x02, 0xd3, 0x22, 0x8e, 0x33, 0x8f, 0x34, 0xc3, 0x22, 0xef,
+       0x8d, 0xf0, 0xa4, 0xa8, 0xf0, 0xcf, 0x8c, 0xf0, 0xa4, 0x28, 0xce,
+       0x8d, 0xf0, 0xa4, 0x2e, 0xfe, 0x22, 0xbc, 0x00, 0x0b, 0xbe, 0x00,
+       0x29, 0xef, 0x8d, 0xf0, 0x84, 0xff, 0xad, 0xf0, 0x22, 0xe4, 0xcc,
+       0xf8, 0x75, 0xf0, 0x08, 0xef, 0x2f, 0xff, 0xee, 0x33, 0xfe, 0xec,
+       0x33, 0xfc, 0xee, 0x9d, 0xec, 0x98, 0x40, 0x05, 0xfc, 0xee, 0x9d,
+       0xfe, 0x0f, 0xd5, 0xf0, 0xe9, 0xe4, 0xce, 0xfd, 0x22, 0xed, 0xf8,
+       0xf5, 0xf0, 0xee, 0x84, 0x20, 0xd2, 0x1c, 0xfe, 0xad, 0xf0, 0x75,
+       0xf0, 0x08, 0xef, 0x2f, 0xff, 0xed, 0x33, 0xfd, 0x40, 0x07, 0x98,
+       0x50, 0x06, 0xd5, 0xf0, 0xf2, 0x22, 0xc3, 0x98, 0xfd, 0x0f, 0xd5,
+       0xf0, 0xea, 0x22, 0xc5, 0xf0, 0xf8, 0xa3, 0xe0, 0x28, 0xf0, 0xc5,
+       0xf0, 0xf8, 0xe5, 0x82, 0x15, 0x82, 0x70, 0x02, 0x15, 0x83, 0xe0,
+       0x38, 0xf0, 0x22, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe,
+       0xa3, 0xe0, 0xff, 0x22, 0xec, 0xf0, 0xa3, 0xed, 0xf0, 0xa3, 0xee,
+       0xf0, 0xa3, 0xef, 0xf0, 0x22, 0xa4, 0x25, 0x82, 0xf5, 0x82, 0xe5,
+       0xf0, 0x35, 0x83, 0xf5, 0x83, 0x22, 0xd0, 0x83, 0xd0, 0x82, 0xf8,
+       0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d, 0xa3, 0xa3,
+       0x93, 0xf8, 0x74, 0x01, 0x93, 0xf5, 0x82, 0x88, 0x83, 0xe4, 0x73,
+       0x74, 0x02, 0x93, 0x68, 0x60, 0xef, 0xa3, 0xa3, 0xa3, 0x80, 0xdf,
+       0x8a, 0x83, 0x89, 0x82, 0xe4, 0x73, 0xe4, 0xff, 0x90, 0x30, 0x8c,
+       0xe4, 0xf0, 0xef, 0x90, 0x1b, 0x51, 0x93, 0x44, 0x80, 0x90, 0x30,
+       0x8d, 0xf0, 0xa3, 0x74, 0x01, 0xf0, 0xa3, 0xe4, 0xf0, 0x90, 0x30,
+       0x8c, 0xe0, 0xfe, 0x74, 0x36, 0x2f, 0xf8, 0xc6, 0xee, 0xc6, 0xa3,
+       0xe0, 0xfe, 0xef, 0x90, 0x1b, 0x51, 0x93, 0x44, 0x80, 0x6e, 0x60,
+       0x01, 0x1f, 0x0f, 0xef, 0xc3, 0x94, 0x09, 0x40, 0xc8, 0x22, 0x00,
+       0x00, 0x00, 0x00, 0xe5, 0x30, 0x12, 0x08, 0x25, 0x08, 0xb1, 0x00,
+       0x09, 0x1f, 0x01, 0x09, 0x87, 0x02, 0x0a, 0x1b, 0x03, 0x0a, 0x6f,
+       0x04, 0x0a, 0xb6, 0x05, 0x0b, 0x29, 0x06, 0x0b, 0x98, 0x07, 0x00,
+       0x00, 0x0b, 0xd0, 0xc2, 0x01, 0x12, 0x00, 0x06, 0x90, 0x30, 0x3a,
+       0xe0, 0xf5, 0x12, 0xe5, 0x26, 0x20, 0xe5, 0x08, 0x90, 0x34, 0x98,
+       0xe0, 0x54, 0xfe, 0xf0, 0x22, 0x90, 0x34, 0x98, 0xe0, 0x44, 0x01,
+       0xf0, 0xe5, 0x26, 0x30, 0xe6, 0x0f, 0xe5, 0x27, 0x30, 0xe6, 0x05,
+       0x53, 0x12, 0xfd, 0x80, 0x12, 0x43, 0x12, 0x02, 0x80, 0x0d, 0xe5,
+       0x27, 0x30, 0xe6, 0x05, 0x43, 0x12, 0x02, 0x80, 0x03, 0x53, 0x12,
+       0xfd, 0xe5, 0x26, 0x30, 0xe7, 0x0f, 0xe5, 0x27, 0x30, 0xe7, 0x05,
+       0x53, 0x12, 0xf7, 0x80, 0x12, 0x43, 0x12, 0x08, 0x80, 0x0d, 0xe5,
+       0x27, 0x30, 0xe7, 0x05, 0x43, 0x12, 0x08, 0x80, 0x03, 0x53, 0x12,
+       0xf7, 0x43, 0x12, 0x01, 0x43, 0x12, 0x04, 0x90, 0x30, 0x3a, 0xe5,
+       0x12, 0xf0, 0x22, 0xc2, 0x01, 0x12, 0x00, 0x06, 0x90, 0x30, 0x3a,
+       0xe0, 0xf5, 0x12, 0xe5, 0x26, 0x20, 0xe5, 0x08, 0x90, 0x34, 0x98,
+       0xe0, 0x54, 0xfe, 0xf0, 0x22, 0x90, 0x34, 0x98, 0xe0, 0x44, 0x01,
+       0xf0, 0xe5, 0x26, 0x54, 0xc0, 0x60, 0x1c, 0xe5, 0x27, 0x30, 0xe6,
+       0x05, 0x53, 0x12, 0xfd, 0x80, 0x03, 0x43, 0x12, 0x02, 0xe5, 0x27,
+       0x30, 0xe7, 0x05, 0x53, 0x12, 0xf7, 0x80, 0x1f, 0x43, 0x12, 0x08,
+       0x80, 0x1a, 0xe5, 0x27, 0x30, 0xe6, 0x05, 0x43, 0x12, 0x02, 0x80,
+       0x03, 0x53, 0x12, 0xfd, 0xe5, 0x27, 0x30, 0xe7, 0x05, 0x43, 0x12,
+       0x08, 0x80, 0x03, 0x53, 0x12, 0xf7, 0x43, 0x12, 0x01, 0x43, 0x12,
+       0x04, 0x90, 0x30, 0x3a, 0xe5, 0x12, 0xf0, 0x22, 0xc2, 0x01, 0x12,
+       0x00, 0x06, 0x90, 0x30, 0x3a, 0xe0, 0xf5, 0x12, 0x43, 0x12, 0x01,
+       0x43, 0x12, 0x04, 0xe5, 0x26, 0x30, 0xe5, 0x5c, 0x90, 0x34, 0x98,
+       0xe0, 0x44, 0x01, 0xf0, 0xe5, 0x26, 0x54, 0xc0, 0x60, 0x1c, 0xe5,
+       0x27, 0x30, 0xe6, 0x05, 0x53, 0x12, 0xfd, 0x80, 0x03, 0x43, 0x12,
+       0x02, 0xe5, 0x27, 0x30, 0xe7, 0x05, 0x53, 0x12, 0xf7, 0x80, 0x30,
+       0x43, 0x12, 0x08, 0x80, 0x2b, 0xe5, 0x27, 0x30, 0xe6, 0x05, 0x43,
+       0x12, 0x02, 0x80, 0x03, 0x53, 0x12, 0xfd, 0xe5, 0x27, 0x30, 0xe7,
+       0x05, 0x43, 0x12, 0x08, 0x80, 0x03, 0x53, 0x12, 0xf7, 0xe5, 0x27,
+       0xf4, 0x54, 0x1f, 0xff, 0x90, 0x30, 0x34, 0xe0, 0x54, 0xe0, 0x4f,
+       0xf0, 0xe4, 0xf5, 0x2c, 0x90, 0x30, 0x3a, 0xe5, 0x12, 0xf0, 0x80,
+       0x15, 0x90, 0x34, 0x98, 0xe0, 0x54, 0xfe, 0xf0, 0xe5, 0x27, 0xf4,
+       0x54, 0x1f, 0xff, 0x90, 0x30, 0x34, 0xe0, 0x54, 0xe0, 0x4f, 0xf0,
+       0x90, 0x30, 0x35, 0xe0, 0xf5, 0x12, 0x53, 0x12, 0xe0, 0xe5, 0x12,
+       0xf0, 0x22, 0xc2, 0x01, 0x12, 0x00, 0x06, 0x90, 0x30, 0x3a, 0xe0,
+       0xf5, 0x12, 0xe5, 0x26, 0x30, 0xe5, 0x3c, 0x90, 0x34, 0x98, 0xe0,
+       0x44, 0x01, 0xf0, 0xe5, 0x27, 0x30, 0xe6, 0x05, 0x53, 0x12, 0xfd,
+       0x80, 0x03, 0x43, 0x12, 0x02, 0xe5, 0x27, 0x30, 0xe7, 0x05, 0x53,
+       0x12, 0xf7, 0x80, 0x03, 0x43, 0x12, 0x08, 0xe5, 0x26, 0x54, 0xc0,
+       0x60, 0x08, 0x43, 0x12, 0x01, 0x43, 0x12, 0x04, 0x80, 0x06, 0x53,
+       0x12, 0xfe, 0x43, 0x12, 0x04, 0x90, 0x30, 0x3a, 0xe5, 0x12, 0xf0,
+       0x22, 0x90, 0x34, 0x98, 0xe0, 0x54, 0xfe, 0xf0, 0x22, 0xc2, 0x01,
+       0x12, 0x00, 0x06, 0x90, 0x30, 0x3a, 0xe0, 0xf5, 0x12, 0xe5, 0x27,
+       0x30, 0xe6, 0x05, 0x43, 0x12, 0x02, 0x80, 0x03, 0x53, 0x12, 0xfd,
+       0xe5, 0x27, 0x30, 0xe7, 0x05, 0x43, 0x12, 0x08, 0x80, 0x03, 0x53,
+       0x12, 0xf7, 0xe5, 0x26, 0x54, 0xc0, 0x60, 0x08, 0x53, 0x12, 0xfe,
+       0x53, 0x12, 0xfb, 0x80, 0x06, 0x43, 0x12, 0x01, 0x43, 0x12, 0x04,
+       0x90, 0x34, 0x98, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x30, 0x3a, 0xe5,
+       0x12, 0xf0, 0x22, 0x20, 0x02, 0x13, 0x12, 0x1c, 0x1e, 0xaf, 0x29,
+       0x7e, 0x00, 0x12, 0x1c, 0x74, 0xaf, 0x35, 0x7e, 0x00, 0x12, 0x1c,
+       0x7b, 0xd2, 0x02, 0x90, 0x30, 0x3a, 0xe0, 0xf5, 0x12, 0xe5, 0x26,
+       0x20, 0xe5, 0x0d, 0xc2, 0x01, 0x12, 0x00, 0x06, 0x90, 0x34, 0x98,
+       0xe0, 0x54, 0xfe, 0xf0, 0x22, 0x90, 0x34, 0x98, 0xe0, 0x44, 0x01,
+       0xf0, 0xe5, 0x26, 0x54, 0xc0, 0x60, 0x2c, 0xc2, 0x01, 0x12, 0x00,
+       0x06, 0xe5, 0x27, 0x30, 0xe6, 0x05, 0x53, 0x12, 0xfd, 0x80, 0x03,
+       0x43, 0x12, 0x02, 0xe5, 0x27, 0x30, 0xe7, 0x05, 0x53, 0x12, 0xf7,
+       0x80, 0x03, 0x43, 0x12, 0x08, 0x43, 0x12, 0x01, 0x43, 0x12, 0x04,
+       0x90, 0x30, 0x3a, 0xe5, 0x12, 0xf0, 0x22, 0x30, 0x01, 0x03, 0x02,
+       0x0b, 0xd0, 0x12, 0x15, 0xc0, 0xd2, 0x01, 0x22, 0xc2, 0x01, 0x12,
+       0x00, 0x06, 0xe5, 0x26, 0x20, 0xe5, 0x09, 0x90, 0x34, 0x98, 0xe0,
+       0x54, 0xfe, 0xf0, 0x80, 0x55, 0x90, 0x34, 0x98, 0xe0, 0x44, 0x01,
+       0xf0, 0xe5, 0x26, 0x30, 0xe6, 0x0f, 0xe5, 0x27, 0x30, 0xe6, 0x05,
+       0x53, 0x12, 0xfd, 0x80, 0x12, 0x43, 0x12, 0x02, 0x80, 0x0d, 0xe5,
+       0x27, 0x30, 0xe6, 0x05, 0x43, 0x12, 0x02, 0x80, 0x03, 0x53, 0x12,
+       0xfd, 0xe5, 0x26, 0x30, 0xe7, 0x0f, 0xe5, 0x27, 0x30, 0xe7, 0x05,
+       0x53, 0x12, 0xf7, 0x80, 0x12, 0x43, 0x12, 0x08, 0x80, 0x0d, 0xe5,
+       0x27, 0x30, 0xe7, 0x05, 0x43, 0x12, 0x08, 0x80, 0x03, 0x53, 0x12,
+       0xf7, 0x43, 0x12, 0x01, 0x53, 0x12, 0xfb, 0x90, 0x30, 0x3a, 0xe5,
+       0x12, 0xf0, 0x90, 0x30, 0x3a, 0xe0, 0xf5, 0x12, 0x22, 0xe5, 0x26,
+       0x30, 0xe5, 0x2c, 0x20, 0x03, 0x21, 0xd2, 0x03, 0x12, 0x1c, 0x1e,
+       0x75, 0x35, 0x06, 0x75, 0x29, 0x09, 0xaf, 0x29, 0x7e, 0x00, 0x12,
+       0x1c, 0x74, 0x90, 0x30, 0x3a, 0xe0, 0xf5, 0x12, 0x53, 0x12, 0xfe,
+       0x43, 0x12, 0x04, 0xe5, 0x12, 0xf0, 0x90, 0x34, 0x98, 0xe0, 0x44,
+       0x01, 0xf0, 0x22, 0x90, 0x34, 0x98, 0xe0, 0x54, 0xfe, 0xf0, 0x22,
+       0xe5, 0x31, 0x64, 0x01, 0x70, 0x41, 0x12, 0x1a, 0xd4, 0x40, 0x03,
+       0x02, 0x0d, 0x4f, 0x12, 0x1b, 0x65, 0x50, 0x20, 0x7e, 0x2b, 0x7f,
+       0x80, 0x7d, 0x03, 0x12, 0x04, 0x0e, 0x7f, 0x01, 0x12, 0x19, 0x78,
+       0x40, 0x09, 0xd2, 0x09, 0x12, 0x0f, 0xee, 0xe4, 0xf5, 0x2f, 0x22,
+       0x12, 0x0d, 0x50, 0x75, 0x2f, 0x01, 0x22, 0x7f, 0x01, 0x12, 0x19,
+       0x78, 0x50, 0x04, 0x75, 0x2f, 0x02, 0x22, 0xd2, 0x09, 0x12, 0x0f,
+       0xee, 0xe4, 0xf5, 0x2f, 0x22, 0x12, 0x1a, 0x1d, 0x50, 0x51, 0x12,
+       0x1b, 0xcb, 0x90, 0x30, 0xf4, 0xe0, 0xf5, 0x2a, 0x7e, 0x30, 0x7f,
+       0xec, 0xa3, 0xe0, 0xfd, 0xe4, 0xfb, 0x12, 0x19, 0x2b, 0xe4, 0xff,
+       0xfe, 0x12, 0x1c, 0x36, 0x90, 0x00, 0x0a, 0x74, 0x02, 0xf0, 0x90,
+       0x00, 0x0b, 0xe0, 0x44, 0x02, 0xff, 0xf0, 0xfd, 0x90, 0x01, 0x05,
+       0x74, 0x20, 0xf0, 0x90, 0x01, 0x06, 0xe0, 0x44, 0x20, 0xf0, 0xed,
+       0x54, 0xbf, 0x90, 0x00, 0x0b, 0xf0, 0x90, 0x34, 0xcc, 0xe0, 0x44,
+       0x01, 0xf0, 0xa3, 0xe0, 0x44, 0x01, 0xf0, 0xa3, 0xe0, 0x44, 0x01,
+       0xf0, 0xd2, 0x04, 0x12, 0x1a, 0x3a, 0x50, 0x43, 0x12, 0x1a, 0x57,
+       0x7e, 0x30, 0x7f, 0xe0, 0x7c, 0x30, 0x7d, 0xec, 0x75, 0x1b, 0x11,
+       0x7b, 0x06, 0x12, 0x14, 0xab, 0x90, 0x30, 0xf5, 0xe0, 0x75, 0xf0,
+       0x20, 0xa4, 0xff, 0xae, 0xf0, 0x12, 0x1c, 0x36, 0x90, 0x00, 0x0b,
+       0xe0, 0x54, 0xfd, 0xff, 0xf0, 0xfd, 0xe4, 0x90, 0x00, 0x04, 0xf0,
+       0x90, 0x01, 0x06, 0xe0, 0x54, 0xdf, 0xf0, 0x90, 0x00, 0x0a, 0x74,
+       0x40, 0xf0, 0x4d, 0x90, 0x00, 0x0b, 0xf0, 0xc2, 0x04, 0x12, 0x1a,
+       0xfe, 0x50, 0x38, 0x12, 0x1a, 0x57, 0x7e, 0x30, 0x7f, 0xe0, 0x7c,
+       0x1c, 0x7d, 0x82, 0x75, 0x1b, 0x12, 0x7b, 0x06, 0x12, 0x14, 0xab,
+       0x90, 0x00, 0x04, 0x74, 0x02, 0xf0, 0x90, 0x00, 0x0a, 0xf0, 0xe4,
+       0xff, 0xfe, 0x12, 0x1c, 0x36, 0x90, 0x00, 0x0b, 0xe0, 0x54, 0xfd,
+       0xf0, 0xe4, 0x90, 0x00, 0x04, 0xf0, 0x90, 0x01, 0x06, 0xe0, 0x54,
+       0xdf, 0xf0, 0xc2, 0x04, 0x12, 0x1b, 0x28, 0x50, 0x25, 0x12, 0x1a,
+       0x57, 0x7f, 0x02, 0x12, 0x19, 0x78, 0x90, 0x01, 0x04, 0xe0, 0x54,
+       0x7f, 0xf0, 0x90, 0x00, 0x0b, 0xe0, 0x54, 0xfd, 0xff, 0xf0, 0xe4,
+       0x90, 0x00, 0x04, 0xf0, 0xef, 0x54, 0xbf, 0x90, 0x00, 0x0b, 0xf0,
+       0xc2, 0x04, 0x12, 0x1a, 0xd4, 0x50, 0x2d, 0x12, 0x1a, 0x57, 0x7e,
+       0x30, 0x7f, 0xe0, 0x7c, 0x1c, 0x7d, 0x82, 0x75, 0x1b, 0x12, 0x7b,
+       0x06, 0x12, 0x14, 0xab, 0x90, 0x00, 0x04, 0x74, 0x02, 0xf0, 0x90,
+       0x00, 0x0a, 0xf0, 0x90, 0x01, 0x06, 0xe0, 0x54, 0xdf, 0xf0, 0x90,
+       0x00, 0x0b, 0xe0, 0x54, 0xbf, 0xf0, 0xc2, 0x04, 0x22, 0x90, 0x34,
+       0xcd, 0xe0, 0xf9, 0x20, 0xe3, 0xf8, 0xe5, 0x2b, 0xf4, 0x60, 0x66,
+       0x90, 0x34, 0xc0, 0x12, 0x08, 0x01, 0x85, 0x34, 0x82, 0x85, 0x33,
+       0x83, 0x75, 0xf0, 0x20, 0xe5, 0x2b, 0x12, 0x08, 0x19, 0xe5, 0x82,
+       0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0x12, 0x08,
+       0x0d, 0x90, 0x34, 0xc8, 0x12, 0x08, 0x01, 0x85, 0x34, 0x82, 0x85,
+       0x33, 0x83, 0x75, 0xf0, 0x20, 0xe5, 0x2b, 0x12, 0x08, 0x19, 0xe5,
+       0x82, 0x24, 0x08, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0x12,
+       0x08, 0x0d, 0x90, 0x34, 0xd0, 0x12, 0x08, 0x01, 0x85, 0x34, 0x82,
+       0x85, 0x33, 0x83, 0x75, 0xf0, 0x20, 0xe5, 0x2b, 0x12, 0x08, 0x19,
+       0xe5, 0x82, 0x24, 0x0c, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83,
+       0x12, 0x08, 0x0d, 0xe5, 0x34, 0x24, 0xf0, 0xff, 0xe5, 0x33, 0x34,
+       0xde, 0xfe, 0xef, 0x78, 0x05, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8,
+       0xf9, 0xf5, 0x2b, 0x85, 0x34, 0x82, 0x85, 0x33, 0x83, 0x75, 0xf0,
+       0x20, 0x12, 0x08, 0x19, 0xe5, 0x82, 0x24, 0x04, 0xf5, 0x82, 0xe4,
+       0x35, 0x83, 0xf5, 0x83, 0x12, 0x08, 0x01, 0x90, 0x34, 0xc0, 0x12,
+       0x08, 0x0d, 0x85, 0x34, 0x82, 0x85, 0x33, 0x83, 0x75, 0xf0, 0x20,
+       0xe5, 0x2b, 0x12, 0x08, 0x19, 0xe5, 0x82, 0x24, 0x08, 0xf5, 0x82,
+       0xe4, 0x35, 0x83, 0xf5, 0x83, 0x12, 0x08, 0x01, 0x90, 0x34, 0xc8,
+       0x12, 0x08, 0x0d, 0x85, 0x34, 0x82, 0x85, 0x33, 0x83, 0x75, 0xf0,
+       0x20, 0xe5, 0x2b, 0x12, 0x08, 0x19, 0xe5, 0x82, 0x24, 0x0c, 0xf5,
+       0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0x12, 0x08, 0x01, 0x90, 0x34,
+       0xc4, 0x12, 0x08, 0x0d, 0x90, 0x01, 0x01, 0xe0, 0x44, 0x40, 0xf0,
+       0x90, 0x01, 0x00, 0xe0, 0x44, 0x08, 0xf0, 0xe9, 0x44, 0x04, 0x90,
+       0x34, 0xcd, 0xf0, 0x90, 0x34, 0xcc, 0xe0, 0x44, 0x01, 0xf0, 0xa3,
+       0xe0, 0x44, 0x01, 0xf0, 0xa3, 0xe0, 0x44, 0x01, 0xf0, 0x22, 0x8f,
+       0x15, 0x8c, 0x16, 0x8d, 0x17, 0xe5, 0x15, 0xc3, 0x94, 0x04, 0x50,
+       0x56, 0xe5, 0x15, 0x94, 0x00, 0x40, 0x06, 0x7a, 0x00, 0x7b, 0x60,
+       0x80, 0x04, 0x7a, 0x00, 0x7b, 0xc0, 0xe5, 0x17, 0xc4, 0xf8, 0x54,
+       0x0f, 0xc8, 0x68, 0xff, 0xe5, 0x16, 0xc4, 0x54, 0xf0, 0x48, 0xfe,
+       0xe5, 0x15, 0x90, 0x1a, 0x8e, 0x93, 0xfd, 0x7c, 0x00, 0x12, 0x07,
+       0x96, 0xef, 0x2b, 0xfb, 0xee, 0x3a, 0xfa, 0xe5, 0x17, 0xc4, 0xf8,
+       0x54, 0x0f, 0xc8, 0x68, 0xff, 0xe5, 0x16, 0xc4, 0x54, 0xf0, 0x48,
+       0xfe, 0xe5, 0x15, 0x93, 0xfd, 0x7c, 0x00, 0x12, 0x07, 0x96, 0xed,
+       0x4c, 0x60, 0x63, 0x0b, 0xbb, 0x00, 0x01, 0x0a, 0x80, 0x5c, 0x7a,
+       0x00, 0x7b, 0x1a, 0xe5, 0x17, 0xae, 0x16, 0x78, 0x02, 0xc3, 0x33,
+       0xce, 0x33, 0xce, 0xd8, 0xf9, 0x24, 0x0b, 0xff, 0xe4, 0x3e, 0xfe,
+       0xe5, 0x15, 0x90, 0x1a, 0x8e, 0x93, 0xfd, 0x7c, 0x00, 0x12, 0x07,
+       0x96, 0xef, 0x78, 0x02, 0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9,
+       0x2b, 0xfb, 0xee, 0x3a, 0xfa, 0xe5, 0x17, 0xae, 0x16, 0x78, 0x02,
+       0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9, 0x24, 0x0b, 0xff, 0xe4,
+       0x3e, 0xfe, 0xe5, 0x15, 0x90, 0x1a, 0x8e, 0x93, 0xfd, 0x7c, 0x00,
+       0x12, 0x07, 0x96, 0xed, 0x4c, 0x60, 0x07, 0x74, 0x04, 0x2b, 0xfb,
+       0xe4, 0x3a, 0xfa, 0xcf, 0xeb, 0xcf, 0xce, 0xea, 0xce, 0x22, 0xe5,
+       0x2e, 0x14, 0x60, 0x1d, 0x14, 0x60, 0x3d, 0x14, 0x60, 0x5d, 0x14,
+       0x70, 0x03, 0x02, 0x0f, 0xd7, 0x24, 0x04, 0x60, 0x03, 0x02, 0x0f,
+       0xed, 0x20, 0x0d, 0x03, 0x02, 0x0f, 0xed, 0x75, 0x2e, 0x01, 0x22,
+       0x90, 0x00, 0x0a, 0xe0, 0xff, 0x30, 0xe5, 0x03, 0x44, 0x20, 0xf0,
+       0xe5, 0x40, 0x45, 0x3f, 0x60, 0x03, 0x02, 0x0f, 0xed, 0x75, 0x2e,
+       0x02, 0x12, 0x19, 0x9b, 0x12, 0x1b, 0x78, 0xaf, 0x28, 0x12, 0x1a,
+       0xa8, 0x22, 0x90, 0x01, 0x03, 0xe0, 0xff, 0x30, 0xe7, 0x76, 0xef,
+       0x44, 0x80, 0x90, 0x01, 0x03, 0xf0, 0x12, 0x08, 0x51, 0x12, 0x19,
+       0xde, 0x12, 0x1b, 0xbb, 0x75, 0x2e, 0x03, 0xaf, 0x22, 0x7e, 0x00,
+       0x12, 0x1c, 0x2a, 0x22, 0xe5, 0x40, 0x45, 0x3f, 0x70, 0x21, 0x12,
+       0x14, 0x41, 0x12, 0x1b, 0x78, 0x12, 0x19, 0xbe, 0x12, 0x1b, 0xbb,
+       0x12, 0x1c, 0x04, 0x30, 0x0d, 0x0b, 0x75, 0x2e, 0x01, 0xaf, 0x32,
+       0x7e, 0x00, 0x12, 0x1c, 0x2a, 0x22, 0xe4, 0xf5, 0x2e, 0x22, 0x90,
+       0x00, 0x0a, 0xe0, 0xff, 0x30, 0xe5, 0x2c, 0x44, 0x20, 0xf0, 0x12,
+       0x14, 0x41, 0x12, 0x1b, 0x78, 0x12, 0x19, 0xbe, 0x12, 0x1b, 0xbb,
+       0x12, 0x1c, 0x04, 0x75, 0x2e, 0x04, 0x22, 0xe5, 0x40, 0x45, 0x3f,
+       0x70, 0x10, 0x30, 0x0d, 0x0a, 0x75, 0x2e, 0x01, 0xaf, 0x32, 0xfe,
+       0x12, 0x1c, 0x2a, 0x22, 0xe4, 0xf5, 0x2e, 0x22, 0x90, 0x00, 0x04,
+       0x74, 0x02, 0xf0, 0x90, 0x00, 0x0a, 0xf0, 0x30, 0x09, 0x32, 0xe5,
+       0x34, 0x45, 0x33, 0x70, 0x02, 0xc3, 0x22, 0x85, 0x34, 0x82, 0x85,
+       0x33, 0x83, 0xc0, 0x83, 0xc0, 0x82, 0xe0, 0xfe, 0xa3, 0xe0, 0xff,
+       0x85, 0x34, 0x82, 0x85, 0x33, 0x83, 0xa3, 0xa3, 0xe0, 0xfc, 0xa3,
+       0xe0, 0xfd, 0xc3, 0xef, 0x9d, 0xff, 0xee, 0x9c, 0xd0, 0x82, 0xd0,
+       0x83, 0xf0, 0xa3, 0xef, 0xf0, 0xe5, 0x34, 0x45, 0x33, 0x70, 0x02,
+       0xc3, 0x22, 0x12, 0x05, 0xed, 0x50, 0xf3, 0x90, 0x00, 0x0a, 0xe0,
+       0x20, 0xe5, 0x03, 0x30, 0x07, 0x41, 0xe5, 0x34, 0x45, 0x33, 0x70,
+       0x02, 0xc3, 0x22, 0x85, 0x34, 0x82, 0x85, 0x33, 0x83, 0xc0, 0x83,
+       0xc0, 0x82, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x85, 0x34, 0x82, 0x85,
+       0x33, 0x83, 0xa3, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xc3, 0xef,
+       0x9d, 0xff, 0xee, 0x9c, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0xa3, 0xef,
+       0xf0, 0xe5, 0x34, 0x45, 0x33, 0x70, 0x02, 0xc3, 0x22, 0x12, 0x05,
+       0xed, 0x50, 0xf3, 0x80, 0xb5, 0x85, 0x34, 0x82, 0x85, 0x33, 0x83,
+       0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x12, 0x16, 0xea, 0xd3, 0x22, 0x12,
+       0x1a, 0xfe, 0x40, 0x05, 0x12, 0x1a, 0xd4, 0x50, 0x44, 0x7e, 0x30,
+       0x7f, 0xe0, 0x7c, 0x1c, 0x7d, 0x82, 0x75, 0x1b, 0x12, 0x7b, 0x06,
+       0x12, 0x14, 0xab, 0x90, 0x00, 0x04, 0x74, 0x02, 0xf0, 0x90, 0x00,
+       0x0a, 0xf0, 0xe4, 0xff, 0xfe, 0x12, 0x1c, 0x36, 0x90, 0x00, 0x0b,
+       0xe0, 0x54, 0xbf, 0xf0, 0x54, 0x7f, 0xff, 0xf0, 0xe4, 0x90, 0x30,
+       0xe9, 0xf0, 0xef, 0x54, 0xfd, 0x90, 0x00, 0x0b, 0xf0, 0xe4, 0x90,
+       0x00, 0x04, 0xf0, 0xd2, 0x09, 0x12, 0x0f, 0xee, 0xe4, 0xf5, 0x2f,
+       0x12, 0x1b, 0x13, 0x50, 0x48, 0x7e, 0x30, 0x7f, 0xe0, 0x7c, 0x1c,
+       0x7d, 0x82, 0x75, 0x1b, 0x12, 0x7b, 0x06, 0x12, 0x14, 0xab, 0x90,
+       0x00, 0x04, 0x74, 0x02, 0xf0, 0x90, 0x00, 0x0a, 0xf0, 0xe4, 0xff,
+       0xfe, 0x12, 0x1c, 0x36, 0x90, 0x00, 0x0b, 0xe0, 0x54, 0xbf, 0xf0,
+       0x54, 0xfd, 0xf0, 0xe4, 0x90, 0x00, 0x04, 0xf0, 0xff, 0x12, 0x19,
+       0x78, 0x50, 0x04, 0x75, 0x2f, 0x07, 0x22, 0x90, 0x01, 0x04, 0xe0,
+       0x54, 0x7f, 0xf0, 0xd2, 0x09, 0x12, 0x0f, 0xee, 0xe4, 0xf5, 0x2f,
+       0x22, 0xc2, 0xaf, 0xe4, 0xf5, 0x2f, 0xf5, 0x88, 0x75, 0xa8, 0x0f,
+       0x75, 0x89, 0x11, 0xf5, 0xb8, 0xf5, 0xe8, 0x75, 0x90, 0x0f, 0x75,
+       0x31, 0xff, 0x75, 0x2b, 0xff, 0x90, 0x22, 0x2e, 0xf0, 0xa3, 0xf0,
+       0x90, 0x22, 0x4e, 0xf0, 0xa3, 0xf0, 0xc2, 0x05, 0xc2, 0x08, 0xc2,
+       0x00, 0xc2, 0x07, 0xc2, 0x04, 0x90, 0x00, 0x0a, 0x74, 0xff, 0xf0,
+       0x90, 0x00, 0x0b, 0x74, 0x01, 0xf0, 0x90, 0x01, 0x03, 0x74, 0xff,
+       0xf0, 0xe4, 0x90, 0x01, 0x04, 0xf0, 0x90, 0x01, 0x05, 0x74, 0xff,
+       0xf0, 0xe4, 0x90, 0x01, 0x06, 0xf0, 0x90, 0x00, 0x04, 0xf0, 0x90,
+       0x30, 0xe8, 0x74, 0x10, 0xf0, 0x90, 0x01, 0x07, 0xf0, 0x90, 0x01,
+       0x08, 0x04, 0xf0, 0x90, 0x01, 0x09, 0x74, 0x48, 0xf0, 0x90, 0x01,
+       0x0a, 0x74, 0x7f, 0xf0, 0x90, 0x01, 0x02, 0x74, 0x1f, 0xf0, 0x90,
+       0x01, 0x00, 0x74, 0x14, 0xf0, 0x90, 0x01, 0x01, 0x74, 0x20, 0xf0,
+       0x90, 0x00, 0x00, 0xe0, 0x44, 0x80, 0xf0, 0x75, 0x49, 0x00, 0x75,
+       0x4a, 0x01, 0xc2, 0x01, 0xd2, 0xaf, 0x22, 0x12, 0x1a, 0xd4, 0x50,
+       0x2d, 0x12, 0x18, 0x48, 0x90, 0x01, 0x06, 0xe0, 0x54, 0xdf, 0xf0,
+       0x7e, 0x30, 0x7f, 0xe0, 0x7c, 0x1c, 0x7d, 0x82, 0x75, 0x1b, 0x12,
+       0x7b, 0x06, 0x12, 0x14, 0xab, 0x90, 0x00, 0x04, 0x74, 0x02, 0xf0,
+       0x90, 0x00, 0x0a, 0xf0, 0xd2, 0x09, 0x12, 0x0f, 0xee, 0xe4, 0xf5,
+       0x2f, 0x22, 0x12, 0x1b, 0x28, 0x50, 0x50, 0x12, 0x18, 0x48, 0x90,
+       0x00, 0x0b, 0xe0, 0x54, 0xfd, 0xf0, 0xe4, 0x90, 0x00, 0x04, 0xf0,
+       0x90, 0x01, 0x03, 0x74, 0x80, 0xf0, 0x90, 0x01, 0x04, 0xe0, 0x44,
+       0x80, 0xf0, 0x7f, 0x02, 0x12, 0x19, 0x78, 0x50, 0x04, 0x75, 0x2f,
+       0x05, 0x22, 0x7e, 0x30, 0x7f, 0xe0, 0x7c, 0x1c, 0x7d, 0x82, 0x75,
+       0x1b, 0x12, 0x7b, 0x06, 0x12, 0x14, 0xab, 0x90, 0x00, 0x04, 0x74,
+       0x02, 0xf0, 0x90, 0x00, 0x0a, 0xf0, 0xd2, 0x09, 0x12, 0x0f, 0xee,
+       0x90, 0x01, 0x04, 0xe0, 0x54, 0x7f, 0xf0, 0xe4, 0xf5, 0x2f, 0x22,
+       0x90, 0x30, 0x30, 0x74, 0x02, 0xf0, 0x75, 0x11, 0x07, 0x75, 0x12,
+       0xd0, 0x90, 0x30, 0x30, 0xe0, 0x30, 0xe0, 0x0e, 0xe5, 0x12, 0x15,
+       0x12, 0x70, 0x02, 0x15, 0x11, 0xe5, 0x12, 0x45, 0x11, 0x70, 0xeb,
+       0xe5, 0x12, 0x45, 0x11, 0x70, 0x12, 0x12, 0x1a, 0x74, 0x90, 0x21,
+       0x00, 0xe0, 0x60, 0x07, 0x90, 0x34, 0x98, 0xe0, 0x44, 0x04, 0xf0,
+       0xc3, 0x22, 0xe4, 0x90, 0x34, 0x58, 0xf0, 0x90, 0x34, 0x32, 0x74,
+       0x1f, 0xf0, 0x75, 0x11, 0x07, 0x75, 0x12, 0xd0, 0x90, 0x34, 0x81,
+       0xe0, 0x64, 0x03, 0x60, 0x0e, 0xe5, 0x12, 0x15, 0x12, 0x70, 0x02,
+       0x15, 0x11, 0xe5, 0x12, 0x45, 0x11, 0x70, 0xea, 0xe5, 0x12, 0x45,
+       0x11, 0x70, 0x12, 0x12, 0x1a, 0x74, 0x90, 0x21, 0x00, 0xe0, 0x60,
+       0x07, 0x90, 0x34, 0x98, 0xe0, 0x44, 0x04, 0xf0, 0xc3, 0x22, 0x90,
+       0x34, 0x98, 0xe0, 0x44, 0x04, 0xf0, 0xe4, 0x90, 0x00, 0x01, 0xf0,
+       0xd3, 0x22, 0x90, 0x30, 0x3a, 0xe0, 0xf5, 0x10, 0x12, 0x1c, 0x57,
+       0x50, 0x26, 0xe5, 0x27, 0x30, 0xe6, 0x05, 0x53, 0x10, 0xfd, 0x80,
+       0x03, 0x43, 0x10, 0x02, 0xe5, 0x27, 0x30, 0xe7, 0x05, 0x53, 0x10,
+       0xf7, 0x80, 0x03, 0x43, 0x10, 0x08, 0x53, 0x10, 0xfe, 0x43, 0x10,
+       0x04, 0x90, 0x30, 0x3a, 0xe5, 0x10, 0xf0, 0x12, 0x1c, 0x4c, 0x50,
+       0x48, 0x90, 0x01, 0x03, 0xe0, 0xf5, 0x10, 0x54, 0x1c, 0x60, 0x3e,
+       0xe5, 0x10, 0x54, 0xe3, 0xf0, 0xa3, 0xe0, 0xf5, 0x10, 0xf0, 0xe5,
+       0x27, 0x30, 0xe6, 0x05, 0x43, 0x10, 0x02, 0x80, 0x03, 0x53, 0x10,
+       0xfd, 0xe5, 0x27, 0x30, 0xe7, 0x05, 0x43, 0x10, 0x08, 0x80, 0x03,
+       0x53, 0x10, 0xf7, 0x53, 0x10, 0xfe, 0x43, 0x10, 0x04, 0x90, 0x30,
+       0x3a, 0xe5, 0x10, 0xf0, 0xaf, 0x29, 0x7e, 0x00, 0x12, 0x1c, 0x74,
+       0xaf, 0x35, 0x7e, 0x00, 0x12, 0x1c, 0x7b, 0x22, 0x12, 0x1a, 0xbf,
+       0x50, 0x72, 0x12, 0x1c, 0x41, 0x85, 0x34, 0x82, 0x85, 0x33, 0x83,
+       0xe0, 0xfc, 0xa3, 0xe0, 0xc3, 0x9f, 0xf5, 0x12, 0xec, 0x9e, 0xf5,
+       0x11, 0xd3, 0xe5, 0x12, 0x94, 0x00, 0xe5, 0x11, 0x64, 0x80, 0x94,
+       0x80, 0x40, 0x06, 0xae, 0x11, 0xaf, 0x12, 0x80, 0x04, 0x7e, 0x00,
+       0x7f, 0x00, 0x8e, 0x11, 0x8f, 0x12, 0xe5, 0x34, 0x24, 0x10, 0xf5,
+       0x82, 0xe4, 0x35, 0x33, 0xf5, 0x83, 0xe0, 0xc3, 0x95, 0x12, 0xf5,
+       0x12, 0xe4, 0x95, 0x11, 0xf5, 0x11, 0xc3, 0x64, 0x80, 0x94, 0x80,
+       0x50, 0x05, 0xe4, 0xf5, 0x11, 0xf5, 0x12, 0xe5, 0x34, 0x24, 0x12,
+       0xff, 0xe4, 0x35, 0x33, 0xfe, 0xad, 0x12, 0x7b, 0x01, 0x12, 0x19,
+       0x2b, 0x90, 0x01, 0x05, 0x74, 0x20, 0xf0, 0x90, 0x01, 0x06, 0xe0,
+       0x44, 0x20, 0xf0, 0x75, 0x2f, 0x03, 0x22, 0xc0, 0xe0, 0xc0, 0xf0,
+       0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x08, 0xc2, 0xaf,