New rtwn(4) driver for RTL8188CE wifi cards.
authorstsp <stsp@openbsd.org>
Thu, 4 Jun 2015 21:08:40 +0000 (21:08 +0000)
committerstsp <stsp@openbsd.org>
Thu, 4 Jun 2015 21:08:40 +0000 (21:08 +0000)
This is a PCI card from the same chip family as supported by urtwn(4) on USB.
Development started in 2013 using urtwn(4) as a starting point but was dormant
for much of the time since. I finally unslacked after uwe@ provided help with
lifting this driver on its feet. As usual we got helpful hints from Theo.

Requires firmware which will be available in ports soon.

There are rate adaptation issues that still need to be fixed, cause unknown.
In my testing the hardware rarely transmits more than 1Mbit/s.

Committing over MAC/BB RTL8188CE, RF 6052 1T1R.

sys/dev/pci/if_rtwn.c [new file with mode: 0644]
sys/dev/pci/if_rtwnreg.h [new file with mode: 0644]

diff --git a/sys/dev/pci/if_rtwn.c b/sys/dev/pci/if_rtwn.c
new file mode 100644 (file)
index 0000000..a2ce6b6
--- /dev/null
@@ -0,0 +1,3287 @@
+/*     $OpenBSD: if_rtwn.c,v 1.1 2015/06/04 21:08:40 stsp Exp $        */
+
+/*-
+ * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2015 Stefan Sperling <stsp@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Driver for Realtek RTL8188CE
+ */
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/task.h>
+#include <sys/timeout.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+#include <sys/endian.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+
+#include <dev/pci/if_rtwnreg.h>
+
+#ifdef RTWN_DEBUG
+#define DPRINTF(x)     do { if (rtwn_debug) printf x; } while (0)
+#define DPRINTFN(n, x) do { if (rtwn_debug >= (n)) printf x; } while (0)
+int rtwn_debug = 0;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n, x)
+#endif
+
+/*
+ * PCI configuration space registers.
+ */
+#define        RTWN_PCI_IOBA           0x10    /* i/o mapped base */
+#define        RTWN_PCI_MMBA           0x18    /* memory mapped base */
+
+#define RTWN_INT_ENABLE        (R92C_IMR_ROK | R92C_IMR_VODOK | R92C_IMR_VIDOK | \
+                       R92C_IMR_BEDOK | R92C_IMR_BKDOK | R92C_IMR_MGNTDOK | \
+                       R92C_IMR_HIGHDOK | R92C_IMR_BDOK | R92C_IMR_RDU | \
+                       R92C_IMR_RXFOVW)
+
+static const struct pci_matchid rtwn_pci_devices[] = {
+       { PCI_VENDOR_REALTEK,   PCI_PRODUCT_REALTEK_RT8188 }
+};
+
+int            rtwn_match(struct device *, void *, void *);
+void           rtwn_attach(struct device *, struct device *, void *);
+int            rtwn_detach(struct device *, int);
+int            rtwn_activate(struct device *, int);
+int            rtwn_alloc_rx_list(struct rtwn_softc *);
+void           rtwn_reset_rx_list(struct rtwn_softc *);
+void           rtwn_free_rx_list(struct rtwn_softc *);
+void           rtwn_setup_rx_desc(struct rtwn_softc *, struct r92c_rx_desc *,
+                   bus_addr_t, size_t, int);
+int            rtwn_alloc_tx_list(struct rtwn_softc *, int);
+void           rtwn_reset_tx_list(struct rtwn_softc *, int);
+void           rtwn_free_tx_list(struct rtwn_softc *, int);
+void           rtwn_write_1(struct rtwn_softc *, uint16_t, uint8_t);
+void           rtwn_write_2(struct rtwn_softc *, uint16_t, uint16_t);
+void           rtwn_write_4(struct rtwn_softc *, uint16_t, uint32_t);
+uint8_t                rtwn_read_1(struct rtwn_softc *, uint16_t);
+uint16_t       rtwn_read_2(struct rtwn_softc *, uint16_t);
+uint32_t       rtwn_read_4(struct rtwn_softc *, uint16_t);
+int            rtwn_fw_cmd(struct rtwn_softc *, uint8_t, const void *, int);
+void           rtwn_rf_write(struct rtwn_softc *, int, uint8_t, uint32_t);
+uint32_t       rtwn_rf_read(struct rtwn_softc *, int, uint8_t);
+void           rtwn_cam_write(struct rtwn_softc *, uint32_t, uint32_t);
+int            rtwn_llt_write(struct rtwn_softc *, uint32_t, uint32_t);
+uint8_t                rtwn_efuse_read_1(struct rtwn_softc *, uint16_t);
+void           rtwn_efuse_read(struct rtwn_softc *);
+int            rtwn_read_chipid(struct rtwn_softc *);
+void           rtwn_read_rom(struct rtwn_softc *);
+int            rtwn_media_change(struct ifnet *);
+int            rtwn_ra_init(struct rtwn_softc *);
+void           rtwn_tsf_sync_enable(struct rtwn_softc *);
+void           rtwn_set_led(struct rtwn_softc *, int, int);
+void           rtwn_calib_to(void *);
+void           rtwn_next_scan(void *);
+int            rtwn_newstate(struct ieee80211com *, enum ieee80211_state,
+                   int);
+void           rtwn_updateedca(struct ieee80211com *);
+int            rtwn_set_key(struct ieee80211com *, struct ieee80211_node *,
+                   struct ieee80211_key *);
+void           rtwn_delete_key(struct ieee80211com *,
+                   struct ieee80211_node *, struct ieee80211_key *);
+void           rtwn_update_avgrssi(struct rtwn_softc *, int, int8_t);
+int8_t         rtwn_get_rssi(struct rtwn_softc *, int, void *);
+void           rtwn_rx_frame(struct rtwn_softc *, struct r92c_rx_desc *,
+                   struct rtwn_rx_data *, int);
+int            rtwn_tx(struct rtwn_softc *, struct mbuf *,
+                   struct ieee80211_node *);
+void           rtwn_tx_done(struct rtwn_softc *, int);
+void           rtwn_start(struct ifnet *);
+void           rtwn_watchdog(struct ifnet *);
+int            rtwn_ioctl(struct ifnet *, u_long, caddr_t);
+int            rtwn_power_on(struct rtwn_softc *);
+int            rtwn_llt_init(struct rtwn_softc *);
+void           rtwn_fw_reset(struct rtwn_softc *);
+int            rtwn_fw_loadpage(struct rtwn_softc *, int, uint8_t *, int);
+int            rtwn_load_firmware(struct rtwn_softc *);
+int            rtwn_dma_init(struct rtwn_softc *);
+void           rtwn_mac_init(struct rtwn_softc *);
+void           rtwn_bb_init(struct rtwn_softc *);
+void           rtwn_rf_init(struct rtwn_softc *);
+void           rtwn_cam_init(struct rtwn_softc *);
+void           rtwn_pa_bias_init(struct rtwn_softc *);
+void           rtwn_rxfilter_init(struct rtwn_softc *);
+void           rtwn_edca_init(struct rtwn_softc *);
+void           rtwn_write_txpower(struct rtwn_softc *, int, uint16_t[]);
+void           rtwn_get_txpower(struct rtwn_softc *, int,
+                   struct ieee80211_channel *, struct ieee80211_channel *,
+                   uint16_t[]);
+void           rtwn_set_txpower(struct rtwn_softc *,
+                   struct ieee80211_channel *, struct ieee80211_channel *);
+void           rtwn_set_chan(struct rtwn_softc *,
+                   struct ieee80211_channel *, struct ieee80211_channel *);
+int            rtwn_iq_calib_chain(struct rtwn_softc *, int, uint16_t[],
+                   uint16_t[]);
+void           rtwn_iq_calib(struct rtwn_softc *);
+void           rtwn_lc_calib(struct rtwn_softc *);
+void           rtwn_temp_calib(struct rtwn_softc *);
+int            rtwn_init(struct ifnet *);
+void           rtwn_init_task(void *);
+void           rtwn_stop(struct ifnet *);
+int            rtwn_intr(void *);
+
+/* Aliases. */
+#define        rtwn_bb_write   rtwn_write_4
+#define rtwn_bb_read   rtwn_read_4
+
+struct cfdriver rtwn_cd = {
+       NULL, "rtwn", DV_IFNET
+};
+
+const struct cfattach rtwn_ca = {
+       sizeof(struct rtwn_softc),
+       rtwn_match,
+       rtwn_attach,
+       rtwn_detach,
+       rtwn_activate
+};
+
+int
+rtwn_match(struct device *parent, void *match, void *aux)
+{
+       return (pci_matchbyid(aux, rtwn_pci_devices,
+           nitems(rtwn_pci_devices)));
+}
+
+void
+rtwn_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct rtwn_softc *sc = (struct rtwn_softc *)self;
+       struct pci_attach_args *pa = aux;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+       int i, error;
+       pcireg_t memtype;
+       pci_intr_handle_t ih;
+       const char *intrstr;
+
+       sc->sc_dmat = pa->pa_dmat;
+       sc->sc_pc = pa->pa_pc;
+       sc->sc_tag = pa->pa_tag;
+
+       timeout_set(&sc->scan_to, rtwn_next_scan, sc);
+       timeout_set(&sc->calib_to, rtwn_calib_to, sc);
+
+       task_set(&sc->init_task, rtwn_init_task, sc);
+
+       pci_set_powerstate(pa->pa_pc, pa->pa_tag, PCI_PMCSR_STATE_D0);
+
+       /* Map control/status registers. */
+       memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, RTWN_PCI_MMBA);
+       error = pci_mapreg_map(pa, RTWN_PCI_MMBA, memtype, 0, &sc->sc_st,
+           &sc->sc_sh, NULL, &sc->sc_mapsize, 0);
+       if (error != 0) {
+               printf(": can't map mem space\n");
+               return;
+       }
+
+       if (pci_intr_map_msi(pa, &ih) && pci_intr_map(pa, &ih)) {
+               printf(": can't map interrupt\n");
+               return;
+       }
+       intrstr = pci_intr_string(sc->sc_pc, ih);
+       sc->sc_ih = pci_intr_establish(sc->sc_pc, ih, IPL_NET,
+           rtwn_intr, sc, sc->sc_dev.dv_xname);
+       if (sc->sc_ih == NULL) {
+               printf(": can't establish interrupt");
+               if (intrstr != NULL)
+                       printf(" at %s", intrstr);
+               printf("\n");
+               return;
+       }
+       printf(": %s\n", intrstr);
+
+       error = rtwn_read_chipid(sc);
+       if (error != 0) {
+               printf("%s: unsupported test chip\n", sc->sc_dev.dv_xname);
+               return;
+       }
+
+       /* Disable PCIe Active State Power Management (ASPM). */
+       if (pci_get_capability(sc->sc_pc, sc->sc_tag, PCI_CAP_PCIEXPRESS,
+           &sc->sc_cap_off, NULL)) {
+               uint32_t lcsr = pci_conf_read(sc->sc_pc, sc->sc_tag,
+                   sc->sc_cap_off + PCI_PCIE_LCSR);
+               lcsr &= ~(PCI_PCIE_LCSR_ASPM_L0S | PCI_PCIE_LCSR_ASPM_L1);
+               pci_conf_write(sc->sc_pc, sc->sc_tag,
+                   sc->sc_cap_off + PCI_PCIE_LCSR, lcsr);
+       }
+
+       /* Allocate Tx/Rx buffers. */
+       error = rtwn_alloc_rx_list(sc);
+       if (error != 0) {
+               printf("%s: could not allocate Rx buffers\n",
+                   sc->sc_dev.dv_xname);
+               return;
+       }
+       for (i = 0; i < RTWN_NTXQUEUES; i++) {
+               error = rtwn_alloc_tx_list(sc, i);
+               if (error != 0) {
+                       printf("%s: could not allocate Tx buffers\n",
+                           sc->sc_dev.dv_xname);
+                       return;
+               }
+       }
+
+       /* Determine number of Tx/Rx chains. */
+       if (sc->chip & RTWN_CHIP_92C) {
+               sc->ntxchains = (sc->chip & RTWN_CHIP_92C_1T2R) ? 1 : 2;
+               sc->nrxchains = 2;
+       } else {
+               sc->ntxchains = 1;
+               sc->nrxchains = 1;
+       }
+       rtwn_read_rom(sc);
+
+       printf("%s: MAC/BB RTL%s, RF 6052 %dT%dR, address %s\n",
+           sc->sc_dev.dv_xname,
+           (sc->chip & RTWN_CHIP_92C) ? "8192CE" : "8188CE",
+           sc->ntxchains, sc->nrxchains,
+           ether_sprintf(ic->ic_myaddr));
+
+       ic->ic_phytype = IEEE80211_T_OFDM;      /* Not only, but not used. */
+       ic->ic_opmode = IEEE80211_M_STA;        /* Default to BSS mode. */
+       ic->ic_state = IEEE80211_S_INIT;
+
+       /* Set device capabilities. */
+       ic->ic_caps =
+           IEEE80211_C_MONITOR |       /* Monitor mode supported. */
+           IEEE80211_C_SHPREAMBLE |    /* Short preamble supported. */
+           IEEE80211_C_SHSLOT |        /* Short slot time supported. */
+           IEEE80211_C_WEP |           /* WEP. */
+           IEEE80211_C_RSN;            /* WPA/RSN. */
+
+#ifndef IEEE80211_NO_HT
+       /* Set HT capabilities. */
+       ic->ic_htcaps =
+           IEEE80211_HTCAP_CBW20_40 |
+           IEEE80211_HTCAP_DSSSCCK40;
+       /* Set supported HT rates. */
+       for (i = 0; i < sc->nrxchains; i++)
+               ic->ic_sup_mcs[i] = 0xff;
+#endif
+
+       /* Set supported .11b and .11g rates. */
+       ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
+       ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g;
+
+       /* Set supported .11b and .11g channels (1 through 14). */
+       for (i = 1; i <= 14; i++) {
+               ic->ic_channels[i].ic_freq =
+                   ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
+               ic->ic_channels[i].ic_flags =
+                   IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
+                   IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
+       }
+
+#ifdef notyet
+       /*
+        * The number of STAs that we can support is limited by the number
+        * of CAM entries used for hardware crypto.
+        */
+       ic->ic_max_nnodes = R92C_CAM_ENTRY_COUNT - 4;
+       if (ic->ic_max_nnodes > IEEE80211_CACHE_SIZE)
+               ic->ic_max_nnodes = IEEE80211_CACHE_SIZE;
+#endif
+
+       ifp->if_softc = sc;
+       ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+       ifp->if_ioctl = rtwn_ioctl;
+       ifp->if_start = rtwn_start;
+       ifp->if_watchdog = rtwn_watchdog;
+       IFQ_SET_READY(&ifp->if_snd);
+       memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
+
+       if_attach(ifp);
+       ieee80211_ifattach(ifp);
+       ic->ic_updateedca = rtwn_updateedca;
+#ifdef notyet
+       ic->ic_set_key = rtwn_set_key;
+       ic->ic_delete_key = rtwn_delete_key;
+#endif
+       /* Override state transition machine. */
+       sc->sc_newstate = ic->ic_newstate;
+       ic->ic_newstate = rtwn_newstate;
+       ieee80211_media_init(ifp, rtwn_media_change, ieee80211_media_status);
+
+#if NBPFILTER > 0
+       bpfattach(&sc->sc_drvbpf, ifp, DLT_IEEE802_11_RADIO,
+           sizeof(struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN);
+
+       sc->sc_rxtap_len = sizeof(sc->sc_rxtapu);
+       sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
+       sc->sc_rxtap.wr_ihdr.it_present = htole32(RTWN_RX_RADIOTAP_PRESENT);
+
+       sc->sc_txtap_len = sizeof(sc->sc_txtapu);
+       sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
+       sc->sc_txtap.wt_ihdr.it_present = htole32(RTWN_TX_RADIOTAP_PRESENT);
+#endif
+}
+
+int
+rtwn_detach(struct device *self, int flags)
+{
+       struct rtwn_softc *sc = (struct rtwn_softc *)self;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       int s, i;
+
+       s = splnet();
+
+       if (timeout_initialized(&sc->scan_to))
+               timeout_del(&sc->scan_to);
+       if (timeout_initialized(&sc->calib_to))
+               timeout_del(&sc->calib_to);
+
+       task_del(systq, &sc->init_task);
+
+       if (ifp->if_softc != NULL) {
+               ieee80211_ifdetach(ifp);
+               if_detach(ifp);
+       }
+
+       /* Free Tx/Rx buffers. */
+       for (i = 0; i < RTWN_NTXQUEUES; i++)
+               rtwn_free_tx_list(sc, i);
+       rtwn_free_rx_list(sc);
+       splx(s);
+
+       return (0);
+}
+
+int
+rtwn_activate(struct device *self, int act)
+{
+       struct rtwn_softc *sc = (struct rtwn_softc *)self;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+
+       switch (act) {
+       case DVACT_SUSPEND:
+               if (ifp->if_flags & IFF_RUNNING)
+                       rtwn_stop(ifp);
+               break;
+       case DVACT_WAKEUP:
+               rtwn_init_task(sc);
+               break;
+       }
+       return (0);
+}
+
+void
+rtwn_setup_rx_desc(struct rtwn_softc *sc, struct r92c_rx_desc *desc,
+    bus_addr_t addr, size_t len, int idx)
+{
+       memset(desc, 0, sizeof(*desc));
+       desc->rxdw0 = htole32(SM(R92C_RXDW0_PKTLEN, len) |
+               ((idx == RTWN_RX_LIST_COUNT - 1) ? R92C_RXDW0_EOR : 0));
+       desc->rxbufaddr = htole32(addr);
+       bus_space_barrier(sc->sc_st, sc->sc_sh, 0, sc->sc_mapsize,
+           BUS_SPACE_BARRIER_WRITE);
+       desc->rxdw0 |= htole32(R92C_RXDW0_OWN);
+}
+
+int
+rtwn_alloc_rx_list(struct rtwn_softc *sc)
+{
+       struct rtwn_rx_ring *rx_ring = &sc->rx_ring;
+       struct rtwn_rx_data *rx_data;
+       size_t size;
+       int i, error = 0;
+
+       /* Allocate Rx descriptors. */
+       size = sizeof(struct r92c_rx_desc) * RTWN_RX_LIST_COUNT;
+       error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, BUS_DMA_NOWAIT,
+               &rx_ring->map);
+       if (error != 0) {
+               printf("%s: could not create rx desc DMA map\n",
+                   sc->sc_dev.dv_xname);
+               rx_ring->map = NULL;
+               goto fail;
+       }
+
+       error = bus_dmamem_alloc(sc->sc_dmat, size, 0, 0, &rx_ring->seg, 1,
+           &rx_ring->nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO);
+       if (error != 0) {
+               printf("%s: could not allocate rx desc\n",
+                   sc->sc_dev.dv_xname);
+               goto fail;
+       }
+
+       error = bus_dmamem_map(sc->sc_dmat, &rx_ring->seg, rx_ring->nsegs,
+           size, (caddr_t *)&rx_ring->desc, 
+           BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
+       if (error != 0) {
+               bus_dmamem_free(sc->sc_dmat, &rx_ring->seg, rx_ring->nsegs);
+               rx_ring->desc = NULL;
+               printf("%s: could not map rx desc\n", sc->sc_dev.dv_xname);
+               goto fail;
+       }
+
+       error = bus_dmamap_load_raw(sc->sc_dmat, rx_ring->map, &rx_ring->seg,
+           1, size, BUS_DMA_NOWAIT);
+       if (error != 0) {
+               printf("%s: could not load rx desc\n",
+                   sc->sc_dev.dv_xname);
+               goto fail;
+       }
+
+       bus_dmamap_sync(sc->sc_dmat, rx_ring->map, 0, size,
+           BUS_DMASYNC_PREWRITE);
+
+       /* Allocate Rx buffers. */
+       for (i = 0; i < RTWN_RX_LIST_COUNT; i++) {
+               rx_data = &rx_ring->rx_data[i];
+
+               error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES,
+                   0, BUS_DMA_NOWAIT, &rx_data->map);
+               if (error != 0) {
+                       printf("%s: could not create rx buf DMA map\n",
+                           sc->sc_dev.dv_xname);
+                       goto fail;
+               }
+
+               rx_data->m = MCLGETI(NULL, M_DONTWAIT, NULL, MCLBYTES);
+               if (rx_data->m == NULL) {
+                       printf("%s: could not allocate rx mbuf\n",
+                           sc->sc_dev.dv_xname);
+                       error = ENOMEM;
+                       goto fail;
+               }
+
+               error = bus_dmamap_load(sc->sc_dmat, rx_data->map,
+                   mtod(rx_data->m, void *), MCLBYTES, NULL,
+                   BUS_DMA_NOWAIT | BUS_DMA_READ);
+               if (error != 0) {
+                       printf("%s: could not load rx buf DMA map\n",
+                           sc->sc_dev.dv_xname);
+                       goto fail;
+               }
+
+               rtwn_setup_rx_desc(sc, &rx_ring->desc[i],
+                   rx_data->map->dm_segs[0].ds_addr, MCLBYTES, i);
+       }
+fail:  if (error != 0)
+               rtwn_free_rx_list(sc);
+       return (error);
+}
+
+void
+rtwn_reset_rx_list(struct rtwn_softc *sc)
+{
+       struct rtwn_rx_ring *rx_ring = &sc->rx_ring;
+       struct rtwn_rx_data *rx_data;
+       int i;
+
+       for (i = 0; i < RTWN_RX_LIST_COUNT; i++) {
+               rx_data = &rx_ring->rx_data[i];
+               rtwn_setup_rx_desc(sc, &rx_ring->desc[i],
+                   rx_data->map->dm_segs[0].ds_addr, MCLBYTES, i);
+       }
+}
+
+void
+rtwn_free_rx_list(struct rtwn_softc *sc)
+{
+       struct rtwn_rx_ring *rx_ring = &sc->rx_ring;
+       struct rtwn_rx_data *rx_data;
+       int i, s;
+
+       s = splnet();
+
+       if (rx_ring->map) {
+               if (rx_ring->desc) {
+                       bus_dmamap_unload(sc->sc_dmat, rx_ring->map);
+                       bus_dmamem_unmap(sc->sc_dmat, (caddr_t)rx_ring->desc,
+                           sizeof (struct r92c_rx_desc) * RTWN_RX_LIST_COUNT);
+                       bus_dmamem_free(sc->sc_dmat, &rx_ring->seg,
+                           rx_ring->nsegs);
+                       rx_ring->desc = NULL;
+               }
+               bus_dmamap_destroy(sc->sc_dmat, rx_ring->map);
+               rx_ring->map = NULL;
+       }
+
+       for (i = 0; i < RTWN_RX_LIST_COUNT; i++) {
+               rx_data = &rx_ring->rx_data[i];
+
+               if (rx_data->m != NULL) {
+                       bus_dmamap_unload(sc->sc_dmat, rx_data->map);
+                       m_freem(rx_data->m);
+                       rx_data->m = NULL;
+               }
+               bus_dmamap_destroy(sc->sc_dmat, rx_data->map);
+               rx_data->map = NULL;
+       }
+
+       splx(s);
+}
+
+int
+rtwn_alloc_tx_list(struct rtwn_softc *sc, int qid)
+{
+       struct rtwn_tx_ring *tx_ring = &sc->tx_ring[qid];
+       struct rtwn_tx_data *tx_data;
+       int i = 0, error = 0;
+
+       error = bus_dmamap_create(sc->sc_dmat,
+           sizeof (struct r92c_tx_desc) * RTWN_TX_LIST_COUNT, 1,
+           sizeof (struct r92c_tx_desc) * RTWN_TX_LIST_COUNT, 0, BUS_DMA_NOWAIT,
+           &tx_ring->map);
+       if (error != 0) {
+               printf("%s: could not create tx ring DMA map\n",
+                   sc->sc_dev.dv_xname);
+               goto fail;
+       }
+
+       error = bus_dmamem_alloc(sc->sc_dmat,
+           sizeof (struct r92c_tx_desc) * RTWN_TX_LIST_COUNT, PAGE_SIZE, 0,
+           &tx_ring->seg, 1, &tx_ring->nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO);
+       if (error != 0) {
+               printf("%s: could not allocate tx ring DMA memory\n",
+                   sc->sc_dev.dv_xname);
+               goto fail;
+       }
+
+       error = bus_dmamem_map(sc->sc_dmat, &tx_ring->seg, tx_ring->nsegs,
+           sizeof (struct r92c_tx_desc) * RTWN_TX_LIST_COUNT,
+           (caddr_t *)&tx_ring->desc, BUS_DMA_NOWAIT);
+       if (error != 0) {
+               bus_dmamem_free(sc->sc_dmat, &tx_ring->seg, tx_ring->nsegs);
+               printf("%s: can't map tx ring DMA memory\n",
+                   sc->sc_dev.dv_xname);
+               goto fail;
+       }
+
+       error = bus_dmamap_load(sc->sc_dmat, tx_ring->map, tx_ring->desc,
+           sizeof (struct r92c_tx_desc) * RTWN_TX_LIST_COUNT, NULL,
+           BUS_DMA_NOWAIT);
+       if (error != 0) {
+               printf("%s: could not load tx ring DMA map\n",
+                   sc->sc_dev.dv_xname);
+               goto fail;
+       }
+
+       for (i = 0; i < RTWN_TX_LIST_COUNT; i++) {
+               struct r92c_tx_desc *desc = &tx_ring->desc[i];
+
+               /* setup tx desc */
+               desc->nextdescaddr = htole32(tx_ring->map->dm_segs[0].ds_addr
+                 + sizeof(struct r92c_tx_desc)
+                 * ((i + 1) % RTWN_TX_LIST_COUNT));
+
+               tx_data = &tx_ring->tx_data[i];
+               error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES,
+                   0, BUS_DMA_NOWAIT, &tx_data->map);
+               if (error != 0) {
+                       printf("%s: could not create tx buf DMA map\n",
+                           sc->sc_dev.dv_xname);
+                       goto fail;
+               }
+               tx_data->m = NULL;
+               tx_data->ni = NULL;
+       }
+fail:
+       if (error != 0)
+               rtwn_free_tx_list(sc, qid);
+       return (error);
+}
+
+void
+rtwn_reset_tx_list(struct rtwn_softc *sc, int qid)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct rtwn_tx_ring *tx_ring = &sc->tx_ring[qid];
+       int i;
+
+       for (i = 0; i < RTWN_TX_LIST_COUNT; i++) {
+               struct r92c_tx_desc *desc = &tx_ring->desc[i];
+               struct rtwn_tx_data *tx_data = &tx_ring->tx_data[i];
+
+               memset(desc, 0, sizeof(*desc) -
+                   (sizeof(desc->reserved) + sizeof(desc->nextdescaddr64) +
+                   sizeof(desc->nextdescaddr)));
+
+               if (tx_data->m != NULL) {
+                       bus_dmamap_unload(sc->sc_dmat, tx_data->map);
+                       m_freem(tx_data->m);
+                       tx_data->m = NULL;
+                       ieee80211_release_node(ic, tx_data->ni);
+                       tx_data->ni = NULL;
+               }
+       }
+
+       bus_dmamap_sync(sc->sc_dmat, tx_ring->map, 0, MCLBYTES,
+           BUS_DMASYNC_POSTWRITE);
+
+       sc->qfullmsk &= ~(1 << qid);
+       tx_ring->queued = 0;
+       tx_ring->cur = 0;
+}
+
+void
+rtwn_free_tx_list(struct rtwn_softc *sc, int qid)
+{
+       struct rtwn_tx_ring *tx_ring = &sc->tx_ring[qid];
+       struct rtwn_tx_data *tx_data;
+       int i;
+
+       if (tx_ring->map != NULL) {
+               if (tx_ring->desc != NULL) {
+                       bus_dmamap_unload(sc->sc_dmat, tx_ring->map);
+                       bus_dmamem_unmap(sc->sc_dmat, (caddr_t)tx_ring->desc,
+                           sizeof (struct r92c_tx_desc) * RTWN_TX_LIST_COUNT);
+                       bus_dmamem_free(sc->sc_dmat, &tx_ring->seg, tx_ring->nsegs);
+               }
+               bus_dmamap_destroy(sc->sc_dmat, tx_ring->map);
+       }
+
+       for (i = 0; i < RTWN_TX_LIST_COUNT; i++) {
+               tx_data = &tx_ring->tx_data[i];
+
+               if (tx_data->m != NULL) {
+                       bus_dmamap_unload(sc->sc_dmat, tx_data->map);
+                       m_freem(tx_data->m);
+                       tx_data->m = NULL;
+               }
+               bus_dmamap_destroy(sc->sc_dmat, tx_data->map);
+       }
+
+       sc->qfullmsk &= ~(1 << qid);
+       tx_ring->queued = 0;
+       tx_ring->cur = 0;
+}
+
+void
+rtwn_write_1(struct rtwn_softc *sc, uint16_t addr, uint8_t val)
+{
+       bus_space_write_1(sc->sc_st, sc->sc_sh, addr, val);
+}
+
+void
+rtwn_write_2(struct rtwn_softc *sc, uint16_t addr, uint16_t val)
+{
+       val = htole16(val);
+       bus_space_write_2(sc->sc_st, sc->sc_sh, addr, val);
+}
+
+void
+rtwn_write_4(struct rtwn_softc *sc, uint16_t addr, uint32_t val)
+{
+       val = htole32(val);
+       bus_space_write_4(sc->sc_st, sc->sc_sh, addr, val);
+}
+
+uint8_t
+rtwn_read_1(struct rtwn_softc *sc, uint16_t addr)
+{
+       return bus_space_read_1(sc->sc_st, sc->sc_sh, addr);
+}
+
+uint16_t
+rtwn_read_2(struct rtwn_softc *sc, uint16_t addr)
+{
+       return bus_space_read_2(sc->sc_st, sc->sc_sh, addr);
+}
+
+uint32_t
+rtwn_read_4(struct rtwn_softc *sc, uint16_t addr)
+{
+       return bus_space_read_4(sc->sc_st, sc->sc_sh, addr);
+}
+
+int
+rtwn_fw_cmd(struct rtwn_softc *sc, uint8_t id, const void *buf, int len)
+{
+       struct r92c_fw_cmd cmd;
+       int ntries;
+
+       /* Wait for current FW box to be empty. */
+       for (ntries = 0; ntries < 100; ntries++) {
+               if (!(rtwn_read_1(sc, R92C_HMETFR) & (1 << sc->fwcur)))
+                       break;
+               DELAY(1);
+       }
+       if (ntries == 100) {
+               printf("%s: could not send firmware command %d\n",
+                   sc->sc_dev.dv_xname, id);
+               return (ETIMEDOUT);
+       }
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.id = id;
+       if (len > 3)
+               cmd.id |= R92C_CMD_FLAG_EXT;
+       KASSERT(len <= sizeof(cmd.msg));
+       memcpy(cmd.msg, buf, len);
+
+       /* Write the first word last since that will trigger the FW. */
+       rtwn_write_2(sc, R92C_HMEBOX_EXT(sc->fwcur), *((uint8_t *)&cmd + 4));
+       rtwn_write_4(sc, R92C_HMEBOX(sc->fwcur), *((uint8_t *)&cmd + 0));
+
+       sc->fwcur = (sc->fwcur + 1) % R92C_H2C_NBOX;
+       return (0);
+}
+
+void
+rtwn_rf_write(struct rtwn_softc *sc, int chain, uint8_t addr, uint32_t val)
+{
+       rtwn_bb_write(sc, R92C_LSSI_PARAM(chain),
+           SM(R92C_LSSI_PARAM_ADDR, addr) |
+           SM(R92C_LSSI_PARAM_DATA, val));
+}
+
+uint32_t
+rtwn_rf_read(struct rtwn_softc *sc, int chain, uint8_t addr)
+{
+       uint32_t reg[R92C_MAX_CHAINS], val;
+
+       reg[0] = rtwn_bb_read(sc, R92C_HSSI_PARAM2(0));
+       if (chain != 0)
+               reg[chain] = rtwn_bb_read(sc, R92C_HSSI_PARAM2(chain));
+
+       rtwn_bb_write(sc, R92C_HSSI_PARAM2(0),
+           reg[0] & ~R92C_HSSI_PARAM2_READ_EDGE);
+       DELAY(1000);
+
+       rtwn_bb_write(sc, R92C_HSSI_PARAM2(chain),
+           RW(reg[chain], R92C_HSSI_PARAM2_READ_ADDR, addr) |
+           R92C_HSSI_PARAM2_READ_EDGE);
+       DELAY(1000);
+
+       rtwn_bb_write(sc, R92C_HSSI_PARAM2(0),
+           reg[0] | R92C_HSSI_PARAM2_READ_EDGE);
+       DELAY(1000);
+
+       if (rtwn_bb_read(sc, R92C_HSSI_PARAM1(chain)) & R92C_HSSI_PARAM1_PI)
+               val = rtwn_bb_read(sc, R92C_HSPI_READBACK(chain));
+       else
+               val = rtwn_bb_read(sc, R92C_LSSI_READBACK(chain));
+       return (MS(val, R92C_LSSI_READBACK_DATA));
+}
+
+void
+rtwn_cam_write(struct rtwn_softc *sc, uint32_t addr, uint32_t data)
+{
+       rtwn_write_4(sc, R92C_CAMWRITE, data);
+       rtwn_write_4(sc, R92C_CAMCMD,
+           R92C_CAMCMD_POLLING | R92C_CAMCMD_WRITE |
+           SM(R92C_CAMCMD_ADDR, addr));
+}
+
+int
+rtwn_llt_write(struct rtwn_softc *sc, uint32_t addr, uint32_t data)
+{
+       int ntries;
+
+       rtwn_write_4(sc, R92C_LLT_INIT,
+           SM(R92C_LLT_INIT_OP, R92C_LLT_INIT_OP_WRITE) |
+           SM(R92C_LLT_INIT_ADDR, addr) |
+           SM(R92C_LLT_INIT_DATA, data));
+       /* Wait for write operation to complete. */
+       for (ntries = 0; ntries < 20; ntries++) {
+               if (MS(rtwn_read_4(sc, R92C_LLT_INIT), R92C_LLT_INIT_OP) ==
+                   R92C_LLT_INIT_OP_NO_ACTIVE)
+                       return (0);
+               DELAY(5);
+       }
+       return (ETIMEDOUT);
+}
+
+uint8_t
+rtwn_efuse_read_1(struct rtwn_softc *sc, uint16_t addr)
+{
+       uint32_t reg;
+       int ntries;
+
+       reg = rtwn_read_4(sc, R92C_EFUSE_CTRL);
+       reg = RW(reg, R92C_EFUSE_CTRL_ADDR, addr);
+       reg &= ~R92C_EFUSE_CTRL_VALID;
+       rtwn_write_4(sc, R92C_EFUSE_CTRL, reg);
+       /* Wait for read operation to complete. */
+       for (ntries = 0; ntries < 100; ntries++) {
+               reg = rtwn_read_4(sc, R92C_EFUSE_CTRL);
+               if (reg & R92C_EFUSE_CTRL_VALID)
+                       return (MS(reg, R92C_EFUSE_CTRL_DATA));
+               DELAY(5);
+       }
+       printf("%s: could not read efuse byte at address 0x%x\n",
+           sc->sc_dev.dv_xname, addr);
+       return (0xff);
+}
+
+void
+rtwn_efuse_read(struct rtwn_softc *sc)
+{
+       uint8_t *rom = (uint8_t *)&sc->rom;
+       uint16_t addr = 0;
+       uint32_t reg;
+       uint8_t off, msk;
+       int i;
+
+       reg = rtwn_read_2(sc, R92C_SYS_ISO_CTRL);
+       if (!(reg & R92C_SYS_ISO_CTRL_PWC_EV12V)) {
+               rtwn_write_2(sc, R92C_SYS_ISO_CTRL,
+                   reg | R92C_SYS_ISO_CTRL_PWC_EV12V);
+       }
+       reg = rtwn_read_2(sc, R92C_SYS_FUNC_EN);
+       if (!(reg & R92C_SYS_FUNC_EN_ELDR)) {
+               rtwn_write_2(sc, R92C_SYS_FUNC_EN,
+                   reg | R92C_SYS_FUNC_EN_ELDR);
+       }
+       reg = rtwn_read_2(sc, R92C_SYS_CLKR);
+       if ((reg & (R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M)) !=
+           (R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M)) {
+               rtwn_write_2(sc, R92C_SYS_CLKR,
+                   reg | R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M);
+       }
+       memset(&sc->rom, 0xff, sizeof(sc->rom));
+       while (addr < 512) {
+               reg = rtwn_efuse_read_1(sc, addr);
+               if (reg == 0xff)
+                       break;
+               addr++;
+               off = reg >> 4;
+               msk = reg & 0xf;
+               for (i = 0; i < 4; i++) {
+                       if (msk & (1 << i))
+                               continue;
+                       rom[off * 8 + i * 2 + 0] =
+                           rtwn_efuse_read_1(sc, addr);
+                       addr++;
+                       rom[off * 8 + i * 2 + 1] =
+                           rtwn_efuse_read_1(sc, addr);
+                       addr++;
+               }
+       }
+#ifdef RTWN_DEBUG
+       if (rtwn_debug >= 2) {
+               /* Dump ROM content. */
+               printf("\n");
+               for (i = 0; i < sizeof(sc->rom); i++)
+                       printf("%02x:", rom[i]);
+               printf("\n");
+       }
+#endif
+}
+
+/* rtwn_read_chipid: reg=0x40073b chipid=0x0 */
+int
+rtwn_read_chipid(struct rtwn_softc *sc)
+{
+       uint32_t reg;
+
+       reg = rtwn_read_4(sc, R92C_SYS_CFG);
+       if (reg & R92C_SYS_CFG_TRP_VAUX_EN)
+               /* Unsupported test chip. */
+               return (EIO);
+
+       if (reg & R92C_SYS_CFG_TYPE_92C) {
+               sc->chip |= RTWN_CHIP_92C;
+               /* Check if it is a castrated 8192C. */
+               if (MS(rtwn_read_4(sc, R92C_HPON_FSM),
+                   R92C_HPON_FSM_CHIP_BONDING_ID) ==
+                   R92C_HPON_FSM_CHIP_BONDING_ID_92C_1T2R)
+                       sc->chip |= RTWN_CHIP_92C_1T2R;
+       }
+       if (reg & R92C_SYS_CFG_VENDOR_UMC) {
+               sc->chip |= RTWN_CHIP_UMC;
+               if (MS(reg, R92C_SYS_CFG_CHIP_VER_RTL) == 0)
+                       sc->chip |= RTWN_CHIP_UMC_A_CUT;
+       }
+       return (0);
+}
+
+void
+rtwn_read_rom(struct rtwn_softc *sc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct r92c_rom *rom = &sc->rom;
+
+       /* Read full ROM image. */
+       rtwn_efuse_read(sc);
+
+       if (rom->id != 0x8129) {
+               printf("%s: invalid EEPROM ID 0x%x\n",
+                       sc->sc_dev.dv_xname, rom->id);
+       }
+
+       /* XXX Weird but this is what the vendor driver does. */
+       sc->pa_setting = rtwn_efuse_read_1(sc, 0x1fa);
+       DPRINTF(("PA setting=0x%x\n", sc->pa_setting));
+
+       sc->board_type = MS(rom->rf_opt1, R92C_ROM_RF1_BOARD_TYPE);
+
+       sc->regulatory = MS(rom->rf_opt1, R92C_ROM_RF1_REGULATORY);
+       DPRINTF(("regulatory type=%d\n", sc->regulatory));
+
+       IEEE80211_ADDR_COPY(ic->ic_myaddr, rom->macaddr);
+}
+
+int
+rtwn_media_change(struct ifnet *ifp)
+{
+       int error;
+
+       error = ieee80211_media_change(ifp);
+       if (error != ENETRESET)
+               return (error);
+
+       if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
+           (IFF_UP | IFF_RUNNING)) {
+               rtwn_stop(ifp);
+               rtwn_init(ifp);
+       }
+       return (0);
+}
+
+/*
+ * Initialize rate adaptation in firmware.
+ */
+int
+rtwn_ra_init(struct rtwn_softc *sc)
+{
+       static const uint8_t map[] =
+           { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 };
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ieee80211_node *ni = ic->ic_bss;
+       struct ieee80211_rateset *rs = &ni->ni_rates;
+       struct r92c_fw_cmd_macid_cfg cmd;
+       uint32_t rates, basicrates;
+       uint8_t mode;
+       int maxrate, maxbasicrate, error, i, j;
+
+       /* Get normal and basic rates mask. */
+       rates = basicrates = 0;
+       maxrate = maxbasicrate = 0;
+       for (i = 0; i < rs->rs_nrates; i++) {
+               /* Convert 802.11 rate to HW rate index. */
+               for (j = 0; j < nitems(map); j++)
+                       if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == map[j])
+                               break;
+               if (j == nitems(map))   /* Unknown rate, skip. */
+                       continue;
+               rates |= 1 << j;
+               if (j > maxrate)
+                       maxrate = j;
+               if (rs->rs_rates[i] & IEEE80211_RATE_BASIC) {
+                       basicrates |= 1 << j;
+                       if (j > maxbasicrate)
+                               maxbasicrate = j;
+               }
+       }
+       if (ic->ic_curmode == IEEE80211_MODE_11B)
+               mode = R92C_RAID_11B;
+       else
+               mode = R92C_RAID_11BG;
+       DPRINTF(("mode=0x%x rates=0x%08x, basicrates=0x%08x\n",
+           mode, rates, basicrates));
+
+       /* Set rates mask for group addressed frames. */
+       cmd.macid = RTWN_MACID_BC | RTWN_MACID_VALID;
+       cmd.mask = htole32(mode << 28 | basicrates);
+       error = rtwn_fw_cmd(sc, R92C_CMD_MACID_CONFIG, &cmd, sizeof(cmd));
+       if (error != 0) {
+               printf("%s: could not add broadcast station\n",
+                   sc->sc_dev.dv_xname);
+               return (error);
+       }
+       /* Set initial MRR rate. */
+       DPRINTF(("maxbasicrate=%d\n", maxbasicrate));
+       rtwn_write_1(sc, R92C_INIDATA_RATE_SEL(RTWN_MACID_BC),
+           maxbasicrate);
+
+       /* Set rates mask for unicast frames. */
+       cmd.macid = RTWN_MACID_BSS | RTWN_MACID_VALID;
+       cmd.mask = htole32(mode << 28 | rates);
+       error = rtwn_fw_cmd(sc, R92C_CMD_MACID_CONFIG, &cmd, sizeof(cmd));
+       if (error != 0) {
+               printf("%s: could not add BSS station\n",
+                   sc->sc_dev.dv_xname);
+               return (error);
+       }
+       /* Set initial MRR rate. */
+       DPRINTF(("maxrate=%d\n", maxrate));
+       rtwn_write_1(sc, R92C_INIDATA_RATE_SEL(RTWN_MACID_BSS),
+           maxrate);
+
+       /* Configure Automatic Rate Fallback Register. */
+       if (ic->ic_curmode == IEEE80211_MODE_11B) {
+               if (rates & 0x0c)
+                       rtwn_write_4(sc, R92C_ARFR(0), htole32(rates & 0x0d));
+               else
+                       rtwn_write_4(sc, R92C_ARFR(0), htole32(rates & 0x0f));
+       } else
+               rtwn_write_4(sc, R92C_ARFR(0), htole32(rates & 0x0ff5));
+
+       /* Indicate highest supported rate. */
+       ni->ni_txrate = rs->rs_nrates - 1;
+       return (0);
+}
+
+void
+rtwn_tsf_sync_enable(struct rtwn_softc *sc)
+{
+       struct ieee80211_node *ni = sc->sc_ic.ic_bss;
+       uint64_t tsf;
+
+       /* Enable TSF synchronization. */
+       rtwn_write_1(sc, R92C_BCN_CTRL,
+           rtwn_read_1(sc, R92C_BCN_CTRL) & ~R92C_BCN_CTRL_DIS_TSF_UDT0);
+
+       rtwn_write_1(sc, R92C_BCN_CTRL,
+           rtwn_read_1(sc, R92C_BCN_CTRL) & ~R92C_BCN_CTRL_EN_BCN);
+
+       /* Set initial TSF. */
+       memcpy(&tsf, ni->ni_tstamp, 8);
+       tsf = letoh64(tsf);
+       tsf = tsf - (tsf % (ni->ni_intval * IEEE80211_DUR_TU));
+       tsf -= IEEE80211_DUR_TU;
+       rtwn_write_4(sc, R92C_TSFTR + 0, tsf);
+       rtwn_write_4(sc, R92C_TSFTR + 4, tsf >> 32);
+
+       rtwn_write_1(sc, R92C_BCN_CTRL,
+           rtwn_read_1(sc, R92C_BCN_CTRL) | R92C_BCN_CTRL_EN_BCN);
+}
+
+void
+rtwn_set_led(struct rtwn_softc *sc, int led, int on)
+{
+       uint8_t reg;
+
+       if (led == RTWN_LED_LINK) {
+               reg = rtwn_read_1(sc, R92C_LEDCFG0) & 0x70;
+               if (!on)
+                       reg |= R92C_LEDCFG0_DIS;
+               rtwn_write_1(sc, R92C_LEDCFG0, reg);
+               sc->ledlink = on;       /* Save LED state. */
+       }
+}
+
+void
+rtwn_calib_to(void *arg)
+{
+       struct rtwn_softc *sc = arg;
+       struct r92c_fw_cmd_rssi cmd;
+
+       if (sc->avg_pwdb != -1) {
+               /* Indicate Rx signal strength to FW for rate adaptation. */
+               memset(&cmd, 0, sizeof(cmd));
+               cmd.macid = 0;  /* BSS. */
+               cmd.pwdb = sc->avg_pwdb;
+               DPRINTFN(3, ("sending RSSI command avg=%d\n", sc->avg_pwdb));
+               rtwn_fw_cmd(sc, R92C_CMD_RSSI_SETTING, &cmd, sizeof(cmd));
+       }
+
+       /* Do temperature compensation. */
+       rtwn_temp_calib(sc);
+
+       timeout_add_sec(&sc->calib_to, 2);
+}
+
+void
+rtwn_next_scan(void *arg)
+{
+       struct rtwn_softc *sc = arg;
+       struct ieee80211com *ic = &sc->sc_ic;
+       int s;
+
+       s = splnet();
+       if (ic->ic_state == IEEE80211_S_SCAN)
+               ieee80211_next_scan(&ic->ic_if);
+       splx(s);
+}
+
+int
+rtwn_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+{
+       struct rtwn_softc *sc = ic->ic_softc;
+       struct ieee80211_node *ni;
+       enum ieee80211_state ostate;
+       uint32_t reg;
+       int s;
+
+       s = splnet();
+       ostate = ic->ic_state;
+
+       if (nstate != ostate)
+               DPRINTF(("newstate %s -> %s\n",
+                   ieee80211_state_name[ostate],
+                   ieee80211_state_name[nstate]));
+
+       if (ostate == IEEE80211_S_RUN) {
+               /* Stop calibration. */
+               timeout_del(&sc->calib_to);
+
+               /* Turn link LED off. */
+               rtwn_set_led(sc, RTWN_LED_LINK, 0);
+
+               /* Set media status to 'No Link'. */
+               reg = rtwn_read_4(sc, R92C_CR);
+               reg = RW(reg, R92C_CR_NETTYPE, R92C_CR_NETTYPE_NOLINK);
+               rtwn_write_4(sc, R92C_CR, reg);
+
+               /* Stop Rx of data frames. */
+               rtwn_write_2(sc, R92C_RXFLTMAP2, 0);
+
+               /* Rest TSF. */
+               rtwn_write_1(sc, R92C_DUAL_TSF_RST, 0x03);
+
+               /* Disable TSF synchronization. */
+               rtwn_write_1(sc, R92C_BCN_CTRL,
+                   rtwn_read_1(sc, R92C_BCN_CTRL) |
+                   R92C_BCN_CTRL_DIS_TSF_UDT0);
+
+               /* Reset EDCA parameters. */
+               rtwn_write_4(sc, R92C_EDCA_VO_PARAM, 0x002f3217);
+               rtwn_write_4(sc, R92C_EDCA_VI_PARAM, 0x005e4317);
+               rtwn_write_4(sc, R92C_EDCA_BE_PARAM, 0x00105320);
+               rtwn_write_4(sc, R92C_EDCA_BK_PARAM, 0x0000a444);
+       }
+       switch (nstate) {
+       case IEEE80211_S_INIT:
+               /* Turn link LED off. */
+               rtwn_set_led(sc, RTWN_LED_LINK, 0);
+               break;
+       case IEEE80211_S_SCAN:
+               if (ostate != IEEE80211_S_SCAN) {
+                       /* Allow Rx from any BSSID. */
+                       rtwn_write_4(sc, R92C_RCR,
+                           rtwn_read_4(sc, R92C_RCR) &
+                           ~(R92C_RCR_CBSSID_DATA | R92C_RCR_CBSSID_BCN));
+
+                       /* Set gain for scanning. */
+                       reg = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(0));
+                       reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x20);
+                       rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), reg);
+
+                       reg = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(1));
+                       reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x20);
+                       rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(1), reg);
+               }
+
+               /* Make link LED blink during scan. */
+               rtwn_set_led(sc, RTWN_LED_LINK, !sc->ledlink);
+
+               /* Pause AC Tx queues. */
+               rtwn_write_1(sc, R92C_TXPAUSE,
+                   rtwn_read_1(sc, R92C_TXPAUSE) | 0x0f);
+               rtwn_set_chan(sc, ic->ic_bss->ni_chan, NULL);
+               timeout_add_msec(&sc->scan_to, 200);
+               break;
+
+       case IEEE80211_S_AUTH:
+               /* Set initial gain under link. */
+               reg = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(0));
+               reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x32);
+               rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), reg);
+
+               reg = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(1));
+               reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x32);
+               rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(1), reg);
+
+               rtwn_set_chan(sc, ic->ic_bss->ni_chan, NULL);
+               break;
+       case IEEE80211_S_ASSOC:
+               break;
+       case IEEE80211_S_RUN:
+               if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+                       rtwn_set_chan(sc, ic->ic_ibss_chan, NULL);
+
+                       /* Enable Rx of data frames. */
+                       rtwn_write_2(sc, R92C_RXFLTMAP2, 0xffff);
+
+                       /* Turn link LED on. */
+                       rtwn_set_led(sc, RTWN_LED_LINK, 1);
+                       break;
+               }
+               ni = ic->ic_bss;
+
+               /* Set media status to 'Associated'. */
+               reg = rtwn_read_4(sc, R92C_CR);
+               reg = RW(reg, R92C_CR_NETTYPE, R92C_CR_NETTYPE_INFRA);
+               rtwn_write_4(sc, R92C_CR, reg);
+
+               /* Set BSSID. */
+               rtwn_write_4(sc, R92C_BSSID + 0, LE_READ_4(&ni->ni_bssid[0]));
+               rtwn_write_4(sc, R92C_BSSID + 4, LE_READ_2(&ni->ni_bssid[4]));
+
+               if (ic->ic_curmode == IEEE80211_MODE_11B)
+                       rtwn_write_1(sc, R92C_INIRTS_RATE_SEL, 0);
+               else    /* 802.11b/g */
+                       rtwn_write_1(sc, R92C_INIRTS_RATE_SEL, 3);
+
+               /* Enable Rx of data frames. */
+               rtwn_write_2(sc, R92C_RXFLTMAP2, 0xffff);
+
+               /* Flush all AC queues. */
+               rtwn_write_1(sc, R92C_TXPAUSE, 0);
+
+               /* Set beacon interval. */
+               rtwn_write_2(sc, R92C_BCN_INTERVAL, ni->ni_intval);
+
+               /* Allow Rx from our BSSID only. */
+               rtwn_write_4(sc, R92C_RCR,
+                   rtwn_read_4(sc, R92C_RCR) |
+                   R92C_RCR_CBSSID_DATA | R92C_RCR_CBSSID_BCN);
+
+               /* Enable TSF synchronization. */
+               rtwn_tsf_sync_enable(sc);
+
+               rtwn_write_1(sc, R92C_SIFS_CCK + 1, 10);
+               rtwn_write_1(sc, R92C_SIFS_OFDM + 1, 10);
+               rtwn_write_1(sc, R92C_SPEC_SIFS + 1, 10);
+               rtwn_write_1(sc, R92C_MAC_SPEC_SIFS + 1, 10);
+               rtwn_write_1(sc, R92C_R2T_SIFS + 1, 10);
+               rtwn_write_1(sc, R92C_T2T_SIFS + 1, 10);
+
+               /* Intialize rate adaptation. */
+               rtwn_ra_init(sc);
+               /* Turn link LED on. */
+               rtwn_set_led(sc, RTWN_LED_LINK, 1);
+
+               sc->avg_pwdb = -1;      /* Reset average RSSI. */
+               /* Reset temperature calibration state machine. */
+               sc->thcal_state = 0;
+               sc->thcal_lctemp = 0;
+               /* Start periodic calibration. */
+               timeout_add_sec(&sc->calib_to, 2);
+               break;
+       }
+       (void)sc->sc_newstate(ic, nstate, arg);
+       splx(s);
+
+       return (0);
+}
+
+void
+rtwn_updateedca(struct ieee80211com *ic)
+{
+       struct rtwn_softc *sc = ic->ic_softc;
+       const uint16_t aci2reg[EDCA_NUM_AC] = {
+               R92C_EDCA_BE_PARAM,
+               R92C_EDCA_BK_PARAM,
+               R92C_EDCA_VI_PARAM,
+               R92C_EDCA_VO_PARAM
+       };
+       struct ieee80211_edca_ac_params *ac;
+       int s, aci, aifs, slottime;
+
+       s = splnet();
+       slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20;
+       for (aci = 0; aci < EDCA_NUM_AC; aci++) {
+               ac = &ic->ic_edca_ac[aci];
+               /* AIFS[AC] = AIFSN[AC] * aSlotTime + aSIFSTime. */
+               aifs = ac->ac_aifsn * slottime + 10;
+               rtwn_write_4(sc, aci2reg[aci],
+                   SM(R92C_EDCA_PARAM_TXOP, ac->ac_txoplimit) |
+                   SM(R92C_EDCA_PARAM_ECWMIN, ac->ac_ecwmin) |
+                   SM(R92C_EDCA_PARAM_ECWMAX, ac->ac_ecwmax) |
+                   SM(R92C_EDCA_PARAM_AIFS, aifs));
+       }
+       splx(s);
+}
+
+int
+rtwn_set_key(struct ieee80211com *ic, struct ieee80211_node *ni,
+    struct ieee80211_key *k)
+{
+       struct rtwn_softc *sc = ic->ic_softc;
+       static const uint8_t etherzeroaddr[6] = { 0 };
+       const uint8_t *macaddr;
+       uint8_t keybuf[16], algo;
+       int i, entry;
+
+       /* Defer setting of WEP keys until interface is brought up. */
+       if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) !=
+           (IFF_UP | IFF_RUNNING))
+               return (0);
+
+       /* Map net80211 cipher to HW crypto algorithm. */
+       switch (k->k_cipher) {
+       case IEEE80211_CIPHER_WEP40:
+               algo = R92C_CAM_ALGO_WEP40;
+               break;
+       case IEEE80211_CIPHER_WEP104:
+               algo = R92C_CAM_ALGO_WEP104;
+               break;
+       case IEEE80211_CIPHER_TKIP:
+               algo = R92C_CAM_ALGO_TKIP;
+               break;
+       case IEEE80211_CIPHER_CCMP:
+               algo = R92C_CAM_ALGO_AES;
+               break;
+       default:
+               /* Fallback to software crypto for other ciphers. */
+               return (ieee80211_set_key(ic, ni, k));
+       }
+       if (k->k_flags & IEEE80211_KEY_GROUP) {
+               macaddr = etherzeroaddr;
+               entry = k->k_id;
+       } else {
+               macaddr = ic->ic_bss->ni_macaddr;
+               entry = 4;
+       }
+       /* Write key. */
+       memset(keybuf, 0, sizeof(keybuf));
+       memcpy(keybuf, k->k_key, MIN(k->k_len, sizeof(keybuf)));
+       for (i = 0; i < 4; i++) {
+               rtwn_cam_write(sc, R92C_CAM_KEY(entry, i),
+                   LE_READ_4(&keybuf[i * 4]));
+       }
+       /* Write CTL0 last since that will validate the CAM entry. */
+       rtwn_cam_write(sc, R92C_CAM_CTL1(entry),
+           LE_READ_4(&macaddr[2]));
+       rtwn_cam_write(sc, R92C_CAM_CTL0(entry),
+           SM(R92C_CAM_ALGO, algo) |
+           SM(R92C_CAM_KEYID, k->k_id) |
+           SM(R92C_CAM_MACLO, LE_READ_2(&macaddr[0])) |
+           R92C_CAM_VALID);
+
+       return (0);
+}
+
+void
+rtwn_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni,
+    struct ieee80211_key *k)
+{
+       struct rtwn_softc *sc = ic->ic_softc;
+       int i, entry;
+
+       if (!(ic->ic_if.if_flags & IFF_RUNNING) ||
+           ic->ic_state != IEEE80211_S_RUN)
+               return; /* Nothing to do. */
+
+       if (k->k_flags & IEEE80211_KEY_GROUP)
+               entry = k->k_id;
+       else
+               entry = 4;
+       rtwn_cam_write(sc, R92C_CAM_CTL0(entry), 0);
+       rtwn_cam_write(sc, R92C_CAM_CTL1(entry), 0);
+       /* Clear key. */
+       for (i = 0; i < 4; i++)
+               rtwn_cam_write(sc, R92C_CAM_KEY(entry, i), 0);
+}
+
+void
+rtwn_update_avgrssi(struct rtwn_softc *sc, int rate, int8_t rssi)
+{
+       int pwdb;
+
+       /* Convert antenna signal to percentage. */
+       if (rssi <= -100 || rssi >= 20)
+               pwdb = 0;
+       else if (rssi >= 0)
+               pwdb = 100;
+       else
+               pwdb = 100 + rssi;
+       if (rate <= 3) {
+               /* CCK gain is smaller than OFDM/MCS gain. */
+               pwdb += 6;
+               if (pwdb > 100)
+                       pwdb = 100;
+               if (pwdb <= 14)
+                       pwdb -= 4;
+               else if (pwdb <= 26)
+                       pwdb -= 8;
+               else if (pwdb <= 34)
+                       pwdb -= 6;
+               else if (pwdb <= 42)
+                       pwdb -= 2;
+       }
+       if (sc->avg_pwdb == -1) /* Init. */
+               sc->avg_pwdb = pwdb;
+       else if (sc->avg_pwdb < pwdb)
+               sc->avg_pwdb = ((sc->avg_pwdb * 19 + pwdb) / 20) + 1;
+       else
+               sc->avg_pwdb = ((sc->avg_pwdb * 19 + pwdb) / 20);
+       DPRINTFN(4, ("PWDB=%d EMA=%d\n", pwdb, sc->avg_pwdb));
+}
+
+int8_t
+rtwn_get_rssi(struct rtwn_softc *sc, int rate, void *physt)
+{
+       static const int8_t cckoff[] = { 16, -12, -26, -46 };
+       struct r92c_rx_phystat *phy;
+       struct r92c_rx_cck *cck;
+       uint8_t rpt;
+       int8_t rssi;
+
+       if (rate <= 3) {
+               cck = (struct r92c_rx_cck *)physt;
+               if (sc->sc_flags & RTWN_FLAG_CCK_HIPWR) {
+                       rpt = (cck->agc_rpt >> 5) & 0x3;
+                       rssi = (cck->agc_rpt & 0x1f) << 1;
+               } else {
+                       rpt = (cck->agc_rpt >> 6) & 0x3;
+                       rssi = cck->agc_rpt & 0x3e;
+               }
+               rssi = cckoff[rpt] - rssi;
+       } else {        /* OFDM/HT. */
+               phy = (struct r92c_rx_phystat *)physt;
+               rssi = ((letoh32(phy->phydw1) >> 1) & 0x7f) - 110;
+       }
+       return (rssi);
+}
+
+void
+rtwn_rx_frame(struct rtwn_softc *sc, struct r92c_rx_desc *rx_desc,
+    struct rtwn_rx_data *rx_data, int desc_idx)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+       struct ieee80211_rxinfo rxi;
+       struct ieee80211_frame *wh;
+       struct ieee80211_node *ni;
+       struct r92c_rx_phystat *phy = NULL;
+       uint32_t rxdw0, rxdw3;
+       struct mbuf *m, *m1;
+       uint8_t rate;
+       int8_t rssi = 0;
+       int infosz, pktlen, shift, error;
+
+       rxdw0 = letoh32(rx_desc->rxdw0);
+       rxdw3 = letoh32(rx_desc->rxdw3);
+
+       if (__predict_false(rxdw0 & (R92C_RXDW0_CRCERR | R92C_RXDW0_ICVERR))) {
+               /*
+                * This should not happen since we setup our Rx filter
+                * to not receive these frames.
+                */
+               ifp->if_ierrors++;
+               return;
+       }
+
+       pktlen = MS(rxdw0, R92C_RXDW0_PKTLEN);
+       if (__predict_false(pktlen < sizeof(*wh) || pktlen > MCLBYTES)) {
+               ifp->if_ierrors++;
+               return;
+       }
+
+       rate = MS(rxdw3, R92C_RXDW3_RATE);
+       infosz = MS(rxdw0, R92C_RXDW0_INFOSZ) * 8;
+       if (infosz > sizeof(struct r92c_rx_phystat))
+               infosz = sizeof(struct r92c_rx_phystat);
+       shift = MS(rxdw0, R92C_RXDW0_SHIFT);
+
+       /* Get RSSI from PHY status descriptor if present. */
+       if (infosz != 0 && (rxdw0 & R92C_RXDW0_PHYST)) {
+               phy = mtod(rx_data->m, struct r92c_rx_phystat *);
+               rssi = rtwn_get_rssi(sc, rate, phy);
+               /* Update our average RSSI. */
+               rtwn_update_avgrssi(sc, rate, rssi);
+       }
+
+       DPRINTFN(5, ("Rx frame len=%d rate=%d infosz=%d shift=%d rssi=%d\n",
+           pktlen, rate, infosz, shift, rssi));
+
+       m1 = MCLGETI(NULL, M_DONTWAIT, NULL, MCLBYTES);
+       if (m1 == NULL) {
+               ifp->if_ierrors++;
+               return;
+       }
+       bus_dmamap_unload(sc->sc_dmat, rx_data->map);
+       error = bus_dmamap_load(sc->sc_dmat, rx_data->map,
+           mtod(m1, void *), MCLBYTES, NULL,
+           BUS_DMA_NOWAIT | BUS_DMA_READ);
+       if (error != 0) {
+               m_freem(m1);
+
+               if (bus_dmamap_load_mbuf(sc->sc_dmat, rx_data->map,
+                   rx_data->m, BUS_DMA_NOWAIT))
+                       panic("%s: could not load old RX mbuf",
+                           sc->sc_dev.dv_xname);
+
+               /* Physical address may have changed. */
+               rtwn_setup_rx_desc(sc, rx_desc,
+                   rx_data->map->dm_segs[0].ds_addr, MCLBYTES, desc_idx);
+
+               ifp->if_ierrors++;
+               return;
+       }
+
+       /* Finalize mbuf. */
+       m = rx_data->m;
+       rx_data->m = m1;
+       m->m_pkthdr.rcvif = ifp;
+       m->m_pkthdr.len = m->m_len = pktlen + infosz + shift;
+
+       /* Update RX descriptor. */
+       rtwn_setup_rx_desc(sc, rx_desc, rx_data->map->dm_segs[0].ds_addr,
+           MCLBYTES, desc_idx);
+
+       /* Get ieee80211 frame header. */
+       if (rxdw0 & R92C_RXDW0_PHYST)
+               m_adj(m, infosz + shift);
+       else
+               m_adj(m, shift);
+       wh = mtod(m, struct ieee80211_frame *);
+
+#if NBPFILTER > 0
+       if (__predict_false(sc->sc_drvbpf != NULL)) {
+               struct rtwn_rx_radiotap_header *tap = &sc->sc_rxtap;
+               struct mbuf mb;
+
+               tap->wr_flags = 0;
+               /* Map HW rate index to 802.11 rate. */
+               tap->wr_flags = 2;
+               if (!(rxdw3 & R92C_RXDW3_HT)) {
+                       switch (rate) {
+                       /* CCK. */
+                       case  0: tap->wr_rate =   2; break;
+                       case  1: tap->wr_rate =   4; break;
+                       case  2: tap->wr_rate =  11; break;
+                       case  3: tap->wr_rate =  22; break;
+                       /* OFDM. */
+                       case  4: tap->wr_rate =  12; break;
+                       case  5: tap->wr_rate =  18; break;
+                       case  6: tap->wr_rate =  24; break;
+                       case  7: tap->wr_rate =  36; break;
+                       case  8: tap->wr_rate =  48; break;
+                       case  9: tap->wr_rate =  72; break;
+                       case 10: tap->wr_rate =  96; break;
+                       case 11: tap->wr_rate = 108; break;
+                       }
+               } else if (rate >= 12) {        /* MCS0~15. */
+                       /* Bit 7 set means HT MCS instead of rate. */
+                       tap->wr_rate = 0x80 | (rate - 12);
+               }
+               tap->wr_dbm_antsignal = rssi;
+               tap->wr_chan_freq = htole16(ic->ic_ibss_chan->ic_freq);
+               tap->wr_chan_flags = htole16(ic->ic_ibss_chan->ic_flags);
+
+               mb.m_data = (caddr_t)tap;
+               mb.m_len = sc->sc_rxtap_len;
+               mb.m_next = m;
+               mb.m_nextpkt = NULL;
+               mb.m_type = 0;
+               mb.m_flags = 0;
+               bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN);
+       }
+#endif
+
+       ni = ieee80211_find_rxnode(ic, wh);
+       rxi.rxi_flags = 0;
+       rxi.rxi_rssi = rssi;
+       rxi.rxi_tstamp = 0;     /* Unused. */
+       ieee80211_input(ifp, m, ni, &rxi);
+       /* Node is no longer needed. */
+       ieee80211_release_node(ic, ni);
+}
+
+int
+rtwn_tx(struct rtwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ieee80211_frame *wh;
+       struct ieee80211_key *k = NULL;
+       struct rtwn_tx_ring *tx_ring;
+       struct rtwn_tx_data *data;
+       struct r92c_tx_desc *txd;
+       uint16_t qos;
+       uint8_t raid, type, tid, qid;
+       int hasqos, error;
+
+       wh = mtod(m, struct ieee80211_frame *);
+       type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+
+       if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+               k = ieee80211_get_txkey(ic, wh, ni);
+               if ((m = ieee80211_encrypt(ic, m, k)) == NULL)
+                       return (ENOBUFS);
+               wh = mtod(m, struct ieee80211_frame *);
+       }
+
+       if ((hasqos = ieee80211_has_qos(wh))) {
+               qos = ieee80211_get_qos(wh);
+               tid = qos & IEEE80211_QOS_TID;
+               qid = ieee80211_up_to_ac(ic, tid);
+       } else if (type != IEEE80211_FC0_TYPE_DATA) {
+               qid = RTWN_VO_QUEUE;
+       } else
+               qid = RTWN_BE_QUEUE;
+
+       /* Grab a Tx buffer from the ring. */
+       tx_ring = &sc->tx_ring[qid];
+       data = &tx_ring->tx_data[tx_ring->cur];
+       if (data->m != NULL) {
+               m_freem(m);
+               return (ENOBUFS);
+       }
+
+       /* Fill Tx descriptor. */
+       txd = &tx_ring->desc[tx_ring->cur];
+       if (htole32(txd->txdw0) & R92C_RXDW0_OWN) {
+               m_freem(m);
+               return (ENOBUFS);
+       }
+       txd->txdw0 = htole32(
+           SM(R92C_TXDW0_PKTLEN, m->m_pkthdr.len) |
+           SM(R92C_TXDW0_OFFSET, sizeof(*txd)) |
+           R92C_TXDW0_FSG | R92C_TXDW0_LSG);
+       if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+               txd->txdw0 |= htole32(R92C_TXDW0_BMCAST);
+
+       txd->txdw1 = 0;
+#ifdef notyet
+       if (k != NULL) {
+               switch (k->k_cipher) {
+               case IEEE80211_CIPHER_WEP40:
+               case IEEE80211_CIPHER_WEP104:
+               case IEEE80211_CIPHER_TKIP:
+                       cipher = R92C_TXDW1_CIPHER_RC4;
+                       break;
+               case IEEE80211_CIPHER_CCMP:
+                       cipher = R92C_TXDW1_CIPHER_AES;
+                       break;
+               default:
+                       cipher = R92C_TXDW1_CIPHER_NONE;
+               }
+               txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER, cipher));
+       }
+#endif
+       txd->txdw4 = 0;
+       txd->txdw5 = 0;
+       if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+           type == IEEE80211_FC0_TYPE_DATA) {
+               if (ic->ic_curmode == IEEE80211_MODE_11B)
+                       raid = R92C_RAID_11B;
+               else
+                       raid = R92C_RAID_11BG;
+               txd->txdw1 |= htole32(
+                   SM(R92C_TXDW1_MACID, RTWN_MACID_BSS) |
+                   SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BE) |
+                   SM(R92C_TXDW1_RAID, raid) |
+                   R92C_TXDW1_AGGBK);
+
+               if (ic->ic_flags & IEEE80211_F_USEPROT) {
+                       if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) {
+                               txd->txdw4 |= htole32(R92C_TXDW4_CTS2SELF |
+                                   R92C_TXDW4_HWRTSEN);
+                       } else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) {
+                               txd->txdw4 |= htole32(R92C_TXDW4_RTSEN |
+                                   R92C_TXDW4_HWRTSEN);
+                       }
+               }
+               /* Send RTS at OFDM24. */
+               txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, 8));
+               txd->txdw5 |= htole32(SM(R92C_TXDW5_RTSRATE_FBLIMIT, 0xf));
+               /* Send data at OFDM54. */
+               txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 11));
+               txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE_FBLIMIT, 0x1f));
+
+       } else {
+               txd->txdw1 |= htole32(
+                   SM(R92C_TXDW1_MACID, 0) |
+                   SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_MGNT) |
+                   SM(R92C_TXDW1_RAID, R92C_RAID_11B));
+
+               /* Force CCK1. */
+               txd->txdw4 |= htole32(R92C_TXDW4_DRVRATE);
+               txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 0));
+       }
+       /* Set sequence number (already little endian). */
+       txd->txdseq = *(uint16_t *)wh->i_seq;
+
+       if (!hasqos) {
+               /* Use HW sequence numbering for non-QoS frames. */
+               txd->txdw4  |= htole32(R92C_TXDW4_HWSEQ);
+               txd->txdseq |= htole16(0x8000);         /* WTF? */
+       } else
+               txd->txdw4 |= htole32(R92C_TXDW4_QOS);
+
+       error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m,
+           BUS_DMA_NOWAIT | BUS_DMA_WRITE);
+       if (error && error != EFBIG) {
+               printf("%s: can't map mbuf (error %d)\n",
+                   sc->sc_dev.dv_xname, error);
+               m_freem(m);
+               return error;
+       }
+       if (error != 0) {
+               /* Too many DMA segments, linearize mbuf. */
+               if (m_defrag(m, M_DONTWAIT)) {
+                       m_freem(m);
+                       return ENOBUFS;
+               }
+
+               error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m,
+                   BUS_DMA_NOWAIT | BUS_DMA_WRITE);
+               if (error != 0) {
+                       printf("%s: can't map mbuf (error %d)\n",
+                           sc->sc_dev.dv_xname, error);
+                       m_freem(m);
+                       return error;
+               }
+       }
+
+       txd->txbufaddr = htole32(data->map->dm_segs[0].ds_addr);
+       txd->txbufsize = htole16(m->m_pkthdr.len);
+       bus_space_barrier(sc->sc_st, sc->sc_sh, 0, sc->sc_mapsize,
+           BUS_SPACE_BARRIER_WRITE);
+       txd->txdw0 |= htole32(R92C_TXDW0_OWN);
+
+       bus_dmamap_sync(sc->sc_dmat, tx_ring->map, 0, MCLBYTES,
+           BUS_DMASYNC_POSTWRITE);
+       bus_dmamap_sync(sc->sc_dmat, data->map, 0, MCLBYTES,
+           BUS_DMASYNC_POSTWRITE);
+
+       data->m = m;
+       data->ni = ni;
+
+#if NBPFILTER > 0
+       if (__predict_false(sc->sc_drvbpf != NULL)) {
+               struct rtwn_tx_radiotap_header *tap = &sc->sc_txtap;
+               struct mbuf mb;
+
+               tap->wt_flags = 0;
+               tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
+               tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
+
+               mb.m_data = (caddr_t)tap;
+               mb.m_len = sc->sc_txtap_len;
+               mb.m_next = m;
+               mb.m_nextpkt = NULL;
+               mb.m_type = 0;
+               mb.m_flags = 0;
+               bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT);
+       }
+#endif
+
+       tx_ring->cur = (tx_ring->cur + 1) % RTWN_TX_LIST_COUNT;
+       tx_ring->queued++;
+
+       if (tx_ring->queued >= (RTWN_TX_LIST_COUNT - 1))
+               sc->qfullmsk |= (1 << qid);
+
+       /* Kick TX. */
+       rtwn_write_2(sc, R92C_PCIE_CTRL_REG, (1 << qid));
+
+       return (0);
+}
+
+void
+rtwn_tx_done(struct rtwn_softc *sc, int qid)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = &ic->ic_if;
+       struct rtwn_tx_ring *tx_ring = &sc->tx_ring[qid];
+       struct rtwn_tx_data *tx_data;
+       struct r92c_tx_desc *tx_desc;
+       int i;
+
+       bus_dmamap_sync(sc->sc_dmat, tx_ring->map, 0, MCLBYTES,
+           BUS_DMASYNC_POSTREAD);
+
+       for (i = 0; i < RTWN_TX_LIST_COUNT; i++) {
+               tx_data = &tx_ring->tx_data[i];
+               if (tx_data->m == NULL)
+                       continue;
+
+               tx_desc = &tx_ring->desc[i];
+               if (letoh32(tx_desc->txdw0) & R92C_TXDW0_OWN)
+                       continue;
+
+               bus_dmamap_unload(sc->sc_dmat, tx_data->map);
+               m_freem(tx_data->m);
+               tx_data->m = NULL;
+               ieee80211_release_node(ic, tx_data->ni);
+               tx_data->ni = NULL;
+
+               ifp->if_opackets++;
+               sc->sc_tx_timer = 0;
+               tx_ring->queued--;
+       }
+
+       if (tx_ring->queued < (RTWN_TX_LIST_COUNT - 1))
+               sc->qfullmsk &= ~(1 << qid);
+       
+       if (sc->qfullmsk == 0) {
+               ifp->if_flags &= (~IFF_OACTIVE);
+               (*ifp->if_start)(ifp);
+       }
+}
+
+void
+rtwn_start(struct ifnet *ifp)
+{
+       struct rtwn_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ieee80211_node *ni;
+       struct mbuf *m;
+
+       if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+               return;
+
+       for (;;) {
+               if (sc->qfullmsk != 0) {
+                       ifp->if_flags |= IFF_OACTIVE;
+                       break;
+               }
+               /* Send pending management frames first. */
+               IF_DEQUEUE(&ic->ic_mgtq, m);
+               if (m != NULL) {
+                       ni = m->m_pkthdr.ph_cookie;
+                       goto sendit;
+               }
+               if (ic->ic_state != IEEE80211_S_RUN)
+                       break;
+
+               /* Encapsulate and send data frames. */
+               IFQ_DEQUEUE(&ifp->if_snd, m);
+               if (m == NULL)
+                       break;
+#if NBPFILTER > 0
+               if (ifp->if_bpf != NULL)
+                       bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
+#endif
+               if ((m = ieee80211_encap(ifp, m, &ni)) == NULL)
+                       continue;
+sendit:
+#if NBPFILTER > 0
+               if (ic->ic_rawbpf != NULL)
+                       bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT);
+#endif
+               if (rtwn_tx(sc, m, ni) != 0) {
+                       ieee80211_release_node(ic, ni);
+                       ifp->if_oerrors++;
+                       continue;
+               }
+
+               sc->sc_tx_timer = 5;
+               ifp->if_timer = 1;
+       }
+}
+
+void
+rtwn_watchdog(struct ifnet *ifp)
+{
+       struct rtwn_softc *sc = ifp->if_softc;
+
+       ifp->if_timer = 0;
+
+       if (sc->sc_tx_timer > 0) {
+               if (--sc->sc_tx_timer == 0) {
+                       printf("%s: device timeout\n", sc->sc_dev.dv_xname);
+                       task_add(systq, &sc->init_task);
+                       ifp->if_oerrors++;
+                       return;
+               }
+               ifp->if_timer = 1;
+       }
+       ieee80211_watchdog(ifp);
+}
+
+int
+rtwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+       struct rtwn_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifaddr *ifa;
+       struct ifreq *ifr;
+       int s, error = 0;
+
+       s = splnet();
+       /*
+        * Prevent processes from entering this function while another
+        * process is tsleep'ing in it.
+        */
+       while ((sc->sc_flags & RTWN_FLAG_BUSY) && error == 0)
+               error = tsleep(&sc->sc_flags, PCATCH, "rtwnioc", 0);
+       if (error != 0) {
+               splx(s);
+               return error;
+       }
+       sc->sc_flags |= RTWN_FLAG_BUSY;
+
+       switch (cmd) {
+       case SIOCSIFADDR:
+               ifa = (struct ifaddr *)data;
+               ifp->if_flags |= IFF_UP;
+               if (ifa->ifa_addr->sa_family == AF_INET)
+                       arp_ifinit(&ic->ic_ac, ifa);
+               /* FALLTHROUGH */
+       case SIOCSIFFLAGS:
+               if (ifp->if_flags & IFF_UP) {
+                       if (!(ifp->if_flags & IFF_RUNNING))
+                               rtwn_init(ifp);
+               } else {
+                       if (ifp->if_flags & IFF_RUNNING)
+                               rtwn_stop(ifp);
+               }
+               break;
+       case SIOCADDMULTI:
+       case SIOCDELMULTI:
+               ifr = (struct ifreq *)data;
+               error = (cmd == SIOCADDMULTI) ?
+                   ether_addmulti(ifr, &ic->ic_ac) :
+                   ether_delmulti(ifr, &ic->ic_ac);
+               if (error == ENETRESET)
+                       error = 0;
+               break;
+       case SIOCS80211CHANNEL:
+               error = ieee80211_ioctl(ifp, cmd, data);
+               if (error == ENETRESET &&
+                   ic->ic_opmode == IEEE80211_M_MONITOR) {
+                       if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
+                           (IFF_UP | IFF_RUNNING))
+                               rtwn_set_chan(sc, ic->ic_ibss_chan, NULL);
+                       error = 0;
+               }
+               break;
+       default:
+               error = ieee80211_ioctl(ifp, cmd, data);
+       }
+
+       if (error == ENETRESET) {
+               if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
+                   (IFF_UP | IFF_RUNNING)) {
+                       rtwn_stop(ifp);
+                       rtwn_init(ifp);
+               }
+               error = 0;
+       }
+       sc->sc_flags &= ~RTWN_FLAG_BUSY;
+       wakeup(&sc->sc_flags);
+       splx(s);
+
+       return (error);
+}
+
+int
+rtwn_power_on(struct rtwn_softc *sc)
+{
+       uint32_t reg;
+       int ntries;
+
+       /* Wait for autoload done bit. */
+       for (ntries = 0; ntries < 1000; ntries++) {
+               if (rtwn_read_1(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_PFM_ALDN)
+                       break;
+               DELAY(5);
+       }
+       if (ntries == 1000) {
+               printf("%s: timeout waiting for chip autoload\n",
+                   sc->sc_dev.dv_xname);
+               return (ETIMEDOUT);
+       }
+
+       /* Unlock ISO/CLK/Power control register. */
+       rtwn_write_1(sc, R92C_RSV_CTRL, 0);
+
+       /* TODO: check if we need this for 8188CE */
+       if (sc->board_type != R92C_BOARD_TYPE_DONGLE) {
+               /* bt coex */
+               reg = rtwn_read_4(sc, R92C_APS_FSMCO);
+               reg |= (R92C_APS_FSMCO_SOP_ABG |
+                       R92C_APS_FSMCO_SOP_AMB |
+                       R92C_APS_FSMCO_XOP_BTCK);
+               rtwn_write_4(sc, R92C_APS_FSMCO, reg);
+       }
+
+       /* Move SPS into PWM mode. */
+       rtwn_write_1(sc, R92C_SPS0_CTRL, 0x2b);
+
+       /* Set low byte to 0x0f, leave others unchanged. */
+       rtwn_write_4(sc, R92C_AFE_XTAL_CTRL,
+           (rtwn_read_4(sc, R92C_AFE_XTAL_CTRL) & 0xffffff00) | 0x0f);
+
+       /* TODO: check if we need this for 8188CE */
+       if (sc->board_type != R92C_BOARD_TYPE_DONGLE) {
+               /* bt coex */
+               reg = rtwn_read_4(sc, R92C_AFE_XTAL_CTRL);
+               reg &= (~0x00024800); /* XXX magic from linux */
+               rtwn_write_4(sc, R92C_AFE_XTAL_CTRL, reg);
+       }
+
+       rtwn_write_2(sc, R92C_SYS_ISO_CTRL,
+         (rtwn_read_2(sc, R92C_SYS_ISO_CTRL) & 0xff) |
+         R92C_SYS_ISO_CTRL_PWC_EV12V | R92C_SYS_ISO_CTRL_DIOR);
+       DELAY(200);
+
+       /* TODO: linux does additional btcoex stuff here */
+
+       /* Auto enable WLAN. */
+       rtwn_write_2(sc, R92C_APS_FSMCO,
+           rtwn_read_2(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_APFM_ONMAC);
+       for (ntries = 0; ntries < 1000; ntries++) {
+               if (!(rtwn_read_2(sc, R92C_APS_FSMCO) &
+                   R92C_APS_FSMCO_APFM_ONMAC))
+                       break;
+               DELAY(5);
+       }
+       if (ntries == 1000) {
+               printf("%s: timeout waiting for MAC auto ON\n",
+                   sc->sc_dev.dv_xname);
+               return (ETIMEDOUT);
+       }
+
+       /* Enable radio, GPIO and LED functions. */
+       rtwn_write_2(sc, R92C_APS_FSMCO,
+           R92C_APS_FSMCO_AFSM_PCIE |
+           R92C_APS_FSMCO_PDN_EN |
+           R92C_APS_FSMCO_PFM_ALDN);
+       /* Release RF digital isolation. */
+       rtwn_write_2(sc, R92C_SYS_ISO_CTRL,
+           rtwn_read_2(sc, R92C_SYS_ISO_CTRL) & ~R92C_SYS_ISO_CTRL_DIOR);
+
+       if (sc->chip & RTWN_CHIP_92C)
+               rtwn_write_1(sc, R92C_PCIE_CTRL_REG + 3, 0x77);
+       else
+               rtwn_write_1(sc, R92C_PCIE_CTRL_REG + 3, 0x22);
+
+       rtwn_write_4(sc, R92C_INT_MIG, 0);
+
+       if (sc->board_type != R92C_BOARD_TYPE_DONGLE) {
+               /* bt coex */
+               reg = rtwn_read_4(sc, R92C_AFE_XTAL_CTRL + 2);
+               reg &= 0xfd; /* XXX magic from linux */
+               rtwn_write_4(sc, R92C_AFE_XTAL_CTRL + 2, reg);
+       }
+
+       rtwn_write_1(sc, R92C_GPIO_MUXCFG,
+           rtwn_read_1(sc, R92C_GPIO_MUXCFG) & ~R92C_GPIO_MUXCFG_RFKILL);
+
+       reg = rtwn_read_1(sc, R92C_GPIO_IO_SEL);
+       if (!(reg & R92C_GPIO_IO_SEL_RFKILL)) {
+               printf("%s: radio is disabled by hardware switch\n",
+                   sc->sc_dev.dv_xname);
+               return (EPERM); /* :-) */
+       }
+
+       /* Initialize MAC. */
+       reg = rtwn_read_1(sc, R92C_APSD_CTRL);
+       rtwn_write_1(sc, R92C_APSD_CTRL,
+           rtwn_read_1(sc, R92C_APSD_CTRL) & ~R92C_APSD_CTRL_OFF);
+       for (ntries = 0; ntries < 200; ntries++) {
+               if (!(rtwn_read_1(sc, R92C_APSD_CTRL) &
+                   R92C_APSD_CTRL_OFF_STATUS))
+                       break;
+               DELAY(500);
+       }
+       if (ntries == 200) {
+               printf("%s: timeout waiting for MAC initialization\n",
+                   sc->sc_dev.dv_xname);
+               return (ETIMEDOUT);
+       }
+
+       /* Enable MAC DMA/WMAC/SCHEDULE/SEC blocks. */
+       reg = rtwn_read_2(sc, R92C_CR);
+       reg |= R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN |
+           R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN |
+           R92C_CR_SCHEDULE_EN | R92C_CR_MACTXEN | R92C_CR_MACRXEN |
+           R92C_CR_ENSEC;
+       rtwn_write_2(sc, R92C_CR, reg);
+
+       rtwn_write_1(sc, 0xfe10, 0x19);
+
+       return (0);
+}
+
+int
+rtwn_llt_init(struct rtwn_softc *sc)
+{
+       int i, error;
+
+       /* Reserve pages [0; R92C_TX_PAGE_COUNT]. */
+       for (i = 0; i < R92C_TX_PAGE_COUNT; i++) {
+               if ((error = rtwn_llt_write(sc, i, i + 1)) != 0)
+                       return (error);
+       }
+       /* NB: 0xff indicates end-of-list. */
+       if ((error = rtwn_llt_write(sc, i, 0xff)) != 0)
+               return (error);
+       /*
+        * Use pages [R92C_TX_PAGE_COUNT + 1; R92C_TXPKTBUF_COUNT - 1]
+        * as ring buffer.
+        */
+       for (++i; i < R92C_TXPKTBUF_COUNT - 1; i++) {
+               if ((error = rtwn_llt_write(sc, i, i + 1)) != 0)
+                       return (error);
+       }
+       /* Make the last page point to the beginning of the ring buffer. */
+       error = rtwn_llt_write(sc, i, R92C_TX_PAGE_COUNT + 1);
+       return (error);
+}
+
+void
+rtwn_fw_reset(struct rtwn_softc *sc)
+{
+       uint16_t reg;
+       int ntries;
+
+       /* Tell 8051 to reset itself. */
+       rtwn_write_1(sc, R92C_HMETFR + 3, 0x20);
+
+       /* Wait until 8051 resets by itself. */
+       for (ntries = 0; ntries < 100; ntries++) {
+               reg = rtwn_read_2(sc, R92C_SYS_FUNC_EN);
+               if (!(reg & R92C_SYS_FUNC_EN_CPUEN))
+                       goto sleep;
+               DELAY(50);
+       }
+       /* Force 8051 reset. */
+       rtwn_write_2(sc, R92C_SYS_FUNC_EN, reg & ~R92C_SYS_FUNC_EN_CPUEN);
+sleep:
+       /* 
+        * We must sleep for one second to let the firmware settle.
+        * Accessing registers too early will hang the whole system.
+        */
+       tsleep(&reg, 0, "rtwnrst", hz);
+}
+
+int
+rtwn_fw_loadpage(struct rtwn_softc *sc, int page, uint8_t *buf, int len)
+{
+       uint32_t reg;
+       int off, mlen, error = 0, i;
+
+       reg = rtwn_read_4(sc, R92C_MCUFWDL);
+       reg = RW(reg, R92C_MCUFWDL_PAGE, page);
+       rtwn_write_4(sc, R92C_MCUFWDL, reg);
+
+       DELAY(5);
+
+       off = R92C_FW_START_ADDR;
+       while (len > 0) {
+               if (len > 196)
+                       mlen = 196;
+               else if (len > 4)
+                       mlen = 4;
+               else
+                       mlen = 1;
+               for (i = 0; i < mlen; i++)
+                       rtwn_write_1(sc, off++, buf[i]);
+               buf += mlen;
+               len -= mlen;
+       }
+
+       return (error);
+}
+
+int
+rtwn_load_firmware(struct rtwn_softc *sc)
+{
+       const struct r92c_fw_hdr *hdr;
+       const char *name;
+       u_char *fw, *ptr;
+       size_t len;
+       uint32_t reg;
+       int mlen, ntries, page, error;
+
+       /* Read firmware image from the filesystem. */
+       if ((sc->chip & (RTWN_CHIP_UMC_A_CUT | RTWN_CHIP_92C)) ==
+           RTWN_CHIP_UMC_A_CUT)
+               name = "rtwn-rtl8192cfwU";
+       else
+               name = "rtwn-rtl8192cfwU_B";
+       if ((error = loadfirmware(name, &fw, &len)) != 0) {
+               printf("%s: could not read firmware %s (error %d)\n",
+                   sc->sc_dev.dv_xname, name, error);
+               return (error);
+       }
+       if (len < sizeof(*hdr)) {
+               printf("%s: firmware too short\n", sc->sc_dev.dv_xname);
+               error = EINVAL;
+               goto fail;
+       }
+       ptr = fw;
+       hdr = (const struct r92c_fw_hdr *)ptr;
+       /* Check if there is a valid FW header and skip it. */
+       if ((letoh16(hdr->signature) >> 4) == 0x88c ||
+           (letoh16(hdr->signature) >> 4) == 0x92c) {
+               DPRINTF(("FW V%d.%d %02d-%02d %02d:%02d\n",
+                   letoh16(hdr->version), letoh16(hdr->subversion),
+                   hdr->month, hdr->date, hdr->hour, hdr->minute));
+               ptr += sizeof(*hdr);
+               len -= sizeof(*hdr);
+       }
+
+       if (rtwn_read_1(sc, R92C_MCUFWDL) & R92C_MCUFWDL_RAM_DL_SEL)
+               rtwn_fw_reset(sc);
+
+       /* Enable FW download. */
+       rtwn_write_2(sc, R92C_SYS_FUNC_EN,
+           rtwn_read_2(sc, R92C_SYS_FUNC_EN) |
+           R92C_SYS_FUNC_EN_CPUEN);
+       rtwn_write_1(sc, R92C_MCUFWDL,
+           rtwn_read_1(sc, R92C_MCUFWDL) | R92C_MCUFWDL_EN);
+       rtwn_write_1(sc, R92C_MCUFWDL + 2,
+           rtwn_read_1(sc, R92C_MCUFWDL + 2) & ~0x08);
+
+       /* Reset the FWDL checksum. */
+       rtwn_write_1(sc, R92C_MCUFWDL,
+           rtwn_read_1(sc, R92C_MCUFWDL) | R92C_MCUFWDL_CHKSUM_RPT);
+
+       for (page = 0; len > 0; page++) {
+               mlen = MIN(len, R92C_FW_PAGE_SIZE);
+               error = rtwn_fw_loadpage(sc, page, ptr, mlen);
+               if (error != 0) {
+                       printf("%s: could not load firmware page %d\n",
+                           sc->sc_dev.dv_xname, page);
+                       goto fail;
+               }
+               ptr += mlen;
+               len -= mlen;
+       }
+
+       /* Disable FW download. */
+       rtwn_write_1(sc, R92C_MCUFWDL,
+           rtwn_read_1(sc, R92C_MCUFWDL) & ~R92C_MCUFWDL_EN);
+       rtwn_write_1(sc, R92C_MCUFWDL + 1, 0);
+
+       /* Wait for checksum report. */
+       for (ntries = 0; ntries < 1000; ntries++) {
+               if (rtwn_read_4(sc, R92C_MCUFWDL) & R92C_MCUFWDL_CHKSUM_RPT)
+                       break;
+               DELAY(5);
+       }
+       if (ntries == 1000) {
+               printf("%s: timeout waiting for checksum report\n",
+                   sc->sc_dev.dv_xname);
+               error = ETIMEDOUT;
+               goto fail;
+       }
+
+       reg = rtwn_read_4(sc, R92C_MCUFWDL);
+       reg = (reg & ~R92C_MCUFWDL_WINTINI_RDY) | R92C_MCUFWDL_RDY;
+       rtwn_write_4(sc, R92C_MCUFWDL, reg);
+       /* Wait for firmware readiness. */
+       for (ntries = 0; ntries < 1000; ntries++) {
+               if (rtwn_read_4(sc, R92C_MCUFWDL) & R92C_MCUFWDL_WINTINI_RDY)
+                       break;
+               DELAY(5);
+       }
+       if (ntries == 1000) {
+               printf("%s: timeout waiting for firmware readiness\n",
+                   sc->sc_dev.dv_xname);
+               error = ETIMEDOUT;
+               goto fail;
+       }
+ fail:
+       free(fw, M_DEVBUF, 0);
+       return (error);
+}
+
+int
+rtwn_dma_init(struct rtwn_softc *sc)
+{
+       uint32_t reg;
+       int error;
+
+       /* Initialize LLT table. */
+       error = rtwn_llt_init(sc);
+       if (error != 0)
+               return error;
+
+       /* Set number of pages for normal priority queue. */
+       rtwn_write_2(sc, R92C_RQPN_NPQ, 0);
+       rtwn_write_4(sc, R92C_RQPN,
+           /* Set number of pages for public queue. */
+           SM(R92C_RQPN_PUBQ, R92C_PUBQ_NPAGES) |
+           /* Set number of pages for high priority queue. */
+           SM(R92C_RQPN_HPQ, R92C_HPQ_NPAGES) |
+           /* Set number of pages for low priority queue. */
+           SM(R92C_RQPN_LPQ, R92C_LPQ_NPAGES) |
+           /* Load values. */
+           R92C_RQPN_LD);
+
+       rtwn_write_1(sc, R92C_TXPKTBUF_BCNQ_BDNY, R92C_TX_PAGE_BOUNDARY);
+       rtwn_write_1(sc, R92C_TXPKTBUF_MGQ_BDNY, R92C_TX_PAGE_BOUNDARY);
+       rtwn_write_1(sc, R92C_TXPKTBUF_WMAC_LBK_BF_HD, R92C_TX_PAGE_BOUNDARY);
+       rtwn_write_1(sc, R92C_TRXFF_BNDY, R92C_TX_PAGE_BOUNDARY);
+       rtwn_write_1(sc, R92C_TDECTRL + 1, R92C_TX_PAGE_BOUNDARY);
+
+       reg = rtwn_read_2(sc, R92C_TRXDMA_CTRL);
+       reg &= ~R92C_TRXDMA_CTRL_QMAP_M;
+       reg |= 0xF771; 
+       rtwn_write_2(sc, R92C_TRXDMA_CTRL, reg);
+
+       rtwn_write_4(sc, R92C_TCR, R92C_TCR_CFENDFORM | (1 << 12) | (1 << 13));
+
+       /* Configure Tx DMA. */
+       rtwn_write_4(sc, R92C_BKQ_DESA,
+               sc->tx_ring[RTWN_BK_QUEUE].map->dm_segs[0].ds_addr);
+       rtwn_write_4(sc, R92C_BEQ_DESA,
+               sc->tx_ring[RTWN_BE_QUEUE].map->dm_segs[0].ds_addr);
+       rtwn_write_4(sc, R92C_VIQ_DESA,
+               sc->tx_ring[RTWN_VI_QUEUE].map->dm_segs[0].ds_addr);
+       rtwn_write_4(sc, R92C_VOQ_DESA,
+               sc->tx_ring[RTWN_VO_QUEUE].map->dm_segs[0].ds_addr);
+       rtwn_write_4(sc, R92C_BCNQ_DESA,
+               sc->tx_ring[RTWN_BEACON_QUEUE].map->dm_segs[0].ds_addr);
+       rtwn_write_4(sc, R92C_MGQ_DESA,
+               sc->tx_ring[RTWN_MGNT_QUEUE].map->dm_segs[0].ds_addr);
+       rtwn_write_4(sc, R92C_HQ_DESA,
+               sc->tx_ring[RTWN_HIGH_QUEUE].map->dm_segs[0].ds_addr);
+
+       /* Configure Rx DMA. */
+       rtwn_write_4(sc, R92C_RX_DESA, sc->rx_ring.map->dm_segs[0].ds_addr);
+
+       /* Set Tx/Rx transfer page boundary. */
+       rtwn_write_2(sc, R92C_TRXFF_BNDY + 2, 0x27ff);
+
+       /* Set Tx/Rx transfer page size. */
+       rtwn_write_1(sc, R92C_PBP,
+           SM(R92C_PBP_PSRX, R92C_PBP_128) |
+           SM(R92C_PBP_PSTX, R92C_PBP_128));
+       return (0);
+}
+
+void
+rtwn_mac_init(struct rtwn_softc *sc)
+{
+       int i;
+
+       /* Write MAC initialization values. */
+       for (i = 0; i < nitems(rtl8192ce_mac); i++)
+               rtwn_write_1(sc, rtl8192ce_mac[i].reg, rtl8192ce_mac[i].val);
+}
+
+void
+rtwn_bb_init(struct rtwn_softc *sc)
+{
+       const struct rtwn_bb_prog *prog;
+       uint32_t reg;
+       int i;
+
+       /* Enable BB and RF. */
+       rtwn_write_2(sc, R92C_SYS_FUNC_EN,
+           rtwn_read_2(sc, R92C_SYS_FUNC_EN) |
+           R92C_SYS_FUNC_EN_BBRSTB | R92C_SYS_FUNC_EN_BB_GLB_RST |
+           R92C_SYS_FUNC_EN_DIO_RF);
+
+       rtwn_write_2(sc, R92C_AFE_PLL_CTRL, 0xdb83);
+
+       rtwn_write_1(sc, R92C_RF_CTRL,
+           R92C_RF_CTRL_EN | R92C_RF_CTRL_RSTB | R92C_RF_CTRL_SDMRSTB);
+
+       rtwn_write_1(sc, R92C_SYS_FUNC_EN,
+           R92C_SYS_FUNC_EN_DIO_PCIE | R92C_SYS_FUNC_EN_PCIEA |
+           R92C_SYS_FUNC_EN_PPLL | R92C_SYS_FUNC_EN_BB_GLB_RST |
+           R92C_SYS_FUNC_EN_BBRSTB);
+
+       rtwn_write_1(sc, R92C_AFE_XTAL_CTRL + 1, 0x80);
+
+       rtwn_write_4(sc, R92C_LEDCFG0,
+           rtwn_read_4(sc, R92C_LEDCFG0) | 0x00800000);
+
+       /* Select BB programming. */ 
+       prog = (sc->chip & RTWN_CHIP_92C) ?
+           &rtl8192ce_bb_prog_2t : &rtl8192ce_bb_prog_1t;
+
+       /* Write BB initialization values. */
+       for (i = 0; i < prog->count; i++) {
+               rtwn_bb_write(sc, prog->regs[i], prog->vals[i]);
+               DELAY(1);
+       }
+
+       if (sc->chip & RTWN_CHIP_92C_1T2R) {
+               /* 8192C 1T only configuration. */
+               reg = rtwn_bb_read(sc, R92C_FPGA0_TXINFO);
+               reg = (reg & ~0x00000003) | 0x2;
+               rtwn_bb_write(sc, R92C_FPGA0_TXINFO, reg);
+
+               reg = rtwn_bb_read(sc, R92C_FPGA1_TXINFO);
+               reg = (reg & ~0x00300033) | 0x00200022;
+               rtwn_bb_write(sc, R92C_FPGA1_TXINFO, reg);
+
+               reg = rtwn_bb_read(sc, R92C_CCK0_AFESETTING);
+               reg = (reg & ~0xff000000) | 0x45 << 24;
+               rtwn_bb_write(sc, R92C_CCK0_AFESETTING, reg);
+
+               reg = rtwn_bb_read(sc, R92C_OFDM0_TRXPATHENA);
+               reg = (reg & ~0x000000ff) | 0x23;
+               rtwn_bb_write(sc, R92C_OFDM0_TRXPATHENA, reg);
+
+               reg = rtwn_bb_read(sc, R92C_OFDM0_AGCPARAM1);
+               reg = (reg & ~0x00000030) | 1 << 4;
+               rtwn_bb_write(sc, R92C_OFDM0_AGCPARAM1, reg);
+
+               reg = rtwn_bb_read(sc, 0xe74);
+               reg = (reg & ~0x0c000000) | 2 << 26;
+               rtwn_bb_write(sc, 0xe74, reg);
+               reg = rtwn_bb_read(sc, 0xe78);
+               reg = (reg & ~0x0c000000) | 2 << 26;
+               rtwn_bb_write(sc, 0xe78, reg);
+               reg = rtwn_bb_read(sc, 0xe7c);
+               reg = (reg & ~0x0c000000) | 2 << 26;
+               rtwn_bb_write(sc, 0xe7c, reg);
+               reg = rtwn_bb_read(sc, 0xe80);
+               reg = (reg & ~0x0c000000) | 2 << 26;
+               rtwn_bb_write(sc, 0xe80, reg);
+               reg = rtwn_bb_read(sc, 0xe88);
+               reg = (reg & ~0x0c000000) | 2 << 26;
+               rtwn_bb_write(sc, 0xe88, reg);
+       }
+
+       /* Write AGC values. */
+       for (i = 0; i < prog->agccount; i++) {
+               rtwn_bb_write(sc, R92C_OFDM0_AGCRSSITABLE,
+                   prog->agcvals[i]);
+               DELAY(1);
+       }
+
+       if (rtwn_bb_read(sc, R92C_HSSI_PARAM2(0)) &
+           R92C_HSSI_PARAM2_CCK_HIPWR)
+               sc->sc_flags |= RTWN_FLAG_CCK_HIPWR;
+}
+
+void
+rtwn_rf_init(struct rtwn_softc *sc)
+{
+       const struct rtwn_rf_prog *prog;
+       uint32_t reg, type;
+       int i, j, idx, off;
+
+       /* Select RF programming based on board type. */
+       if (!(sc->chip & RTWN_CHIP_92C)) {
+               if (sc->board_type == R92C_BOARD_TYPE_MINICARD)
+                       prog = rtl8188ce_rf_prog;
+               else if (sc->board_type == R92C_BOARD_TYPE_HIGHPA)
+                       prog = rtl8188ru_rf_prog;
+               else
+                       prog = rtl8188cu_rf_prog;
+       } else
+               prog = rtl8192ce_rf_prog;
+
+       for (i = 0; i < sc->nrxchains; i++) {
+               /* Save RF_ENV control type. */
+               idx = i / 2;
+               off = (i % 2) * 16;
+               reg = rtwn_bb_read(sc, R92C_FPGA0_RFIFACESW(idx));
+               type = (reg >> off) & 0x10;
+
+               /* Set RF_ENV enable. */
+               reg = rtwn_bb_read(sc, R92C_FPGA0_RFIFACEOE(i));
+               reg |= 0x100000;
+               rtwn_bb_write(sc, R92C_FPGA0_RFIFACEOE(i), reg);
+               DELAY(1);
+               /* Set RF_ENV output high. */
+               reg = rtwn_bb_read(sc, R92C_FPGA0_RFIFACEOE(i));
+               reg |= 0x10;
+               rtwn_bb_write(sc, R92C_FPGA0_RFIFACEOE(i), reg);
+               DELAY(1);
+               /* Set address and data lengths of RF registers. */
+               reg = rtwn_bb_read(sc, R92C_HSSI_PARAM2(i));
+               reg &= ~R92C_HSSI_PARAM2_ADDR_LENGTH;
+               rtwn_bb_write(sc, R92C_HSSI_PARAM2(i), reg);
+               DELAY(1);
+               reg = rtwn_bb_read(sc, R92C_HSSI_PARAM2(i));
+               reg &= ~R92C_HSSI_PARAM2_DATA_LENGTH;
+               rtwn_bb_write(sc, R92C_HSSI_PARAM2(i), reg);
+               DELAY(1);
+
+               /* Write RF initialization values for this chain. */
+               for (j = 0; j < prog[i].count; j++) {
+                       if (prog[i].regs[j] >= 0xf9 &&
+                           prog[i].regs[j] <= 0xfe) {
+                               /*
+                                * These are fake RF registers offsets that
+                                * indicate a delay is required.
+                                */
+                               DELAY(50);
+                               continue;
+                       }
+                       rtwn_rf_write(sc, i, prog[i].regs[j],
+                           prog[i].vals[j]);
+                       DELAY(1);
+               }
+
+               /* Restore RF_ENV control type. */
+               reg = rtwn_bb_read(sc, R92C_FPGA0_RFIFACESW(idx));
+               reg &= ~(0x10 << off) | (type << off);
+               rtwn_bb_write(sc, R92C_FPGA0_RFIFACESW(idx), reg);
+
+               /* Cache RF register CHNLBW. */
+               sc->rf_chnlbw[i] = rtwn_rf_read(sc, i, R92C_RF_CHNLBW);
+       }
+
+       if ((sc->chip & (RTWN_CHIP_UMC_A_CUT | RTWN_CHIP_92C)) ==
+           RTWN_CHIP_UMC_A_CUT) {
+               rtwn_rf_write(sc, 0, R92C_RF_RX_G1, 0x30255);
+               rtwn_rf_write(sc, 0, R92C_RF_RX_G2, 0x50a00);
+       }
+}
+
+void
+rtwn_cam_init(struct rtwn_softc *sc)
+{
+       /* Invalidate all CAM entries. */
+       rtwn_write_4(sc, R92C_CAMCMD,
+           R92C_CAMCMD_POLLING | R92C_CAMCMD_CLR);
+}
+
+void
+rtwn_pa_bias_init(struct rtwn_softc *sc)
+{
+       uint8_t reg;
+       int i;
+
+       for (i = 0; i < sc->nrxchains; i++) {
+               if (sc->pa_setting & (1 << i))
+                       continue;
+               rtwn_rf_write(sc, i, R92C_RF_IPA, 0x0f406);
+               rtwn_rf_write(sc, i, R92C_RF_IPA, 0x4f406);
+               rtwn_rf_write(sc, i, R92C_RF_IPA, 0x8f406);
+               rtwn_rf_write(sc, i, R92C_RF_IPA, 0xcf406);
+       }
+       if (!(sc->pa_setting & 0x10)) {
+               reg = rtwn_read_1(sc, 0x16);
+               reg = (reg & ~0xf0) | 0x90;
+               rtwn_write_1(sc, 0x16, reg);
+       }
+}
+
+void
+rtwn_rxfilter_init(struct rtwn_softc *sc)
+{
+       /* Initialize Rx filter. */
+       /* TODO: use better filter for monitor mode. */
+       rtwn_write_4(sc, R92C_RCR,
+           R92C_RCR_AAP | R92C_RCR_APM | R92C_RCR_AM | R92C_RCR_AB |
+           R92C_RCR_APP_ICV | R92C_RCR_AMF | R92C_RCR_HTC_LOC_CTRL |
+           R92C_RCR_APP_MIC | R92C_RCR_APP_PHYSTS);
+       /* Accept all multicast frames. */
+       rtwn_write_4(sc, R92C_MAR + 0, 0xffffffff);
+       rtwn_write_4(sc, R92C_MAR + 4, 0xffffffff);
+       /* Accept all management frames. */
+       rtwn_write_2(sc, R92C_RXFLTMAP0, 0xffff);
+       /* Reject all control frames. */
+       rtwn_write_2(sc, R92C_RXFLTMAP1, 0x0000);
+       /* Accept all data frames. */
+       rtwn_write_2(sc, R92C_RXFLTMAP2, 0xffff);
+}
+
+void
+rtwn_edca_init(struct rtwn_softc *sc)
+{
+       rtwn_write_2(sc, R92C_SPEC_SIFS, 0x1010);
+       rtwn_write_2(sc, R92C_MAC_SPEC_SIFS, 0x1010);
+       rtwn_write_2(sc, R92C_SIFS_CCK, 0x1010);
+       rtwn_write_2(sc, R92C_SIFS_OFDM, 0x0e0e);
+       rtwn_write_4(sc, R92C_EDCA_BE_PARAM, 0x005ea42b);
+       rtwn_write_4(sc, R92C_EDCA_BK_PARAM, 0x0000a44f);
+       rtwn_write_4(sc, R92C_EDCA_VI_PARAM, 0x005e4322);
+       rtwn_write_4(sc, R92C_EDCA_VO_PARAM, 0x002f3222);
+}
+
+void
+rtwn_write_txpower(struct rtwn_softc *sc, int chain,
+    uint16_t power[RTWN_RIDX_COUNT])
+{
+       uint32_t reg;
+
+       /* Write per-CCK rate Tx power. */
+       if (chain == 0) {
+               reg = rtwn_bb_read(sc, R92C_TXAGC_A_CCK1_MCS32);
+               reg = RW(reg, R92C_TXAGC_A_CCK1,  power[0]);
+               rtwn_bb_write(sc, R92C_TXAGC_A_CCK1_MCS32, reg);
+               reg = rtwn_bb_read(sc, R92C_TXAGC_B_CCK11_A_CCK2_11);
+               reg = RW(reg, R92C_TXAGC_A_CCK2,  power[1]);
+               reg = RW(reg, R92C_TXAGC_A_CCK55, power[2]);
+               reg = RW(reg, R92C_TXAGC_A_CCK11, power[3]);
+               rtwn_bb_write(sc, R92C_TXAGC_B_CCK11_A_CCK2_11, reg);
+       } else {
+               reg = rtwn_bb_read(sc, R92C_TXAGC_B_CCK1_55_MCS32);
+               reg = RW(reg, R92C_TXAGC_B_CCK1,  power[0]);
+               reg = RW(reg, R92C_TXAGC_B_CCK2,  power[1]);
+               reg = RW(reg, R92C_TXAGC_B_CCK55, power[2]);
+               rtwn_bb_write(sc, R92C_TXAGC_B_CCK1_55_MCS32, reg);
+               reg = rtwn_bb_read(sc, R92C_TXAGC_B_CCK11_A_CCK2_11);
+               reg = RW(reg, R92C_TXAGC_B_CCK11, power[3]);
+               rtwn_bb_write(sc, R92C_TXAGC_B_CCK11_A_CCK2_11, reg);
+       }
+       /* Write per-OFDM rate Tx power. */
+       rtwn_bb_write(sc, R92C_TXAGC_RATE18_06(chain),
+           SM(R92C_TXAGC_RATE06, power[ 4]) |
+           SM(R92C_TXAGC_RATE09, power[ 5]) |
+           SM(R92C_TXAGC_RATE12, power[ 6]) |
+           SM(R92C_TXAGC_RATE18, power[ 7]));
+       rtwn_bb_write(sc, R92C_TXAGC_RATE54_24(chain),
+           SM(R92C_TXAGC_RATE24, power[ 8]) |
+           SM(R92C_TXAGC_RATE36, power[ 9]) |
+           SM(R92C_TXAGC_RATE48, power[10]) |
+           SM(R92C_TXAGC_RATE54, power[11]));
+       /* Write per-MCS Tx power. */
+       rtwn_bb_write(sc, R92C_TXAGC_MCS03_MCS00(chain),
+           SM(R92C_TXAGC_MCS00,  power[12]) |
+           SM(R92C_TXAGC_MCS01,  power[13]) |
+           SM(R92C_TXAGC_MCS02,  power[14]) |
+           SM(R92C_TXAGC_MCS03,  power[15]));
+       rtwn_bb_write(sc, R92C_TXAGC_MCS07_MCS04(chain),
+           SM(R92C_TXAGC_MCS04,  power[16]) |
+           SM(R92C_TXAGC_MCS05,  power[17]) |
+           SM(R92C_TXAGC_MCS06,  power[18]) |
+           SM(R92C_TXAGC_MCS07,  power[19]));
+       rtwn_bb_write(sc, R92C_TXAGC_MCS11_MCS08(chain),
+           SM(R92C_TXAGC_MCS08,  power[20]) |
+           SM(R92C_TXAGC_MCS09,  power[21]) |
+           SM(R92C_TXAGC_MCS10,  power[22]) |
+           SM(R92C_TXAGC_MCS11,  power[23]));
+       rtwn_bb_write(sc, R92C_TXAGC_MCS15_MCS12(chain),
+           SM(R92C_TXAGC_MCS12,  power[24]) |
+           SM(R92C_TXAGC_MCS13,  power[25]) |
+           SM(R92C_TXAGC_MCS14,  power[26]) |
+           SM(R92C_TXAGC_MCS15,  power[27]));
+}
+
+void
+rtwn_get_txpower(struct rtwn_softc *sc, int chain,
+    struct ieee80211_channel *c, struct ieee80211_channel *extc,
+    uint16_t power[RTWN_RIDX_COUNT])
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct r92c_rom *rom = &sc->rom;
+       uint16_t cckpow, ofdmpow, htpow, diff, max;
+       const struct rtwn_txpwr *base;
+       int ridx, chan, group;
+
+       /* Determine channel group. */
+       chan = ieee80211_chan2ieee(ic, c);      /* XXX center freq! */
+       if (chan <= 3)
+               group = 0;
+       else if (chan <= 9)
+               group = 1;
+       else
+               group = 2;
+
+       /* Get original Tx power based on board type and RF chain. */
+       if (!(sc->chip & RTWN_CHIP_92C)) {
+               if (sc->board_type == R92C_BOARD_TYPE_HIGHPA)
+                       base = &rtl8188ru_txagc[chain];
+               else
+                       base = &rtl8192cu_txagc[chain];
+       } else
+               base = &rtl8192cu_txagc[chain];
+
+       memset(power, 0, RTWN_RIDX_COUNT * sizeof(power[0]));
+       if (sc->regulatory == 0) {
+               for (ridx = 0; ridx <= 3; ridx++)
+                       power[ridx] = base->pwr[0][ridx];
+       }
+       for (ridx = 4; ridx < RTWN_RIDX_COUNT; ridx++) {
+               if (sc->regulatory == 3) {
+                       power[ridx] = base->pwr[0][ridx];
+                       /* Apply vendor limits. */
+                       if (extc != NULL)
+                               max = rom->ht40_max_pwr[group];
+                       else
+                               max = rom->ht20_max_pwr[group];
+                       max = (max >> (chain * 4)) & 0xf;
+                       if (power[ridx] > max)
+                               power[ridx] = max;
+               } else if (sc->regulatory == 1) {
+                       if (extc == NULL)
+                               power[ridx] = base->pwr[group][ridx];
+               } else if (sc->regulatory != 2)
+                       power[ridx] = base->pwr[0][ridx];
+       }
+
+       /* Compute per-CCK rate Tx power. */
+       cckpow = rom->cck_tx_pwr[chain][group];
+       for (ridx = 0; ridx <= 3; ridx++) {
+               power[ridx] += cckpow;
+               if (power[ridx] > R92C_MAX_TX_PWR)
+                       power[ridx] = R92C_MAX_TX_PWR;
+       }
+
+       htpow = rom->ht40_1s_tx_pwr[chain][group];
+       if (sc->ntxchains > 1) {
+               /* Apply reduction for 2 spatial streams. */
+               diff = rom->ht40_2s_tx_pwr_diff[group];
+               diff = (diff >> (chain * 4)) & 0xf;
+               htpow = (htpow > diff) ? htpow - diff : 0;
+       }
+
+       /* Compute per-OFDM rate Tx power. */
+       diff = rom->ofdm_tx_pwr_diff[group];
+       diff = (diff >> (chain * 4)) & 0xf;
+       ofdmpow = htpow + diff; /* HT->OFDM correction. */
+       for (ridx = 4; ridx <= 11; ridx++) {
+               power[ridx] += ofdmpow;
+               if (power[ridx] > R92C_MAX_TX_PWR)
+                       power[ridx] = R92C_MAX_TX_PWR;
+       }
+
+       /* Compute per-MCS Tx power. */
+       if (extc == NULL) {
+               diff = rom->ht20_tx_pwr_diff[group];
+               diff = (diff >> (chain * 4)) & 0xf;
+               htpow += diff;  /* HT40->HT20 correction. */
+       }
+       for (ridx = 12; ridx <= 27; ridx++) {
+               power[ridx] += htpow;
+               if (power[ridx] > R92C_MAX_TX_PWR)
+                       power[ridx] = R92C_MAX_TX_PWR;
+       }
+#ifdef RTWN_DEBUG
+       if (rtwn_debug >= 4) {
+               /* Dump per-rate Tx power values. */
+               printf("Tx power for chain %d:\n", chain);
+               for (ridx = 0; ridx < RTWN_RIDX_COUNT; ridx++)
+                       printf("Rate %d = %u\n", ridx, power[ridx]);
+       }
+#endif
+}
+
+void
+rtwn_set_txpower(struct rtwn_softc *sc, struct ieee80211_channel *c,
+    struct ieee80211_channel *extc)
+{
+       uint16_t power[RTWN_RIDX_COUNT];
+       int i;
+
+       for (i = 0; i < sc->ntxchains; i++) {
+               /* Compute per-rate Tx power values. */
+               rtwn_get_txpower(sc, i, c, extc, power);
+               /* Write per-rate Tx power values to hardware. */
+               rtwn_write_txpower(sc, i, power);
+       }
+}
+
+void
+rtwn_set_chan(struct rtwn_softc *sc, struct ieee80211_channel *c,
+    struct ieee80211_channel *extc)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       u_int chan;
+       int i;
+
+       chan = ieee80211_chan2ieee(ic, c);      /* XXX center freq! */
+
+       /* Set Tx power for this new channel. */
+       rtwn_set_txpower(sc, c, extc);
+
+       for (i = 0; i < sc->nrxchains; i++) {
+               rtwn_rf_write(sc, i, R92C_RF_CHNLBW,
+                   RW(sc->rf_chnlbw[i], R92C_RF_CHNLBW_CHNL, chan));
+       }
+#ifndef IEEE80211_NO_HT
+       if (extc != NULL) {
+               uint32_t reg;
+
+               /* Is secondary channel below or above primary? */
+               int prichlo = c->ic_freq < extc->ic_freq;
+
+               rtwn_write_1(sc, R92C_BWOPMODE,
+                   rtwn_read_1(sc, R92C_BWOPMODE) & ~R92C_BWOPMODE_20MHZ);
+
+               reg = rtwn_read_1(sc, R92C_RRSR + 2);
+               reg = (reg & ~0x6f) | (prichlo ? 1 : 2) << 5;
+               rtwn_write_1(sc, R92C_RRSR + 2, reg);
+
+               rtwn_bb_write(sc, R92C_FPGA0_RFMOD,
+                   rtwn_bb_read(sc, R92C_FPGA0_RFMOD) | R92C_RFMOD_40MHZ);
+               rtwn_bb_write(sc, R92C_FPGA1_RFMOD,
+                   rtwn_bb_read(sc, R92C_FPGA1_RFMOD) | R92C_RFMOD_40MHZ);
+
+               /* Set CCK side band. */
+               reg = rtwn_bb_read(sc, R92C_CCK0_SYSTEM);
+               reg = (reg & ~0x00000010) | (prichlo ? 0 : 1) << 4;
+               rtwn_bb_write(sc, R92C_CCK0_SYSTEM, reg);
+
+               reg = rtwn_bb_read(sc, R92C_OFDM1_LSTF);
+               reg = (reg & ~0x00000c00) | (prichlo ? 1 : 2) << 10;
+               rtwn_bb_write(sc, R92C_OFDM1_LSTF, reg);
+
+               rtwn_bb_write(sc, R92C_FPGA0_ANAPARAM2,
+                   rtwn_bb_read(sc, R92C_FPGA0_ANAPARAM2) &
+                   ~R92C_FPGA0_ANAPARAM2_CBW20);
+
+               reg = rtwn_bb_read(sc, 0x818);
+               reg = (reg & ~0x0c000000) | (prichlo ? 2 : 1) << 26;
+               rtwn_bb_write(sc, 0x818, reg);
+
+               /* Select 40MHz bandwidth. */
+               rtwn_rf_write(sc, 0, R92C_RF_CHNLBW,
+                   (sc->rf_chnlbw[0] & ~0xfff) | chan);
+       } else
+#endif
+       {
+               rtwn_write_1(sc, R92C_BWOPMODE,
+                   rtwn_read_1(sc, R92C_BWOPMODE) | R92C_BWOPMODE_20MHZ);
+
+               rtwn_bb_write(sc, R92C_FPGA0_RFMOD,
+                   rtwn_bb_read(sc, R92C_FPGA0_RFMOD) & ~R92C_RFMOD_40MHZ);
+               rtwn_bb_write(sc, R92C_FPGA1_RFMOD,
+                   rtwn_bb_read(sc, R92C_FPGA1_RFMOD) & ~R92C_RFMOD_40MHZ);
+
+               rtwn_bb_write(sc, R92C_FPGA0_ANAPARAM2,
+                   rtwn_bb_read(sc, R92C_FPGA0_ANAPARAM2) |
+                   R92C_FPGA0_ANAPARAM2_CBW20);
+
+               /* Select 20MHz bandwidth. */
+               rtwn_rf_write(sc, 0, R92C_RF_CHNLBW,
+                   (sc->rf_chnlbw[0] & ~0xfff) | R92C_RF_CHNLBW_BW20 | chan);
+       }
+}
+
+int
+rtwn_iq_calib_chain(struct rtwn_softc *sc, int chain, uint16_t tx[2],
+    uint16_t rx[2])
+{
+       uint32_t status;
+       int offset = chain * 0x20;
+
+       if (chain == 0) {       /* IQ calibration for chain 0. */
+               /* IQ calibration settings for chain 0. */
+               rtwn_bb_write(sc, 0xe30, 0x10008c1f);
+               rtwn_bb_write(sc, 0xe34, 0x10008c1f);
+               rtwn_bb_write(sc, 0xe38, 0x82140102);
+
+               if (sc->ntxchains > 1) {
+                       rtwn_bb_write(sc, 0xe3c, 0x28160202);   /* 2T */
+                       /* IQ calibration settings for chain 1. */
+                       rtwn_bb_write(sc, 0xe50, 0x10008c22);
+                       rtwn_bb_write(sc, 0xe54, 0x10008c22);
+                       rtwn_bb_write(sc, 0xe58, 0x82140102);
+                       rtwn_bb_write(sc, 0xe5c, 0x28160202);
+               } else
+                       rtwn_bb_write(sc, 0xe3c, 0x28160502);   /* 1T */
+
+               /* LO calibration settings. */
+               rtwn_bb_write(sc, 0xe4c, 0x001028d1);
+               /* We're doing LO and IQ calibration in one shot. */
+               rtwn_bb_write(sc, 0xe48, 0xf9000000);
+               rtwn_bb_write(sc, 0xe48, 0xf8000000);
+
+       } else {                /* IQ calibration for chain 1. */
+               /* We're doing LO and IQ calibration in one shot. */
+               rtwn_bb_write(sc, 0xe60, 0x00000002);
+               rtwn_bb_write(sc, 0xe60, 0x00000000);
+       }
+
+       /* Give LO and IQ calibrations the time to complete. */
+       DELAY(1);
+
+       /* Read IQ calibration status. */
+       status = rtwn_bb_read(sc, 0xeac);
+
+       if (status & (1 << (28 + chain * 3)))
+               return (0);     /* Tx failed. */
+       /* Read Tx IQ calibration results. */
+       tx[0] = (rtwn_bb_read(sc, 0xe94 + offset) >> 16) & 0x3ff;
+       tx[1] = (rtwn_bb_read(sc, 0xe9c + offset) >> 16) & 0x3ff;
+       if (tx[0] == 0x142 || tx[1] == 0x042)
+               return (0);     /* Tx failed. */
+
+       if (status & (1 << (27 + chain * 3)))
+               return (1);     /* Rx failed. */
+       /* Read Rx IQ calibration results. */
+       rx[0] = (rtwn_bb_read(sc, 0xea4 + offset) >> 16) & 0x3ff;
+       rx[1] = (rtwn_bb_read(sc, 0xeac + offset) >> 16) & 0x3ff;
+       if (rx[0] == 0x132 || rx[1] == 0x036)
+               return (1);     /* Rx failed. */
+
+       return (3);     /* Both Tx and Rx succeeded. */
+}
+
+void
+rtwn_iq_calib(struct rtwn_softc *sc)
+{
+       /* TODO */
+}
+
+void
+rtwn_lc_calib(struct rtwn_softc *sc)
+{
+       uint32_t rf_ac[2];
+       uint8_t txmode;
+       int i;
+
+       txmode = rtwn_read_1(sc, R92C_OFDM1_LSTF + 3);
+       if ((txmode & 0x70) != 0) {
+               /* Disable all continuous Tx. */
+               rtwn_write_1(sc, R92C_OFDM1_LSTF + 3, txmode & ~0x70);
+
+               /* Set RF mode to standby mode. */
+               for (i = 0; i < sc->nrxchains; i++) {
+                       rf_ac[i] = rtwn_rf_read(sc, i, R92C_RF_AC);
+                       rtwn_rf_write(sc, i, R92C_RF_AC,
+                           RW(rf_ac[i], R92C_RF_AC_MODE,
+                               R92C_RF_AC_MODE_STANDBY));
+               }
+       } else {
+               /* Block all Tx queues. */
+               rtwn_write_1(sc, R92C_TXPAUSE, 0xff);
+       }
+       /* Start calibration. */
+       rtwn_rf_write(sc, 0, R92C_RF_CHNLBW,
+           rtwn_rf_read(sc, 0, R92C_RF_CHNLBW) | R92C_RF_CHNLBW_LCSTART);
+
+       /* Give calibration the time to complete. */
+       DELAY(100);
+
+       /* Restore configuration. */
+       if ((txmode & 0x70) != 0) {
+               /* Restore Tx mode. */
+               rtwn_write_1(sc, R92C_OFDM1_LSTF + 3, txmode);
+               /* Restore RF mode. */
+               for (i = 0; i < sc->nrxchains; i++)
+                       rtwn_rf_write(sc, i, R92C_RF_AC, rf_ac[i]);
+       } else {
+               /* Unblock all Tx queues. */
+               rtwn_write_1(sc, R92C_TXPAUSE, 0x00);
+       }
+}
+
+void
+rtwn_temp_calib(struct rtwn_softc *sc)
+{
+       int temp;
+
+       if (sc->thcal_state == 0) {
+               /* Start measuring temperature. */
+               rtwn_rf_write(sc, 0, R92C_RF_T_METER, 0x60);
+               sc->thcal_state = 1;
+               return;
+       }
+       sc->thcal_state = 0;
+
+       /* Read measured temperature. */
+       temp = rtwn_rf_read(sc, 0, R92C_RF_T_METER) & 0x1f;
+       if (temp == 0)  /* Read failed, skip. */
+               return;
+       DPRINTFN(2, ("temperature=%d\n", temp));
+
+       /*
+        * Redo LC calibration if temperature changed significantly since
+        * last calibration.
+        */
+       if (sc->thcal_lctemp == 0) {
+               /* First LC calibration is performed in rtwn_init(). */
+               sc->thcal_lctemp = temp;
+       } else if (abs(temp - sc->thcal_lctemp) > 1) {
+               DPRINTF(("LC calib triggered by temp: %d -> %d\n",
+                   sc->thcal_lctemp, temp));
+               rtwn_lc_calib(sc);
+               /* Record temperature of last LC calibration. */
+               sc->thcal_lctemp = temp;
+       }
+}
+
+int
+rtwn_init(struct ifnet *ifp)
+{
+       struct rtwn_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       uint32_t reg;
+       int i, error;
+
+       /* Init firmware commands ring. */
+       sc->fwcur = 0;
+
+       /* Power on adapter. */
+       error = rtwn_power_on(sc);
+       if (error != 0) {
+               printf("%s: could not power on adapter\n",
+                   sc->sc_dev.dv_xname);
+               goto fail;
+       }
+
+       /* Initialize DMA. */
+       error = rtwn_dma_init(sc);
+       if (error != 0) {
+               printf("%s: could not initialize DMA\n",
+                   sc->sc_dev.dv_xname);
+               goto fail;
+       }
+
+       /* Set info size in Rx descriptors (in 64-bit words). */
+       rtwn_write_1(sc, R92C_RX_DRVINFO_SZ, 4);
+
+       /* Disable interrupts. */
+       rtwn_write_4(sc, R92C_HISR, 0x00000000);
+       rtwn_write_4(sc, R92C_HIMR, 0x00000000);
+
+       /* Set MAC address. */
+       IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl));
+       for (i = 0; i < IEEE80211_ADDR_LEN; i++)
+               rtwn_write_1(sc, R92C_MACID + i, ic->ic_myaddr[i]);
+
+       /* Set initial network type. */
+       reg = rtwn_read_4(sc, R92C_CR);
+       reg = RW(reg, R92C_CR_NETTYPE, R92C_CR_NETTYPE_INFRA);
+       rtwn_write_4(sc, R92C_CR, reg);
+
+       rtwn_rxfilter_init(sc);
+
+       reg = rtwn_read_4(sc, R92C_RRSR);
+       reg = RW(reg, R92C_RRSR_RATE_BITMAP, R92C_RRSR_RATE_ALL);
+       rtwn_write_4(sc, R92C_RRSR, reg);
+
+       /* Set short/long retry limits. */
+       rtwn_write_2(sc, R92C_RL,
+           SM(R92C_RL_SRL, 0x07) | SM(R92C_RL_LRL, 0x07));
+
+       /* Initialize EDCA parameters. */
+       rtwn_edca_init(sc);
+
+       /* Set data and response automatic rate fallback retry counts. */
+       rtwn_write_4(sc, R92C_DARFRC + 0, 0x01000000);
+       rtwn_write_4(sc, R92C_DARFRC + 4, 0x07060504);
+       rtwn_write_4(sc, R92C_RARFRC + 0, 0x01000000);
+       rtwn_write_4(sc, R92C_RARFRC + 4, 0x07060504);
+
+       rtwn_write_2(sc, R92C_FWHW_TXQ_CTRL, 0x1f80);
+
+       /* Set ACK timeout. */
+       rtwn_write_1(sc, R92C_ACKTO, 0x40);
+
+       /* Initialize beacon parameters. */
+       rtwn_write_2(sc, R92C_TBTT_PROHIBIT, 0x6404);
+       rtwn_write_1(sc, R92C_DRVERLYINT, 0x05);
+       rtwn_write_1(sc, R92C_BCNDMATIM, 0x02);
+       rtwn_write_2(sc, R92C_BCNTCFG, 0x660f);
+
+       /* Setup AMPDU aggregation. */
+       rtwn_write_4(sc, R92C_AGGLEN_LMT, 0x99997631);  /* MCS7~0 */
+       rtwn_write_1(sc, R92C_AGGR_BREAK_TIME, 0x16);
+
+       rtwn_write_1(sc, R92C_BCN_MAX_ERR, 0xff);
+       rtwn_write_1(sc, R92C_BCN_CTRL, R92C_BCN_CTRL_DIS_TSF_UDT0);
+
+       rtwn_write_4(sc, R92C_PIFS, 0x1c);
+       rtwn_write_4(sc, R92C_MCUTST_1, 0x0);
+
+       /* Load 8051 microcode. */
+       error = rtwn_load_firmware(sc);
+       if (error != 0)
+               goto fail;
+
+       /* Initialize MAC/BB/RF blocks. */
+       rtwn_mac_init(sc);
+       rtwn_bb_init(sc);
+       rtwn_rf_init(sc);
+
+       /* Turn CCK and OFDM blocks on. */
+       reg = rtwn_bb_read(sc, R92C_FPGA0_RFMOD);
+       reg |= R92C_RFMOD_CCK_EN;
+       rtwn_bb_write(sc, R92C_FPGA0_RFMOD, reg);
+       reg = rtwn_bb_read(sc, R92C_FPGA0_RFMOD);
+       reg |= R92C_RFMOD_OFDM_EN;
+       rtwn_bb_write(sc, R92C_FPGA0_RFMOD, reg);
+
+       /* Clear per-station keys table. */
+       rtwn_cam_init(sc);
+
+       /* Enable hardware sequence numbering. */
+       rtwn_write_1(sc, R92C_HWSEQ_CTRL, 0xff);
+
+       /* Perform LO and IQ calibrations. */
+       rtwn_iq_calib(sc);
+       /* Perform LC calibration. */
+       rtwn_lc_calib(sc);
+
+       rtwn_pa_bias_init(sc);
+
+       /* Initialize GPIO setting. */
+       rtwn_write_1(sc, R92C_GPIO_MUXCFG,
+           rtwn_read_1(sc, R92C_GPIO_MUXCFG) & ~R92C_GPIO_MUXCFG_ENBT);
+
+       /* Fix for lower temperature. */
+       rtwn_write_1(sc, 0x15, 0xe9);
+
+       /* Set default channel. */
+       ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+       rtwn_set_chan(sc, ic->ic_ibss_chan, NULL);
+
+       /* CLear pending interrupts. */
+       rtwn_write_4(sc, R92C_HISR, 0xffffffff);
+
+       /* Enable interrupts. */
+       rtwn_write_4(sc, R92C_HIMR, RTWN_INT_ENABLE);
+
+       /* We're ready to go. */
+       ifp->if_flags &= ~IFF_OACTIVE;
+       ifp->if_flags |= IFF_RUNNING;
+
+#ifdef notyet
+       if (ic->ic_flags & IEEE80211_F_WEPON) {
+               /* Install WEP keys. */
+               for (i = 0; i < IEEE80211_WEP_NKID; i++)
+                       rtwn_set_key(ic, NULL, &ic->ic_nw_keys[i]);
+       }
+#endif
+       if (ic->ic_opmode == IEEE80211_M_MONITOR)
+               ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+       else
+               ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+       return (0);
+ fail:
+       rtwn_stop(ifp);
+       return (error);
+}
+
+void
+rtwn_init_task(void *arg1)
+{
+       struct rtwn_softc *sc = arg1;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
+       int s;
+
+       s = splnet();
+       while (sc->sc_flags & RTWN_FLAG_BUSY)
+               tsleep(&sc->sc_flags, 0, "rtwnpwr", 0);
+       sc->sc_flags |= RTWN_FLAG_BUSY;
+
+       rtwn_stop(ifp);
+
+       if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == IFF_UP)
+               rtwn_init(ifp);
+
+       sc->sc_flags &= ~RTWN_FLAG_BUSY;
+       wakeup(&sc->sc_flags);
+       splx(s);
+}
+
+void
+rtwn_stop(struct ifnet *ifp)
+{
+       struct rtwn_softc *sc = ifp->if_softc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       int s, i;
+       u_int16_t reg;
+
+       sc->sc_tx_timer = 0;
+       ifp->if_timer = 0;
+       ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+
+       s = splnet();
+       ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+
+       timeout_del(&sc->scan_to);
+       timeout_del(&sc->calib_to);
+
+       task_del(systq, &sc->init_task);
+
+       /* Disable interrupts. */
+       rtwn_write_4(sc, R92C_HISR, 0x00000000);
+       rtwn_write_4(sc, R92C_HIMR, 0x00000000);
+
+       /* Stop hardware. */
+       rtwn_write_1(sc, R92C_TXPAUSE, 0xff);
+       rtwn_write_1(sc, R92C_RF_CTRL, 0x00);
+       reg = rtwn_read_1(sc, R92C_SYS_FUNC_EN);
+       reg |= R92C_SYS_FUNC_EN_BB_GLB_RST;
+       rtwn_write_1(sc, R92C_SYS_FUNC_EN, reg);
+       reg &= ~R92C_SYS_FUNC_EN_BB_GLB_RST;
+       rtwn_write_1(sc, R92C_SYS_FUNC_EN, reg);
+       reg = rtwn_read_2(sc, R92C_CR);
+       reg &= ~(R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN |
+           R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN |
+           R92C_CR_SCHEDULE_EN | R92C_CR_MACTXEN | R92C_CR_MACRXEN |
+           R92C_CR_ENSEC);
+       rtwn_write_2(sc, R92C_CR, reg);
+       if (rtwn_read_1(sc, R92C_MCUFWDL) & R92C_MCUFWDL_RAM_DL_SEL)
+               rtwn_fw_reset(sc);
+       /* TODO: linux does additional btcoex stuff here */
+       rtwn_write_2(sc, R92C_AFE_PLL_CTRL, 0x80); /* linux magic number */
+       rtwn_write_1(sc, R92C_SPS0_CTRL, 0x23); /* ditto */
+       rtwn_write_1(sc, R92C_AFE_XTAL_CTRL, 0x0e); /* different with btcoex */
+       rtwn_write_1(sc, R92C_RSV_CTRL, 0x0e);
+       rtwn_write_1(sc, R92C_APS_FSMCO, R92C_APS_FSMCO_PDN_EN);
+
+       for (i = 0; i < RTWN_NTXQUEUES; i++)
+               rtwn_reset_tx_list(sc, i);
+       rtwn_reset_rx_list(sc);
+
+       splx(s);
+}
+
+int
+rtwn_intr(void *xsc)
+{
+       struct rtwn_softc *sc = xsc;
+       u_int32_t status;
+       int i;
+
+       status = rtwn_read_4(sc, R92C_HISR);
+       if (status == 0 || status == 0xffffffff)
+               return (0);
+
+       /* Disable interrupts. */
+       rtwn_write_4(sc, R92C_HIMR, 0x00000000);
+
+       /* Ack interrupts. */
+       rtwn_write_4(sc, R92C_HISR, status);
+
+       /* Vendor driver treats RX errors like ROK... */
+       if (status & (R92C_IMR_ROK | R92C_IMR_RXFOVW | R92C_IMR_RDU)) {
+               bus_dmamap_sync(sc->sc_dmat, sc->rx_ring.map, 0,
+                   sizeof(struct r92c_rx_desc) * RTWN_RX_LIST_COUNT,
+                   BUS_DMASYNC_POSTREAD);
+
+               for (i = 0; i < RTWN_RX_LIST_COUNT; i++) {
+                       struct r92c_rx_desc *rx_desc = &sc->rx_ring.desc[i];
+                       struct rtwn_rx_data *rx_data = &sc->rx_ring.rx_data[i];
+
+                       if (letoh32(rx_desc->rxdw0) & R92C_RXDW0_OWN)
+                               continue;
+
+                       rtwn_rx_frame(sc, rx_desc, rx_data, i);
+               }
+       }
+
+       if (status & R92C_IMR_BDOK)
+               rtwn_tx_done(sc, RTWN_BEACON_QUEUE);
+       if (status & R92C_IMR_HIGHDOK)
+               rtwn_tx_done(sc, RTWN_HIGH_QUEUE);
+       if (status & R92C_IMR_MGNTDOK)
+               rtwn_tx_done(sc, RTWN_MGNT_QUEUE);
+       if (status & R92C_IMR_BKDOK)
+               rtwn_tx_done(sc, RTWN_BK_QUEUE);
+       if (status & R92C_IMR_BEDOK)
+               rtwn_tx_done(sc, RTWN_BE_QUEUE);
+       if (status & R92C_IMR_VIDOK)
+               rtwn_tx_done(sc, RTWN_VI_QUEUE);
+       if (status & R92C_IMR_VODOK)
+               rtwn_tx_done(sc, RTWN_VO_QUEUE);
+
+       /* Enable interrupts. */
+       rtwn_write_4(sc, R92C_HIMR, RTWN_INT_ENABLE);
+
+       return (1);
+}
diff --git a/sys/dev/pci/if_rtwnreg.h b/sys/dev/pci/if_rtwnreg.h
new file mode 100644 (file)
index 0000000..62be0d8
--- /dev/null
@@ -0,0 +1,2088 @@
+/*     $OpenBSD: if_rtwnreg.h,v 1.1 2015/06/04 21:08:40 stsp Exp $     */
+
+/*-
+ * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2015 Stefan Sperling <stsp@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define R92C_MAX_CHAINS        2
+
+/* Maximum number of output pipes is 3. */
+#define R92C_MAX_EPOUT 3
+
+#define R92C_MAX_TX_PWR        0x3f
+
+#define R92C_PUBQ_NPAGES       176
+#define R92C_HPQ_NPAGES                41
+#define R92C_LPQ_NPAGES                28
+#define R92C_TXPKTBUF_COUNT    256
+#define R92C_TX_PAGE_COUNT     \
+       (R92C_PUBQ_NPAGES + R92C_HPQ_NPAGES + R92C_LPQ_NPAGES)
+#define R92C_TX_PAGE_BOUNDARY  (R92C_TX_PAGE_COUNT + 1)
+
+#define R92C_H2C_NBOX  4
+
+/* USB Requests. */
+#define R92C_REQ_REGS  0x05
+
+/*
+ * MAC registers.
+ */
+/* System Configuration. */
+#define R92C_SYS_ISO_CTRL              0x000
+#define R92C_SYS_FUNC_EN               0x002
+#define R92C_APS_FSMCO                 0x004
+#define R92C_SYS_CLKR                  0x008
+#define R92C_AFE_MISC                  0x010
+#define R92C_SPS0_CTRL                 0x011
+#define R92C_SPS_OCP_CFG               0x018
+#define R92C_RSV_CTRL                  0x01c
+#define R92C_RF_CTRL                   0x01f
+#define R92C_LDOA15_CTRL               0x020
+#define R92C_LDOV12D_CTRL              0x021
+#define R92C_LDOHCI12_CTRL             0x022
+#define R92C_LPLDO_CTRL                        0x023
+#define R92C_AFE_XTAL_CTRL             0x024
+#define R92C_AFE_PLL_CTRL              0x028
+#define R92C_EFUSE_CTRL                        0x030
+#define R92C_EFUSE_TEST                        0x034
+#define R92C_PWR_DATA                  0x038
+#define R92C_CAL_TIMER                 0x03c
+#define R92C_ACLK_MON                  0x03e
+#define R92C_GPIO_MUXCFG               0x040
+#define R92C_GPIO_IO_SEL               0x042
+#define R92C_MAC_PINMUX_CFG            0x043
+#define R92C_GPIO_PIN_CTRL             0x044
+#define R92C_GPIO_INTM                 0x048
+#define R92C_LEDCFG0                   0x04c
+#define R92C_LEDCFG1                   0x04d
+#define R92C_LEDCFG2                   0x04e
+#define R92C_LEDCFG3                   0x04f
+#define R92C_FSIMR                     0x050
+#define R92C_FSISR                     0x054
+#define R92C_HSIMR                     0x058
+#define R92C_HSISR                     0x05c
+#define R92C_MCUFWDL                   0x080
+#define R92C_HMEBOX_EXT(idx)           (0x088 + (idx) * 2)
+#define R92C_BIST_SCAN                 0x0d0
+#define R92C_BIST_RPT                  0x0d4
+#define R92C_BIST_ROM_RPT              0x0d8
+#define R92C_USB_SIE_INTF              0x0e0
+#define R92C_PCIE_MIO_INTF             0x0e4
+#define R92C_PCIE_MIO_INTD             0x0e8
+#define R92C_HPON_FSM                  0x0ec
+#define R92C_SYS_CFG                   0x0f0
+/* MAC General Configuration. */
+#define R92C_CR                                0x100
+#define R92C_PBP                       0x104
+#define R92C_TRXDMA_CTRL               0x10c
+#define R92C_TRXFF_BNDY                        0x114
+#define R92C_TRXFF_STATUS              0x118
+#define R92C_RXFF_PTR                  0x11c
+#define R92C_HIMR                      0x120
+#define R92C_HISR                      0x124
+#define R92C_HIMRE                     0x128
+#define R92C_HISRE                     0x12c
+#define R92C_CPWM                      0x12f
+#define R92C_FWIMR                     0x130
+#define R92C_FWISR                     0x134
+#define R92C_PKTBUF_DBG_CTRL           0x140
+#define R92C_PKTBUF_DBG_DATA_L         0x144
+#define R92C_PKTBUF_DBG_DATA_H         0x148
+#define R92C_TC0_CTRL(i)               (0x150 + (i) * 4)
+#define R92C_TCUNIT_BASE               0x164
+#define R92C_MBIST_START               0x174
+#define R92C_MBIST_DONE                        0x178
+#define R92C_MBIST_FAIL                        0x17c
+#define R92C_C2HEVT_MSG_NORMAL         0x1a0
+#define R92C_C2HEVT_MSG_TEST           0x1b8
+#define R92C_C2HEVT_CLEAR              0x1bf
+#define R92C_MCUTST_1                  0x1c0
+#define R92C_FMETHR                    0x1c8
+#define R92C_HMETFR                    0x1cc
+#define R92C_HMEBOX(idx)               (0x1d0 + (idx) * 4)
+#define R92C_LLT_INIT                  0x1e0
+#define R92C_BB_ACCESS_CTRL            0x1e8
+#define R92C_BB_ACCESS_DATA            0x1ec
+/* Tx DMA Configuration. */
+#define R92C_RQPN                      0x200
+#define R92C_FIFOPAGE                  0x204
+#define R92C_TDECTRL                   0x208
+#define R92C_TXDMA_OFFSET_CHK          0x20c
+#define R92C_TXDMA_STATUS              0x210
+#define R92C_RQPN_NPQ                  0x214
+/* Rx DMA Configuration. */
+#define R92C_RXDMA_AGG_PG_TH           0x280
+#define R92C_RXPKT_NUM                 0x284
+#define R92C_RXDMA_STATUS              0x288
+
+#define R92C_PCIE_CTRL_REG             0x300
+#define R92C_INT_MIG                   0x304
+#define R92C_BCNQ_DESA                 0x308
+#define R92C_HQ_DESA                   0x310
+#define R92C_MGQ_DESA                  0x318
+#define R92C_VOQ_DESA                  0x320
+#define R92C_VIQ_DESA                  0x328
+#define R92C_BEQ_DESA                  0x330
+#define R92C_BKQ_DESA                  0x338
+#define R92C_RX_DESA                   0x340
+#define R92C_DBI                       0x348
+#define R92C_MDIO                      0x354
+#define R92C_DBG_SEL                   0x360
+#define R92C_PCIE_HRPWM                        0x361
+#define R92C_PCIE_HCPWM                        0x363
+#define R92C_UART_CTRL                 0x364
+#define R92C_UART_TX_DES               0x370
+#define R92C_UART_RX_DES               0x378
+
+#define R92C_VOQ_INFORMATION                   0x0400
+#define R92C_VIQ_INFORMATION                   0x0404
+#define R92C_BEQ_INFORMATION                   0x0408
+#define R92C_BKQ_INFORMATION                   0x040C
+#define R92C_MGQ_INFORMATION                   0x0410
+#define R92C_HGQ_INFORMATION                   0x0414
+#define R92C_BCNQ_INFORMATION                  0x0418
+#define R92C_CPU_MGQ_INFORMATION               0x041C
+
+/* Protocol Configuration. */
+#define R92C_FWHW_TXQ_CTRL             0x420
+#define R92C_HWSEQ_CTRL                        0x423
+#define R92C_TXPKTBUF_BCNQ_BDNY                0x424
+#define R92C_TXPKTBUF_MGQ_BDNY         0x425
+#define R92C_SPEC_SIFS                 0x428
+#define R92C_RL                                0x42a
+#define R92C_DARFRC                    0x430
+#define R92C_RARFRC                    0x438
+#define R92C_RRSR                      0x440
+#define R92C_ARFR(i)                   (0x444 + (i) * 4)
+#define R92C_AGGLEN_LMT                        0x458
+#define R92C_AMPDU_MIN_SPACE           0x45c
+#define R92C_TXPKTBUF_WMAC_LBK_BF_HD   0x45d
+#define R92C_FAST_EDCA_CTRL            0x460
+#define R92C_RD_RESP_PKT_TH            0x463
+#define R92C_INIRTS_RATE_SEL           0x480
+#define R92C_INIDATA_RATE_SEL(macid)   (0x484 + (macid))
+/* EDCA Configuration. */
+#define R92C_EDCA_VO_PARAM             0x500
+#define R92C_EDCA_VI_PARAM             0x504
+#define R92C_EDCA_BE_PARAM             0x508
+#define R92C_EDCA_BK_PARAM             0x50c
+#define R92C_BCNTCFG                   0x510
+#define R92C_PIFS                      0x512
+#define R92C_RDG_PIFS                  0x513
+#define R92C_SIFS_CCK                  0x514
+#define R92C_SIFS_OFDM                 0x516
+#define R92C_AGGR_BREAK_TIME           0x51a
+#define R92C_SLOT                      0x51b
+#define R92C_TX_PTCL_CTRL              0x520
+#define R92C_TXPAUSE                   0x522
+#define R92C_DIS_TXREQ_CLR             0x523
+#define R92C_RD_CTRL                   0x524
+#define R92C_TBTT_PROHIBIT             0x540
+#define R92C_RD_NAV_NXT                        0x544
+#define R92C_NAV_PROT_LEN              0x546
+#define R92C_BCN_CTRL                  0x550
+#define R92C_USTIME_TSF                        0x551
+#define R92C_MBID_NUM                  0x552
+#define R92C_DUAL_TSF_RST              0x553
+#define R92C_BCN_INTERVAL              0x554
+#define R92C_DRVERLYINT                        0x558
+#define R92C_BCNDMATIM                 0x559
+#define R92C_ATIMWND                   0x55a
+#define R92C_BCN_MAX_ERR               0x55d
+#define R92C_RXTSF_OFFSET_CCK          0x55e
+#define R92C_RXTSF_OFFSET_OFDM         0x55f
+#define R92C_TSFTR                     0x560
+#define R92C_INIT_TSFTR                        0x564
+#define R92C_PSTIMER                   0x580
+#define R92C_TIMER0                    0x584
+#define R92C_TIMER1                    0x588
+#define R92C_ACMHWCTRL                 0x5c0
+#define R92C_ACMRSTCTRL                        0x5c1
+#define R92C_ACMAVG                    0x5c2
+#define R92C_VO_ADMTIME                        0x5c4
+#define R92C_VI_ADMTIME                        0x5c6
+#define R92C_BE_ADMTIME                        0x5c8
+#define R92C_EDCA_RANDOM_GEN           0x5cc
+#define R92C_SCH_TXCMD                 0x5d0
+/* WMAC Configuration. */
+#define R92C_APSD_CTRL                 0x600
+#define R92C_BWOPMODE                  0x603
+#define R92C_TCR                       0x604
+#define R92C_RCR                       0x608
+#define R92C_RX_DRVINFO_SZ             0x60f
+#define R92C_MACID                     0x610
+#define R92C_BSSID                     0x618
+#define R92C_MAR                       0x620
+#define R92C_MAC_SPEC_SIFS             0x63a
+#define R92C_R2T_SIFS                  0x63c
+#define R92C_T2T_SIFS                  0x63e
+#define R92C_ACKTO                     0x640
+#define R92C_CAMCMD                    0x670
+#define R92C_CAMWRITE                  0x674
+#define R92C_CAMREAD                   0x678
+#define R92C_CAMDBG                    0x67c
+#define R92C_SECCFG                    0x680
+#define R92C_RXFLTMAP0                 0x6a0
+#define R92C_RXFLTMAP1                 0x6a2
+#define R92C_RXFLTMAP2                 0x6a4
+
+/* Bits for R92C_SYS_ISO_CTRL. */
+#define R92C_SYS_ISO_CTRL_MD2PP                0x0001
+#define R92C_SYS_ISO_CTRL_UA2USB       0x0002
+#define R92C_SYS_ISO_CTRL_UD2CORE      0x0004
+#define R92C_SYS_ISO_CTRL_PA2PCIE      0x0008
+#define R92C_SYS_ISO_CTRL_PD2CORE      0x0010
+#define R92C_SYS_ISO_CTRL_IP2MAC       0x0020
+#define R92C_SYS_ISO_CTRL_DIOP         0x0040
+#define R92C_SYS_ISO_CTRL_DIOE         0x0080
+#define R92C_SYS_ISO_CTRL_EB2CORE      0x0100
+#define R92C_SYS_ISO_CTRL_DIOR         0x0200
+#define R92C_SYS_ISO_CTRL_PWC_EV25V    0x4000
+#define R92C_SYS_ISO_CTRL_PWC_EV12V    0x8000
+
+/* Bits for R92C_SYS_FUNC_EN. */
+#define R92C_SYS_FUNC_EN_BBRSTB                0x0001
+#define R92C_SYS_FUNC_EN_BB_GLB_RST    0x0002
+#define R92C_SYS_FUNC_EN_USBA          0x0004
+#define R92C_SYS_FUNC_EN_UPLL          0x0008
+#define R92C_SYS_FUNC_EN_USBD          0x0010
+#define R92C_SYS_FUNC_EN_DIO_PCIE      0x0020
+#define R92C_SYS_FUNC_EN_PCIEA         0x0040
+#define R92C_SYS_FUNC_EN_PPLL          0x0080
+#define R92C_SYS_FUNC_EN_PCIED         0x0100
+#define R92C_SYS_FUNC_EN_DIOE          0x0200
+#define R92C_SYS_FUNC_EN_CPUEN         0x0400
+#define R92C_SYS_FUNC_EN_DCORE         0x0800
+#define R92C_SYS_FUNC_EN_ELDR          0x1000
+#define R92C_SYS_FUNC_EN_DIO_RF                0x2000
+#define R92C_SYS_FUNC_EN_HWPDN         0x4000
+#define R92C_SYS_FUNC_EN_MREGEN                0x8000
+
+/* Bits for R92C_APS_FSMCO. */
+#define R92C_APS_FSMCO_PFM_LDALL       0x00000001
+#define R92C_APS_FSMCO_PFM_ALDN                0x00000002
+#define R92C_APS_FSMCO_PFM_LDKP                0x00000004
+#define R92C_APS_FSMCO_PFM_WOWL                0x00000008
+#define R92C_APS_FSMCO_PDN_EN          0x00000010
+#define R92C_APS_FSMCO_PDN_PL          0x00000020
+#define R92C_APS_FSMCO_APFM_ONMAC      0x00000100
+#define R92C_APS_FSMCO_APFM_OFF                0x00000200
+#define R92C_APS_FSMCO_APFM_RSM                0x00000400
+#define R92C_APS_FSMCO_AFSM_HSUS       0x00000800
+#define R92C_APS_FSMCO_AFSM_PCIE       0x00001000
+#define R92C_APS_FSMCO_APDM_MAC                0x00002000
+#define R92C_APS_FSMCO_APDM_HOST       0x00004000
+#define R92C_APS_FSMCO_APDM_HPDN       0x00008000
+#define R92C_APS_FSMCO_RDY_MACON       0x00010000
+#define R92C_APS_FSMCO_SUS_HOST                0x00020000
+#define R92C_APS_FSMCO_ROP_ALD         0x00100000
+#define R92C_APS_FSMCO_ROP_PWR         0x00200000
+#define R92C_APS_FSMCO_ROP_SPS         0x00400000
+#define R92C_APS_FSMCO_SOP_MRST                0x02000000
+#define R92C_APS_FSMCO_SOP_FUSE                0x04000000
+#define R92C_APS_FSMCO_SOP_ABG         0x08000000
+#define R92C_APS_FSMCO_SOP_AMB         0x10000000
+#define R92C_APS_FSMCO_SOP_RCK         0x20000000
+#define R92C_APS_FSMCO_SOP_A8M         0x40000000
+#define R92C_APS_FSMCO_XOP_BTCK                0x80000000
+
+/* Bits for R92C_SYS_CLKR. */
+#define R92C_SYS_CLKR_ANAD16V_EN       0x00000001
+#define R92C_SYS_CLKR_ANA8M            0x00000002
+#define R92C_SYS_CLKR_MACSLP           0x00000010
+#define R92C_SYS_CLKR_LOADER_EN                0x00000020
+#define R92C_SYS_CLKR_80M_SSC_DIS      0x00000080
+#define R92C_SYS_CLKR_80M_SSC_EN_HO    0x00000100
+#define R92C_SYS_CLKR_PHY_SSC_RSTB     0x00000200
+#define R92C_SYS_CLKR_SEC_EN           0x00000400
+#define R92C_SYS_CLKR_MAC_EN           0x00000800
+#define R92C_SYS_CLKR_SYS_EN           0x00001000
+#define R92C_SYS_CLKR_RING_EN          0x00002000
+
+/* Bits for R92C_RF_CTRL. */
+#define R92C_RF_CTRL_EN                0x01
+#define R92C_RF_CTRL_RSTB      0x02
+#define R92C_RF_CTRL_SDMRSTB   0x04
+
+/* Bits for R92C_LDOV12D_CTRL. */
+#define R92C_LDOV12D_CTRL_LDV12_EN     0x01
+
+/* Bits for R92C_EFUSE_CTRL. */
+#define R92C_EFUSE_CTRL_DATA_M 0x000000ff
+#define R92C_EFUSE_CTRL_DATA_S 0
+#define R92C_EFUSE_CTRL_ADDR_M 0x0003ff00
+#define R92C_EFUSE_CTRL_ADDR_S 8
+#define R92C_EFUSE_CTRL_VALID  0x80000000
+
+/* Bits for R92C_GPIO_MUXCFG. */
+#define R92C_GPIO_MUXCFG_RFKILL        0x0008
+#define R92C_GPIO_MUXCFG_ENBT  0x0020
+
+/* Bits for R92C_GPIO_IO_SEL. */
+#define R92C_GPIO_IO_SEL_RFKILL        0x0008
+
+/* Bits for R92C_LEDCFG0. */
+#define R92C_LEDCFG0_DIS       0x08
+
+/* Bits for R92C_MCUFWDL. */
+#define R92C_MCUFWDL_EN                        0x00000001
+#define R92C_MCUFWDL_RDY               0x00000002
+#define R92C_MCUFWDL_CHKSUM_RPT                0x00000004
+#define R92C_MCUFWDL_MACINI_RDY                0x00000008
+#define R92C_MCUFWDL_BBINI_RDY         0x00000010
+#define R92C_MCUFWDL_RFINI_RDY         0x00000020
+#define R92C_MCUFWDL_WINTINI_RDY       0x00000040
+#define R92C_MCUFWDL_RAM_DL_SEL                0x00000080 /* 1: RAM, 0: ROM */
+#define R92C_MCUFWDL_PAGE_M            0x00070000
+#define R92C_MCUFWDL_PAGE_S            16
+#define R92C_MCUFWDL_CPRST             0x00800000
+
+/* Bits for R92C_HPON_FSM. */
+#define R92C_HPON_FSM_CHIP_BONDING_ID_S                22
+#define R92C_HPON_FSM_CHIP_BONDING_ID_M                0x00c00000
+#define R92C_HPON_FSM_CHIP_BONDING_ID_92C_1T2R 1
+
+/* Bits for R92C_SYS_CFG. */
+#define R92C_SYS_CFG_XCLK_VLD          0x00000001
+#define R92C_SYS_CFG_ACLK_VLD          0x00000002
+#define R92C_SYS_CFG_UCLK_VLD          0x00000004
+#define R92C_SYS_CFG_PCLK_VLD          0x00000008
+#define R92C_SYS_CFG_PCIRSTB           0x00000010
+#define R92C_SYS_CFG_V15_VLD           0x00000020
+#define R92C_SYS_CFG_TRP_B15V_EN       0x00000080
+#define R92C_SYS_CFG_SIC_IDLE          0x00000100
+#define R92C_SYS_CFG_BD_MAC2           0x00000200
+#define R92C_SYS_CFG_BD_MAC1           0x00000400
+#define R92C_SYS_CFG_IC_MACPHY_MODE    0x00000800
+#define R92C_SYS_CFG_CHIP_VER_RTL_M    0x0000f000
+#define R92C_SYS_CFG_CHIP_VER_RTL_S    12
+#define R92C_SYS_CFG_BT_FUNC           0x00010000
+#define R92C_SYS_CFG_VENDOR_UMC                0x00080000
+#define R92C_SYS_CFG_PAD_HWPD_IDN      0x00400000
+#define R92C_SYS_CFG_TRP_VAUX_EN       0x00800000
+#define R92C_SYS_CFG_TRP_BT_EN         0x01000000
+#define R92C_SYS_CFG_BD_PKG_SEL                0x02000000
+#define R92C_SYS_CFG_BD_HCI_SEL                0x04000000
+#define R92C_SYS_CFG_TYPE_92C          0x08000000
+
+/* Bits for R92C_CR. */
+#define R92C_CR_HCI_TXDMA_EN   0x00000001
+#define R92C_CR_HCI_RXDMA_EN   0x00000002
+#define R92C_CR_TXDMA_EN       0x00000004
+#define R92C_CR_RXDMA_EN       0x00000008
+#define R92C_CR_PROTOCOL_EN    0x00000010
+#define R92C_CR_SCHEDULE_EN    0x00000020
+#define R92C_CR_MACTXEN                0x00000040
+#define R92C_CR_MACRXEN                0x00000080
+#define R92C_CR_ENSEC          0x00000200
+#define R92C_CR_NETTYPE_S      16
+#define R92C_CR_NETTYPE_M      0x00030000
+#define R92C_CR_NETTYPE_NOLINK 0
+#define R92C_CR_NETTYPE_ADHOC  1
+#define R92C_CR_NETTYPE_INFRA  2
+#define R92C_CR_NETTYPE_AP     3
+
+/* Bits for R92C_PBP. */
+#define R92C_PBP_PSRX_M                0x0f
+#define R92C_PBP_PSRX_S                0
+#define R92C_PBP_PSTX_M                0xf0
+#define R92C_PBP_PSTX_S                4
+#define R92C_PBP_64            0
+#define R92C_PBP_128           1
+#define R92C_PBP_256           2
+#define R92C_PBP_512           3
+#define R92C_PBP_1024          4
+
+/* Bits for R92C_TRXDMA_CTRL. */
+#define R92C_TRXDMA_CTRL_RXDMA_AGG_EN          0x0004
+#define R92C_TRXDMA_CTRL_TXDMA_VOQ_MAP_M       0x0030
+#define R92C_TRXDMA_CTRL_TXDMA_VOQ_MAP_S       4
+#define R92C_TRXDMA_CTRL_TXDMA_VIQ_MAP_M       0x00c0
+#define R92C_TRXDMA_CTRL_TXDMA_VIQ_MAP_S       6
+#define R92C_TRXDMA_CTRL_TXDMA_BEQ_MAP_M       0x0300
+#define R92C_TRXDMA_CTRL_TXDMA_BEQ_MAP_S       8
+#define R92C_TRXDMA_CTRL_TXDMA_BKQ_MAP_M       0x0c00
+#define R92C_TRXDMA_CTRL_TXDMA_BKQ_MAP_S       10
+#define R92C_TRXDMA_CTRL_TXDMA_MGQ_MAP_M       0x3000
+#define R92C_TRXDMA_CTRL_TXDMA_MGQ_MAP_S       12
+#define R92C_TRXDMA_CTRL_TXDMA_HIQ_MAP_M       0xc000
+#define R92C_TRXDMA_CTRL_TXDMA_HIQ_MAP_S       14
+#define R92C_TRXDMA_CTRL_QUEUE_LOW             1
+#define R92C_TRXDMA_CTRL_QUEUE_NORMAL          2
+#define R92C_TRXDMA_CTRL_QUEUE_HIGH            3
+#define R92C_TRXDMA_CTRL_QMAP_M                        0xfff0
+#define R92C_TRXDMA_CTRL_QMAP_S                        4
+/* Shortcuts. */
+#define R92C_TRXDMA_CTRL_QMAP_3EP              0xf5b0
+#define R92C_TRXDMA_CTRL_QMAP_HQ_LQ            0xf5f0
+#define R92C_TRXDMA_CTRL_QMAP_HQ_NQ            0xfaf0
+#define R92C_TRXDMA_CTRL_QMAP_LQ               0x5550
+#define R92C_TRXDMA_CTRL_QMAP_NQ               0xaaa0
+#define R92C_TRXDMA_CTRL_QMAP_HQ               0xfff0
+
+/* Bits for R92C_LLT_INIT. */
+#define R92C_LLT_INIT_DATA_M           0x000000ff
+#define R92C_LLT_INIT_DATA_S           0
+#define R92C_LLT_INIT_ADDR_M           0x0000ff00
+#define R92C_LLT_INIT_ADDR_S           8
+#define R92C_LLT_INIT_OP_M             0xc0000000
+#define R92C_LLT_INIT_OP_S             30
+#define R92C_LLT_INIT_OP_NO_ACTIVE     0
+#define R92C_LLT_INIT_OP_WRITE         1
+
+/* Bits for R92C_RQPN. */
+#define R92C_RQPN_HPQ_M                0x000000ff
+#define R92C_RQPN_HPQ_S                0
+#define R92C_RQPN_LPQ_M                0x0000ff00
+#define R92C_RQPN_LPQ_S                8
+#define R92C_RQPN_PUBQ_M       0x00ff0000
+#define R92C_RQPN_PUBQ_S       16
+#define R92C_RQPN_LD           0x80000000
+
+/* Bits for R92C_TDECTRL. */
+#define R92C_TDECTRL_BLK_DESC_NUM_M    0x0000000f
+#define R92C_TDECTRL_BLK_DESC_NUM_S    4
+
+/* Bits for R92C_FWHW_TXQ_CTRL. */
+#define R92C_FWHW_TXQ_CTRL_AMPDU_RTY_NEW       0x80
+
+/* Bits for R92C_SPEC_SIFS. */
+#define R92C_SPEC_SIFS_CCK_M   0x00ff
+#define R92C_SPEC_SIFS_CCK_S   0
+#define R92C_SPEC_SIFS_OFDM_M  0xff00
+#define R92C_SPEC_SIFS_OFDM_S  8
+
+/* Bits for R92C_RL. */
+#define R92C_RL_LRL_M          0x003f
+#define R92C_RL_LRL_S          0
+#define R92C_RL_SRL_M          0x3f00
+#define R92C_RL_SRL_S          8
+
+/* Bits for R92C_RRSR. */
+#define R92C_RRSR_RATE_BITMAP_M                0x000fffff
+#define R92C_RRSR_RATE_BITMAP_S                0
+#define R92C_RRSR_RATE_CCK_ONLY_1M     0xffff1
+#define R92C_RRSR_RATE_ALL             0xfffff
+#define R92C_RRSR_RSC_LOWSUBCHNL       0x00200000
+#define R92C_RRSR_RSC_UPSUBCHNL                0x00400000
+#define R92C_RRSR_SHORT                        0x00800000
+
+/* Bits for R92C_EDCA_XX_PARAM. */
+#define R92C_EDCA_PARAM_AIFS_M         0x000000ff
+#define R92C_EDCA_PARAM_AIFS_S         0
+#define R92C_EDCA_PARAM_ECWMIN_M       0x00000f00
+#define R92C_EDCA_PARAM_ECWMIN_S       8
+#define R92C_EDCA_PARAM_ECWMAX_M       0x0000f000
+#define R92C_EDCA_PARAM_ECWMAX_S       12
+#define R92C_EDCA_PARAM_TXOP_M         0xffff0000
+#define R92C_EDCA_PARAM_TXOP_S         16
+
+/* Bits for R92C_TXPAUSE. */
+#define R92C_TXPAUSE_AC_VO             0x01
+#define R92C_TXPAUSE_AC_VI             0x02
+#define R92C_TXPAUSE_AC_BE             0x04
+#define R92C_TXPAUSE_AC_BK             0x08
+
+/* Bits for R92C_BCN_CTRL. */
+#define R92C_BCN_CTRL_EN_MBSSID                0x02
+#define R92C_BCN_CTRL_TXBCN_RPT                0x04
+#define R92C_BCN_CTRL_EN_BCN           0x08
+#define R92C_BCN_CTRL_DIS_TSF_UDT0     0x10
+
+/* Bits for R92C_APSD_CTRL. */
+#define R92C_APSD_CTRL_OFF             0x40
+#define R92C_APSD_CTRL_OFF_STATUS      0x80
+
+/* Bits for R92C_BWOPMODE. */
+#define R92C_BWOPMODE_11J      0x01
+#define R92C_BWOPMODE_5G       0x02
+#define R92C_BWOPMODE_20MHZ    0x04
+
+/* Bits for R92C_TCR. */
+#define R92C_TCR_TSFRST                0x00000001
+#define R92C_TCR_DIS_GCLK      0x00000002
+#define R92C_TCR_PAD_SEL       0x00000004
+#define R92C_TCR_PWR_ST                0x00000040
+#define R92C_TCR_PWRBIT_OW_EN  0x00000080
+#define R92C_TCR_ACRC          0x00000100
+#define R92C_TCR_CFENDFORM     0x00000200
+#define R92C_TCR_ICV           0x00000400
+
+/* Bits for R92C_RCR. */
+#define R92C_RCR_AAP           0x00000001
+#define R92C_RCR_APM           0x00000002
+#define R92C_RCR_AM            0x00000004
+#define R92C_RCR_AB            0x00000008
+#define R92C_RCR_ADD3          0x00000010
+#define R92C_RCR_APWRMGT       0x00000020
+#define R92C_RCR_CBSSID_DATA   0x00000040
+#define R92C_RCR_CBSSID_BCN    0x00000080
+#define R92C_RCR_ACRC32                0x00000100
+#define R92C_RCR_AICV          0x00000200
+#define R92C_RCR_ADF           0x00000800
+#define R92C_RCR_ACF           0x00001000
+#define R92C_RCR_AMF           0x00002000
+#define R92C_RCR_HTC_LOC_CTRL  0x00004000
+#define R92C_RCR_MFBEN         0x00400000
+#define R92C_RCR_LSIGEN                0x00800000
+#define R92C_RCR_ENMBID                0x01000000
+#define R92C_RCR_APP_BA_SSN    0x08000000
+#define R92C_RCR_APP_PHYSTS    0x10000000
+#define R92C_RCR_APP_ICV       0x20000000
+#define R92C_RCR_APP_MIC       0x40000000
+#define R92C_RCR_APPFCS                0x80000000
+
+/* Bits for R92C_CAMCMD. */
+#define R92C_CAMCMD_ADDR_M     0x0000ffff
+#define R92C_CAMCMD_ADDR_S     0
+#define R92C_CAMCMD_WRITE      0x00010000
+#define R92C_CAMCMD_CLR                0x40000000
+#define R92C_CAMCMD_POLLING    0x80000000
+
+/* IMR */
+
+/*Beacon DMA interrupt 6 */
+#define R92C_IMR_BCNDMAINT6    0x80000000
+/*Beacon DMA interrupt 5 */
+#define R92C_IMR_BCNDMAINT5    0x40000000
+/*Beacon DMA interrupt 4 */
+#define R92C_IMR_BCNDMAINT4    0x20000000
+/*Beacon DMA interrupt 3 */
+#define R92C_IMR_BCNDMAINT3    0x10000000
+/*Beacon DMA interrupt 2 */
+#define R92C_IMR_BCNDMAINT2    0x08000000
+/*Beacon DMA interrupt 1 */
+#define R92C_IMR_BCNDMAINT1    0x04000000      
+/*Beacon Queue DMA OK interrupt 8 */
+#define R92C_IMR_BCNDOK8       0x02000000
+/*Beacon Queue DMA OK interrupt 7 */
+#define R92C_IMR_BCNDOK7       0x01000000
+/*Beacon Queue DMA OK interrupt 6 */
+#define R92C_IMR_BCNDOK6       0x00800000
+/*Beacon Queue DMA OK interrupt 5 */
+#define R92C_IMR_BCNDOK5       0x00400000
+/*Beacon Queue DMA OK interrupt 4 */
+#define R92C_IMR_BCNDOK4       0x00200000
+/*Beacon Queue DMA OK interrupt 3 */
+#define R92C_IMR_BCNDOK3       0x00100000
+/*Beacon Queue DMA OK interrupt 2 */
+#define R92C_IMR_BCNDOK2       0x00080000
+/*Beacon Queue DMA OK interrupt 1 */
+#define R92C_IMR_BCNDOK1       0x00040000
+/*Timeout interrupt 2 */
+#define R92C_IMR_TIMEOUT2      0x00020000
+/*Timeout interrupt 1 */
+#define R92C_IMR_TIMEOUT1      0x00010000
+/*Transmit FIFO Overflow */
+#define R92C_IMR_TXFOVW                0x00008000
+/*Power save time out interrupt */
+#define R92C_IMR_PSTIMEOUT     0x00004000
+/*Beacon DMA interrupt 0 */
+#define R92C_IMR_BCNINT                0x00002000
+/*Receive FIFO Overflow */
+#define R92C_IMR_RXFOVW                0x00001000
+/*Receive Descriptor Unavailable */
+#define R92C_IMR_RDU           0x00000800
+/*For 92C,ATIM Window End interrupt */
+#define R92C_IMR_ATIMEND       0x00000400
+/*Beacon Queue DMA OK interrupt */
+#define R92C_IMR_BDOK          0x00000200
+/*High Queue DMA OK interrupt */
+#define R92C_IMR_HIGHDOK       0x00000100
+/*Transmit Beacon OK interrupt */
+#define R92C_IMR_TBDOK         0x00000080
+/*Management Queue DMA OK interrupt */
+#define R92C_IMR_MGNTDOK       0x00000040
+/*For 92C,Transmit Beacon Error interrupt */
+#define R92C_IMR_TBDER         0x00000020
+/*AC_BK DMA OK interrupt */
+#define R92C_IMR_BKDOK         0x00000010
+/*AC_BE DMA OK interrupt */
+#define R92C_IMR_BEDOK         0x00000008
+/*AC_VI DMA OK interrupt */
+#define R92C_IMR_VIDOK         0x00000004
+/*AC_VO DMA interrupt */
+#define R92C_IMR_VODOK         0x00000002
+/*Receive DMA OK interrupt */
+#define R92C_IMR_ROK           0x00000001
+
+#define R92C_IBSS_INT_MASK                     (R92C_IMR_BCNINT | R92C_IMR_TBDOK | R92C_IMR_TBDER)
+
+/*
+ * Baseband registers.
+ */
+#define R92C_FPGA0_RFMOD               0x800
+#define R92C_FPGA0_TXINFO              0x804
+#define R92C_HSSI_PARAM1(chain)                (0x820 + (chain) * 8)
+#define R92C_HSSI_PARAM2(chain)                (0x824 + (chain) * 8)
+#define R92C_TXAGC_RATE18_06(i)                (((i) == 0) ? 0xe00 : 0x830)
+#define R92C_TXAGC_RATE54_24(i)                (((i) == 0) ? 0xe04 : 0x834)
+#define R92C_TXAGC_A_CCK1_MCS32                0xe08
+#define R92C_TXAGC_B_CCK1_55_MCS32     0x838
+#define R92C_TXAGC_B_CCK11_A_CCK2_11   0x86c
+#define R92C_TXAGC_MCS03_MCS00(i)      (((i) == 0) ? 0xe10 : 0x83c)
+#define R92C_TXAGC_MCS07_MCS04(i)      (((i) == 0) ? 0xe14 : 0x848)
+#define R92C_TXAGC_MCS11_MCS08(i)      (((i) == 0) ? 0xe18 : 0x84c)
+#define R92C_TXAGC_MCS15_MCS12(i)      (((i) == 0) ? 0xe1c : 0x868)
+#define R92C_LSSI_PARAM(chain)         (0x840 + (chain) * 4)
+#define R92C_FPGA0_RFIFACEOE(chain)    (0x860 + (chain) * 4)
+#define R92C_FPGA0_RFIFACESW(idx)      (0x870 + (idx) * 4)
+#define R92C_FPGA0_RFPARAM(idx)                (0x878 + (idx) * 4)
+#define R92C_FPGA0_ANAPARAM2           0x884
+#define R92C_LSSI_READBACK(chain)      (0x8a0 + (chain) * 4)
+#define R92C_HSPI_READBACK(chain)      (0x8b8 + (chain) * 4)
+#define R92C_FPGA1_RFMOD               0x900
+#define R92C_FPGA1_TXINFO              0x90c
+#define R92C_CCK0_SYSTEM               0xa00
+#define R92C_CCK0_AFESETTING           0xa04
+#define R92C_OFDM0_TRXPATHENA          0xc04
+#define R92C_OFDM0_TRMUXPAR            0xc08
+#define R92C_OFDM0_AGCCORE1(chain)     (0xc50 + (chain) * 8)
+#define R92C_OFDM0_AGCPARAM1           0xc70
+#define R92C_OFDM0_AGCRSSITABLE                0xc78
+#define R92C_OFDM1_LSTF                        0xd00
+
+/* Bits for R92C_FPGA[01]_RFMOD. */
+#define R92C_RFMOD_40MHZ       0x00000001
+#define R92C_RFMOD_JAPAN       0x00000002
+#define R92C_RFMOD_CCK_TXSC    0x00000030
+#define R92C_RFMOD_CCK_EN      0x01000000
+#define R92C_RFMOD_OFDM_EN     0x02000000
+
+/* Bits for R92C_HSSI_PARAM1(i). */
+#define R92C_HSSI_PARAM1_PI    0x00000100
+
+/* Bits for R92C_HSSI_PARAM2(i). */
+#define R92C_HSSI_PARAM2_CCK_HIPWR     0x00000200
+#define R92C_HSSI_PARAM2_ADDR_LENGTH   0x00000400
+#define R92C_HSSI_PARAM2_DATA_LENGTH   0x00000800
+#define R92C_HSSI_PARAM2_READ_ADDR_M   0x7f800000
+#define R92C_HSSI_PARAM2_READ_ADDR_S   23
+#define R92C_HSSI_PARAM2_READ_EDGE     0x80000000
+
+/* Bits for R92C_TXAGC_A_CCK1_MCS32. */
+#define R92C_TXAGC_A_CCK1_M    0x0000ff00
+#define R92C_TXAGC_A_CCK1_S    8
+
+/* Bits for R92C_TXAGC_B_CCK11_A_CCK2_11. */
+#define R92C_TXAGC_B_CCK11_M   0x000000ff
+#define R92C_TXAGC_B_CCK11_S   0
+#define R92C_TXAGC_A_CCK2_M    0x0000ff00
+#define R92C_TXAGC_A_CCK2_S    8
+#define R92C_TXAGC_A_CCK55_M   0x00ff0000
+#define R92C_TXAGC_A_CCK55_S   16
+#define R92C_TXAGC_A_CCK11_M   0xff000000
+#define R92C_TXAGC_A_CCK11_S   24
+
+/* Bits for R92C_TXAGC_B_CCK1_55_MCS32. */
+#define R92C_TXAGC_B_CCK1_M    0x0000ff00
+#define R92C_TXAGC_B_CCK1_S    8
+#define R92C_TXAGC_B_CCK2_M    0x00ff0000
+#define R92C_TXAGC_B_CCK2_S    16
+#define R92C_TXAGC_B_CCK55_M   0xff000000
+#define R92C_TXAGC_B_CCK55_S   24
+
+/* Bits for R92C_TXAGC_RATE18_06(x). */
+#define R92C_TXAGC_RATE06_M    0x000000ff
+#define R92C_TXAGC_RATE06_S    0
+#define R92C_TXAGC_RATE09_M    0x0000ff00
+#define R92C_TXAGC_RATE09_S    8
+#define R92C_TXAGC_RATE12_M    0x00ff0000
+#define R92C_TXAGC_RATE12_S    16
+#define R92C_TXAGC_RATE18_M    0xff000000
+#define R92C_TXAGC_RATE18_S    24
+
+/* Bits for R92C_TXAGC_RATE54_24(x). */
+#define R92C_TXAGC_RATE24_M    0x000000ff
+#define R92C_TXAGC_RATE24_S    0
+#define R92C_TXAGC_RATE36_M    0x0000ff00
+#define R92C_TXAGC_RATE36_S    8
+#define R92C_TXAGC_RATE48_M    0x00ff0000
+#define R92C_TXAGC_RATE48_S    16
+#define R92C_TXAGC_RATE54_M    0xff000000
+#define R92C_TXAGC_RATE54_S    24
+
+/* Bits for R92C_TXAGC_MCS03_MCS00(x). */
+#define R92C_TXAGC_MCS00_M     0x000000ff
+#define R92C_TXAGC_MCS00_S     0
+#define R92C_TXAGC_MCS01_M     0x0000ff00
+#define R92C_TXAGC_MCS01_S     8
+#define R92C_TXAGC_MCS02_M     0x00ff0000
+#define R92C_TXAGC_MCS02_S     16
+#define R92C_TXAGC_MCS03_M     0xff000000
+#define R92C_TXAGC_MCS03_S     24
+
+/* Bits for R92C_TXAGC_MCS07_MCS04(x). */
+#define R92C_TXAGC_MCS04_M     0x000000ff
+#define R92C_TXAGC_MCS04_S     0
+#define R92C_TXAGC_MCS05_M     0x0000ff00
+#define R92C_TXAGC_MCS05_S     8
+#define R92C_TXAGC_MCS06_M     0x00ff0000
+#define R92C_TXAGC_MCS06_S     16
+#define R92C_TXAGC_MCS07_M     0xff000000
+#define R92C_TXAGC_MCS07_S     24
+
+/* Bits for R92C_TXAGC_MCS11_MCS08(x). */
+#define R92C_TXAGC_MCS08_M     0x000000ff
+#define R92C_TXAGC_MCS08_S     0
+#define R92C_TXAGC_MCS09_M     0x0000ff00
+#define R92C_TXAGC_MCS09_S     8
+#define R92C_TXAGC_MCS10_M     0x00ff0000
+#define R92C_TXAGC_MCS10_S     16
+#define R92C_TXAGC_MCS11_M     0xff000000
+#define R92C_TXAGC_MCS11_S     24
+
+/* Bits for R92C_TXAGC_MCS15_MCS12(x). */
+#define R92C_TXAGC_MCS12_M     0x000000ff
+#define R92C_TXAGC_MCS12_S     0
+#define R92C_TXAGC_MCS13_M     0x0000ff00
+#define R92C_TXAGC_MCS13_S     8
+#define R92C_TXAGC_MCS14_M     0x00ff0000
+#define R92C_TXAGC_MCS14_S     16
+#define R92C_TXAGC_MCS15_M     0xff000000
+#define R92C_TXAGC_MCS15_S     24
+
+/* Bits for R92C_LSSI_PARAM(i). */
+#define R92C_LSSI_PARAM_DATA_M 0x000fffff
+#define R92C_LSSI_PARAM_DATA_S 0
+#define R92C_LSSI_PARAM_ADDR_M 0x03f00000
+#define R92C_LSSI_PARAM_ADDR_S 20
+
+/* Bits for R92C_FPGA0_ANAPARAM2. */
+#define R92C_FPGA0_ANAPARAM2_CBW20     0x00000400
+
+/* Bits for R92C_LSSI_READBACK(i). */
+#define R92C_LSSI_READBACK_DATA_M      0x000fffff
+#define R92C_LSSI_READBACK_DATA_S      0
+
+/* Bits for R92C_OFDM0_AGCCORE1(i). */
+#define R92C_OFDM0_AGCCORE1_GAIN_M     0x0000007f
+#define R92C_OFDM0_AGCCORE1_GAIN_S     0
+
+
+/*
+ * USB registers.
+ */
+#define R92C_USB_INFO                  0xfe17
+#define R92C_USB_SPECIAL_OPTION                0xfe55
+#define R92C_USB_HCPWM                 0xfe57
+#define R92C_USB_HRPWM                 0xfe58
+#define R92C_USB_DMA_AGG_TO            0xfe5b
+#define R92C_USB_AGG_TO                        0xfe5c
+#define R92C_USB_AGG_TH                        0xfe5d
+#define R92C_USB_VID                   0xfe60
+#define R92C_USB_PID                   0xfe62
+#define R92C_USB_OPTIONAL              0xfe64
+#define R92C_USB_EP                    0xfe65
+#define R92C_USB_PHY                   0xfe68
+#define R92C_USB_MAC_ADDR              0xfe70
+#define R92C_USB_STRING                        0xfe80
+
+/* Bits for R92C_USB_SPECIAL_OPTION. */
+#define R92C_USB_SPECIAL_OPTION_AGG_EN 0x08
+
+/* Bits for R92C_USB_EP. */
+#define R92C_USB_EP_HQ_M       0x000f
+#define R92C_USB_EP_HQ_S       0
+#define R92C_USB_EP_NQ_M       0x00f0
+#define R92C_USB_EP_NQ_S       4
+#define R92C_USB_EP_LQ_M       0x0f00
+#define R92C_USB_EP_LQ_S       8
+
+
+/*
+ * Firmware base address.
+ */
+#define R92C_FW_START_ADDR     0x1000
+#define R92C_FW_PAGE_SIZE      4096
+
+
+/*
+ * RF (6052) registers.
+ */
+#define R92C_RF_AC             0x00
+#define R92C_RF_IQADJ_G(i)     (0x01 + (i))
+#define R92C_RF_POW_TRSW       0x05
+#define R92C_RF_GAIN_RX                0x06
+#define R92C_RF_GAIN_TX                0x07
+#define R92C_RF_TXM_IDAC       0x08
+#define R92C_RF_BS_IQGEN       0x0f
+#define R92C_RF_MODE1          0x10
+#define R92C_RF_MODE2          0x11
+#define R92C_RF_RX_AGC_HP      0x12
+#define R92C_RF_TX_AGC         0x13
+#define R92C_RF_BIAS           0x14
+#define R92C_RF_IPA            0x15
+#define R92C_RF_POW_ABILITY    0x17
+#define R92C_RF_CHNLBW         0x18
+#define R92C_RF_RX_G1          0x1a
+#define R92C_RF_RX_G2          0x1b
+#define R92C_RF_RX_BB2         0x1c
+#define R92C_RF_RX_BB1         0x1d
+#define R92C_RF_RCK1           0x1e
+#define R92C_RF_RCK2           0x1f
+#define R92C_RF_TX_G(i)                (0x20 + (i))
+#define R92C_RF_TX_BB1         0x23
+#define R92C_RF_T_METER                0x24
+#define R92C_RF_SYN_G(i)       (0x25 + (i))
+#define R92C_RF_RCK_OS         0x30
+#define R92C_RF_TXPA_G(i)      (0x31 + (i))
+
+/* Bits for R92C_RF_AC. */
+#define R92C_RF_AC_MODE_M      0x70000
+#define R92C_RF_AC_MODE_S      16
+#define R92C_RF_AC_MODE_STANDBY        1
+
+/* Bits for R92C_RF_CHNLBW. */
+#define R92C_RF_CHNLBW_CHNL_M  0x003ff
+#define R92C_RF_CHNLBW_CHNL_S  0
+#define R92C_RF_CHNLBW_BW20    0x00400
+#define R92C_RF_CHNLBW_LCSTART 0x08000
+
+
+/*
+ * CAM entries.
+ */
+#define R92C_CAM_ENTRY_COUNT   32
+
+#define R92C_CAM_CTL0(entry)   ((entry) * 8 + 0)
+#define R92C_CAM_CTL1(entry)   ((entry) * 8 + 1)
+#define R92C_CAM_KEY(entry, i) ((entry) * 8 + 2 + (i))
+
+/* Bits for R92C_CAM_CTL0(i). */
+#define R92C_CAM_KEYID_M       0x00000003
+#define R92C_CAM_KEYID_S       0
+#define R92C_CAM_ALGO_M                0x0000001c
+#define R92C_CAM_ALGO_S                2
+#define R92C_CAM_ALGO_NONE     0
+#define R92C_CAM_ALGO_WEP40    1
+#define R92C_CAM_ALGO_TKIP     2
+#define R92C_CAM_ALGO_AES      4
+#define R92C_CAM_ALGO_WEP104   5
+#define R92C_CAM_VALID         0x00008000
+#define R92C_CAM_MACLO_M       0xffff0000
+#define R92C_CAM_MACLO_S       16
+
+/* Rate adaptation modes. */
+#define R92C_RAID_11GN 1
+#define R92C_RAID_11N  3
+#define R92C_RAID_11BG 4
+#define R92C_RAID_11G  5       /* "pure" 11g */
+#define R92C_RAID_11B  6
+
+
+/* Macros to access unaligned little-endian memory. */
+#define LE_READ_2(x)   ((x)[0] | (x)[1] << 8)
+#define LE_READ_4(x)   ((x)[0] | (x)[1] << 8 | (x)[2] << 16 | (x)[3] << 24)
+
+/*
+ * Macros to access subfields in registers.
+ */
+/* Mask and Shift (getter). */
+#define MS(val, field)                                                 \
+       (((val) & field##_M) >> field##_S)
+
+/* Shift and Mask (setter). */
+#define SM(field, val)                                                 \
+       (((val) << field##_S) & field##_M)
+
+/* Rewrite. */
+#define RW(var, field, val)                                            \
+       (((var) & ~field##_M) | SM(field, val))
+
+/*
+ * Firmware image header.
+ */
+struct r92c_fw_hdr {
+       /* QWORD0 */
+       uint16_t        signature;
+       uint8_t         category;
+       uint8_t         function;
+       uint16_t        version;
+       uint16_t        subversion;
+       /* QWORD1 */
+       uint8_t         month;
+       uint8_t         date;
+       uint8_t         hour;
+       uint8_t         minute;
+       uint16_t        ramcodesize;
+       uint16_t        reserved2;
+       /* QWORD2 */
+       uint32_t        svnidx;
+       uint32_t        reserved3;
+       /* QWORD3 */
+       uint32_t        reserved4;
+       uint32_t        reserved5;
+} __packed;
+
+/*
+ * Host to firmware commands.
+ */
+struct r92c_fw_cmd {
+       uint8_t id;
+#define R92C_CMD_AP_OFFLOAD            0
+#define R92C_CMD_SET_PWRMODE           1
+#define R92C_CMD_JOINBSS_RPT           2
+#define R92C_CMD_RSVD_PAGE             3
+#define R92C_CMD_RSSI                  4
+#define R92C_CMD_RSSI_SETTING          5
+#define R92C_CMD_MACID_CONFIG          6
+#define R92C_CMD_MACID_PS_MODE         7
+#define R92C_CMD_P2P_PS_OFFLOAD                8
+#define R92C_CMD_SELECTIVE_SUSPEND     9
+#define R92C_CMD_FLAG_EXT              0x80
+
+       uint8_t msg[5];
+} __packed;
+
+/* Structure for R92C_CMD_RSSI_SETTING. */
+struct r92c_fw_cmd_rssi {
+       uint8_t macid;
+       uint8_t reserved;
+       uint8_t pwdb;
+} __packed;
+
+/* Structure for R92C_CMD_MACID_CONFIG. */
+struct r92c_fw_cmd_macid_cfg {
+       uint32_t        mask;
+       uint8_t         macid;
+#define RTWN_MACID_BSS         0
+#define RTWN_MACID_BC          4       /* Broadcast. */
+#define RTWN_MACID_VALID       0x80
+} __packed;
+
+/*
+ * RTL8192CU ROM image.
+ */
+struct r92c_rom {
+       uint16_t        id;             /* 0x8129 */
+       uint8_t         reserved1[5];
+       uint8_t         dbg_sel;
+       uint16_t        reserved2;
+       uint16_t        vid;
+       uint16_t        pid;
+       uint8_t         usb_opt;
+       uint8_t         ep_setting;
+       uint16_t        reserved3;
+       uint8_t         usb_phy;
+       uint8_t         reserved4[3];
+       uint8_t         macaddr[6];
+       uint8_t         string[61];     /* "Realtek" */
+       uint8_t         subcustomer_id;
+       uint8_t         cck_tx_pwr[R92C_MAX_CHAINS][3];
+       uint8_t         ht40_1s_tx_pwr[R92C_MAX_CHAINS][3];
+       uint8_t         ht40_2s_tx_pwr_diff[3];
+       uint8_t         ht20_tx_pwr_diff[3];
+       uint8_t         ofdm_tx_pwr_diff[3];
+       uint8_t         ht40_max_pwr[3];
+       uint8_t         ht20_max_pwr[3];
+       uint8_t         xtal_calib;
+       uint8_t         tssi[R92C_MAX_CHAINS];
+       uint8_t         thermal_meter;
+       uint8_t         rf_opt1;
+#define R92C_ROM_RF1_REGULATORY_M      0x07
+#define R92C_ROM_RF1_REGULATORY_S      0
+#define R92C_ROM_RF1_BOARD_TYPE_M      0xe0
+#define R92C_ROM_RF1_BOARD_TYPE_S      5
+#define R92C_BOARD_TYPE_DONGLE         0
+#define R92C_BOARD_TYPE_HIGHPA         1
+#define R92C_BOARD_TYPE_MINICARD       2
+#define R92C_BOARD_TYPE_SOLO           3
+#define R92C_BOARD_TYPE_COMBO          4
+
+       uint8_t         rf_opt2;
+       uint8_t         rf_opt3;
+       uint8_t         rf_opt4;
+       uint8_t         channel_plan;
+       uint8_t         version;
+       uint8_t         curstomer_id;
+} __packed;
+
+/* Rx MAC descriptor. */
+struct r92c_rx_desc {
+       uint32_t        rxdw0;
+#define R92C_RXDW0_PKTLEN_M    0x00003fff
+#define R92C_RXDW0_PKTLEN_S    0
+#define R92C_RXDW0_CRCERR      0x00004000
+#define R92C_RXDW0_ICVERR      0x00008000
+#define R92C_RXDW0_INFOSZ_M    0x000f0000
+#define R92C_RXDW0_INFOSZ_S    16
+#define R92C_RXDW0_QOS         0x00800000
+#define R92C_RXDW0_SHIFT_M     0x03000000
+#define R92C_RXDW0_SHIFT_S     24
+#define R92C_RXDW0_PHYST       0x04000000
+#define R92C_RXDW0_DECRYPTED   0x08000000
+#define R92C_RXDW0_LS          0x10000000
+#define R92C_RXDW0_FS          0x20000000
+#define R92C_RXDW0_EOR         0x40000000
+#define R92C_RXDW0_OWN         0x80000000
+
+       uint32_t        rxdw1;
+       uint32_t        rxdw2;
+#define R92C_RXDW2_PKTCNT_M    0x00ff0000
+#define R92C_RXDW2_PKTCNT_S    16
+
+       uint32_t        rxdw3;
+#define R92C_RXDW3_RATE_M      0x0000003f
+#define R92C_RXDW3_RATE_S      0
+#define R92C_RXDW3_HT          0x00000040
+#define R92C_RXDW3_HTC         0x00000400
+
+       uint32_t        rxdw4;
+       uint32_t        rxdw5;
+
+       uint32_t        rxbufaddr;
+       uint32_t        rxbufaddr64;
+} __packed __attribute__((aligned(4)));
+
+/* Rx PHY descriptor. */
+struct r92c_rx_phystat {
+       uint32_t        phydw0;
+       uint32_t        phydw1;
+       uint32_t        phydw2;
+       uint32_t        phydw3;
+       uint32_t        phydw4;
+       uint32_t        phydw5;
+       uint32_t        phydw6;
+       uint32_t        phydw7;
+} __packed __attribute__((aligned(4)));
+
+/* Rx PHY CCK descriptor. */
+struct r92c_rx_cck {
+       uint8_t         adc_pwdb[4];
+       uint8_t         sq_rpt;
+       uint8_t         agc_rpt;
+} __packed;
+
+/* Tx MAC descriptor. */
+struct r92c_tx_desc {
+       uint32_t        txdw0;
+#define R92C_TXDW0_PKTLEN_M    0x0000ffff
+#define R92C_TXDW0_PKTLEN_S    0
+#define R92C_TXDW0_OFFSET_M    0x00ff0000
+#define R92C_TXDW0_OFFSET_S    16
+#define R92C_TXDW0_BMCAST      0x01000000
+#define R92C_TXDW0_LSG         0x04000000
+#define R92C_TXDW0_FSG         0x08000000
+#define R92C_TXDW0_OWN         0x80000000
+
+       uint32_t        txdw1;
+#define R92C_TXDW1_MACID_M     0x0000001f
+#define R92C_TXDW1_MACID_S     0
+#define R92C_TXDW1_AGGEN       0x00000020
+#define R92C_TXDW1_AGGBK       0x00000040
+#define R92C_TXDW1_QSEL_M      0x00001f00
+#define R92C_TXDW1_QSEL_S      8
+#define R92C_TXDW1_QSEL_BE     0x00
+#define R92C_TXDW1_QSEL_BK     0x02
+#define R92C_TXDW1_QSEL_VI     0x05
+#define R92C_TXDW1_QSEL_VO     0x07
+#define R92C_TXDW1_QSEL_BEACON 0x10
+#define R92C_TXDW1_QSEL_HIGH   0x11
+#define R92C_TXDW1_QSEL_MGNT   0x12
+#define R92C_TXDW1_QSEL_CMD    0x13
+#define R92C_TXDW1_RAID_M      0x000f0000
+#define R92C_TXDW1_RAID_S      16
+#define R92C_TXDW1_CIPHER_M    0x00c00000
+#define R92C_TXDW1_CIPHER_S    22
+#define R92C_TXDW1_CIPHER_NONE 0
+#define R92C_TXDW1_CIPHER_RC4  1
+#define R92C_TXDW1_CIPHER_AES  3
+#define R92C_TXDW1_PKTOFF_M    0x7c000000
+#define R92C_TXDW1_PKTOFF_S    26
+
+       uint32_t        txdw2;
+       uint16_t        txdw3;
+       uint16_t        txdseq;
+
+       uint32_t        txdw4;
+#define R92C_TXDW4_RTSRATE_M   0x0000003f
+#define R92C_TXDW4_RTSRATE_S   0
+#define R92C_TXDW4_QOS         0x00000040
+#define R92C_TXDW4_HWSEQ       0x00000080
+#define R92C_TXDW4_DRVRATE     0x00000100
+#define R92C_TXDW4_CTS2SELF    0x00000800
+#define R92C_TXDW4_RTSEN       0x00001000
+#define R92C_TXDW4_HWRTSEN     0x00002000
+#define R92C_TXDW4_SCO_M       0x003f0000
+#define R92C_TXDW4_SCO_S       20
+#define R92C_TXDW4_SCO_SCA     1
+#define R92C_TXDW4_SCO_SCB     2
+#define R92C_TXDW4_40MHZ       0x02000000
+
+       uint32_t        txdw5;
+#define R92C_TXDW5_DATARATE_M          0x0000003f
+#define R92C_TXDW5_DATARATE_S          0
+#define R92C_TXDW5_SGI                 0x00000040
+#define R92C_TXDW5_DATARATE_FBLIMIT_M  0x00001f00
+#define R92C_TXDW5_DATARATE_FBLIMIT_S  8
+#define R92C_TXDW5_RTSRATE_FBLIMIT_M   0x0001e000
+#define R92C_TXDW5_RTSRATE_FBLIMIT_S   13
+#define R92C_TXDW5_RETRY_LIMIT_ENABLE  0x00020000
+#define R92C_TXDW5_DATA_RETRY_LIMIT_M  0x00fc0000
+#define R92C_TXDW5_DATA_RETRY_LIMIT_S  18
+#define R92C_TXDW5_AGGNUM_M            0xff000000
+#define R92C_TXDW5_AGGNUM_S            24
+
+       uint32_t        txdw6;
+
+       uint16_t        txbufsize;
+       uint16_t        pad;
+
+       uint32_t        txbufaddr;
+       uint32_t        txbufaddr64;
+
+       uint32_t        nextdescaddr;
+       uint32_t        nextdescaddr64;
+
+       uint32_t        reserved[4];
+} __packed __attribute__((aligned(4)));
+
+
+/*
+ * Driver definitions.
+ */
+#define RTWN_NTXQUEUES                 9
+#define RTWN_RX_LIST_COUNT             256
+#define RTWN_TX_LIST_COUNT             256
+#define RTWN_HOST_CMD_RING_COUNT       32
+
+/* TX queue indices. */
+#define RTWN_BK_QUEUE                  0
+#define RTWN_BE_QUEUE                  1
+#define RTWN_VI_QUEUE                  2
+#define RTWN_VO_QUEUE                  3
+#define RTWN_BEACON_QUEUE              4
+#define RTWN_TXCMD_QUEUE               5
+#define RTWN_MGNT_QUEUE                        6
+#define RTWN_HIGH_QUEUE                        7
+#define RTWN_HCCA_QUEUE                        8
+
+/* RX queue indices. */
+#define RTWN_RX_QUEUE                  0
+
+#define RTWN_RXBUFSZ   (16 * 1024)
+#define RTWN_TXBUFSZ   (sizeof(struct r92c_tx_desc) + IEEE80211_MAX_LEN)
+
+#define RTWN_RIDX_COUNT        28
+
+#define RTWN_TX_TIMEOUT        5000    /* ms */
+
+#define RTWN_LED_LINK  0
+#define RTWN_LED_DATA  1
+
+struct rtwn_rx_radiotap_header {
+       struct ieee80211_radiotap_header wr_ihdr;
+       uint8_t         wr_flags;
+       uint8_t         wr_rate;
+       uint16_t        wr_chan_freq;
+       uint16_t        wr_chan_flags;
+       uint8_t         wr_dbm_antsignal;
+} __packed;
+
+#define RTWN_RX_RADIOTAP_PRESENT                       \
+       (1 << IEEE80211_RADIOTAP_FLAGS |                \
+        1 << IEEE80211_RADIOTAP_RATE |                 \
+        1 << IEEE80211_RADIOTAP_CHANNEL |              \
+        1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL)
+
+struct rtwn_tx_radiotap_header {
+       struct ieee80211_radiotap_header wt_ihdr;
+       uint8_t         wt_flags;
+       uint16_t        wt_chan_freq;
+       uint16_t        wt_chan_flags;
+} __packed;
+
+#define RTWN_TX_RADIOTAP_PRESENT                       \
+       (1 << IEEE80211_RADIOTAP_FLAGS |                \
+        1 << IEEE80211_RADIOTAP_CHANNEL)
+
+struct rtwn_softc;
+
+struct rtwn_rx_data {
+       bus_dmamap_t            map;
+       struct mbuf             *m;
+};
+
+struct rtwn_rx_ring {
+       struct r92c_rx_desc     *desc;
+       bus_dmamap_t            map;
+       bus_dma_segment_t       seg;
+       int                     nsegs;
+       struct rtwn_rx_data     rx_data[RTWN_RX_LIST_COUNT];
+
+};
+struct rtwn_tx_data {
+       bus_dmamap_t                    map;
+       struct mbuf                     *m;
+       struct ieee80211_node           *ni;
+};
+
+struct rtwn_tx_ring {
+       bus_dmamap_t            map;
+       bus_dma_segment_t       seg;
+       int                     nsegs;
+       struct r92c_tx_desc     *desc;
+       struct rtwn_tx_data     tx_data[RTWN_TX_LIST_COUNT];
+       int                     queued;
+       int                     cur;
+};
+
+struct rtwn_host_cmd {
+       void    (*cb)(struct rtwn_softc *, void *);
+       uint8_t data[256];
+};
+
+struct rtwn_cmd_key {
+       struct ieee80211_key    key;
+       uint16_t                associd;
+};
+
+struct rtwn_host_cmd_ring {
+       struct rtwn_host_cmd    cmd[RTWN_HOST_CMD_RING_COUNT];
+       int                     cur;
+       int                     next;
+       int                     queued;
+};
+
+struct rtwn_softc {
+       struct device                   sc_dev;
+       struct ieee80211com             sc_ic;
+       int                             (*sc_newstate)(struct ieee80211com *,
+                                           enum ieee80211_state, int);
+
+       /* PCI specific goo. */
+       bus_dma_tag_t           sc_dmat;
+       pci_chipset_tag_t       sc_pc;
+       pcitag_t                sc_tag;
+       void                    *sc_ih;
+       bus_space_tag_t         sc_st;
+       bus_space_handle_t      sc_sh;
+       bus_size_t              sc_mapsize;
+       int                     sc_cap_off;
+
+
+       struct timeout                  scan_to;
+       struct timeout                  calib_to;
+       struct task                     init_task;
+       int                             ac2idx[EDCA_NUM_AC];
+       u_int                           sc_flags;
+#define RTWN_FLAG_CCK_HIPWR    0x01
+#define RTWN_FLAG_BUSY         0x02
+
+       u_int                           chip;
+#define RTWN_CHIP_88C          0x00
+#define RTWN_CHIP_92C          0x01
+#define RTWN_CHIP_92C_1T2R     0x02
+#define RTWN_CHIP_UMC          0x04
+#define RTWN_CHIP_UMC_A_CUT    0x08
+
+       uint8_t                         board_type;
+       uint8_t                         regulatory;
+       uint8_t                         pa_setting;
+       int                             avg_pwdb;
+       int                             thcal_state;
+       int                             thcal_lctemp;
+       int                             ntxchains;
+       int                             nrxchains;
+       int                             ledlink;
+
+       int                             sc_tx_timer;
+       int                             fwcur;
+       struct rtwn_rx_ring             rx_ring;
+       struct rtwn_tx_ring             tx_ring[RTWN_NTXQUEUES];
+       uint32_t                        qfullmsk;
+       struct r92c_rom                 rom;
+
+       uint32_t                        rf_chnlbw[R92C_MAX_CHAINS];
+#if NBPFILTER > 0
+       caddr_t                         sc_drvbpf;
+
+       union {
+               struct rtwn_rx_radiotap_header th;
+               uint8_t pad[64];
+       }                               sc_rxtapu;
+#define sc_rxtap       sc_rxtapu.th
+       int                             sc_rxtap_len;
+
+       union {
+               struct rtwn_tx_radiotap_header th;
+               uint8_t pad[64];
+       }                               sc_txtapu;
+#define sc_txtap       sc_txtapu.th
+       int                             sc_txtap_len;
+#endif
+};
+
+/*
+ * MAC initialization values.
+ */
+static const struct {
+       uint16_t        reg;
+       uint8_t         val;
+} rtl8192ce_mac[] = {
+       { 0x420, 0x80 }, { 0x423, 0x00 }, { 0x430, 0x00 }, { 0x431, 0x00 },
+       { 0x432, 0x00 }, { 0x433, 0x01 }, { 0x434, 0x04 }, { 0x435, 0x05 },
+       { 0x436, 0x06 }, { 0x437, 0x07 }, { 0x438, 0x00 }, { 0x439, 0x00 },
+       { 0x43a, 0x00 }, { 0x43b, 0x01 }, { 0x43c, 0x04 }, { 0x43d, 0x05 },
+       { 0x43e, 0x06 }, { 0x43f, 0x07 }, { 0x440, 0x5d }, { 0x441, 0x01 },
+       { 0x442, 0x00 }, { 0x444, 0x15 }, { 0x445, 0xf0 }, { 0x446, 0x0f },
+       { 0x447, 0x00 }, { 0x458, 0x41 }, { 0x459, 0xa8 }, { 0x45a, 0x72 },
+       { 0x45b, 0xb9 }, { 0x460, 0x88 }, { 0x461, 0x88 }, { 0x462, 0x06 },
+       { 0x463, 0x03 }, { 0x4c8, 0x04 }, { 0x4c9, 0x08 }, { 0x4cc, 0x02 },
+       { 0x4cd, 0x28 }, { 0x4ce, 0x01 }, { 0x500, 0x26 }, { 0x501, 0xa2 },
+       { 0x502, 0x2f }, { 0x503, 0x00 }, { 0x504, 0x28 }, { 0x505, 0xa3 },
+       { 0x506, 0x5e }, { 0x507, 0x00 }, { 0x508, 0x2b }, { 0x509, 0xa4 },
+       { 0x50a, 0x5e }, { 0x50b, 0x00 }, { 0x50c, 0x4f }, { 0x50d, 0xa4 },
+       { 0x50e, 0x00 }, { 0x50f, 0x00 }, { 0x512, 0x1c }, { 0x514, 0x0a },
+       { 0x515, 0x10 }, { 0x516, 0x0a }, { 0x517, 0x10 }, { 0x51a, 0x16 },
+       { 0x524, 0x0f }, { 0x525, 0x4f }, { 0x546, 0x20 }, { 0x547, 0x00 },
+       { 0x559, 0x02 }, { 0x55a, 0x02 }, { 0x55d, 0xff }, { 0x605, 0x30 },
+       { 0x608, 0x0e }, { 0x609, 0x2a }, { 0x652, 0x20 }, { 0x63c, 0x0a },
+       { 0x63d, 0x0e }, { 0x700, 0x21 }, { 0x701, 0x43 }, { 0x702, 0x65 },
+       { 0x703, 0x87 }, { 0x708, 0x21 }, { 0x709, 0x43 }, { 0x70a, 0x65 },
+       { 0x70b, 0x87 }
+};
+
+/*
+ * Baseband initialization values.
+ */
+struct rtwn_bb_prog {
+       int             count;
+       const uint16_t  *regs;
+       const uint32_t  *vals;
+       int             agccount;
+       const uint32_t  *agcvals;
+};
+
+/*
+ * RTL8192CU and RTL8192CE-VAU.
+ */
+static const uint16_t rtl8192ce_bb_regs[] = {
+       0x024, 0x028, 0x800, 0x804, 0x808, 0x80c, 0x810, 0x814, 0x818,
+       0x81c, 0x820, 0x824, 0x828, 0x82c, 0x830, 0x834, 0x838, 0x83c,
+       0x840, 0x844, 0x848, 0x84c, 0x850, 0x854, 0x858, 0x85c, 0x860,
+       0x864, 0x868, 0x86c, 0x870, 0x874, 0x878, 0x87c, 0x880, 0x884,
+       0x888, 0x88c, 0x890, 0x894, 0x898, 0x89c, 0x900, 0x904, 0x908,
+       0x90c, 0xa00, 0xa04, 0xa08, 0xa0c, 0xa10, 0xa14, 0xa18, 0xa1c,
+       0xa20, 0xa24, 0xa28, 0xa2c, 0xa70, 0xa74, 0xc00, 0xc04, 0xc08,
+       0xc0c, 0xc10, 0xc14, 0xc18, 0xc1c, 0xc20, 0xc24, 0xc28, 0xc2c,
+       0xc30, 0xc34, 0xc38, 0xc3c, 0xc40, 0xc44, 0xc48, 0xc4c, 0xc50,
+       0xc54, 0xc58, 0xc5c, 0xc60, 0xc64, 0xc68, 0xc6c, 0xc70, 0xc74,
+       0xc78, 0xc7c, 0xc80, 0xc84, 0xc88, 0xc8c, 0xc90, 0xc94, 0xc98,
+       0xc9c, 0xca0, 0xca4, 0xca8, 0xcac, 0xcb0, 0xcb4, 0xcb8, 0xcbc,
+       0xcc0, 0xcc4, 0xcc8, 0xccc, 0xcd0, 0xcd4, 0xcd8, 0xcdc, 0xce0,
+       0xce4, 0xce8, 0xcec, 0xd00, 0xd04, 0xd08, 0xd0c, 0xd10, 0xd14,
+       0xd18, 0xd2c, 0xd30, 0xd34, 0xd38, 0xd3c, 0xd40, 0xd44, 0xd48,
+       0xd4c, 0xd50, 0xd54, 0xd58, 0xd5c, 0xd60, 0xd64, 0xd68, 0xd6c,
+       0xd70, 0xd74, 0xd78, 0xe00, 0xe04, 0xe08, 0xe10, 0xe14, 0xe18,
+       0xe1c, 0xe28, 0xe30, 0xe34, 0xe38, 0xe3c, 0xe40, 0xe44, 0xe48,
+       0xe4c, 0xe50, 0xe54, 0xe58, 0xe5c, 0xe60, 0xe68, 0xe6c, 0xe70,
+       0xe74, 0xe78, 0xe7c, 0xe80, 0xe84, 0xe88, 0xe8c, 0xed0, 0xed4,
+       0xed8, 0xedc, 0xee0, 0xeec, 0xf14, 0xf4c, 0xf00
+};
+
+static const uint32_t rtl8192ce_bb_vals_2t[] = {
+       0x0011800f, 0x00ffdb83, 0x80040002, 0x00000003, 0x0000fc00,
+       0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000,
+       0x01000100, 0x00390004, 0x01000100, 0x00390004, 0x27272727,
+       0x27272727, 0x27272727, 0x27272727, 0x00010000, 0x00010000,
+       0x27272727, 0x27272727, 0x00000000, 0x00000000, 0x569a569a,
+       0x0c1b25a4, 0x66e60230, 0x061f0130, 0x27272727, 0x2b2b2b27,
+       0x07000700, 0x22184000, 0x08080808, 0x00000000, 0xc0083070,
+       0x000004d5, 0x00000000, 0xcc0000c0, 0x00000800, 0xfffffffe,
+       0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000,
+       0x81121313, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f,
+       0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000,
+       0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007,
+       0x48071d40, 0x03a05633, 0x000000e4, 0x6c6c6c6c, 0x08800000,
+       0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994,
+       0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f,
+       0x69543420, 0x43bc0094, 0x69543420, 0x433c0094, 0x00000000,
+       0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x018610db,
+       0x0000001f, 0x00b91612, 0x40000100, 0x20f60000, 0x40000100,
+       0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f,
+       0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427,
+       0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c,
+       0x00080740, 0x00020403, 0x0000907f, 0x20010201, 0xa0633333,
+       0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000,
+       0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064,
+       0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e,
+       0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a,
+       0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000,
+       0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00,
+       0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f,
+       0x02140102, 0x28160d05, 0x00000010, 0x001b25a4, 0x63db25a4,
+       0x63db25a4, 0x0c1b25a4, 0x0c1b25a4, 0x0c1b25a4, 0x0c1b25a4,
+       0x63db25a4, 0x0c1b25a4, 0x63db25a4, 0x63db25a4, 0x63db25a4,
+       0x63db25a4, 0x001b25a4, 0x001b25a4, 0x6fdb25a4, 0x00000003,
+       0x00000000, 0x00000300
+};
+
+static const uint32_t rtl8192ce_bb_vals_1t[] = {
+       0x0011800f, 0x00ffdb83, 0x80040000, 0x00000001, 0x0000fc00,
+       0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000,
+       0x01000100, 0x00390004, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x569a569a,
+       0x001b25a4, 0x66e60230, 0x061f0130, 0x00000000, 0x32323200,
+       0x07000700, 0x22004000, 0x00000808, 0x00000000, 0xc0083070,
+       0x000004d5, 0x00000000, 0xccc000c0, 0x00000800, 0xfffffffe,
+       0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000,
+       0x81121111, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f,
+       0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000,
+       0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007,
+       0x48071d40, 0x03a05611, 0x000000e4, 0x6c6c6c6c, 0x08800000,
+       0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994,
+       0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f,
+       0x69543420, 0x43bc0094, 0x69543420, 0x433c0094, 0x00000000,
+       0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x018610db,
+       0x0000001f, 0x00b91612, 0x40000100, 0x20f60000, 0x40000100,
+       0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f,
+       0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427,
+       0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c,
+       0x00080740, 0x00020401, 0x0000907f, 0x20010201, 0xa0633333,
+       0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000,
+       0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064,
+       0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e,
+       0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a,
+       0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000,
+       0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00,
+       0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f,
+       0x02140102, 0x28160d05, 0x00000010, 0x001b25a4, 0x631b25a0,
+       0x631b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0,
+       0x631b25a0, 0x081b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0,
+       0x631b25a0, 0x001b25a0, 0x001b25a0, 0x6b1b25a0, 0x00000003,
+       0x00000000, 0x00000300,
+};
+
+static const uint32_t rtl8192ce_agc_vals[] = {
+       0x7b000001, 0x7b010001, 0x7b020001, 0x7b030001, 0x7b040001,
+       0x7b050001, 0x7a060001, 0x79070001, 0x78080001, 0x77090001,
+       0x760a0001, 0x750b0001, 0x740c0001, 0x730d0001, 0x720e0001,
+       0x710f0001, 0x70100001, 0x6f110001, 0x6e120001, 0x6d130001,
+       0x6c140001, 0x6b150001, 0x6a160001, 0x69170001, 0x68180001,
+       0x67190001, 0x661a0001, 0x651b0001, 0x641c0001, 0x631d0001,
+       0x621e0001, 0x611f0001, 0x60200001, 0x49210001, 0x48220001,
+       0x47230001, 0x46240001, 0x45250001, 0x44260001, 0x43270001,
+       0x42280001, 0x41290001, 0x402a0001, 0x262b0001, 0x252c0001,
+       0x242d0001, 0x232e0001, 0x222f0001, 0x21300001, 0x20310001,
+       0x06320001, 0x05330001, 0x04340001, 0x03350001, 0x02360001,
+       0x01370001, 0x00380001, 0x00390001, 0x003a0001, 0x003b0001,
+       0x003c0001, 0x003d0001, 0x003e0001, 0x003f0001, 0x7b400001,
+       0x7b410001, 0x7b420001, 0x7b430001, 0x7b440001, 0x7b450001,
+       0x7a460001, 0x79470001, 0x78480001, 0x77490001, 0x764a0001,
+       0x754b0001, 0x744c0001, 0x734d0001, 0x724e0001, 0x714f0001,
+       0x70500001, 0x6f510001, 0x6e520001, 0x6d530001, 0x6c540001,
+       0x6b550001, 0x6a560001, 0x69570001, 0x68580001, 0x67590001,
+       0x665a0001, 0x655b0001, 0x645c0001, 0x635d0001, 0x625e0001,
+       0x615f0001, 0x60600001, 0x49610001, 0x48620001, 0x47630001,
+       0x46640001, 0x45650001, 0x44660001, 0x43670001, 0x42680001,
+       0x41690001, 0x406a0001, 0x266b0001, 0x256c0001, 0x246d0001,
+       0x236e0001, 0x226f0001, 0x21700001, 0x20710001, 0x06720001,
+       0x05730001, 0x04740001, 0x03750001, 0x02760001, 0x01770001,
+       0x00780001, 0x00790001, 0x007a0001, 0x007b0001, 0x007c0001,
+       0x007d0001, 0x007e0001, 0x007f0001, 0x3800001e, 0x3801001e,
+       0x3802001e, 0x3803001e, 0x3804001e, 0x3805001e, 0x3806001e,
+       0x3807001e, 0x3808001e, 0x3c09001e, 0x3e0a001e, 0x400b001e,
+       0x440c001e, 0x480d001e, 0x4c0e001e, 0x500f001e, 0x5210001e,
+       0x5611001e, 0x5a12001e, 0x5e13001e, 0x6014001e, 0x6015001e,
+       0x6016001e, 0x6217001e, 0x6218001e, 0x6219001e, 0x621a001e,
+       0x621b001e, 0x621c001e, 0x621d001e, 0x621e001e, 0x621f001e
+};
+
+static const struct rtwn_bb_prog rtl8192ce_bb_prog_2t = {
+       nitems(rtl8192ce_bb_regs),
+       rtl8192ce_bb_regs,
+       rtl8192ce_bb_vals_2t,
+       nitems(rtl8192ce_agc_vals),
+       rtl8192ce_agc_vals
+};
+
+static const struct rtwn_bb_prog rtl8192ce_bb_prog_1t = {
+       nitems(rtl8192ce_bb_regs),
+       rtl8192ce_bb_regs,
+       rtl8192ce_bb_vals_1t,
+       nitems(rtl8192ce_agc_vals),
+       rtl8192ce_agc_vals
+};
+
+/*
+ * RTL8188CU.
+ */
+static const uint32_t rtl8192cu_bb_vals[] = {
+       0x0011800d, 0x00ffdb83, 0x80040002, 0x00000003, 0x0000fc00,
+       0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000,
+       0x01000100, 0x00390004, 0x01000100, 0x00390004, 0x27272727,
+       0x27272727, 0x27272727, 0x27272727, 0x00010000, 0x00010000,
+       0x27272727, 0x27272727, 0x00000000, 0x00000000, 0x569a569a,
+       0x0c1b25a4, 0x66e60230, 0x061f0130, 0x27272727, 0x2b2b2b27,
+       0x07000700, 0x22184000, 0x08080808, 0x00000000, 0xc0083070,
+       0x000004d5, 0x00000000, 0xcc0000c0, 0x00000800, 0xfffffffe,
+       0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000,
+       0x81121313, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f,
+       0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000,
+       0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007,
+       0x48071d40, 0x03a05633, 0x000000e4, 0x6c6c6c6c, 0x08800000,
+       0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994,
+       0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f,
+       0x6954341e, 0x43bc0094, 0x6954341e, 0x433c0094, 0x00000000,
+       0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x0186115b,
+       0x0000001f, 0x00b99612, 0x40000100, 0x20f60000, 0x40000100,
+       0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f,
+       0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427,
+       0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c,
+       0x00080740, 0x00020403, 0x0000907f, 0x20010201, 0xa0633333,
+       0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000,
+       0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064,
+       0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e,
+       0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a,
+       0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000,
+       0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00,
+       0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f,
+       0x02140102, 0x28160d05, 0x00000010, 0x001b25a4, 0x63db25a4,
+       0x63db25a4, 0x0c1b25a4, 0x0c1b25a4, 0x0c1b25a4, 0x0c1b25a4,
+       0x63db25a4, 0x0c1b25a4, 0x63db25a4, 0x63db25a4, 0x63db25a4,
+       0x63db25a4, 0x001b25a4, 0x001b25a4, 0x6fdb25a4, 0x00000003,
+       0x00000000, 0x00000300
+};
+
+static const struct rtwn_bb_prog rtl8192cu_bb_prog = {
+       nitems(rtl8192ce_bb_regs),
+       rtl8192ce_bb_regs,
+       rtl8192cu_bb_vals,
+       nitems(rtl8192ce_agc_vals),
+       rtl8192ce_agc_vals
+};
+
+/*
+ * RTL8188CE-VAU.
+ */
+static const uint32_t rtl8188ce_bb_vals[] = {
+       0x0011800d, 0x00ffdb83, 0x80040000, 0x00000001, 0x0000fc00,
+       0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000,
+       0x01000100, 0x00390004, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x569a569a,
+       0x001b25a4, 0x66e60230, 0x061f0130, 0x00000000, 0x32323200,
+       0x07000700, 0x22004000, 0x00000808, 0x00000000, 0xc0083070,
+       0x000004d5, 0x00000000, 0xccc000c0, 0x00000800, 0xfffffffe,
+       0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000,
+       0x81121111, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f,
+       0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000,
+       0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007,
+       0x48071d40, 0x03a05611, 0x000000e4, 0x6c6c6c6c, 0x08800000,
+       0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994,
+       0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f,
+       0x6954341e, 0x43bc0094, 0x6954341e, 0x433c0094, 0x00000000,
+       0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x018610db,
+       0x0000001f, 0x00b91612, 0x40000100, 0x20f60000, 0x40000100,
+       0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f,
+       0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427,
+       0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c,
+       0x00080740, 0x00020401, 0x0000907f, 0x20010201, 0xa0633333,
+       0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000,
+       0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064,
+       0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e,
+       0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a,
+       0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000,
+       0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00,
+       0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f,
+       0x02140102, 0x28160d05, 0x00000008, 0x001b25a4, 0x631b25a0,
+       0x631b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0,
+       0x631b25a0, 0x081b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0,
+       0x631b25a0, 0x001b25a0, 0x001b25a0, 0x6b1b25a0, 0x00000003,
+       0x00000000, 0x00000300
+};
+
+static const uint32_t rtl8188ce_agc_vals[] = {
+       0x7b000001, 0x7b010001, 0x7b020001, 0x7b030001, 0x7b040001,
+       0x7b050001, 0x7a060001, 0x79070001, 0x78080001, 0x77090001,
+       0x760a0001, 0x750b0001, 0x740c0001, 0x730d0001, 0x720e0001,
+       0x710f0001, 0x70100001, 0x6f110001, 0x6e120001, 0x6d130001,
+       0x6c140001, 0x6b150001, 0x6a160001, 0x69170001, 0x68180001,
+       0x67190001, 0x661a0001, 0x651b0001, 0x641c0001, 0x631d0001,
+       0x621e0001, 0x611f0001, 0x60200001, 0x49210001, 0x48220001,
+       0x47230001, 0x46240001, 0x45250001, 0x44260001, 0x43270001,
+       0x42280001, 0x41290001, 0x402a0001, 0x262b0001, 0x252c0001,
+       0x242d0001, 0x232e0001, 0x222f0001, 0x21300001, 0x20310001,
+       0x06320001, 0x05330001, 0x04340001, 0x03350001, 0x02360001,
+       0x01370001, 0x00380001, 0x00390001, 0x003a0001, 0x003b0001,
+       0x003c0001, 0x003d0001, 0x003e0001, 0x003f0001, 0x7b400001,
+       0x7b410001, 0x7b420001, 0x7b430001, 0x7b440001, 0x7b450001,
+       0x7a460001, 0x79470001, 0x78480001, 0x77490001, 0x764a0001,
+       0x754b0001, 0x744c0001, 0x734d0001, 0x724e0001, 0x714f0001,
+       0x70500001, 0x6f510001, 0x6e520001, 0x6d530001, 0x6c540001,
+       0x6b550001, 0x6a560001, 0x69570001, 0x68580001, 0x67590001,
+       0x665a0001, 0x655b0001, 0x645c0001, 0x635d0001, 0x625e0001,
+       0x615f0001, 0x60600001, 0x49610001, 0x48620001, 0x47630001,
+       0x46640001, 0x45650001, 0x44660001, 0x43670001, 0x42680001,
+       0x41690001, 0x406a0001, 0x266b0001, 0x256c0001, 0x246d0001,
+       0x236e0001, 0x226f0001, 0x21700001, 0x20710001, 0x06720001,
+       0x05730001, 0x04740001, 0x03750001, 0x02760001, 0x01770001,
+       0x00780001, 0x00790001, 0x007a0001, 0x007b0001, 0x007c0001,
+       0x007d0001, 0x007e0001, 0x007f0001, 0x3800001e, 0x3801001e,
+       0x3802001e, 0x3803001e, 0x3804001e, 0x3805001e, 0x3806001e,
+       0x3807001e, 0x3808001e, 0x3c09001e, 0x3e0a001e, 0x400b001e,
+       0x440c001e, 0x480d001e, 0x4c0e001e, 0x500f001e, 0x5210001e,
+       0x5611001e, 0x5a12001e, 0x5e13001e, 0x6014001e, 0x6015001e,
+       0x6016001e, 0x6217001e, 0x6218001e, 0x6219001e, 0x621a001e,
+       0x621b001e, 0x621c001e, 0x621d001e, 0x621e001e, 0x621f001e
+};
+
+static const struct rtwn_bb_prog rtl8188ce_bb_prog = {
+       nitems(rtl8192ce_bb_regs),
+       rtl8192ce_bb_regs,
+       rtl8188ce_bb_vals,
+       nitems(rtl8188ce_agc_vals),
+       rtl8188ce_agc_vals
+};
+
+static const uint32_t rtl8188cu_bb_vals[] = {
+       0x0011800d, 0x00ffdb83, 0x80040000, 0x00000001, 0x0000fc00,
+       0x0000000a, 0x10005388, 0x020c3d10, 0x02200385, 0x00000000,
+       0x01000100, 0x00390004, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x569a569a,
+       0x001b25a4, 0x66e60230, 0x061f0130, 0x00000000, 0x32323200,
+       0x07000700, 0x22004000, 0x00000808, 0x00000000, 0xc0083070,
+       0x000004d5, 0x00000000, 0xccc000c0, 0x00000800, 0xfffffffe,
+       0x40302010, 0x00706050, 0x00000000, 0x00000023, 0x00000000,
+       0x81121111, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e68120f,
+       0x9500bb78, 0x11144028, 0x00881117, 0x89140f00, 0x1a1b0000,
+       0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007,
+       0x48071d40, 0x03a05611, 0x000000e4, 0x6c6c6c6c, 0x08800000,
+       0x40000100, 0x08800000, 0x40000100, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf, 0x49795994,
+       0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f,
+       0x6954341e, 0x43bc0094, 0x6954341e, 0x433c0094, 0x00000000,
+       0x5116848b, 0x47c00bff, 0x00000036, 0x2c7f000d, 0x018610db,
+       0x0000001f, 0x00b91612, 0x40000100, 0x20f60000, 0x40000100,
+       0x20200000, 0x00121820, 0x00000000, 0x00121820, 0x00007f7f,
+       0x00000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x28000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x64b22427,
+       0x00766932, 0x00222222, 0x00000000, 0x37644302, 0x2f97d40c,
+       0x00080740, 0x00020401, 0x0000907f, 0x20010201, 0xa0633333,
+       0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000, 0x80608000,
+       0x00000000, 0x00027293, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x6437140a, 0x00000000, 0x00000000, 0x30032064,
+       0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e,
+       0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a, 0x03902a2a,
+       0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x00000000,
+       0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00,
+       0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f,
+       0x02140102, 0x28160d05, 0x00000008, 0x001b25a4, 0x631b25a0,
+       0x631b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0,
+       0x631b25a0, 0x081b25a0, 0x631b25a0, 0x631b25a0, 0x631b25a0,
+       0x631b25a0, 0x001b25a0, 0x001b25a0, 0x6b1b25a0, 0x00000003,
+       0x00000000, 0x00000300
+};
+
+static const struct rtwn_bb_prog rtl8188cu_bb_prog = {
+       nitems(rtl8192ce_bb_regs),
+       rtl8192ce_bb_regs,
+       rtl8188cu_bb_vals,
+       nitems(rtl8188ce_agc_vals),
+       rtl8188ce_agc_vals
+};
+
+/*
+ * RTL8188RU.
+ */
+static const uint16_t rtl8188ru_bb_regs[] = {
+       0x024, 0x028, 0x040, 0x800, 0x804, 0x808, 0x80c, 0x810, 0x814,
+       0x818, 0x81c, 0x820, 0x824, 0x828, 0x82c, 0x830, 0x834, 0x838,
+       0x83c, 0x840, 0x844, 0x848, 0x84c, 0x850, 0x854, 0x858, 0x85c,
+       0x860, 0x864, 0x868, 0x86c, 0x870, 0x874, 0x878, 0x87c, 0x880,
+       0x884, 0x888, 0x88c, 0x890, 0x894, 0x898, 0x89c, 0x900, 0x904,
+       0x908, 0x90c, 0xa00, 0xa04, 0xa08, 0xa0c, 0xa10, 0xa14, 0xa18,
+       0xa1c, 0xa20, 0xa24, 0xa28, 0xa2c, 0xa70, 0xa74, 0xc00, 0xc04,
+       0xc08, 0xc0c, 0xc10, 0xc14, 0xc18, 0xc1c, 0xc20, 0xc24, 0xc28,
+       0xc2c, 0xc30, 0xc34, 0xc38, 0xc3c, 0xc40, 0xc44, 0xc48, 0xc4c,
+       0xc50, 0xc54, 0xc58, 0xc5c, 0xc60, 0xc64, 0xc68, 0xc6c, 0xc70,
+       0xc74, 0xc78, 0xc7c, 0xc80, 0xc84, 0xc88, 0xc8c, 0xc90, 0xc94,
+       0xc98, 0xc9c, 0xca0, 0xca4, 0xca8, 0xcac, 0xcb0, 0xcb4, 0xcb8,
+       0xcbc, 0xcc0, 0xcc4, 0xcc8, 0xccc, 0xcd0, 0xcd4, 0xcd8, 0xcdc,
+       0xce0, 0xce4, 0xce8, 0xcec, 0xd00, 0xd04, 0xd08, 0xd0c, 0xd10,
+       0xd14, 0xd18, 0xd2c, 0xd30, 0xd34, 0xd38, 0xd3c, 0xd40, 0xd44,
+       0xd48, 0xd4c, 0xd50, 0xd54, 0xd58, 0xd5c, 0xd60, 0xd64, 0xd68,
+       0xd6c, 0xd70, 0xd74, 0xd78, 0xe00, 0xe04, 0xe08, 0xe10, 0xe14,
+       0xe18, 0xe1c, 0xe28, 0xe30, 0xe34, 0xe38, 0xe3c, 0xe40, 0xe44,
+       0xe48, 0xe4c, 0xe50, 0xe54, 0xe58, 0xe5c, 0xe60, 0xe68, 0xe6c,
+       0xe70, 0xe74, 0xe78, 0xe7c, 0xe80, 0xe84, 0xe88, 0xe8c, 0xed0,
+       0xed4, 0xed8, 0xedc, 0xee0, 0xeec, 0xee8, 0xf14, 0xf4c, 0xf00
+};
+
+static const uint32_t rtl8188ru_bb_vals[] = {
+       0x0011800d, 0x00ffdb83, 0x000c0004, 0x80040000, 0x00000001,
+       0x0000fc00, 0x0000000a, 0x10005388, 0x020c3d10, 0x02200385,
+       0x00000000, 0x01000100, 0x00390204, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x569a569a, 0x001b25a4, 0x66e60230, 0x061f0130, 0x00000000,
+       0x32323200, 0x03000300, 0x22004000, 0x00000808, 0x00ffc3f1,
+       0xc0083070, 0x000004d5, 0x00000000, 0xccc000c0, 0x00000800,
+       0xfffffffe, 0x40302010, 0x00706050, 0x00000000, 0x00000023,
+       0x00000000, 0x81121111, 0x00d047c8, 0x80ff000c, 0x8c838300,
+       0x2e68120f, 0x9500bb78, 0x11144028, 0x00881117, 0x89140f00,
+       0x15160000, 0x070b0f12, 0x00000104, 0x00d30000, 0x101fbf00,
+       0x00000007, 0x48071d40, 0x03a05611, 0x000000e4, 0x6c6c6c6c,
+       0x08800000, 0x40000100, 0x08800000, 0x40000100, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x69e9ac44, 0x469652cf,
+       0x49795994, 0x0a97971c, 0x1f7c403f, 0x000100b7, 0xec020107,
+       0x007f037f, 0x6954342e, 0x43bc0094, 0x6954342f, 0x433c0094,
+       0x00000000, 0x5116848b, 0x47c00bff, 0x00000036, 0x2c56000d,
+       0x018610db, 0x0000001f, 0x00b91612, 0x24000090, 0x20f60000,
+       0x24000090, 0x20200000, 0x00121820, 0x00000000, 0x00121820,
+       0x00007f7f, 0x00000000, 0x00000080, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x28000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x64b22427, 0x00766932, 0x00222222, 0x00000000, 0x37644302,
+       0x2f97d40c, 0x00080740, 0x00020401, 0x0000907f, 0x20010201,
+       0xa0633333, 0x3333bc43, 0x7a8f5b6b, 0xcc979975, 0x00000000,
+       0x80608000, 0x00000000, 0x00027293, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x6437140a, 0x00000000, 0x00000000,
+       0x30032064, 0x4653de68, 0x04518a3c, 0x00002101, 0x2a201c16,
+       0x1812362e, 0x322c2220, 0x000e3c24, 0x2a2a2a2a, 0x2a2a2a2a,
+       0x03902a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a, 0x2a2a2a2a,
+       0x00000000, 0x1000dc1f, 0x10008c1f, 0x02140102, 0x681604c2,
+       0x01007c00, 0x01004800, 0xfb000000, 0x000028d1, 0x1000dc1f,
+       0x10008c1f, 0x02140102, 0x28160d05, 0x00000010, 0x001b25a4,
+       0x631b25a0, 0x631b25a0, 0x081b25a0, 0x081b25a0, 0x081b25a0,
+       0x081b25a0, 0x631b25a0, 0x081b25a0, 0x631b25a0, 0x631b25a0,
+       0x631b25a0, 0x631b25a0, 0x001b25a0, 0x001b25a0, 0x6b1b25a0,
+       0x31555448, 0x00000003, 0x00000000, 0x00000300
+};
+
+static const uint32_t rtl8188ru_agc_vals[] = {
+       0x7b000001, 0x7b010001, 0x7b020001, 0x7b030001, 0x7b040001,
+       0x7b050001, 0x7b060001, 0x7b070001, 0x7b080001, 0x7a090001,
+       0x790a0001, 0x780b0001, 0x770c0001, 0x760d0001, 0x750e0001,
+       0x740f0001, 0x73100001, 0x72110001, 0x71120001, 0x70130001,
+       0x6f140001, 0x6e150001, 0x6d160001, 0x6c170001, 0x6b180001,
+       0x6a190001, 0x691a0001, 0x681b0001, 0x671c0001, 0x661d0001,
+       0x651e0001, 0x641f0001, 0x63200001, 0x62210001, 0x61220001,
+       0x60230001, 0x46240001, 0x45250001, 0x44260001, 0x43270001,
+       0x42280001, 0x41290001, 0x402a0001, 0x262b0001, 0x252c0001,
+       0x242d0001, 0x232e0001, 0x222f0001, 0x21300001, 0x20310001,
+       0x06320001, 0x05330001, 0x04340001, 0x03350001, 0x02360001,
+       0x01370001, 0x00380001, 0x00390001, 0x003a0001, 0x003b0001,
+       0x003c0001, 0x003d0001, 0x003e0001, 0x003f0001, 0x7b400001,
+       0x7b410001, 0x7b420001, 0x7b430001, 0x7b440001, 0x7b450001,
+       0x7b460001, 0x7b470001, 0x7b480001, 0x7a490001, 0x794a0001,
+       0x784b0001, 0x774c0001, 0x764d0001, 0x754e0001, 0x744f0001,
+       0x73500001, 0x72510001, 0x71520001, 0x70530001, 0x6f540001,
+       0x6e550001, 0x6d560001, 0x6c570001, 0x6b580001, 0x6a590001,
+       0x695a0001, 0x685b0001, 0x675c0001, 0x665d0001, 0x655e0001,
+       0x645f0001, 0x63600001, 0x62610001, 0x61620001, 0x60630001,
+       0x46640001, 0x45650001, 0x44660001, 0x43670001, 0x42680001,
+       0x41690001, 0x406a0001, 0x266b0001, 0x256c0001, 0x246d0001,
+       0x236e0001, 0x226f0001, 0x21700001, 0x20710001, 0x06720001,
+       0x05730001, 0x04740001, 0x03750001, 0x02760001, 0x01770001,
+       0x00780001, 0x00790001, 0x007a0001, 0x007b0001, 0x007c0001,
+       0x007d0001, 0x007e0001, 0x007f0001, 0x3800001e, 0x3801001e,
+       0x3802001e, 0x3803001e, 0x3804001e, 0x3805001e, 0x3806001e,
+       0x3807001e, 0x3808001e, 0x3c09001e, 0x3e0a001e, 0x400b001e,
+       0x440c001e, 0x480d001e, 0x4c0e001e, 0x500f001e, 0x5210001e,
+       0x5611001e, 0x5a12001e, 0x5e13001e, 0x6014001e, 0x6015001e,
+       0x6016001e, 0x6217001e, 0x6218001e, 0x6219001e, 0x621a001e,
+       0x621b001e, 0x621c001e, 0x621d001e, 0x621e001e, 0x621f001e
+};
+
+static const struct rtwn_bb_prog rtl8188ru_bb_prog = {
+       nitems(rtl8188ru_bb_regs),
+       rtl8188ru_bb_regs,
+       rtl8188ru_bb_vals,
+       nitems(rtl8188ru_agc_vals),
+       rtl8188ru_agc_vals
+};
+
+/*
+ * RF initialization values.
+ */
+struct rtwn_rf_prog {
+       int             count;
+       const uint8_t   *regs;
+       const uint32_t  *vals;
+};
+
+/*
+ * RTL8192CU and RTL8192CE-VAU.
+ */
+static const uint8_t rtl8192ce_rf1_regs[] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+       0x0f, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
+       0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2a, 0x2b,
+       0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b,
+       0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b,
+       0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a,
+       0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c,
+       0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2b, 0x2b,
+       0x2c, 0x2a, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x10,
+       0x11, 0x10, 0x11, 0x10, 0x11, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13,
+       0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14,
+       0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x00,
+       0x18, 0xfe, 0xfe, 0x1f, 0xfe, 0xfe, 0x1e, 0x1f, 0x00
+};
+
+static const uint32_t rtl8192ce_rf1_vals[] = {
+       0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb1,
+       0x54867, 0x8992e, 0x0e52c, 0x39ce7, 0x00451, 0x00000, 0x10255,
+       0x60a00, 0xfc378, 0xa1250, 0x4445f, 0x80001, 0x0b614, 0x6c000,
+       0x00000, 0x01558, 0x00060, 0x00483, 0x4f000, 0xec7d9, 0x577c0,
+       0x04783, 0x00001, 0x21334, 0x00000, 0x00054, 0x00001, 0x00808,
+       0x53333, 0x0000c, 0x00002, 0x00808, 0x5b333, 0x0000d, 0x00003,
+       0x00808, 0x63333, 0x0000d, 0x00004, 0x00808, 0x6b333, 0x0000d,
+       0x00005, 0x00808, 0x73333, 0x0000d, 0x00006, 0x00709, 0x5b333,
+       0x0000d, 0x00007, 0x00709, 0x63333, 0x0000d, 0x00008, 0x0060a,
+       0x4b333, 0x0000d, 0x00009, 0x0060a, 0x53333, 0x0000d, 0x0000a,
+       0x0060a, 0x5b333, 0x0000d, 0x0000b, 0x0060a, 0x63333, 0x0000d,
+       0x0000c, 0x0060a, 0x6b333, 0x0000d, 0x0000d, 0x0060a, 0x73333,
+       0x0000d, 0x0000e, 0x0050b, 0x66666, 0x0001a, 0xe0000, 0x4000f,
+       0xe31fc, 0x6000f, 0xff9f8, 0x2000f, 0x203f9, 0x3000f, 0xff500,
+       0x00000, 0x00000, 0x8000f, 0x3f100, 0x9000f, 0x23100, 0x32000,
+       0x71000, 0xb0000, 0xfc000, 0x287af, 0x244b7, 0x204ab, 0x1c49f,
+       0x18493, 0x14297, 0x10295, 0x0c298, 0x0819c, 0x040a8, 0x0001c,
+       0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f424, 0x4f424, 0x8f424,
+       0xcf424, 0xe0330, 0xa0330, 0x60330, 0x20330, 0x10159, 0x0f401,
+       0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x44457, 0x80000,
+       0x30159
+};
+
+static const uint8_t rtl8192ce_rf2_regs[] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+       0x0f, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+       0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15,
+       0x15, 0x15, 0x16, 0x16, 0x16, 0x16
+};
+
+static const uint32_t rtl8192ce_rf2_vals[] = {
+       0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb1,
+       0x54867, 0x8992e, 0x0e52c, 0x39ce7, 0x00451, 0x32000, 0x71000,
+       0xb0000, 0xfc000, 0x287af, 0x244b7, 0x204ab, 0x1c49f, 0x18493,
+       0x14297, 0x10295, 0x0c298, 0x0819c, 0x040a8, 0x0001c, 0x1944c,
+       0x59444, 0x9944c, 0xd9444, 0x0f424, 0x4f424, 0x8f424, 0xcf424,
+       0xe0330, 0xa0330, 0x60330, 0x20330
+};
+
+static const struct rtwn_rf_prog rtl8192ce_rf_prog[] = {
+       {
+               nitems(rtl8192ce_rf1_regs),
+               rtl8192ce_rf1_regs,
+               rtl8192ce_rf1_vals
+       },
+       {
+               nitems(rtl8192ce_rf2_regs),
+               rtl8192ce_rf2_regs,
+               rtl8192ce_rf2_vals
+       }
+};
+
+/*
+ * RTL8188CE-VAU.
+ */
+static const uint32_t rtl8188ce_rf_vals[] = {
+       0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb1,
+       0x54867, 0x8992e, 0x0e52c, 0x39ce7, 0x00451, 0x00000, 0x10255,
+       0x60a00, 0xfc378, 0xa1250, 0x4445f, 0x80001, 0x0b614, 0x6c000,
+       0x00000, 0x01558, 0x00060, 0x00483, 0x4f200, 0xec7d9, 0x577c0,
+       0x04783, 0x00001, 0x21334, 0x00000, 0x00054, 0x00001, 0x00808,
+       0x53333, 0x0000c, 0x00002, 0x00808, 0x5b333, 0x0000d, 0x00003,
+       0x00808, 0x63333, 0x0000d, 0x00004, 0x00808, 0x6b333, 0x0000d,
+       0x00005, 0x00808, 0x73333, 0x0000d, 0x00006, 0x00709, 0x5b333,
+       0x0000d, 0x00007, 0x00709, 0x63333, 0x0000d, 0x00008, 0x0060a,
+       0x4b333, 0x0000d, 0x00009, 0x0060a, 0x53333, 0x0000d, 0x0000a,
+       0x0060a, 0x5b333, 0x0000d, 0x0000b, 0x0060a, 0x63333, 0x0000d,
+       0x0000c, 0x0060a, 0x6b333, 0x0000d, 0x0000d, 0x0060a, 0x73333,
+       0x0000d, 0x0000e, 0x0050b, 0x66666, 0x0001a, 0xe0000, 0x4000f,
+       0xe31fc, 0x6000f, 0xff9f8, 0x2000f, 0x203f9, 0x3000f, 0xff500,
+       0x00000, 0x00000, 0x8000f, 0x3f100, 0x9000f, 0x23100, 0x32000,
+       0x71000, 0xb0000, 0xfc000, 0x287b3, 0x244b7, 0x204ab, 0x1c49f,
+       0x18493, 0x1429b, 0x10299, 0x0c29c, 0x081a0, 0x040ac, 0x00020,
+       0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f424, 0x4f424, 0x8f424,
+       0xcf424, 0xe0330, 0xa0330, 0x60330, 0x20330, 0x10159, 0x0f401,
+       0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x44457, 0x80000,
+       0x30159
+};
+
+static const struct rtwn_rf_prog rtl8188ce_rf_prog[] = {
+       {
+               nitems(rtl8192ce_rf1_regs),
+               rtl8192ce_rf1_regs,
+               rtl8188ce_rf_vals
+       }
+};
+
+
+/*
+ * RTL8188CU.
+ */
+static const uint32_t rtl8188cu_rf_vals[] = {
+       0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb1,
+       0x54867, 0x8992e, 0x0e52c, 0x39ce7, 0x00451, 0x00000, 0x10255,
+       0x60a00, 0xfc378, 0xa1250, 0x4445f, 0x80001, 0x0b614, 0x6c000,
+       0x00000, 0x01558, 0x00060, 0x00483, 0x4f000, 0xec7d9, 0x577c0,
+       0x04783, 0x00001, 0x21334, 0x00000, 0x00054, 0x00001, 0x00808,
+       0x53333, 0x0000c, 0x00002, 0x00808, 0x5b333, 0x0000d, 0x00003,
+       0x00808, 0x63333, 0x0000d, 0x00004, 0x00808, 0x6b333, 0x0000d,
+       0x00005, 0x00808, 0x73333, 0x0000d, 0x00006, 0x00709, 0x5b333,
+       0x0000d, 0x00007, 0x00709, 0x63333, 0x0000d, 0x00008, 0x0060a,
+       0x4b333, 0x0000d, 0x00009, 0x0060a, 0x53333, 0x0000d, 0x0000a,
+       0x0060a, 0x5b333, 0x0000d, 0x0000b, 0x0060a, 0x63333, 0x0000d,
+       0x0000c, 0x0060a, 0x6b333, 0x0000d, 0x0000d, 0x0060a, 0x73333,
+       0x0000d, 0x0000e, 0x0050b, 0x66666, 0x0001a, 0xe0000, 0x4000f,
+       0xe31fc, 0x6000f, 0xff9f8, 0x2000f, 0x203f9, 0x3000f, 0xff500,
+       0x00000, 0x00000, 0x8000f, 0x3f100, 0x9000f, 0x23100, 0x32000,
+       0x71000, 0xb0000, 0xfc000, 0x287b3, 0x244b7, 0x204ab, 0x1c49f,
+       0x18493, 0x1429b, 0x10299, 0x0c29c, 0x081a0, 0x040ac, 0x00020,
+       0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f405, 0x4f405, 0x8f405,
+       0xcf405, 0xe0330, 0xa0330, 0x60330, 0x20330, 0x10159, 0x0f401,
+       0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x44457, 0x80000,
+       0x30159
+};
+
+static const struct rtwn_rf_prog rtl8188cu_rf_prog[] = {
+       {
+               nitems(rtl8192ce_rf1_regs),
+               rtl8192ce_rf1_regs,
+               rtl8188cu_rf_vals
+       }
+};
+
+/*
+ * RTL8188RU.
+ */
+static const uint32_t rtl8188ru_rf_vals[] = {
+       0x30159, 0x31284, 0x98000, 0x18c63, 0x210e7, 0x2044f, 0x1adb0,
+       0x54867, 0x8992e, 0x0e529, 0x39ce7, 0x00451, 0x00000, 0x00255,
+       0x60a00, 0xfc378, 0xa1250, 0x4445f, 0x80001, 0x0b614, 0x6c000,
+       0x0083c, 0x01558, 0x00060, 0x00483, 0x4f000, 0xec7d9, 0x977c0,
+       0x04783, 0x00001, 0x21334, 0x00000, 0x00054, 0x00001, 0x00808,
+       0x53333, 0x0000c, 0x00002, 0x00808, 0x5b333, 0x0000d, 0x00003,
+       0x00808, 0x63333, 0x0000d, 0x00004, 0x00808, 0x6b333, 0x0000d,
+       0x00005, 0x00808, 0x73333, 0x0000d, 0x00006, 0x00709, 0x5b333,
+       0x0000d, 0x00007, 0x00709, 0x63333, 0x0000d, 0x00008, 0x0060a,
+       0x4b333, 0x0000d, 0x00009, 0x0060a, 0x53333, 0x0000d, 0x0000a,
+       0x0060a, 0x5b333, 0x0000d, 0x0000b, 0x0060a, 0x63333, 0x0000d,
+       0x0000c, 0x0060a, 0x6b333, 0x0000d, 0x0000d, 0x0060a, 0x73333,
+       0x0000d, 0x0000e, 0x0050b, 0x66666, 0x0001a, 0xe0000, 0x4000f,
+       0xe31fc, 0x6000f, 0xff9f8, 0x2000f, 0x203f9, 0x3000f, 0xff500,
+       0x00000, 0x00000, 0x8000f, 0x3f100, 0x9000f, 0x23100, 0xd8000,
+       0x90000, 0x51000, 0x12000, 0x28fb4, 0x24fa8, 0x207a4, 0x1c798,
+       0x183a4, 0x14398, 0x101a4, 0x0c198, 0x080a4, 0x04098, 0x00014,
+       0x1944c, 0x59444, 0x9944c, 0xd9444, 0x0f405, 0x4f405, 0x8f405,
+       0xcf405, 0xe0330, 0xa0330, 0x60330, 0x20330, 0x10159, 0x0f401,
+       0x00000, 0x00000, 0x80003, 0x00000, 0x00000, 0x44457, 0x80000,
+       0x30159
+};
+
+static const struct rtwn_rf_prog rtl8188ru_rf_prog[] = {
+       {
+               nitems(rtl8192ce_rf1_regs),
+               rtl8192ce_rf1_regs,
+               rtl8188ru_rf_vals
+       }
+};
+
+struct rtwn_txpwr {
+       uint8_t pwr[3][28];
+};
+
+/*
+ * Per RF chain/group/rate Tx gain values.
+ */
+static const struct rtwn_txpwr rtl8192cu_txagc[] = {
+       { {     /* Chain 0. */
+       {       /* Group 0. */
+       0x00, 0x00, 0x00, 0x00,                         /* CCK1~11. */
+       0x0c, 0x0c, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, /* OFDM6~54. */
+       0x0e, 0x0d, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, /* MCS0~7. */
+       0x0e, 0x0d, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02  /* MCS8~15. */
+       },
+       {       /* Group 1. */
+       0x00, 0x00, 0x00, 0x00,                         /* CCK1~11. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  /* MCS8~15. */
+       },
+       {       /* Group 2. */
+       0x00, 0x00, 0x00, 0x00,                         /* CCK1~11. */
+       0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x00, /* OFDM6~54. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  /* MCS8~15. */
+       }
+       } },
+       { {     /* Chain 1. */
+       {       /* Group 0. */
+       0x00, 0x00, 0x00, 0x00,                         /* CCK1~11. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  /* MCS8~15. */
+       },
+       {       /* Group 1. */
+       0x00, 0x00, 0x00, 0x00,                         /* CCK1~11. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  /* MCS8~15. */
+       },
+       {       /* Group 2. */
+       0x00, 0x00, 0x00, 0x00,                         /* CCK1~11. */
+       0x04, 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x00, /* OFDM6~54. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  /* MCS8~15. */
+       }
+       } }
+};
+
+static const struct rtwn_txpwr rtl8188ru_txagc[] = {
+       { {     /* Chain 0. */
+       {       /* Group 0. */
+       0x00, 0x00, 0x00, 0x00,                         /* CCK1~11. */
+       0x08, 0x08, 0x08, 0x06, 0x06, 0x04, 0x04, 0x00, /* OFDM6~54. */
+       0x08, 0x06, 0x06, 0x04, 0x04, 0x02, 0x02, 0x00, /* MCS0~7. */
+       0x08, 0x06, 0x06, 0x04, 0x04, 0x02, 0x02, 0x00  /* MCS8~15. */
+       },
+       {       /* Group 1. */
+       0x00, 0x00, 0x00, 0x00,                         /* CCK1~11. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  /* MCS8~15. */
+       },
+       {       /* Group 2. */
+       0x00, 0x00, 0x00, 0x00,                         /* CCK1~11. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* OFDM6~54. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MCS0~7. */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  /* MCS8~15. */
+       }
+       } }
+};