- Port rtw(4) from NetBSD, which supports various RealTek 8180 chip based
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Sun, 3 Sep 2006 07:37:58 +0000 (07:37 +0000)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Sun, 3 Sep 2006 07:37:58 +0000 (07:37 +0000)
  wireless NIC.
- Put NetBSD 802.11 duration related structures and functions in rtw.c and
  rtwvar.h, and rename them to rtw_xxxx.
- Fix various ieee80211_node leakages in TX path.
- Use spare RX DMA map to recover from bus_dmamap_load_mbuf() failure.
- Utilize TX rate control algorithm framework in our 802.11 layer, support
  Onoe TX rate control algorithm.
- Hook rtw(4) into module building.
- Hook rtw(4) into GENERIC and LINT.

Thank David Young and many other people for their work on this driver.

Tested with a Linksys WPC11 ver.4

23 files changed:
sys/conf/files
sys/config/GENERIC
sys/config/LINT
sys/dev/netif/Makefile
sys/dev/netif/rtw/Makefile [new file with mode: 0644]
sys/dev/netif/rtw/if_rtw_pci.c [new file with mode: 0644]
sys/dev/netif/rtw/max2820reg.h [new file with mode: 0644]
sys/dev/netif/rtw/rtw.c [new file with mode: 0644]
sys/dev/netif/rtw/rtwbitop.h [new file with mode: 0644]
sys/dev/netif/rtw/rtwphy.c [new file with mode: 0644]
sys/dev/netif/rtw/rtwphy.h [new file with mode: 0644]
sys/dev/netif/rtw/rtwphyio.c [new file with mode: 0644]
sys/dev/netif/rtw/rtwphyio.h [new file with mode: 0644]
sys/dev/netif/rtw/rtwreg.h [new file with mode: 0644]
sys/dev/netif/rtw/rtwvar.h [new file with mode: 0644]
sys/dev/netif/rtw/sa2400reg.h [new file with mode: 0644]
sys/dev/netif/rtw/si4136reg.h [new file with mode: 0644]
sys/dev/netif/rtw/smc93cx6.c [new file with mode: 0644]
sys/dev/netif/rtw/smc93cx6var.h [new file with mode: 0644]
sys/i386/conf/GENERIC
sys/i386/conf/LINT
sys/netproto/802_11/ieee80211_crypto.h
sys/netproto/802_11/wlan/ieee80211_crypto.c

index 466a615..beff709 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.138 2006/09/01 15:12:11 sephe Exp $
+# $DragonFly: src/sys/conf/files,v 1.139 2006/09/03 07:37:58 sephe Exp $
 #
 # The long compile-with and dependency lines are required because of
 # limitations in config: backslash-newline doesn't work in strings, and
@@ -384,6 +384,11 @@ dev/netif/vx/if_vx_pci.c           optional vx pci
 dev/netif/wi/if_wi.c                   optional wi
 dev/netif/wi/if_wi_pccard.c            optional wi pccard
 dev/netif/wi/if_wi_pci.c               optional wi pci
+dev/netif/rtw/rtw.c                    optional rtw
+dev/netif/rtw/rtwphy.c                 optional rtw
+dev/netif/rtw/rtwphyio.c               optional rtw
+dev/netif/rtw/smc93cx6.c               optional rtw
+dev/netif/rtw/if_rtw_pci.c             optional rtw pci
 dev/netif/xe/if_xe.c                   optional xe
 dev/netif/xe/if_xe_pccard.c            optional xe pccard
 vfs/gnu/ext2fs/ext2_alloc.c            optional ext2fs
index 79baf10..bdfa8f8 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.36 2006/05/20 09:13:09 sephe Exp $
+# $DragonFly: src/sys/config/GENERIC,v 1.37 2006/09/03 07:37:58 sephe Exp $
 
 machine                i386
 cpu            I386_CPU
@@ -229,6 +229,7 @@ device              wlan_wep        # 802.11 WEP support
 device         an
 #device                awi             # PRISM I IEEE 802.11b wireless NIC
 device         ral             # Ralink Technology 802.11 wireless NIC
+device         rtw             # RealTek 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 f473986..1cdd700 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.89 2006/09/01 15:12:11 sephe Exp $
+# $DragonFly: src/sys/config/LINT,v 1.90 2006/09/03 07:37:58 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
@@ -1421,6 +1421,7 @@ device            an              # Aironet Communications 4500/4800
 device         ipw             # Intel PRO/Wireless 2100
 device         iwi             # Intel PRO/Wireless 2200BG/2915ABG
 device         wi              # WaveLAN/IEEE, PRISM-II, Spectrum24 802.11DS
+device         rtw             # RealTek 8180
 device         acx             # TI ACX100/ACX111
 device wl0 at isa? port 0x300  # T1 speed ISA/radio lan
 device         xe              # Xircom PCMCIA
index 8085d23..8cfaaac 100644 (file)
@@ -1,8 +1,8 @@
-# $DragonFly: src/sys/dev/netif/Makefile,v 1.21 2006/08/27 03:36:08 sephe Exp $
+# $DragonFly: src/sys/dev/netif/Makefile,v 1.22 2006/09/03 07:37:58 sephe Exp $
 #
 
 SUBDIR= an acx ar ath aue axe bfe bge cue dc ed em ep fwe fxp gx ipw iwi kue \
-       lge lnc mii_layer my nfe 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
+       lge lnc mii_layer my nfe nge nv pcn ral ray re rl rtw 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/rtw/Makefile b/sys/dev/netif/rtw/Makefile
new file mode 100644 (file)
index 0000000..0d9957a
--- /dev/null
@@ -0,0 +1,14 @@
+# $DragonFly: src/sys/dev/netif/rtw/Makefile,v 1.1 2006/09/03 07:37:58 sephe Exp $
+KMOD   = if_rtw
+
+SRCS   = if_rtw_pci.c smc93cx6.c rtwphyio.c rtwphy.c rtw.c
+SRCS   += device_if.h bus_if.h pci_if.h opt_aic7xxx.h
+
+.if !defined(BUILDING_WITH_KERNEL)
+opt_aic7xxx.h:
+       echo "" > ${.TARGET}
+.endif
+
+KMODDEPS= wlan wlan_ratectl_onoe
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/netif/rtw/if_rtw_pci.c b/sys/dev/netif/rtw/if_rtw_pci.c
new file mode 100644 (file)
index 0000000..5eafccd
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2006 The DragonFly Project.  All rights reserved.
+ * 
+ * This code is derived from software contributed to The DragonFly Project
+ * by Sepherosa Ziehau <sepherosa@gmail.com>
+ * 
+ * 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. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS 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: if_rtw_pci.c,v 1.4 2005/12/04 17:44:02 christos Exp $
+ * $DragonFly: src/sys/dev/netif/rtw/if_rtw_pci.c,v 1.1 2006/09/03 07:37:58 sephe Exp $
+ */
+
+/*
+ * Copyright (c) 1998, 1999, 2000, 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+ * NASA Ames Research Center; Charles M. Hannum; and David Young.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the NetBSD
+ *     Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 THE FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+/*
+ * PCI bus front-end for the Realtek RTL8180 802.11 MAC/BBP chip.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <bus/pci/pcivar.h>
+#include <bus/pci/pcireg.h>
+#include <bus/pci/pcidevs.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_media.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+#include <netproto/802_11/ieee80211_radiotap.h>
+
+#include "rtwreg.h"
+#include "sa2400reg.h"
+#include "rtwvar.h"
+
+/*
+ * PCI configuration space registers
+ */
+#define        RTW_PCI_IOBA            0x10    /* i/o mapped base */
+#define        RTW_PCI_MMBA            0x14    /* memory mapped base */
+
+static const struct rtw_pci_reg {
+       int     reg_type;
+       int     reg_rid;
+} rtw_pci_regs[] = {
+       /* Prefer IO memory over IO port */
+       { SYS_RES_MEMORY, RTW_PCI_MMBA },
+       { SYS_RES_IOPORT, RTW_PCI_IOBA }
+};
+
+static const struct rtw_pci_product {
+       uint16_t        app_vendor;     /* PCI vendor ID */
+       uint16_t        app_product;    /* PCI product ID */
+       const char      *app_product_name;
+} rtw_pci_products[] = {
+       { PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RT8180,
+         "Realtek RTL8180 802.11 MAC/BBP" },
+       { PCI_VENDOR_BELKIN, PCI_PRODUCT_BELKIN_F5D6001,
+         "Belkin F5D6001" },
+
+       { 0, 0, NULL }
+};
+
+static int     rtw_pci_probe(device_t);
+static int     rtw_pci_attach(device_t);
+static int     rtw_pci_detach(device_t);
+static int     rtw_pci_shutdown(device_t);
+
+static device_method_t rtw_pci_methods[] = {
+       DEVMETHOD(device_probe,         rtw_pci_probe),
+       DEVMETHOD(device_attach,        rtw_pci_attach),
+       DEVMETHOD(device_detach,        rtw_pci_detach),
+       DEVMETHOD(device_shutdown,      rtw_pci_shutdown),
+#if 0
+       DEVMETHOD(device_suspend,       rtw_pci_suspend),
+       DEVMETHOD(device_resume,        rtw_pci_resume),
+#endif
+       { 0, 0 }
+};
+
+static driver_t rtw_pci_driver = {
+       "rtw",
+       rtw_pci_methods,
+       sizeof(struct rtw_softc)
+};
+
+DRIVER_MODULE(rtw, pci, rtw_pci_driver, rtw_devclass, 0, 0);
+DRIVER_MODULE(rtw, cardbus, rtw_pci_driver, rtw_devclass, 0, 0);
+
+MODULE_DEPEND(rtw, wlan, 1, 1, 1);
+MODULE_DEPEND(rtw, wlan_ratectl_onoe, 1, 1, 1);
+MODULE_DEPEND(rtw, pci, 1, 1, 1);
+MODULE_DEPEND(rtw, cardbus, 1, 1, 1);
+
+static int
+rtw_pci_probe(device_t dev)
+{
+       const struct rtw_pci_product *app;
+       uint16_t vid, did;
+
+       vid = pci_get_vendor(dev);
+       did = pci_get_device(dev);
+       for (app = rtw_pci_products; app->app_product_name != NULL; app++) {
+               if (vid == app->app_vendor && did == app->app_product) {
+                       device_set_desc(dev, app->app_product_name);
+                       return 0;
+               }
+       }
+       return ENXIO;
+}
+
+static int
+rtw_pci_attach(device_t dev)
+{
+       struct rtw_softc *sc = device_get_softc(dev);
+       struct rtw_regs *regs = &sc->sc_regs;
+       int i, error;
+
+       /*
+        * No power management hooks.
+        * XXX Maybe we should add some!
+        */
+       sc->sc_flags |= RTW_F_ENABLED;
+
+       sc->sc_rev = pci_get_revid(dev);
+
+#ifndef BURN_BRIDGES
+       if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
+               uint32_t mem, port, irq;
+                                               
+               mem = pci_read_config(dev, RTW_PCI_MMBA, 4);
+               port = pci_read_config(dev, RTW_PCI_IOBA, 4);
+               irq = pci_read_config(dev, PCIR_INTLINE, 4);
+               
+               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);
+
+               pci_write_config(dev, RTW_PCI_MMBA, mem, 4);
+               pci_write_config(dev, RTW_PCI_IOBA, port, 4);
+               pci_write_config(dev, PCIR_INTLINE, irq, 4);
+       }
+#endif /* !BURN_BRIDGES */
+
+       /* Enable PCI bus master */
+       pci_enable_busmaster(dev);
+
+       /* Allocate IO memory/port */
+#define N(arr) sizeof(arr) / sizeof(arr[0])
+       for (i = 0; i < N(rtw_pci_regs); ++i) {
+               regs->r_rid = rtw_pci_regs[i].reg_rid;
+               regs->r_type = rtw_pci_regs[i].reg_type;
+               regs->r_res = bus_alloc_resource_any(dev, regs->r_type,
+                                                    &regs->r_rid, RF_ACTIVE);
+               if (regs->r_res != NULL)
+                       break;
+       }
+#undef N
+       if (regs->r_res == NULL) {
+               device_printf(dev, "can't allocate IO mem/port\n");
+               return ENXIO;
+       }
+       regs->r_bh = rman_get_bushandle(regs->r_res);
+       regs->r_bt = rman_get_bustag(regs->r_res);
+
+       error = rtw_attach(dev);
+       if (error)
+               rtw_pci_detach(dev);
+       return error;
+}
+
+static int
+rtw_pci_detach(device_t dev)
+{
+       struct rtw_softc *sc = device_get_softc(dev);
+       struct rtw_regs *regs = &sc->sc_regs;
+
+       if (device_is_attached(dev))
+               rtw_detach(dev);
+
+       if (regs->r_res != NULL) {
+               bus_release_resource(dev, regs->r_type, regs->r_rid,
+                                    regs->r_res);
+       }
+       return 0;
+}
+
+static int
+rtw_pci_shutdown(device_t dev)
+{
+       rtw_stop(device_get_softc(dev), 1);
+       return 0;
+}
diff --git a/sys/dev/netif/rtw/max2820reg.h b/sys/dev/netif/rtw/max2820reg.h
new file mode 100644 (file)
index 0000000..2b477bf
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2004 David Young.  All rights reserved.
+ *
+ * This code was written by David Young.
+ *
+ * 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. Neither the name of the author nor the names of any co-contributors
+ *    may 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: max2820reg.h,v 1.5 2006/03/08 08:26:50 dyoung Exp $
+ * $DragonFly: src/sys/dev/netif/rtw/max2820reg.h,v 1.1 2006/09/03 07:37:58 sephe Exp $
+ */
+
+#ifndef _DEV_IC_MAX2820REG_H_
+#define        _DEV_IC_MAX2820REG_H_
+
+/*
+ * Serial bus format for Maxim MAX2820/MAX2820A/MAX2821/MAX2821A
+ * 2.4GHz 802.11b Zero-IF Transceivers
+ */
+#define MAX2820_TWI_ADDR_MASK  __BITS(15,12)
+#define MAX2820_TWI_DATA_MASK  __BITS(11,0)
+
+/*
+ * Registers for Maxim MAX2820/MAX2820A/MAX2821/MAX2821A 2.4GHz
+ * 802.11b Zero-IF Transceivers
+ */
+#define MAX2820_TEST           0               /* Test Register */
+#define MAX2820_TEST_DEFAULT   __BITS(2,0)     /* Always set to this value. */
+
+#define MAX2820_ENABLE         1               /* Block-Enable Register */
+#define        MAX2820_ENABLE_RSVD1    __BIT(11)       /* reserved */
+#define        MAX2820_ENABLE_PAB      __BIT(10)       /* Transmit Baseband Filters
+                                                * Enable
+                                                * PAB_EN = SHDNB &&
+                                                *   (MAX2820_ENABLE_PAB ||
+                                                *    TX_ON)
+                                                */
+#define        MAX2820_ENABLE_TXFLT    __BIT(9)        /* Transmit Baseband Filters
+                                                * Enable
+                                                * TXFLT_EN = SHDNB &&
+                                                *   (MAX2820_ENABLE_TXFLT ||
+                                                *    TX_ON)
+                                                */
+#define        MAX2820_ENABLE_TXUVD    __BIT(8)        /* Tx Upconverter, VGA, and
+                                                * Driver Amp Enable
+                                                * TXUVD_EN = SHDNB &&
+                                                *   (MAX2820_ENABLE_TXUVD ||
+                                                *    TX_ON)
+                                                */
+#define        MAX2820_ENABLE_DET      __BIT(7)        /* Receive Detector Enable
+                                                * DET_EN = SHDNB &&
+                                                *   (MAX2820_ENABLE_DET ||
+                                                *    RX_ON)
+                                                */
+#define        MAX2820_ENABLE_RXDFA    __BIT(6)        /* Rx Downconverter, Filters,
+                                                * and AGC Amps Enable
+                                                * RXDFA_EN = SHDNB &&
+                                                *   (MAX2820_ENABLE_RXDFA ||
+                                                *    RX_ON)
+                                                */
+#define        MAX2820_ENABLE_RXLNA    __BIT(5)        /* Receive LNA Enable
+                                                * AT_EN = SHDNB &&
+                                                *   (MAX2820_ENABLE_RXLNA ||
+                                                *    RX_ON)
+                                                */
+#define        MAX2820_ENABLE_AT       __BIT(4)        /* Auto-tuner Enable
+                                                * AT_EN = SHDNB &&
+                                                *   (MAX2820_ENABLE_AT ||
+                                                *    RX_ON || TX_ON)
+                                                */
+#define        MAX2820_ENABLE_CP       __BIT(3)        /* PLL Charge-Pump Enable
+                                                * CP_EN = SHDNB
+                                                *   && MAX2820_ENABLE_CP
+                                                */
+#define        MAX2820_ENABLE_PLL      __BIT(2)        /* PLL Enable
+                                                * PLL_EN = SHDNB
+                                                *   && MAX2820_ENABLE_PLL
+                                                */
+#define        MAX2820_ENABLE_VCO      __BIT(1)        /* VCO Enable
+                                                * VCO_EN = SHDNB
+                                                *   && MAX2820_ENABLE_VCO
+                                                */
+#define        MAX2820_ENABLE_RSVD0    __BIT(0)        /* reserved */
+#define        MAX2820_ENABLE_DEFAULT  (MAX2820_ENABLE_AT|MAX2820_ENABLE_CP|\
+                                MAX2820_ENABLE_PLL|MAX2820_ENABLE_VCO)
+
+#define MAX2820_SYNTH          2               /* Synthesizer Register */
+#define        MAX2820_SYNTH_RSVD0     __BITS(11,7)    /* reserved */
+#define        MAX2820_SYNTH_ICP       __BIT(6)        /* Charge-Pump Current Select
+                                                * 0 = +/-1mA
+                                                * 1 = +/-2mA
+                                                */
+#define        MAX2820_SYNTH_R_MASK    __BITS(5,0)     /* Reference Frequency Divider
+                                                * 0 = 22MHz
+                                                * 1 = 44MHz
+                                                */
+#define        MAX2820_SYNTH_R_22MHZ   SHIFTIN(0, MAX2820_SYNTH_R_MASK)
+#define        MAX2820_SYNTH_R_44MHZ   SHIFTIN(1, MAX2820_SYNTH_R_MASK)
+#define        MAX2820_SYNTH_ICP_DEFAULT       MAX2820_SYNTH_ICP
+#define        MAX2820_SYNTH_R_DEFAULT         SHIFTIN(0, MAX2820_SYNTH_R_MASK)
+
+#define MAX2820_CHANNEL                3               /* Channel Frequency Register */
+#define        MAX2820_CHANNEL_RSVD    __BITS(11,7)    /* reserved */
+#define        MAX2820_CHANNEL_CF_MASK __BITS(6,0)     /* Channel Frequency Select
+                                                * fLO = 2400MHz + CF * 1MHz
+                                                */
+#define        MAX2820_CHANNEL_RSVD_DEFAULT    SHIFTIN(0, MAX2820_CHANNEL_RSVD)
+#define        MAX2820_CHANNEL_CF_DEFAULT      SHIFTIN(37, MAX2820_CHANNEL_CF_MASK)
+
+#define MAX2820_RECEIVE                4               /* Receiver Settings Register
+                                                * MAX2820/MAX2821
+                                                */
+#define        MAX2820_RECEIVE_2C_MASK __BITS(11,9)    /* VGA DC Offset Nulling
+                                                * Parameter 2
+                                                */
+#define        MAX2820_RECEIVE_1C_MASK __BITS(8,6)     /* VGA DC Offset Nulling
+                                                * Parameter 1
+                                                */
+#define        MAX2820_RECEIVE_DL_MASK __BITS(5,4)     /* Rx Level Detector Midpoint
+                                                * Select
+                                                * 11, 01 = 50.2mVp
+                                                * 10     = 70.9mVp
+                                                * 00     = 35.5mVp
+                                                */
+#define        MAX2820_RECEIVE_SF      __BIT(3)        /* Special Function Select
+                                                * 0 = OFF
+                                                * 1 = ON
+                                                */
+#define        MAX2820_RECEIVE_BW_MASK __BITS(2,0)     /* Receive Filter -3dB Frequency
+                                                * Select (all frequencies are
+                                                * approximate)
+                                                */
+/* 8.5MHz */
+#define        MAX2820_RECEIVE_BW_8_5MHZ       SHIFTIN(0, MAX2820_RECEIVE_BW_MASK)
+#define        MAX2820_RECEIVE_BW_8MHZ         SHIFTIN(1, MAX2820_RECEIVE_BW_MASK)
+#define        MAX2820_RECEIVE_BW_7_5MHZ       SHIFTIN(2, MAX2820_RECEIVE_BW_MASK)
+#define        MAX2820_RECEIVE_BW_7MHZ         SHIFTIN(3, MAX2820_RECEIVE_BW_MASK)
+#define        MAX2820_RECEIVE_BW_6_5MHZ       SHIFTIN(4, MAX2820_RECEIVE_BW_MASK)
+#define        MAX2820_RECEIVE_BW_6MHZ         SHIFTIN(5, MAX2820_RECEIVE_BW_MASK)
+#define        MAX2820_RECEIVE_2C_DEFAULT      SHIFTIN(7, MAX2820_RECEIVE_2C_MASK)
+#define        MAX2820_RECEIVE_1C_DEFAULT      SHIFTIN(7, MAX2820_RECEIVE_1C_MASK)
+#define        MAX2820_RECEIVE_DL_DEFAULT      SHIFTIN(1, MAX2820_RECEIVE_DL_MASK)
+#define        MAX2820_RECEIVE_SF_DEFAULT      SHIFTIN(0, MAX2820_RECEIVE_SF)
+#define        MAX2820_RECEIVE_BW_DEFAULT      MAX2820_RECEIVE_BW_7_5MHZ
+
+#define MAX2820A_RECEIVE       4               /* Receiver Settings Register,
+                                                * MAX2820A/MAX2821A
+                                                */
+/* VGA DC Offset Nulling Parameter 2 */
+#define        MAX2820A_RECEIVE_2C_MASK        __BITS(11,9)
+#define        MAX2820A_RECEIVE_2C_DEFAULT     SHIFTIN(7, MAX2820A_RECEIVE_2C_MASK)
+/* VGA DC Offset Nulling Parameter 1 */
+#define        MAX2820A_RECEIVE_1C_MASK        __BITS(8,6)
+#define        MAX2820A_RECEIVE_1C_DEFAULT     SHIFTIN(7, MAX2820A_RECEIVE_1C_MASK)
+#define        MAX2820A_RECEIVE_RSVD0_MASK     __BITS(5,3)
+#define        MAX2820A_RECEIVE_RSVD0_DEFAULT  SHIFTIN(2, MAX2820A_RECEIVE_RSVD0_MASK)
+#define        MAX2820A_RECEIVE_RSVD1_MASK     __BITS(2,0)
+#define        MAX2820A_RECEIVE_RSVD1_DEFAULT  SHIFTIN(2,MAX2820_RECEIVE_RSVD1_MASK)
+
+#define MAX2820_TRANSMIT       5               /* Transmitter Settings Reg. */
+#define MAX2820_TRANSMIT_RSVD_MASK     __BITS(11,4)    /* reserved */
+#define MAX2820_TRANSMIT_PA_MASK       __BITS(3,0)     /* PA Bias Select
+                                                        * 15 = Highest
+                                                        * 0 = Lowest
+                                                        */
+#define MAX2820_TRANSMIT_PA_DEFAULT    SHIFTIN(0, MAX2820_TRANSMIT_PA_MASK)
+
+#endif /* _DEV_IC_MAX2820REG_H_ */
diff --git a/sys/dev/netif/rtw/rtw.c b/sys/dev/netif/rtw/rtw.c
new file mode 100644 (file)
index 0000000..05441d7
--- /dev/null
@@ -0,0 +1,4450 @@
+/*
+ * Copyright (c) 2006 The DragonFly Project.  All rights reserved.
+ * 
+ * This code is derived from software contributed to The DragonFly Project
+ * by Sepherosa Ziehau <sepherosa@gmail.com>
+ * 
+ * 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. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS 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: rtw.c,v 1.72 2006/03/28 00:48:10 dyoung Exp $
+ * $DragonFly: src/sys/dev/netif/rtw/rtw.c,v 1.1 2006/09/03 07:37:58 sephe Exp $
+ */
+
+/*
+ * Copyright (c) 2004, 2005 David Young.  All rights reserved.
+ *
+ * Programmed for NetBSD by David Young.
+ *
+ * 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.
+ */
+
+/*
+ * Device driver for the Realtek RTL8180 802.11 MAC/BBP.
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/serialize.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/ifq_var.h>
+#include <net/ethernet.h>
+#include <net/bpf.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+#include <netproto/802_11/ieee80211_radiotap.h>
+
+#include "rtwbitop.h"
+#include "rtwreg.h"
+#include "rtwvar.h"
+#include "rtwphyio.h"
+#include "rtwphy.h"
+#include "smc93cx6var.h"
+
+/* XXX */
+#define IEEE80211_DUR_DS_LONG_PREAMBLE 144
+#define IEEE80211_DUR_DS_SHORT_PREAMBLE        72
+#define IEEE80211_DUR_DS_SLOW_PLCPHDR  48
+#define IEEE80211_DUR_DS_FAST_PLCPHDR  24
+#define IEEE80211_DUR_DS_SLOW_ACK      112
+#define IEEE80211_DUR_DS_SLOW_CTS      112
+#define IEEE80211_DUR_DS_SIFS          10
+
+struct rtw_txsegs {
+       int                     nseg;
+       bus_dma_segment_t       segs[RTW_MAXPKTSEGS];
+};
+
+devclass_t     rtw_devclass;
+
+static const struct ieee80211_rateset rtw_rates_11b = { 4, { 2, 4, 11, 22 } };
+
+SYSCTL_NODE(_hw, OID_AUTO, rtw, CTLFLAG_RD, 0,
+           "Realtek RTL818x 802.11 controls");
+
+/* [0, SHIFTOUT(RTW_CONFIG4_RFTYPE_MASK, RTW_CONFIG4_RFTYPE_MASK)] */
+static int     rtw_rfprog_fallback = 0;
+SYSCTL_INT(_hw_rtw, OID_AUTO, rfprog_fallback, CTLFLAG_RW,
+          &rtw_rfprog_fallback, 0, "fallback RF programming method");
+
+static int     rtw_host_rfio = 0;              /* 0/1 */
+SYSCTL_INT(_hw_rtw, OID_AUTO, host_rfio, CTLFLAG_RW,
+          &rtw_host_rfio, 0, "enable host control of RF I/O");
+
+#ifdef RTW_DEBUG
+int            rtw_debug = 0;                  /* [0, RTW_DEBUG_MAX] */
+SYSCTL_INT(_hw_rtw, OID_AUTO, debug, CTLFLAG_RW, &rtw_debug, 0, "debug level");
+
+static int     rtw_rxbufs_limit = RTW_RXQLEN;  /* [0, RTW_RXQLEN] */
+SYSCTL_INT(_hw_rtw, OID_AUTO, rxbufs_limit, CTLFLAG_RW, &rtw_rxbufs_limit, 0,
+          "rx buffers limit");
+#endif /* RTW_DEBUG */
+
+#if 0
+static int     rtw_xmtr_restart = 0;
+SYSCTL_INT(_hw_rtw, OID_AUTO, xmtr_restart, CTLFLAG_RW, &rtw_xmtr_restart, 0,
+          "gratuitously reset xmtr on rcvr error");
+
+static int     rtw_ring_reset = 0;
+SYSCTL_INT(_hw_rtw, OID_AUTO, ring_reset, CTLFLAG_RW, &rtw_ring_reset, 0,
+          "reset ring pointers on rcvr error");
+#endif
+
+static int     rtw_do_chip_reset = 0;
+SYSCTL_INT(_hw_rtw, OID_AUTO, chip_reset, CTLFLAG_RW, &rtw_do_chip_reset, 0,
+          "gratuitously reset chip on rcvr error");
+
+int            rtw_dwelltime = 200;    /* milliseconds */
+
+/* XXX */
+static struct ieee80211_cipher rtw_cipher_wep;
+
+static void    rtw_led_init(struct rtw_softc *);
+static void    rtw_led_newstate(struct rtw_softc *, enum ieee80211_state);
+static void    rtw_led_slowblink(void *);
+static void    rtw_led_fastblink(void *);
+static void    rtw_led_set(struct rtw_softc *);
+
+static void    rtw_init(void *);
+static void    rtw_start(struct ifnet *);
+static int     rtw_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *);
+static void    rtw_watchdog(struct ifnet *);
+static void    rtw_intr(void *);
+
+static void    rtw_intr_rx(struct rtw_softc *, uint16_t);
+static void    rtw_intr_tx(struct rtw_softc *, uint16_t);
+static void    rtw_intr_beacon(struct rtw_softc *, uint16_t);
+static void    rtw_intr_atim(struct rtw_softc *);
+static void    rtw_intr_ioerror(struct rtw_softc *, uint16_t);
+static void    rtw_intr_timeout(struct rtw_softc *);
+
+static int     rtw_dequeue(struct ifnet *, struct rtw_txsoft_blk **,
+                           struct rtw_txdesc_blk **, struct mbuf **,
+                           struct ieee80211_node **);
+static struct mbuf *rtw_load_txbuf(struct rtw_softc *, struct rtw_txsoft *,
+                                  struct rtw_txsegs *, int, struct mbuf *);
+
+static void    rtw_idle(struct rtw_softc *);
+static void    rtw_txring_fixup(struct rtw_softc *);
+static void    rtw_rxring_fixup(struct rtw_softc *);
+static int     rtw_txring_next(struct rtw_regs *, struct rtw_txdesc_blk *);
+static void    rtw_reset_oactive(struct rtw_softc *);
+
+static int     rtw_enable(struct rtw_softc *);
+static void    rtw_disable(struct rtw_softc *);
+static void    rtw_io_enable(struct rtw_softc *, uint8_t, int);
+static int     rtw_pwrstate(struct rtw_softc *, enum rtw_pwrstate);
+static void    rtw_set_access(struct rtw_softc *, enum rtw_access);
+
+static void    rtw_continuous_tx_enable(struct rtw_softc *, int);
+static void    rtw_txdac_enable(struct rtw_softc *, int);
+static void    rtw_anaparm_enable(struct rtw_regs *, int);
+static void    rtw_config0123_enable(struct rtw_regs *, int);
+
+static void    rtw_transmit_config(struct rtw_regs *);
+static void    rtw_set_rfprog(struct rtw_softc *);
+static void    rtw_enable_interrupts(struct rtw_softc *);
+static void    rtw_pktfilt_load(struct rtw_softc *);
+static void    rtw_wep_setkeys(struct rtw_softc *);
+static void    rtw_resume_ticks(struct rtw_softc *);
+static void    rtw_set_nettype(struct rtw_softc *, enum ieee80211_opmode);
+
+static int     rtw_reset(struct rtw_softc *);
+static int     rtw_chip_reset(struct rtw_softc *);
+static int     rtw_recall_eeprom(struct rtw_softc *);
+static int     rtw_srom_read(struct rtw_softc *);
+static int     rtw_srom_parse(struct rtw_softc *);
+static struct rtw_rf *rtw_rf_attach(struct rtw_softc *, enum rtw_rfchipid, int);
+
+static uint8_t rtw_check_phydelay(struct rtw_regs *, uint32_t);
+static void    rtw_identify_country(struct rtw_softc *);
+static int     rtw_identify_sta(struct rtw_softc *);
+
+static int     rtw_swring_setup(struct rtw_softc *);
+static void    rtw_hwring_setup(struct rtw_softc *);
+
+static int     rtw_desc_blk_alloc(struct rtw_softc *);
+static void    rtw_desc_blk_free(struct rtw_softc *);
+static int     rtw_soft_blk_alloc(struct rtw_softc *);
+static void    rtw_soft_blk_free(struct rtw_softc *);
+
+static void    rtw_txdesc_blk_init_all(struct rtw_softc *);
+static void    rtw_txsoft_blk_init_all(struct rtw_softc *);
+static void    rtw_rxdesc_blk_init_all(struct rtw_softc *);
+static int     rtw_rxsoft_blk_init_all(struct rtw_softc *);
+
+static void    rtw_txdesc_blk_reset_all(struct rtw_softc *);
+
+static int     rtw_rxsoft_alloc(struct rtw_softc *, struct rtw_rxsoft *, int);
+static void    rtw_rxdesc_init(struct rtw_softc *, int idx, int);
+
+#ifdef RTW_DEBUG
+static void    rtw_print_txdesc(struct rtw_softc *, const char *,
+                                struct rtw_txsoft *, struct rtw_txdesc_blk *,
+                                int);
+#endif /* RTW_DEBUG */
+
+static int     rtw_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static void    rtw_next_scan(void *);
+
+static int     rtw_key_delete(struct ieee80211com *,
+                              const struct ieee80211_key *);
+static int     rtw_key_set(struct ieee80211com *,
+                           const struct ieee80211_key *,
+                           const u_int8_t[IEEE80211_ADDR_LEN]);
+static void    rtw_key_update_end(struct ieee80211com *);
+static void    rtw_key_update_begin(struct ieee80211com *);
+static int     rtw_wep_decap(struct ieee80211_key *, struct mbuf *, int);
+
+static int     rtw_compute_duration1(int, int, uint32_t, int,
+                                     struct rtw_duration *);
+static int     rtw_compute_duration(const struct ieee80211_frame_min *,
+                                    const struct ieee80211_key *, int,
+                                    uint32_t, int, int,
+                                    struct rtw_duration *,
+                                    struct rtw_duration *, int *, int);
+
+#ifdef RTW_DEBUG
+static void
+rtw_print_regs(struct rtw_regs *regs, const char *dvname, const char *where)
+{
+#define PRINTREG32(sc, reg)                            \
+       RTW_DPRINTF(RTW_DEBUG_REGDUMP,                  \
+           ("%s: reg[ " #reg " / %03x ] = %08x\n",     \
+           dvname, reg, RTW_READ(regs, reg)))
+
+#define PRINTREG16(sc, reg)                            \
+       RTW_DPRINTF(RTW_DEBUG_REGDUMP,                  \
+           ("%s: reg[ " #reg " / %03x ] = %04x\n",     \
+           dvname, reg, RTW_READ16(regs, reg)))
+
+#define PRINTREG8(sc, reg)                             \
+       RTW_DPRINTF(RTW_DEBUG_REGDUMP,                  \
+           ("%s: reg[ " #reg " / %03x ] = %02x\n",     \
+           dvname, reg, RTW_READ8(regs, reg)))
+
+       RTW_DPRINTF(RTW_DEBUG_REGDUMP, ("%s: %s\n", dvname, where));
+
+       PRINTREG32(regs, RTW_IDR0);
+       PRINTREG32(regs, RTW_IDR1);
+       PRINTREG32(regs, RTW_MAR0);
+       PRINTREG32(regs, RTW_MAR1);
+       PRINTREG32(regs, RTW_TSFTRL);
+       PRINTREG32(regs, RTW_TSFTRH);
+       PRINTREG32(regs, RTW_TLPDA);
+       PRINTREG32(regs, RTW_TNPDA);
+       PRINTREG32(regs, RTW_THPDA);
+       PRINTREG32(regs, RTW_TCR);
+       PRINTREG32(regs, RTW_RCR);
+       PRINTREG32(regs, RTW_TINT);
+       PRINTREG32(regs, RTW_TBDA);
+       PRINTREG32(regs, RTW_ANAPARM);
+       PRINTREG32(regs, RTW_BB);
+       PRINTREG32(regs, RTW_PHYCFG);
+       PRINTREG32(regs, RTW_WAKEUP0L);
+       PRINTREG32(regs, RTW_WAKEUP0H);
+       PRINTREG32(regs, RTW_WAKEUP1L);
+       PRINTREG32(regs, RTW_WAKEUP1H);
+       PRINTREG32(regs, RTW_WAKEUP2LL);
+       PRINTREG32(regs, RTW_WAKEUP2LH);
+       PRINTREG32(regs, RTW_WAKEUP2HL);
+       PRINTREG32(regs, RTW_WAKEUP2HH);
+       PRINTREG32(regs, RTW_WAKEUP3LL);
+       PRINTREG32(regs, RTW_WAKEUP3LH);
+       PRINTREG32(regs, RTW_WAKEUP3HL);
+       PRINTREG32(regs, RTW_WAKEUP3HH);
+       PRINTREG32(regs, RTW_WAKEUP4LL);
+       PRINTREG32(regs, RTW_WAKEUP4LH);
+       PRINTREG32(regs, RTW_WAKEUP4HL);
+       PRINTREG32(regs, RTW_WAKEUP4HH);
+       PRINTREG32(regs, RTW_DK0);
+       PRINTREG32(regs, RTW_DK1);
+       PRINTREG32(regs, RTW_DK2);
+       PRINTREG32(regs, RTW_DK3);
+       PRINTREG32(regs, RTW_RETRYCTR);
+       PRINTREG32(regs, RTW_RDSAR);
+       PRINTREG32(regs, RTW_FER);
+       PRINTREG32(regs, RTW_FEMR);
+       PRINTREG32(regs, RTW_FPSR);
+       PRINTREG32(regs, RTW_FFER);
+
+       /* 16-bit registers */
+       PRINTREG16(regs, RTW_BRSR);
+       PRINTREG16(regs, RTW_IMR);
+       PRINTREG16(regs, RTW_ISR);
+       PRINTREG16(regs, RTW_BCNITV);
+       PRINTREG16(regs, RTW_ATIMWND);
+       PRINTREG16(regs, RTW_BINTRITV);
+       PRINTREG16(regs, RTW_ATIMTRITV);
+       PRINTREG16(regs, RTW_CRC16ERR);
+       PRINTREG16(regs, RTW_CRC0);
+       PRINTREG16(regs, RTW_CRC1);
+       PRINTREG16(regs, RTW_CRC2);
+       PRINTREG16(regs, RTW_CRC3);
+       PRINTREG16(regs, RTW_CRC4);
+       PRINTREG16(regs, RTW_CWR);
+
+       /* 8-bit registers */
+       PRINTREG8(regs, RTW_CR);
+       PRINTREG8(regs, RTW_9346CR);
+       PRINTREG8(regs, RTW_CONFIG0);
+       PRINTREG8(regs, RTW_CONFIG1);
+       PRINTREG8(regs, RTW_CONFIG2);
+       PRINTREG8(regs, RTW_MSR);
+       PRINTREG8(regs, RTW_CONFIG3);
+       PRINTREG8(regs, RTW_CONFIG4);
+       PRINTREG8(regs, RTW_TESTR);
+       PRINTREG8(regs, RTW_PSR);
+       PRINTREG8(regs, RTW_SCR);
+       PRINTREG8(regs, RTW_PHYDELAY);
+       PRINTREG8(regs, RTW_CRCOUNT);
+       PRINTREG8(regs, RTW_PHYADDR);
+       PRINTREG8(regs, RTW_PHYDATAW);
+       PRINTREG8(regs, RTW_PHYDATAR);
+       PRINTREG8(regs, RTW_CONFIG5);
+       PRINTREG8(regs, RTW_TPPOLL);
+
+       PRINTREG16(regs, RTW_BSSID16);
+       PRINTREG32(regs, RTW_BSSID32);
+#undef PRINTREG32
+#undef PRINTREG16
+#undef PRINTREG8
+}
+#endif /* RTW_DEBUG */
+
+static void
+rtw_continuous_tx_enable(struct rtw_softc *sc, int enable)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+       uint32_t tcr;
+
+       tcr = RTW_READ(regs, RTW_TCR);
+       tcr &= ~RTW_TCR_LBK_MASK;
+       if (enable)
+               tcr |= RTW_TCR_LBK_CONT;
+       else
+               tcr |= RTW_TCR_LBK_NORMAL;
+       RTW_WRITE(regs, RTW_TCR, tcr);
+       RTW_SYNC(regs, RTW_TCR, RTW_TCR);
+       rtw_set_access(sc, RTW_ACCESS_ANAPARM);
+       rtw_txdac_enable(sc, !enable);
+       rtw_set_access(sc, RTW_ACCESS_ANAPARM);/* XXX Voodoo from Linux. */
+       rtw_set_access(sc, RTW_ACCESS_NONE);
+}
+
+#ifdef RTW_DEBUG
+static const char *
+rtw_access_string(enum rtw_access access)
+{
+       switch (access) {
+       case RTW_ACCESS_NONE:
+               return "none";
+       case RTW_ACCESS_CONFIG:
+               return "config";
+       case RTW_ACCESS_ANAPARM:
+               return "anaparm";
+       default:
+               return "unknown";
+       }
+}
+#endif /* RTW_DEBUG */
+
+static void
+rtw_set_access1(struct rtw_regs *regs, enum rtw_access naccess)
+{
+       KKASSERT(naccess >= RTW_ACCESS_NONE && naccess <= RTW_ACCESS_ANAPARM);
+       KKASSERT(regs->r_access >= RTW_ACCESS_NONE &&
+                regs->r_access <= RTW_ACCESS_ANAPARM);
+
+       if (naccess == regs->r_access)
+               return;
+
+       switch (naccess) {
+       case RTW_ACCESS_NONE:
+               switch (regs->r_access) {
+               case RTW_ACCESS_ANAPARM:
+                       rtw_anaparm_enable(regs, 0);
+                       /*FALLTHROUGH*/
+               case RTW_ACCESS_CONFIG:
+                       rtw_config0123_enable(regs, 0);
+                       /*FALLTHROUGH*/
+               case RTW_ACCESS_NONE:
+                       break;
+               }
+               break;
+       case RTW_ACCESS_CONFIG:
+               switch (regs->r_access) {
+               case RTW_ACCESS_NONE:
+                       rtw_config0123_enable(regs, 1);
+                       /*FALLTHROUGH*/
+               case RTW_ACCESS_CONFIG:
+                       break;
+               case RTW_ACCESS_ANAPARM:
+                       rtw_anaparm_enable(regs, 0);
+                       break;
+               }
+               break;
+       case RTW_ACCESS_ANAPARM:
+               switch (regs->r_access) {
+               case RTW_ACCESS_NONE:
+                       rtw_config0123_enable(regs, 1);
+                       /*FALLTHROUGH*/
+               case RTW_ACCESS_CONFIG:
+                       rtw_anaparm_enable(regs, 1);
+                       /*FALLTHROUGH*/
+               case RTW_ACCESS_ANAPARM:
+                       break;
+               }
+               break;
+       }
+}
+
+static void
+rtw_set_access(struct rtw_softc *sc, enum rtw_access access)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+
+       rtw_set_access1(regs, access);
+       RTW_DPRINTF(RTW_DEBUG_ACCESS,
+           ("%s: access %s -> %s\n", sc->sc_ic.ic_if.if_xname,
+           rtw_access_string(regs->r_access),
+           rtw_access_string(access)));
+       regs->r_access = access;
+}
+
+/*
+ * Enable registers, switch register banks.
+ */
+static void
+rtw_config0123_enable(struct rtw_regs *regs, int enable)
+{
+       uint8_t ecr;
+
+       ecr = RTW_READ8(regs, RTW_9346CR);
+       ecr &= ~(RTW_9346CR_EEM_MASK | RTW_9346CR_EECS | RTW_9346CR_EESK);
+       if (enable) {
+               ecr |= RTW_9346CR_EEM_CONFIG;
+       } else {
+               RTW_WBW(regs, RTW_9346CR, MAX(RTW_CONFIG0, RTW_CONFIG3));
+               ecr |= RTW_9346CR_EEM_NORMAL;
+       }
+       RTW_WRITE8(regs, RTW_9346CR, ecr);
+       RTW_SYNC(regs, RTW_9346CR, RTW_9346CR);
+}
+
+/* requires rtw_config0123_enable(, 1) */
+static void
+rtw_anaparm_enable(struct rtw_regs *regs, int enable)
+{
+       uint8_t cfg3;
+
+       cfg3 = RTW_READ8(regs, RTW_CONFIG3);
+       cfg3 |= RTW_CONFIG3_CLKRUNEN;
+       if (enable)
+               cfg3 |= RTW_CONFIG3_PARMEN;
+       else
+               cfg3 &= ~RTW_CONFIG3_PARMEN;
+       RTW_WRITE8(regs, RTW_CONFIG3, cfg3);
+       RTW_SYNC(regs, RTW_CONFIG3, RTW_CONFIG3);
+}
+
+/* requires rtw_anaparm_enable(, 1) */
+static void
+rtw_txdac_enable(struct rtw_softc *sc, int enable)
+{
+       uint32_t anaparm;
+       struct rtw_regs *regs = &sc->sc_regs;
+
+       anaparm = RTW_READ(regs, RTW_ANAPARM);
+       if (enable)
+               anaparm &= ~RTW_ANAPARM_TXDACOFF;
+       else
+               anaparm |= RTW_ANAPARM_TXDACOFF;
+       RTW_WRITE(regs, RTW_ANAPARM, anaparm);
+       RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM);
+}
+
+static int
+rtw_chip_reset1(struct rtw_softc *sc)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+       uint8_t cr;
+       int i;
+
+       RTW_WRITE8(regs, RTW_CR, RTW_CR_RST);
+
+       RTW_WBR(regs, RTW_CR, RTW_CR);
+
+       for (i = 0; i < 1000; i++) {
+               if ((cr = RTW_READ8(regs, RTW_CR) & RTW_CR_RST) == 0) {
+                       RTW_DPRINTF(RTW_DEBUG_RESET,
+                           ("%s: reset in %dus\n",
+                            sc->sc_ic.ic_if.if_xname, i));
+                       return 0;
+               }
+               RTW_RBR(regs, RTW_CR, RTW_CR);
+               DELAY(10); /* 10us */
+       }
+
+       if_printf(&sc->sc_ic.ic_if, "reset failed\n");
+       return ETIMEDOUT;
+}
+
+static int
+rtw_chip_reset(struct rtw_softc *sc)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+       uint32_t tcr;
+
+       /* from Linux driver */
+       tcr = RTW_TCR_CWMIN | RTW_TCR_MXDMA_2048 |
+             SHIFTIN(7, RTW_TCR_SRL_MASK) | SHIFTIN(7, RTW_TCR_LRL_MASK);
+
+       RTW_WRITE(regs, RTW_TCR, tcr);
+
+       RTW_WBW(regs, RTW_CR, RTW_TCR);
+
+       return rtw_chip_reset1(sc);
+}
+
+static int
+rtw_wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
+{
+       struct ieee80211_key keycopy;
+       const struct ieee80211_cipher *wep_cipher;
+
+       RTW_DPRINTF(RTW_DEBUG_KEY, ("%s:\n", __func__));
+
+       keycopy = *k;
+       keycopy.wk_flags &= ~IEEE80211_KEY_SWCRYPT;
+
+       wep_cipher = ieee80211_crypto_cipher(IEEE80211_CIPHER_WEP);
+       KKASSERT(wep_cipher != NULL);
+
+       return wep_cipher->ic_decap(&keycopy, m, hdrlen);
+}
+
+static int
+rtw_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
+{
+       struct rtw_softc *sc = ic->ic_ifp->if_softc;
+       u_int keyix = k->wk_keyix;
+
+       DPRINTF(sc, RTW_DEBUG_KEY, ("%s: delete key %u\n", __func__, keyix));
+
+       if (keyix >= IEEE80211_WEP_NKID)
+               return 0;
+       if (k->wk_keylen != 0)
+               sc->sc_flags &= ~RTW_F_DK_VALID;
+       return 1;
+}
+
+static int
+rtw_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
+           const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+       struct rtw_softc *sc = ic->ic_ifp->if_softc;
+
+       DPRINTF(sc, RTW_DEBUG_KEY, ("%s: set key %u\n", __func__, k->wk_keyix));
+
+       if (k->wk_keyix >= IEEE80211_WEP_NKID)
+               return 0;
+
+       sc->sc_flags &= ~RTW_F_DK_VALID;
+       return 1;
+}
+
+static void
+rtw_key_update_begin(struct ieee80211com *ic)
+{
+#ifdef RTW_DEBUG
+       struct ifnet *ifp = ic->ic_ifp;
+       struct rtw_softc *sc = ifp->if_softc;
+#endif
+
+       DPRINTF(sc, RTW_DEBUG_KEY, ("%s:\n", __func__));
+}
+
+static void
+rtw_key_update_end(struct ieee80211com *ic)
+{
+       struct ifnet *ifp = ic->ic_ifp;
+       struct rtw_softc *sc = ifp->if_softc;
+
+       DPRINTF(sc, RTW_DEBUG_KEY, ("%s:\n", __func__));
+
+       if ((sc->sc_flags & RTW_F_DK_VALID) != 0 ||
+           (sc->sc_flags & RTW_F_ENABLED) == 0 ||
+           (sc->sc_flags & RTW_F_INVALID) != 0)
+               return;
+
+       rtw_io_enable(sc, RTW_CR_RE | RTW_CR_TE, 0);
+       rtw_wep_setkeys(sc);
+       rtw_io_enable(sc, RTW_CR_RE | RTW_CR_TE,
+                     (ifp->if_flags & IFF_RUNNING) != 0);
+}
+
+static __inline int
+rtw_key_hwsupp(uint32_t flags, const struct ieee80211_key *k)
+{
+       if (k->wk_cipher->ic_cipher != IEEE80211_CIPHER_WEP)
+               return 0;
+
+       return ((flags & RTW_C_RXWEP_40) != 0 && k->wk_keylen == 5) ||
+              ((flags & RTW_C_RXWEP_104) != 0 && k->wk_keylen == 13);
+}
+
+static void
+rtw_wep_setkeys(struct rtw_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ieee80211_key *wk = ic->ic_nw_keys;
+       const struct ieee80211_cipher *wep_cipher;
+       struct rtw_regs *regs = &sc->sc_regs;
+       union rtw_keys *rk = &sc->sc_keys;
+       uint8_t psr, scr;
+       int i, keylen;
+
+       memset(rk->rk_keys, 0, sizeof(rk->rk_keys));
+
+       wep_cipher = ieee80211_crypto_cipher(IEEE80211_CIPHER_WEP);
+       KKASSERT(wep_cipher != NULL);
+
+       /* Temporarily use software crypto for all keys. */
+       for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+               if (wk[i].wk_cipher == &rtw_cipher_wep)
+                       wk[i].wk_cipher = wep_cipher;
+       }
+
+       rtw_set_access(sc, RTW_ACCESS_CONFIG);
+
+       psr = RTW_READ8(regs, RTW_PSR);
+       scr = RTW_READ8(regs, RTW_SCR);
+       scr &= ~(RTW_SCR_KM_MASK | RTW_SCR_TXSECON | RTW_SCR_RXSECON);
+
+       if ((sc->sc_ic.ic_flags & IEEE80211_F_PRIVACY) == 0)
+               goto out;
+
+       for (keylen = i = 0; i < IEEE80211_WEP_NKID; i++) {
+               if (!rtw_key_hwsupp(sc->sc_flags, &wk[i]))
+                       continue;
+               if (i == ic->ic_def_txkey) {
+                       keylen = wk[i].wk_keylen;
+                       break;
+               }
+               keylen = MAX(keylen, wk[i].wk_keylen);
+       }
+
+       if (keylen == 5)
+               scr |= RTW_SCR_KM_WEP40 | RTW_SCR_RXSECON;
+       else if (keylen == 13)
+               scr |= RTW_SCR_KM_WEP104 | RTW_SCR_RXSECON;
+
+       for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+               if (wk[i].wk_keylen != keylen ||
+                   wk[i].wk_cipher->ic_cipher != IEEE80211_CIPHER_WEP)
+                       continue;
+               /* h/w will decrypt, s/w still strips headers */
+               wk[i].wk_cipher = &rtw_cipher_wep;
+               memcpy(rk->rk_keys[i], wk[i].wk_key, wk[i].wk_keylen);
+       }
+out:
+       RTW_WRITE8(regs, RTW_PSR, psr & ~RTW_PSR_PSEN);
+
+       bus_space_write_region_4(regs->r_bt, regs->r_bh, RTW_DK0, rk->rk_words,
+               sizeof(rk->rk_words) / sizeof(rk->rk_words[0]));
+
+       RTW_WBW(regs, RTW_DK0, RTW_PSR);
+       RTW_WRITE8(regs, RTW_PSR, psr);
+       RTW_WBW(regs, RTW_PSR, RTW_SCR);
+       RTW_WRITE8(regs, RTW_SCR, scr);
+       RTW_SYNC(regs, RTW_SCR, RTW_SCR);
+       rtw_set_access(sc, RTW_ACCESS_NONE);
+       sc->sc_flags |= RTW_F_DK_VALID;
+}
+
+static int
+rtw_recall_eeprom(struct rtw_softc *sc)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+       int i;
+       uint8_t ecr;
+
+       ecr = RTW_READ8(regs, RTW_9346CR);
+       ecr = (ecr & ~RTW_9346CR_EEM_MASK) | RTW_9346CR_EEM_AUTOLOAD;
+       RTW_WRITE8(regs, RTW_9346CR, ecr);
+
+       RTW_WBR(regs, RTW_9346CR, RTW_9346CR);
+
+       /* wait 25ms for completion */
+       for (i = 0; i < 250; i++) {
+               ecr = RTW_READ8(regs, RTW_9346CR);
+               if ((ecr & RTW_9346CR_EEM_MASK) == RTW_9346CR_EEM_NORMAL) {
+                       RTW_DPRINTF(RTW_DEBUG_RESET,
+                           ("%s: recall EEPROM in %dus\n",
+                            sc->sc_ic.ic_if.if_xname, i * 100));
+                       return 0;
+               }
+               RTW_RBR(regs, RTW_9346CR, RTW_9346CR);
+               DELAY(100);
+       }
+       if_printf(&sc->sc_ic.ic_if, "recall EEPROM failed\n");
+       return ETIMEDOUT;
+}
+
+static int
+rtw_reset(struct rtw_softc *sc)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+       uint8_t config1;
+       int rc;
+
+       sc->sc_flags &= ~RTW_F_DK_VALID;
+
+       rc = rtw_chip_reset(sc);
+       if (rc)
+               return rc;
+
+       rtw_recall_eeprom(sc);  /* ignore err */
+
+       config1 = RTW_READ8(regs, RTW_CONFIG1);
+       RTW_WRITE8(regs, RTW_CONFIG1, config1 & ~RTW_CONFIG1_PMEN);
+       /* TBD turn off maximum power saving? */
+       return 0;
+}
+
+static int
+rtw_srom_parse(struct rtw_softc *sc)
+{
+       struct rtw_srom *sr = &sc->sc_srom;
+       char scratch[sizeof("unknown 0xXX")];
+       uint8_t mac[IEEE80211_ADDR_LEN];
+       const char *rfname, *paname;
+       uint16_t srom_version;
+       int i;
+
+       sc->sc_flags &= ~(RTW_F_DIGPHY | RTW_F_DFLANTB | RTW_F_ANTDIV);
+       sc->sc_rcr &= ~(RTW_RCR_ENCS1 | RTW_RCR_ENCS2);
+
+       srom_version = RTW_SR_GET16(sr, RTW_SR_VERSION);
+       if_printf(&sc->sc_ic.ic_if, "SROM version %d.%d",
+                 srom_version >> 8, srom_version & 0xff);
+
+       if (srom_version <= 0x0101) {
+               printf(" is not understood, limping along with defaults\n");
+
+               /* Default values */
+               sc->sc_flags |= (RTW_F_DIGPHY | RTW_F_ANTDIV);
+               sc->sc_csthr = RTW_SR_ENERGYDETTHR_DEFAULT;
+               sc->sc_rcr |= RTW_RCR_ENCS1;
+               sc->sc_rfchipid = RTW_RFCHIPID_PHILIPS;
+               return 0;
+       }
+       printf("\n");
+
+       for (i = 0; i < IEEE80211_ADDR_LEN; i++)
+               mac[i] = RTW_SR_GET(sr, RTW_SR_MAC + i);
+
+       RTW_DPRINTF(RTW_DEBUG_ATTACH,
+                   ("%s: EEPROM MAC %6D\n", sc->sc_ic.ic_if.if_xname, mac, ":"));
+
+       sc->sc_csthr = RTW_SR_GET(sr, RTW_SR_ENERGYDETTHR);
+
+       if ((RTW_SR_GET(sr, RTW_SR_CONFIG2) & RTW_CONFIG2_ANT) != 0)
+               sc->sc_flags |= RTW_F_ANTDIV;
+
+       /*
+        * Note well: the sense of the RTW_SR_RFPARM_DIGPHY bit seems
+        * to be reversed.
+        */
+       if ((RTW_SR_GET(sr, RTW_SR_RFPARM) & RTW_SR_RFPARM_DIGPHY) == 0)
+               sc->sc_flags |= RTW_F_DIGPHY;
+       if ((RTW_SR_GET(sr, RTW_SR_RFPARM) & RTW_SR_RFPARM_DFLANTB) != 0)
+               sc->sc_flags |= RTW_F_DFLANTB;
+
+       sc->sc_rcr |= SHIFTIN(SHIFTOUT(RTW_SR_GET(sr, RTW_SR_RFPARM),
+                                      RTW_SR_RFPARM_CS_MASK), RTW_RCR_ENCS1);
+
+       if ((RTW_SR_GET(sr, RTW_SR_CONFIG0) & RTW_CONFIG0_WEP104) != 0)
+               sc->sc_flags |= RTW_C_RXWEP_104;
+
+       sc->sc_flags |= RTW_C_RXWEP_40; /* XXX */
+
+       sc->sc_rfchipid = RTW_SR_GET(sr, RTW_SR_RFCHIPID);
+       switch (sc->sc_rfchipid) {
+       case RTW_RFCHIPID_GCT:          /* this combo seen in the wild */
+               rfname = "GCT GRF5101";
+               paname = "Winspring WS9901";
+               break;
+       case RTW_RFCHIPID_MAXIM:
+               rfname = "Maxim MAX2820";       /* guess */
+               paname = "Maxim MAX2422";       /* guess */
+               break;
+       case RTW_RFCHIPID_INTERSIL:
+               rfname = "Intersil HFA3873";    /* guess */
+               paname = "Intersil <unknown>";
+               break;
+       case RTW_RFCHIPID_PHILIPS:      /* this combo seen in the wild */
+               rfname = "Philips SA2400A";
+               paname = "Philips SA2411";
+               break;
+       case RTW_RFCHIPID_RFMD:
+               /* this is the same front-end as an atw(4)! */
+               rfname = "RFMD RF2948B, "       /* mentioned in Realtek docs */
+                        "LNA: RFMD RF2494, "   /* mentioned in Realtek docs */
+                        "SYN: Silicon Labs Si4126";    /* inferred from
+                                                        * reference driver
+                                                        */
+               paname = "RFMD RF2189";         /* mentioned in Realtek docs */
+               break;
+       case RTW_RFCHIPID_RESERVED:
+               rfname = paname = "reserved";
+               break;
+       default:
+               snprintf(scratch, sizeof(scratch), "unknown 0x%02x",
+                        sc->sc_rfchipid);
+               rfname = paname = scratch;
+       }
+       if_printf(&sc->sc_ic.ic_if, "RF: %s, PA: %s\n", rfname, paname);
+
+       switch (RTW_SR_GET(sr, RTW_SR_CONFIG0) & RTW_CONFIG0_GL_MASK) {
+       case RTW_CONFIG0_GL_USA:
+       case _RTW_CONFIG0_GL_USA:
+               sc->sc_locale = RTW_LOCALE_USA;
+               break;
+       case RTW_CONFIG0_GL_EUROPE:
+               sc->sc_locale = RTW_LOCALE_EUROPE;
+               break;
+       case RTW_CONFIG0_GL_JAPAN:
+               sc->sc_locale = RTW_LOCALE_JAPAN;
+               break;
+       default:
+               sc->sc_locale = RTW_LOCALE_UNKNOWN;
+               break;
+       }
+       return 0;
+}
+
+static int
+rtw_srom_read(struct rtw_softc *sc)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+       struct rtw_srom *sr = &sc->sc_srom;
+       struct seeprom_descriptor sd;
+       uint8_t ecr;
+       int rc;
+
+       memset(&sd, 0, sizeof(sd));
+
+       ecr = RTW_READ8(regs, RTW_9346CR);
+
+       if ((sc->sc_flags & RTW_F_9356SROM) != 0) {
+               RTW_DPRINTF(RTW_DEBUG_ATTACH,
+                           ("%s: 93c56 SROM\n", sc->sc_ic.ic_if.if_xname));
+               sr->sr_size = 256;
+               sd.sd_chip = C56_66;
+       } else {
+               RTW_DPRINTF(RTW_DEBUG_ATTACH,
+                           ("%s: 93c46 SROM\n", sc->sc_ic.ic_if.if_xname));
+               sr->sr_size = 128;
+               sd.sd_chip = C46;
+       }
+
+       ecr &= ~(RTW_9346CR_EEDI | RTW_9346CR_EEDO | RTW_9346CR_EESK |
+           RTW_9346CR_EEM_MASK | RTW_9346CR_EECS);
+       ecr |= RTW_9346CR_EEM_PROGRAM;
+
+       RTW_WRITE8(regs, RTW_9346CR, ecr);
+
+       sr->sr_content = malloc(sr->sr_size, M_DEVBUF, M_WAITOK | M_ZERO);
+
+       /*
+        * RTL8180 has a single 8-bit register for controlling the
+        * 93cx6 SROM.  There is no "ready" bit. The RTL8180
+        * input/output sense is the reverse of read_seeprom's.
+        */
+       sd.sd_tag = regs->r_bt;
+       sd.sd_bsh = regs->r_bh;
+       sd.sd_regsize = 1;
+       sd.sd_control_offset = RTW_9346CR;
+       sd.sd_status_offset = RTW_9346CR;
+       sd.sd_dataout_offset = RTW_9346CR;
+       sd.sd_CK = RTW_9346CR_EESK;
+       sd.sd_CS = RTW_9346CR_EECS;
+       sd.sd_DI = RTW_9346CR_EEDO;
+       sd.sd_DO = RTW_9346CR_EEDI;
+       /* make read_seeprom enter EEPROM read/write mode */
+       sd.sd_MS = ecr;
+       sd.sd_RDY = 0;
+
+       /* TBD bus barriers */
+       if (!read_seeprom(&sd, sr->sr_content, 0, sr->sr_size / 2)) {
+               if_printf(&sc->sc_ic.ic_if, "could not read SROM\n");
+               free(sr->sr_content, M_DEVBUF);
+               sr->sr_content = NULL;
+               return EIO;     /* XXX */
+       }
+
+       /* end EEPROM read/write mode */
+       RTW_WRITE8(regs, RTW_9346CR,
+                  (ecr & ~RTW_9346CR_EEM_MASK) | RTW_9346CR_EEM_NORMAL);
+       RTW_WBRW(regs, RTW_9346CR, RTW_9346CR);
+
+       rc = rtw_recall_eeprom(sc);
+       if (rc)
+               return rc;
+
+#ifdef RTW_DEBUG
+       {
+               int i;
+               RTW_DPRINTF(RTW_DEBUG_ATTACH,
+                   ("\n%s: serial ROM:\n\t", sc->sc_ic.ic_if.if_xname));
+               for (i = 0; i < sr->sr_size/2; i++) {
+                       if (((i % 8) == 0) && (i != 0))
+                               RTW_DPRINTF(RTW_DEBUG_ATTACH, ("\n\t"));
+                       RTW_DPRINTF(RTW_DEBUG_ATTACH,
+                           (" %04x", sr->sr_content[i]));
+               }
+               RTW_DPRINTF(RTW_DEBUG_ATTACH, ("\n"));
+       }
+#endif /* RTW_DEBUG */
+       return 0;
+}
+
+static void
+rtw_set_rfprog(struct rtw_softc *sc)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+       const char *method;
+       uint8_t cfg4;
+
+       cfg4 = RTW_READ8(regs, RTW_CONFIG4) & ~RTW_CONFIG4_RFTYPE_MASK;
+
+       switch (sc->sc_rfchipid) {
+       default:
+               cfg4 |= SHIFTIN(rtw_rfprog_fallback, RTW_CONFIG4_RFTYPE_MASK);
+               method = "fallback";
+               break;
+       case RTW_RFCHIPID_INTERSIL:
+               cfg4 |= RTW_CONFIG4_RFTYPE_INTERSIL;
+               method = "Intersil";
+               break;
+       case RTW_RFCHIPID_PHILIPS:
+               cfg4 |= RTW_CONFIG4_RFTYPE_PHILIPS;
+               method = "Philips";
+               break;
+       case RTW_RFCHIPID_GCT:  /* XXX a guess */
+       case RTW_RFCHIPID_RFMD:
+               cfg4 |= RTW_CONFIG4_RFTYPE_RFMD;
+               method = "RFMD";
+               break;
+       }
+
+       RTW_WRITE8(regs, RTW_CONFIG4, cfg4);
+
+       RTW_WBR(regs, RTW_CONFIG4, RTW_CONFIG4);
+
+       RTW_DPRINTF(RTW_DEBUG_INIT,
+                   ("%s: %s RF programming method, %#02x\n",
+                    sc->sc_ic.ic_if.if_xname, method,
+                    RTW_READ8(regs, RTW_CONFIG4)));
+}
+
+static __inline void
+rtw_init_channels(struct rtw_softc *sc)
+{
+       const char *name = NULL;
+       struct ieee80211_channel *chans = sc->sc_ic.ic_channels;
+       int i;
+#define ADD_CHANNEL(_chans, _chan) do {                                \
+       _chans[_chan].ic_flags = IEEE80211_CHAN_B;              \
+       _chans[_chan].ic_freq =                                 \
+           ieee80211_ieee2mhz(_chan, _chans[_chan].ic_flags);  \
+} while (0)
+
+       switch (sc->sc_locale) {
+       case RTW_LOCALE_USA:    /* 1-11 */
+               name = "USA";
+               for (i = 1; i <= 11; i++)
+                       ADD_CHANNEL(chans, i);
+               break;
+       case RTW_LOCALE_JAPAN:  /* 1-14 */
+               name = "Japan";
+               ADD_CHANNEL(chans, 14);
+               for (i = 1; i <= 14; i++)
+                       ADD_CHANNEL(chans, i);
+               break;
+       case RTW_LOCALE_EUROPE: /* 1-13 */
+               name = "Europe";
+               for (i = 1; i <= 13; i++)
+                       ADD_CHANNEL(chans, i);
+               break;
+       default:                        /* 10-11 allowed by most countries */
+               name = "<unknown>";
+               for (i = 10; i <= 11; i++)
+                       ADD_CHANNEL(chans, i);
+               break;
+       }
+       if_printf(&sc->sc_ic.ic_if, "Geographic Location %s\n", name);
+#undef ADD_CHANNEL
+}
+
+
+static void
+rtw_identify_country(struct rtw_softc *sc)
+{
+       uint8_t cfg0;
+
+       cfg0 = RTW_READ8(&sc->sc_regs, RTW_CONFIG0);
+       switch (cfg0 & RTW_CONFIG0_GL_MASK) {
+       case RTW_CONFIG0_GL_USA:
+       case _RTW_CONFIG0_GL_USA:
+               sc->sc_locale = RTW_LOCALE_USA;
+               break;
+       case RTW_CONFIG0_GL_JAPAN:
+               sc->sc_locale = RTW_LOCALE_JAPAN;
+               break;
+       case RTW_CONFIG0_GL_EUROPE:
+               sc->sc_locale = RTW_LOCALE_EUROPE;
+               break;
+       default:
+               sc->sc_locale = RTW_LOCALE_UNKNOWN;
+               break;
+       }
+}
+
+static int
+rtw_identify_sta(struct rtw_softc *sc)
+{
+       static const uint8_t empty_macaddr[IEEE80211_ADDR_LEN] = {
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+       struct rtw_regs *regs = &sc->sc_regs;
+       uint8_t *addr = sc->sc_ic.ic_myaddr;
+       uint32_t idr0, idr1;
+
+       idr0 = RTW_READ(regs, RTW_IDR0);
+       idr1 = RTW_READ(regs, RTW_IDR1);
+
+       addr[0] = SHIFTOUT(idr0, __BITS(0,  7));
+       addr[1] = SHIFTOUT(idr0, __BITS(8,  15));
+       addr[2] = SHIFTOUT(idr0, __BITS(16, 23));
+       addr[3] = SHIFTOUT(idr0, __BITS(24 ,31));
+
+       addr[4] = SHIFTOUT(idr1, __BITS(0,  7));
+       addr[5] = SHIFTOUT(idr1, __BITS(8, 15));
+
+       if (IEEE80211_ADDR_EQ(addr, empty_macaddr)) {
+               if_printf(&sc->sc_ic.ic_if, "could not get mac address\n");
+               return ENXIO;
+       }
+       return 0;
+}
+
+static uint8_t
+rtw_chan2txpower(struct rtw_srom *sr, struct ieee80211com *ic,
+                struct ieee80211_channel *chan)
+{
+       u_int idx = RTW_SR_TXPOWER1 + ieee80211_chan2ieee(ic, chan) - 1;
+
+       KASSERT(idx >= RTW_SR_TXPOWER1 && idx <= RTW_SR_TXPOWER14,
+               ("%s: channel %d out of range", __func__,
+                idx - RTW_SR_TXPOWER1 + 1));
+       return RTW_SR_GET(sr, idx);
+}
+
+static void
+rtw_txdesc_blk_init_all(struct rtw_softc *sc)
+{
+       /* nfree: the number of free descriptors in each ring.
+        * The beacon ring is a special case: I do not let the
+        * driver use all of the descriptors on the beacon ring.
+        * The reasons are two-fold:
+        *
+        * (1) A BEACON descriptor's OWN bit is (apparently) not
+        * updated, so the driver cannot easily know if the descriptor
+        * belongs to it, or if it is racing the NIC.  If the NIC
+        * does not OWN every descriptor, then the driver can safely
+        * update the descriptors when RTW_TBDA points at tdb_next.
+        *
+        * (2) I hope that the NIC will process more than one BEACON
+        * descriptor in a single beacon interval, since that will
+        * enable multiple-BSS support.  Since the NIC does not
+        * clear the OWN bit, there is no natural place for it to
+        * stop processing BEACON desciptors.  Maybe it will *not*
+        * stop processing them!  I do not want to chance the NIC
+        * looping around and around a saturated beacon ring, so
+        * I will leave one descriptor unOWNed at all times.
+        */
+       int nfree[RTW_NTXPRI] = {
+               RTW_NTXDESCLO,
+               RTW_NTXDESCMD,
+               RTW_NTXDESCHI,
+               RTW_NTXDESCBCN - 1
+       };
+       struct rtw_txdesc_blk *tdb;
+       int pri;
+
+       for (tdb = sc->sc_txdesc_blk, pri = 0; pri < RTW_NTXPRI; tdb++, pri++) {
+               tdb->tdb_nfree = nfree[pri];
+               tdb->tdb_next = 0;
+
+               bus_dmamap_sync(tdb->tdb_dmat, tdb->tdb_dmamap,
+                               BUS_DMASYNC_PREWRITE);
+       }
+}
+
+static void
+rtw_txsoft_blk_init_all(struct rtw_softc *sc)
+{
+       struct rtw_txsoft_blk *tsb;
+       int pri;
+
+       for (tsb = sc->sc_txsoft_blk, pri = 0; pri < RTW_NTXPRI; tsb++, pri++) {
+               int i;
+
+               STAILQ_INIT(&tsb->tsb_dirtyq);
+               STAILQ_INIT(&tsb->tsb_freeq);
+               for (i = 0; i < tsb->tsb_ndesc; i++) {
+                       struct rtw_txsoft *ts;
+
+                       ts = &tsb->tsb_desc[i];
+                       ts->ts_mbuf = NULL;
+                       STAILQ_INSERT_TAIL(&tsb->tsb_freeq, ts, ts_q);
+               }
+               tsb->tsb_tx_timer = 0;
+       }
+}
+
+static void
+rtw_rxbuf_dma_map(void *arg, bus_dma_segment_t *seg, int nseg,
+                 bus_size_t mapsize, int error)
+{
+       if (error)
+               return;
+
+       KASSERT(nseg == 1, ("too many rx mbuf seg\n"));
+
+       *((bus_addr_t *)arg) = seg->ds_addr;
+}
+
+static int
+rtw_rxsoft_alloc(struct rtw_softc *sc, struct rtw_rxsoft *rs, int waitok)
+{
+       bus_addr_t paddr;
+       bus_dmamap_t map;
+       struct mbuf *m;
+       int rc;
+
+       m = m_getcl(waitok ? MB_WAIT : MB_DONTWAIT, MT_DATA, M_PKTHDR);
+       if (m == NULL)
+               return ENOBUFS;
+
+       m->m_pkthdr.len = m->m_len = MCLBYTES;
+
+       rc = bus_dmamap_load_mbuf(sc->sc_rxsoft_dmat, sc->sc_rxsoft_dmamap, m,
+                                 rtw_rxbuf_dma_map, &paddr,
+                                 waitok ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
+       if (rc) {
+               if_printf(&sc->sc_ic.ic_if, "can't load rx mbuf\n");
+               m_freem(m);
+               return rc;
+       }
+
+       if (rs->rs_mbuf != NULL)
+               bus_dmamap_unload(sc->sc_rxsoft_dmat, rs->rs_dmamap);
+
+       /* Swap DMA map */
+       map = rs->rs_dmamap;
+       rs->rs_dmamap = sc->sc_rxsoft_dmamap;
+       sc->sc_rxsoft_dmamap = map;
+
+       rs->rs_mbuf = m;
+       rs->rs_phyaddr = paddr;
+
+       bus_dmamap_sync(sc->sc_rxsoft_dmat, rs->rs_dmamap, BUS_DMASYNC_PREREAD);
+       return 0;
+}
+
+static int
+rtw_rxsoft_blk_init_all(struct rtw_softc *sc)
+{
+       int i, rc = 0;
+
+       for (i = 0; i < RTW_RXQLEN; i++) {
+               struct rtw_rxsoft *rs;
+
+               rs = &sc->sc_rxsoft[i];
+               /* we're in rtw_init, so there should be no mbufs allocated */
+               KKASSERT(rs->rs_mbuf == NULL);
+#ifdef RTW_DEBUG
+               if (i == rtw_rxbufs_limit) {
+                       if_printf(&sc->sc_ic.ic_if,
+                                 "TEST hit %d-buffer limit\n", i);
+                       rc = ENOBUFS;
+                       break;
+               }
+#endif /* RTW_DEBUG */
+               rc = rtw_rxsoft_alloc(sc, rs, 1);
+               if (rc)
+                       break;
+       }
+       return rc;
+}
+
+static void
+rtw_rxdesc_init(struct rtw_softc *sc, int idx, int kick)
+{
+       struct rtw_rxdesc_blk *rdb = &sc->sc_rxdesc_blk;
+       struct rtw_rxdesc *rd = &rdb->rdb_desc[idx];
+       struct rtw_rxsoft *rs = &sc->sc_rxsoft[idx];
+       uint32_t ctl;
+
+#ifdef RTW_DEBUG
+       uint32_t octl, obuf;
+
+       obuf = rd->rd_buf;
+       octl = rd->rd_ctl;
+#endif /* RTW_DEBUG */
+
+       rd->rd_buf = htole32(rs->rs_phyaddr);
+
+       ctl = SHIFTIN(rs->rs_mbuf->m_len, RTW_RXCTL_LENGTH_MASK) |
+             RTW_RXCTL_OWN | RTW_RXCTL_FS | RTW_RXCTL_LS;
+
+       if (idx == rdb->rdb_ndesc - 1)
+               ctl |= RTW_RXCTL_EOR;
+
+       rd->rd_ctl = htole32(ctl);
+
+       RTW_DPRINTF(kick ? (RTW_DEBUG_RECV_DESC | RTW_DEBUG_IO_KICK)
+                        : RTW_DEBUG_RECV_DESC,
+                   ("%s: rd %p buf %08x -> %08x ctl %08x -> %08x\n",
+                    sc->sc_ic.ic_if.if_xname, rd, le32toh(obuf),
+                    le32toh(rd->rd_buf), le32toh(octl), le32toh(rd->rd_ctl)));
+}
+
+static void
+rtw_rxdesc_blk_init_all(struct rtw_softc *sc)
+{
+       struct rtw_rxdesc_blk *rdb = &sc->sc_rxdesc_blk;
+       int i;
+
+       for (i = 0; i < rdb->rdb_ndesc; i++)
+               rtw_rxdesc_init(sc, i, 1);
+
+       bus_dmamap_sync(rdb->rdb_dmat, rdb->rdb_dmamap, BUS_DMASYNC_PREWRITE);
+}
+
+static void
+rtw_io_enable(struct rtw_softc *sc, uint8_t flags, int enable)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+       uint8_t cr;
+
+       RTW_DPRINTF(RTW_DEBUG_IOSTATE,
+                   ("%s: %s 0x%02x\n", sc->sc_ic.ic_if.if_xname,
+                    enable ? "enable" : "disable", flags));
+
+       cr = RTW_READ8(regs, RTW_CR);
+
+       /* XXX reference source does not enable MULRW */
+#if 0
+       /* enable PCI Read/Write Multiple */
+       cr |= RTW_CR_MULRW;
+#endif
+
+       RTW_RBW(regs, RTW_CR, RTW_CR);  /* XXX paranoia? */
+       if (enable)
+               cr |= flags;
+       else
+               cr &= ~flags;
+       RTW_WRITE8(regs, RTW_CR, cr);
+       RTW_SYNC(regs, RTW_CR, RTW_CR);
+}
+
+static void
+rtw_intr_rx(struct rtw_softc *sc, uint16_t isr)
+{
+#define        IS_BEACON(__fc0)                                                \
+    ((__fc0 & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==\
+     (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON))
+
+       /*
+        * convert rates:
+        * hardware -> net80211
+        */
+       static const int ratetbl[4] = { 2, 4, 11, 22 };
+       struct ifnet *ifp = &sc->sc_if;
+       struct rtw_rxdesc_blk *rdb = &sc->sc_rxdesc_blk;
+       int next, nproc = 0, sync = 0;
+
+       KKASSERT(rdb->rdb_next < rdb->rdb_ndesc);
+
+       bus_dmamap_sync(rdb->rdb_dmat, rdb->rdb_dmamap, BUS_DMASYNC_POSTREAD);
+
+       for (next = rdb->rdb_next; ; next = (next + 1) % rdb->rdb_ndesc) {
+               struct ieee80211_node *ni;
+               struct ieee80211_frame_min *wh;
+               struct rtw_rxdesc *rd;
+               struct rtw_rxsoft *rs;
+               struct mbuf *m;
+               int hwrate, len, rate, rssi, sq, error;
+               uint32_t hrssi, hstat, htsfth, htsftl;
+
+               rd = &rdb->rdb_desc[next];
+               rs = &sc->sc_rxsoft[next];
+
+               hstat = le32toh(rd->rd_stat);
+               hrssi = le32toh(rd->rd_rssi);
+               htsfth = le32toh(rd->rd_tsfth);
+               htsftl = le32toh(rd->rd_tsftl);
+
+               RTW_DPRINTF(RTW_DEBUG_RECV_DESC,
+                           ("%s: rxdesc[%d] hstat %08x hrssi %08x "
+                            "htsft %08x%08x\n", ifp->if_xname,
+                            next, hstat, hrssi, htsfth, htsftl));
+
+               ++nproc;
+
+               /* still belongs to NIC */
+               if (hstat & RTW_RXSTAT_OWN) {
+                       if (nproc > 1)
+                               break;
+
+                       /* sometimes the NIC skips to the 0th descriptor */
+                       rd = &rdb->rdb_desc[0];
+                       if (rd->rd_stat & htole32(RTW_RXSTAT_OWN))
+                               break;
+                       RTW_DPRINTF(RTW_DEBUG_BUGS,
+                                   ("%s: NIC skipped from rxdesc[%u] "
+                                    "to rxdesc[0]\n", ifp->if_xname, next));
+                       next = rdb->rdb_ndesc - 1;
+                       continue;
+               }
+
+#ifdef RTW_DEBUG
+#define PRINTSTAT(flag) do { \
+       if ((hstat & flag) != 0) { \
+               printf("%s" #flag, delim); \
+               delim = ","; \
+       } \
+} while (0)
+               if (rtw_debug & RTW_DEBUG_RECV_DESC) {
+                       const char *delim = "<";
+
+                       if_printf(ifp, "%s", "");
+                       if ((hstat & RTW_RXSTAT_DEBUG) != 0) {
+                               printf("status %08x", hstat);
+                               PRINTSTAT(RTW_RXSTAT_SPLCP);
+                               PRINTSTAT(RTW_RXSTAT_MAR);
+                               PRINTSTAT(RTW_RXSTAT_PAR);
+                               PRINTSTAT(RTW_RXSTAT_BAR);
+                               PRINTSTAT(RTW_RXSTAT_PWRMGT);
+                               PRINTSTAT(RTW_RXSTAT_CRC32);
+                               PRINTSTAT(RTW_RXSTAT_ICV);
+                               printf(">, ");
+                       }
+               }
+#endif /* RTW_DEBUG */
+
+               if (hstat & RTW_RXSTAT_IOERROR) {
+                       if_printf(ifp, "DMA error/FIFO overflow %08x, "
+                                 "rx descriptor %d\n",
+                                 hstat & RTW_RXSTAT_IOERROR, next);
+                       ifp->if_ierrors++;
+                       goto next;
+               }
+
+               len = SHIFTOUT(hstat, RTW_RXSTAT_LENGTH_MASK);
+               if (len < IEEE80211_MIN_LEN) {
+                       sc->sc_ic.ic_stats.is_rx_tooshort++;
+                       goto next;
+               }
+
+               /* CRC is included with the packet; trim it off. */
+               len -= IEEE80211_CRC_LEN;
+
+               hwrate = SHIFTOUT(hstat, RTW_RXSTAT_RATE_MASK);
+               if (hwrate >= sizeof(ratetbl) / sizeof(ratetbl[0])) {
+                       if_printf(ifp, "unknown rate #%d\n",
+                                 SHIFTOUT(hstat, RTW_RXSTAT_RATE_MASK));
+                       ifp->if_ierrors++;
+                       goto next;
+               }
+               rate = ratetbl[hwrate];
+
+#ifdef RTW_DEBUG
+               RTW_DPRINTF(RTW_DEBUG_RECV_DESC,
+                           ("%s rate %d.%d Mb/s, time %08x%08x\n",
+                            ifp->if_xname, (rate * 5) / 10,
+                            (rate * 5) % 10, htsfth, htsftl));
+#endif /* RTW_DEBUG */
+
+               if ((hstat & RTW_RXSTAT_RES) &&
+                   sc->sc_ic.ic_opmode != IEEE80211_M_MONITOR)
+                       goto next;
+
+               /* if bad flags, skip descriptor */
+               if ((hstat & RTW_RXSTAT_ONESEG) != RTW_RXSTAT_ONESEG) {
+                       if_printf(ifp, "too many rx segments\n");
+                       goto next;
+               }
+
+               bus_dmamap_sync(sc->sc_rxsoft_dmat, rs->rs_dmamap,
+                               BUS_DMASYNC_POSTREAD);
+
+               m = rs->rs_mbuf;
+
+               /* if temporarily out of memory, re-use mbuf */
+               error = rtw_rxsoft_alloc(sc, rs, 0);
+               if (error) {
+                       if_printf(ifp, "%s: rtw_rxsoft_alloc(, %d) failed, "
+                           "dropping packet\n", ifp->if_xname, next);
+                       goto next;
+               }
+
+               if (sc->sc_rfchipid == RTW_RFCHIPID_PHILIPS) {
+                       rssi = SHIFTOUT(hrssi, RTW_RXRSSI_RSSI);
+               } else {
+                       rssi = SHIFTOUT(hrssi, RTW_RXRSSI_IMR_RSSI);
+                       /* TBD find out each front-end's LNA gain in the
+                        * front-end's units
+                        */
+                       if ((hrssi & RTW_RXRSSI_IMR_LNA) == 0)
+                               rssi |= 0x80;
+               }
+               sq = SHIFTOUT(hrssi, RTW_RXRSSI_SQ);
+
+               /*
+                * Note well: now we cannot recycle the rs_mbuf unless
+                * we restore its original length.
+                */
+               m->m_pkthdr.rcvif = ifp;
+               m->m_pkthdr.len = m->m_len = len;
+
+               wh = mtod(m, struct ieee80211_frame_min *);
+
+               if (!IS_BEACON(wh->i_fc[0]))
+                       sc->sc_led_state.ls_event |= RTW_LED_S_RX;
+
+               /* TBD use _MAR, _BAR, _PAR flags as hints to _find_rxnode? */
+               ni = ieee80211_find_rxnode(&sc->sc_ic, wh);
+
+               sc->sc_tsfth = htsfth;
+
+#ifdef RTW_DEBUG
+               if ((ifp->if_flags & (IFF_DEBUG | IFF_LINK2)) ==
+                   (IFF_DEBUG | IFF_LINK2)) {
+                       ieee80211_dump_pkt(mtod(m, uint8_t *), m->m_pkthdr.len,
+                                          rate, rssi);
+               }
+#endif /* RTW_DEBUG */
+
+               if (sc->sc_radiobpf != NULL) {
+                       struct rtw_rx_radiotap_header *rr = &sc->sc_rxtap;
+
+                       rr->rr_tsft =
+                           htole64(((uint64_t)htsfth << 32) | htsftl);
+
+                       if ((hstat & RTW_RXSTAT_SPLCP) != 0)
+                               rr->rr_flags = IEEE80211_RADIOTAP_F_SHORTPRE;
+
+                       rr->rr_flags = 0;
+                       rr->rr_rate = rate;
+                       rr->rr_antsignal = rssi;
+                       rr->rr_barker_lock = htole16(sq);
+
+                       bpf_ptap(sc->sc_radiobpf, m, rr, sizeof(sc->sc_rxtapu));
+               }
+
+               ieee80211_input(&sc->sc_ic, m, ni, rssi, htsftl);
+               ieee80211_free_node(ni);
+next:
+               rtw_rxdesc_init(sc, next, 0);
+               sync = 1;
+       }
+
+       if (sync) {
+               bus_dmamap_sync(rdb->rdb_dmat, rdb->rdb_dmamap,
+                               BUS_DMASYNC_PREWRITE);
+       }
+
+       rdb->rdb_next = next;
+       KKASSERT(rdb->rdb_next < rdb->rdb_ndesc);
+#undef IS_BEACON
+}
+
+static __inline void
+rtw_txsoft_release(bus_dma_tag_t dmat, struct rtw_txsoft *ts,
+                  int data_retry, int rts_retry, int error, int ratectl)
+{
+       struct mbuf *m;
+       struct ieee80211_node *ni;
+
+       if (!ts->ts_ratectl)
+               ratectl = 0;
+
+       m = ts->ts_mbuf;
+       ni = ts->ts_ni;
+       KKASSERT(m != NULL);
+       KKASSERT(ni != NULL);
+       ts->ts_mbuf = NULL;
+       ts->ts_ni = NULL;
+
+       if (ratectl) {
+               struct ieee80211_ratectl_res rc_res;
+
+               rc_res.rc_res_rateidx = ts->ts_rateidx;
+               rc_res.rc_res_tries = data_retry + rts_retry + 1;
+
+               ieee80211_ratectl_tx_complete(ni, m->m_pkthdr.len,
+                                             &rc_res, 1,
+                                             data_retry, rts_retry,
+                                             error);
+       }
+
+       bus_dmamap_sync(dmat, ts->ts_dmamap, BUS_DMASYNC_POSTWRITE);
+       bus_dmamap_unload(dmat, ts->ts_dmamap);
+       m_freem(m);
+       ieee80211_free_node(ni);
+}
+
+static __inline void
+rtw_collect_txpkt(struct rtw_softc *sc, struct rtw_txdesc_blk *tdb,
+                 struct rtw_txsoft *ts, int ndesc)
+{
+       uint32_t hstat;
+       int data_retry, rts_retry, error;
+       struct rtw_txdesc *tdn;
+       const char *condstring;
+       struct ifnet *ifp = &sc->sc_if;
+
+       tdb->tdb_nfree += ndesc;
+
+       tdn = &tdb->tdb_desc[ts->ts_last];
+
+       hstat = le32toh(tdn->td_stat);
+       rts_retry = SHIFTOUT(hstat, RTW_TXSTAT_RTSRETRY_MASK);
+       data_retry = SHIFTOUT(hstat, RTW_TXSTAT_DRC_MASK);
+
+       ifp->if_collisions += rts_retry + data_retry;
+
+       if ((hstat & RTW_TXSTAT_TOK) != 0) {
+               condstring = "ok";
+               error = 0;
+       } else {
+               ifp->if_oerrors++;
+               condstring = "error";
+               error = 1;
+       }
+
+       rtw_txsoft_release(sc->sc_txsoft_dmat, ts, data_retry, rts_retry,
+                          error, 1);
+
+       DPRINTF(sc, RTW_DEBUG_XMIT_DESC,
+               ("%s: ts %p txdesc[%d, %d] %s tries rts %u data %u\n",
+                ifp->if_xname, ts, ts->ts_first, ts->ts_last,
+                condstring, rts_retry, data_retry));
+}
+
+static void
+rtw_reset_oactive(struct rtw_softc *sc)
+{
+       int pri;
+#ifdef RTW_DEBUG
+       short oflags = sc->sc_if.if_flags;
+#endif
+
+       for (pri = 0; pri < RTW_NTXPRI; pri++) {
+               struct rtw_txsoft_blk *tsb = &sc->sc_txsoft_blk[pri];
+               struct rtw_txdesc_blk *tdb = &sc->sc_txdesc_blk[pri];
+
+               if (!STAILQ_EMPTY(&tsb->tsb_freeq) && tdb->tdb_nfree > 0)
+                       sc->sc_if.if_flags &= ~IFF_OACTIVE;
+       }
+
+#ifdef RTW_DEBUG
+       if (oflags != sc->sc_if.if_flags) {
+               DPRINTF(sc, RTW_DEBUG_OACTIVE,
+                       ("%s: reset OACTIVE\n", sc->sc_ic.ic_if.if_xname));
+       }
+#endif
+}
+
+/* Collect transmitted packets. */
+static __inline void
+rtw_collect_txring(struct rtw_softc *sc, struct rtw_txsoft_blk *tsb,
+                  struct rtw_txdesc_blk *tdb, int force)
+{
+       struct rtw_txsoft *ts;
+       int ndesc;
+
+       while ((ts = STAILQ_FIRST(&tsb->tsb_dirtyq)) != NULL) {
+               ndesc = 1 + ts->ts_last - ts->ts_first;
+               if (ts->ts_last < ts->ts_first)
+                       ndesc += tdb->tdb_ndesc;
+
+               KKASSERT(ndesc > 0);
+
+               bus_dmamap_sync(tdb->tdb_dmat, tdb->tdb_dmamap,
+                               BUS_DMASYNC_POSTREAD);
+
+               if (force) {
+                       int i;
+
+                       for (i = ts->ts_first; ; i = RTW_NEXT_IDX(tdb, i)) {
+                               tdb->tdb_desc[i].td_stat &=
+                                       ~htole32(RTW_TXSTAT_OWN);
+                               if (i == ts->ts_last)
+                                       break;
+                       }
+                       bus_dmamap_sync(tdb->tdb_dmat, tdb->tdb_dmamap,
+                                       BUS_DMASYNC_PREWRITE);
+               } else if ((tdb->tdb_desc[ts->ts_last].td_stat &
+                           htole32(RTW_TXSTAT_OWN)) != 0) {
+                       break;
+               }
+
+               rtw_collect_txpkt(sc, tdb, ts, ndesc);
+               STAILQ_REMOVE_HEAD(&tsb->tsb_dirtyq, ts_q);
+               STAILQ_INSERT_TAIL(&tsb->tsb_freeq, ts, ts_q);
+       }
+       /* no more pending transmissions, cancel watchdog */ 
+       if (ts == NULL)
+               tsb->tsb_tx_timer = 0;
+       rtw_reset_oactive(sc);
+}
+
+static void
+rtw_intr_tx(struct rtw_softc *sc, uint16_t isr)
+{
+       int pri;
+
+       for (pri = 0; pri < RTW_NTXPRI; pri++) {
+               rtw_collect_txring(sc, &sc->sc_txsoft_blk[pri],
+                                  &sc->sc_txdesc_blk[pri], 0);
+       }
+       if (isr)
+               rtw_start(&sc->sc_ic.ic_if);
+}
+
+static __inline struct mbuf *
+rtw_beacon_alloc(struct rtw_softc *sc, struct ieee80211_node *ni)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ieee80211_beacon_offsets boff;
+       struct mbuf *m;
+
+       m = ieee80211_beacon_alloc(ic, ni, &boff);
+       if (m != NULL) {
+               RTW_DPRINTF(RTW_DEBUG_BEACON,
+                           ("%s: m %p len %u\n", ic->ic_if.if_xname, m,
+                            m->m_len));
+       }
+       return m;
+}
+
+static void
+rtw_intr_beacon(struct rtw_softc *sc, uint16_t isr)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct rtw_regs *regs = &sc->sc_regs;
+       struct rtw_txdesc_blk *tdb = &sc->sc_txdesc_blk[RTW_TXPRIBCN];
+       struct rtw_txsoft_blk *tsb = &sc->sc_txsoft_blk[RTW_TXPRIBCN];
+
+#ifdef RTW_DEBUG
+       uint32_t tsfth, tsftl;
+
+       tsfth = RTW_READ(regs, RTW_TSFTRH);
+       tsftl = RTW_READ(regs, RTW_TSFTRL);
+#endif
+
+       if (isr & (RTW_INTR_TBDOK | RTW_INTR_TBDER)) {
+#ifdef RTW_DEBUG
+               int next = rtw_txring_next(regs, tdb);
+#endif
+
+               RTW_DPRINTF(RTW_DEBUG_BEACON,
+                           ("%s: beacon ring %sprocessed, "
+                            "isr = %#04x, next %d expected %d, %llu\n",
+                            ic->ic_if.if_xname,
+                            (next == tdb->tdb_next) ? "" : "un",
+                            isr, next, tdb->tdb_next,
+                            (uint64_t)tsfth << 32 | tsftl));
+
+               if ((RTW_READ8(regs, RTW_TPPOLL) & RTW_TPPOLL_BQ) == 0){
+                       rtw_collect_txring(sc, tsb, tdb, 1);
+                       tdb->tdb_next = 0;
+               }
+       }
+       /* Start beacon transmission. */
+
+       if ((isr & RTW_INTR_BCNINT) && ic->ic_state == IEEE80211_S_RUN &&
+           STAILQ_EMPTY(&tsb->tsb_dirtyq)) {
+               struct mbuf *m;
+
+               RTW_DPRINTF(RTW_DEBUG_BEACON,
+                           ("%s: beacon prep. time, isr = %#04x, %llu\n",
+                            ic->ic_if.if_xname, isr,
+                            (uint64_t)tsfth << 32 | tsftl));
+
+               m = rtw_beacon_alloc(sc, ic->ic_bss);
+               if (m == NULL) {
+                       if_printf(&ic->ic_if, "could not allocate beacon\n");
+                       return;
+               }
+
+               m->m_pkthdr.rcvif = (void *)ieee80211_ref_node(ic->ic_bss);
+
+               IF_ENQUEUE(&sc->sc_beaconq, m);
+
+               rtw_start(&ic->ic_if);
+       }
+}
+
+static void
+rtw_intr_atim(struct rtw_softc *sc)
+{
+       /* TBD */
+       return;
+}
+
+#ifdef RTW_DEBUG
+static void
+rtw_dump_rings(struct rtw_softc *sc)
+{
+       struct rtw_rxdesc_blk *rdb;
+       int desc, pri;
+
+       if ((rtw_debug & RTW_DEBUG_IO_KICK) == 0)
+               return;
+
+       for (pri = 0; pri < RTW_NTXPRI; pri++) {
+               struct rtw_txdesc_blk *tdb = &sc->sc_txdesc_blk[pri];
+
+               if_printf(&sc->sc_ic.ic_if, "txpri %d ndesc %d nfree %d\n",
+                         pri, tdb->tdb_ndesc, tdb->tdb_nfree);
+               for (desc = 0; desc < tdb->tdb_ndesc; desc++)
+                       rtw_print_txdesc(sc, ".", NULL, tdb, desc);
+       }
+
+       rdb = &sc->sc_rxdesc_blk;
+
+       for (desc = 0; desc < RTW_RXQLEN; desc++) {
+               struct rtw_rxdesc *rd = &rdb->rdb_desc[desc];
+
+               if_printf(&sc->sc_ic.ic_if,
+                         "%sctl %08x rsvd0/rssi %08x buf/tsftl %08x "
+                         "rsvd1/tsfth %08x\n",
+                         (desc >= rdb->rdb_ndesc) ? "UNUSED " : "",
+                         le32toh(rd->rd_ctl), le32toh(rd->rd_rssi),
+                         le32toh(rd->rd_buf), le32toh(rd->rd_tsfth));
+       }
+}
+#endif /* RTW_DEBUG */
+
+static void
+rtw_hwring_setup(struct rtw_softc *sc)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+       struct rtw_rxdesc_blk *rdb = &sc->sc_rxdesc_blk;
+       int pri;
+
+       for (pri = 0; pri < RTW_NTXPRI; pri++) {
+               struct rtw_txdesc_blk *tdb = &sc->sc_txdesc_blk[pri];
+
+               RTW_WRITE(regs, tdb->tdb_basereg, tdb->tdb_base);
+               RTW_DPRINTF(RTW_DEBUG_XMIT_DESC,
+                           ("%s: reg[tdb->tdb_basereg] <- %u\n",
+                            sc->sc_ic.ic_if.if_xname, tdb->tdb_base));
+       }
+
+       RTW_WRITE(regs, RTW_RDSAR, rdb->rdb_base);
+       RTW_DPRINTF(RTW_DEBUG_RECV_DESC,
+                   ("%s: reg[RDSAR] <- %u\n", sc->sc_ic.ic_if.if_xname,
+                    rdb->rdb_base));
+
+       RTW_SYNC(regs, RTW_TLPDA, RTW_RDSAR);
+}
+
+static int
+rtw_swring_setup(struct rtw_softc *sc)
+{
+       int rc;
+
+       rtw_txdesc_blk_init_all(sc);
+       rtw_txsoft_blk_init_all(sc);
+
+       rc = rtw_rxsoft_blk_init_all(sc);
+       if (rc) {
+               if_printf(&sc->sc_ic.ic_if, "could not allocate rx buffers\n");
+               return rc;
+       }
+
+       rtw_rxdesc_blk_init_all(sc);
+       sc->sc_rxdesc_blk.rdb_next = 0;
+       return 0;
+}
+
+static int
+rtw_txring_next(struct rtw_regs *regs, struct rtw_txdesc_blk *tdb)
+{
+       return (le32toh(RTW_READ(regs, tdb->tdb_basereg)) - tdb->tdb_base) /
+               sizeof(struct rtw_txdesc);
+}
+
+static void
+rtw_txring_fixup(struct rtw_softc *sc)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+       int pri;
+
+       for (pri = 0; pri < RTW_NTXPRI; pri++) {
+               struct rtw_txdesc_blk *tdb = &sc->sc_txdesc_blk[pri];
+               int next;
+
+               next = rtw_txring_next(regs, tdb);
+               if (tdb->tdb_next == next)
+                       continue;
+               if_printf(&sc->sc_ic.ic_if,
+                         "tx-ring %d expected next %d, read %d\n",
+                         pri, tdb->tdb_next, next);
+               tdb->tdb_next = MIN(next, tdb->tdb_ndesc - 1);
+       }
+}
+
+static void
+rtw_rxring_fixup(struct rtw_softc *sc)
+{
+       struct rtw_rxdesc_blk *rdb = &sc->sc_rxdesc_blk;
+       uint32_t rdsar;
+       int next;
+
+       rdsar = le32toh(RTW_READ(&sc->sc_regs, RTW_RDSAR));
+       next = (rdsar - rdb->rdb_base) / sizeof(struct rtw_rxdesc);
+
+       if (rdb->rdb_next != next) {
+               if_printf(&sc->sc_ic.ic_if,
+                         "rx-ring expected next %d, read %d\n",
+                         rdb->rdb_next, next);
+               rdb->rdb_next = MIN(next, rdb->rdb_ndesc - 1);
+       }
+}
+
+static void
+rtw_txdesc_blk_reset_all(struct rtw_softc *sc)
+{
+       int pri;
+
+       for (pri = 0; pri < RTW_NTXPRI; pri++) {
+               rtw_collect_txring(sc, &sc->sc_txsoft_blk[pri],
+                                  &sc->sc_txdesc_blk[pri], 1);
+       }
+}
+
+static void
+rtw_intr_ioerror(struct rtw_softc *sc, uint16_t isr)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+       int xmtr = 0, rcvr = 0;
+       uint8_t cr = 0;
+
+       if (isr & RTW_INTR_TXFOVW) {
+               if_printf(&sc->sc_ic.ic_if, "tx fifo underflow\n");
+               rcvr = xmtr = 1;
+               cr |= RTW_CR_TE | RTW_CR_RE;
+       }
+
+       if (isr & (RTW_INTR_RDU | RTW_INTR_RXFOVW)) {
+               cr |= RTW_CR_RE;
+               rcvr = 1;
+       }
+
+       RTW_DPRINTF(RTW_DEBUG_BUGS,
+                   ("%s: restarting xmit/recv, isr %04x\n",
+                    sc->sc_ic.ic_if.if_xname, isr));
+
+#ifdef RTW_DEBUG
+       rtw_dump_rings(sc);
+#endif /* RTW_DEBUG */
+
+       rtw_io_enable(sc, cr, 0);
+
+       /* Collect rx'd packets.  Refresh rx buffers. */
+       if (rcvr)
+               rtw_intr_rx(sc, 0);
+
+       /*
+        * Collect tx'd packets.
+        * XXX let's hope this stops the transmit timeouts.
+        */
+       if (xmtr)
+               rtw_txdesc_blk_reset_all(sc);
+
+       RTW_WRITE16(regs, RTW_IMR, 0);
+       RTW_SYNC(regs, RTW_IMR, RTW_IMR);
+
+       if (rtw_do_chip_reset) {
+               rtw_chip_reset1(sc);
+               rtw_wep_setkeys(sc);
+       }
+
+       rtw_rxdesc_blk_init_all(sc);
+
+#ifdef RTW_DEBUG
+       rtw_dump_rings(sc);
+#endif /* RTW_DEBUG */
+
+       RTW_WRITE16(regs, RTW_IMR, sc->sc_inten);
+       RTW_SYNC(regs, RTW_IMR, RTW_IMR);
+
+       if (rcvr)
+               rtw_rxring_fixup(sc);
+
+       rtw_io_enable(sc, cr, 1);
+
+       if (xmtr)
+               rtw_txring_fixup(sc);
+}
+
+static __inline void
+rtw_suspend_ticks(struct rtw_softc *sc)
+{
+       RTW_DPRINTF(RTW_DEBUG_TIMEOUT,
+                   ("%s: suspending ticks\n", sc->sc_ic.ic_if.if_xname));
+       sc->sc_do_tick = 0;
+}
+
+static void
+rtw_resume_ticks(struct rtw_softc *sc)
+{
+       uint32_t tsftrl0, tsftrl1, next_tick;
+       struct rtw_regs *regs = &sc->sc_regs;
+
+       tsftrl0 = RTW_READ(regs, RTW_TSFTRL);
+
+       tsftrl1 = RTW_READ(regs, RTW_TSFTRL);
+       next_tick = tsftrl1 + 1000000;
+       RTW_WRITE(regs, RTW_TINT, next_tick);
+
+       sc->sc_do_tick = 1;
+
+       RTW_DPRINTF(RTW_DEBUG_TIMEOUT,
+                   ("%s: resume ticks delta %#08x now %#08x next %#08x\n",
+                    sc->sc_ic.ic_if.if_xname, tsftrl1 - tsftrl0, tsftrl1,
+                    next_tick));
+}
+
+static void
+rtw_intr_timeout(struct rtw_softc *sc)
+{
+       RTW_DPRINTF(RTW_DEBUG_TIMEOUT,
+                   ("%s: timeout\n", sc->sc_ic.ic_if.if_xname));
+       if (sc->sc_do_tick)
+               rtw_resume_ticks(sc);
+}
+
+static void
+rtw_intr(void *arg)
+{
+       struct rtw_softc *sc = arg;
+       struct rtw_regs *regs = &sc->sc_regs;
+       struct ifnet *ifp = &sc->sc_if;
+       int i;
+
+       /*
+        * If the interface isn't running, the interrupt couldn't
+        * possibly have come from us.
+        */
+       if ((sc->sc_flags & RTW_F_ENABLED) == 0 ||
+           (ifp->if_flags & IFF_RUNNING) == 0) {
+               RTW_DPRINTF(RTW_DEBUG_INTR,
+                           ("%s: stray interrupt\n", ifp->if_xname));
+               return;
+       }
+
+       for (i = 0; i < 10; i++) {
+               uint16_t isr;
+
+               isr = RTW_READ16(regs, RTW_ISR);
+
+               RTW_WRITE16(regs, RTW_ISR, isr);
+               RTW_WBR(regs, RTW_ISR, RTW_ISR);
+
+               if (sc->sc_intr_ack != NULL)
+                       sc->sc_intr_ack(regs);
+
+               if (isr == 0)
+                       break;
+
+#ifdef RTW_DEBUG
+#define PRINTINTR(flag) do { \
+       if ((isr & flag) != 0) { \
+               printf("%s" #flag, delim); \
+               delim = ","; \
+       } \
+} while (0)
+
+               if ((rtw_debug & RTW_DEBUG_INTR) != 0 && isr != 0) {
+                       const char *delim = "<";
+
+                       if_printf(ifp, "reg[ISR] = %x", isr);
+
+                       PRINTINTR(RTW_INTR_TXFOVW);
+                       PRINTINTR(RTW_INTR_TIMEOUT);
+                       PRINTINTR(RTW_INTR_BCNINT);
+                       PRINTINTR(RTW_INTR_ATIMINT);
+                       PRINTINTR(RTW_INTR_TBDER);
+                       PRINTINTR(RTW_INTR_TBDOK);
+                       PRINTINTR(RTW_INTR_THPDER);
+                       PRINTINTR(RTW_INTR_THPDOK);
+                       PRINTINTR(RTW_INTR_TNPDER);
+                       PRINTINTR(RTW_INTR_TNPDOK);
+                       PRINTINTR(RTW_INTR_RXFOVW);
+                       PRINTINTR(RTW_INTR_RDU);
+                       PRINTINTR(RTW_INTR_TLPDER);
+                       PRINTINTR(RTW_INTR_TLPDOK);
+                       PRINTINTR(RTW_INTR_RER);
+                       PRINTINTR(RTW_INTR_ROK);
+
+                       printf(">\n");
+               }
+#undef PRINTINTR
+#endif /* RTW_DEBUG */
+
+               if (isr & RTW_INTR_RX)
+                       rtw_intr_rx(sc, isr & RTW_INTR_RX);
+               if (isr & RTW_INTR_TX)
+                       rtw_intr_tx(sc, isr & RTW_INTR_TX);
+               if (isr & RTW_INTR_BEACON)
+                       rtw_intr_beacon(sc, isr & RTW_INTR_BEACON);
+               if (isr & RTW_INTR_ATIMINT)
+                       rtw_intr_atim(sc);
+               if (isr & RTW_INTR_IOERROR)
+                       rtw_intr_ioerror(sc, isr & RTW_INTR_IOERROR);
+               if (isr & RTW_INTR_TIMEOUT)
+                       rtw_intr_timeout(sc);
+       }
+}
+
+/* Must be called at splnet. */
+void
+rtw_stop(struct rtw_softc *sc, int disable)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+       struct rtw_regs *regs = &sc->sc_regs;
+       int i;
+
+       if ((sc->sc_flags & RTW_F_ENABLED) == 0)
+               return;
+
+       rtw_suspend_ticks(sc);
+
+       ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+
+       if ((sc->sc_flags & RTW_F_INVALID) == 0) {
+               /* Disable interrupts. */
+               RTW_WRITE16(regs, RTW_IMR, 0);
+
+               RTW_WBW(regs, RTW_TPPOLL, RTW_IMR);
+
+               /*
+                * Stop the transmit and receive processes. First stop DMA,
+                * then disable receiver and transmitter.
+                */
+               RTW_WRITE8(regs, RTW_TPPOLL, RTW_TPPOLL_SALL);
+
+               RTW_SYNC(regs, RTW_TPPOLL, RTW_IMR);
+
+               rtw_io_enable(sc, RTW_CR_RE | RTW_CR_TE, 0);
+       }
+
+       /* Free pending TX mbufs */
+       for (i = 0; i < RTW_NTXPRI; ++i) {
+               struct rtw_txsoft_blk *tsb = &sc->sc_txsoft_blk[i];
+               struct rtw_txsoft *ts;
+
+               while ((ts = STAILQ_FIRST(&tsb->tsb_dirtyq)) != NULL) {
+                       rtw_txsoft_release(sc->sc_txsoft_dmat, ts, 0, 0, 0, 0);
+                       STAILQ_REMOVE_HEAD(&tsb->tsb_dirtyq, ts_q);
+                       STAILQ_INSERT_TAIL(&tsb->tsb_freeq, ts, ts_q);
+               }
+               tsb->tsb_tx_timer = 0;
+       }
+
+       /* Free pending RX mbufs */
+       for (i = 0; i < RTW_RXQLEN; i++) {
+               struct rtw_rxsoft *rs = &sc->sc_rxsoft[i];
+
+               if (rs->rs_mbuf != NULL) {
+                       bus_dmamap_sync(sc->sc_rxsoft_dmat, rs->rs_dmamap,
+                                       BUS_DMASYNC_POSTREAD);
+                       bus_dmamap_unload(sc->sc_rxsoft_dmat, rs->rs_dmamap);
+                       m_freem(rs->rs_mbuf);
+                       rs->rs_mbuf = NULL;
+               }
+       }
+
+       if (disable)
+               rtw_disable(sc);
+
+       /* Mark the interface as not running.  Cancel the watchdog timer. */
+       ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+       ifp->if_timer = 0;
+}
+
+#ifdef RTW_DEBUG
+const char *
+rtw_pwrstate_string(enum rtw_pwrstate power)
+{
+       switch (power) {
+       case RTW_ON:
+               return "on";
+       case RTW_SLEEP:
+               return "sleep";
+       case RTW_OFF:
+               return "off";
+       default:
+               return "unknown";
+       }
+}
+#endif /* RTW_DEBUG */
+
+/*
+ * XXX For Maxim, I am using the RFMD settings gleaned from the
+ * reference driver, plus a magic Maxim "ON" value that comes from
+ * the Realtek document "Windows PG for Rtl8180."
+ */
+static void
+rtw_maxim_pwrstate(struct rtw_regs *regs, enum rtw_pwrstate power,
+                  int before_rf, int digphy)
+{
+       uint32_t anaparm;
+
+       anaparm = RTW_READ(regs, RTW_ANAPARM);
+       anaparm &= ~(RTW_ANAPARM_RFPOW_MASK | RTW_ANAPARM_TXDACOFF);
+
+       switch (power) {
+       case RTW_OFF:
+               if (before_rf)
+                       return;
+               anaparm |= RTW_ANAPARM_RFPOW_MAXIM_OFF;
+               anaparm |= RTW_ANAPARM_TXDACOFF;
+               break;
+       case RTW_SLEEP:
+               if (!before_rf)
+                       return;
+               anaparm |= RTW_ANAPARM_RFPOW_MAXIM_SLEEP;
+               anaparm |= RTW_ANAPARM_TXDACOFF;
+               break;
+       case RTW_ON:
+               if (!before_rf)
+                       return;
+               anaparm |= RTW_ANAPARM_RFPOW_MAXIM_ON;
+               break;
+       }
+       RTW_DPRINTF(RTW_DEBUG_PWR,
+           ("%s: power state %s, %s RF, reg[ANAPARM] <- %08x\n",
+           __func__, rtw_pwrstate_string(power),
+           (before_rf) ? "before" : "after", anaparm));
+
+       RTW_WRITE(regs, RTW_ANAPARM, anaparm);
+       RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM);
+}
+
+/* XXX I am using the RFMD settings gleaned from the reference
+ * driver.  They agree
+ */
+static void
+rtw_rfmd_pwrstate(struct rtw_regs *regs, enum rtw_pwrstate power,
+                 int before_rf, int digphy)
+{
+       uint32_t anaparm;
+
+       anaparm = RTW_READ(regs, RTW_ANAPARM);
+       anaparm &= ~(RTW_ANAPARM_RFPOW_MASK | RTW_ANAPARM_TXDACOFF);
+
+       switch (power) {
+       case RTW_OFF:
+               if (before_rf)
+                       return;
+               anaparm |= RTW_ANAPARM_RFPOW_RFMD_OFF;
+               anaparm |= RTW_ANAPARM_TXDACOFF;
+               break;
+       case RTW_SLEEP:
+               if (!before_rf)
+                       return;
+               anaparm |= RTW_ANAPARM_RFPOW_RFMD_SLEEP;
+               anaparm |= RTW_ANAPARM_TXDACOFF;
+               break;
+       case RTW_ON:
+               if (!before_rf)
+                       return;
+               anaparm |= RTW_ANAPARM_RFPOW_RFMD_ON;
+               break;
+       }
+       RTW_DPRINTF(RTW_DEBUG_PWR,
+           ("%s: power state %s, %s RF, reg[ANAPARM] <- %08x\n",
+           __func__, rtw_pwrstate_string(power),
+           (before_rf) ? "before" : "after", anaparm));
+
+       RTW_WRITE(regs, RTW_ANAPARM, anaparm);
+       RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM);
+}
+
+static void
+rtw_philips_pwrstate(struct rtw_regs *regs, enum rtw_pwrstate power,
+                    int before_rf, int digphy)
+{
+       uint32_t anaparm;
+
+       anaparm = RTW_READ(regs, RTW_ANAPARM);
+       anaparm &= ~(RTW_ANAPARM_RFPOW_MASK | RTW_ANAPARM_TXDACOFF);
+
+       switch (power) {
+       case RTW_OFF:
+               if (before_rf)
+                       return;
+               anaparm |= RTW_ANAPARM_RFPOW_PHILIPS_OFF;
+               anaparm |= RTW_ANAPARM_TXDACOFF;
+               break;
+       case RTW_SLEEP:
+               if (!before_rf)
+                       return;
+               anaparm |= RTW_ANAPARM_RFPOW_PHILIPS_SLEEP;
+               anaparm |= RTW_ANAPARM_TXDACOFF;
+               break;
+       case RTW_ON:
+               if (!before_rf)
+                       return;
+               if (digphy) {
+                       anaparm |= RTW_ANAPARM_RFPOW_DIG_PHILIPS_ON;
+                       /* XXX guess */
+                       anaparm |= RTW_ANAPARM_TXDACOFF;
+               } else
+                       anaparm |= RTW_ANAPARM_RFPOW_ANA_PHILIPS_ON;
+               break;
+       }
+       RTW_DPRINTF(RTW_DEBUG_PWR,
+           ("%s: power state %s, %s RF, reg[ANAPARM] <- %08x\n",
+           __func__, rtw_pwrstate_string(power),
+           (before_rf) ? "before" : "after", anaparm));
+
+       RTW_WRITE(regs, RTW_ANAPARM, anaparm);
+       RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM);
+}
+
+static __inline void
+rtw_pwrstate0(struct rtw_softc *sc, enum rtw_pwrstate power, int before_rf,
+             int digphy)
+{
+       rtw_set_access(sc, RTW_ACCESS_ANAPARM);
+       sc->sc_pwrstate_cb(&sc->sc_regs, power, before_rf, digphy);
+       rtw_set_access(sc, RTW_ACCESS_NONE);
+}
+
+static int
+rtw_pwrstate(struct rtw_softc *sc, enum rtw_pwrstate power)
+{
+       int rc;
+
+       RTW_DPRINTF(RTW_DEBUG_PWR,
+                   ("%s: %s->%s\n", sc->sc_ic.ic_if.if_xname,
+                   rtw_pwrstate_string(sc->sc_pwrstate),
+                   rtw_pwrstate_string(power)));
+
+       if (sc->sc_pwrstate == power)
+               return 0;
+
+       rtw_pwrstate0(sc, power, 1, sc->sc_flags & RTW_F_DIGPHY);
+       rc = rtw_rf_pwrstate(sc->sc_rf, power);
+       rtw_pwrstate0(sc, power, 0, sc->sc_flags & RTW_F_DIGPHY);
+
+       switch (power) {
+       case RTW_ON:
+               /* TBD set LEDs */
+               break;
+       case RTW_SLEEP:
+               /* TBD */
+               break;
+       case RTW_OFF:
+               /* TBD */
+               break;
+       }
+       if (rc == 0)
+               sc->sc_pwrstate = power;
+       else
+               sc->sc_pwrstate = RTW_OFF;
+       return rc;
+}
+
+static int
+rtw_tune(struct rtw_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct rtw_tx_radiotap_header *rt = &sc->sc_txtap;
+       struct rtw_rx_radiotap_header *rr = &sc->sc_rxtap;
+       u_int chan;
+       int rc, antdiv, dflantb;
+
+       antdiv = sc->sc_flags & RTW_F_ANTDIV;
+       dflantb = sc->sc_flags & RTW_F_DFLANTB;
+
+       chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
+       if (chan == IEEE80211_CHAN_ANY)
+               panic("%s: chan == IEEE80211_CHAN_ANY\n", ic->ic_if.if_xname);
+
+       rt->rt_chan_freq = htole16(ic->ic_curchan->ic_freq);
+       rt->rt_chan_flags = htole16(ic->ic_curchan->ic_flags);
+
+       rr->rr_chan_freq = htole16(ic->ic_curchan->ic_freq);
+       rr->rr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+
+       if (chan == sc->sc_cur_chan) {
+               RTW_DPRINTF(RTW_DEBUG_TUNE,
+                           ("%s: already tuned chan #%d\n",
+                            ic->ic_if.if_xname, chan));
+               return 0;
+       }
+
+       rtw_suspend_ticks(sc);
+
+       rtw_io_enable(sc, RTW_CR_RE | RTW_CR_TE, 0);
+
+       /* TBD wait for Tx to complete */
+
+       KKASSERT((sc->sc_flags & RTW_F_ENABLED) != 0);
+
+       rc = rtw_phy_init(&sc->sc_regs, sc->sc_rf,
+                         rtw_chan2txpower(&sc->sc_srom, ic, ic->ic_curchan),
+                         sc->sc_csthr, ic->ic_curchan->ic_freq, antdiv,
+                         dflantb, RTW_ON);
+       if (rc != 0) {
+               /* XXX condition on powersaving */
+               printf("%s: phy init failed\n", ic->ic_if.if_xname);
+       }
+
+       sc->sc_cur_chan = chan;
+
+       rtw_io_enable(sc, RTW_CR_RE | RTW_CR_TE, 1);
+
+       rtw_resume_ticks(sc);
+
+       return rc;
+}
+
+static void
+rtw_disable(struct rtw_softc *sc)
+{
+       int rc;
+
+       if ((sc->sc_flags & RTW_F_ENABLED) == 0)
+               return;
+
+       /* turn off PHY */
+       if ((sc->sc_flags & RTW_F_INVALID) == 0 &&
+           (rc = rtw_pwrstate(sc, RTW_OFF)) != 0)
+               if_printf(&sc->sc_ic.ic_if, "failed to turn off PHY\n");
+
+       sc->sc_flags &= ~RTW_F_ENABLED;
+}
+
+static int
+rtw_enable(struct rtw_softc *sc)
+{
+       if ((sc->sc_flags & RTW_F_ENABLED) == 0) {
+               sc->sc_flags |= RTW_F_ENABLED;
+               /*
+                * Power may have been removed, and WEP keys thus reset.
+                */
+               sc->sc_flags &= ~RTW_F_DK_VALID;
+       }
+       return (0);
+}
+
+static void
+rtw_transmit_config(struct rtw_regs *regs)
+{
+       uint32_t tcr;
+
+       tcr = RTW_READ(regs, RTW_TCR);
+
+       tcr |= RTW_TCR_CWMIN;
+       tcr &= ~RTW_TCR_MXDMA_MASK;
+       tcr |= RTW_TCR_MXDMA_256;
+       tcr |= RTW_TCR_SAT;             /* send ACK as fast as possible */
+       tcr &= ~RTW_TCR_LBK_MASK;
+       tcr |= RTW_TCR_LBK_NORMAL;      /* normal operating mode */
+
+       /* set short/long retry limits */
+       tcr &= ~(RTW_TCR_SRL_MASK|RTW_TCR_LRL_MASK);
+       tcr |= SHIFTIN(4, RTW_TCR_SRL_MASK) | SHIFTIN(4, RTW_TCR_LRL_MASK);
+
+       tcr &= ~RTW_TCR_CRC;    /* NIC appends CRC32 */
+
+       RTW_WRITE(regs, RTW_TCR, tcr);
+       RTW_SYNC(regs, RTW_TCR, RTW_TCR);
+}
+
+static void
+rtw_enable_interrupts(struct rtw_softc *sc)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+
+       sc->sc_inten = RTW_INTR_RX|RTW_INTR_TX|RTW_INTR_BEACON|RTW_INTR_ATIMINT;
+       sc->sc_inten |= RTW_INTR_IOERROR|RTW_INTR_TIMEOUT;
+
+       RTW_WRITE16(regs, RTW_IMR, sc->sc_inten);
+       RTW_WBW(regs, RTW_IMR, RTW_ISR);
+       RTW_WRITE16(regs, RTW_ISR, 0xffff);
+       RTW_SYNC(regs, RTW_IMR, RTW_ISR);
+
+       /* XXX necessary? */
+       if (sc->sc_intr_ack != NULL)
+               sc->sc_intr_ack(regs);
+}
+
+static void
+rtw_set_nettype(struct rtw_softc *sc, enum ieee80211_opmode opmode)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+       uint8_t msr;
+
+       /* I'm guessing that MSR is protected as CONFIG[0123] are. */
+       rtw_set_access(sc, RTW_ACCESS_CONFIG);
+
+       msr = RTW_READ8(regs, RTW_MSR) & ~RTW_MSR_NETYPE_MASK;
+
+       switch (opmode) {
+       case IEEE80211_M_AHDEMO:
+       case IEEE80211_M_IBSS:
+               msr |= RTW_MSR_NETYPE_ADHOC_OK;
+               break;
+       case IEEE80211_M_HOSTAP:
+               msr |= RTW_MSR_NETYPE_AP_OK;
+               break;
+       case IEEE80211_M_MONITOR:
+               /* XXX */
+               msr |= RTW_MSR_NETYPE_NOLINK;
+               break;
+       case IEEE80211_M_STA:
+               msr |= RTW_MSR_NETYPE_INFRA_OK;
+               break;
+       }
+       RTW_WRITE8(regs, RTW_MSR, msr);
+
+       rtw_set_access(sc, RTW_ACCESS_NONE);
+}
+
+#define        rtw_calchash(addr) \
+       (ether_crc32_be((addr), IEEE80211_ADDR_LEN) >> 26)
+
+static void
+rtw_pktfilt_load(struct rtw_softc *sc)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+       struct ifmultiaddr *ifma;
+       uint32_t hashes[2] = { 0, 0 };
+       int hash;
+
+       /* XXX might be necessary to stop Rx/Tx engines while setting filters */
+
+       sc->sc_rcr &= ~RTW_RCR_PKTFILTER_MASK;
+       sc->sc_rcr &= ~(RTW_RCR_MXDMA_MASK | RTW_RCR_RXFTH_MASK);
+
+       sc->sc_rcr |= RTW_RCR_PKTFILTER_DEFAULT;
+       /* MAC auto-reset PHY (huh?) */
+       sc->sc_rcr |= RTW_RCR_ENMARP;
+       /* DMA whole Rx packets, only.  Set Tx DMA burst size to 1024 bytes. */
+       sc->sc_rcr |= RTW_RCR_MXDMA_1024 | RTW_RCR_RXFTH_WHOLE;
+
+       switch (ic->ic_opmode) {
+       case IEEE80211_M_MONITOR:
+               sc->sc_rcr |= RTW_RCR_MONITOR;
+               break;
+       case IEEE80211_M_AHDEMO:
+       case IEEE80211_M_IBSS:
+               /* receive broadcasts in our BSS */
+               sc->sc_rcr |= RTW_RCR_ADD3;
+               break;
+       default:
+               break;
+       }
+
+       ifp->if_flags &= ~IFF_ALLMULTI;
+
+       /* XXX accept all broadcast if scanning */
+       if ((ifp->if_flags & IFF_BROADCAST) != 0)
+               sc->sc_rcr |= RTW_RCR_AB;       /* accept all broadcast */
+
+       if (ifp->if_flags & IFF_PROMISC) {
+               sc->sc_rcr |= RTW_RCR_AB;       /* accept all broadcast */
+allmulti:
+               ifp->if_flags |= IFF_ALLMULTI;
+               goto setit;
+       }
+
+       /*
+        * Program the 64-bit multicast hash filter.
+        */
+       LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+               if (ifma->ifma_addr->sa_family != AF_LINK)
+                       continue;
+
+               hash = rtw_calchash(
+                       LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+               hashes[hash >> 5] |= (1 << (hash & 0x1f));
+               sc->sc_rcr |= RTW_RCR_AM;
+       }
+
+       /* all bits set => hash is useless */
+       if (~(hashes[0] & hashes[1]) == 0)
+               goto allmulti;
+
+setit:
+       if (ifp->if_flags & IFF_ALLMULTI) {
+               sc->sc_rcr |= RTW_RCR_AM;       /* accept all multicast */
+               hashes[0] = hashes[1] = 0xffffffff;
+       }
+
+       RTW_WRITE(regs, RTW_MAR0, hashes[0]);
+       RTW_WRITE(regs, RTW_MAR1, hashes[1]);
+       RTW_WRITE(regs, RTW_RCR, sc->sc_rcr);
+       RTW_SYNC(regs, RTW_MAR0, RTW_RCR); /* RTW_MAR0 < RTW_MAR1 < RTW_RCR */
+
+       DPRINTF(sc, RTW_DEBUG_PKTFILT,
+               ("%s: RTW_MAR0 %08x RTW_MAR1 %08x RTW_RCR %08x\n",
+                ifp->if_xname, RTW_READ(regs, RTW_MAR0),
+                RTW_READ(regs, RTW_MAR1), RTW_READ(regs, RTW_RCR)));
+}
+
+/* Must be called at splnet. */
+static void
+rtw_init(void *xsc)
+{
+       struct rtw_softc *sc = xsc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+       struct rtw_regs *regs = &sc->sc_regs;
+       int rc = 0;
+
+       rc = rtw_enable(sc);
+       if (rc)
+               goto out;
+
+       /* Cancel pending I/O and reset. */
+       rtw_stop(sc, 0);
+
+       DPRINTF(sc, RTW_DEBUG_TUNE,
+               ("%s: channel %d freq %d flags 0x%04x\n", ifp->if_xname,
+               ieee80211_chan2ieee(ic, ic->ic_curchan),
+               ic->ic_curchan->ic_freq, ic->ic_curchan->ic_flags));
+
+       rc = rtw_pwrstate(sc, RTW_OFF);
+       if (rc)
+               goto out;
+
+       rc = rtw_swring_setup(sc);
+       if (rc)
+               goto out;
+
+       rtw_transmit_config(regs);
+
+       rtw_set_access(sc, RTW_ACCESS_CONFIG);
+
+       RTW_WRITE8(regs, RTW_MSR, 0x0); /* no link */
+       RTW_WBW(regs, RTW_MSR, RTW_BRSR);
+
+       /* long PLCP header, 1Mb/2Mb basic rate */
+       RTW_WRITE16(regs, RTW_BRSR, RTW_BRSR_MBR8180_2MBPS);
+       RTW_SYNC(regs, RTW_BRSR, RTW_BRSR);
+
+       rtw_set_access(sc, RTW_ACCESS_ANAPARM);
+       rtw_set_access(sc, RTW_ACCESS_NONE);
+
+       /* XXX from reference sources */
+       RTW_WRITE(regs, RTW_FEMR, 0xffff);
+       RTW_SYNC(regs, RTW_FEMR, RTW_FEMR);
+
+       rtw_set_rfprog(sc);
+
+       RTW_WRITE8(regs, RTW_PHYDELAY, sc->sc_phydelay);
+       /* from Linux driver */
+       RTW_WRITE8(regs, RTW_CRCOUNT, RTW_CRCOUNT_MAGIC);
+
+       RTW_SYNC(regs, RTW_PHYDELAY, RTW_CRCOUNT);
+
+       rtw_enable_interrupts(sc);
+
+       rtw_pktfilt_load(sc);
+
+       rtw_hwring_setup(sc);
+
+       rtw_wep_setkeys(sc);
+
+       rtw_io_enable(sc, RTW_CR_RE | RTW_CR_TE, 1);
+
+       ifp->if_flags |= IFF_RUNNING;
+       ic->ic_state = IEEE80211_S_INIT;
+
+       RTW_WRITE16(regs, RTW_BSSID16, 0x0);
+       RTW_WRITE(regs, RTW_BSSID32, 0x0);
+
+       rtw_resume_ticks(sc);
+
+       rtw_set_nettype(sc, IEEE80211_M_MONITOR);
+
+       if (ic->ic_opmode == IEEE80211_M_MONITOR)
+               ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+       else
+               ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+
+out:
+       if (rc)
+               if_printf(ifp, "interface not running\n");
+}
+
+static void
+rtw_led_init(struct rtw_softc *sc)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+       uint8_t cfg0, cfg1;
+
+       rtw_set_access(sc, RTW_ACCESS_CONFIG);
+
+       cfg0 = RTW_READ8(regs, RTW_CONFIG0);
+       cfg0 |= RTW_CONFIG0_LEDGPOEN;
+       RTW_WRITE8(regs, RTW_CONFIG0, cfg0);
+
+       cfg1 = RTW_READ8(regs, RTW_CONFIG1);
+       RTW_DPRINTF(RTW_DEBUG_LED,
+                   ("%s: read %02x from reg[CONFIG1]\n",
+                    sc->sc_ic.ic_if.if_xname, cfg1));
+
+       cfg1 &= ~RTW_CONFIG1_LEDS_MASK;
+       cfg1 |= RTW_CONFIG1_LEDS_TX_RX;
+       RTW_WRITE8(regs, RTW_CONFIG1, cfg1);
+
+       rtw_set_access(sc, RTW_ACCESS_NONE);
+}
+
+/*
+ * IEEE80211_S_INIT:           LED1 off
+ *
+ * IEEE80211_S_AUTH,
+ * IEEE80211_S_ASSOC,
+ * IEEE80211_S_SCAN:           LED1 blinks @ 1 Hz, blinks at 5Hz for tx/rx
+ *
+ * IEEE80211_S_RUN:            LED1 on, blinks @ 5Hz for tx/rx
+ */
+static void
+rtw_led_newstate(struct rtw_softc *sc, enum ieee80211_state nstate)
+{
+       struct rtw_led_state *ls = &sc->sc_led_state;
+
+       switch (nstate) {
+       case IEEE80211_S_INIT:
+               rtw_led_init(sc);
+               callout_stop(&ls->ls_slow_ch);
+               callout_stop(&ls->ls_fast_ch);
+               ls->ls_slowblink = 0;
+               ls->ls_actblink = 0;
+               ls->ls_default = 0;
+               break;
+       case IEEE80211_S_SCAN:
+               callout_reset(&ls->ls_slow_ch, RTW_LED_SLOW_TICKS,
+                             rtw_led_slowblink, sc);
+               callout_reset(&ls->ls_fast_ch, RTW_LED_FAST_TICKS,
+                             rtw_led_fastblink, sc);
+               /*FALLTHROUGH*/
+       case IEEE80211_S_AUTH:
+       case IEEE80211_S_ASSOC:
+               ls->ls_default = RTW_LED1;
+               ls->ls_actblink = RTW_LED1;
+               ls->ls_slowblink = RTW_LED1;
+               break;
+       case IEEE80211_S_RUN:
+               ls->ls_slowblink = 0;
+               break;
+       }
+       rtw_led_set(sc);
+}
+
+static void
+rtw_led_set(struct rtw_softc *sc)
+{
+       struct rtw_led_state *ls = &sc->sc_led_state;
+       struct rtw_regs *regs = &sc->sc_regs;
+       uint8_t led_condition, mask, newval, val;
+       bus_size_t ofs;
+
+       led_condition = ls->ls_default;
+
+       if (ls->ls_state & RTW_LED_S_SLOW)
+               led_condition ^= ls->ls_slowblink;
+       if (ls->ls_state & (RTW_LED_S_RX|RTW_LED_S_TX))
+               led_condition ^= ls->ls_actblink;
+
+       RTW_DPRINTF(RTW_DEBUG_LED,
+                   ("%s: LED condition %02x\n", sc->sc_ic.ic_if.if_xname,
+                    led_condition));
+
+       switch (sc->sc_hwverid) {
+       default:
+       case 'F':
+               ofs = RTW_PSR;
+               newval = mask = RTW_PSR_LEDGPO0 | RTW_PSR_LEDGPO1;
+               if (led_condition & RTW_LED0)
+                       newval &= ~RTW_PSR_LEDGPO0;
+               if (led_condition & RTW_LED1)
+                       newval &= ~RTW_PSR_LEDGPO1;
+               break;
+       case 'D':
+               ofs = RTW_9346CR;
+               mask = RTW_9346CR_EEM_MASK | RTW_9346CR_EEDI | RTW_9346CR_EECS;
+               newval = RTW_9346CR_EEM_PROGRAM;
+               if (led_condition & RTW_LED0)
+                       newval |= RTW_9346CR_EEDI;
+               if (led_condition & RTW_LED1)
+                       newval |= RTW_9346CR_EECS;
+               break;
+       }
+       val = RTW_READ8(regs, ofs);
+       RTW_DPRINTF(RTW_DEBUG_LED,
+                   ("%s: read %02x from reg[%02x]\n",
+                    sc->sc_ic.ic_if.if_xname, val, ofs));
+       val &= ~mask;
+       val |= newval;
+       RTW_WRITE8(regs, ofs, val);
+       RTW_DPRINTF(RTW_DEBUG_LED,
+                   ("%s: wrote %02x to reg[%02x]\n",
+                    sc->sc_ic.ic_if.if_xname, val, ofs));
+       RTW_SYNC(regs, ofs, ofs);
+}
+
+static void
+rtw_led_fastblink(void *arg)
+{
+       struct rtw_softc *sc = arg;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       struct rtw_led_state *ls = &sc->sc_led_state;
+       int ostate;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       ostate = ls->ls_state;
+       ls->ls_state ^= ls->ls_event;
+
+       if ((ls->ls_event & RTW_LED_S_TX) == 0)
+               ls->ls_state &= ~RTW_LED_S_TX;
+
+       if ((ls->ls_event & RTW_LED_S_RX) == 0)
+               ls->ls_state &= ~RTW_LED_S_RX;
+
+       ls->ls_event = 0;
+
+       if (ostate != ls->ls_state)
+               rtw_led_set(sc);
+
+       callout_reset(&ls->ls_fast_ch, RTW_LED_FAST_TICKS,
+                     rtw_led_fastblink, sc);
+
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+static void
+rtw_led_slowblink(void *arg)
+{
+       struct rtw_softc *sc = arg;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       struct rtw_led_state *ls = &sc->sc_led_state;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       ls->ls_state ^= RTW_LED_S_SLOW;
+       rtw_led_set(sc);
+       callout_reset(&ls->ls_slow_ch, RTW_LED_SLOW_TICKS,
+                     rtw_led_slowblink, sc);
+
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+static int
+rtw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
+{
+       struct rtw_softc *sc = ifp->if_softc;
+       int rc = 0;
+
+       switch (cmd) {
+       case SIOCSIFFLAGS:
+               if (ifp->if_flags & IFF_UP) {
+                       if ((ifp->if_flags & IFF_RUNNING) == 0)
+                               rtw_init(sc);
+                       RTW_PRINT_REGS(&sc->sc_regs, ifp->if_xname, __func__);
+               } else if (sc->sc_flags & RTW_F_ENABLED) {
+                       RTW_PRINT_REGS(&sc->sc_regs, ifp->if_xname, __func__);
+                       rtw_stop(sc, 1);
+               }
+               break;
+       case SIOCADDMULTI:
+       case SIOCDELMULTI:
+               if (ifp->if_flags & IFF_RUNNING)
+                       rtw_pktfilt_load(sc);
+               break;
+       default:
+               rc = ieee80211_ioctl(&sc->sc_ic, cmd, data, cr);
+               if (rc == ENETRESET) {
+                       if (sc->sc_flags & RTW_F_ENABLED)
+                               rtw_init(sc);
+                       rc = 0;
+               }
+               break;
+       }
+       return rc;
+}
+
+/*
+ * Select a transmit ring with at least one h/w and s/w descriptor free.
+ * Return 0 on success, -1 on failure.
+ */
+static __inline int
+rtw_txring_choose(struct rtw_softc *sc, struct rtw_txsoft_blk **tsbp,
+                 struct rtw_txdesc_blk **tdbp, int pri)
+{
+       struct rtw_txsoft_blk *tsb;
+       struct rtw_txdesc_blk *tdb;
+
+       KKASSERT(pri >= 0 && pri < RTW_NTXPRI);
+
+       tsb = &sc->sc_txsoft_blk[pri];
+       tdb = &sc->sc_txdesc_blk[pri];
+
+       if (STAILQ_EMPTY(&tsb->tsb_freeq) || tdb->tdb_nfree == 0) {
+               if (tsb->tsb_tx_timer == 0)
+                       tsb->tsb_tx_timer = 5;
+               *tsbp = NULL;
+               *tdbp = NULL;
+               return -1;
+       }
+       *tsbp = tsb;
+       *tdbp = tdb;
+       return 0;
+}
+
+static __inline struct mbuf *
+rtw_80211_dequeue(struct rtw_softc *sc, struct ifqueue *ifq, int pri,
+                 struct rtw_txsoft_blk **tsbp, struct rtw_txdesc_blk **tdbp,
+                 struct ieee80211_node **nip, int *if_flagsp)
+{
+       struct mbuf *m;
+       struct ifnet *ifp = &sc->sc_if;
+
+       if (IF_QEMPTY(ifq))
+               return NULL;
+       if (rtw_txring_choose(sc, tsbp, tdbp, pri) == -1) {
+               DPRINTF(sc, RTW_DEBUG_XMIT_RSRC,
+                       ("%s: no ring %d descriptor\n", ifp->if_xname, pri));
+               *if_flagsp |= IFF_OACTIVE;
+               ifp->if_timer = 1;
+               return NULL;
+       }
+       IF_DEQUEUE(ifq, m);
+       *nip = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+       m->m_pkthdr.rcvif = NULL;
+       KKASSERT(*nip != NULL);
+       return m;
+}
+
+/*
+ * Point *mp at the next 802.11 frame to transmit.  Point *tsbp
+ * at the driver's selection of transmit control block for the packet.
+ */
+static int
+rtw_dequeue(struct ifnet *ifp, struct rtw_txsoft_blk **tsbp,
+           struct rtw_txdesc_blk **tdbp, struct mbuf **mp,
+           struct ieee80211_node **nip)
+{
+       struct rtw_softc *sc = ifp->if_softc;
+       int *if_flagsp = &ifp->if_flags;
+       struct ether_header *eh;
+       struct mbuf *m0;
+       int pri;
+
+       DPRINTF(sc, RTW_DEBUG_XMIT,
+               ("%s: enter %s\n", ifp->if_xname, __func__));
+
+       if (sc->sc_ic.ic_state == IEEE80211_S_RUN &&
+           (*mp = rtw_80211_dequeue(sc, &sc->sc_beaconq, RTW_TXPRIBCN, tsbp,
+                                    tdbp, nip, if_flagsp)) != NULL) {
+               DPRINTF(sc, RTW_DEBUG_XMIT,
+                       ("%s: dequeue beacon frame\n", ifp->if_xname));
+               return 0;
+       }
+
+       if ((*mp = rtw_80211_dequeue(sc, &sc->sc_ic.ic_mgtq, RTW_TXPRIMD, tsbp,
+                                    tdbp, nip, if_flagsp)) != NULL) {
+               DPRINTF(sc, RTW_DEBUG_XMIT,
+                       ("%s: dequeue mgt frame\n", ifp->if_xname));
+               return 0;
+       }
+
+       *mp = NULL;
+
+       if (sc->sc_ic.ic_state != IEEE80211_S_RUN) {
+               DPRINTF(sc, RTW_DEBUG_XMIT,
+                       ("%s: not running\n", ifp->if_xname));
+               return 0;
+       }
+
+       m0 = ifq_poll(&ifp->if_snd);
+       if (m0 == NULL) {
+               DPRINTF(sc, RTW_DEBUG_XMIT,
+                       ("%s: no frame ready\n", ifp->if_xname));
+               return 0;
+       }
+
+       pri = ((m0->m_flags & M_PWR_SAV) != 0) ? RTW_TXPRIHI : RTW_TXPRIMD;
+
+       if (rtw_txring_choose(sc, tsbp, tdbp, pri) == -1) {
+               DPRINTF(sc, RTW_DEBUG_XMIT_RSRC,
+                       ("%s: no ring %d descriptor\n", ifp->if_xname, pri));
+               *if_flagsp |= IFF_OACTIVE;
+               sc->sc_if.if_timer = 1;
+               return 0;
+       }
+
+       ifq_dequeue(&ifp->if_snd, m0);
+       DPRINTF(sc, RTW_DEBUG_XMIT,
+               ("%s: dequeue data frame\n", ifp->if_xname));
+
+       BPF_MTAP(ifp, m0);
+
+       eh = mtod(m0, struct ether_header *);
+       *nip = ieee80211_find_txnode(&sc->sc_ic, eh->ether_dhost);
+       if (*nip == NULL) {
+               /* NB: ieee80211_find_txnode does stat+msg */
+               m_freem(m0);
+               return -1;
+       }
+
+       if ((m0 = ieee80211_encap(&sc->sc_ic, m0, *nip)) == NULL) {
+               DPRINTF(sc, RTW_DEBUG_XMIT,
+                       ("%s: encap error\n", ifp->if_xname));
+               ieee80211_free_node(*nip);
+               ifp->if_oerrors++;
+               return -1;
+       }
+
+       ifp->if_opackets++;
+       DPRINTF(sc, RTW_DEBUG_XMIT,
+               ("%s: leave %s\n", ifp->if_xname, __func__));
+       *mp = m0;
+       return 0;
+}
+
+static __inline int
+rtw_txsegs_too_short(struct rtw_txsegs *segs)
+{
+       int i;
+
+       for (i = 0; i < segs->nseg; i++) {
+               if (segs->segs[i].ds_len < 4)
+                       return 1;
+       }
+       return 0;
+}
+
+static __inline int
+rtw_txsegs_too_long(struct rtw_txsegs *segs)
+{
+       int i;
+
+       for (i = 0; i < segs->nseg; i++) {
+               if (segs->segs[i].ds_len > RTW_TXLEN_LENGTH_MASK)
+                       return 1;
+       }
+       return 0;
+}
+
+static void
+rtw_txbuf_dma_map(void *arg, bus_dma_segment_t *seg, int nseg,
+                 bus_size_t mapsize, int error)
+{
+       struct rtw_txsegs *s = arg;
+
+       if (error)
+               return;
+
+       KASSERT(nseg <= RTW_MAXPKTSEGS, ("too many tx mbuf seg\n"));
+
+       s->nseg = nseg;
+       bcopy(seg, s->segs, sizeof(*seg) * nseg);
+}
+
+static struct mbuf *
+rtw_load_txbuf(struct rtw_softc *sc, struct rtw_txsoft *ts,
+              struct rtw_txsegs *segs, int ndesc_free, struct mbuf *m)
+{
+       int unload = 0, error;
+
+       error = bus_dmamap_load_mbuf(sc->sc_txsoft_dmat, ts->ts_dmamap, m,
+                                    rtw_txbuf_dma_map, segs, BUS_DMA_NOWAIT);
+       if (error && error != E2BIG) {
+               if_printf(&sc->sc_ic.ic_if, "can't load tx mbuf1\n");
+               goto back;
+       }
+
+       if (error || segs->nseg > ndesc_free || rtw_txsegs_too_short(segs)) {
+               struct mbuf *m_new;
+
+               if (error == 0)
+                       bus_dmamap_unload(sc->sc_txsoft_dmat, ts->ts_dmamap);
+
+               m_new = m_defrag(m, MB_DONTWAIT);
+               if (m_new == NULL) {
+                       if_printf(&sc->sc_ic.ic_if, "can't defrag tx mbuf\n");
+                       error = ENOBUFS;
+                       goto back;
+               }
+               m = m_new;
+
+               error = bus_dmamap_load_mbuf(sc->sc_txsoft_dmat, ts->ts_dmamap,
+                                            m, rtw_txbuf_dma_map, segs,
+                                            BUS_DMA_NOWAIT);
+               if (error) {
+                       if_printf(&sc->sc_ic.ic_if, "can't load tx mbuf2\n");
+                       goto back;
+               }
+               unload = 1;
+
+               error = E2BIG;
+               if (segs->nseg > ndesc_free) {
+                       if_printf(&sc->sc_ic.ic_if, "not enough free txdesc\n");
+                       goto back;
+               }
+               if (rtw_txsegs_too_short(segs)) {
+                       if_printf(&sc->sc_ic.ic_if, "segment too short\n");
+                       goto back;
+               }
+               error = 0;
+       }
+
+       if (rtw_txsegs_too_long(segs)) {
+               if_printf(&sc->sc_ic.ic_if, "segment too long\n");
+               unload = 1;
+               error = E2BIG;
+       }
+
+back:
+       if (error) {
+               if (unload)
+                       bus_dmamap_unload(sc->sc_txsoft_dmat, ts->ts_dmamap);
+               m_freem(m);
+               m = NULL;
+       } else {
+               bus_dmamap_sync(sc->sc_txsoft_dmat, ts->ts_dmamap,
+                               BUS_DMASYNC_PREWRITE);
+       }
+       return m;
+}
+
+#ifdef RTW_DEBUG
+static void
+rtw_print_txdesc(struct rtw_softc *sc, const char *action,
+                struct rtw_txsoft *ts, struct rtw_txdesc_blk *tdb, int desc)
+{
+       struct rtw_txdesc *td = &tdb->tdb_desc[desc];
+
+       DPRINTF(sc, RTW_DEBUG_XMIT_DESC,
+               ("%s: %p %s txdesc[%d] "
+                "next %#08x buf %#08x "
+                "ctl0 %#08x ctl1 %#08x len %#08x\n",
+                sc->sc_ic.ic_if.if_xname, ts, action,
+                desc, le32toh(td->td_buf), le32toh(td->td_next),
+                le32toh(td->td_ctl0), le32toh(td->td_ctl1),
+                le32toh(td->td_len)));
+}
+#endif /* RTW_DEBUG */
+
+static void
+rtw_start(struct ifnet *ifp)
+{
+       struct rtw_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ieee80211_node *ni;
+       struct rtw_txsoft *ts;
+       struct mbuf *m0;
+       uint32_t proto_ctl0;
+
+       DPRINTF(sc, RTW_DEBUG_XMIT,
+               ("%s: enter %s\n", ifp->if_xname, __func__));
+
+       if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+               goto out;
+
+       /* XXX do real rate control */
+       proto_ctl0 = RTW_TXCTL0_RTSRATE_1MBPS;
+
+       if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+               proto_ctl0 |= RTW_TXCTL0_SPLCP;
+
+       for (;;) {
+               struct rtw_txsegs segs;
+               struct rtw_duration *d0;
+               struct ieee80211_frame_min *wh;
+               struct rtw_txsoft_blk *tsb;
+               struct rtw_txdesc_blk *tdb;
+               struct rtw_txdesc *td;
+               struct ieee80211_key *k;
+               uint32_t ctl0, ctl1;
+               uint8_t tppoll;
+               int desc, i, lastdesc, npkt, rate, rateidx, ratectl;
+
+               if (rtw_dequeue(ifp, &tsb, &tdb, &m0, &ni) == -1)
+                       continue;
+               if (m0 == NULL)
+                       break;
+
+               wh = mtod(m0, struct ieee80211_frame_min *);
+
+               if ((wh->i_fc[1] & IEEE80211_FC1_WEP) != 0 &&
+                   (k = ieee80211_crypto_encap(ic, ni, m0)) == NULL) {
+                       ieee80211_free_node(ni);
+                       m_freem(m0);
+                       break;
+               } else {
+                       k = NULL;
+               }
+
+               ts = STAILQ_FIRST(&tsb->tsb_freeq);
+
+               m0 = rtw_load_txbuf(sc, ts, &segs, tdb->tdb_nfree, m0);
+               if (m0 == NULL || segs.nseg == 0) {
+                       DPRINTF(sc, RTW_DEBUG_XMIT,
+                               ("%s: %s failed\n", ifp->if_xname, __func__));
+                       goto post_dequeue_err;
+               }
+
+               /*
+                * Note well: rtw_load_txbuf may have created a new chain,
+                * so we must find the header once more.
+                */
+               wh = mtod(m0, struct ieee80211_frame_min *);
+
+               if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
+                   IEEE80211_FC0_TYPE_MGT) {
+                       rateidx = 0;
+                       rate = 2;       /* 1Mbit/s */
+                       ratectl = 0;
+               } else {
+                       ieee80211_ratectl_findrate(ni, m0->m_pkthdr.len,
+                                                  &rateidx, 1);
+                       rate = IEEE80211_RS_RATE(&ni->ni_rates, rateidx);
+                       ratectl =1;
+
+                       if (rate == 0) {
+                               if_printf(ifp, "incorrect rate\n");
+                               rateidx = 0;
+                               rate = 2;       /* 1Mbit/s */
+                               ratectl = 0;
+                       }
+               }
+
+#ifdef RTW_DEBUG
+               if ((ifp->if_flags & (IFF_DEBUG | IFF_LINK2)) ==
+                   (IFF_DEBUG | IFF_LINK2)) {
+                       ieee80211_dump_pkt(mtod(m0, uint8_t *),
+                                          (segs.nseg == 1) ? m0->m_pkthdr.len
+                                                           : sizeof(wh),
+                                          rate, 0);
+               }
+#endif /* RTW_DEBUG */
+               ctl0 = proto_ctl0 |
+                      SHIFTIN(m0->m_pkthdr.len, RTW_TXCTL0_TPKTSIZE_MASK);
+
+               switch (rate) {
+               default:
+               case 2:
+                       ctl0 |= RTW_TXCTL0_RATE_1MBPS;
+                       break;
+               case 4:
+                       ctl0 |= RTW_TXCTL0_RATE_2MBPS;
+                       break;
+               case 11:
+                       ctl0 |= RTW_TXCTL0_RATE_5MBPS;
+                       break;
+               case 22:
+                       ctl0 |= RTW_TXCTL0_RATE_11MBPS;
+                       break;
+               }
+               /* XXX >= ? Compare after fragmentation? */
+               if (m0->m_pkthdr.len > ic->ic_rtsthreshold)
+                       ctl0 |= RTW_TXCTL0_RTSEN;
+
+                /*
+                * XXX Sometimes writes a bogus keyid; h/w doesn't
+                 * seem to care, since we don't activate h/w Tx
+                 * encryption.
+                */
+               if (k != NULL) {
+                       ctl0 |= SHIFTIN(k->wk_keyix, RTW_TXCTL0_KEYID_MASK) &
+                               RTW_TXCTL0_KEYID_MASK;
+               }
+
+               if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
+                   IEEE80211_FC0_TYPE_MGT) {
+                       ctl0 &= ~(RTW_TXCTL0_SPLCP | RTW_TXCTL0_RTSEN);
+                       if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+                           IEEE80211_FC0_SUBTYPE_BEACON)
+                               ctl0 |= RTW_TXCTL0_BEACON;
+               }
+
+               if (rtw_compute_duration(wh, k, m0->m_pkthdr.len,
+                   ic->ic_flags, ic->ic_fragthreshold,
+                   rate, &ts->ts_d0, &ts->ts_dn, &npkt,
+                   (ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) ==
+                   (IFF_DEBUG|IFF_LINK2)) == -1) {
+                       DPRINTF(sc, RTW_DEBUG_XMIT,
+                           ("%s: fail compute duration\n", __func__));
+                       goto post_load_err;
+               }
+
+               d0 = &ts->ts_d0;
+
+               *(uint16_t*)wh->i_dur = htole16(d0->d_data_dur);
+
+               ctl1 = SHIFTIN(d0->d_plcp_len, RTW_TXCTL1_LENGTH_MASK) |
+                      SHIFTIN(d0->d_rts_dur, RTW_TXCTL1_RTSDUR_MASK);
+
+               if (d0->d_residue)
+                       ctl1 |= RTW_TXCTL1_LENGEXT;
+
+               /* TBD fragmentation */
+
+               ts->ts_first = tdb->tdb_next;
+               KKASSERT(ts->ts_first < tdb->tdb_ndesc);
+
+               if (ic->ic_rawbpf != NULL)
+                       bpf_mtap(ic->ic_rawbpf, m0);
+
+               if (sc->sc_radiobpf != NULL) {
+                       struct rtw_tx_radiotap_header *rt = &sc->sc_txtap;
+
+                       rt->rt_flags = 0;
+                       rt->rt_rate = rate;
+
+                       bpf_ptap(sc->sc_radiobpf, m0, rt,
+                                sizeof(sc->sc_txtapu));
+               }
+
+               for (i = 0, lastdesc = desc = ts->ts_first; i < segs.nseg;
+                    i++, desc = RTW_NEXT_IDX(tdb, desc)) {
+                       td = &tdb->tdb_desc[desc];
+                       td->td_ctl0 = htole32(ctl0);
+                       if (i != 0)
+                               td->td_ctl0 |= htole32(RTW_TXCTL0_OWN);
+                       td->td_ctl1 = htole32(ctl1);
+                       td->td_buf = htole32(segs.segs[i].ds_addr);
+                       td->td_len = htole32(segs.segs[i].ds_len);
+                       lastdesc = desc;
+#ifdef RTW_DEBUG
+                       rtw_print_txdesc(sc, "load", ts, tdb, desc);
+#endif /* RTW_DEBUG */
+               }
+
+               KKASSERT(desc < tdb->tdb_ndesc);
+
+               ts->ts_ni = ni;
+               KKASSERT(ni != NULL);
+               ts->ts_mbuf = m0;
+               ts->ts_rateidx = rateidx;
+               ts->ts_ratectl = ratectl;
+               ts->ts_last = lastdesc;
+               tdb->tdb_desc[ts->ts_last].td_ctl0 |= htole32(RTW_TXCTL0_LS);
+               tdb->tdb_desc[ts->ts_first].td_ctl0 |= htole32(RTW_TXCTL0_FS);
+
+#ifdef RTW_DEBUG
+               rtw_print_txdesc(sc, "FS on", ts, tdb, ts->ts_first);
+               rtw_print_txdesc(sc, "LS on", ts, tdb, ts->ts_last);
+#endif /* RTW_DEBUG */
+
+               tdb->tdb_nfree -= segs.nseg;
+               tdb->tdb_next = desc;
+
+               tdb->tdb_desc[ts->ts_first].td_ctl0 |= htole32(RTW_TXCTL0_OWN);
+
+#ifdef RTW_DEBUG
+               rtw_print_txdesc(sc, "OWN on", ts, tdb, ts->ts_first);
+#endif /* RTW_DEBUG */
+
+               STAILQ_REMOVE_HEAD(&tsb->tsb_freeq, ts_q);
+               STAILQ_INSERT_TAIL(&tsb->tsb_dirtyq, ts, ts_q);
+
+               if (tsb != &sc->sc_txsoft_blk[RTW_TXPRIBCN])
+                       sc->sc_led_state.ls_event |= RTW_LED_S_TX;
+               tsb->tsb_tx_timer = 5;
+               ifp->if_timer = 1;
+               tppoll = RTW_READ8(&sc->sc_regs, RTW_TPPOLL);
+               tppoll &= ~RTW_TPPOLL_SALL;
+               tppoll |= tsb->tsb_poll & RTW_TPPOLL_ALL;
+               RTW_WRITE8(&sc->sc_regs, RTW_TPPOLL, tppoll);
+               RTW_SYNC(&sc->sc_regs, RTW_TPPOLL, RTW_TPPOLL);
+
+               bus_dmamap_sync(tdb->tdb_dmat, tdb->tdb_dmamap,
+                               BUS_DMASYNC_PREWRITE);
+       }
+out:
+       DPRINTF(sc, RTW_DEBUG_XMIT,
+               ("%s: leave %s\n", ifp->if_xname, __func__));
+       return;
+
+post_load_err:
+       bus_dmamap_unload(sc->sc_txsoft_dmat, ts->ts_dmamap);
+       m_freem(m0);
+post_dequeue_err:
+       ieee80211_free_node(ni);
+
+       DPRINTF(sc, RTW_DEBUG_XMIT,
+               ("%s: leave %s\n", ifp->if_xname, __func__));
+}
+
+static void
+rtw_idle(struct rtw_softc *sc)
+{
+       struct rtw_regs *regs = &sc->sc_regs;
+       int active;
+
+       /* request stop DMA; wait for packets to stop transmitting. */
+
+       RTW_WRITE8(regs, RTW_TPPOLL, RTW_TPPOLL_SALL);
+       RTW_WBR(regs, RTW_TPPOLL, RTW_TPPOLL);
+
+       for (active = 0;
+            active < 300 &&
+            (RTW_READ8(regs, RTW_TPPOLL) & RTW_TPPOLL_ACTIVE) != 0;
+            active++)
+               DELAY(10);
+       if_printf(&sc->sc_ic.ic_if, "transmit DMA idle in %dus\n", active * 10);
+}
+
+static void
+rtw_watchdog(struct ifnet *ifp)
+{
+       int pri, tx_timeouts = 0;
+       struct rtw_softc *sc = ifp->if_softc;
+
+       ifp->if_timer = 0;
+
+       if ((sc->sc_flags & RTW_F_ENABLED) == 0)
+               return;
+
+       for (pri = 0; pri < RTW_NTXPRI; pri++) {
+               struct rtw_txsoft_blk *tsb = &sc->sc_txsoft_blk[pri];
+
+               if (tsb->tsb_tx_timer == 0)
+                       continue;
+               else if (--tsb->tsb_tx_timer == 0) {
+                       if (STAILQ_EMPTY(&tsb->tsb_dirtyq))
+                               continue;
+                       if_printf(ifp, "transmit timeout, priority %d\n", pri);
+                       ifp->if_oerrors++;
+                       tx_timeouts++;
+               } else {
+                       ifp->if_timer = 1;
+               }
+       }
+
+       if (tx_timeouts > 0) {
+               /*
+                * Stop Tx DMA, disable xmtr, flush Tx rings, enable xmtr,
+                * reset s/w tx-ring pointers, and start transmission.
+                *
+                * TBD Stop/restart just the broken rings?
+                */
+               rtw_idle(sc);
+               rtw_io_enable(sc, RTW_CR_TE, 0);
+               rtw_txdesc_blk_reset_all(sc);
+               rtw_io_enable(sc, RTW_CR_TE, 1);
+               rtw_txring_fixup(sc);
+               rtw_start(ifp);
+       }
+       ieee80211_watchdog(&sc->sc_ic);
+}
+
+static void
+rtw_next_scan(void *arg)
+{
+       struct ieee80211com *ic = arg;
+       struct ifnet *ifp = &ic->ic_if;
+
+       lwkt_serialize_enter(ifp->if_serializer);
+
+       /* don't call rtw_start w/o network interrupts blocked */
+       if (ic->ic_state == IEEE80211_S_SCAN)
+               ieee80211_next_scan(ic);
+
+       lwkt_serialize_exit(ifp->if_serializer);
+}
+
+static void
+rtw_join_bss(struct rtw_softc *sc, uint8_t *bssid, uint16_t intval0)
+{
+       uint16_t bcnitv, bintritv, intval;
+       int i;
+       struct rtw_regs *regs = &sc->sc_regs;
+
+       for (i = 0; i < IEEE80211_ADDR_LEN; i++)
+               RTW_WRITE8(regs, RTW_BSSID + i, bssid[i]);
+
+       RTW_SYNC(regs, RTW_BSSID16, RTW_BSSID32);
+
+       rtw_set_access(sc, RTW_ACCESS_CONFIG);
+
+       intval = MIN(intval0, SHIFTOUT_MASK(RTW_BCNITV_BCNITV_MASK));
+
+       bcnitv = RTW_READ16(regs, RTW_BCNITV) & ~RTW_BCNITV_BCNITV_MASK;
+       bcnitv |= SHIFTIN(intval, RTW_BCNITV_BCNITV_MASK);
+       RTW_WRITE16(regs, RTW_BCNITV, bcnitv);
+       /* interrupt host 1ms before the TBTT */
+       bintritv = RTW_READ16(regs, RTW_BINTRITV) & ~RTW_BINTRITV_BINTRITV;
+       bintritv |= SHIFTIN(1000, RTW_BINTRITV_BINTRITV);
+       RTW_WRITE16(regs, RTW_BINTRITV, bintritv);
+       /* magic from Linux */
+       RTW_WRITE16(regs, RTW_ATIMWND, SHIFTIN(1, RTW_ATIMWND_ATIMWND));
+       RTW_WRITE16(regs, RTW_ATIMTRITV, SHIFTIN(2, RTW_ATIMTRITV_ATIMTRITV));
+       rtw_set_access(sc, RTW_ACCESS_NONE);
+
+       rtw_io_enable(sc, RTW_CR_RE | RTW_CR_TE, 1);
+}
+
+/* Synchronize the hardware state with the software state. */
+static int
+rtw_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+{
+       struct ifnet *ifp = ic->ic_ifp;
+       struct rtw_softc *sc = ifp->if_softc;
+       enum ieee80211_state ostate;
+       int error;
+
+       ostate = ic->ic_state;
+
+       ieee80211_ratectl_newstate(ic, nstate);
+       rtw_led_newstate(sc, nstate);
+
+       if (nstate == IEEE80211_S_INIT) {
+               callout_stop(&sc->sc_scan_ch);
+               sc->sc_cur_chan = IEEE80211_CHAN_ANY;
+               return sc->sc_mtbl.mt_newstate(ic, nstate, arg);
+       }
+
+       if (ostate == IEEE80211_S_INIT && nstate != IEEE80211_S_INIT)
+               rtw_pwrstate(sc, RTW_ON);
+
+       error = rtw_tune(sc);
+       if (error != 0)
+               return error;
+
+       switch (nstate) {
+       case IEEE80211_S_INIT:
+               panic("%s: unexpected state IEEE80211_S_INIT\n", __func__);
+               break;
+       case IEEE80211_S_SCAN:
+               if (ostate != IEEE80211_S_SCAN) {
+                       memset(ic->ic_bss->ni_bssid, 0, IEEE80211_ADDR_LEN);
+                       rtw_set_nettype(sc, IEEE80211_M_MONITOR);
+               }
+
+               callout_reset(&sc->sc_scan_ch, rtw_dwelltime * hz / 1000,
+                             rtw_next_scan, ic);
+
+               break;
+       case IEEE80211_S_RUN:
+               switch (ic->ic_opmode) {
+               case IEEE80211_M_HOSTAP:
+               case IEEE80211_M_IBSS:
+                       rtw_set_nettype(sc, IEEE80211_M_MONITOR);
+                       /*FALLTHROUGH*/
+               case IEEE80211_M_AHDEMO:
+               case IEEE80211_M_STA:
+                       rtw_join_bss(sc, ic->ic_bss->ni_bssid,
+                                    ic->ic_bss->ni_intval);
+                       break;
+               case IEEE80211_M_MONITOR:
+                       break;
+               }
+               rtw_set_nettype(sc, ic->ic_opmode);
+               break;
+       case IEEE80211_S_ASSOC:
+       case IEEE80211_S_AUTH:
+               break;
+       }
+
+       if (nstate != IEEE80211_S_SCAN)
+               callout_stop(&sc->sc_scan_ch);
+
+       return sc->sc_mtbl.mt_newstate(ic, nstate, arg);
+}
+
+/* Extend a 32-bit TSF timestamp to a 64-bit timestamp. */
+static uint64_t
+rtw_tsf_extend(struct rtw_regs *regs, uint32_t rstamp)
+{
+       uint32_t tsftl, tsfth;
+
+       tsfth = RTW_READ(regs, RTW_TSFTRH);
+       tsftl = RTW_READ(regs, RTW_TSFTRL);
+       if (tsftl < rstamp)     /* Compensate for rollover. */
+               tsfth--;
+       return ((uint64_t)tsfth << 32) | rstamp;
+}
+
+static void
+rtw_recv_mgmt(struct ieee80211com *ic, struct mbuf *m,
+             struct ieee80211_node *ni, int subtype, int rssi, uint32_t rstamp)
+{
+       struct ifnet *ifp = &ic->ic_if;
+       struct rtw_softc *sc = ifp->if_softc;
+
+       sc->sc_mtbl.mt_recv_mgmt(ic, m, ni, subtype, rssi, rstamp);
+
+       switch (subtype) {
+       case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+       case IEEE80211_FC0_SUBTYPE_BEACON:
+               if (ic->ic_opmode == IEEE80211_M_IBSS &&
+                   ic->ic_state == IEEE80211_S_RUN) {
+                       uint64_t tsf = rtw_tsf_extend(&sc->sc_regs, rstamp);
+
+                       if (le64toh(ni->ni_tstamp.tsf) >= tsf)
+                               ieee80211_ibss_merge(ni);
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+#ifdef foo
+static struct ieee80211_node *
+rtw_node_alloc(struct ieee80211_node_table *nt)
+{
+       struct ifnet *ifp = nt->nt_ic->ic_ifp;
+       struct rtw_softc *sc = (struct rtw_softc *)ifp->if_softc;
+       struct ieee80211_node *ni = (*sc->sc_mtbl.mt_node_alloc)(nt);
+
+       DPRINTF(sc, RTW_DEBUG_NODE,
+           ("%s: alloc node %p\n", sc->sc_dev.dv_xname, ni));
+       return ni;
+}
+
+static void
+rtw_node_free(struct ieee80211_node *ni)
+{
+       struct ieee80211com *ic = ni->ni_ic;
+       struct ifnet *ifp = ic->ic_ifp;
+       struct rtw_softc *sc = (struct rtw_softc *)ifp->if_softc;
+
+       DPRINTF(sc, RTW_DEBUG_NODE,
+           ("%s: freeing node %p %s\n", sc->sc_dev.dv_xname, ni,
+           ether_sprintf(ni->ni_bssid)));
+       sc->sc_mtbl.mt_node_free(ni);
+}
+#endif
+
+static int
+rtw_media_change(struct ifnet *ifp)
+{
+       int error;
+
+       error = ieee80211_media_change(ifp);
+       if (error == ENETRESET) {
+               if ((ifp->if_flags & (IFF_RUNNING|IFF_UP)) ==
+                   (IFF_RUNNING|IFF_UP))
+                       rtw_init(ifp);          /* XXX lose error */
+               error = 0;
+       }
+       return error;
+}
+
+static void
+rtw_media_status(struct ifnet *ifp, struct ifmediareq *imr)
+{
+       struct rtw_softc *sc = ifp->if_softc;
+
+       if ((sc->sc_flags & RTW_F_ENABLED) == 0) {
+               imr->ifm_active = IFM_IEEE80211 | IFM_NONE;
+               imr->ifm_status = 0;
+               return;
+       }
+       ieee80211_media_status(ifp, imr);
+}
+
+static __inline void
+rtw_set80211methods(struct rtw_mtbl *mtbl, struct ieee80211com *ic)
+{
+       mtbl->mt_newstate = ic->ic_newstate;
+       ic->ic_newstate = rtw_newstate;
+
+       mtbl->mt_recv_mgmt = ic->ic_recv_mgmt;
+       ic->ic_recv_mgmt = rtw_recv_mgmt;
+
+#ifdef foo
+       mtbl->mt_node_free = ic->ic_node_free;
+       ic->ic_node_free = rtw_node_free;
+
+       mtbl->mt_node_alloc = ic->ic_node_alloc;
+       ic->ic_node_alloc = rtw_node_alloc;
+#endif
+
+       ic->ic_crypto.cs_key_delete = rtw_key_delete;
+       ic->ic_crypto.cs_key_set = rtw_key_set;
+       ic->ic_crypto.cs_key_update_begin = rtw_key_update_begin;
+       ic->ic_crypto.cs_key_update_end = rtw_key_update_end;
+}
+
+static __inline void
+rtw_init_radiotap(struct rtw_softc *sc)
+{
+       sc->sc_rxtap.rr_ihdr.it_len = htole16(sizeof(sc->sc_rxtapu));
+       sc->sc_rxtap.rr_ihdr.it_present = htole32(RTW_RX_RADIOTAP_PRESENT);
+
+       sc->sc_txtap.rt_ihdr.it_len = htole16(sizeof(sc->sc_txtapu));
+       sc->sc_txtap.rt_ihdr.it_present = htole32(RTW_TX_RADIOTAP_PRESENT);
+}
+
+static struct rtw_rf *
+rtw_rf_attach(struct rtw_softc *sc, enum rtw_rfchipid rfchipid, int digphy)
+{
+       rtw_rf_write_t rf_write;
+       struct rtw_rf *rf;
+
+       switch (rfchipid) {
+       default:
+               rf_write = rtw_rf_hostwrite;
+               break;
+       case RTW_RFCHIPID_INTERSIL:
+       case RTW_RFCHIPID_PHILIPS:
+       case RTW_RFCHIPID_GCT:  /* XXX a guess */
+       case RTW_RFCHIPID_RFMD:
+               rf_write = (rtw_host_rfio) ? rtw_rf_hostwrite : rtw_rf_macwrite;
+               break;
+       }
+
+       switch (rfchipid) {
+       case RTW_RFCHIPID_GCT:
+               rf = rtw_grf5101_create(&sc->sc_regs, rf_write, 0);
+               sc->sc_pwrstate_cb = rtw_maxim_pwrstate;
+               break;
+       case RTW_RFCHIPID_MAXIM:
+               rf = rtw_max2820_create(&sc->sc_regs, rf_write, 0);
+               sc->sc_pwrstate_cb = rtw_maxim_pwrstate;
+               break;
+       case RTW_RFCHIPID_PHILIPS:
+               rf = rtw_sa2400_create(&sc->sc_regs, rf_write, digphy);
+               sc->sc_pwrstate_cb = rtw_philips_pwrstate;
+               break;
+       case RTW_RFCHIPID_RFMD:
+               /* XXX RFMD has no RF constructor */
+               sc->sc_pwrstate_cb = rtw_rfmd_pwrstate;
+               /*FALLTHROUGH*/
+       default:
+               return NULL;
+       }
+       rf->rf_continuous_tx_cb =
+           (rtw_continuous_tx_cb_t)rtw_continuous_tx_enable;
+       rf->rf_continuous_tx_arg = sc;
+       return rf;
+}
+
+/* Revision C and later use a different PHY delay setting than
+ * revisions A and B.
+ */
+static uint8_t
+rtw_check_phydelay(struct rtw_regs *regs, uint32_t old_rcr)
+{
+#define REVAB (RTW_RCR_MXDMA_UNLIMITED | RTW_RCR_AICV)
+#define REVC (REVAB | RTW_RCR_RXFTH_WHOLE)
+
+       uint8_t phydelay = SHIFTIN(0x6, RTW_PHYDELAY_PHYDELAY);
+
+       RTW_WRITE(regs, RTW_RCR, REVAB);
+       RTW_WBW(regs, RTW_RCR, RTW_RCR);
+       RTW_WRITE(regs, RTW_RCR, REVC);
+
+       RTW_WBR(regs, RTW_RCR, RTW_RCR);
+       if ((RTW_READ(regs, RTW_RCR) & REVC) == REVC)
+               phydelay |= RTW_PHYDELAY_REVC_MAGIC;
+
+       RTW_WRITE(regs, RTW_RCR, old_rcr);      /* restore RCR */
+       RTW_SYNC(regs, RTW_RCR, RTW_RCR);
+
+       return phydelay;
+#undef REVC
+#undef REVAB
+}
+
+int
+rtw_attach(device_t dev)
+{
+       struct rtw_softc *sc = device_get_softc(dev);
+       struct ieee80211com *ic = &sc->sc_ic;
+       const struct ieee80211_cipher *wep_cipher;
+       struct ifnet *ifp = &ic->ic_if;
+       int rc;
+
+       wep_cipher = ieee80211_crypto_cipher(IEEE80211_CIPHER_WEP);
+       KKASSERT(wep_cipher != NULL);
+
+       memcpy(&rtw_cipher_wep, wep_cipher, sizeof(rtw_cipher_wep));
+       rtw_cipher_wep.ic_decap = rtw_wep_decap;
+
+       if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+
+       switch (RTW_READ(&sc->sc_regs, RTW_TCR) & RTW_TCR_HWVERID_MASK) {
+       case RTW_TCR_HWVERID_F:
+               sc->sc_hwverid = 'F';
+               break;
+       case RTW_TCR_HWVERID_D:
+               sc->sc_hwverid = 'D';
+               break;
+       default:
+               sc->sc_hwverid = '?';
+               break;
+       }
+
+       sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+                                               &sc->sc_irq_rid,
+                                               RF_ACTIVE | RF_SHAREABLE);
+       if (sc->sc_irq_res == NULL) {
+               device_printf(dev, "could not alloc irq res\n");
+               return ENXIO;
+       }
+
+       /* Allocate h/w desc blocks */
+       rc = rtw_desc_blk_alloc(sc);
+       if (rc)
+               goto err;
+
+       /* Allocate s/w desc blocks */
+       rc = rtw_soft_blk_alloc(sc);
+       if (rc)
+               goto err;
+
+       /* Reset the chip to a known state. */
+       rc = rtw_reset(sc);
+       if (rc) {
+               device_printf(dev, "could not reset\n");
+               goto err;
+       }
+
+       sc->sc_rcr = RTW_READ(&sc->sc_regs, RTW_RCR);
+
+       if ((sc->sc_rcr & RTW_RCR_9356SEL) != 0)
+               sc->sc_flags |= RTW_F_9356SROM;
+
+       rc = rtw_srom_read(sc);
+       if (rc)
+               goto err;
+
+       rc = rtw_srom_parse(sc);
+       if (rc) {
+               device_printf(dev, "malformed serial ROM\n");
+               goto err;
+       }
+
+       device_printf(dev, "%s PHY\n",
+                     ((sc->sc_flags & RTW_F_DIGPHY) != 0) ? "digital"
+                                                          : "analog");
+
+       device_printf(dev, "CS threshold %u\n", sc->sc_csthr);
+
+       sc->sc_rf = rtw_rf_attach(sc, sc->sc_rfchipid,
+                                 sc->sc_flags & RTW_F_DIGPHY);
+       if (sc->sc_rf == NULL) {
+               device_printf(dev, "could not attach RF\n");
+               rc = ENXIO;
+               goto err;
+       }
+
+       sc->sc_phydelay = rtw_check_phydelay(&sc->sc_regs, sc->sc_rcr);
+
+       RTW_DPRINTF(RTW_DEBUG_ATTACH,
+                   ("%s: PHY delay %d\n", ifp->if_xname, sc->sc_phydelay));
+
+       if (sc->sc_locale == RTW_LOCALE_UNKNOWN)
+               rtw_identify_country(sc);
+
+       rtw_init_channels(sc);
+
+       rc = rtw_identify_sta(sc);
+       if (rc)
+               goto err;
+
+       ifp->if_softc = sc;
+       ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
+       ifp->if_init = rtw_init;
+       ifp->if_ioctl = rtw_ioctl;
+       ifp->if_start = rtw_start;
+       ifp->if_watchdog = rtw_watchdog;
+       ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN);
+       ifq_set_ready(&ifp->if_snd);
+
+       ic->ic_phytype = IEEE80211_T_DS;
+       ic->ic_opmode = IEEE80211_M_STA;
+       ic->ic_caps = IEEE80211_C_PMGT |
+                     IEEE80211_C_IBSS |
+                     IEEE80211_C_HOSTAP |
+                     IEEE80211_C_MONITOR;
+       ic->ic_sup_rates[IEEE80211_MODE_11B] = rtw_rates_11b;
+
+       /* initialize led callout */
+       callout_init(&sc->sc_led_state.ls_fast_ch);
+       callout_init(&sc->sc_led_state.ls_slow_ch);
+
+       ic->ic_ratectl.rc_st_ratectl_cap = IEEE80211_RATECTL_CAP_ONOE;
+       ic->ic_ratectl.rc_st_ratectl = IEEE80211_RATECTL_ONOE;
+
+       /*
+        * Call MI attach routines.
+        */
+       ieee80211_ifattach(&sc->sc_ic);
+
+       /* Override some ieee80211 methods */
+       rtw_set80211methods(&sc->sc_mtbl, &sc->sc_ic);
+
+       /*
+        * possibly we should fill in our own sc_send_prresp, since
+        * the RTL8180 is probably sending probe responses in ad hoc
+        * mode.
+        */
+
+       /* complete initialization */
+       ieee80211_media_init(&sc->sc_ic, rtw_media_change, rtw_media_status);
+       callout_init(&sc->sc_scan_ch);
+
+       rtw_init_radiotap(sc);
+
+       bpfattach_dlt(ifp, DLT_IEEE802_11_RADIO,
+                     sizeof(struct ieee80211_frame) + 64, &sc->sc_radiobpf);
+
+       rc = bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE, rtw_intr, sc,
+                           &sc->sc_irq_handle, ifp->if_serializer);
+       if (rc) {
+               device_printf(dev, "can't set up interrupt\n");
+               bpfdetach(ifp);
+               ieee80211_ifdetach(ic);
+               goto err;
+       }
+
+       device_printf(dev, "hardware version %c\n", sc->sc_hwverid);
+       if (bootverbose)
+               ieee80211_announce(ic);
+       return 0;
+err:
+       rtw_detach(dev);
+       return rc;
+}
+
+int
+rtw_detach(device_t dev)
+{
+       struct rtw_softc *sc = device_get_softc(dev);
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+       if (device_is_attached(dev)) {
+               lwkt_serialize_enter(ifp->if_serializer);
+
+               rtw_stop(sc, 1);
+               sc->sc_flags |= RTW_F_INVALID;
+
+               callout_stop(&sc->sc_scan_ch);
+               bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_handle);
+
+               lwkt_serialize_exit(ifp->if_serializer);
+
+               ieee80211_ifdetach(&sc->sc_ic);
+       }
+
+       if (sc->sc_rf != NULL)
+               rtw_rf_destroy(sc->sc_rf);
+
+       if (sc->sc_srom.sr_content != NULL)
+               free(sc->sc_srom.sr_content, M_DEVBUF);
+
+       if (sc->sc_irq_res != NULL) {
+               bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
+                                    sc->sc_irq_res);
+       }
+
+       rtw_soft_blk_free(sc);
+       rtw_desc_blk_free(sc);
+       return 0;
+}
+
+static void
+rtw_desc_dma_addr(void *arg, bus_dma_segment_t *seg, int nseg, int error)
+{
+       if (error)
+               return;
+
+       KASSERT(nseg == 1, ("too many desc segments\n"));
+       *((uint32_t *)arg) = seg->ds_addr;      /* XXX bus_addr_t */
+}
+
+static int
+rtw_dma_alloc(struct rtw_softc *sc, bus_dma_tag_t *dmat, int len,
+             void **desc, uint32_t *phyaddr, bus_dmamap_t *dmamap)
+{
+       int error;
+
+       error = bus_dma_tag_create(NULL, RTW_DESC_ALIGNMENT, 0,
+                                  BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
+                                  NULL, NULL, len, 1, len, 0, dmat);
+       if (error) {
+               if_printf(&sc->sc_ic.ic_if, "could not alloc desc DMA tag");
+               return error;
+       }
+
+       error = bus_dmamem_alloc(*dmat, desc, BUS_DMA_WAITOK | BUS_DMA_ZERO,
+                                dmamap);
+       if (error) {
+               if_printf(&sc->sc_ic.ic_if, "could not alloc desc DMA mem");
+               return error;
+       }
+
+       error = bus_dmamap_load(*dmat, *dmamap, *desc, len,
+                               rtw_desc_dma_addr, phyaddr, BUS_DMA_WAITOK);
+       if (error) {
+               if_printf(&sc->sc_ic.ic_if, "could not load desc DMA mem");
+               bus_dmamem_free(*dmat, *desc, *dmamap);
+               *desc = NULL;
+               return error;
+       }
+       return 0;
+}
+
+static void
+rtw_dma_free(struct rtw_softc *sc __unused, bus_dma_tag_t *dmat, void **desc,
+            bus_dmamap_t *dmamap)
+{
+       if (*desc != NULL) {
+               bus_dmamap_unload(*dmat, *dmamap);
+               bus_dmamem_free(*dmat, *desc, *dmamap);
+               *desc = NULL;
+       }
+
+       if (*dmat != NULL) {
+               bus_dma_tag_destroy(*dmat);
+               *dmat = NULL;
+       }
+}
+
+static void
+rtw_txdesc_blk_free(struct rtw_softc *sc, int q_no)
+{
+       struct rtw_txdesc_blk *tdb = &sc->sc_txdesc_blk[q_no];
+
+       rtw_dma_free(sc, &tdb->tdb_dmat, (void **)&tdb->tdb_desc,
+                    &tdb->tdb_dmamap);
+}
+
+static int
+rtw_txdesc_blk_alloc(struct rtw_softc *sc, int q_len, int q_no,
+                    bus_size_t q_basereg)
+{
+       struct rtw_txdesc_blk *tdb = &sc->sc_txdesc_blk[q_no];
+       int i, error;
+
+       /*
+        * Setup TX h/w desc
+        */
+       error = rtw_dma_alloc(sc, &tdb->tdb_dmat,
+                             q_len * sizeof(*tdb->tdb_desc),
+                             (void **)&tdb->tdb_desc, &tdb->tdb_base,
+                             &tdb->tdb_dmamap);
+       if (error) {
+               printf("%dth tx\n", q_no);
+               return error;
+       }
+       tdb->tdb_basereg = q_basereg;
+
+       tdb->tdb_ndesc = q_len;
+       for (i = 0; i < tdb->tdb_ndesc; ++i)
+               tdb->tdb_desc[i].td_next = htole32(RTW_NEXT_DESC(tdb, i));
+
+       return 0;
+}
+
+static void
+rtw_rxdesc_blk_free(struct rtw_softc *sc)
+{
+       struct rtw_rxdesc_blk *rdb = &sc->sc_rxdesc_blk;
+
+       rtw_dma_free(sc, &rdb->rdb_dmat, (void **)&rdb->rdb_desc,
+                    &rdb->rdb_dmamap);
+}
+
+static int
+rtw_rxdesc_blk_alloc(struct rtw_softc *sc, int q_len)
+{
+       struct rtw_rxdesc_blk *rdb = &sc->sc_rxdesc_blk;
+       int error;
+
+       /*
+        * Setup RX h/w desc
+        */
+       error = rtw_dma_alloc(sc, &rdb->rdb_dmat,
+                             q_len * sizeof(*rdb->rdb_desc),
+                             (void **)&rdb->rdb_desc, &rdb->rdb_base,
+                             &rdb->rdb_dmamap);
+       if (error) {
+               printf("rx\n");
+       } else {
+               rdb->rdb_ndesc = q_len;
+       }
+
+       return error;
+}
+
+static void
+rtw_txsoft_blk_free(struct rtw_softc *sc, int n_sd, int q_no)
+{
+       struct rtw_txsoft_blk *tsb = &sc->sc_txsoft_blk[q_no];
+
+       if (tsb->tsb_desc != NULL) {
+               int i;
+
+               for (i = 0; i < n_sd; ++i) {
+                       bus_dmamap_destroy(sc->sc_txsoft_dmat,
+                                          tsb->tsb_desc[i].ts_dmamap);
+               }
+               free(tsb->tsb_desc, M_DEVBUF);
+               tsb->tsb_desc = NULL;
+       }
+}
+
+static int
+rtw_txsoft_blk_alloc(struct rtw_softc *sc, int q_len, int q_no, uint8_t q_poll)
+{
+       struct rtw_txsoft_blk *tsb = &sc->sc_txsoft_blk[q_no];
+       int i, error;
+
+       STAILQ_INIT(&tsb->tsb_dirtyq);
+       STAILQ_INIT(&tsb->tsb_freeq);
+       tsb->tsb_ndesc = q_len;
+       tsb->tsb_desc = malloc(q_len * sizeof(*tsb->tsb_desc), M_DEVBUF,
+                              M_WAITOK | M_ZERO);
+       tsb->tsb_poll = q_poll;
+
+       for (i = 0; i < tsb->tsb_ndesc; ++i) {
+               error = bus_dmamap_create(sc->sc_txsoft_dmat, 0,
+                                         &tsb->tsb_desc[i].ts_dmamap);
+               if (error) {
+                       if_printf(&sc->sc_ic.ic_if, "could not create DMA map "
+                                 "for soft tx desc\n");
+                       rtw_txsoft_blk_free(sc, i, q_no);
+                       return error;
+               }
+       }
+       return 0;
+}
+
+static void
+rtw_rxsoft_blk_free(struct rtw_softc *sc, int n_sd)
+{
+       if (sc->sc_rxsoft_free) {
+               int i;
+
+               for (i = 0; i < n_sd; ++i) {
+                       bus_dmamap_destroy(sc->sc_rxsoft_dmat,
+                                          sc->sc_rxsoft[i].rs_dmamap);
+               }
+               sc->sc_rxsoft_free = 0;
+       }
+}
+
+static int
+rtw_rxsoft_blk_alloc(struct rtw_softc *sc, int q_len)
+{
+       int i, error;
+
+       sc->sc_rxsoft_free = 1;
+
+       /*
+        * Setup RX s/w desc
+        */
+       for (i = 0; i < q_len; ++i) {
+               error = bus_dmamap_create(sc->sc_rxsoft_dmat, 0,
+                                         &sc->sc_rxsoft[i].rs_dmamap);
+               if (error) {
+                       if_printf(&sc->sc_ic.ic_if, "could not create DMA map "
+                                 "for soft rx desc\n");
+                       rtw_rxsoft_blk_free(sc, i);
+                       return error;
+               }
+       }
+       return 0;
+}
+
+#define TXQ_PARAM(q, poll, breg)                       \
+       [RTW_TXPRI ## q] = {                            \
+               .txq_len        = RTW_TXQLEN ## q,      \
+               .txq_poll       = poll,                 \
+               .txq_basereg    = breg                  \
+       }
+static const struct {
+       int             txq_len;
+       uint8_t         txq_poll;
+       bus_size_t      txq_basereg;
+} txq_params[RTW_NTXPRI] = {
+       TXQ_PARAM(LO, RTW_TPPOLL_LPQ | RTW_TPPOLL_SLPQ, RTW_TLPDA),
+       TXQ_PARAM(MD, RTW_TPPOLL_NPQ | RTW_TPPOLL_SNPQ, RTW_TNPDA),
+       TXQ_PARAM(HI, RTW_TPPOLL_HPQ | RTW_TPPOLL_SHPQ, RTW_THPDA),
+       TXQ_PARAM(BCN, RTW_TPPOLL_BQ | RTW_TPPOLL_SBQ, RTW_TBDA)
+};
+#undef TXQ_PARAM
+
+static int
+rtw_desc_blk_alloc(struct rtw_softc *sc)
+{
+       int i, error;
+
+       /* Create h/w TX desc */
+       for (i = 0; i < RTW_NTXPRI; ++i) {
+               error = rtw_txdesc_blk_alloc(sc, txq_params[i].txq_len, i,
+                                            txq_params[i].txq_basereg);
+               if (error)
+                       return error;
+       }
+
+       /* Create h/w RX desc */
+       return rtw_rxdesc_blk_alloc(sc, RTW_RXQLEN);
+}
+
+static void
+rtw_desc_blk_free(struct rtw_softc *sc)
+{
+       int i;
+
+       for (i = 0; i < RTW_NTXPRI; ++i)
+               rtw_txdesc_blk_free(sc, i);
+       rtw_rxdesc_blk_free(sc);
+}
+
+static int
+rtw_soft_blk_alloc(struct rtw_softc *sc)
+{
+       int i, error;
+
+       /* Create DMA tag for TX mbuf */
+       error = bus_dma_tag_create(NULL, 1, 0,
+                                  BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
+                                  NULL, NULL,
+                                  MCLBYTES, RTW_MAXPKTSEGS, MCLBYTES,
+                                  0, &sc->sc_txsoft_dmat);
+       if (error) {
+               if_printf(&sc->sc_ic.ic_if, "could not alloc txsoft DMA tag\n");
+               return error;
+       }
+
+       /* Create DMA tag for RX mbuf */
+       error = bus_dma_tag_create(NULL, 1, 0,
+                                  BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
+                                  NULL, NULL,
+                                  MCLBYTES, 1, MCLBYTES,
+                                  0, &sc->sc_rxsoft_dmat);
+       if (error) {
+               if_printf(&sc->sc_ic.ic_if, "could not alloc rxsoft DMA tag\n");
+               return error;
+       }
+
+       /* Create a spare DMA map for RX mbuf */
+       error = bus_dmamap_create(sc->sc_rxsoft_dmat, 0, &sc->sc_rxsoft_dmamap);
+       if (error) {
+               if_printf(&sc->sc_ic.ic_if, "could not alloc spare rxsoft "
+                         "DMA map\n");
+               bus_dma_tag_destroy(sc->sc_rxsoft_dmat);
+               sc->sc_rxsoft_dmat = NULL;
+               return error;
+       }
+
+       /* Create s/w TX desc */
+       for (i = 0; i < RTW_NTXPRI; ++i) {
+               error = rtw_txsoft_blk_alloc(sc, txq_params[i].txq_len, i,
+                                            txq_params[i].txq_poll);
+               if (error)
+                       return error;
+       }
+
+       /* Create s/w RX desc */
+       return rtw_rxsoft_blk_alloc(sc, RTW_RXQLEN);
+}
+
+static void
+rtw_soft_blk_free(struct rtw_softc *sc)
+{
+       int i;
+
+       for (i = 0; i < RTW_NTXPRI; ++i)
+               rtw_txsoft_blk_free(sc, txq_params[i].txq_len, i);
+
+       rtw_rxsoft_blk_free(sc, RTW_RXQLEN);
+
+       if (sc->sc_txsoft_dmat != NULL) {
+               bus_dma_tag_destroy(sc->sc_txsoft_dmat);
+               sc->sc_txsoft_dmat = NULL;
+       }
+
+       if (sc->sc_rxsoft_dmat != NULL) {
+               bus_dmamap_destroy(sc->sc_rxsoft_dmat, sc->sc_rxsoft_dmamap);
+               bus_dma_tag_destroy(sc->sc_rxsoft_dmat);
+               sc->sc_rxsoft_dmat = NULL;
+       }
+}
+
+/*
+ * Arguments in:
+ *
+ * paylen:  payload length (no FCS, no WEP header)
+ *
+ * hdrlen:  header length
+ *
+ * rate:    MSDU speed, units 500kb/s
+ *
+ * flags:   IEEE80211_F_SHPREAMBLE (use short preamble),
+ *          IEEE80211_F_SHSLOT (use short slot length)
+ *
+ * Arguments out:
+ *
+ * d:       802.11 Duration field for RTS,
+ *          802.11 Duration field for data frame,
+ *          PLCP Length for data frame,
+ *          residual octets at end of data slot
+ */
+static int
+rtw_compute_duration1(int len, int use_ack, uint32_t icflags, int rate,
+                     struct rtw_duration *d)
+{
+       int pre, ctsrate;
+       int ack, bitlen, data_dur, remainder;
+
+       /*
+        * RTS reserves medium for SIFS | CTS | SIFS | (DATA) | SIFS | ACK
+        * DATA reserves medium for SIFS | ACK
+        *
+        * XXXMYC: no ACK on multicast/broadcast or control packets
+        */
+
+       bitlen = len * 8;
+
+       pre = IEEE80211_DUR_DS_SIFS;
+       if (icflags & IEEE80211_F_SHPREAMBLE) {
+               pre += IEEE80211_DUR_DS_SHORT_PREAMBLE +
+                      IEEE80211_DUR_DS_FAST_PLCPHDR;
+       } else {
+               pre += IEEE80211_DUR_DS_LONG_PREAMBLE +
+                      IEEE80211_DUR_DS_SLOW_PLCPHDR;
+       }
+
+       d->d_residue = 0;
+       data_dur = (bitlen * 2) / rate;
+       remainder = (bitlen * 2) % rate;
+       if (remainder != 0) {
+               d->d_residue = (rate - remainder) / 16;
+               data_dur++;
+       }
+
+       switch (rate) {
+       case 2:         /* 1 Mb/s */
+       case 4:         /* 2 Mb/s */
+               /* 1 - 2 Mb/s WLAN: send ACK/CTS at 1 Mb/s */
+               ctsrate = 2;
+               break;
+       case 11:        /* 5.5 Mb/s */
+       case 22:        /* 11  Mb/s */
+       case 44:        /* 22  Mb/s */
+               /* 5.5 - 11 Mb/s WLAN: send ACK/CTS at 2 Mb/s */
+               ctsrate = 4;
+               break;
+       default:
+               /* TBD */
+               return -1;
+       }
+
+       d->d_plcp_len = data_dur;
+
+       ack = (use_ack) ? pre + (IEEE80211_DUR_DS_SLOW_ACK * 2) / ctsrate : 0;
+
+       d->d_rts_dur = pre + (IEEE80211_DUR_DS_SLOW_CTS * 2) / ctsrate +
+                      pre + data_dur +
+                      ack;
+
+       d->d_data_dur = ack;
+       return 0;
+}
+
+/*
+ * Arguments in:
+ *
+ * wh:      802.11 header
+ *
+ * paylen:  payload length (no FCS, no WEP header)
+ *
+ * rate:    MSDU speed, units 500kb/s
+ *
+ * fraglen: fragment length, set to maximum (or higher) for no
+ *          fragmentation
+ *
+ * flags:   IEEE80211_F_PRIVACY (hardware adds WEP),
+ *          IEEE80211_F_SHPREAMBLE (use short preamble),
+ *          IEEE80211_F_SHSLOT (use short slot length)
+ *
+ * Arguments out:
+ *
+ * d0: 802.11 Duration fields (RTS/Data), PLCP Length, Service fields
+ *     of first/only fragment
+ *
+ * dn: 802.11 Duration fields (RTS/Data), PLCP Length, Service fields
+ *     of last fragment
+ *
+ * rtw_compute_duration assumes crypto-encapsulation, if any,
+ * has already taken place.
+ */
+static int
+rtw_compute_duration(const struct ieee80211_frame_min *wh,
+                    const struct ieee80211_key *wk, int len,
+                    uint32_t icflags, int fraglen, int rate,
+                    struct rtw_duration *d0, struct rtw_duration *dn,
+                    int *npktp, int debug)
+{
+       int ack, rc;
+       int cryptolen,  /* crypto overhead: header+trailer */
+           firstlen,   /* first fragment's payload + overhead length */
+           hdrlen,     /* header length w/o driver padding */
+           lastlen,    /* last fragment's payload length w/ overhead */
+           lastlen0,   /* last fragment's payload length w/o overhead */
+           npkt,       /* number of fragments */
+           overlen,    /* non-802.11 header overhead per fragment */
+           paylen;     /* payload length w/o overhead */
+
+       hdrlen = ieee80211_anyhdrsize((const void *)wh);
+
+        /* Account for padding required by the driver. */
+       if (icflags & IEEE80211_F_DATAPAD)
+               paylen = len - roundup(hdrlen, sizeof(u_int32_t));
+       else
+               paylen = len - hdrlen;
+
+       overlen = IEEE80211_CRC_LEN;
+
+       if (wk != NULL) {
+               cryptolen = wk->wk_cipher->ic_header +
+                           wk->wk_cipher->ic_trailer;
+               paylen -= cryptolen;
+               overlen += cryptolen;
+       }
+
+       npkt = paylen / fraglen;
+       lastlen0 = paylen % fraglen;
+
+       if (npkt == 0) {                /* no fragments */
+               lastlen = paylen + overlen;
+       } else if (lastlen0 != 0) {     /* a short "tail" fragment */
+               lastlen = lastlen0 + overlen;
+               npkt++;
+       } else {                        /* full-length "tail" fragment */
+               lastlen = fraglen + overlen;
+       }
+
+       if (npktp != NULL)
+               *npktp = npkt;
+
+       if (npkt > 1)
+               firstlen = fraglen + overlen;
+       else
+               firstlen = paylen + overlen;
+
+       if (debug) {
+               printf("%s: npkt %d firstlen %d lastlen0 %d lastlen %d "
+                   "fraglen %d overlen %d len %d rate %d icflags %08x\n",
+                   __func__, npkt, firstlen, lastlen0, lastlen, fraglen,
+                   overlen, len, rate, icflags);
+       }
+
+       ack = (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+              (wh->i_fc[1] & IEEE80211_FC0_TYPE_MASK) !=
+              IEEE80211_FC0_TYPE_CTL);
+
+       rc = rtw_compute_duration1(firstlen + hdrlen, ack, icflags, rate, d0);
+       if (rc == -1)
+               return rc;
+
+       if (npkt <= 1) {
+               *dn = *d0;
+               return 0;
+       }
+       return rtw_compute_duration1(lastlen + hdrlen, ack, icflags, rate, dn);
+}
diff --git a/sys/dev/netif/rtw/rtwbitop.h b/sys/dev/netif/rtw/rtwbitop.h
new file mode 100644 (file)
index 0000000..2657285
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2004, 2005 David Young.  All rights reserved.
+ *
+ * Programmed for NetBSD by David Young.
+ *
+ * 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.
+ *
+ * $DragonFly: src/sys/dev/netif/rtw/Attic/rtwbitop.h,v 1.1 2006/09/03 07:37:58 sephe Exp $
+ */
+
+/*
+ * BIT(n): Return a bitmask with bit m set, where the least
+ *         significant bit is bit 0.
+ *
+ * BITS(m, n): Return a bitmask with bits m through n, inclusive,
+ *             set.  It does not matter whether m>n or m<=n.
+ *             The least significant bit is bit 0.
+ *
+ * A "bitfield" is a span of consecutive bits defined by a
+ * bitmask, where 1s select the bits in the bitfield.  SHIFTIN,
+ * SHIFTOUT, and SHIFTOUT_MASK help read and write bitfields
+ * from device registers.
+ *
+ * SHIFTIN(v, mask): Left-shift bits `v' into the bitfield
+ *                   defined by `mask', and return them.  No
+ *                   side-effects.
+ *
+ * SHIFTOUT(v, mask): Extract and return the bitfield selected
+ *                    by `mask' from `v', right-shifting the
+ *                    bits so that the rightmost selected bit
+ *                    is at bit 0.  No side-effects.
+ *
+ * SHIFTOUT_MASK(mask): Right-shift the bits in `mask' so that
+ *                      the rightmost non-zero bit is at bit
+ *                      0.  This is useful for finding the
+ *                      greatest unsigned value that a bitfield
+ *                      can hold.  No side-effects.  Note that
+ *                      SHIFTOUT_MASK(m) = SHIFTOUT(m, m).
+ */
+
+#ifndef _RTWBITOP_H
+#define _RTWBITOP_H
+
+/* __BIT(n): nth bit, where __BIT(0) == 0x1. */
+#define        __BIT(__n) (((__n) == 32) ? 0 : ((uint32_t)1 << (__n)))
+
+/* __BITS(m, n): bits m through n, m < n. */
+#define        __BITS(__m, __n)        \
+       ((__BIT(MAX((__m), (__n)) + 1) - 1) ^ (__BIT(MIN((__m), (__n))) - 1))
+
+/* find least significant bit that is set */
+#define        __LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask))
+
+#define        SHIFTOUT(__x, __mask) (((__x) & (__mask)) / __LOWEST_SET_BIT(__mask))
+#define        SHIFTIN(__x, __mask) ((__x) * __LOWEST_SET_BIT(__mask))
+#define        SHIFTOUT_MASK(__mask) SHIFTOUT((__mask), (__mask))
+
+#endif /* !_RTWBITOP_H */
diff --git a/sys/dev/netif/rtw/rtwphy.c b/sys/dev/netif/rtw/rtwphy.c
new file mode 100644 (file)
index 0000000..2e69fba
--- /dev/null
@@ -0,0 +1,812 @@
+/*
+ * Copyright (c) 2004, 2005 David Young.  All rights reserved.
+ *
+ * Programmed for NetBSD by David Young.
+ *
+ * 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: rtwphy.c,v 1.9 2006/03/08 00:24:06 dyoung Exp $
+ * $DragonFly: src/sys/dev/netif/rtw/rtwphy.c,v 1.1 2006/09/03 07:37:58 sephe Exp $
+ */
+
+/*
+ * Control the Philips SA2400 RF front-end and the baseband processor
+ * built into the Realtek RTL8180.
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/socket.h>
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_media.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+#include <netproto/802_11/ieee80211_radiotap.h>
+
+#include "rtwbitop.h"
+#include "rtwreg.h"
+#include "max2820reg.h"
+#include "sa2400reg.h"
+#include "rtwvar.h"
+#include "rtwphyio.h"
+#include "rtwphy.h"
+
+static int     rtw_max2820_pwrstate(struct rtw_rf *, enum rtw_pwrstate);
+static int     rtw_sa2400_pwrstate(struct rtw_rf *, enum rtw_pwrstate);
+
+#define        GCT_WRITE(__gr, __addr, __val, __label)                 \
+do {                                                           \
+       if (rtw_rfbus_write(&(__gr)->gr_bus, RTW_RFCHIPID_GCT,  \
+           (__addr), (__val)) == -1)                           \
+               goto __label;                                   \
+} while(0)
+
+static int
+rtw_bbp_preinit(struct rtw_regs *regs, u_int antatten0, int dflantb, u_int freq)
+{
+       u_int antatten = antatten0;
+
+       if (dflantb)
+               antatten |= RTW_BBP_ANTATTEN_DFLANTB;
+       if (freq == 2484) /* channel 14 */
+               antatten |= RTW_BBP_ANTATTEN_CHAN14;
+       return rtw_bbp_write(regs, RTW_BBP_ANTATTEN, antatten);
+}
+
+static int
+rtw_bbp_init(struct rtw_regs *regs, struct rtw_bbpset *bb, int antdiv,
+            int dflantb, uint8_t cs_threshold, u_int freq)
+{
+       int rc;
+       uint32_t sys2, sys3;
+
+       sys2 = bb->bb_sys2;
+       if (antdiv)
+               sys2 |= RTW_BBP_SYS2_ANTDIV;
+       sys3 = bb->bb_sys3 | SHIFTIN(cs_threshold, RTW_BBP_SYS3_CSTHRESH_MASK);
+
+#define        RTW_BBP_WRITE_OR_RETURN(reg, val) \
+       if ((rc = rtw_bbp_write(regs, reg, val)) != 0) \
+               return rc;
+
+       RTW_BBP_WRITE_OR_RETURN(RTW_BBP_SYS1,           bb->bb_sys1);
+       RTW_BBP_WRITE_OR_RETURN(RTW_BBP_TXAGC,          bb->bb_txagc);
+       RTW_BBP_WRITE_OR_RETURN(RTW_BBP_LNADET,         bb->bb_lnadet);
+       RTW_BBP_WRITE_OR_RETURN(RTW_BBP_IFAGCINI,       bb->bb_ifagcini);
+       RTW_BBP_WRITE_OR_RETURN(RTW_BBP_IFAGCLIMIT,     bb->bb_ifagclimit);
+       RTW_BBP_WRITE_OR_RETURN(RTW_BBP_IFAGCDET,       bb->bb_ifagcdet);
+
+       if ((rc = rtw_bbp_preinit(regs, bb->bb_antatten, dflantb, freq)) != 0)
+               return rc;
+
+       RTW_BBP_WRITE_OR_RETURN(RTW_BBP_TRL,            bb->bb_trl);
+       RTW_BBP_WRITE_OR_RETURN(RTW_BBP_SYS2,           sys2);
+       RTW_BBP_WRITE_OR_RETURN(RTW_BBP_SYS3,           sys3);
+       RTW_BBP_WRITE_OR_RETURN(RTW_BBP_CHESTLIM,       bb->bb_chestlim);
+       RTW_BBP_WRITE_OR_RETURN(RTW_BBP_CHSQLIM,        bb->bb_chsqlim);
+       return 0;
+}
+
+static int
+rtw_sa2400_txpower(struct rtw_rf *rf, uint8_t opaque_txpower)
+{
+       struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf;
+       struct rtw_rfbus *bus = &sa->sa_bus;
+
+       return rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_TX,
+                              opaque_txpower);
+}
+
+/* make sure we're using the same settings as the reference driver */
+static void
+verify_syna(u_int freq, uint32_t val)
+{
+       uint32_t expected_val = ~val;
+
+       switch (freq) {
+       case 2412:
+               expected_val = 0x0000096c; /* ch 1 */
+               break;
+       case 2417:
+               expected_val = 0x00080970; /* ch 2 */
+               break;
+       case 2422:
+               expected_val = 0x00100974; /* ch 3 */
+               break;
+       case 2427:
+               expected_val = 0x00180978; /* ch 4 */
+               break;
+       case 2432:
+               expected_val = 0x00000980; /* ch 5 */
+               break;
+       case 2437:
+               expected_val = 0x00080984; /* ch 6 */
+               break;
+       case 2442:
+               expected_val = 0x00100988; /* ch 7 */
+               break;
+       case 2447:
+               expected_val = 0x0018098c; /* ch 8 */
+               break;
+       case 2452:
+               expected_val = 0x00000994; /* ch 9 */
+               break;
+       case 2457:
+               expected_val = 0x00080998; /* ch 10 */
+               break;
+       case 2462:
+               expected_val = 0x0010099c; /* ch 11 */
+               break;
+       case 2467:
+               expected_val = 0x001809a0; /* ch 12 */
+               break;
+        case 2472:
+               expected_val = 0x000009a8; /* ch 13 */
+               break;
+        case 2484:
+               expected_val = 0x000009b4; /* ch 14 */
+               break;
+       }
+       KKASSERT(val == expected_val);
+}
+
+/* freq is in MHz */
+static int
+rtw_sa2400_tune(struct rtw_rf *rf, u_int freq)
+{
+       struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf;
+       struct rtw_rfbus *bus = &sa->sa_bus;
+       int rc;
+       uint32_t syna, synb, sync;
+       int n, nf;
+
+       /*
+        * XO = 44MHz, R = 11, hence N is in units of XO / R = 4MHz.
+        *
+        * The channel spacing (5MHz) is not divisible by 4MHz, so
+        * we set the fractional part of N to compensate.
+        */
+       n = freq / 4;
+       nf = (freq % 4) * 2;
+
+       syna = SHIFTIN(nf, SA2400_SYNA_NF_MASK) |
+              SHIFTIN(n, SA2400_SYNA_N_MASK);
+       verify_syna(freq, syna);
+
+       /*
+        * Divide the 44MHz crystal down to 4MHz. Set the fractional
+        * compensation charge pump value to agree with the fractional
+        * modulus.
+        */
+       synb = SHIFTIN(11, SA2400_SYNB_R_MASK) | SA2400_SYNB_L_NORMAL |
+              SA2400_SYNB_ON | SA2400_SYNB_ONE |
+              SHIFTIN(80, SA2400_SYNB_FC_MASK); /* agrees w/ SA2400_SYNA_FM = 0 */
+
+       sync = SA2400_SYNC_CP_NORMAL;
+
+       rc = rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_SYNA, syna);
+       if (rc != 0)
+               return rc;
+
+       rc = rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_SYNB, synb);
+       if (rc != 0)
+               return rc;
+
+       rc = rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_SYNC, sync);
+       if (rc != 0)
+               return rc;
+
+       return rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_SYND, 0x0);
+}
+
+static int
+rtw_sa2400_pwrstate(struct rtw_rf *rf, enum rtw_pwrstate power)
+{
+       struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf;
+       struct rtw_rfbus *bus = &sa->sa_bus;
+       uint32_t opmode;
+
+       opmode = SA2400_OPMODE_DEFAULTS;
+       switch (power) {
+       case RTW_ON:
+               opmode |= SA2400_OPMODE_MODE_TXRX;
+               break;
+       case RTW_SLEEP:
+               opmode |= SA2400_OPMODE_MODE_WAIT;
+               break;
+       case RTW_OFF:
+               opmode |= SA2400_OPMODE_MODE_SLEEP;
+               break;
+       }
+
+       if (sa->sa_digphy)
+               opmode |= SA2400_OPMODE_DIGIN;
+
+       return rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_OPMODE,
+                              opmode);
+}
+
+static int
+rtw_sa2400_manrx_init(struct rtw_sa2400 *sa)
+{
+       uint32_t manrx;
+
+       /*
+        * XXX we are not supposed to be in RXMGC mode when we do this?
+        */
+       manrx = SA2400_MANRX_AHSN;
+       manrx |= SA2400_MANRX_TEN;
+       manrx |= SHIFTIN(1023, SA2400_MANRX_RXGAIN_MASK);
+
+       return rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_MANRX,
+                              manrx);
+}
+
+static int
+rtw_sa2400_vcocal_start(struct rtw_sa2400 *sa, int start)
+{
+       uint32_t opmode;
+
+       opmode = SA2400_OPMODE_DEFAULTS;
+       if (start)
+               opmode |= SA2400_OPMODE_MODE_VCOCALIB;
+       else
+               opmode |= SA2400_OPMODE_MODE_SLEEP;
+
+       if (sa->sa_digphy)
+               opmode |= SA2400_OPMODE_DIGIN;
+
+       return rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_OPMODE,
+                              opmode);
+}
+
+static int
+rtw_sa2400_vco_calibration(struct rtw_sa2400 *sa)
+{
+       int rc;
+
+       /* calibrate VCO */
+       if ((rc = rtw_sa2400_vcocal_start(sa, 1)) != 0)
+               return rc;
+       DELAY(2200);    /* 2.2 milliseconds */
+       /* XXX superfluous: SA2400 automatically entered SLEEP mode. */
+       return rtw_sa2400_vcocal_start(sa, 0);
+}
+
+static int
+rtw_sa2400_filter_calibration(struct rtw_sa2400 *sa)
+{
+       uint32_t opmode;
+
+       opmode = SA2400_OPMODE_DEFAULTS | SA2400_OPMODE_MODE_FCALIB;
+       if (sa->sa_digphy)
+               opmode |= SA2400_OPMODE_DIGIN;
+
+       return rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_OPMODE,
+                              opmode);
+}
+
+static int
+rtw_sa2400_dc_calibration(struct rtw_sa2400 *sa)
+{
+       struct rtw_rf *rf = &sa->sa_rf;
+       int rc;
+       uint32_t dccal;
+
+       rf->rf_continuous_tx_cb(rf->rf_continuous_tx_arg, 1);
+
+       dccal = SA2400_OPMODE_DEFAULTS | SA2400_OPMODE_MODE_TXRX;
+
+       rc = rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_OPMODE,
+                            dccal);
+       if (rc != 0)
+               return rc;
+
+       /*
+        * DCALIB after being in Tx mode for 5 microseconds
+        */
+       DELAY(5);
+
+       dccal &= ~SA2400_OPMODE_MODE_MASK;
+       dccal |= SA2400_OPMODE_MODE_DCALIB;
+
+       rc = rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_OPMODE,
+                            dccal);
+       if (rc != 0)
+               return rc;
+
+       DELAY(20);      /* calibration takes at most 20 microseconds */
+
+       rf->rf_continuous_tx_cb(rf->rf_continuous_tx_arg, 0);
+       return 0;
+}
+
+static int
+rtw_sa2400_agc_init(struct rtw_sa2400 *sa)
+{
+       uint32_t agc;
+
+       agc = SHIFTIN(25, SA2400_AGC_MAXGAIN_MASK);
+       agc |= SHIFTIN(7, SA2400_AGC_BBPDELAY_MASK);
+       agc |= SHIFTIN(15, SA2400_AGC_LNADELAY_MASK);
+       agc |= SHIFTIN(27, SA2400_AGC_RXONDELAY_MASK);
+
+       return rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_AGC,
+                              agc);
+}
+
+static void
+rtw_sa2400_destroy(struct rtw_rf *rf)
+{
+       struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf;
+
+       memset(sa, 0, sizeof(*sa));
+       free(sa, M_DEVBUF);
+}
+
+static int
+rtw_sa2400_calibrate(struct rtw_rf *rf, u_int freq)
+{
+       struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf;
+       int i, rc;
+
+       /* XXX reference driver calibrates VCO twice. Is it a bug? */
+       for (i = 0; i < 2; i++) {
+               if ((rc = rtw_sa2400_vco_calibration(sa)) != 0)
+                       return rc;
+       }
+       /* VCO calibration erases synthesizer registers, so re-tune */
+       if ((rc = rtw_sa2400_tune(rf, freq)) != 0)
+               return rc;
+       if ((rc = rtw_sa2400_filter_calibration(sa)) != 0)
+               return rc;
+       /* analog PHY needs DC calibration */
+       if (!sa->sa_digphy)
+               return rtw_sa2400_dc_calibration(sa);
+       return 0;
+}
+
+static int
+rtw_sa2400_init(struct rtw_rf *rf, u_int freq, uint8_t opaque_txpower,
+               enum rtw_pwrstate power)
+{
+       struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf;
+       int rc;
+
+       if ((rc = rtw_sa2400_txpower(rf, opaque_txpower)) != 0)
+               return rc;
+
+       /* skip configuration if it's time to sleep or to power-down. */
+       if (power == RTW_SLEEP || power == RTW_OFF)
+               return rtw_sa2400_pwrstate(rf, power);
+
+       /* go to sleep for configuration */
+       if ((rc = rtw_sa2400_pwrstate(rf, RTW_SLEEP)) != 0)
+               return rc;
+
+       if ((rc = rtw_sa2400_tune(rf, freq)) != 0)
+               return rc;
+       if ((rc = rtw_sa2400_agc_init(sa)) != 0)
+               return rc;
+       if ((rc = rtw_sa2400_manrx_init(sa)) != 0)
+               return rc;
+       if ((rc = rtw_sa2400_calibrate(rf, freq)) != 0)
+               return rc;
+
+       /* enter Tx/Rx mode */
+       return rtw_sa2400_pwrstate(rf, power);
+}
+
+struct rtw_rf *
+rtw_sa2400_create(struct rtw_regs *regs, rtw_rf_write_t rf_write, int digphy)
+{
+       struct rtw_sa2400 *sa;
+       struct rtw_rfbus *bus;
+       struct rtw_rf *rf;
+       struct rtw_bbpset *bb;
+
+       sa = malloc(sizeof(*sa), M_DEVBUF, M_WAITOK | M_ZERO);
+
+       sa->sa_digphy = digphy;
+
+       rf = &sa->sa_rf;
+       bus = &sa->sa_bus;
+
+       rf->rf_init = rtw_sa2400_init;
+       rf->rf_destroy = rtw_sa2400_destroy;
+       rf->rf_txpower = rtw_sa2400_txpower;
+       rf->rf_tune = rtw_sa2400_tune;
+       rf->rf_pwrstate = rtw_sa2400_pwrstate;
+       bb = &rf->rf_bbpset;
+
+       /* XXX magic */
+       bb->bb_antatten = RTW_BBP_ANTATTEN_PHILIPS_MAGIC;
+       bb->bb_chestlim =       0x00;
+       bb->bb_chsqlim =        0xa0;
+       bb->bb_ifagcdet =       0x64;
+       bb->bb_ifagcini =       0x90;
+       bb->bb_ifagclimit =     0x1a;
+       bb->bb_lnadet =         0xe0;
+       bb->bb_sys1 =           0x98;
+       bb->bb_sys2 =           0x47;
+       bb->bb_sys3 =           0x90;
+       bb->bb_trl =            0x88;
+       bb->bb_txagc =          0x38;
+
+       bus->b_regs = regs;
+       bus->b_write = rf_write;
+
+       return &sa->sa_rf;
+}
+
+static int
+rtw_grf5101_txpower(struct rtw_rf *rf, uint8_t opaque_txpower)
+{
+       struct rtw_grf5101 *gr = (struct rtw_grf5101 *)rf;
+
+       GCT_WRITE(gr, 0x15, 0, err);
+       GCT_WRITE(gr, 0x06, opaque_txpower, err);
+       GCT_WRITE(gr, 0x15, 0x10, err);
+       GCT_WRITE(gr, 0x15, 0x00, err);
+       return 0;
+err:
+       return -1;
+}
+
+static int
+rtw_grf5101_pwrstate(struct rtw_rf *rf, enum rtw_pwrstate power)
+{
+       struct rtw_grf5101 *gr = (struct rtw_grf5101 *)rf;
+
+       switch (power) {
+       case RTW_OFF:
+       case RTW_SLEEP:
+               GCT_WRITE(gr, 0x07, 0x0000, err);
+               GCT_WRITE(gr, 0x1f, 0x0045, err);
+               GCT_WRITE(gr, 0x1f, 0x0005, err);
+               GCT_WRITE(gr, 0x00, 0x08e4, err);
+       default:
+               break;
+       case RTW_ON:
+               GCT_WRITE(gr, 0x1f, 0x0001, err);
+               DELAY(10);
+               GCT_WRITE(gr, 0x1f, 0x0001, err);
+               DELAY(10);
+               GCT_WRITE(gr, 0x1f, 0x0041, err);
+               DELAY(10);
+               GCT_WRITE(gr, 0x1f, 0x0061, err);
+               DELAY(10);
+               GCT_WRITE(gr, 0x00, 0x0ae4, err);
+               DELAY(10);
+               GCT_WRITE(gr, 0x07, 0x1000, err);
+               DELAY(100);
+               break;
+       }
+
+       return 0;
+err:
+       return -1;
+}
+
+static int
+rtw_grf5101_tune(struct rtw_rf *rf, u_int freq)
+{
+       int channel;
+       struct rtw_grf5101 *gr = (struct rtw_grf5101 *)rf;
+
+       if (freq == 2484) {
+               channel = 14;
+       } else if ((channel = (freq - 2412) / 5 + 1) < 1 || channel > 13) {
+               RTW_DPRINTF(RTW_DEBUG_PHY,
+                   ("%s: invalid channel %d (freq %d)\n", __func__, channel,
+                    freq));
+               return -1;
+       }
+
+       GCT_WRITE(gr, 0x07, 0, err);
+       GCT_WRITE(gr, 0x0b, channel - 1, err);
+       GCT_WRITE(gr, 0x07, 0x1000, err);
+       return 0;
+err:
+       return -1;
+}
+
+static int
+rtw_grf5101_init(struct rtw_rf *rf, u_int freq, uint8_t opaque_txpower,
+                enum rtw_pwrstate power)
+{
+       int rc;
+       struct rtw_grf5101 *gr = (struct rtw_grf5101 *)rf;
+
+       /*
+         * These values have been derived from the rtl8180-sa2400
+         * Linux driver.  It is unknown what they all do, GCT refuse
+         * to release any documentation so these are more than
+         * likely sub optimal settings
+        */
+
+       GCT_WRITE(gr, 0x01, 0x1a23, err);
+       GCT_WRITE(gr, 0x02, 0x4971, err);
+       GCT_WRITE(gr, 0x03, 0x41de, err);
+       GCT_WRITE(gr, 0x04, 0x2d80, err);
+
+       GCT_WRITE(gr, 0x05, 0x61ff, err);
+
+       GCT_WRITE(gr, 0x06, 0x0, err);
+
+       GCT_WRITE(gr, 0x08, 0x7533, err);
+       GCT_WRITE(gr, 0x09, 0xc401, err);
+       GCT_WRITE(gr, 0x0a, 0x0, err);
+       GCT_WRITE(gr, 0x0c, 0x1c7, err);
+       GCT_WRITE(gr, 0x0d, 0x29d3, err);
+       GCT_WRITE(gr, 0x0e, 0x2e8, err);
+       GCT_WRITE(gr, 0x10, 0x192, err);
+       GCT_WRITE(gr, 0x11, 0x248, err);
+       GCT_WRITE(gr, 0x12, 0x0, err);
+       GCT_WRITE(gr, 0x13, 0x20c4, err);
+       GCT_WRITE(gr, 0x14, 0xf4fc, err);
+       GCT_WRITE(gr, 0x15, 0x0, err);
+       GCT_WRITE(gr, 0x16, 0x1500, err);
+
+       if ((rc = rtw_grf5101_txpower(rf, opaque_txpower)) != 0)
+               return rc;
+
+       if ((rc = rtw_grf5101_tune(rf, freq)) != 0)
+               return rc;
+
+       return 0;
+err:
+       return -1;
+}
+
+static void
+rtw_grf5101_destroy(struct rtw_rf *rf)
+{
+       struct rtw_grf5101 *gr = (struct rtw_grf5101 *)rf;
+
+       memset(gr, 0, sizeof(*gr));
+       free(gr, M_DEVBUF);
+}
+
+struct rtw_rf *
+rtw_grf5101_create(struct rtw_regs *regs, rtw_rf_write_t rf_write, int digphy)
+{
+       struct rtw_grf5101 *gr;
+       struct rtw_rfbus *bus;
+       struct rtw_rf *rf;
+       struct rtw_bbpset *bb;
+
+       gr = malloc(sizeof(*gr), M_DEVBUF, M_WAITOK | M_ZERO);
+
+       rf = &gr->gr_rf;
+       bus = &gr->gr_bus;
+
+       rf->rf_init = rtw_grf5101_init;
+       rf->rf_destroy = rtw_grf5101_destroy;
+       rf->rf_txpower = rtw_grf5101_txpower;
+       rf->rf_tune = rtw_grf5101_tune;
+       rf->rf_pwrstate = rtw_grf5101_pwrstate;
+       bb = &rf->rf_bbpset;
+
+       /* XXX magic */
+       bb->bb_antatten = RTW_BBP_ANTATTEN_GCT_MAGIC;
+       bb->bb_chestlim =       0x00;
+       bb->bb_chsqlim =        0xa0;
+       bb->bb_ifagcdet =       0x64;
+       bb->bb_ifagcini =       0x90;
+       bb->bb_ifagclimit =     0x1e;
+       bb->bb_lnadet =         0xc0;
+       bb->bb_sys1 =           0xa8;
+       bb->bb_sys2 =           0x47;
+       bb->bb_sys3 =           0x9b;
+       bb->bb_trl =            0x88;
+       bb->bb_txagc =          0x08;
+
+       bus->b_regs = regs;
+       bus->b_write = rf_write;
+
+       return &gr->gr_rf;
+}
+
+/* freq is in MHz */
+static int
+rtw_max2820_tune(struct rtw_rf *rf, u_int freq)
+{
+       struct rtw_max2820 *mx = (struct rtw_max2820 *)rf;
+       struct rtw_rfbus *bus = &mx->mx_bus;
+
+       if (freq < 2400 || freq > 2499)
+               return -1;
+
+       return rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_CHANNEL,
+                              SHIFTIN(freq - 2400, MAX2820_CHANNEL_CF_MASK));
+}
+
+static void
+rtw_max2820_destroy(struct rtw_rf *rf)
+{
+       struct rtw_max2820 *mx = (struct rtw_max2820 *)rf;
+
+       memset(mx, 0, sizeof(*mx));
+       free(mx, M_DEVBUF);
+}
+
+static int
+rtw_max2820_init(struct rtw_rf *rf, u_int freq, uint8_t opaque_txpower,
+                enum rtw_pwrstate power)
+{
+       struct rtw_max2820 *mx = (struct rtw_max2820 *)rf;
+       struct rtw_rfbus *bus = &mx->mx_bus;
+       int rc;
+
+       rc = rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_TEST,
+                            MAX2820_TEST_DEFAULT);
+       if (rc != 0)
+               return rc;
+
+       rc = rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_ENABLE,
+                            MAX2820_ENABLE_DEFAULT);
+       if (rc != 0)
+               return rc;
+
+       /* skip configuration if it's time to sleep or to power-down. */
+       if ((rc = rtw_max2820_pwrstate(rf, power)) != 0)
+               return rc;
+       else if (power == RTW_OFF || power == RTW_SLEEP)
+               return 0;
+
+       rc = rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_SYNTH,
+                            MAX2820_SYNTH_R_44MHZ);
+       if (rc != 0)
+               return rc;
+
+       if ((rc = rtw_max2820_tune(rf, freq)) != 0)
+               return rc;
+
+       /*
+        * XXX The MAX2820 datasheet indicates that 1C and 2C should not
+        * be changed from 7, however, the reference driver sets them
+        * to 4 and 1, respectively.
+        */
+       rc = rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_RECEIVE,
+                            MAX2820_RECEIVE_DL_DEFAULT |
+                            SHIFTIN(4, MAX2820A_RECEIVE_1C_MASK) |
+                            SHIFTIN(1, MAX2820A_RECEIVE_2C_MASK));
+       if (rc != 0)
+               return rc;
+
+       return rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_TRANSMIT,
+                              MAX2820_TRANSMIT_PA_DEFAULT);
+}
+
+static int
+rtw_max2820_txpower(struct rtw_rf *rf, uint8_t opaque_txpower)
+{
+       /* TBD */
+       return 0;
+}
+
+static int
+rtw_max2820_pwrstate(struct rtw_rf *rf, enum rtw_pwrstate power)
+{
+       uint32_t enable;
+       struct rtw_max2820 *mx;
+       struct rtw_rfbus *bus;
+
+       mx = (struct rtw_max2820 *)rf;
+       bus = &mx->mx_bus;
+
+       switch (power) {
+       case RTW_OFF:
+       case RTW_SLEEP:
+       default:
+               enable = 0x0;
+               break;
+       case RTW_ON:
+               enable = MAX2820_ENABLE_DEFAULT;
+               break;
+       }
+       return rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_ENABLE, enable);
+}
+
+struct rtw_rf *
+rtw_max2820_create(struct rtw_regs *regs, rtw_rf_write_t rf_write, int is_a)
+{
+       struct rtw_max2820 *mx;
+       struct rtw_rfbus *bus;
+       struct rtw_rf *rf;
+       struct rtw_bbpset *bb;
+
+       mx = malloc(sizeof(*mx), M_DEVBUF, M_WAITOK | M_ZERO);
+
+       mx->mx_is_a = is_a;
+
+       rf = &mx->mx_rf;
+       bus = &mx->mx_bus;
+
+       rf->rf_init = rtw_max2820_init;
+       rf->rf_destroy = rtw_max2820_destroy;
+       rf->rf_txpower = rtw_max2820_txpower;
+       rf->rf_tune = rtw_max2820_tune;
+       rf->rf_pwrstate = rtw_max2820_pwrstate;
+       bb = &rf->rf_bbpset;
+
+       /* XXX magic */
+       bb->bb_antatten = RTW_BBP_ANTATTEN_MAXIM_MAGIC;
+       bb->bb_chestlim =       0;
+       bb->bb_chsqlim =        159;
+       bb->bb_ifagcdet =       100;
+       bb->bb_ifagcini =       144;
+       bb->bb_ifagclimit =     26;
+       bb->bb_lnadet =         248;
+       bb->bb_sys1 =           136;
+       bb->bb_sys2 =           71;
+       bb->bb_sys3 =           155;
+       bb->bb_trl =            136;
+       bb->bb_txagc =          8;
+
+       bus->b_regs = regs;
+       bus->b_write = rf_write;
+
+       return &mx->mx_rf;
+}
+
+/* freq is in MHz */
+int
+rtw_phy_init(struct rtw_regs *regs, struct rtw_rf *rf, uint8_t opaque_txpower,
+            uint8_t cs_threshold, u_int freq, int antdiv, int dflantb,
+            enum rtw_pwrstate power)
+{
+       int rc;
+       RTW_DPRINTF(RTW_DEBUG_PHY,
+           ("%s: txpower %u csthresh %u freq %u antdiv %u dflantb %u "
+            "pwrstate %s\n", __func__, opaque_txpower, cs_threshold, freq,
+            antdiv, dflantb, rtw_pwrstate_string(power)));
+
+       /* XXX is this really necessary? */
+       if ((rc = rtw_rf_txpower(rf, opaque_txpower)) != 0)
+               return rc;
+
+       rc = rtw_bbp_preinit(regs, rf->rf_bbpset.bb_antatten, dflantb, freq);
+       if (rc != 0)
+               return rc;
+
+       if ((rc = rtw_rf_tune(rf, freq)) != 0)
+               return rc;
+
+       /* initialize RF  */
+       if ((rc = rtw_rf_init(rf, freq, opaque_txpower, power)) != 0)
+               return rc;
+#if 0  /* what is this redundant tx power setting here for? */
+       if ((rc = rtw_rf_txpower(rf, opaque_txpower)) != 0)
+               return rc;
+#endif
+       return rtw_bbp_init(regs, &rf->rf_bbpset, antdiv, dflantb, cs_threshold,                            freq);
+}
diff --git a/sys/dev/netif/rtw/rtwphy.h b/sys/dev/netif/rtw/rtwphy.h
new file mode 100644 (file)
index 0000000..08ac4f7
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2004, 2005 David Young.  All rights reserved.
+ *
+ * Programmed for NetBSD by David Young.
+ *
+ * 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: rtwphy.h,v 1.5 2005/12/29 22:27:17 dyoung Exp $
+ * $DragonFly: src/sys/dev/netif/rtw/rtwphy.h,v 1.1 2006/09/03 07:37:58 sephe Exp $
+ */
+#ifndef _DEV_IC_RTWPHY_H
+#define _DEV_IC_RTWPHY_H
+
+struct rtw_rf  *rtw_grf5101_create(struct rtw_regs *, rtw_rf_write_t, int);
+struct rtw_rf  *rtw_sa2400_create(struct rtw_regs *, rtw_rf_write_t, int);
+struct rtw_rf  *rtw_max2820_create(struct rtw_regs *, rtw_rf_write_t, int);
+
+int            rtw_phy_init(struct rtw_regs *, struct rtw_rf *, uint8_t,
+                            uint8_t, u_int, int, int, enum rtw_pwrstate);
+
+#endif /* _DEV_IC_RTWPHY_H */
diff --git a/sys/dev/netif/rtw/rtwphyio.c b/sys/dev/netif/rtw/rtwphyio.c
new file mode 100644 (file)
index 0000000..b60cbe3
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2004, 2005 David Young.  All rights reserved.
+ *
+ * Programmed for NetBSD by David Young.
+ *
+ * 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: rtwphyio.c,v 1.11 2006/03/08 00:24:06 dyoung Exp $
+ * $DragonFly: src/sys/dev/netif/rtw/rtwphyio.c,v 1.1 2006/09/03 07:37:58 sephe Exp $
+ */
+
+/*
+ * Control input/output with the Philips SA2400 RF front-end and
+ * the baseband processor built into the Realtek RTL8180.
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/socket.h>
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_var.h>
+#include <net/if_media.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+#include <netproto/802_11/ieee80211_radiotap.h>
+
+#include "rtwbitop.h"
+#include "rtwreg.h"
+#include "max2820reg.h"
+#include "sa2400reg.h"
+#include "si4136reg.h"
+#include "rtwvar.h"
+#include "rtwphyio.h"
+#include "rtwphy.h"
+
+static int     rtw_macbangbits_timeout = 100;
+
+uint8_t
+rtw_bbp_read(struct rtw_regs *regs, u_int addr)
+{
+       KKASSERT((addr & ~SHIFTOUT_MASK(RTW_BB_ADDR_MASK)) == 0);
+       RTW_WRITE(regs, RTW_BB,
+           SHIFTIN(addr, RTW_BB_ADDR_MASK) | RTW_BB_RD_MASK | RTW_BB_WR_MASK);
+       DELAY(10);      /* XXX */
+       RTW_WBR(regs, RTW_BB, RTW_BB);
+       return SHIFTOUT(RTW_READ(regs, RTW_BB), RTW_BB_RD_MASK);
+}
+
+int
+rtw_bbp_write(struct rtw_regs *regs, u_int addr, u_int val)
+{
+#define        BBP_WRITE_ITERS 50
+#define        BBP_WRITE_DELAY 1
+       int i;
+       uint32_t wrbbp, rdbbp;
+
+       RTW_DPRINTF(RTW_DEBUG_PHYIO,
+           ("%s: bbp[%u] <- %u\n", __func__, addr, val));
+
+       KKASSERT((addr & ~SHIFTOUT_MASK(RTW_BB_ADDR_MASK)) == 0);
+       KKASSERT((val & ~SHIFTOUT_MASK(RTW_BB_WR_MASK)) == 0);
+
+       wrbbp = SHIFTIN(addr, RTW_BB_ADDR_MASK) | RTW_BB_WREN |
+           SHIFTIN(val, RTW_BB_WR_MASK) | RTW_BB_RD_MASK,
+
+       rdbbp = SHIFTIN(addr, RTW_BB_ADDR_MASK) |
+           RTW_BB_WR_MASK | RTW_BB_RD_MASK;
+
+       RTW_DPRINTF(RTW_DEBUG_PHYIO,
+           ("%s: rdbbp = %#08x, wrbbp = %#08x\n", __func__, rdbbp, wrbbp));
+
+       for (i = BBP_WRITE_ITERS; --i >= 0; ) {
+               RTW_RBW(regs, RTW_BB, RTW_BB);
+               RTW_WRITE(regs, RTW_BB, wrbbp);
+               RTW_SYNC(regs, RTW_BB, RTW_BB);
+               RTW_WRITE(regs, RTW_BB, rdbbp);
+               RTW_SYNC(regs, RTW_BB, RTW_BB);
+               DELAY(BBP_WRITE_DELAY); /* 1 microsecond */
+               if (SHIFTOUT(RTW_READ(regs, RTW_BB),
+                                   RTW_BB_RD_MASK) == val) {
+                       RTW_DPRINTF(RTW_DEBUG_PHYIO,
+                           ("%s: finished in %dus\n", __func__,
+                           BBP_WRITE_DELAY * (BBP_WRITE_ITERS - i)));
+                       return 0;
+               }
+               DELAY(BBP_WRITE_DELAY); /* again */
+       }
+       printf("%s: timeout\n", __func__);
+       return -1;
+}
+
+/* Help rtw_rf_hostwrite bang bits to RF over 3-wire interface. */
+static __inline void
+rtw_rf_hostbangbits(struct rtw_regs *regs, uint32_t bits, int lo_to_hi,
+                   u_int nbits)
+{
+       int i;
+       uint32_t mask, reg;
+
+       KKASSERT(nbits <= 32);
+
+       RTW_DPRINTF(RTW_DEBUG_PHYIO,
+           ("%s: %u bits, %#08x, %s\n", __func__, nbits, bits,
+           (lo_to_hi) ? "lo to hi" : "hi to lo"));
+
+       reg = RTW_PHYCFG_HST;
+       RTW_WRITE(regs, RTW_PHYCFG, reg);
+       RTW_SYNC(regs, RTW_PHYCFG, RTW_PHYCFG);
+
+       if (lo_to_hi)
+               mask = 0x1;
+       else
+               mask = 1 << (nbits - 1);
+
+       for (i = 0; i < nbits; i++) {
+               RTW_DPRINTF(RTW_DEBUG_PHYBITIO,
+                   ("%s: bits %#08x mask %#08x -> bit %#08x\n",
+                   __func__, bits, mask, bits & mask));
+
+               if ((bits & mask) != 0)
+                       reg |= RTW_PHYCFG_HST_DATA;
+               else
+                       reg &= ~RTW_PHYCFG_HST_DATA;
+
+               reg |= RTW_PHYCFG_HST_CLK;
+               RTW_WRITE(regs, RTW_PHYCFG, reg);
+               RTW_SYNC(regs, RTW_PHYCFG, RTW_PHYCFG);
+
+               DELAY(2);       /* arbitrary delay */
+
+               reg &= ~RTW_PHYCFG_HST_CLK;
+               RTW_WRITE(regs, RTW_PHYCFG, reg);
+               RTW_SYNC(regs, RTW_PHYCFG, RTW_PHYCFG);
+
+               if (lo_to_hi)
+                       mask <<= 1;
+               else
+                       mask >>= 1;
+       }
+
+       reg |= RTW_PHYCFG_HST_EN;
+       KKASSERT((reg & RTW_PHYCFG_HST_CLK) == 0);
+       RTW_WRITE(regs, RTW_PHYCFG, reg);
+       RTW_SYNC(regs, RTW_PHYCFG, RTW_PHYCFG);
+}
+
+/* Help rtw_rf_macwrite: tell MAC to bang bits to RF over the 3-wire
+ * interface.
+ */
+static __inline int
+rtw_rf_macbangbits(struct rtw_regs *regs, uint32_t reg)
+{
+       int i;
+
+       RTW_DPRINTF(RTW_DEBUG_PHY, ("%s: %#08x\n", __func__, reg));
+
+       RTW_WRITE(regs, RTW_PHYCFG, RTW_PHYCFG_MAC_POLL | reg);
+
+       RTW_WBR(regs, RTW_PHYCFG, RTW_PHYCFG);
+
+       for (i = rtw_macbangbits_timeout; --i >= 0; DELAY(1)) {
+               if ((RTW_READ(regs, RTW_PHYCFG) & RTW_PHYCFG_MAC_POLL) == 0) {
+                       RTW_DPRINTF(RTW_DEBUG_PHY,
+                           ("%s: finished in %dus\n", __func__,
+                           rtw_macbangbits_timeout - i));
+                       return 0;
+               }
+               RTW_RBR(regs, RTW_PHYCFG, RTW_PHYCFG);  /* XXX paranoia? */
+       }
+
+       printf("%s: RTW_PHYCFG_MAC_POLL still set.\n", __func__);
+       return -1;
+}
+
+static uint32_t
+rtw_grf5101_host_crypt(u_int addr, uint32_t val)
+{
+       /* TBD */
+       return 0;
+}
+
+static uint32_t
+rtw_grf5101_mac_crypt(u_int addr, uint32_t val)
+{
+       uint32_t data_and_addr;
+#define EXTRACT_NIBBLE(d, which) (((d) >> (4 * (which))) & 0xf)
+       static uint8_t caesar[16] = {0x0, 0x8, 0x4, 0xc,
+                                     0x2, 0xa, 0x6, 0xe,
+                                     0x1, 0x9, 0x5, 0xd,
+                                     0x3, 0xb, 0x7, 0xf};
+
+       data_and_addr =  caesar[EXTRACT_NIBBLE(val, 2)] |
+                       (caesar[EXTRACT_NIBBLE(val, 1)] <<  4) |
+                       (caesar[EXTRACT_NIBBLE(val, 0)] <<  8) |
+                       (caesar[(addr >> 1) & 0xf]      << 12) |
+                       ((addr & 0x1)                   << 16) |
+                       (caesar[EXTRACT_NIBBLE(val, 3)] << 24);
+       return SHIFTIN(data_and_addr,
+           RTW_PHYCFG_MAC_PHILIPS_ADDR_MASK|RTW_PHYCFG_MAC_PHILIPS_DATA_MASK);
+#undef EXTRACT_NIBBLE
+}
+
+static __inline const char *
+rtw_rfchipid_string(enum rtw_rfchipid rfchipid)
+{
+       switch (rfchipid) {
+       case RTW_RFCHIPID_MAXIM:
+               return "Maxim";
+       case RTW_RFCHIPID_PHILIPS:
+               return "Philips";
+       case RTW_RFCHIPID_GCT:
+               return "GCT";
+       case RTW_RFCHIPID_RFMD:
+               return "RFMD";
+       case RTW_RFCHIPID_INTERSIL:
+               return "Intersil";
+       default:
+               return "unknown";
+       }
+}
+
+/* Bang bits over the 3-wire interface. */
+int
+rtw_rf_hostwrite(struct rtw_regs *regs, enum rtw_rfchipid rfchipid,
+                u_int addr, uint32_t val)
+{
+       u_int nbits;
+       int lo_to_hi;
+       uint32_t bits;
+
+       RTW_DPRINTF(RTW_DEBUG_PHYIO, ("%s: %s[%u] <- %#08x\n", __func__,
+           rtw_rfchipid_string(rfchipid), addr, val));
+
+       switch (rfchipid) {
+       case RTW_RFCHIPID_MAXIM:
+               nbits = 16;
+               lo_to_hi = 0;
+               bits = SHIFTIN(val, MAX2820_TWI_DATA_MASK) |
+                      SHIFTIN(addr, MAX2820_TWI_ADDR_MASK);
+               break;
+       case RTW_RFCHIPID_PHILIPS:
+               KKASSERT((addr & ~SHIFTOUT_MASK(SA2400_TWI_ADDR_MASK)) == 0);
+               KKASSERT((val & ~SHIFTOUT_MASK(SA2400_TWI_DATA_MASK)) == 0);
+               bits = SHIFTIN(val, SA2400_TWI_DATA_MASK) |
+                      SHIFTIN(addr, SA2400_TWI_ADDR_MASK) | SA2400_TWI_WREN;
+               nbits = 32;
+               lo_to_hi = 1;
+               break;
+       case RTW_RFCHIPID_GCT:
+               KKASSERT((addr & ~SHIFTOUT_MASK(SI4126_TWI_ADDR_MASK)) == 0);
+               KKASSERT((val & ~SHIFTOUT_MASK(SI4126_TWI_DATA_MASK)) == 0);
+               bits = rtw_grf5101_host_crypt(addr, val);
+               nbits = 21;
+               lo_to_hi = 1;
+               break;
+       case RTW_RFCHIPID_RFMD:
+               KKASSERT((addr & ~SHIFTOUT_MASK(SI4126_TWI_ADDR_MASK)) == 0);
+               KKASSERT((val & ~SHIFTOUT_MASK(SI4126_TWI_DATA_MASK)) == 0);
+               bits = SHIFTIN(val, SI4126_TWI_DATA_MASK) |
+                      SHIFTIN(addr, SI4126_TWI_ADDR_MASK);
+               nbits = 22;
+               lo_to_hi = 0;
+               break;
+       case RTW_RFCHIPID_INTERSIL:
+       default:
+               printf("%s: unknown rfchipid %d\n", __func__, rfchipid);
+               return -1;
+       }
+
+       rtw_rf_hostbangbits(regs, bits, lo_to_hi, nbits);
+
+       return 0;
+}
+
+static uint32_t
+rtw_maxim_swizzle(u_int addr, uint32_t val)
+{
+       uint32_t hidata, lodata;
+
+       KKASSERT((val & ~(RTW_MAXIM_LODATA_MASK|RTW_MAXIM_HIDATA_MASK)) == 0);
+       lodata = SHIFTOUT(val, RTW_MAXIM_LODATA_MASK);
+       hidata = SHIFTOUT(val, RTW_MAXIM_HIDATA_MASK);
+       return SHIFTIN(lodata, RTW_PHYCFG_MAC_MAXIM_LODATA_MASK) |
+           SHIFTIN(hidata, RTW_PHYCFG_MAC_MAXIM_HIDATA_MASK) |
+           SHIFTIN(addr, RTW_PHYCFG_MAC_MAXIM_ADDR_MASK);
+}
+
+/* Tell the MAC what to bang over the 3-wire interface. */
+int
+rtw_rf_macwrite(struct rtw_regs *regs, enum rtw_rfchipid rfchipid,
+               u_int addr, uint32_t val)
+{
+       uint32_t reg;
+
+       RTW_DPRINTF(RTW_DEBUG_PHYIO, ("%s: %s[%u] <- %#08x\n", __func__,
+           rtw_rfchipid_string(rfchipid), addr, val));
+
+       switch (rfchipid) {
+       case RTW_RFCHIPID_GCT:
+               reg = rtw_grf5101_mac_crypt(addr, val);
+               break;
+       case RTW_RFCHIPID_MAXIM:
+               reg = rtw_maxim_swizzle(addr, val);
+               break;
+       default:                /* XXX */
+       case RTW_RFCHIPID_PHILIPS:
+               KKASSERT(
+                   (addr & ~SHIFTOUT_MASK(RTW_PHYCFG_MAC_PHILIPS_ADDR_MASK)) == 0);
+               KKASSERT(
+                   (val & ~SHIFTOUT_MASK(RTW_PHYCFG_MAC_PHILIPS_DATA_MASK)) == 0);
+
+               reg = SHIFTIN(addr, RTW_PHYCFG_MAC_PHILIPS_ADDR_MASK) |
+                     SHIFTIN(val, RTW_PHYCFG_MAC_PHILIPS_DATA_MASK);
+       }
+
+       switch (rfchipid) {
+       case RTW_RFCHIPID_GCT:
+       case RTW_RFCHIPID_MAXIM:
+       case RTW_RFCHIPID_RFMD:
+               reg |= RTW_PHYCFG_MAC_RFTYPE_RFMD;
+               break;
+       case RTW_RFCHIPID_INTERSIL:
+               reg |= RTW_PHYCFG_MAC_RFTYPE_INTERSIL;
+               break;
+       case RTW_RFCHIPID_PHILIPS:
+               reg |= RTW_PHYCFG_MAC_RFTYPE_PHILIPS;
+               break;
+       default:
+               printf("%s: unknown rfchipid %d\n", __func__, rfchipid);
+               return -1;
+       }
+
+       return rtw_rf_macbangbits(regs, reg);
+}
diff --git a/sys/dev/netif/rtw/rtwphyio.h b/sys/dev/netif/rtw/rtwphyio.h
new file mode 100644 (file)
index 0000000..af12c86
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2004, 2005 David Young.  All rights reserved.
+ *
+ * Programmed for NetBSD by David Young.
+ *
+ * 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: rtwphyio.h,v 1.3 2005/12/11 12:21:28 christos Exp $
+ * $DragonFly: src/sys/dev/netif/rtw/rtwphyio.h,v 1.1 2006/09/03 07:37:58 sephe Exp $
+ */
+#ifndef _DEV_IC_RTWPHYIO_H
+#define _DEV_IC_RTWPHYIO_H
+
+int    rtw_rf_hostwrite(struct rtw_regs *, enum rtw_rfchipid, u_int, uint32_t);
+int    rtw_rf_macwrite(struct rtw_regs *, enum rtw_rfchipid, u_int, uint32_t);
+uint8_t        rtw_bbp_read(struct rtw_regs *, u_int);
+int    rtw_bbp_write(struct rtw_regs *, u_int, u_int);
+
+#endif /* _DEV_IC_RTWPHYIO_H */
diff --git a/sys/dev/netif/rtw/rtwreg.h b/sys/dev/netif/rtw/rtwreg.h
new file mode 100644 (file)
index 0000000..eae59f4
--- /dev/null
@@ -0,0 +1,1100 @@
+/*
+ * Copyright (c) 2004, 2005 David Young.  All rights reserved.
+ *
+ * Programmed for NetBSD by David Young.
+ *
+ * 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: rtwreg.h,v 1.19 2006/03/10 23:37:35 dyoung Exp $
+ * $DragonFly: src/sys/dev/netif/rtw/rtwreg.h,v 1.1 2006/09/03 07:37:58 sephe Exp $
+ */
+
+/* RTL8180L Host Control and Status Registers */
+
+#define RTW_IDR0       0x00    /* ID Register: MAC addr, 6 bytes.
+                                * Auto-loaded from EEPROM. Read by byte,
+                                * by word, or by double word, but write
+                                * only by double word.
+                                */
+#define RTW_IDR1       0x04
+
+#define RTW_MAR0       0x08    /* Multicast filter, 64b. */
+#define RTW_MAR1       0x0c
+
+#define RTW_TSFTRL     0x18    /* Timing Synchronization Function Timer
+                                * Register, low word, 32b, read-only.
+                                */
+#define RTW_TSFTRH     0x1c    /* High word, 32b, read-only. */
+#define        RTW_TLPDA       0x20    /* Transmit Low Priority Descriptors Start
+                                * Address, 32b, 256-byte alignment.
+                                */
+#define        RTW_TNPDA       0x24    /* Transmit Normal Priority Descriptors Start
+                                * Address, 32b, 256-byte alignment.
+                                */
+#define        RTW_THPDA       0x28    /* Transmit High Priority Descriptors Start
+                                * Address, 32b, 256-byte alignment.
+                                */
+
+#define RTW_BRSR       0x2c    /* Basic Rate Set Register, 16b */
+#define        RTW_BRSR_BPLCP  __BIT(8)/* 1: use short PLCP header for CTS/ACK packet,
+                                * 0: use long PLCP header
+                                */
+#define RTW_BRSR_MBR8180_MASK  __BITS(1,0)     /* Maximum Basic Service Rate */
+#define RTW_BRSR_MBR8180_1MBPS SHIFTIN(0, RTW_BRSR_MBR8180_MASK)
+#define RTW_BRSR_MBR8180_2MBPS SHIFTIN(1, RTW_BRSR_MBR8180_MASK)
+#define RTW_BRSR_MBR8180_5MBPS SHIFTIN(2, RTW_BRSR_MBR8180_MASK)
+#define RTW_BRSR_MBR8180_11MBPS        SHIFTIN(3, RTW_BRSR_MBR8180_MASK)
+
+/* 8181 and 8180 docs conflict! */
+#define RTW_BRSR_MBR8181_1MBPS __BIT(0)
+#define RTW_BRSR_MBR8181_2MBPS __BIT(1)
+#define RTW_BRSR_MBR8181_5MBPS __BIT(2)
+#define RTW_BRSR_MBR8181_11MBPS        __BIT(3)
+
+#define RTW_BSSID      0x2e
+/* BSSID, 6 bytes */
+#define RTW_BSSID16    0x2e            /* first two bytes */
+#define RTW_BSSID32    (0x2e + 4)      /* remaining four bytes */
+#define RTW_BSSID0     RTW_BSSID16             /* BSSID[0], 8b */
+#define RTW_BSSID1     (RTW_BSSID0 + 1)        /* BSSID[1], 8b */
+#define RTW_BSSID2     (RTW_BSSID1 + 1)        /* BSSID[2], 8b */
+#define RTW_BSSID3     (RTW_BSSID2 + 1)        /* BSSID[3], 8b */
+#define RTW_BSSID4     (RTW_BSSID3 + 1)        /* BSSID[4], 8b */
+#define RTW_BSSID5     (RTW_BSSID4 + 1)        /* BSSID[5], 8b */
+
+#define        RTW_CR          0x37    /* Command Register, 8b */
+#define        RTW_CR_RST      __BIT(4)/* Reset: host sets to 1 to disable
+                                * transmitter & receiver, reinitialize FIFO.
+                                * RTL8180L sets to 0 to signal completion.
+                                */
+#define        RTW_CR_RE       __BIT(3)/* Receiver Enable: host enables receiver
+                                * by writing 1. RTL8180L indicates receiver
+                                * is active with 1. After power-up, host
+                                * must wait for reset before writing.
+                                */
+#define        RTW_CR_TE       __BIT(2)/* Transmitter Enable: host enables transmitter
+                                * by writing 1. RTL8180L indicates transmitter
+                                * is active with 1. After power-up, host
+                                * must wait for reset before writing.
+                                */
+#define        RTW_CR_MULRW    __BIT(0)/* PCI Multiple Read/Write enable: 1 enables,
+                                * 0 disables. XXX RTL8180, only?
+                                */
+
+#define        RTW_IMR         0x3c    /* Interrupt Mask Register, 16b */
+#define        RTW_ISR         0x3e    /* Interrupt status register, 16b */
+
+#define RTW_INTR_TXFOVW                __BIT(15)       /* Tx FIFO underflow */
+#define RTW_INTR_TIMEOUT       __BIT(14)       /* Time Out: 1 indicates
+                                                * RTW_TSFTR[0:31] = RTW_TINT
+                                                */
+/* Beacon Time Out: time for host to prepare beacon:
+ * RTW_TSFTR % (RTW_BCNITV_BCNITV * TU) =
+ * (RTW_BCNITV_BCNITV * TU - RTW_BINTRITV)
+ */
+#define RTW_INTR_BCNINT                __BIT(13)
+/* ATIM Time Out: ATIM interval will pass,
+ * RTW_TSFTR % (RTW_BCNITV_BCNITV * TU) =
+ * (RTW_ATIMWND_ATIMWND * TU - RTW_ATIMTRITV)
+ */
+#define RTW_INTR_ATIMINT       __BIT(12)
+/* Tx Beacon Descriptor Error: beacon transmission aborted because frame Rx'd */
+#define RTW_INTR_TBDER __BIT(11)
+#define RTW_INTR_TBDOK __BIT(10)       /* Tx Beacon Descriptor OK */
+#define RTW_INTR_THPDER        __BIT(9)/* Tx High Priority Descriptor Error:
+                                * reached short/long retry limit
+                                */
+#define RTW_INTR_THPDOK        __BIT(8)/* Tx High Priority Descriptor OK */
+#define RTW_INTR_TNPDER        __BIT(7)/* Tx Normal Priority Descriptor Error:
+                                * reached short/long retry limit
+                                */
+#define RTW_INTR_TNPDOK        __BIT(6)/* Tx Normal Priority Descriptor OK */
+#define RTW_INTR_RXFOVW        __BIT(5)/* Rx FIFO Overflow: either RDU (see below)
+                                * or PCI bus too slow/busy
+                                */
+#define RTW_INTR_RDU   __BIT(4)/* Rx Descriptor Unavailable */
+#define RTW_INTR_TLPDER        __BIT(3)/* Tx Normal Priority Descriptor Error
+                                * reached short/long retry limit
+                                */
+#define RTW_INTR_TLPDOK        __BIT(2)/* Tx Normal Priority Descriptor OK */
+#define RTW_INTR_RER   __BIT(1)/* Rx Error: CRC32 or ICV error */
+#define RTW_INTR_ROK   __BIT(0)/* Rx OK */
+
+/* Convenient interrupt conjunctions. */
+#define RTW_INTR_RX    (RTW_INTR_RER|RTW_INTR_ROK)
+#define RTW_INTR_TX    (RTW_INTR_TLPDER|RTW_INTR_TLPDOK|RTW_INTR_THPDER|\
+                        RTW_INTR_THPDOK|RTW_INTR_TNPDER|RTW_INTR_TNPDOK|\
+                        RTW_INTR_TBDER|RTW_INTR_TBDOK)
+#define RTW_INTR_BEACON        (RTW_INTR_BCNINT|RTW_INTR_TBDER|RTW_INTR_TBDOK)
+#define RTW_INTR_IOERROR       (RTW_INTR_TXFOVW|RTW_INTR_RXFOVW|RTW_INTR_RDU)
+
+#define        RTW_TCR         0x40    /* Transmit Configuration Register, 32b */
+#define RTW_TCR_CWMIN  __BIT(31)/* 1: CWmin = 8, 0: CWmin = 32. */
+#define RTW_TCR_SWSEQ  __BIT(30)/* 1: host assigns 802.11 sequence number,
+                                * 0: hardware assigns sequence number
+                                */
+/* Hardware version ID, read-only */
+#define RTW_TCR_HWVERID_MASK   __BITS(29, 25)
+#define RTW_TCR_HWVERID_D      SHIFTIN(26, RTW_TCR_HWVERID_MASK)
+#define RTW_TCR_HWVERID_F      SHIFTIN(27, RTW_TCR_HWVERID_MASK)
+#define RTW_TCR_HWVERID_RTL8180        RTW_TCR_HWVERID_F
+
+/* Set ACK/CTS Timeout (EIFS).
+ * 1: ACK rate = max(RTW_BRSR_MBR, Rx rate) (XXX not min? typo in datasheet?)
+ * 0: ACK rate = 1Mbps
+ */
+#define RTW_TCR_SAT    __BIT(24)
+/* Max DMA Burst Size per Tx DMA Burst */
+#define RTW_TCR_MXDMA_MASK     __BITS(23,21)
+#define RTW_TCR_MXDMA_16       SHIFTIN(0, RTW_TCR_MXDMA_MASK)
+#define RTW_TCR_MXDMA_32       SHIFTIN(1, RTW_TCR_MXDMA_MASK)
+#define RTW_TCR_MXDMA_64       SHIFTIN(2, RTW_TCR_MXDMA_MASK)
+#define RTW_TCR_MXDMA_128      SHIFTIN(3, RTW_TCR_MXDMA_MASK)
+#define RTW_TCR_MXDMA_256      SHIFTIN(4, RTW_TCR_MXDMA_MASK)
+#define RTW_TCR_MXDMA_512      SHIFTIN(5, RTW_TCR_MXDMA_MASK)
+#define RTW_TCR_MXDMA_1024     SHIFTIN(6, RTW_TCR_MXDMA_MASK)
+#define RTW_TCR_MXDMA_2048     SHIFTIN(7, RTW_TCR_MXDMA_MASK)
+
+/* disable 802.11 random backoff */
+#define RTW_TCR_DISCW          __BIT(20)
+
+/* host lets RTL8180 append ICV to WEP packets */
+#define RTW_TCR_ICV            __BIT(19)
+
+/* Loopback Test: disables TXI/TXQ outputs. */
+#define RTW_TCR_LBK_MASK       __BITS(18,17)
+#define RTW_TCR_LBK_NORMAL     SHIFTIN(0, RTW_TCR_LBK_MASK) /* normal ops */
+#define RTW_TCR_LBK_MAC                SHIFTIN(1, RTW_TCR_LBK_MASK) /* MAC loopback */
+#define RTW_TCR_LBK_BBP                SHIFTIN(2, RTW_TCR_LBK_MASK) /* baseband loop. */
+#define RTW_TCR_LBK_CONT       SHIFTIN(3, RTW_TCR_LBK_MASK) /* continuous Tx */
+
+#define RTW_TCR_CRC    __BIT(16)       /* 0: RTL8180 appends CRC32
+                                        * 1: host appends CRC32
+                                        *
+                                        * (I *think* this is right.
+                                        *  The docs have a mysterious
+                                        *  description in the
+                                        *  passive voice.)
+                                        */
+#define RTW_TCR_SRL_MASK       __BITS(15,8)    /* Short Retry Limit */
+#define RTW_TCR_LRL_MASK       __BITS(7,0)     /* Long Retry Limit */
+
+#define        RTW_RCR         0x44    /* Receive Configuration Register, 32b */
+/* only do Early Rx on packets longer than 1536 bytes */
+#define RTW_RCR_ONLYERLPKT     __BIT(31)
+/* enable carrier sense method 2 */
+#define RTW_RCR_ENCS2          __BIT(30)
+/* enable carrier sense method 1 */
+#define RTW_RCR_ENCS1          __BIT(29)
+#define RTW_RCR_ENMARP         __BIT(28)       /* enable MAC auto-reset PHY */
+/* Check BSSID/ToDS/FromDS: set "Link On" when received BSSID
+ * matches RTW_BSSID and received ToDS/FromDS are appropriate
+ * according to RTW_MSR_NETYPE.
+ */
+#define RTW_RCR_CBSSID         __BIT(23)
+ /* accept packets w/ PWRMGMT bit set */
+#define RTW_RCR_APWRMGT                __BIT(22)
+/* when RTW_MSR_NETYPE == RTW_MSR_NETYPE_INFRA_OK, accept
+ * broadcast/multicast packets whose 3rd address matches RTL8180's MAC.
+ */
+#define RTW_RCR_ADD3           __BIT(21)
+#define RTW_RCR_AMF            __BIT(20)       /* accept management frames */
+#define RTW_RCR_ACF            __BIT(19)       /* accept control frames */
+#define RTW_RCR_ADF            __BIT(18)       /* accept data frames */
+/* Rx FIFO Threshold: RTL8180 begins PCI transfer when this many data
+ * bytes are received
+ */
+#define RTW_RCR_RXFTH_MASK     __BITS(15,13)
+#define RTW_RCR_RXFTH_64       SHIFTIN(2, RTW_RCR_RXFTH_MASK)
+#define RTW_RCR_RXFTH_128      SHIFTIN(3, RTW_RCR_RXFTH_MASK)
+#define RTW_RCR_RXFTH_256      SHIFTIN(4, RTW_RCR_RXFTH_MASK)
+#define RTW_RCR_RXFTH_512      SHIFTIN(5, RTW_RCR_RXFTH_MASK)
+#define RTW_RCR_RXFTH_1024     SHIFTIN(6, RTW_RCR_RXFTH_MASK)
+#define RTW_RCR_RXFTH_WHOLE    SHIFTIN(7, RTW_RCR_RXFTH_MASK)
+
+#define RTW_RCR_AICV           __BIT(12)/* accept frames w/ ICV errors */
+
+/* Max DMA Burst Size per Rx DMA Burst */
+#define RTW_RCR_MXDMA_MASK     __BITS(10,8)
+#define RTW_RCR_MXDMA_16       SHIFTIN(0, RTW_RCR_MXDMA_MASK)
+#define RTW_RCR_MXDMA_32       SHIFTIN(1, RTW_RCR_MXDMA_MASK)
+#define RTW_RCR_MXDMA_64       SHIFTIN(2, RTW_RCR_MXDMA_MASK)
+#define RTW_RCR_MXDMA_128      SHIFTIN(3, RTW_RCR_MXDMA_MASK)
+#define RTW_RCR_MXDMA_256      SHIFTIN(4, RTW_RCR_MXDMA_MASK)
+#define RTW_RCR_MXDMA_512      SHIFTIN(5, RTW_RCR_MXDMA_MASK)
+#define RTW_RCR_MXDMA_1024     SHIFTIN(6, RTW_RCR_MXDMA_MASK)
+#define RTW_RCR_MXDMA_UNLIMITED        SHIFTIN(7, RTW_RCR_MXDMA_MASK)
+
+/* EEPROM type, read-only. 1: EEPROM is 93c56, 0: 93c46 */
+#define RTW_RCR_9356SEL                __BIT(6)
+
+#define RTW_RCR_ACRC32         __BIT(5)/* accept frames w/ CRC32 errors */
+#define RTW_RCR_AB             __BIT(3)/* accept broadcast frames */
+#define RTW_RCR_AM             __BIT(2)/* accept multicast frames */
+/* accept physical match frames. XXX means PLCP header ok? */
+#define RTW_RCR_APM            __BIT(1)
+#define RTW_RCR_AAP            __BIT(0)/* accept frames w/ destination */
+
+/* Additional bits to set in monitor mode. */
+#define RTW_RCR_MONITOR (              \
+    RTW_RCR_AAP |                      \
+    RTW_RCR_ACF |                      \
+    RTW_RCR_ACRC32 |                   \
+    RTW_RCR_AICV |                     \
+    0)
+
+/* The packet filter bits. */
+#define        RTW_RCR_PKTFILTER_MASK (\
+    RTW_RCR_AAP |              \
+    RTW_RCR_AB |               \
+    RTW_RCR_ACF |              \
+    RTW_RCR_ACRC32 |           \
+    RTW_RCR_ADD3 |             \
+    RTW_RCR_ADF |              \
+    RTW_RCR_AICV |             \
+    RTW_RCR_AM |               \
+    RTW_RCR_AMF |              \
+    RTW_RCR_APM |              \
+    RTW_RCR_APWRMGT |          \
+    0)
+
+/* Receive power-management frames and mgmt/ctrl/data frames. */
+#define        RTW_RCR_PKTFILTER_DEFAULT       (       \
+    RTW_RCR_ADF |                              \
+    RTW_RCR_AMF |                              \
+    RTW_RCR_APM |                              \
+    RTW_RCR_APWRMGT |                          \
+    0)
+
+#define RTW_TINT       0x48    /* Timer Interrupt Register, 32b */
+#define        RTW_TBDA        0x4c    /* Transmit Beacon Descriptor Start Address,
+                                * 32b, 256-byte alignment
+                                */
+#define RTW_9346CR     0x50    /* 93c46/93c56 Command Register, 8b */
+#define RTW_9346CR_EEM_MASK    __BITS(7,6)     /* Operating Mode */
+#define RTW_9346CR_EEM_NORMAL  SHIFTIN(0, RTW_9346CR_EEM_MASK)
+/* Load the EEPROM. Reset registers to defaults.
+ * Takes ~2ms. RTL8180 indicates completion with RTW_9346CR_EEM_NORMAL.
+ * XXX RTL8180 only?
+ */
+#define RTW_9346CR_EEM_AUTOLOAD        SHIFTIN(1, RTW_9346CR_EEM_MASK)
+/* Disable network & bus-master operations and enable
+ * _EECS, _EESK, _EEDI, _EEDO.
+ * XXX RTL8180 only?
+ */
+#define RTW_9346CR_EEM_PROGRAM SHIFTIN(2, RTW_9346CR_EEM_MASK)
+/* Enable RTW_CONFIG[0123] registers. */
+#define RTW_9346CR_EEM_CONFIG  SHIFTIN(3, RTW_9346CR_EEM_MASK)
+/* EEPROM pin status/control in _EEM_CONFIG, _EEM_AUTOLOAD modes.
+ * XXX RTL8180 only?
+ */
+#define RTW_9346CR_EECS        __BIT(3)
+#define RTW_9346CR_EESK        __BIT(2)
+#define RTW_9346CR_EEDI        __BIT(1)
+#define RTW_9346CR_EEDO        __BIT(0)        /* read-only */
+
+#define RTW_CONFIG0    0x51    /* Configuration Register 0, 8b */
+#define RTW_CONFIG0_WEP40      __BIT(7)/* implements 40-bit WEP,
+                                        * XXX RTL8180 only?
+                                        */
+#define RTW_CONFIG0_WEP104     __BIT(6)/* implements 104-bit WEP,
+                                        * from EEPROM, read-only
+                                        * XXX RTL8180 only?
+                                        */
+#define RTW_CONFIG0_LEDGPOEN   __BIT(4)/* 1: RTW_PSR_LEDGPO[01] control
+                                        *    LED[01] pins.
+                                        * 0: LED behavior defined by
+                                        *    RTW_CONFIG1_LEDS10_MASK
+                                        * XXX RTL8180 only?
+                                        */
+/* auxiliary power is present, read-only */
+#define RTW_CONFIG0_AUXPWR     __BIT(3)
+/* Geographic Location, read-only */
+#define RTW_CONFIG0_GL_MASK            __BITS(1,0)
+/* _RTW_CONFIG0_GL_* is what the datasheet says, but RTW_CONFIG0_GL_*
+ * work.
+ */
+#define _RTW_CONFIG0_GL_USA            SHIFTIN(3, RTW_CONFIG0_GL_MASK)
+#define RTW_CONFIG0_GL_EUROPE          SHIFTIN(2, RTW_CONFIG0_GL_MASK)
+#define RTW_CONFIG0_GL_JAPAN           SHIFTIN(1, RTW_CONFIG0_GL_MASK)
+#define RTW_CONFIG0_GL_USA             SHIFTIN(0, RTW_CONFIG0_GL_MASK)
+/* RTL8181 datasheet says RTW_CONFIG0_GL_JAPAN = 0. */
+
+#define RTW_CONFIG1    0x52    /* Configuration Register 1, 8b */
+
+/* LED configuration. From EEPROM. Read/write.
+ *
+ * Setting                             LED0            LED1
+ * -------                             ----            ----
+ * RTW_CONFIG1_LEDS_ACT_INFRA          Activity        Infrastructure
+ * RTW_CONFIG1_LEDS_ACT_LINK           Activity        Link
+ * RTW_CONFIG1_LEDS_TX_RX              Tx              Rx
+ * RTW_CONFIG1_LEDS_LINKACT_INFRA      Link/Activity   Infrastructure
+ */
+#define RTW_CONFIG1_LEDS_MASK  __BITS(7,6)
+#define RTW_CONFIG1_LEDS_ACT_INFRA     SHIFTIN(0, RTW_CONFIG1_LEDS_MASK)
+#define RTW_CONFIG1_LEDS_ACT_LINK      SHIFTIN(1, RTW_CONFIG1_LEDS_MASK)
+#define RTW_CONFIG1_LEDS_TX_RX         SHIFTIN(2, RTW_CONFIG1_LEDS_MASK)
+#define RTW_CONFIG1_LEDS_LINKACT_INFRA SHIFTIN(3, RTW_CONFIG1_LEDS_MASK)
+
+/* LWAKE Output Signal. Only applicable to Cardbus. Pulse width is 150ms.
+ *
+ *                                   RTW_CONFIG1_LWACT
+ *                             0                       1
+ * RTW_CONFIG4_LWPTN   0       active high             active low
+ *                     1       positive pulse          negative pulse
+ */
+#define RTW_CONFIG1_LWACT      __BIT(4)
+
+#define RTW_CONFIG1_MEMMAP     __BIT(3)/* using PCI memory space, read-only */
+#define RTW_CONFIG1_IOMAP      __BIT(2)/* using PCI I/O space, read-only */
+#define RTW_CONFIG1_VPD                __BIT(1)/* if set, VPD from offsets
+                                        * 0x40-0x7f in EEPROM are at
+                                        * registers 0x60-0x67 of PCI
+                                        * Configuration Space (XXX huh?)
+                                        */
+#define RTW_CONFIG1_PMEN       __BIT(0)/* Power Management Enable: TBD */
+
+#define RTW_CONFIG2    0x53    /* Configuration Register 2, 8b */
+#define RTW_CONFIG2_LCK        __BIT(7)/* clocks are locked, read-only:
+                                * Tx frequency & symbol clocks
+                                * are derived from the same OSC
+                                */
+#define RTW_CONFIG2_ANT        __BIT(6)        /* diversity enabled, read-only */
+#define RTW_CONFIG2_DPS        __BIT(3)        /* Descriptor Polling State: enable
+                                        * test mode.
+                                        */
+#define RTW_CONFIG2_PAPESIGN   __BIT(2)                /* TBD, from EEPROM */
+#define RTW_CONFIG2_PAPETIME_MASK      __BITS(1,0)     /* TBD, from EEPROM */
+
+#define        RTW_ANAPARM     0x54    /* Analog parameter, 32b */
+#define RTW_ANAPARM_RFPOW0_MASK        __BITS(30,28)           /* undocumented bits
+                                                        * which appear to
+                                                        * control the power
+                                                        * state of the RF
+                                                        * components
+                                                        */
+#define        RTW_ANAPARM_RFPOW_MASK  \
+    (RTW_ANAPARM_RFPOW0_MASK|RTW_ANAPARM_RFPOW1_MASK)
+
+#define RTW_ANAPARM_TXDACOFF   __BIT(27)               /* 1: disable Tx DAC,
+                                                        * 0: enable
+                                                        */
+#define RTW_ANAPARM_RFPOW1_MASK        __BITS(26,20)           /* undocumented bits
+                                                        * which appear to
+                                                        * control the power
+                                                        * state of the RF
+                                                        * components
+                                                        */
+
+/*
+ * Maxim On/Sleep/Off control
+ */
+#define RTW_ANAPARM_RFPOW_MAXIM_ON     SHIFTIN(0x8, RTW_ANAPARM_RFPOW1_MASK)
+
+/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */
+#define RTW_ANAPARM_RFPOW_MAXIM_SLEEP  SHIFTIN(0x378, RTW_ANAPARM_RFPOW1_MASK)
+
+/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */
+#define RTW_ANAPARM_RFPOW_MAXIM_OFF    SHIFTIN(0x379, RTW_ANAPARM_RFPOW1_MASK)
+
+/*
+ * RFMD On/Sleep/Off control
+ */
+#define RTW_ANAPARM_RFPOW_RFMD_ON      SHIFTIN(0x408, RTW_ANAPARM_RFPOW1_MASK)
+
+/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */
+#define RTW_ANAPARM_RFPOW_RFMD_SLEEP   SHIFTIN(0x378, RTW_ANAPARM_RFPOW1_MASK)
+
+/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */
+#define RTW_ANAPARM_RFPOW_RFMD_OFF     SHIFTIN(0x379, RTW_ANAPARM_RFPOW1_MASK)
+
+/*
+ * Philips On/Sleep/Off control
+ */
+#define RTW_ANAPARM_RFPOW_ANA_PHILIPS_ON       \
+    SHIFTIN(0x328, RTW_ANAPARM_RFPOW1_MASK)
+#define RTW_ANAPARM_RFPOW_DIG_PHILIPS_ON       \
+    SHIFTIN(0x008, RTW_ANAPARM_RFPOW1_MASK)
+
+/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */
+#define RTW_ANAPARM_RFPOW_PHILIPS_SLEEP\
+    SHIFTIN(0x378, RTW_ANAPARM_RFPOW1_MASK)
+
+/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */
+#define RTW_ANAPARM_RFPOW_PHILIPS_OFF\
+    SHIFTIN(0x379, RTW_ANAPARM_RFPOW1_MASK)
+
+#define RTW_ANAPARM_RFPOW_PHILIPS_ON   SHIFTIN(0x328, RTW_ANAPARM_RFPOW1_MASK)
+
+#define RTW_ANAPARM_CARDSP_MASK        __BITS(19,0)            /* undocumented
+                                                        * card-specific
+                                                        * bits from the
+                                                        * EEPROM.
+                                                        */
+
+#define RTW_MSR                0x58    /* Media Status Register, 8b */
+/* Network Type and Link Status */
+#define RTW_MSR_NETYPE_MASK    __BITS(3,2)
+/* AP, XXX RTL8181 only? */
+#define RTW_MSR_NETYPE_AP_OK   SHIFTIN(3, RTW_MSR_NETYPE_MASK)
+/* infrastructure link ok */
+#define RTW_MSR_NETYPE_INFRA_OK        SHIFTIN(2, RTW_MSR_NETYPE_MASK)
+/* ad-hoc link ok */
+#define RTW_MSR_NETYPE_ADHOC_OK        SHIFTIN(1, RTW_MSR_NETYPE_MASK)
+/* no link */
+#define RTW_MSR_NETYPE_NOLINK  SHIFTIN(0, RTW_MSR_NETYPE_MASK)
+
+#define RTW_CONFIG3    0x59    /* Configuration Register 3, 8b */
+#define RTW_CONFIG3_GNTSEL     __BIT(7)        /* Grant Select, read-only */
+#define RTW_CONFIG3_PARMEN     __BIT(6)        /* Set RTW_CONFIG3_PARMEN and
+                                                * RTW_9346CR_EEM_CONFIG to
+                                                * allow RTW_ANAPARM writes.
+                                                */
+#define RTW_CONFIG3_MAGIC      __BIT(5)/* Valid when RTW_CONFIG1_PMEN is
+                                        * set. If set, RTL8180 wakes up
+                                        * OS when Magic Packet is Rx'd.
+                                        */
+#define RTW_CONFIG3_CARDBEN    __BIT(3)/* Cardbus-related registers
+                                        * and functions are enabled,
+                                        * read-only. XXX RTL8180 only.
+                                        */
+#define RTW_CONFIG3_CLKRUNEN   __BIT(2)/* CLKRUN enabled, read-only.
+                                        * XXX RTL8180 only.
+                                        */
+#define RTW_CONFIG3_FUNCREGEN  __BIT(1)/* Function Registers Enabled,
+                                        * read-only. XXX RTL8180 only.
+                                        */
+#define RTW_CONFIG3_FBTBEN     __BIT(0)/* Fast back-to-back enabled,
+                                        * read-only.
+                                        */
+#define RTW_CONFIG4    0x5A    /* Configuration Register 4, 8b */
+#define RTW_CONFIG4_VCOPDN     __BIT(7)/* VCO Power Down
+                                        * 0: normal operation
+                                        *    (power-on default)
+                                        * 1: power-down VCO, RF front-end,
+                                        *    and most RTL8180 components.
+                                        */
+#define RTW_CONFIG4_PWROFF     __BIT(6)/* Power Off
+                                        * 0: normal operation
+                                        *    (power-on default)
+                                        * 1: power-down RF front-end,
+                                        *    and most RTL8180 components,
+                                        *    but leave VCO on.
+                                        *
+                                        * XXX RFMD front-end only?
+                                        */
+#define RTW_CONFIG4_PWRMGT     __BIT(5)/* Power Management
+                                        * 0: normal operation
+                                        *    (power-on default)
+                                        * 1: set Tx packet's PWRMGMT bit.
+                                        */
+#define RTW_CONFIG4_LWPME      __BIT(4)/* LANWAKE vs. PMEB: Cardbus-only
+                                        * 0: LWAKE & PMEB asserted
+                                        *    simultaneously
+                                        * 1: LWAKE asserted only if
+                                        *    both PMEB is asserted and
+                                        *    ISOLATEB is low.
+                                        * XXX RTL8180 only.
+                                        */
+#define RTW_CONFIG4_LWPTN      __BIT(2)/* see RTW_CONFIG1_LWACT
+                                        * XXX RTL8180 only.
+                                        */
+/* Radio Front-End Programming Method */
+#define RTW_CONFIG4_RFTYPE_MASK        __BITS(1,0)
+#define RTW_CONFIG4_RFTYPE_INTERSIL    SHIFTIN(1, RTW_CONFIG4_RFTYPE_MASK)
+#define RTW_CONFIG4_RFTYPE_RFMD                SHIFTIN(2, RTW_CONFIG4_RFTYPE_MASK)
+#define RTW_CONFIG4_RFTYPE_PHILIPS     SHIFTIN(3, RTW_CONFIG4_RFTYPE_MASK)
+
+#define RTW_TESTR      0x5B    /* TEST mode register, 8b */
+
+#define RTW_PSR                0x5e    /* Page Select Register, 8b */
+#define RTW_PSR_GPO    __BIT(7)/* Control/status of pin 52. */
+#define RTW_PSR_GPI    __BIT(6)/* Status of pin 64. */
+#define RTW_PSR_LEDGPO1        __BIT(5)/* Status/control of LED1 pin if
+                                * RTW_CONFIG0_LEDGPOEN is set.
+                                */
+#define RTW_PSR_LEDGPO0        __BIT(4)/* Status/control of LED0 pin if
+                                * RTW_CONFIG0_LEDGPOEN is set.
+                                */
+#define RTW_PSR_UWF    __BIT(1)/* Enable Unicast Wakeup Frame */
+#define RTW_PSR_PSEN   __BIT(0)/* 1: page 1, 0: page 0 */
+
+#define RTW_SCR                0x5f    /* Security Configuration Register, 8b */
+#define RTW_SCR_KM_MASK        __BITS(5,4)     /* Key Mode */
+#define RTW_SCR_KM_WEP104      SHIFTIN(1, RTW_SCR_KM_MASK)
+#define RTW_SCR_KM_WEP40       SHIFTIN(0, RTW_SCR_KM_MASK)
+#define RTW_SCR_TXSECON                __BIT(1)/* Enable Tx WEP. Invalid if
+                                        * neither RTW_CONFIG0_WEP40 nor
+                                        * RTW_CONFIG0_WEP104 is set.
+                                        */
+#define RTW_SCR_RXSECON                __BIT(0)/* Enable Rx WEP. Invalid if
+                                        * neither RTW_CONFIG0_WEP40 nor
+                                        * RTW_CONFIG0_WEP104 is set.
+                                        */
+
+#define        RTW_BCNITV      0x70    /* Beacon Interval Register, 16b */
+#define        RTW_BCNITV_BCNITV_MASK  __BITS(9,0)     /* TU between TBTT, written
+                                                * by host.
+                                                */
+#define        RTW_ATIMWND     0x72    /* ATIM Window Register, 16b */
+#define        RTW_ATIMWND_ATIMWND     __BITS(9,0)     /* ATIM Window length in TU,
+                                                * written by host.
+                                                */
+
+#define RTW_BINTRITV   0x74    /* Beacon Interrupt Interval Register, 16b */
+#define        RTW_BINTRITV_BINTRITV   __BITS(9,0)     /* RTL8180 wakes host with
+                                                * RTW_INTR_BCNINT at BINTRITV
+                                                * microseconds before TBTT
+                                                */
+#define RTW_ATIMTRITV  0x76    /* ATIM Interrupt Interval Register, 16b */
+#define        RTW_ATIMTRITV_ATIMTRITV __BITS(9,0)     /* RTL8180 wakes host with
+                                                * RTW_INTR_ATIMINT at ATIMTRITV
+                                                * microseconds before end of
+                                                * ATIM Window
+                                                */
+
+#define RTW_PHYDELAY   0x78    /* PHY Delay Register, 8b */
+#define RTW_PHYDELAY_REVC_MAGIC        __BIT(3)        /* Rev. C magic from reference
+                                                * driver
+                                                */
+#define RTW_PHYDELAY_PHYDELAY  __BITS(2,0)     /* microsecond Tx delay between
+                                                * MAC and RF front-end
+                                                */
+#define RTW_CRCOUNT    0x79    /* Carrier Sense Counter, 8b */
+#define        RTW_CRCOUNT_MAGIC       0x4c
+
+#define RTW_CRC16ERR   0x7a    /* CRC16 error count, 16b, XXX RTL8181 only? */
+
+#define RTW_BB 0x7c            /* Baseband interface, 32b */
+/* used for writing RTL8180's integrated baseband processor */
+#define RTW_BB_RD_MASK         __BITS(23,16)   /* data to read */
+#define RTW_BB_WR_MASK         __BITS(15,8)    /* data to write */
+#define RTW_BB_WREN            __BIT(7)        /* write enable */
+#define RTW_BB_ADDR_MASK       __BITS(6,0)     /* address */
+
+#define RTW_PHYADDR    0x7c    /* Address register for PHY interface, 8b */
+#define RTW_PHYDATAW   0x7d    /* Write data to PHY, 8b, write-only */
+#define RTW_PHYDATAR   0x7e    /* Read data from PHY, 8b (?), read-only */
+
+#define RTW_PHYCFG     0x80    /* PHY Configuration Register, 32b */
+#define RTW_PHYCFG_MAC_POLL    __BIT(31)       /* if !RTW_PHYCFG_HST,
+                                                * host sets. MAC clears
+                                                * after banging bits.
+                                                */
+#define        RTW_PHYCFG_HST          __BIT(30)       /* 1: host bangs bits
+                                                * 0: MAC bangs bits
+                                                */
+#define RTW_PHYCFG_MAC_RFTYPE_MASK     __BITS(29,28)
+#define RTW_PHYCFG_MAC_RFTYPE_INTERSIL SHIFTIN(0, RTW_PHYCFG_MAC_RFTYPE_MASK)
+#define RTW_PHYCFG_MAC_RFTYPE_RFMD     SHIFTIN(1, RTW_PHYCFG_MAC_RFTYPE_MASK)
+#define RTW_PHYCFG_MAC_RFTYPE_GCT      RTW_PHYCFG_MAC_RFTYPE_RFMD
+#define RTW_PHYCFG_MAC_RFTYPE_PHILIPS  SHIFTIN(3, RTW_PHYCFG_MAC_RFTYPE_MASK)
+#define RTW_PHYCFG_MAC_PHILIPS_ADDR_MASK       __BITS(27,24)
+#define RTW_PHYCFG_MAC_PHILIPS_DATA_MASK       __BITS(23,0)
+#define RTW_PHYCFG_MAC_MAXIM_LODATA_MASK       __BITS(27,24)
+#define RTW_PHYCFG_MAC_MAXIM_ADDR_MASK         __BITS(11,8)
+#define RTW_PHYCFG_MAC_MAXIM_HIDATA_MASK       __BITS(7,0)
+#define        RTW_PHYCFG_HST_EN               __BIT(2)
+#define        RTW_PHYCFG_HST_CLK              __BIT(1)
+#define        RTW_PHYCFG_HST_DATA             __BIT(0)
+
+#define        RTW_MAXIM_HIDATA_MASK                   __BITS(11,4)
+#define        RTW_MAXIM_LODATA_MASK                   __BITS(3,0)
+
+/**
+ ** 0x84 - 0xD3, page 1, selected when RTW_PSR[PSEN] == 1.
+ **/
+
+#define        RTW_WAKEUP0L    0x84    /* Power Management Wakeup Frame */
+#define        RTW_WAKEUP0H    0x88    /* 32b */
+
+#define        RTW_WAKEUP1L    0x8c
+#define        RTW_WAKEUP1H    0x90
+
+#define        RTW_WAKEUP2LL   0x94
+#define        RTW_WAKEUP2LH   0x98
+
+#define        RTW_WAKEUP2HL   0x9c
+#define        RTW_WAKEUP2HH   0xa0
+
+#define        RTW_WAKEUP3LL   0xa4
+#define        RTW_WAKEUP3LH   0xa8
+
+#define        RTW_WAKEUP3HL   0xac
+#define        RTW_WAKEUP3HH   0xb0
+
+#define        RTW_WAKEUP4LL   0xb4
+#define        RTW_WAKEUP4LH   0xb8
+
+#define        RTW_WAKEUP4HL   0xbc
+#define        RTW_WAKEUP4HH   0xc0
+
+#define RTW_CRC0       0xc4    /* CRC of wakeup frame 0, 16b */
+#define RTW_CRC1       0xc6    /* CRC of wakeup frame 1, 16b */
+#define RTW_CRC2       0xc8    /* CRC of wakeup frame 2, 16b */
+#define RTW_CRC3       0xca    /* CRC of wakeup frame 3, 16b */
+#define RTW_CRC4       0xcc    /* CRC of wakeup frame 4, 16b */
+
+/**
+ ** 0x84 - 0xD3, page 0, selected when RTW_PSR[PSEN] == 0.
+ **/
+
+/* Default Key Registers, each 128b
+ *
+ * If RTW_SCR_KM_WEP104, 104 lsb are the key.
+ * If RTW_SCR_KM_WEP40, 40 lsb are the key.
+ */
+#define RTW_DK0                0x90    /* Default Key 0 Register, 128b */
+#define RTW_DK1                0xa0    /* Default Key 1 Register, 128b */
+#define RTW_DK2                0xb0    /* Default Key 2 Register, 128b */
+#define RTW_DK3                0xc0    /* Default Key 3 Register, 128b */
+
+#define        RTW_CONFIG5     0xd8    /* Configuration Register 5, 8b */
+#define RTW_CONFIG5_TXFIFOOK   __BIT(7)/* Tx FIFO self-test pass, read-only */
+#define RTW_CONFIG5_RXFIFOOK   __BIT(6)/* Rx FIFO self-test pass, read-only */
+#define RTW_CONFIG5_CALON      __BIT(5)/* 1: start calibration cycle
+                                        *    and raise AGCRESET pin.
+                                        * 0: lower AGCRESET pin
+                                        */
+#define RTW_CONFIG5_EACPI      __BIT(2)/* Enable ACPI Wake up, default 0 */
+#define RTW_CONFIG5_LANWAKE    __BIT(1)/* Enable LAN Wake signal,
+                                        * from EEPROM
+                                        */
+#define RTW_CONFIG5_PMESTS     __BIT(0)/* 1: both software & PCI Reset
+                                        *    reset PME_Status
+                                        * 0: only software resets PME_Status
+                                        *
+                                        * From EEPROM.
+                                        */
+
+#define        RTW_TPPOLL      0xd9    /* Transmit Priority Polling Register, 8b,
+                                * write-only.
+                                */
+#define RTW_TPPOLL_BQ  __BIT(7)/* RTL8180 clears to notify host of a beacon
+                                * Tx. Host writes have no effect.
+                                */
+#define RTW_TPPOLL_HPQ __BIT(6)/* Host writes 1 to notify RTL8180 of
+                                * high-priority Tx packets, RTL8180 clears
+                                * to after high-priority Tx is complete.
+                                */
+#define RTW_TPPOLL_NPQ __BIT(5)/* If RTW_CONFIG2_DPS is set,
+                                * host writes 1 to notify RTL8180 of
+                                * normal-priority Tx packets, RTL8180 clears
+                                * after normal-priority Tx is complete.
+                                *
+                                * If RTW_CONFIG2_DPS is clear, host writes
+                                * have no effect. RTL8180 clears after
+                                * normal-priority Tx is complete.
+                                */
+#define RTW_TPPOLL_LPQ __BIT(4)/* Host writes 1 to notify RTL8180 of
+                                * low-priority Tx packets, RTL8180 clears
+                                * after low-priority Tx is complete.
+                                */
+#define RTW_TPPOLL_SBQ __BIT(3)/* Host writes 1 to tell RTL8180 to
+                                * stop beacon DMA. This bit is invalid
+                                * when RTW_CONFIG2_DPS is set.
+                                */
+#define RTW_TPPOLL_SHPQ        __BIT(2)/* Host writes 1 to tell RTL8180 to
+                                * stop high-priority DMA.
+                                */
+#define RTW_TPPOLL_SNPQ        __BIT(1)/* Host writes 1 to tell RTL8180 to
+                                * stop normal-priority DMA. This bit is invalid
+                                * when RTW_CONFIG2_DPS is set.
+                                */
+#define RTW_TPPOLL_SLPQ        __BIT(0)/* Host writes 1 to tell RTL8180 to
+                                * stop low-priority DMA.
+                                */
+
+/* Start all queues. */
+#define        RTW_TPPOLL_ALL  (RTW_TPPOLL_BQ | RTW_TPPOLL_HPQ | \
+                        RTW_TPPOLL_NPQ | RTW_TPPOLL_LPQ)
+/* Check all queues' activity. */
+#define        RTW_TPPOLL_ACTIVE       RTW_TPPOLL_ALL
+/* Stop all queues. */
+#define        RTW_TPPOLL_SALL (RTW_TPPOLL_SBQ | RTW_TPPOLL_SHPQ | \
+                        RTW_TPPOLL_SNPQ | RTW_TPPOLL_SLPQ)
+
+#define        RTW_CWR         0xdc    /* Contention Window Register, 16b, read-only */
+/* Contention Window: indicates number of contention windows before Tx
+ */
+#define        RTW_CWR_CW      __BITS(9,0)
+
+/* Retry Count Register, 16b, read-only */
+#define        RTW_RETRYCTR    0xde
+/* Retry Count: indicates number of retries after Tx */
+#define        RTW_RETRYCTR_RETRYCT    __BITS(7,0)
+
+#define RTW_RDSAR      0xe4    /* Receive descriptor Start Address Register,
+                                * 32b, 256-byte alignment.
+                                */
+/* Function Event Register, 32b, Cardbus only. Only valid when
+ * both RTW_CONFIG3_CARDBEN and RTW_CONFIG3_FUNCREGEN are set.
+ */
+#define RTW_FER                0xf0
+#define RTW_FER_INTR   __BIT(15)       /* set when RTW_FFER_INTR is set */
+#define RTW_FER_GWAKE  __BIT(4)        /* General Wakeup */
+/* Function Event Mask Register, 32b, Cardbus only. Only valid when
+ * both RTW_CONFIG3_CARDBEN and RTW_CONFIG3_FUNCREGEN are set.
+ */
+#define RTW_FEMR       0xf4
+#define RTW_FEMR_INTR  __BIT(15)       /* set when RTW_FFER_INTR is set */
+#define RTW_FEMR_WKUP  __BIT(14)       /* Wakeup Mask */
+#define RTW_FEMR_GWAKE __BIT(4)        /* General Wakeup */
+/* Function Present State Register, 32b, read-only, Cardbus only.
+ * Only valid when both RTW_CONFIG3_CARDBEN and RTW_CONFIG3_FUNCREGEN
+ * are set.
+ */
+#define RTW_FPSR       0xf8
+#define RTW_FPSR_INTR  __BIT(15)       /* TBD */
+#define RTW_FPSR_GWAKE __BIT(4)        /* General Wakeup: TBD */
+/* Function Force Event Register, 32b, write-only, Cardbus only.
+ * Only valid when both RTW_CONFIG3_CARDBEN and RTW_CONFIG3_FUNCREGEN
+ * are set.
+ */
+#define RTW_FFER       0xfc
+#define RTW_FFER_INTR  __BIT(15)       /* TBD */
+#define RTW_FFER_GWAKE __BIT(4)        /* General Wakeup: TBD */
+
+/* Serial EEPROM offsets */
+#define RTW_SR_ID      0x00    /* 16b */
+#define RTW_SR_VID     0x02    /* 16b */
+#define RTW_SR_DID     0x04    /* 16b */
+#define RTW_SR_SVID    0x06    /* 16b */
+#define RTW_SR_SMID    0x08    /* 16b */
+#define RTW_SR_MNGNT   0x0a
+#define RTW_SR_MXLAT   0x0b
+#define RTW_SR_RFCHIPID        0x0c
+#define RTW_SR_CONFIG3 0x0d
+#define RTW_SR_MAC     0x0e    /* 6 bytes */
+#define RTW_SR_CONFIG0 0x14
+#define RTW_SR_CONFIG1 0x15
+#define RTW_SR_PMC     0x16    /* Power Management Capabilities, 16b */
+#define RTW_SR_CONFIG2 0x18
+#define RTW_SR_CONFIG4 0x19
+#define RTW_SR_ANAPARM 0x1a    /* Analog Parameters, 32b */
+#define RTW_SR_TESTR   0x1e
+#define RTW_SR_CONFIG5 0x1f
+#define RTW_SR_TXPOWER1                0x20
+#define RTW_SR_TXPOWER2                0x21
+#define RTW_SR_TXPOWER3                0x22
+#define RTW_SR_TXPOWER4                0x23
+#define RTW_SR_TXPOWER5                0x24
+#define RTW_SR_TXPOWER6                0x25
+#define RTW_SR_TXPOWER7                0x26
+#define RTW_SR_TXPOWER8                0x27
+#define RTW_SR_TXPOWER9                0x28
+#define RTW_SR_TXPOWER10       0x29
+#define RTW_SR_TXPOWER11       0x2a
+#define RTW_SR_TXPOWER12       0x2b
+#define RTW_SR_TXPOWER13       0x2c
+#define RTW_SR_TXPOWER14       0x2d
+#define RTW_SR_CHANNELPLAN     0x2e    /* bitmap of channels to scan */
+#define RTW_SR_ENERGYDETTHR    0x2f    /* energy-detect threshold */
+#define RTW_SR_ENERGYDETTHR_DEFAULT    0x0c    /* use this if old SROM */
+#define RTW_SR_CISPOINTER      0x30    /* 16b */
+#define RTW_SR_RFPARM          0x32    /* RF-specific parameter */
+#define RTW_SR_RFPARM_DIGPHY   __BIT(0)        /* 1: digital PHY */
+#define RTW_SR_RFPARM_DFLANTB  __BIT(1)        /* 1: antenna B is default */
+#define RTW_SR_RFPARM_CS_MASK  __BITS(2,3)     /* carrier-sense type */
+#define RTW_SR_VERSION         0x3c    /* EEPROM content version, 16b */
+#define RTW_SR_CRC             0x3e    /* EEPROM content CRC, 16b */
+#define RTW_SR_VPD             0x40    /* Vital Product Data, 64 bytes */
+#define RTW_SR_CIS             0x80    /* CIS Data, 93c56 only, 128 bytes*/
+
+/*
+ * RTL8180 Transmit/Receive Descriptors
+ */
+
+/* the first descriptor in each ring must be on a 256-byte boundary */
+#define RTW_DESC_ALIGNMENT 256
+
+/* Tx descriptor */
+struct rtw_txdesc {
+       uint32_t        td_ctl0;
+       uint32_t        td_ctl1;
+       uint32_t        td_buf;
+       uint32_t        td_len;
+       uint32_t        td_next;
+       uint32_t        td_rsvd[3];
+};
+
+#define td_stat td_ctl0
+
+#define RTW_TXCTL0_OWN                 __BIT(31)       /* 1: ready to Tx */
+#define RTW_TXCTL0_RSVD0               __BIT(30)       /* reserved */
+#define RTW_TXCTL0_FS                  __BIT(29)       /* first segment */
+#define RTW_TXCTL0_LS                  __BIT(28)       /* last segment */
+
+#define RTW_TXCTL0_RATE_MASK           __BITS(27,24)   /* Tx rate */
+#define RTW_TXCTL0_RATE_1MBPS          SHIFTIN(0, RTW_TXCTL0_RATE_MASK)
+#define RTW_TXCTL0_RATE_2MBPS          SHIFTIN(1, RTW_TXCTL0_RATE_MASK)
+#define RTW_TXCTL0_RATE_5MBPS          SHIFTIN(2, RTW_TXCTL0_RATE_MASK)
+#define RTW_TXCTL0_RATE_11MBPS         SHIFTIN(3, RTW_TXCTL0_RATE_MASK)
+
+#define RTW_TXCTL0_RTSEN               __BIT(23)       /* RTS Enable */
+
+#define RTW_TXCTL0_RTSRATE_MASK                __BITS(22,19)   /* Tx rate */
+#define RTW_TXCTL0_RTSRATE_1MBPS       SHIFTIN(0, RTW_TXCTL0_RTSRATE_MASK)
+#define RTW_TXCTL0_RTSRATE_2MBPS       SHIFTIN(1, RTW_TXCTL0_RTSRATE_MASK)
+#define RTW_TXCTL0_RTSRATE_5MBPS       SHIFTIN(2, RTW_TXCTL0_RTSRATE_MASK)
+#define RTW_TXCTL0_RTSRATE_11MBPS      SHIFTIN(3, RTW_TXCTL0_RTSRATE_MASK)
+
+#define RTW_TXCTL0_BEACON              __BIT(18)       /* packet is a beacon */
+#define RTW_TXCTL0_MOREFRAG            __BIT(17)       /* another fragment
+                                                        * follows
+                                                        */
+/* add short PLCP preamble and header */
+#define RTW_TXCTL0_SPLCP               __BIT(16)
+#define RTW_TXCTL0_KEYID_MASK          __BITS(15,14)   /* default key id */
+#define RTW_TXCTL0_RSVD1_MASK          __BITS(13,12)   /* reserved */
+#define RTW_TXCTL0_TPKTSIZE_MASK       __BITS(11,0)    /* Tx packet size
+                                                        * in bytes
+                                                        */
+
+#define RTW_TXSTAT_OWN         RTW_TXCTL0_OWN
+#define RTW_TXSTAT_RSVD0       RTW_TXCTL0_RSVD0
+#define RTW_TXSTAT_FS          RTW_TXCTL0_FS
+#define RTW_TXSTAT_LS          RTW_TXCTL0_LS
+#define RTW_TXSTAT_RSVD1_MASK  __BITS(27,16)
+#define RTW_TXSTAT_TOK         __BIT(15)
+#define RTW_TXSTAT_RTSRETRY_MASK       __BITS(14,8)    /* RTS retry count */
+#define RTW_TXSTAT_DRC_MASK            __BITS(7,0)     /* Data retry count */
+
+#define RTW_TXCTL1_LENGEXT     __BIT(31)       /* supplements _LENGTH
+                                                * in packets sent 5.5Mb/s or
+                                                * faster
+                                                */
+#define RTW_TXCTL1_LENGTH_MASK __BITS(30,16)   /* PLCP length (microseconds) */
+#define RTW_TXCTL1_RTSDUR_MASK __BITS(15,0)    /* RTS Duration
+                                                * (microseconds)
+                                                */
+
+#define RTW_TXLEN_LENGTH_MASK  __BITS(11,0)    /* Tx buffer length in bytes */
+
+/* Rx descriptor */
+struct rtw_rxdesc {
+    uint32_t   rd_ctl;
+    uint32_t   rd_rsvd0;
+    uint32_t   rd_buf;
+    uint32_t   rd_rsvd1;
+};
+
+#define rd_stat rd_ctl
+#define rd_rssi rd_rsvd0
+#define rd_tsftl rd_buf                /* valid only when RTW_RXSTAT_LS is set */
+#define rd_tsfth rd_rsvd1      /* valid only when RTW_RXSTAT_LS is set */
+
+#define RTW_RXCTL_OWN          __BIT(31)       /* 1: owned by NIC */
+#define RTW_RXCTL_EOR          __BIT(30)       /* end of ring */
+#define RTW_RXCTL_FS           __BIT(29)       /* first segment */
+#define RTW_RXCTL_LS           __BIT(28)       /* last segment */
+#define RTW_RXCTL_RSVD0_MASK   __BITS(29,12)   /* reserved */
+#define RTW_RXCTL_LENGTH_MASK  __BITS(11,0)    /* Rx buffer length */
+
+#define RTW_RXSTAT_OWN         RTW_RXCTL_OWN
+#define RTW_RXSTAT_EOR         RTW_RXCTL_EOR
+#define RTW_RXSTAT_FS          RTW_RXCTL_FS    /* first segment */
+#define RTW_RXSTAT_LS          RTW_RXCTL_LS    /* last segment */
+#define RTW_RXSTAT_DMAFAIL     __BIT(27)       /* DMA failure on this pkt */
+#define RTW_RXSTAT_BOVF                __BIT(26)       /* buffer overflow XXX means
+                                                * FIFO exhausted?
+                                                */
+#define RTW_RXSTAT_SPLCP       __BIT(25)       /* Rx'd with short preamble
+                                                * and PLCP header
+                                                */
+#define RTW_RXSTAT_RSVD1       __BIT(24)       /* reserved */
+#define RTW_RXSTAT_RATE_MASK   __BITS(23,20)   /* Rx rate */
+#define RTW_RXSTAT_RATE_1MBPS  SHIFTIN(0, RTW_RXSTAT_RATE_MASK)
+#define RTW_RXSTAT_RATE_2MBPS  SHIFTIN(1, RTW_RXSTAT_RATE_MASK)
+#define RTW_RXSTAT_RATE_5MBPS  SHIFTIN(2, RTW_RXSTAT_RATE_MASK)
+#define RTW_RXSTAT_RATE_11MBPS SHIFTIN(3, RTW_RXSTAT_RATE_MASK)
+#define RTW_RXSTAT_MIC         __BIT(19)       /* XXX from reference driver */
+#define RTW_RXSTAT_MAR         __BIT(18)       /* is multicast */
+#define RTW_RXSTAT_PAR         __BIT(17)       /* matches RTL8180's MAC */
+#define RTW_RXSTAT_BAR         __BIT(16)       /* is broadcast */
+#define RTW_RXSTAT_RES         __BIT(15)       /* error summary. valid when
+                                                * RTW_RXSTAT_LS set. indicates
+                                                * that either RTW_RXSTAT_CRC32
+                                                * or RTW_RXSTAT_ICV is set.
+                                                */
+#define RTW_RXSTAT_PWRMGT      __BIT(14)       /* 802.11 PWRMGMT bit is set */
+#define RTW_RXSTAT_CRC16       __BIT(14)       /* XXX CRC16 error, from
+                                                * reference driver
+                                                */
+#define RTW_RXSTAT_CRC32       __BIT(13)       /* CRC32 error */
+#define RTW_RXSTAT_ICV         __BIT(12)       /* ICV error */
+#define RTW_RXSTAT_LENGTH_MASK __BITS(11,0)    /* frame length, including
+                                                * CRC32
+                                                */
+
+/* Convenient status conjunction. */
+#define RTW_RXSTAT_ONESEG      (RTW_RXSTAT_FS|RTW_RXSTAT_LS)
+/* Convenient status disjunctions. */
+#define RTW_RXSTAT_IOERROR     (RTW_RXSTAT_DMAFAIL|RTW_RXSTAT_BOVF)
+#define RTW_RXSTAT_DEBUG       (RTW_RXSTAT_SPLCP|RTW_RXSTAT_MAR|\
+                                RTW_RXSTAT_PAR|RTW_RXSTAT_BAR|\
+                                RTW_RXSTAT_PWRMGT|RTW_RXSTAT_CRC32|\
+                                RTW_RXSTAT_ICV)
+
+
+#define RTW_RXRSSI_VLAN                __BITS(31,16)   /* XXX from reference driver */
+/* for Philips RF front-ends */
+#define RTW_RXRSSI_RSSI                __BITS(15,8)    /* RF energy at the PHY */
+/* for RF front-ends by Intersil, Maxim, RFMD */
+#define RTW_RXRSSI_IMR_RSSI    __BITS(15,9)    /* RF energy at the PHY */
+#define RTW_RXRSSI_IMR_LNA     __BIT(8)        /* 1: LNA activated */
+#define RTW_RXRSSI_SQ          __BITS(7,0)     /* Barker code-lock quality */
+
+#define RTW_READ8(regs, ofs)                                           \
+       bus_space_read_1((regs)->r_bt, (regs)->r_bh, (ofs))
+
+#define RTW_READ16(regs, ofs)                                          \
+       bus_space_read_2((regs)->r_bt, (regs)->r_bh, (ofs))
+
+#define RTW_READ(regs, ofs)                                            \
+       bus_space_read_4((regs)->r_bt, (regs)->r_bh, (ofs))
+
+#define RTW_WRITE8(regs, ofs, val)                                     \
+       bus_space_write_1((regs)->r_bt, (regs)->r_bh, (ofs), (val))
+
+#define RTW_WRITE16(regs, ofs, val)                                    \
+       bus_space_write_2((regs)->r_bt, (regs)->r_bh, (ofs), (val))
+
+#define RTW_WRITE(regs, ofs, val)                                      \
+       bus_space_write_4((regs)->r_bt, (regs)->r_bh, (ofs), (val))
+
+#define        RTW_ISSET(regs, reg, mask)                                      \
+       (RTW_READ((regs), (reg)) & (mask))
+
+#define        RTW_CLR(regs, reg, mask)                                        \
+       RTW_WRITE((regs), (reg), RTW_READ((regs), (reg)) & ~(mask))
+
+/* bus_space(9) lied? */
+#ifndef BUS_SPACE_BARRIER_SYNC
+#define BUS_SPACE_BARRIER_SYNC (BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE)
+#endif
+
+#ifndef BUS_SPACE_BARRIER_READ_BEFORE_READ
+#define BUS_SPACE_BARRIER_READ_BEFORE_READ BUS_SPACE_BARRIER_READ
+#endif
+
+#ifndef BUS_SPACE_BARRIER_READ_BEFORE_WRITE
+#define BUS_SPACE_BARRIER_READ_BEFORE_WRITE BUS_SPACE_BARRIER_READ
+#endif
+
+#ifndef BUS_SPACE_BARRIER_WRITE_BEFORE_READ
+#define BUS_SPACE_BARRIER_WRITE_BEFORE_READ BUS_SPACE_BARRIER_WRITE
+#endif
+
+#ifndef BUS_SPACE_BARRIER_WRITE_BEFORE_WRITE
+#define BUS_SPACE_BARRIER_WRITE_BEFORE_WRITE BUS_SPACE_BARRIER_WRITE
+#endif
+
+/*
+ * Bus barrier
+ *
+ * Complete outstanding read and/or write ops on [reg0, reg1]
+ * ([reg1, reg0]) before starting new ops on the same region. See
+ * acceptable bus_space_barrier(9) for the flag definitions.
+ */
+#define RTW_BARRIER(regs, reg0, reg1, flags)                   \
+       bus_space_barrier((regs)->r_bh, (regs)->r_bt,           \
+           MIN(reg0, reg1), MAX(reg0, reg1) - MIN(reg0, reg1) + 4, flags)
+
+/*
+ * Barrier convenience macros.
+ */
+/* sync */
+#define RTW_SYNC(regs, reg0, reg1)                             \
+       RTW_BARRIER(regs, reg0, reg1, BUS_SPACE_BARRIER_SYNC)
+
+/* write-before-write */
+#define RTW_WBW(regs, reg0, reg1)                              \
+       RTW_BARRIER(regs, reg0, reg1, BUS_SPACE_BARRIER_WRITE_BEFORE_WRITE)
+
+/* write-before-read */
+#define RTW_WBR(regs, reg0, reg1)                              \
+       RTW_BARRIER(regs, reg0, reg1, BUS_SPACE_BARRIER_WRITE_BEFORE_READ)
+
+/* read-before-read */
+#define RTW_RBR(regs, reg0, reg1)                              \
+       RTW_BARRIER(regs, reg0, reg1, BUS_SPACE_BARRIER_READ_BEFORE_READ)
+
+/* read-before-read */
+#define RTW_RBW(regs, reg0, reg1)                              \
+       RTW_BARRIER(regs, reg0, reg1, BUS_SPACE_BARRIER_READ_BEFORE_WRITE)
+
+#define RTW_WBRW(regs, reg0, reg1)                             \
+               RTW_BARRIER(regs, reg0, reg1,                   \
+                   BUS_SPACE_BARRIER_WRITE_BEFORE_READ |       \
+                   BUS_SPACE_BARRIER_WRITE_BEFORE_WRITE)
+
+/*
+ * Registers for RTL8180L's built-in baseband modem.
+ */
+#define RTW_BBP_SYS1           0x00
+#define RTW_BBP_TXAGC          0x03    /* guess: transmit auto gain control */
+#define RTW_BBP_LNADET         0x04    /* guess: low-noise amplifier activation
+                                        * threshold
+                                        */
+#define RTW_BBP_IFAGCINI       0x05    /* guess: intermediate frequency (IF)
+                                        * auto-gain control (AGC) initial value
+                                        */
+#define RTW_BBP_IFAGCLIMIT     0x06    /* guess: IF AGC maximum value */
+#define RTW_BBP_IFAGCDET       0x07    /* guess: activation threshold for
+                                        * IF AGC loop
+                                        */
+
+#define RTW_BBP_ANTATTEN       0x10    /* guess: antenna & attenuation */
+#define RTW_BBP_ANTATTEN_GCT_MAGIC             0xa3
+#define RTW_BBP_ANTATTEN_PHILIPS_MAGIC         0x91
+#define RTW_BBP_ANTATTEN_INTERSIL_MAGIC                0x92
+#define RTW_BBP_ANTATTEN_RFMD_MAGIC            0x93
+#define RTW_BBP_ANTATTEN_MAXIM_MAGIC           0xb3
+#define        RTW_BBP_ANTATTEN_DFLANTB                0x40
+#define        RTW_BBP_ANTATTEN_CHAN14                 0x0c
+
+#define RTW_BBP_TRL                    0x11    /* guess: transmit/receive
+                                                * switch latency
+                                                */
+#define RTW_BBP_SYS2                   0x12
+#define RTW_BBP_SYS2_ANTDIV            0x80    /* enable antenna diversity */
+#define RTW_BBP_SYS2_RATE_MASK         __BITS(5,4)     /* loopback rate?
+                                                        * 0: 1Mbps
+                                                        * 1: 2Mbps
+                                                        * 2: 5.5Mbps
+                                                        * 3: 11Mbps
+                                                        */
+#define RTW_BBP_SYS3                   0x13
+/* carrier-sense threshold */
+#define RTW_BBP_SYS3_CSTHRESH_MASK     __BITS(0,3)
+#define RTW_BBP_CHESTLIM       0x19    /* guess: channel energy-detect
+                                        * threshold
+                                        */
+#define RTW_BBP_CHSQLIM                0x1a    /* guess: channel signal-quality
+                                        * threshold
+                                        */
diff --git a/sys/dev/netif/rtw/rtwvar.h b/sys/dev/netif/rtw/rtwvar.h
new file mode 100644 (file)
index 0000000..88f40a5
--- /dev/null
@@ -0,0 +1,532 @@
+/*
+ * Copyright (c) 2004, 2005 David Young.  All rights reserved.
+ *
+ * Driver for the Realtek RTL8180 802.11 MAC/BBP by David Young.
+ *
+ * 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: rtwvar.h,v 1.28 2006/02/16 20:17:16 perry Exp $
+ * $DragonFly: src/sys/dev/netif/rtw/rtwvar.h,v 1.1 2006/09/03 07:37:58 sephe Exp $
+ */
+
+#ifndef _DEV_IC_RTWVAR_H_
+#define        _DEV_IC_RTWVAR_H_
+
+/*
+ * 802.11 frame duration definitions.
+ */
+
+struct rtw_duration {
+       uint16_t        d_rts_dur;
+       uint16_t        d_data_dur;
+       uint16_t        d_plcp_len;
+       uint8_t         d_residue;      /* unused octets in time slot */
+};
+
+#ifdef RTW_DEBUG
+#define        RTW_DEBUG_TUNE          0x0000001
+#define        RTW_DEBUG_PKTFILT       0x0000002
+#define        RTW_DEBUG_XMIT          0x0000004
+#define        RTW_DEBUG_XMIT_DESC     0x0000008
+#define        RTW_DEBUG_NODE          0x0000010
+#define        RTW_DEBUG_PWR           0x0000020
+#define        RTW_DEBUG_ATTACH        0x0000040
+#define        RTW_DEBUG_REGDUMP       0x0000080
+#define        RTW_DEBUG_ACCESS        0x0000100
+#define        RTW_DEBUG_RESET         0x0000200
+#define        RTW_DEBUG_INIT          0x0000400
+#define        RTW_DEBUG_IOSTATE       0x0000800
+#define        RTW_DEBUG_RECV          0x0001000
+#define        RTW_DEBUG_RECV_DESC     0x0002000
+#define        RTW_DEBUG_IO_KICK       0x0004000
+#define        RTW_DEBUG_INTR          0x0008000
+#define        RTW_DEBUG_PHY           0x0010000
+#define        RTW_DEBUG_PHYIO         0x0020000
+#define        RTW_DEBUG_PHYBITIO      0x0040000
+#define        RTW_DEBUG_TIMEOUT       0x0080000
+#define        RTW_DEBUG_BUGS          0x0100000
+#define        RTW_DEBUG_BEACON        0x0200000
+#define        RTW_DEBUG_LED           0x0400000
+#define        RTW_DEBUG_KEY           0x0800000
+#define        RTW_DEBUG_XMIT_RSRC     0x1000000
+#define        RTW_DEBUG_OACTIVE       0x2000000
+#define        RTW_DEBUG_MAX           0x3ffffff
+
+extern int     rtw_debug;
+#define RTW_DPRINTF(__flags, __x)      \
+       if ((rtw_debug & (__flags)) != 0) printf __x
+#define        DPRINTF(__sc, __flags, __x)                             \
+       if (((__sc)->sc_if.if_flags & IFF_DEBUG) != 0)  \
+               RTW_DPRINTF(__flags, __x)
+#define        RTW_PRINT_REGS(__regs, __dvname, __where)       \
+       rtw_print_regs((__regs), (__dvname), (__where))
+#else /* RTW_DEBUG */
+#define RTW_DPRINTF(__flags, __x)
+#define        DPRINTF(__sc, __flags, __x)
+#define        RTW_PRINT_REGS(__regs, __dvname, __where)
+#endif /* RTW_DEBUG */
+
+enum rtw_locale {
+       RTW_LOCALE_USA  = 0,
+       RTW_LOCALE_EUROPE,
+       RTW_LOCALE_JAPAN,
+       RTW_LOCALE_UNKNOWN
+};
+
+enum rtw_rfchipid {
+       RTW_RFCHIPID_RESERVED   = 0,
+       RTW_RFCHIPID_INTERSIL   = 1,
+       RTW_RFCHIPID_RFMD       = 2,
+       RTW_RFCHIPID_PHILIPS    = 3,
+       RTW_RFCHIPID_MAXIM      = 4,
+       RTW_RFCHIPID_GCT        = 5
+};
+
+/* sc_flags */
+#define RTW_F_ENABLED          0x00000001      /* chip is enabled */
+#define RTW_F_DIGPHY           0x00000002      /* digital PHY */
+#define RTW_F_DFLANTB          0x00000004      /* B antenna is default */
+#define RTW_F_ANTDIV           0x00000010      /* h/w antenna diversity */
+#define RTW_F_9356SROM         0x00000020      /* 93c56 SROM */
+#define RTW_F_SLEEP            0x00000040      /* chip is asleep */
+#define RTW_F_INVALID          0x00000080      /* chip is absent */
+#define        RTW_F_DK_VALID          0x00000100      /* keys in DK0-DK3 are valid */
+#define        RTW_C_RXWEP_40          0x00000200      /* h/w decrypts 40-bit WEP */
+#define        RTW_C_RXWEP_104         0x00000400      /* h/w decrypts 104-bit WEP */
+       /* all PHY flags */
+#define RTW_F_ALLPHY           (RTW_F_DIGPHY|RTW_F_DFLANTB|RTW_F_ANTDIV)
+
+enum rtw_access {
+       RTW_ACCESS_NONE         = 0,
+       RTW_ACCESS_CONFIG       = 1,
+       RTW_ACCESS_ANAPARM      = 2
+};
+
+struct rtw_regs {
+       bus_space_tag_t         r_bt;
+       bus_space_handle_t      r_bh;
+       enum rtw_access         r_access;
+       int                     r_type;
+       int                     r_rid;
+       struct resource         *r_res;
+};
+
+#define RTW_SR_GET(sr, ofs) \
+    (((sr)->sr_content[(ofs)/2] >> (((ofs) % 2 == 0) ? 0 : 8)) & 0xff)
+
+#define RTW_SR_GET16(sr, ofs) \
+    (RTW_SR_GET((sr), (ofs)) | (RTW_SR_GET((sr), (ofs) + 1) << 8))
+
+struct rtw_srom {
+       uint16_t        *sr_content;
+       uint16_t        sr_size;
+};
+
+struct rtw_rxsoft {
+       struct mbuf     *rs_mbuf;
+       bus_dmamap_t    rs_dmamap;
+       bus_addr_t      rs_phyaddr;
+};
+
+struct rtw_txsoft {
+       STAILQ_ENTRY(rtw_txsoft)        ts_q;
+       struct mbuf                     *ts_mbuf;
+       bus_dmamap_t                    ts_dmamap;
+       struct ieee80211_node           *ts_ni;         /* destination node */
+       int                             ts_ratectl;
+       int                             ts_rateidx;
+       u_int                           ts_first;       /* 1st hw descriptor */
+       u_int                           ts_last;        /* last hw descriptor */
+       struct rtw_duration             ts_d0;
+       struct rtw_duration             ts_dn;
+};
+
+#define RTW_NTXPRI     4       /* number of Tx priorities */
+#define RTW_TXPRILO    0
+#define RTW_TXPRIMD    1
+#define RTW_TXPRIHI    2
+#define RTW_TXPRIBCN   3       /* beacon priority */
+
+#define RTW_MAXPKTSEGS 64      /* Max 64 segments per Tx packet */
+
+#define CASSERT(cond, complaint)       \
+       complaint[(cond) ? 0 : -1] = complaint[(cond) ? 0 : -1]
+
+/*
+ * Note well: the descriptor rings must begin on RTW_DESC_ALIGNMENT
+ * boundaries.  I allocate them consecutively from one buffer, so
+ * just round up.
+ */
+#define RTW_TXQLENLO   64      /* low-priority queue length */
+#define RTW_TXQLENMD   64      /* medium-priority */
+#define RTW_TXQLENHI   64      /* high-priority */
+#define RTW_TXQLENBCN  8       /* beacon */
+
+#define RTW_NTXDESCLO  RTW_TXQLENLO
+#define RTW_NTXDESCMD  RTW_TXQLENMD
+#define RTW_NTXDESCHI  RTW_TXQLENHI
+#define RTW_NTXDESCBCN RTW_TXQLENBCN
+
+#define RTW_NTXDESCTOTAL       (RTW_NTXDESCLO + RTW_NTXDESCMD +        \
+                                RTW_NTXDESCHI + RTW_NTXDESCBCN)
+
+#define RTW_RXQLEN     64
+
+struct rtw_rxdesc_blk {
+       int                     rdb_ndesc;
+       int                     rdb_next;
+
+       bus_dma_tag_t           rdb_dmat;
+       bus_dmamap_t            rdb_dmamap;
+       struct rtw_rxdesc       *rdb_desc;
+
+       uint32_t                rdb_base;       /* XXX bus_addr_t */
+};
+
+struct rtw_txdesc_blk {
+       int                     tdb_ndesc;
+       int                     tdb_next;
+       int                     tdb_nfree;
+
+       bus_dma_tag_t           tdb_dmat;
+       bus_dmamap_t            tdb_dmamap;
+       struct rtw_txdesc       *tdb_desc;
+
+       bus_size_t              tdb_basereg;
+       uint32_t                tdb_base;       /* XXX bus_addr_t */
+};
+
+#define RTW_NEXT_IDX(__htc, __idx)     (((__idx) + 1) % (__htc)->tdb_ndesc)
+
+#define RTW_NEXT_DESC(__htc, __idx)    \
+       ((__htc)->tdb_base +            \
+        sizeof(struct rtw_txdesc) * RTW_NEXT_IDX((__htc), (__idx)))
+
+STAILQ_HEAD(rtw_txq, rtw_txsoft);
+
+struct rtw_txsoft_blk {
+       /* dirty/free s/w descriptors */
+       struct rtw_txq          tsb_dirtyq;
+       struct rtw_txq          tsb_freeq;
+       u_int                   tsb_ndesc;
+       int                     tsb_tx_timer;
+       struct rtw_txsoft       *tsb_desc;
+       uint8_t                 tsb_poll;
+};
+
+struct rtw_descs {
+       struct rtw_txdesc       hd_txlo[RTW_NTXDESCLO];
+       struct rtw_txdesc       hd_txmd[RTW_NTXDESCMD];
+       struct rtw_txdesc       hd_txhi[RTW_NTXDESCMD];
+       struct rtw_rxdesc       hd_rx[RTW_RXQLEN];
+       struct rtw_txdesc       hd_bcn[RTW_NTXDESCBCN];
+};
+#define RTW_DESC_OFFSET(ring, i)       offsetof(struct rtw_descs, ring[i])
+#define RTW_RING_OFFSET(ring)          RTW_DESC_OFFSET(ring, 0)
+#define RTW_RING_BASE(sc, ring)                ((sc)->sc_desc_physaddr +       \
+                                        RTW_RING_OFFSET(ring))
+
+/* Radio capture format for RTL8180. */
+
+#define RTW_RX_RADIOTAP_PRESENT                                        \
+       ((1 << IEEE80211_RADIOTAP_TSFT)                 |       \
+        (1 << IEEE80211_RADIOTAP_FLAGS)                |       \
+        (1 << IEEE80211_RADIOTAP_RATE)                 |       \
+        (1 << IEEE80211_RADIOTAP_CHANNEL)              |       \
+        (1 << IEEE80211_RADIOTAP_LOCK_QUALITY)         |       \
+        (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)         |       \
+        0)
+
+struct rtw_rx_radiotap_header {
+       struct ieee80211_radiotap_header rr_ihdr;
+       uint64_t        rr_tsft;
+       uint8_t         rr_flags;
+       uint8_t         rr_rate;
+       uint16_t        rr_chan_freq;
+       uint16_t        rr_chan_flags;
+       uint16_t        rr_barker_lock;
+       uint8_t         rr_antsignal;
+} __packed;
+
+#define RTW_TX_RADIOTAP_PRESENT                                \
+       ((1 << IEEE80211_RADIOTAP_FLAGS)        |       \
+        (1 << IEEE80211_RADIOTAP_RATE)         |       \
+        (1 << IEEE80211_RADIOTAP_CHANNEL)      |       \
+        0)
+
+struct rtw_tx_radiotap_header {
+       struct ieee80211_radiotap_header rt_ihdr;
+       uint8_t         rt_flags;
+       uint8_t         rt_rate;
+       uint16_t        rt_chan_freq;
+       uint16_t        rt_chan_flags;
+} __packed;
+
+enum rtw_attach_state {FINISHED, FINISH_DESCMAP_LOAD, FINISH_DESCMAP_CREATE,
+       FINISH_DESC_MAP, FINISH_DESC_ALLOC, FINISH_RXMAPS_CREATE,
+       FINISH_TXMAPS_CREATE, FINISH_RESET, FINISH_READ_SROM, FINISH_PARSE_SROM,
+       FINISH_RF_ATTACH, FINISH_ID_STA, FINISH_TXDESCBLK_SETUP,
+       FINISH_TXCTLBLK_SETUP, DETACHED};
+
+struct rtw_hooks {
+       void                    *rh_shutdown;   /* shutdown hook */
+       void                    *rh_power;      /* power management hook */
+};
+
+struct rtw_mtbl {
+       int                     (*mt_newstate)(struct ieee80211com *,
+                                              enum ieee80211_state, int);
+       void                    (*mt_recv_mgmt)(struct ieee80211com *,
+                                               struct mbuf *,
+                                               struct ieee80211_node *,
+                                               int, int, uint32_t);
+       struct ieee80211_node   *(*mt_node_alloc)(struct ieee80211_node_table*);
+       void                    (*mt_node_free)(struct ieee80211_node *);
+};
+
+enum rtw_pwrstate { RTW_OFF = 0, RTW_SLEEP, RTW_ON };
+
+typedef void (*rtw_continuous_tx_cb_t)(void *arg, int);
+
+struct rtw_phy {
+       struct rtw_rf   *p_rf;
+       struct rtw_regs *p_regs;
+};
+
+struct rtw_bbpset {
+       u_int   bb_antatten;
+       u_int   bb_chestlim;
+       u_int   bb_chsqlim;
+       u_int   bb_ifagcdet;
+       u_int   bb_ifagcini;
+       u_int   bb_ifagclimit;
+       u_int   bb_lnadet;
+       u_int   bb_sys1;
+       u_int   bb_sys2;
+       u_int   bb_sys3;
+       u_int   bb_trl;
+       u_int   bb_txagc;
+};
+
+struct rtw_rf {
+       void                    (*rf_destroy)(struct rtw_rf *);
+       /* args: frequency, txpower, power state */
+       int                     (*rf_init)(struct rtw_rf *, u_int, uint8_t,
+                                          enum rtw_pwrstate);
+       /* arg: power state */
+       int                     (*rf_pwrstate)(struct rtw_rf *,
+                                              enum rtw_pwrstate);
+       /* arg: frequency */
+       int                     (*rf_tune)(struct rtw_rf *, u_int);
+       /* arg: txpower */
+       int                     (*rf_txpower)(struct rtw_rf *, uint8_t);
+       rtw_continuous_tx_cb_t  rf_continuous_tx_cb;
+       void                    *rf_continuous_tx_arg;
+       struct rtw_bbpset       rf_bbpset;
+};
+
+static __inline void
+rtw_rf_destroy(struct rtw_rf *rf)
+{
+       rf->rf_destroy(rf);
+}
+
+static __inline int
+rtw_rf_init(struct rtw_rf *rf, u_int freq, uint8_t opaque_txpower,
+           enum rtw_pwrstate power)
+{
+       return rf->rf_init(rf, freq, opaque_txpower, power);
+}
+
+static __inline int
+rtw_rf_pwrstate(struct rtw_rf *rf, enum rtw_pwrstate power)
+{
+       return rf->rf_pwrstate(rf, power);
+}
+
+static __inline int
+rtw_rf_tune(struct rtw_rf *rf, u_int freq)
+{
+       return rf->rf_tune(rf, freq);
+}
+
+static __inline int
+rtw_rf_txpower(struct rtw_rf *rf, uint8_t opaque_txpower)
+{
+       return rf->rf_txpower(rf, opaque_txpower);
+}
+
+typedef int (*rtw_rf_write_t)(struct rtw_regs *, enum rtw_rfchipid, u_int,
+                             uint32_t);
+
+struct rtw_rfbus {
+       struct rtw_regs         *b_regs;
+       rtw_rf_write_t          b_write;
+};
+
+static __inline int
+rtw_rfbus_write(struct rtw_rfbus *bus, enum rtw_rfchipid rfchipid, u_int addr,
+               uint32_t val)
+{
+       return bus->b_write(bus->b_regs, rfchipid, addr, val);
+}
+
+struct rtw_max2820 {
+       struct rtw_rf           mx_rf;
+       struct rtw_rfbus        mx_bus;
+       int                     mx_is_a;        /* 1: MAX2820A/MAX2821A */
+};
+
+struct rtw_grf5101 {
+       struct rtw_rf           gr_rf;
+       struct rtw_rfbus        gr_bus;
+};
+
+struct rtw_sa2400 {
+       struct rtw_rf           sa_rf;
+       struct rtw_rfbus        sa_bus;
+       int                     sa_digphy;      /* 1: digital PHY */
+};
+
+typedef void (*rtw_pwrstate_t)(struct rtw_regs *, enum rtw_pwrstate, int, int);
+
+union rtw_keys {
+       uint8_t         rk_keys[4][16];
+       uint32_t        rk_words[16];
+};
+
+#define        RTW_LED_SLOW_TICKS      MAX(1, hz/2)
+#define        RTW_LED_FAST_TICKS      MAX(1, hz/10)
+
+struct rtw_led_state {
+#define        RTW_LED0        0x1
+#define        RTW_LED1        0x2
+       uint8_t         ls_slowblink:2;
+       uint8_t         ls_actblink:2;
+       uint8_t         ls_default:2;
+       uint8_t         ls_state;
+       uint8_t         ls_event;
+#define        RTW_LED_S_RX    0x1
+#define        RTW_LED_S_TX    0x2
+#define        RTW_LED_S_SLOW  0x4
+       struct callout  ls_slow_ch;
+       struct callout  ls_fast_ch;
+};
+
+struct rtw_softc {
+       struct ieee80211com     sc_ic;
+       struct rtw_regs         sc_regs;
+       uint32_t                sc_flags;
+
+       struct resource         *sc_irq_res;
+       int                     sc_irq_rid;
+       void                    *sc_irq_handle;
+
+       enum rtw_rfchipid       sc_rfchipid;
+       enum rtw_locale         sc_locale;
+       uint8_t                 sc_phydelay;
+
+       /* s/w Tx/Rx descriptors */
+       bus_dma_tag_t           sc_txsoft_dmat;
+       struct rtw_txsoft_blk   sc_txsoft_blk[RTW_NTXPRI];
+       struct rtw_txdesc_blk   sc_txdesc_blk[RTW_NTXPRI];
+
+       int                     sc_rxsoft_free;
+       bus_dmamap_t            sc_rxsoft_dmamap;
+       bus_dma_tag_t           sc_rxsoft_dmat;
+       struct rtw_rxsoft       sc_rxsoft[RTW_RXQLEN];
+       struct rtw_rxdesc_blk   sc_rxdesc_blk;
+
+       struct rtw_srom         sc_srom;
+
+       enum rtw_pwrstate       sc_pwrstate;
+
+       rtw_pwrstate_t          sc_pwrstate_cb;
+
+       struct rtw_rf           *sc_rf;
+
+       uint16_t                sc_inten;
+
+       /* interrupt acknowledge hook */
+       void                    (*sc_intr_ack)(struct rtw_regs *);
+
+       void                    (*sc_power)(struct rtw_softc *, int);
+       struct rtw_mtbl         sc_mtbl;
+       struct rtw_hooks        sc_hooks;
+
+       struct bpf_if           *sc_radiobpf;
+
+       struct callout          sc_scan_ch;
+       u_int                   sc_cur_chan;
+
+       uint32_t                sc_tsfth;       /* most significant TSFT bits */
+       uint32_t                sc_rcr;         /* RTW_RCR */
+       uint8_t                 sc_csthr;       /* carrier-sense threshold */
+
+       int                     sc_do_tick;     /* indicate 1s ticks */
+       struct timeval          sc_tick0;       /* first tick */
+
+       uint8_t                 sc_rev;         /* PCI/Cardbus revision */
+
+       uint32_t                sc_anaparm;     /* register RTW_ANAPARM */
+
+       union {
+               struct rtw_rx_radiotap_header tap;
+               uint8_t         pad[64];
+       } sc_rxtapu;
+       union {
+               struct rtw_tx_radiotap_header tap;
+               uint8_t         pad[64];
+       } sc_txtapu;
+       union rtw_keys          sc_keys;
+       struct ifqueue          sc_beaconq;
+       struct rtw_led_state    sc_led_state;
+       int                     sc_hwverid;
+};
+
+#define        sc_if           sc_ic.ic_if
+#define sc_rxtap       sc_rxtapu.tap
+#define sc_txtap       sc_txtapu.tap
+
+extern int             rtw_host_rfio;
+extern devclass_t      rtw_devclass;
+
+#if 0
+void           rtw_txdac_enable(struct rtw_softc *, int);
+void           rtw_anaparm_enable(struct rtw_regs *, int);
+void           rtw_config0123_enable(struct rtw_regs *, int);
+void           rtw_continuous_tx_enable(struct rtw_softc *, int);
+void           rtw_set_access(struct rtw_regs *, enum rtw_access);
+#endif
+
+int            rtw_attach(device_t);
+int            rtw_detach(device_t);
+void           rtw_stop(struct rtw_softc *, int);
+#ifdef RTW_DEBUG
+const char     *rtw_pwrstate_string(enum rtw_pwrstate);
+#endif
+
+#endif /* _DEV_IC_RTWVAR_H_ */
diff --git a/sys/dev/netif/rtw/sa2400reg.h b/sys/dev/netif/rtw/sa2400reg.h
new file mode 100644 (file)
index 0000000..27074db
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2005 David Young.  All rights reserved.
+ *
+ * This code was written by David Young.
+ *
+ * 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. Neither the name of the author nor the names of any co-contributors
+ *    may 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: sa2400reg.h,v 1.6 2006/03/08 08:26:50 dyoung Exp $
+ * $DragonFly: src/sys/dev/netif/rtw/sa2400reg.h,v 1.1 2006/09/03 07:37:58 sephe Exp $
+ */
+
+#ifndef _DEV_IC_SA2400REG_H_
+#define        _DEV_IC_SA2400REG_H_
+
+/*
+ * Serial bus format for Philips SA2400 Single-chip Transceiver.
+ */
+#define SA2400_TWI_DATA_MASK   __BITS(31,8)
+#define SA2400_TWI_WREN                __BIT(7)                /* enable write */
+#define SA2400_TWI_ADDR_MASK   __BITS(6,0)
+
+/*
+ * Registers for Philips SA2400 Single-chip Transceiver.
+ */
+#define SA2400_SYNA            0               /* Synthesizer Register A */
+#define SA2400_SYNA_FM         __BIT(21)       /* fractional modulus select,
+                                                * 0: /8 (default)
+                                                * 1: /5
+                                                */
+#define        SA2400_SYNA_NF_MASK     __BITS(20,18)   /* fractional increment value,
+                                                * 0 to 7, default 4
+                                                */
+#define        SA2400_SYNA_N_MASK      __BITS(17,2)    /* main divider division ratio,
+                                                * 512 to 65535, default 615
+                                                */
+
+#define SA2400_SYNB            1               /* Synthesizer Register B */
+#define SA2400_SYNB_R_MASK     __BITS(21,12)   /* reference divider ratio,
+                                                * 4 to 1023, default 11
+                                                */
+#define SA2400_SYNB_L_MASK     __BITS(11,10)   /* lock detect mode */
+#define SA2400_SYNB_L_INACTIVE0        SHIFTIN(0, SA2400_SYNB_L_MASK)
+#define SA2400_SYNB_L_INACTIVE1        SHIFTIN(1, SA2400_SYNB_L_MASK)
+#define SA2400_SYNB_L_NORMAL   SHIFTIN(2, SA2400_SYNB_L_MASK)
+#define SA2400_SYNB_L_INACTIVE2        SHIFTIN(3, SA2400_SYNB_L_MASK)
+
+#define        SA2400_SYNB_ON          __BIT(9)        /* power on/off,
+                                                * 0: inverted chip mode control
+                                                * 1: as defined by chip mode
+                                                *    (see SA2400_OPMODE)
+                                                */
+#define        SA2400_SYNB_ONE         __BIT(8)        /* always 1 */
+#define        SA2400_SYNB_FC_MASK     __BITS(7,0)     /* fractional compensation
+                                                * charge pump current DAC,
+                                                * 0 to 255, default 80.
+                                                */
+
+#define SA2400_SYNC            2               /* Synthesizer Register C */
+#define SA2400_SYNC_CP_MASK    __BITS(7,6)     /* charge pump current
+                                                * setting
+                                                */
+#define SA2400_SYNC_CP_NORMAL_ SHIFTIN(0, SA2400_SYNC_CP_MASK)
+#define SA2400_SYNC_CP_THIRD_  SHIFTIN(1, SA2400_SYNC_CP_MASK)
+#define SA2400_SYNC_CP_NORMAL  SHIFTIN(2, SA2400_SYNC_CP_MASK) /* recommended */
+#define SA2400_SYNC_CP_THIRD   SHIFTIN(3, SA2400_SYNC_CP_MASK)
+
+#define SA2400_SYNC_SM_MASK    __BITS(5,3)     /* comparison divider select,
+                                                * 0 to 4, extra division
+                                                * ratio is 2**SM.
+                                                */
+#define SA2400_SYNC_ZERO       __BIT(2)        /* always 0 */
+
+#define SA2400_SYND            3               /* Synthesizer Register D */
+#define SA2400_SYND_ZERO1_MASK __BITS(21,17)   /* always 0 */
+#define SA2400_SYND_TPHPSU     __BIT(16)       /* T[phpsu], 1: disable
+                                                * PHP speedup pump,
+                                                * overrides SA2400_SYND_TSPU
+                                                */
+#define SA2400_SYND_TPSU       __BIT(15)       /* T[spu], 1: speedup on,
+                                                * 0: speedup off
+                                                */
+#define SA2400_SYND_ZERO2_MASK __BITS(14,3)    /* always 0 */
+
+#define        SA2400_OPMODE           4               /* Operating mode, filter tuner,
+                                                * other controls
+                                                */
+/* 1: in Rx mode, RSSI-ADC always on 0: RSSI-ADC only on during AGC */
+#define SA2400_OPMODE_ADC      __BIT(19)
+/* read-only filter tuner error: 1 if tuner out of range */
+#define SA2400_OPMODE_FTERR    __BIT(18)
+/* Rx & Tx filter tuning, write tuning value (test mode only) or
+ * read tuner setting (in normal mode).
+ */
+#define SA2400_OPMODE_FILTTUNE_MASK    __BITS(17,15)
+
+/* external reference voltage (pad v2p5) on */
+#define SA2400_OPMODE_V2P5     __BIT(14)
+/* external reference current ... */
+#define SA2400_OPMODE_I1M      __BIT(13)
+/* external reference current ... */
+#define SA2400_OPMODE_I0P3     __BIT(12)
+#define SA2400_OPMODE_IN22     __BIT(10)       /* xtal input frequency,
+                                                * 0: 44 MHz
+                                                * 1: 22 MHz
+                                                */
+#define SA2400_OPMODE_CLK      __BIT(9)        /* reference clock output on */
+#define SA2400_OPMODE_XO       __BIT(8)        /* xtal oscillator on */
+#define SA2400_OPMODE_DIGIN    __BIT(7)        /* use digital Tx inputs
+                                                * (FIRDAC)
+                                                */
+#define SA2400_OPMODE_RXLV     __BIT(6)        /* Rx output common mode
+                                                * voltage,
+                                                *&n