kernel/usb4bsd: Sync urtwn(4) with current FreeBSD.
authorSascha Wildner <saw@online.de>
Wed, 30 Jul 2014 21:24:51 +0000 (23:24 +0200)
committerSascha Wildner <saw@online.de>
Wed, 30 Jul 2014 21:24:51 +0000 (23:24 +0200)
This adds support for several (RTL8188EU based) adapters, among other
changes.

It should also fix the hangs we were seeing (using usb_pause_ls() now).

Thanks to Max Herrgaard <herrgaard@gmail.com> for testing it on a
RTL8188EU based adapter and to Christian Koch <cfkoch@sdf.lonestar.org>
for testing it with a RTL8188CU based one (Adafruit USB WiFi).

etc/devd/usb.conf
share/man/man4/urtwn.4
share/man/man4/urtwnfw.4
sys/bus/u4b/usbdevs
sys/bus/u4b/wlan/if_urtwn.c
sys/bus/u4b/wlan/if_urtwnreg.h
sys/bus/u4b/wlan/urtwnfw/Makefile
sys/bus/u4b/wlan/urtwnfw/urtwnrtl8188eu/Makefile [new file with mode: 0644]
sys/conf/files
sys/contrib/dev/urtwn/urtwn-rtl8188eufw.fw.uu [new file with mode: 0644]

index 3975ae6..c22d212 100644 (file)
@@ -1187,6 +1187,14 @@ nomatch 32 {
        action "kldload -n if_run";
 };
 
+nomatch 32 {
+       match "bus" "uhub[0-9]+";
+       match "mode" "host";
+       match "vendor" "0x0b05";
+       match "product" "0x17ba";
+       action "kldload -n if_urtwn";
+};
+
 nomatch 32 {
        match "bus" "uhub[0-9]+";
        match "mode" "host";
@@ -1247,7 +1255,7 @@ nomatch 32 {
        match "bus" "uhub[0-9]+";
        match "mode" "host";
        match "vendor" "0x0bda";
-       match "product" "(0x018a|0x317f|0x8170|0x8176|0x8177|0x8178|0x817a|0x817b|0x817c|0x817d|0x817e|0x818a|0x8754)";
+       match "product" "(0x0179|0x018a|0x317f|0x8170|0x8176|0x8176|0x8177|0x8178|0x8179|0x817a|0x817b|0x817c|0x817d|0x817e|0x818a|0x8754)";
        action "kldload -n if_urtwn";
 };
 
@@ -2835,5 +2843,5 @@ nomatch 32 {
        action "kldload -n umass";
 };
 
-# 1416 USB entries processed
+# 1420 USB entries processed
 
index e83ca24..c97a053 100644 (file)
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.\" $FreeBSD: head/share/man/man4/urtwn.4 257408 2013-10-31 01:57:05Z kevlo $
+.\" $FreeBSD: head/share/man/man4/urtwn.4 267938 2014-06-26 21:46:14Z bapt $
 .\"
-.Dd March 16, 2014
+.Dd July 30, 2014
 .Dt URTWN 4
 .Os
 .Sh NAME
 .Nm urtwn
-.Nd Realtek RTL8188CU/RTL8192CU USB IEEE 802.11b/g/n wireless network device
+.Nd Realtek RTL8188CU/RTL8188EU/RTL8192CU USB IEEE 802.11b/g/n wireless network device
 .Sh SYNOPSIS
 To compile this driver into the kernel,
 place the following lines in your
@@ -51,11 +51,11 @@ legal.realtek.license_ack=1
 The
 .Nm
 driver supports USB 2.0 wireless network devices based on Realtek
-RTL8188CUS, RTL8188CE-VAU, RTL8188RU and RTL8192CU chipsets.
+RTL8188CUS, RTL8188CE-VAU, RTL8188EUS, RTL8188RU and RTL8192CU chipsets.
 .Pp
-The RTL8188CUS is a highly integrated 802.11n adapter that combines
-a MAC, a 1T1R capable baseband and an RF in a single chip.
-It operates in the 2GHz spectrum only.
+The RTL8188CUS and RTL8188EUS are highly integrated 802.11n adapter that
+combine a MAC, a 1T1R capable baseband and an RF in a single chip.
+They operate in the 2GHz spectrum only.
 The RTL8188RU is a high-power variant of the RTL8188CUS.
 The RTL8188CE-VAU is a PCI Express Mini Card adapter that attaches
 to the USB interface.
@@ -81,15 +81,22 @@ must be agreed to by adding the following line to
 firmware license
 .El
 .Sh HARDWARE
-The following adapters should work:
+The
+.Nm
+driver supports Realtek RTL8188CU/RTL8188EU/RTL8192CU based USB
+IEEE 802.11b/g/n wireless network adapters, including:
 .Pp
 .Bl -tag -width Ds -offset indent -compact
+.It Adafruit USB WiFi (802.11b/g/n) Module
+.It ASUS USB-N10 NANO
 .It Belkin F7D1102 Surf Wireless Micro
 .It D-Link DWA-131
 .It Edimax EW-7811Un
 .It Netgear WNA1000M
 .It Realtek RTL8192CU
 .It Realtek RTL8188CUS
+.It TP-LINK TL-WN723N v3
+.It TP-LINK TL-WN725N v2
 .El
 .Sh EXAMPLES
 Join an existing BSS network (i.e., connect to an access point):
index f9c0793..a122ab0 100644 (file)
@@ -1,3 +1,4 @@
+.\"
 .\" Copyright (c) 2013 Kevin Lo
 .\" All rights reserved.
 .\"
@@ -20,9 +21,9 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $FreeBSD: head/share/man/man4/urtwnfw.4 257408 2013-10-31 01:57:05Z kevlo $
+.\" $FreeBSD: head/share/man/man4/urtwnfw.4 264912 2014-04-25 08:01:22Z kevlo $
 .\"
-.Dd March 6, 2014
+.Dd July 30, 2014
 .Dt URTWNFW 4
 .Os
 .Sh NAME
@@ -42,6 +43,7 @@ kernel configuration file:
 .\".Bd -ragged -offset indent
 .\".Cd "device urtwn-rtl8192cfwT"
 .\".Cd "device urtwn-rtl8192cfwU"
+.\".Cd "device urtwn-rtl8188eufw"
 .\".Ed
 .Pp
 Alternatively, to load the driver as a
@@ -50,10 +52,11 @@ module at boot time, place the following line in
 .Bd -literal -offset indent
 urtwn-rtl8192cfwT_load="YES"
 urtwn-rtl8192cfwU_load="YES"
+urtwn-rtl8188eufw_load="YES"
 .Ed
 .Sh DESCRIPTION
 This module provides access to firmware sets for the
-Realtek RTL8188CUS, RTL8188CE-VAU, RTL8188RU and RTL8192CU
+Realtek RTL8188CUS, RTL8188CE-VAU, RTL8188EUS, RTL8188RU and RTL8192CU
 chip based USB WiFi adapters.
 It may be
 statically linked into the kernel, or loaded as a module.
index ef62d0d..5888858 100644 (file)
@@ -1148,6 +1148,7 @@ product ASUS RTL8192CU            0x17ab  RTL8192CU
 product ASUS USBN10            0x1786  USB-N10
 product ASUS RTL8192CU         0x17ab  RTL8192CU
 product ASUS USBN66            0x17ad  USB-N66
+product ASUS USBN10NANO                0x17ba  USB-N10 Nano
 product ASUS RTL8192SU         0x1791  RTL8192SU
 product ASUS A730W             0x4202  ASUS MyPal A730W
 product ASUS P535              0x420f  ASUS P535 PDA
@@ -3633,6 +3634,7 @@ product RATOC REXUSB60F           0xb020  USB serial adapter REX-USB60F
 /* Green House and CompUSA OEM this part */
 product REALTEK DUMMY          0x0000  Dummy product
 product REALTEK USB20CRW       0x0158  USB20CRW Card Reader
+product REALTEK RTL8188ETV     0x0179  RTL8188ETV
 product REALTEK RTL8188CTV      0x018a  RTL8188CTV
 product REALTEK RTL8188RU_2    0x317f  RTL8188RU
 product REALTEK USBKR100       0x8150  USBKR100 USB Ethernet
index 8419c4c..4c84eca 100644 (file)
@@ -2,6 +2,7 @@
 
 /*-
  * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
- * $FreeBSD: src/sys/dev/usb/wlan/if_urtwn.c,v 1.10 2013/07/15 00:33:16 svnexp Exp $
+ * $FreeBSD: head/sys/dev/usb/wlan/if_urtwn.c 268487 2014-07-10 09:42:34Z kevlo $
  */
 
 /*
- * Driver for Realtek RTL8188CE-VAU/RTL8188CUS/RTL8188RU/RTL8192CU.
+ * Driver for Realtek RTL8188CE-VAU/RTL8188CUS/RTL8188EU/RTL8188RU/RTL8192CU.
  */
 
 #include <sys/param.h>
@@ -42,6 +43,7 @@
 
 #include <net/bpf.h>
 #include <net/if.h>
+#include <net/if_var.h>
 #include <net/if_arp.h>
 #include <net/ethernet.h>
 #include <net/if_dl.h>
@@ -83,10 +85,14 @@ SYSCTL_INT(_hw_usb_urtwn, OID_AUTO, debug, CTLFLAG_RW, &urtwn_debug, 0,
 /* various supported device vendors/products */
 static const STRUCT_USB_HOST_ID urtwn_devs[] = {
 #define URTWN_DEV(v,p)  { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) }
+#define        URTWN_RTL8188E_DEV(v,p) \
+       { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, URTWN_RTL8188E) }
+#define URTWN_RTL8188E  1
        URTWN_DEV(ABOCOM,       RTL8188CU_1),
        URTWN_DEV(ABOCOM,       RTL8188CU_2),
        URTWN_DEV(ABOCOM,       RTL8192CU),
        URTWN_DEV(ASUS,         RTL8192CU),
+       URTWN_DEV(ASUS,         USBN10NANO),
        URTWN_DEV(AZUREWAVE,    RTL8188CE_1),
        URTWN_DEV(AZUREWAVE,    RTL8188CE_2),
        URTWN_DEV(AZUREWAVE,    RTL8188CU),
@@ -134,12 +140,17 @@ static const STRUCT_USB_HOST_ID urtwn_devs[] = {
        URTWN_DEV(REALTEK,      RTL8191CU),
        URTWN_DEV(REALTEK,      RTL8192CE),
        URTWN_DEV(REALTEK,      RTL8192CU),
+       URTWN_DEV(REALTEK,      RTL8188CU_0),
        URTWN_DEV(SITECOMEU,    RTL8188CU_1),
        URTWN_DEV(SITECOMEU,    RTL8188CU_2),
        URTWN_DEV(SITECOMEU,    RTL8192CU),
        URTWN_DEV(TRENDNET,     RTL8188CU),
        URTWN_DEV(TRENDNET,     RTL8192CU),
        URTWN_DEV(ZYXEL,        RTL8192CU),
+       /* URTWN_RTL8188E */
+       URTWN_RTL8188E_DEV(REALTEK,     RTL8188ETV),
+       URTWN_RTL8188E_DEV(REALTEK,     RTL8188EU),
+#undef URTWN_RTL8188E_DEV
 #undef URTWN_DEV
 };
 
@@ -184,15 +195,19 @@ static uint16_t           urtwn_read_2(struct urtwn_softc *, uint16_t);
 static uint32_t                urtwn_read_4(struct urtwn_softc *, uint16_t);
 static int             urtwn_fw_cmd(struct urtwn_softc *, uint8_t, 
                            const void *, int);
-static void            urtwn_rf_write(struct urtwn_softc *, int, uint8_t, 
-                           uint32_t);
+static void            urtwn_r92c_rf_write(struct urtwn_softc *, int,
+                           uint8_t, uint32_t);
+static void            urtwn_r88e_rf_write(struct urtwn_softc *, int, 
+                           uint8_t, uint32_t);
 static uint32_t                urtwn_rf_read(struct urtwn_softc *, int, uint8_t);
 static int             urtwn_llt_write(struct urtwn_softc *, uint32_t, 
                            uint32_t);
 static uint8_t         urtwn_efuse_read_1(struct urtwn_softc *, uint16_t);
 static void            urtwn_efuse_read(struct urtwn_softc *);
+static void            urtwn_efuse_switch_power(struct urtwn_softc *);
 static int             urtwn_read_chipid(struct urtwn_softc *);
 static void            urtwn_read_rom(struct urtwn_softc *);
+static void            urtwn_r88e_read_rom(struct urtwn_softc *);
 static int             urtwn_ra_init(struct urtwn_softc *);
 static void            urtwn_tsf_sync_enable(struct urtwn_softc *);
 static void            urtwn_set_led(struct urtwn_softc *, int, int);
@@ -201,20 +216,24 @@ static int                urtwn_newstate(struct ieee80211vap *,
 static void            urtwn_watchdog(void *);
 static void            urtwn_update_avgrssi(struct urtwn_softc *, int, int8_t);
 static int8_t          urtwn_get_rssi(struct urtwn_softc *, int, void *);
+static int8_t          urtwn_r88e_get_rssi(struct urtwn_softc *, int, void *);
 static int             urtwn_tx_start(struct urtwn_softc *,
                            struct ieee80211_node *, struct mbuf *,
                            struct urtwn_data *);
-static void            urtwn_start_locked(struct ifnet *);
 static void            urtwn_start(struct ifnet *, struct ifaltq_subque *);
+static void            urtwn_start_locked(struct ifnet *);
 static int             urtwn_ioctl(struct ifnet *, u_long, caddr_t,
                            struct ucred *);
-static int             urtwn_power_on(struct urtwn_softc *);
+static int             urtwn_r92c_power_on(struct urtwn_softc *);
+static int             urtwn_r88e_power_on(struct urtwn_softc *);
 static int             urtwn_llt_init(struct urtwn_softc *);
 static void            urtwn_fw_reset(struct urtwn_softc *);
+static void            urtwn_r88e_fw_reset(struct urtwn_softc *);
 static int             urtwn_fw_loadpage(struct urtwn_softc *, int, 
                            const uint8_t *, int);
 static int             urtwn_load_firmware(struct urtwn_softc *);
-static int             urtwn_dma_init(struct urtwn_softc *);
+static int             urtwn_r92c_dma_init(struct urtwn_softc *);
+static int             urtwn_r88e_dma_init(struct urtwn_softc *);
 static void            urtwn_mac_init(struct urtwn_softc *);
 static void            urtwn_bb_init(struct urtwn_softc *);
 static void            urtwn_rf_init(struct urtwn_softc *);
@@ -227,6 +246,9 @@ static void         urtwn_write_txpower(struct urtwn_softc *, int,
 static void            urtwn_get_txpower(struct urtwn_softc *, int,
                            struct ieee80211_channel *, 
                            struct ieee80211_channel *, uint16_t[]);
+static void            urtwn_r88e_get_txpower(struct urtwn_softc *, int,
+                           struct ieee80211_channel *, 
+                           struct ieee80211_channel *, uint16_t[]);
 static void            urtwn_set_txpower(struct urtwn_softc *,
                            struct ieee80211_channel *, 
                            struct ieee80211_channel *);
@@ -241,11 +263,12 @@ static void               urtwn_iq_calib(struct urtwn_softc *);
 static void            urtwn_lc_calib(struct urtwn_softc *);
 static void            urtwn_init(void *);
 static void            urtwn_init_locked(void *);
-static void            urtwn_stop(struct ifnet *, int);
-static void            urtwn_stop_locked(struct ifnet *, int);
+static void            urtwn_stop(struct ifnet *);
+static void            urtwn_stop_locked(struct ifnet *);
 static void            urtwn_abort_xfers(struct urtwn_softc *);
 static int             urtwn_raw_xmit(struct ieee80211_node *, struct mbuf *,
                            const struct ieee80211_bpf_params *);
+static void            urtwn_ms_delay(struct urtwn_softc *);
 
 /* Aliases. */
 #define        urtwn_bb_write  urtwn_write_4
@@ -346,6 +369,8 @@ urtwn_attach(device_t self)
        device_set_usb_desc(self);
        sc->sc_udev = uaa->device;
        sc->sc_dev = self;
+       if (USB_GET_DRIVER_INFO(uaa) == URTWN_RTL8188E)
+               sc->chip |= URTWN_CHIP_88E;
 
        lockinit(&sc->sc_lock, device_get_nameunit(self), 0, LK_CANRECURSE);
        callout_init(&sc->sc_watchdog_ch);
@@ -376,10 +401,15 @@ urtwn_attach(device_t self)
                sc->ntxchains = 1;
                sc->nrxchains = 1;
        }
-       urtwn_read_rom(sc);
+
+       if (sc->chip & URTWN_CHIP_88E)
+               urtwn_r88e_read_rom(sc);
+       else
+               urtwn_read_rom(sc);
 
        device_printf(sc->sc_dev, "MAC/BB RTL%s, RF 6052 %dT%dR\n",
            (sc->chip & URTWN_CHIP_92C) ? "8192CU" :
+           (sc->chip & URTWN_CHIP_88E) ? "8188EU" :
            (sc->board_type == R92C_BOARD_TYPE_HIGHPA) ? "8188RU" :
            (sc->board_type == R92C_BOARD_TYPE_MINICARD) ? "8188CE-VAU" :
            "8188CUS", sc->ntxchains, sc->nrxchains);
@@ -456,21 +486,42 @@ urtwn_detach(device_t self)
        struct urtwn_softc *sc = device_get_softc(self);
        struct ifnet *ifp = sc->sc_ifp;
        struct ieee80211com *ic = ifp->if_l2com;
-       
-       if (!device_is_attached(self))
-               return (0);
+       unsigned int x;
 
        wlan_serialize_enter();
-       urtwn_stop(ifp, 1);
+
+       /* Prevent further ioctls. */
+       URTWN_LOCK(sc);
+       sc->sc_flags |= URTWN_DETACHED;
+       URTWN_UNLOCK(sc);
+
+       urtwn_stop(ifp);
 
        callout_stop_sync(&sc->sc_watchdog_ch);
 
-       /* stop all USB transfers */
-       usbd_transfer_unsetup(sc->sc_xfer, URTWN_N_TRANSFER);
-       ieee80211_ifdetach(ic);
+       /* Prevent further allocations from RX/TX data lists. */
+       URTWN_LOCK(sc);
+       STAILQ_INIT(&sc->sc_tx_active);
+       STAILQ_INIT(&sc->sc_tx_inactive);
+       STAILQ_INIT(&sc->sc_tx_pending);
+
+       STAILQ_INIT(&sc->sc_rx_active);
+       STAILQ_INIT(&sc->sc_rx_inactive);
+       URTWN_UNLOCK(sc);
 
+       /* drain USB transfers */
+       for (x = 0; x != URTWN_N_TRANSFER; x++)
+               usbd_transfer_drain(sc->sc_xfer[x]);
+
+       /* Free data buffers. */
+       URTWN_LOCK(sc);
        urtwn_free_tx_list(sc);
        urtwn_free_rx_list(sc);
+       URTWN_UNLOCK(sc);
+
+       /* stop all USB transfers */
+       usbd_transfer_unsetup(sc->sc_xfer, URTWN_N_TRANSFER);
+       ieee80211_ifdetach(ic);
 
        if_free(ifp);
        lockuninit(&sc->sc_lock);
@@ -548,8 +599,13 @@ urtwn_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
            M_80211_VAP, M_INTWAIT | M_ZERO);
        vap = &uvp->vap;
        /* enable s/w bmiss handling for sta mode */
-       ieee80211_vap_setup(ic, vap, name, unit, opmode, 
-           flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
+
+       if (ieee80211_vap_setup(ic, vap, name, unit, opmode, 
+           flags | IEEE80211_CLONE_NOBEACONS, bssid, mac) != 0) {
+               /* out of memory */
+               kfree(uvp, M_80211_VAP);
+               return (NULL);
+       }
 
        /* override state transition machine */
        uvp->newstate = vap->iv_newstate;
@@ -609,7 +665,10 @@ urtwn_rx_frame(struct urtwn_softc *sc, uint8_t *buf, int pktlen, int *rssi_p)
 
        /* Get RSSI from PHY status descriptor if present. */
        if (infosz != 0 && (rxdw0 & R92C_RXDW0_PHYST)) {
-               rssi = urtwn_get_rssi(sc, rate, &stat[1]);
+               if (sc->chip & URTWN_CHIP_88E) 
+                       rssi = urtwn_r88e_get_rssi(sc, rate, &stat[1]);
+               else
+                       rssi = urtwn_get_rssi(sc, rate, &stat[1]);
                /* Update our average RSSI. */
                urtwn_update_avgrssi(sc, rate, rssi);
                /*
@@ -862,7 +921,6 @@ tr_setup:
                }
                STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next);
                STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next);
-
                usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen);
                usbd_transfer_submit(xfer);
 
@@ -1005,7 +1063,7 @@ urtwn_fw_cmd(struct urtwn_softc *sc, uint8_t id, const void *buf, int len)
        for (ntries = 0; ntries < 100; ntries++) {
                if (!(urtwn_read_1(sc, R92C_HMETFR) & (1 << sc->fwcur)))
                        break;
-               DELAY(1);
+               urtwn_ms_delay(sc);
        }
        if (ntries == 100) {
                device_printf(sc->sc_dev,
@@ -1029,14 +1087,31 @@ urtwn_fw_cmd(struct urtwn_softc *sc, uint8_t id, const void *buf, int len)
        return (0);
 }
 
-static void
+static __inline void
 urtwn_rf_write(struct urtwn_softc *sc, int chain, uint8_t addr, uint32_t val)
+{
+
+       sc->sc_rf_write(sc, chain, addr, val);
+}
+
+static void
+urtwn_r92c_rf_write(struct urtwn_softc *sc, int chain, uint8_t addr,
+    uint32_t val)
 {
        urtwn_bb_write(sc, R92C_LSSI_PARAM(chain),
            SM(R92C_LSSI_PARAM_ADDR, addr) |
            SM(R92C_LSSI_PARAM_DATA, val));
 }
 
+static void
+urtwn_r88e_rf_write(struct urtwn_softc *sc, int chain, uint8_t addr,
+uint32_t val)
+{
+       urtwn_bb_write(sc, R92C_LSSI_PARAM(chain),
+           SM(R88E_LSSI_PARAM_ADDR, addr) |
+           SM(R92C_LSSI_PARAM_DATA, val));
+}
+
 static uint32_t
 urtwn_rf_read(struct urtwn_softc *sc, int chain, uint8_t addr)
 {
@@ -1048,16 +1123,16 @@ urtwn_rf_read(struct urtwn_softc *sc, int chain, uint8_t addr)
 
        urtwn_bb_write(sc, R92C_HSSI_PARAM2(0),
            reg[0] & ~R92C_HSSI_PARAM2_READ_EDGE);
-       DELAY(1000);
+       urtwn_ms_delay(sc);
 
        urtwn_bb_write(sc, R92C_HSSI_PARAM2(chain),
            RW(reg[chain], R92C_HSSI_PARAM2_READ_ADDR, addr) |
            R92C_HSSI_PARAM2_READ_EDGE);
-       DELAY(1000);
+       urtwn_ms_delay(sc);
 
        urtwn_bb_write(sc, R92C_HSSI_PARAM2(0),
            reg[0] | R92C_HSSI_PARAM2_READ_EDGE);
-       DELAY(1000);
+       urtwn_ms_delay(sc);
 
        if (urtwn_bb_read(sc, R92C_HSSI_PARAM1(chain)) & R92C_HSSI_PARAM1_PI)
                val = urtwn_bb_read(sc, R92C_HSPI_READBACK(chain));
@@ -1080,7 +1155,7 @@ urtwn_llt_write(struct urtwn_softc *sc, uint32_t addr, uint32_t data)
                if (MS(urtwn_read_4(sc, R92C_LLT_INIT), R92C_LLT_INIT_OP) ==
                    R92C_LLT_INIT_OP_NO_ACTIVE)
                        return (0);
-               DELAY(5);
+               urtwn_ms_delay(sc);
        }
        return (ETIMEDOUT);
 }
@@ -1100,7 +1175,7 @@ urtwn_efuse_read_1(struct urtwn_softc *sc, uint16_t addr)
                reg = urtwn_read_4(sc, R92C_EFUSE_CTRL);
                if (reg & R92C_EFUSE_CTRL_VALID)
                        return (MS(reg, R92C_EFUSE_CTRL_DATA));
-               DELAY(5);
+               urtwn_ms_delay(sc);
        }
        device_printf(sc->sc_dev, 
            "could not read efuse byte at address 0x%x\n", addr);
@@ -1116,22 +1191,8 @@ urtwn_efuse_read(struct urtwn_softc *sc)
        uint8_t off, msk;
        int i;
 
-       reg = urtwn_read_2(sc, R92C_SYS_ISO_CTRL);
-       if (!(reg & R92C_SYS_ISO_CTRL_PWC_EV12V)) {
-               urtwn_write_2(sc, R92C_SYS_ISO_CTRL,
-                   reg | R92C_SYS_ISO_CTRL_PWC_EV12V);
-       }
-       reg = urtwn_read_2(sc, R92C_SYS_FUNC_EN);
-       if (!(reg & R92C_SYS_FUNC_EN_ELDR)) {
-               urtwn_write_2(sc, R92C_SYS_FUNC_EN,
-                   reg | R92C_SYS_FUNC_EN_ELDR);
-       }
-       reg = urtwn_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)) {
-               urtwn_write_2(sc, R92C_SYS_CLKR,
-                   reg | R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M);
-       }
+       urtwn_efuse_switch_power(sc);
+
        memset(&sc->rom, 0xff, sizeof(sc->rom));
        while (addr < 512) {
                reg = urtwn_efuse_read_1(sc, addr);
@@ -1161,12 +1222,37 @@ urtwn_efuse_read(struct urtwn_softc *sc)
        }
 #endif
 }
+static void
+urtwn_efuse_switch_power(struct urtwn_softc *sc)
+{
+       uint32_t reg;
+
+       reg = urtwn_read_2(sc, R92C_SYS_ISO_CTRL);
+       if (!(reg & R92C_SYS_ISO_CTRL_PWC_EV12V)) {
+               urtwn_write_2(sc, R92C_SYS_ISO_CTRL,
+                   reg | R92C_SYS_ISO_CTRL_PWC_EV12V);
+       }
+       reg = urtwn_read_2(sc, R92C_SYS_FUNC_EN);
+       if (!(reg & R92C_SYS_FUNC_EN_ELDR)) {
+               urtwn_write_2(sc, R92C_SYS_FUNC_EN,
+                   reg | R92C_SYS_FUNC_EN_ELDR);
+       }
+       reg = urtwn_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)) {
+               urtwn_write_2(sc, R92C_SYS_CLKR,
+                   reg | R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M);
+       }
+}
 
 static int
 urtwn_read_chipid(struct urtwn_softc *sc)
 {
        uint32_t reg;
 
+       if (sc->chip & URTWN_CHIP_88E)
+               return (0);
+
        reg = urtwn_read_4(sc, R92C_SYS_CFG);
        if (reg & R92C_SYS_CFG_TRP_VAUX_EN)
                return (EIO);
@@ -1203,8 +1289,70 @@ urtwn_read_rom(struct urtwn_softc *sc)
 
        sc->regulatory = MS(rom->rf_opt1, R92C_ROM_RF1_REGULATORY);
        DPRINTF("regulatory type=%d\n", sc->regulatory);
-
        IEEE80211_ADDR_COPY(sc->sc_bssid, rom->macaddr);
+
+       sc->sc_rf_write = urtwn_r92c_rf_write;
+       sc->sc_power_on = urtwn_r92c_power_on;
+       sc->sc_dma_init = urtwn_r92c_dma_init;
+}
+
+static void
+urtwn_r88e_read_rom(struct urtwn_softc *sc)
+{
+       uint8_t *rom = sc->r88e_rom;
+       uint16_t addr = 0;
+       uint32_t reg;
+       uint8_t off, msk, tmp;
+       int i;
+
+       off = 0;
+       urtwn_efuse_switch_power(sc);
+
+       /* Read full ROM image. */
+       memset(&sc->r88e_rom, 0xff, sizeof(sc->r88e_rom));
+       while (addr < 1024) {
+               reg = urtwn_efuse_read_1(sc, addr);
+               if (reg == 0xff)
+                       break;
+               addr++;
+               if ((reg & 0x1f) == 0x0f) {
+                       tmp = (reg & 0xe0) >> 5;
+                       reg = urtwn_efuse_read_1(sc, addr);
+                       if ((reg & 0x0f) != 0x0f)
+                               off = ((reg & 0xf0) >> 1) | tmp;
+                       addr++;
+               } else
+                       off = reg >> 4;
+               msk = reg & 0xf;
+               for (i = 0; i < 4; i++) {
+                       if (msk & (1 << i))
+                               continue;
+                       rom[off * 8 + i * 2 + 0] =
+                           urtwn_efuse_read_1(sc, addr);
+                       addr++;
+                       rom[off * 8 + i * 2 + 1] =
+                           urtwn_efuse_read_1(sc, addr);
+                       addr++;
+               }
+       }
+
+       addr = 0x10;
+       for (i = 0; i < 6; i++)
+               sc->cck_tx_pwr[i] = sc->r88e_rom[addr++];
+       for (i = 0; i < 5; i++)
+               sc->ht40_tx_pwr[i] = sc->r88e_rom[addr++];
+       sc->bw20_tx_pwr_diff = (sc->r88e_rom[addr] & 0xf0) >> 4;
+       if (sc->bw20_tx_pwr_diff & 0x08)
+               sc->bw20_tx_pwr_diff |= 0xf0;
+       sc->ofdm_tx_pwr_diff = (sc->r88e_rom[addr] & 0xf);
+       if (sc->ofdm_tx_pwr_diff & 0x08)
+               sc->ofdm_tx_pwr_diff |= 0xf0;
+       sc->regulatory = MS(sc->r88e_rom[0xc1], R92C_ROM_RF1_REGULATORY);
+       IEEE80211_ADDR_COPY(sc->sc_bssid, &sc->r88e_rom[0xd7]);
+
+       sc->sc_rf_write = urtwn_r88e_rf_write;
+       sc->sc_power_on = urtwn_r88e_power_on;
+       sc->sc_dma_init = urtwn_r88e_dma_init;
 }
 
 /*
@@ -1322,13 +1470,26 @@ static void
 urtwn_set_led(struct urtwn_softc *sc, int led, int on)
 {
        uint8_t reg;
-
+       
        if (led == URTWN_LED_LINK) {
-               reg = urtwn_read_1(sc, R92C_LEDCFG0) & 0x70;
-               if (!on)
-                       reg |= R92C_LEDCFG0_DIS;
-               urtwn_write_1(sc, R92C_LEDCFG0, reg);
-               sc->ledlink = on;       /* Save LED state. */
+               if (sc->chip & URTWN_CHIP_88E) {
+                       reg = urtwn_read_1(sc, R92C_LEDCFG2) & 0xf0;
+                       urtwn_write_1(sc, R92C_LEDCFG2, reg | 0x60);
+                       if (!on) {
+                               reg = urtwn_read_1(sc, R92C_LEDCFG2) & 0x90;
+                               urtwn_write_1(sc, R92C_LEDCFG2,
+                                   reg | R92C_LEDCFG0_DIS);
+                               urtwn_write_1(sc, R92C_MAC_PINMUX_CFG,
+                                   urtwn_read_1(sc, R92C_MAC_PINMUX_CFG) &
+                                   0xfe);
+                       }
+               } else {
+                       reg = urtwn_read_1(sc, R92C_LEDCFG0) & 0x70;
+                       if (!on)
+                               reg |= R92C_LEDCFG0_DIS;
+                       urtwn_write_1(sc, R92C_LEDCFG0, reg);
+               }
+               sc->ledlink = on;       /* Save LED state. */
        }
 }
 
@@ -1393,11 +1554,12 @@ urtwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
                        reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x20);
                        urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), reg);
 
-                       reg = urtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(1));
-                       reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x20);
-                       urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(1), reg);
+                       if (!(sc->chip & URTWN_CHIP_88E)) {
+                               reg = urtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(1));
+                               reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x20);
+                               urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(1), reg);
+                       }
                }
-
                /* Make link LED blink during scan. */
                urtwn_set_led(sc, URTWN_LED_LINK, !sc->ledlink);
 
@@ -1413,10 +1575,11 @@ urtwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
                reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x32);
                urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), reg);
 
-               reg = urtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(1));
-               reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x32);
-               urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(1), reg);
-
+               if (!(sc->chip & URTWN_CHIP_88E)) {
+                       reg = urtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(1));
+                       reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x32);
+                       urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(1), reg);
+               }
                urtwn_set_chan(sc, ic->ic_curchan, NULL);
                break;
        case IEEE80211_S_RUN:
@@ -1469,7 +1632,11 @@ urtwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
                urtwn_write_1(sc, R92C_T2T_SIFS + 1, 10);
 
                /* Intialize rate adaptation. */
-               urtwn_ra_init(sc);
+               if (sc->chip & URTWN_CHIP_88E)
+                       ni->ni_txrate =
+                           ni->ni_rates.rs_rates[ni->ni_rates.rs_nrates-1];
+               else 
+                       urtwn_ra_init(sc);
                /* Turn link LED on. */
                urtwn_set_led(sc, URTWN_LED_LINK, 1);
 
@@ -1514,19 +1681,21 @@ urtwn_update_avgrssi(struct urtwn_softc *sc, int rate, int8_t rssi)
                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->chip & URTWN_CHIP_88E)) {
+               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;
@@ -1563,6 +1732,58 @@ urtwn_get_rssi(struct urtwn_softc *sc, int rate, void *physt)
        return (rssi);
 }
 
+static int8_t
+urtwn_r88e_get_rssi(struct urtwn_softc *sc, int rate, void *physt)
+{
+       struct r92c_rx_phystat *phy;
+       struct r88e_rx_cck *cck;
+       uint8_t cck_agc_rpt, lna_idx, vga_idx;
+       int8_t rssi;
+
+       rssi = 0;
+       if (rate <= 3) {
+               cck = (struct r88e_rx_cck *)physt;
+               cck_agc_rpt = cck->agc_rpt;
+               lna_idx = (cck_agc_rpt & 0xe0) >> 5;
+               vga_idx = cck_agc_rpt & 0x1f; 
+               switch (lna_idx) {
+               case 7:
+                       if (vga_idx <= 27)
+                               rssi = -100 + 2* (27 - vga_idx);
+                       else
+                               rssi = -100;
+                       break;
+               case 6:
+                       rssi = -48 + 2 * (2 - vga_idx);
+                       break;
+               case 5:
+                       rssi = -42 + 2 * (7 - vga_idx);
+                       break;
+               case 4:
+                       rssi = -36 + 2 * (7 - vga_idx);
+                       break;
+               case 3:
+                       rssi = -24 + 2 * (7 - vga_idx);
+                       break;
+               case 2:
+                       rssi = -12 + 2 * (5 - vga_idx);
+                       break;
+               case 1:
+                       rssi = 8 - (2 * vga_idx);
+                       break;
+               case 0:
+                       rssi = 14 - (2 * vga_idx);
+                       break;
+               }
+               rssi += 6;
+       } else {        /* OFDM/HT. */
+               phy = (struct r92c_rx_phystat *)physt;
+               rssi = ((le32toh(phy->phydw1) >> 1) & 0x7f) - 110;
+       }
+       return (rssi);
+}
+
+
 static int
 urtwn_tx_start(struct urtwn_softc *sc, struct ieee80211_node *ni, 
     struct mbuf *m0, struct urtwn_data *data)
@@ -1590,6 +1811,8 @@ urtwn_tx_start(struct urtwn_softc *sc, struct ieee80211_node *ni,
         * Software crypto.
         */
        wh = mtod(m0, struct ieee80211_frame *);
+       type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+
        if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
                k = ieee80211_crypto_encap(ni, m0);
                if (k == NULL) {
@@ -1604,7 +1827,7 @@ urtwn_tx_start(struct urtwn_softc *sc, struct ieee80211_node *ni,
                wh = mtod(m0, struct ieee80211_frame *);
        }
        
-       switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
+       switch (type) {
        case IEEE80211_FC0_TYPE_CTL:
        case IEEE80211_FC0_TYPE_MGT:
                xfer = sc->sc_xfer[URTWN_BULK_TX_VO];
@@ -1628,20 +1851,24 @@ urtwn_tx_start(struct urtwn_softc *sc, struct ieee80211_node *ni,
            R92C_TXDW0_OWN | R92C_TXDW0_FSG | R92C_TXDW0_LSG);
        if (IEEE80211_IS_MULTICAST(wh->i_addr1))
                txd->txdw0 |= htole32(R92C_TXDW0_BMCAST);
-
-       type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
        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, URTWN_MACID_BSS) |
-                   SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BE) |
-                   SM(R92C_TXDW1_RAID, raid) |
-                   R92C_TXDW1_AGGBK);
-
+               if (sc->chip & URTWN_CHIP_88E) {
+                       txd->txdw1 |= htole32(
+                           SM(R88E_TXDW1_MACID, URTWN_MACID_BSS) |
+                           SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BE) |
+                           SM(R92C_TXDW1_RAID, raid));
+                       txd->txdw2 |= htole32(R88E_TXDW2_AGGBK);
+               } else {
+                       txd->txdw1 |= htole32(
+                           SM(R92C_TXDW1_MACID, URTWN_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 |
@@ -1655,7 +1882,10 @@ urtwn_tx_start(struct urtwn_softc *sc, struct ieee80211_node *ni,
                txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, 8));
                txd->txdw5 |= htole32(0x0001ff00);
                /* Send data at OFDM54. */
-               txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 11));
+               if (sc->chip & URTWN_CHIP_88E)
+                       txd->txdw5 |= htole32(0x13 & 0x3f);
+               else
+                       txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 11));
        } else {
                txd->txdw1 |= htole32(
                    SM(R92C_TXDW1_MACID, 0) |
@@ -1703,6 +1933,16 @@ urtwn_tx_start(struct urtwn_softc *sc, struct ieee80211_node *ni,
        return (0);
 }
 
+static void
+urtwn_start(struct ifnet *ifp, struct ifaltq_subque *ifsq)
+{
+       if ((ifp->if_flags & IFF_RUNNING) == 0)
+               return;
+
+       ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq);
+       urtwn_start_locked(ifp);
+}
+
 static void
 urtwn_start_locked(struct ifnet *ifp)
 {
@@ -1711,9 +1951,6 @@ urtwn_start_locked(struct ifnet *ifp)
        struct mbuf *m;
        struct urtwn_data *bf;
 
-       if ((ifp->if_flags & IFF_RUNNING) == 0)
-               return;
-
        URTWN_LOCK(sc);
        for (;;) {
                m = ifq_dequeue(&ifp->if_snd);
@@ -1740,20 +1977,20 @@ urtwn_start_locked(struct ifnet *ifp)
        URTWN_UNLOCK(sc);
 }
 
-static void
-urtwn_start(struct ifnet *ifp, struct ifaltq_subque *ifsq)
-{
-       ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq);
-       urtwn_start_locked(ifp);
-}
-
 static int
 urtwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *ucred)
 {
+       struct urtwn_softc *sc = ifp->if_softc;
        struct ieee80211com *ic = ifp->if_l2com;
        struct ifreq *ifr = (struct ifreq *) data;
        int error = 0, startall = 0;
 
+       URTWN_LOCK(sc);
+       error = (sc->sc_flags & URTWN_DETACHED) ? ENXIO : 0;
+       URTWN_UNLOCK(sc);
+       if (error != 0)
+               return (error);
+
        switch (cmd) {
        case SIOCSIFFLAGS:
                if (ifp->if_flags & IFF_UP) {
@@ -1763,7 +2000,7 @@ urtwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *ucred)
                        }
                } else {
                        if (ifp->if_flags & IFF_RUNNING)
-                               urtwn_stop(ifp, 1);
+                               urtwn_stop(ifp);
                }
                if (startall)
                        ieee80211_start_all(ic);
@@ -1837,8 +2074,15 @@ urtwn_alloc_tx_list(struct urtwn_softc *sc)
        return (0);
 }
 
-static int
+static __inline int
 urtwn_power_on(struct urtwn_softc *sc)
+{
+
+       return sc->sc_power_on(sc);
+}
+
+static int
+urtwn_r92c_power_on(struct urtwn_softc *sc)
 {
        uint32_t reg;
        int ntries;
@@ -1847,7 +2091,7 @@ urtwn_power_on(struct urtwn_softc *sc)
        for (ntries = 0; ntries < 1000; ntries++) {
                if (urtwn_read_1(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_PFM_ALDN)
                        break;
-               DELAY(5);
+               urtwn_ms_delay(sc);
        }
        if (ntries == 1000) {
                device_printf(sc->sc_dev,
@@ -1859,13 +2103,13 @@ urtwn_power_on(struct urtwn_softc *sc)
        urtwn_write_1(sc, R92C_RSV_CTRL, 0);
        /* Move SPS into PWM mode. */
        urtwn_write_1(sc, R92C_SPS0_CTRL, 0x2b);
-       DELAY(100);
+       urtwn_ms_delay(sc);
 
        reg = urtwn_read_1(sc, R92C_LDOV12D_CTRL);
        if (!(reg & R92C_LDOV12D_CTRL_LDV12_EN)) {
                urtwn_write_1(sc, R92C_LDOV12D_CTRL,
                    reg | R92C_LDOV12D_CTRL_LDV12_EN);
-               DELAY(100);
+               urtwn_ms_delay(sc);
                urtwn_write_1(sc, R92C_SYS_ISO_CTRL,
                    urtwn_read_1(sc, R92C_SYS_ISO_CTRL) &
                    ~R92C_SYS_ISO_CTRL_MD2PP);
@@ -1875,10 +2119,10 @@ urtwn_power_on(struct urtwn_softc *sc)
        urtwn_write_2(sc, R92C_APS_FSMCO,
            urtwn_read_2(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_APFM_ONMAC);
        for (ntries = 0; ntries < 1000; ntries++) {
-               if (urtwn_read_2(sc, R92C_APS_FSMCO) &
-                   R92C_APS_FSMCO_APFM_ONMAC)
+               if (!(urtwn_read_2(sc, R92C_APS_FSMCO) &
+                   R92C_APS_FSMCO_APFM_ONMAC))
                        break;
-               DELAY(5);
+               urtwn_ms_delay(sc);
        }
        if (ntries == 1000) {
                device_printf(sc->sc_dev,
@@ -1902,7 +2146,7 @@ urtwn_power_on(struct urtwn_softc *sc)
                if (!(urtwn_read_1(sc, R92C_APSD_CTRL) &
                    R92C_APSD_CTRL_OFF_STATUS))
                        break;
-               DELAY(5);
+               urtwn_ms_delay(sc);
        }
        if (ntries == 200) {
                device_printf(sc->sc_dev,
@@ -1922,13 +2166,74 @@ urtwn_power_on(struct urtwn_softc *sc)
        return (0);
 }
 
+static int
+urtwn_r88e_power_on(struct urtwn_softc *sc)
+{
+       uint8_t val;
+       uint32_t reg;
+       int ntries;
+
+       /* Wait for power ready bit. */
+       for (ntries = 0; ntries < 5000; ntries++) {
+               val = urtwn_read_1(sc, 0x6) & 0x2;
+               if (val == 0x2)
+                       break;
+               urtwn_ms_delay(sc);
+       }
+       if (ntries == 5000) {
+               device_printf(sc->sc_dev,
+                   "timeout waiting for chip power up\n");
+               return (ETIMEDOUT);
+       }
+
+       /* Reset BB. */
+       urtwn_write_1(sc, R92C_SYS_FUNC_EN,
+           urtwn_read_1(sc, R92C_SYS_FUNC_EN) & ~(R92C_SYS_FUNC_EN_BBRSTB |
+           R92C_SYS_FUNC_EN_BB_GLB_RST));
+
+       urtwn_write_1(sc, 0x26, urtwn_read_1(sc, 0x26) | 0x80);
+
+       /* Disable HWPDN. */
+       urtwn_write_1(sc, 0x5, urtwn_read_1(sc, 0x5) & ~0x80);
+
+       /* Disable WL suspend. */
+       urtwn_write_1(sc, 0x5, urtwn_read_1(sc, 0x5) & ~0x18);
+
+       urtwn_write_1(sc, 0x5, urtwn_read_1(sc, 0x5) | 0x1);
+       for (ntries = 0; ntries < 5000; ntries++) {
+               if (!(urtwn_read_1(sc, 0x5) & 0x1))
+                       break;
+               urtwn_ms_delay(sc);
+       }
+       if (ntries == 5000)
+               return (ETIMEDOUT);
+
+       /* Enable LDO normal mode. */
+       urtwn_write_1(sc, 0x23, urtwn_read_1(sc, 0x23) & ~0x10);
+
+       /* Enable MAC DMA/WMAC/SCHEDULE/SEC blocks. */
+       urtwn_write_2(sc, R92C_CR, 0);
+       reg = urtwn_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_ENSEC | R92C_CR_CALTMR_EN;
+       urtwn_write_2(sc, R92C_CR, reg);
+
+       return (0);
+}
+
 static int
 urtwn_llt_init(struct urtwn_softc *sc)
 {
-       int i, error;
+       int i, error, page_count, pktbuf_count;
+
+       page_count = (sc->chip & URTWN_CHIP_88E) ?
+           R88E_TX_PAGE_COUNT : R92C_TX_PAGE_COUNT;
+       pktbuf_count = (sc->chip & URTWN_CHIP_88E) ?
+           R88E_TXPKTBUF_COUNT : R92C_TXPKTBUF_COUNT;
 
-       /* Reserve pages [0; R92C_TX_PAGE_COUNT]. */
-       for (i = 0; i < R92C_TX_PAGE_COUNT; i++) {
+       /* Reserve pages [0; page_count]. */
+       for (i = 0; i < page_count; i++) {
                if ((error = urtwn_llt_write(sc, i, i + 1)) != 0)
                        return (error);
        }
@@ -1936,15 +2241,15 @@ urtwn_llt_init(struct urtwn_softc *sc)
        if ((error = urtwn_llt_write(sc, i, 0xff)) != 0)
                return (error);
        /*
-        * Use pages [R92C_TX_PAGE_COUNT + 1; R92C_TXPKTBUF_COUNT - 1]
+        * Use pages [page_count + 1; pktbuf_count - 1]
         * as ring buffer.
         */
-       for (++i; i < R92C_TXPKTBUF_COUNT - 1; i++) {
+       for (++i; i < pktbuf_count - 1; i++) {
                if ((error = urtwn_llt_write(sc, i, i + 1)) != 0)
                        return (error);
        }
        /* Make the last page point to the beginning of the ring buffer. */
-       error = urtwn_llt_write(sc, i, R92C_TX_PAGE_COUNT + 1);
+       error = urtwn_llt_write(sc, i, page_count + 1);
        return (error);
 }
 
@@ -1962,12 +2267,22 @@ urtwn_fw_reset(struct urtwn_softc *sc)
                reg = urtwn_read_2(sc, R92C_SYS_FUNC_EN);
                if (!(reg & R92C_SYS_FUNC_EN_CPUEN))
                        return;
-               DELAY(50);
+               urtwn_ms_delay(sc);
        }
        /* Force 8051 reset. */
        urtwn_write_2(sc, R92C_SYS_FUNC_EN, reg & ~R92C_SYS_FUNC_EN_CPUEN);
 }
 
+static void
+urtwn_r88e_fw_reset(struct urtwn_softc *sc)
+{
+       uint16_t reg;
+
+       reg = urtwn_read_2(sc, R92C_SYS_FUNC_EN);
+       urtwn_write_2(sc, R92C_SYS_FUNC_EN, reg & ~R92C_SYS_FUNC_EN_CPUEN);
+       urtwn_write_2(sc, R92C_SYS_FUNC_EN, reg | R92C_SYS_FUNC_EN_CPUEN);
+}
+
 static int
 urtwn_fw_loadpage(struct urtwn_softc *sc, int page, const uint8_t *buf, int len)
 {
@@ -2009,14 +2324,18 @@ urtwn_load_firmware(struct urtwn_softc *sc)
        uint32_t reg;
        int mlen, ntries, page, error;
 
+       URTWN_UNLOCK(sc);
        /* Read firmware image from the filesystem. */
-       if ((sc->chip & (URTWN_CHIP_UMC_A_CUT | URTWN_CHIP_92C)) ==
-           URTWN_CHIP_UMC_A_CUT)
+       if (sc->chip & URTWN_CHIP_88E)
+               imagename = "urtwn-rtl8188eufw";
+       else if ((sc->chip & (URTWN_CHIP_UMC_A_CUT | URTWN_CHIP_92C)) ==
+                   URTWN_CHIP_UMC_A_CUT)
                imagename = "urtwn-rtl8192cfwU";
        else
                imagename = "urtwn-rtl8192cfwT";
 
        fw = firmware_get(imagename);
+       URTWN_LOCK(sc);
        if (fw == NULL) {
                device_printf(sc->sc_dev,
                    "failed loadfirmware of file %s\n", imagename);
@@ -2034,6 +2353,7 @@ urtwn_load_firmware(struct urtwn_softc *sc)
        hdr = (const struct r92c_fw_hdr *)ptr;
        /* Check if there is a valid FW header and skip it. */
        if ((le16toh(hdr->signature) >> 4) == 0x88c ||
+           (le16toh(hdr->signature) >> 4) == 0x88e ||
            (le16toh(hdr->signature) >> 4) == 0x92c) {
                DPRINTF("FW V%d.%d %02d-%02d %02d:%02d\n",
                    le16toh(hdr->version), le16toh(hdr->subversion),
@@ -2042,18 +2362,28 @@ urtwn_load_firmware(struct urtwn_softc *sc)
                len -= sizeof(*hdr);
        }
 
-       if (urtwn_read_1(sc, R92C_MCUFWDL) & 0x80) {
-               urtwn_fw_reset(sc);
+       if (urtwn_read_1(sc, R92C_MCUFWDL) & R92C_MCUFWDL_RAM_DL_SEL) {
+               if (sc->chip & URTWN_CHIP_88E)
+                       urtwn_r88e_fw_reset(sc);
+               else
+                       urtwn_fw_reset(sc);
                urtwn_write_1(sc, R92C_MCUFWDL, 0);
        }
-       urtwn_write_2(sc, R92C_SYS_FUNC_EN,
-           urtwn_read_2(sc, R92C_SYS_FUNC_EN) |
-           R92C_SYS_FUNC_EN_CPUEN);
+
+       if (!(sc->chip & URTWN_CHIP_88E)) {
+               urtwn_write_2(sc, R92C_SYS_FUNC_EN,
+                   urtwn_read_2(sc, R92C_SYS_FUNC_EN) |
+                   R92C_SYS_FUNC_EN_CPUEN);
+       }
        urtwn_write_1(sc, R92C_MCUFWDL,
            urtwn_read_1(sc, R92C_MCUFWDL) | R92C_MCUFWDL_EN);
        urtwn_write_1(sc, R92C_MCUFWDL + 2,
            urtwn_read_1(sc, R92C_MCUFWDL + 2) & ~0x08);
 
+       /* Reset the FWDL checksum. */
+       urtwn_write_1(sc, R92C_MCUFWDL,
+           urtwn_read_1(sc, R92C_MCUFWDL) | R92C_MCUFWDL_CHKSUM_RPT);
+
        for (page = 0; len > 0; page++) {
                mlen = min(len, R92C_FW_PAGE_SIZE);
                error = urtwn_fw_loadpage(sc, page, ptr, mlen);
@@ -2073,7 +2403,7 @@ urtwn_load_firmware(struct urtwn_softc *sc)
        for (ntries = 0; ntries < 1000; ntries++) {
                if (urtwn_read_4(sc, R92C_MCUFWDL) & R92C_MCUFWDL_CHKSUM_RPT)
                        break;
-               DELAY(5);
+               urtwn_ms_delay(sc);
        }
        if (ntries == 1000) {
                device_printf(sc->sc_dev,
@@ -2085,11 +2415,13 @@ urtwn_load_firmware(struct urtwn_softc *sc)
        reg = urtwn_read_4(sc, R92C_MCUFWDL);
        reg = (reg & ~R92C_MCUFWDL_WINTINI_RDY) | R92C_MCUFWDL_RDY;
        urtwn_write_4(sc, R92C_MCUFWDL, reg);
+       if (sc->chip & URTWN_CHIP_88E)
+               urtwn_r88e_fw_reset(sc);
        /* Wait for firmware readiness. */
        for (ntries = 0; ntries < 1000; ntries++) {
                if (urtwn_read_4(sc, R92C_MCUFWDL) & R92C_MCUFWDL_WINTINI_RDY)
                        break;
-               DELAY(5);
+               urtwn_ms_delay(sc);
        }
        if (ntries == 1000) {
                device_printf(sc->sc_dev,
@@ -2102,8 +2434,15 @@ fail:
        return (error);
 }
 
-static int
+static __inline int
 urtwn_dma_init(struct urtwn_softc *sc)
+{
+            
+       return sc->sc_dma_init(sc);
+}
+
+static int
+urtwn_r92c_dma_init(struct urtwn_softc *sc)
 {
        int hashq, hasnq, haslq, nqueues, nqpages, nrempages;
        uint32_t reg;
@@ -2182,14 +2521,75 @@ urtwn_dma_init(struct urtwn_softc *sc)
        return (0);
 }
 
+static int
+urtwn_r88e_dma_init(struct urtwn_softc *sc)
+{
+       struct usb_interface *iface;
+       uint32_t reg;
+       int nqueues;
+       int error;
+
+       /* Initialize LLT table. */
+       error = urtwn_llt_init(sc);
+       if (error != 0)
+               return (error);
+
+       /* Get Tx queues to USB endpoints mapping. */
+       iface = usbd_get_iface(sc->sc_udev, 0);
+       nqueues = iface->idesc->bNumEndpoints - 1;
+       if (nqueues == 0)
+               return (EIO);
+
+       /* Set number of pages for normal priority queue. */
+       urtwn_write_2(sc, R92C_RQPN_NPQ, 0);
+       urtwn_write_2(sc, R92C_RQPN_NPQ, 0x000d);
+       urtwn_write_4(sc, R92C_RQPN, 0x808e000d);
+
+       urtwn_write_1(sc, R92C_TXPKTBUF_BCNQ_BDNY, R88E_TX_PAGE_BOUNDARY);
+       urtwn_write_1(sc, R92C_TXPKTBUF_MGQ_BDNY, R88E_TX_PAGE_BOUNDARY);
+       urtwn_write_1(sc, R92C_TXPKTBUF_WMAC_LBK_BF_HD, R88E_TX_PAGE_BOUNDARY);
+       urtwn_write_1(sc, R92C_TRXFF_BNDY, R88E_TX_PAGE_BOUNDARY);
+       urtwn_write_1(sc, R92C_TDECTRL + 1, R88E_TX_PAGE_BOUNDARY);
+
+       /* Set queue to USB pipe mapping. */
+       reg = urtwn_read_2(sc, R92C_TRXDMA_CTRL);
+       reg &= ~R92C_TRXDMA_CTRL_QMAP_M;
+       if (nqueues == 1)
+               reg |= R92C_TRXDMA_CTRL_QMAP_LQ;
+       else if (nqueues == 2)
+               reg |= R92C_TRXDMA_CTRL_QMAP_HQ_NQ;
+       else
+               reg |= R92C_TRXDMA_CTRL_QMAP_3EP;
+       urtwn_write_2(sc, R92C_TRXDMA_CTRL, reg);
+
+       /* Set Tx/Rx transfer page boundary. */
+       urtwn_write_2(sc, R92C_TRXFF_BNDY + 2, 0x23ff);
+
+       /* Set Tx/Rx transfer page size. */
+       urtwn_write_1(sc, R92C_PBP,
+           SM(R92C_PBP_PSRX, R92C_PBP_128) |
+           SM(R92C_PBP_PSTX, R92C_PBP_128));
+
+       return (0);
+}
+
 static void
 urtwn_mac_init(struct urtwn_softc *sc)
 {
        int i;
 
        /* Write MAC initialization values. */
-       for (i = 0; i < NELEM(rtl8192cu_mac); i++)
-               urtwn_write_1(sc, rtl8192cu_mac[i].reg, rtl8192cu_mac[i].val);
+       if (sc->chip & URTWN_CHIP_88E) {
+               for (i = 0; i < NELEM(rtl8188eu_mac); i++) {
+                       urtwn_write_1(sc, rtl8188eu_mac[i].reg,
+                           rtl8188eu_mac[i].val);
+               }
+               urtwn_write_1(sc, R92C_MAX_AGGR_NUM, 0x07);
+       } else {
+               for (i = 0; i < NELEM(rtl8192cu_mac); i++)
+                       urtwn_write_1(sc, rtl8192cu_mac[i].reg,
+                           rtl8192cu_mac[i].val);
+       }
 }
 
 static void
@@ -2197,6 +2597,7 @@ urtwn_bb_init(struct urtwn_softc *sc)
 {
        const struct urtwn_bb_prog *prog;
        uint32_t reg;
+       uint8_t crystalcap;
        int i;
 
        /* Enable BB and RF. */
@@ -2205,7 +2606,8 @@ urtwn_bb_init(struct urtwn_softc *sc)
            R92C_SYS_FUNC_EN_BBRSTB | R92C_SYS_FUNC_EN_BB_GLB_RST |
            R92C_SYS_FUNC_EN_DIO_RF);
 
-       urtwn_write_2(sc, R92C_AFE_PLL_CTRL, 0xdb83);
+       if (!(sc->chip & URTWN_CHIP_88E))
+               urtwn_write_2(sc, R92C_AFE_PLL_CTRL, 0xdb83);
 
        urtwn_write_1(sc, R92C_RF_CTRL,
            R92C_RF_CTRL_EN | R92C_RF_CTRL_RSTB | R92C_RF_CTRL_SDMRSTB);
@@ -2213,12 +2615,16 @@ urtwn_bb_init(struct urtwn_softc *sc)
            R92C_SYS_FUNC_EN_USBA | R92C_SYS_FUNC_EN_USBD |
            R92C_SYS_FUNC_EN_BB_GLB_RST | R92C_SYS_FUNC_EN_BBRSTB);
 
-       urtwn_write_1(sc, R92C_LDOHCI12_CTRL, 0x0f);
-       urtwn_write_1(sc, 0x15, 0xe9);
-       urtwn_write_1(sc, R92C_AFE_XTAL_CTRL + 1, 0x80);
+       if (!(sc->chip & URTWN_CHIP_88E)) {
+               urtwn_write_1(sc, R92C_LDOHCI12_CTRL, 0x0f);
+               urtwn_write_1(sc, 0x15, 0xe9);
+               urtwn_write_1(sc, R92C_AFE_XTAL_CTRL + 1, 0x80);
+       }
 
        /* Select BB programming based on board type. */
-       if (!(sc->chip & URTWN_CHIP_92C)) {
+       if (sc->chip & URTWN_CHIP_88E)
+               prog = &rtl8188eu_bb_prog;
+       else if (!(sc->chip & URTWN_CHIP_92C)) {
                if (sc->board_type == R92C_BOARD_TYPE_MINICARD)
                        prog = &rtl8188ce_bb_prog;
                else if (sc->board_type == R92C_BOARD_TYPE_HIGHPA)
@@ -2234,7 +2640,7 @@ urtwn_bb_init(struct urtwn_softc *sc)
        /* Write BB initialization values. */
        for (i = 0; i < prog->count; i++) {
                urtwn_bb_write(sc, prog->regs[i], prog->vals[i]);
-               DELAY(1);
+               urtwn_ms_delay(sc);
        }
 
        if (sc->chip & URTWN_CHIP_92C_1T2R) {
@@ -2280,12 +2686,28 @@ urtwn_bb_init(struct urtwn_softc *sc)
        for (i = 0; i < prog->agccount; i++) {
                urtwn_bb_write(sc, R92C_OFDM0_AGCRSSITABLE,
                    prog->agcvals[i]);
-               DELAY(1);
+               urtwn_ms_delay(sc);
+       }
+
+       if (sc->chip & URTWN_CHIP_88E) {
+               urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), 0x69553422);
+               urtwn_ms_delay(sc);
+               urtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), 0x69553420);
+               urtwn_ms_delay(sc);
+
+               crystalcap = sc->r88e_rom[0xb9];
+               if (crystalcap == 0xff)
+                       crystalcap = 0x20;
+               crystalcap &= 0x3f;
+               reg = urtwn_bb_read(sc, R92C_AFE_XTAL_CTRL);
+               urtwn_bb_write(sc, R92C_AFE_XTAL_CTRL,
+                   RW(reg, R92C_AFE_XTAL_CTRL_ADDR,
+                   crystalcap | crystalcap << 6));
+       } else {
+               if (urtwn_bb_read(sc, R92C_HSSI_PARAM2(0)) &
+                   R92C_HSSI_PARAM2_CCK_HIPWR)
+                       sc->sc_flags |= URTWN_FLAG_CCK_HIPWR;
        }
-
-       if (urtwn_bb_read(sc, R92C_HSSI_PARAM2(0)) &
-           R92C_HSSI_PARAM2_CCK_HIPWR)
-               sc->sc_flags |= URTWN_FLAG_CCK_HIPWR;
 }
 
 void
@@ -2296,7 +2718,9 @@ urtwn_rf_init(struct urtwn_softc *sc)
        int i, j, idx, off;
 
        /* Select RF programming based on board type. */
-       if (!(sc->chip & URTWN_CHIP_92C)) {
+       if (sc->chip & URTWN_CHIP_88E)
+               prog = rtl8188eu_rf_prog;
+       else if (!(sc->chip & URTWN_CHIP_92C)) {
                if (sc->board_type == R92C_BOARD_TYPE_MINICARD)
                        prog = rtl8188ce_rf_prog;
                else if (sc->board_type == R92C_BOARD_TYPE_HIGHPA)
@@ -2317,21 +2741,21 @@ urtwn_rf_init(struct urtwn_softc *sc)
                reg = urtwn_bb_read(sc, R92C_FPGA0_RFIFACEOE(i));
                reg |= 0x100000;
                urtwn_bb_write(sc, R92C_FPGA0_RFIFACEOE(i), reg);
-               DELAY(1);
+               urtwn_ms_delay(sc);
                /* Set RF_ENV output high. */
                reg = urtwn_bb_read(sc, R92C_FPGA0_RFIFACEOE(i));
                reg |= 0x10;
                urtwn_bb_write(sc, R92C_FPGA0_RFIFACEOE(i), reg);
-               DELAY(1);
+               urtwn_ms_delay(sc);
                /* Set address and data lengths of RF registers. */
                reg = urtwn_bb_read(sc, R92C_HSSI_PARAM2(i));
                reg &= ~R92C_HSSI_PARAM2_ADDR_LENGTH;
                urtwn_bb_write(sc, R92C_HSSI_PARAM2(i), reg);
-               DELAY(1);
+               urtwn_ms_delay(sc);
                reg = urtwn_bb_read(sc, R92C_HSSI_PARAM2(i));
                reg &= ~R92C_HSSI_PARAM2_DATA_LENGTH;
                urtwn_bb_write(sc, R92C_HSSI_PARAM2(i), reg);
-               DELAY(1);
+               urtwn_ms_delay(sc);
 
                /* Write RF initialization values for this chain. */
                for (j = 0; j < prog[i].count; j++) {
@@ -2341,12 +2765,12 @@ urtwn_rf_init(struct urtwn_softc *sc)
                                 * These are fake RF registers offsets that
                                 * indicate a delay is required.
                                 */
-                               usb_pause_mtx(&sc->sc_lock, 50);
+                               usb_pause_mtx(&sc->sc_lock, hz / 20);
                                continue;
                        }
                        urtwn_rf_write(sc, i, prog[i].regs[j],
                            prog[i].vals[j]);
-                       DELAY(1);
+                       urtwn_ms_delay(sc);
                }
 
                /* Restore RF_ENV control type. */
@@ -2477,7 +2901,7 @@ urtwn_write_txpower(struct urtwn_softc *sc, int chain,
            SM(R92C_TXAGC_MCS07,  power[19]));
        urtwn_bb_write(sc, R92C_TXAGC_MCS11_MCS08(chain),
            SM(R92C_TXAGC_MCS08,  power[20]) |
-           SM(R92C_TXAGC_MCS08,  power[21]) |
+           SM(R92C_TXAGC_MCS09,  power[21]) |
            SM(R92C_TXAGC_MCS10,  power[22]) |
            SM(R92C_TXAGC_MCS11,  power[23]));
        urtwn_bb_write(sc, R92C_TXAGC_MCS15_MCS12(chain),
@@ -2586,6 +3010,75 @@ urtwn_get_txpower(struct urtwn_softc *sc, int chain,
 #endif
 }
 
+void
+urtwn_r88e_get_txpower(struct urtwn_softc *sc, int chain,
+    struct ieee80211_channel *c, struct ieee80211_channel *extc,
+    uint16_t power[URTWN_RIDX_COUNT])
+{
+       struct ieee80211com *ic = sc->sc_ifp->if_l2com;
+       uint16_t cckpow, ofdmpow, bw20pow, htpow;
+       const struct urtwn_r88e_txpwr *base;
+       int ridx, chan, group;
+
+       /* Determine channel group. */
+       chan = ieee80211_chan2ieee(ic, c);      /* XXX center freq! */
+       if (chan <= 2)
+               group = 0;
+       else if (chan <= 5)
+               group = 1;
+       else if (chan <= 8)
+               group = 2;
+       else if (chan <= 11)
+               group = 3;
+       else if (chan <= 13)
+               group = 4;
+       else
+               group = 5;
+
+       /* Get original Tx power based on board type and RF chain. */
+       base = &rtl8188eu_txagc[chain];
+
+       memset(power, 0, URTWN_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 < URTWN_RIDX_COUNT; ridx++) {
+               if (sc->regulatory == 3)
+                       power[ridx] = base->pwr[0][ridx];
+               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 = sc->cck_tx_pwr[group];
+       for (ridx = 0; ridx <= 3; ridx++) {
+               power[ridx] += cckpow;
+               if (power[ridx] > R92C_MAX_TX_PWR)
+                       power[ridx] = R92C_MAX_TX_PWR;
+       }
+
+       htpow = sc->ht40_tx_pwr[group];
+
+       /* Compute per-OFDM rate Tx power. */
+       ofdmpow = htpow + sc->ofdm_tx_pwr_diff;
+       for (ridx = 4; ridx <= 11; ridx++) {
+               power[ridx] += ofdmpow;
+               if (power[ridx] > R92C_MAX_TX_PWR)
+                       power[ridx] = R92C_MAX_TX_PWR;
+       }
+
+       bw20pow = htpow + sc->bw20_tx_pwr_diff;
+       for (ridx = 12; ridx <= 27; ridx++) {
+               power[ridx] += bw20pow;
+               if (power[ridx] > R92C_MAX_TX_PWR)
+                       power[ridx] = R92C_MAX_TX_PWR;
+       }
+}
+
 void
 urtwn_set_txpower(struct urtwn_softc *sc, struct ieee80211_channel *c,
     struct ieee80211_channel *extc)
@@ -2595,7 +3088,10 @@ urtwn_set_txpower(struct urtwn_softc *sc, struct ieee80211_channel *c,
 
        for (i = 0; i < sc->ntxchains; i++) {
                /* Compute per-rate Tx power values. */
-               urtwn_get_txpower(sc, i, c, extc, power);
+               if (sc->chip & URTWN_CHIP_88E)
+                       urtwn_r88e_get_txpower(sc, i, c, extc, power);
+               else
+                       urtwn_get_txpower(sc, i, c, extc, power);
                /* Write per-rate Tx power values to hardware. */
                urtwn_write_txpower(sc, i, power);
        }
@@ -2700,13 +3196,17 @@ urtwn_set_chan(struct urtwn_softc *sc, struct ieee80211_channel *c,
                urtwn_bb_write(sc, R92C_FPGA1_RFMOD,
                    urtwn_bb_read(sc, R92C_FPGA1_RFMOD) & ~R92C_RFMOD_40MHZ);
 
-               urtwn_bb_write(sc, R92C_FPGA0_ANAPARAM2,
-                   urtwn_bb_read(sc, R92C_FPGA0_ANAPARAM2) |
-                   R92C_FPGA0_ANAPARAM2_CBW20);
-
+               if (!(sc->chip & URTWN_CHIP_88E)) {
+                       urtwn_bb_write(sc, R92C_FPGA0_ANAPARAM2,
+                           urtwn_bb_read(sc, R92C_FPGA0_ANAPARAM2) |
+                           R92C_FPGA0_ANAPARAM2_CBW20);
+               }
+                       
                /* Select 20MHz bandwidth. */
                urtwn_rf_write(sc, 0, R92C_RF_CHNLBW,
-                   (sc->rf_chnlbw[0] & ~0xfff) | R92C_RF_CHNLBW_BW20 | chan);
+                   (sc->rf_chnlbw[0] & ~0xfff) | chan | 
+                   ((sc->chip & URTWN_CHIP_88E) ? R88E_RF_CHNLBW_BW20 :
+                   R92C_RF_CHNLBW_BW20));
        }
 }
 
@@ -2744,7 +3244,7 @@ urtwn_lc_calib(struct urtwn_softc *sc)
            urtwn_rf_read(sc, 0, R92C_RF_CHNLBW) | R92C_RF_CHNLBW_LCSTART);
 
        /* Give calibration the time to complete. */
-       usb_pause_mtx(&sc->sc_lock, 100);
+       usb_pause_mtx(&sc->sc_lock, hz / 10);           /* 100ms */
 
        /* Restore configuration. */
        if ((txmode & 0x70) != 0) {
@@ -2767,8 +3267,10 @@ urtwn_init_locked(void *arg)
        uint32_t reg;
        int error;
 
+       URTWN_ASSERT_LOCKED(sc);
+
        if (ifp->if_flags & IFF_RUNNING)
-               urtwn_stop_locked(ifp, 0);
+               urtwn_stop_locked(ifp);
 
        /* Init firmware commands ring. */
        sc->fwcur = 0;
@@ -2796,8 +3298,19 @@ urtwn_init_locked(void *arg)
        urtwn_write_1(sc, R92C_RX_DRVINFO_SZ, 4);
 
        /* Init interrupts. */
-       urtwn_write_4(sc, R92C_HISR, 0xffffffff);
-       urtwn_write_4(sc, R92C_HIMR, 0xffffffff);
+       if (sc->chip & URTWN_CHIP_88E) {
+               urtwn_write_4(sc, R88E_HISR, 0xffffffff);
+               urtwn_write_4(sc, R88E_HIMR, R88E_HIMR_CPWM | R88E_HIMR_CPWM2 |
+                   R88E_HIMR_TBDER | R88E_HIMR_PSTIMEOUT);
+               urtwn_write_4(sc, R88E_HIMRE, R88E_HIMRE_RXFOVW |
+                   R88E_HIMRE_TXFOVW | R88E_HIMRE_RXERR | R88E_HIMRE_TXERR);
+               urtwn_write_1(sc, R92C_USB_SPECIAL_OPTION,
+                   urtwn_read_1(sc, R92C_USB_SPECIAL_OPTION) |
+                   R92C_USB_SPECIAL_OPTION_INT_BULK_SEL);
+       } else {
+               urtwn_write_4(sc, R92C_HISR, 0xffffffff);
+               urtwn_write_4(sc, R92C_HIMR, 0xffffffff);
+       }
 
        /* Set MAC address. */
        urtwn_write_region_1(sc, R92C_MACID, IF_LLADDR(ifp),
@@ -2822,10 +3335,12 @@ urtwn_init_locked(void *arg)
        urtwn_edca_init(sc);
 
        /* Setup rate fallback. */
-       urtwn_write_4(sc, R92C_DARFRC + 0, 0x00000000);
-       urtwn_write_4(sc, R92C_DARFRC + 4, 0x10080404);
-       urtwn_write_4(sc, R92C_RARFRC + 0, 0x04030201);
-       urtwn_write_4(sc, R92C_RARFRC + 4, 0x08070605);
+       if (!(sc->chip & URTWN_CHIP_88E)) {
+               urtwn_write_4(sc, R92C_DARFRC + 0, 0x00000000);
+               urtwn_write_4(sc, R92C_DARFRC + 4, 0x10080404);
+               urtwn_write_4(sc, R92C_RARFRC + 0, 0x04030201);
+               urtwn_write_4(sc, R92C_RARFRC + 4, 0x08070605);
+       }
 
        urtwn_write_1(sc, R92C_FWHW_TXQ_CTRL,
            urtwn_read_1(sc, R92C_FWHW_TXQ_CTRL) |
@@ -2844,23 +3359,28 @@ urtwn_init_locked(void *arg)
            urtwn_read_1(sc, R92C_USB_SPECIAL_OPTION) |
            R92C_USB_SPECIAL_OPTION_AGG_EN);
        urtwn_write_1(sc, R92C_RXDMA_AGG_PG_TH, 48);
-       urtwn_write_1(sc, R92C_USB_DMA_AGG_TO, 4);
+       if (sc->chip & URTWN_CHIP_88E)
+               urtwn_write_1(sc, R92C_RXDMA_AGG_PG_TH + 1, 4);
+       else
+               urtwn_write_1(sc, R92C_USB_DMA_AGG_TO, 4);
        urtwn_write_1(sc, R92C_USB_AGG_TH, 8);
        urtwn_write_1(sc, R92C_USB_AGG_TO, 6);
 
        /* Initialize beacon parameters. */
+       urtwn_write_2(sc, R92C_BCN_CTRL, 0x1010);
        urtwn_write_2(sc, R92C_TBTT_PROHIBIT, 0x6404);
        urtwn_write_1(sc, R92C_DRVERLYINT, 0x05);
        urtwn_write_1(sc, R92C_BCNDMATIM, 0x02);
        urtwn_write_2(sc, R92C_BCNTCFG, 0x660f);
 
-       /* Setup AMPDU aggregation. */
-       urtwn_write_4(sc, R92C_AGGLEN_LMT, 0x99997631); /* MCS7~0 */
-       urtwn_write_1(sc, R92C_AGGR_BREAK_TIME, 0x16);
-       urtwn_write_2(sc, 0x4ca, 0x0708);
+       if (!(sc->chip & URTWN_CHIP_88E)) {
+               /* Setup AMPDU aggregation. */
+               urtwn_write_4(sc, R92C_AGGLEN_LMT, 0x99997631); /* MCS7~0 */
+               urtwn_write_1(sc, R92C_AGGR_BREAK_TIME, 0x16);
+               urtwn_write_2(sc, R92C_MAX_AGGR_NUM, 0x0708);
 
-       urtwn_write_1(sc, R92C_BCN_MAX_ERR, 0xff);
-       urtwn_write_1(sc, R92C_BCN_CTRL, R92C_BCN_CTRL_DIS_TSF_UDT0);
+               urtwn_write_1(sc, R92C_BCN_MAX_ERR, 0xff);
+       }
 
        /* Load 8051 microcode. */
        error = urtwn_load_firmware(sc);
@@ -2872,6 +3392,12 @@ urtwn_init_locked(void *arg)
        urtwn_bb_init(sc);
        urtwn_rf_init(sc);
 
+       if (sc->chip & URTWN_CHIP_88E) {
+               urtwn_write_2(sc, R92C_CR,
+                   urtwn_read_2(sc, R92C_CR) | R92C_CR_MACTXEN |
+                   R92C_CR_MACRXEN);
+       }
+
        /* Turn CCK and OFDM blocks on. */
        reg = urtwn_bb_read(sc, R92C_FPGA0_RFMOD);
        reg |= R92C_RFMOD_CCK_EN;
@@ -2892,18 +3418,21 @@ urtwn_init_locked(void *arg)
        urtwn_lc_calib(sc);
 
        /* Fix USB interference issue. */
-       urtwn_write_1(sc, 0xfe40, 0xe0);
-       urtwn_write_1(sc, 0xfe41, 0x8d);
-       urtwn_write_1(sc, 0xfe42, 0x80);
+       if (!(sc->chip & URTWN_CHIP_88E)) {
+               urtwn_write_1(sc, 0xfe40, 0xe0);
+               urtwn_write_1(sc, 0xfe41, 0x8d);
+               urtwn_write_1(sc, 0xfe42, 0x80);
 
-       urtwn_pa_bias_init(sc);
+               urtwn_pa_bias_init(sc);
+       }
 
        /* Initialize GPIO setting. */
        urtwn_write_1(sc, R92C_GPIO_MUXCFG,
            urtwn_read_1(sc, R92C_GPIO_MUXCFG) & ~R92C_GPIO_MUXCFG_ENBT);
 
        /* Fix for lower temperature. */
-       urtwn_write_1(sc, 0x15, 0xe9);
+       if (!(sc->chip & URTWN_CHIP_88E))
+               urtwn_write_1(sc, 0x15, 0xe9);
 
        usbd_transfer_start(sc->sc_xfer[URTWN_BULK_RX]);
 
@@ -2926,11 +3455,12 @@ urtwn_init(void *arg)
 }
 
 static void
-urtwn_stop_locked(struct ifnet *ifp, int disable)
+urtwn_stop_locked(struct ifnet *ifp)
 {
        struct urtwn_softc *sc = ifp->if_softc;
 
-       (void)disable;
+       URTWN_ASSERT_LOCKED(sc);
+
        ifp->if_flags &= ~IFF_RUNNING;
        ifq_clr_oactive(&ifp->if_snd);
 
@@ -2939,12 +3469,12 @@ urtwn_stop_locked(struct ifnet *ifp, int disable)
 }
 
 static void
-urtwn_stop(struct ifnet *ifp, int disable)
+urtwn_stop(struct ifnet *ifp)
 {
        struct urtwn_softc *sc = ifp->if_softc;
 
        URTWN_LOCK(sc);
-       urtwn_stop_locked(ifp, disable);
+       urtwn_stop_locked(ifp);
        URTWN_UNLOCK(sc);
 }
 
@@ -2998,13 +3528,19 @@ urtwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
        return (0);
 }
 
+static void
+urtwn_ms_delay(struct urtwn_softc *sc)
+{
+       usb_pause_ls(&sc->sc_lock, &wlan_global_serializer, hz / 100);
+}
+
 static device_method_t urtwn_methods[] = {
        /* Device interface */
        DEVMETHOD(device_probe,         urtwn_match),
        DEVMETHOD(device_attach,        urtwn_attach),
        DEVMETHOD(device_detach,        urtwn_detach),
 
-       { 0, 0 }
+       DEVMETHOD_END
 };
 
 static driver_t urtwn_driver = {
index 41b3070..68f37bf 100644 (file)
@@ -14,7 +14,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  * 
  * $OpenBSD: if_urtwnreg.h,v 1.3 2010/11/16 18:02:59 damien Exp $
- * $FreeBSD: src/sys/dev/usb/wlan/if_urtwnreg.h,v 1.1 2013/06/09 00:34:38 svnexp Exp $
+ * $FreeBSD: head/sys/dev/usb/wlan/if_urtwnreg.h 264912 2014-04-25 08:01:22Z kevlo $
  */
 
 #define URTWN_CONFIG_INDEX     0
@@ -33,6 +33,9 @@
 #define R92C_TXPKTBUF_COUNT    256
 #define R92C_TX_PAGE_COUNT     248
 #define R92C_TX_PAGE_BOUNDARY  (R92C_TX_PAGE_COUNT + 1)
+#define R88E_TXPKTBUF_COUNT    177
+#define R88E_TX_PAGE_COUNT     169
+#define R88E_TX_PAGE_BOUNDARY  (R88E_TX_PAGE_COUNT + 1)
 
 #define R92C_H2C_NBOX  4
 
 #define R92C_HSISR                     0x05c
 #define R92C_MCUFWDL                   0x080
 #define R92C_HMEBOX_EXT(idx)           (0x088 + (idx) * 2)
+#define R88E_HIMR                      0x0b0
+#define R88E_HISR                      0x0b4
+#define R88E_HIMRE                     0x0b8
+#define R88E_HISRE                     0x0bc
+#define R92C_EFUSE_ACCESS               0x0cf
 #define R92C_BIST_SCAN                 0x0d0
 #define R92C_BIST_RPT                  0x0d4
 #define R92C_BIST_ROM_RPT              0x0d8
 #define R92C_LLT_INIT                  0x1e0
 #define R92C_BB_ACCESS_CTRL            0x1e8
 #define R92C_BB_ACCESS_DATA            0x1ec
+#define R88E_HMEBOX_EXT(idx)            (0x1f0 + (idx) * 4)
 /* Tx DMA Configuration. */
 #define R92C_RQPN                      0x200
 #define R92C_FIFOPAGE                  0x204
 #define R92C_RD_RESP_PKT_TH            0x463
 #define R92C_INIRTS_RATE_SEL           0x480
 #define R92C_INIDATA_RATE_SEL(macid)   (0x484 + (macid))
+#define R92C_MAX_AGGR_NUM              0x4ca
 /* EDCA Configuration. */
 #define R92C_EDCA_VO_PARAM             0x500
 #define R92C_EDCA_VI_PARAM             0x504
 /* Bits for R92C_LDOV12D_CTRL. */
 #define R92C_LDOV12D_CTRL_LDV12_EN     0x01
 
+/* Bits for R92C_AFE_XTAL_CTRL. */
+#define R92C_AFE_XTAL_CTRL_ADDR_M      0x007ff800
+#define R92C_AFE_XTAL_CTRL_ADDR_S      11
+
 /* Bits for R92C_EFUSE_CTRL. */
 #define R92C_EFUSE_CTRL_DATA_M 0x000000ff
 #define R92C_EFUSE_CTRL_DATA_S 0
 #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
 #define R92C_MCUFWDL_PAGE_M            0x00070000
 #define R92C_MCUFWDL_PAGE_S            16
 #define R92C_MCUFWDL_CPRST             0x00800000
 
+/* Bits for R88E_HIMR. */
+#define R88E_HIMR_CPWM                 0x00000100
+#define R88E_HIMR_CPWM2                        0x00000200
+#define R88E_HIMR_TBDER                        0x04000000
+#define R88E_HIMR_PSTIMEOUT            0x20000000
+
+/* Bits for R88E_HIMRE.*/
+#define R88E_HIMRE_RXFOVW              0x00000100
+#define R88E_HIMRE_TXFOVW              0x00000200
+#define R88E_HIMRE_RXERR               0x00000400
+#define R88E_HIMRE_TXERR               0x00000800
+
+/* Bits for R92C_EFUSE_ACCESS. */
+#define R92C_EFUSE_ACCESS_OFF          0x00
+#define R92C_EFUSE_ACCESS_ON           0x69
+
 /* 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_CR_MACTXEN                0x00000040
 #define R92C_CR_MACRXEN                0x00000080
 #define R92C_CR_ENSEC          0x00000200
+#define R92C_CR_CALTMR_EN      0x00000400
 #define R92C_CR_NETTYPE_S      16
 #define R92C_CR_NETTYPE_M      0x00030000
 #define R92C_CR_NETTYPE_NOLINK 0
 #define R92C_LSSI_PARAM_DATA_S 0
 #define R92C_LSSI_PARAM_ADDR_M 0x03f00000
 #define R92C_LSSI_PARAM_ADDR_S 20
+#define R88E_LSSI_PARAM_ADDR_M 0x0ff00000
+#define R88E_LSSI_PARAM_ADDR_S 20
 
 /* Bits for R92C_FPGA0_ANAPARAM2. */
 #define R92C_FPGA0_ANAPARAM2_CBW20     0x00000400
 #define R92C_USB_STRING                        0xfe80
 
 /* Bits for R92C_USB_SPECIAL_OPTION. */
-#define R92C_USB_SPECIAL_OPTION_AGG_EN 0x08
+#define R92C_USB_SPECIAL_OPTION_AGG_EN         0x08
+#define R92C_USB_SPECIAL_OPTION_INT_BULK_SEL   0x10
 
 /* Bits for R92C_USB_EP. */
 #define R92C_USB_EP_HQ_M       0x000f
 #define R92C_RF_CHNLBW_CHNL_M  0x003ff
 #define R92C_RF_CHNLBW_CHNL_S  0
 #define R92C_RF_CHNLBW_BW20    0x00400
+#define R88E_RF_CHNLBW_BW20    0x00c00
 #define R92C_RF_CHNLBW_LCSTART 0x08000
 
 
@@ -941,6 +977,26 @@ struct r92c_rx_cck {
        uint8_t         agc_rpt;
 } __packed;
 
+struct r88e_rx_cck {
+       uint8_t         path_agc[2];
+       uint8_t         sig_qual;
+       uint8_t         agc_rpt;
+       uint8_t         rpt_b;
+       uint8_t         reserved1;
+       uint8_t         noise_power;
+       uint8_t         path_cfotail[2];        
+       uint8_t         pcts_mask[2];   
+       uint8_t         stream_rxevm[2];        
+       uint8_t         path_rxsnr[2];
+       uint8_t         noise_power_db_lsb;
+       uint8_t         reserved2[3];
+       uint8_t         stream_csi[2];
+       uint8_t         stream_target_csi[2];
+       uint8_t         sig_evm;
+       uint8_t         reserved3;
+       uint8_t         reserved4;
+} __packed;
+
 /* Tx MAC descriptor. */
 struct r92c_tx_desc {
        uint32_t        txdw0;
@@ -956,6 +1012,8 @@ struct r92c_tx_desc {
        uint32_t        txdw1;
 #define R92C_TXDW1_MACID_M     0x0000001f
 #define R92C_TXDW1_MACID_S     0
+#define R88E_TXDW1_MACID_M     0x0000003f
+#define R88E_TXDW1_MACID_S     0
 #define R92C_TXDW1_AGGEN       0x00000020
 #define R92C_TXDW1_AGGBK       0x00000040
 #define R92C_TXDW1_QSEL_M      0x00001f00
@@ -973,6 +1031,8 @@ struct r92c_tx_desc {
 #define R92C_TXDW1_PKTOFF_S    26
 
        uint32_t        txdw2;
+#define R88E_TXDW2_AGGBK       0x00010000
+
        uint16_t        txdw3;
        uint16_t        txdseq;
 
@@ -1030,7 +1090,7 @@ struct urtwn_rx_radiotap_header {
        uint16_t        wr_chan_freq;
        uint16_t        wr_chan_flags;
        uint8_t         wr_dbm_antsignal;
-} __packed;
+} __packed __aligned(8);
 
 #define URTWN_RX_RADIOTAP_PRESENT                      \
        (1 << IEEE80211_RADIOTAP_FLAGS |                \
@@ -1043,7 +1103,7 @@ struct urtwn_tx_radiotap_header {
        uint8_t         wt_flags;
        uint16_t        wt_chan_freq;
        uint16_t        wt_chan_flags;
-} __packed;
+} __packed __aligned(8);
 
 #define URTWN_TX_RADIOTAP_PRESENT                      \
        (1 << IEEE80211_RADIOTAP_FLAGS |                \
@@ -1119,12 +1179,19 @@ struct urtwn_softc {
        int                             ac2idx[WME_NUM_AC];
        u_int                           sc_flags;
 #define URTWN_FLAG_CCK_HIPWR   0x01
+#define URTWN_DETACHED         0x02
 
        u_int                           chip;
-#define URTWN_CHIP_92C         0x01
-#define URTWN_CHIP_92C_1T2R    0x02
-#define URTWN_CHIP_UMC         0x04
-#define URTWN_CHIP_UMC_A_CUT   0x08
+#define        URTWN_CHIP_92C          0x01
+#define        URTWN_CHIP_92C_1T2R     0x02
+#define        URTWN_CHIP_UMC          0x04
+#define        URTWN_CHIP_UMC_A_CUT    0x08
+#define        URTWN_CHIP_88E          0x10
+
+       void                            (*sc_rf_write)(struct urtwn_softc *,
+                                           int, uint8_t, uint32_t);
+       int                             (*sc_power_on)(struct urtwn_softc *);
+       int                             (*sc_dma_init)(struct urtwn_softc *);
 
        uint8_t                         board_type;
        uint8_t                         regulatory;
@@ -1152,6 +1219,11 @@ struct urtwn_softc {
        void                            *fw_virtaddr;
 
        struct r92c_rom                 rom;
+       uint8_t                         r88e_rom[512];
+       uint8_t                         cck_tx_pwr[6];
+       uint8_t                         ht40_tx_pwr[5];
+       int8_t                          bw20_tx_pwr_diff;
+       int8_t                          ofdm_tx_pwr_diff;
        uint8_t                         sc_bssid[IEEE80211_ADDR_LEN];
                
        struct callout                  sc_watchdog_ch;
@@ -1189,7 +1261,31 @@ struct urtwn_softc {
 static const struct {
        uint16_t        reg;
        uint8_t         val;
-} rtl8192cu_mac[] = {
+} rtl8188eu_mac[] = {
+       { 0x026, 0x41 }, { 0x027, 0x35 }, { 0x040, 0x00 }, { 0x428, 0x0a },
+       { 0x429, 0x10 }, { 0x430, 0x00 }, { 0x431, 0x01 }, { 0x432, 0x02 },
+       { 0x433, 0x04 }, { 0x434, 0x05 }, { 0x435, 0x06 }, { 0x436, 0x07 },
+       { 0x437, 0x08 }, { 0x438, 0x00 }, { 0x439, 0x00 }, { 0x43a, 0x01 },
+       { 0x43b, 0x02 }, { 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, 0x66 }, { 0x461, 0x66 }, { 0x480, 0x08 }, { 0x4c8, 0xff },
+       { 0x4c9, 0x08 }, { 0x4cc, 0xff }, { 0x4cd, 0xff }, { 0x4ce, 0x01 },
+       { 0x4d3, 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 }, { 0x516, 0x0a },
+       { 0x525, 0x4f }, { 0x550, 0x10 }, { 0x551, 0x10 }, { 0x559, 0x02 },
+       { 0x55d, 0xff }, { 0x605, 0x30 }, { 0x608, 0x0e }, { 0x609, 0x2a },
+       { 0x620, 0xff }, { 0x621, 0xff }, { 0x622, 0xff }, { 0x623, 0xff },
+       { 0x624, 0xff }, { 0x625, 0xff }, { 0x626, 0xff }, { 0x627, 0xff },
+       { 0x652, 0x20 }, { 0x63c, 0x0a }, { 0x63d, 0x0a }, { 0x63e, 0x0e },
+       { 0x63f, 0x0e }, { 0x640, 0x40 }, { 0x66e, 0x05 }, { 0x700, 0x21 },
+       { 0x701, 0x43 }, { 0x702, 0x65 }, { 0x703, 0x87 }, { 0x708, 0x21 },
+       { 0x709, 0x43 }, { 0x70a, 0x65 }, { 0x70b, 0x87 }
+}, rtl8192cu_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 },
@@ -1524,6 +1620,115 @@ static const struct urtwn_bb_prog rtl8188cu_bb_prog = {
        rtl8188ce_agc_vals
 };
 
+/*
+ * RTL8188EU.
+ */
+static const uint16_t rtl8188eu_bb_regs[] = {
+       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, 0x910, 0x914, 0xa00, 0xa04,
+       0xa08, 0xa0c, 0xa10, 0xa14, 0xa18, 0xa1c, 0xa20, 0xa24,
+       0xa28, 0xa2c, 0xa70, 0xa74, 0xa78, 0xa7c, 0xa80, 0xb2c,
+       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, 0xee8, 0xeec, 0xf14, 0xf4c, 0xf00
+};
+
+static const uint32_t rtl8188eu_bb_vals[] = {
+       0x80040000, 0x00000003, 0x0000fc00, 0x0000000a, 0x10001331,
+       0x020c3d10, 0x02200385, 0x00000000, 0x01000100, 0x00390204,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x569a11a9, 0x01000014, 0x66f60110,
+       0x061f0649, 0x00000000, 0x27272700, 0x07000760, 0x25004000,
+       0x00000808, 0x00000000, 0xb0000c1c, 0x00000001, 0x00000000,
+       0xccc000c0, 0x00000800, 0xfffffffe, 0x40302010, 0x00706050,
+       0x00000000, 0x00000023, 0x00000000, 0x81121111, 0x00000002,
+       0x00000201, 0x00d047c8, 0x80ff000c, 0x8c838300, 0x2e7f120f,
+       0x9500bb78, 0x1114d028, 0x00881117, 0x89140f00, 0x1a1b0000,
+       0x090e1317, 0x00000204, 0x00d30000, 0x101fbf00, 0x00000007,
+       0x00000900, 0x225b0606, 0x218075b1, 0x80000000, 0x48071d40,
+       0x03a05611, 0x000000e4, 0x6c6c6c6c, 0x08800000, 0x40000100,
+       0x08800000, 0x40000100, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x69e9ac47, 0x469652af, 0x49795994, 0x0a97971c,
+       0x1f7c403f, 0x000100b7, 0xec020107, 0x007f037f, 0x69553420,
+       0x43bc0094, 0x00013169, 0x00250492, 0x00000000, 0x7112848b,
+       0x47c00bff, 0x00000036, 0x2c7f000d, 0x020610db, 0x0000001f,
+       0x00b91612, 0x390000e4, 0x20f60000, 0x40000100, 0x20200000,
+       0x00091521, 0x00000000, 0x00121820, 0x00007f7f, 0x00000000,
+       0x000300a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x28000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x64b22427, 0x00766932,
+       0x00222222, 0x00000000, 0x37644302, 0x2f97d40c, 0x00000740,
+       0x00020401, 0x0000907f, 0x20010201, 0xa0633333, 0x3333bc43,
+       0x7a8f5b6f, 0xcc979975, 0x00000000, 0x80608000, 0x00000000,
+       0x00127353, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x6437140a, 0x00000000, 0x00000282, 0x30032064, 0x4653de68,
+       0x04518a3c, 0x00002101, 0x2a201c16, 0x1812362e, 0x322c2220,
+       0x000e3c24, 0x2d2d2d2d, 0x2d2d2d2d, 0x0390272d, 0x2d2d2d2d,
+       0x2d2d2d2d, 0x2d2d2d2d, 0x2d2d2d2d, 0x00000000, 0x1000dc1f,
+       0x10008c1f, 0x02140102, 0x681604c2, 0x01007c00, 0x01004800,
+       0xfb000000, 0x000028d1, 0x1000dc1f, 0x10008c1f, 0x02140102,
+       0x28160d05, 0x00000008, 0x001b25a4, 0x00c00014, 0x00c00014,
+       0x01000014, 0x01000014, 0x01000014, 0x01000014, 0x00c00014,
+       0x01000014, 0x00c00014, 0x00c00014, 0x00c00014, 0x00c00014,
+       0x00000014, 0x00000014, 0x21555448, 0x01c00014, 0x00000003,
+       0x00000000, 0x00000300
+};
+
+static const uint32_t rtl8188eu_agc_vals[] = {
+       0xfb000001, 0xfb010001, 0xfb020001, 0xfb030001, 0xfb040001,
+       0xfb050001, 0xfa060001, 0xf9070001, 0xf8080001, 0xf7090001,
+       0xf60a0001, 0xf50b0001, 0xf40c0001, 0xf30d0001, 0xf20e0001,
+       0xf10f0001, 0xf0100001, 0xef110001, 0xee120001, 0xed130001,
+       0xec140001, 0xeb150001, 0xea160001, 0xe9170001, 0xe8180001,
+       0xe7190001, 0xe61a0001, 0xe51b0001, 0xe41c0001, 0xe31d0001,
+       0xe21e0001, 0xe11f0001, 0x8a200001, 0x89210001, 0x88220001,
+       0x87230001, 0x86240001, 0x85250001, 0x84260001, 0x83270001,
+       0x82280001, 0x6b290001, 0x6a2a0001, 0x692b0001, 0x682c0001,
+       0x672d0001, 0x662e0001, 0x652f0001, 0x64300001, 0x63310001,
+       0x62320001, 0x61330001, 0x46340001, 0x45350001, 0x44360001,
+       0x43370001, 0x42380001, 0x41390001, 0x403a0001, 0x403b0001,
+       0x403c0001, 0x403d0001, 0x403e0001, 0x403f0001, 0xfb400001,
+       0xfb410001, 0xfb420001, 0xfb430001, 0xfb440001, 0xfb450001,
+       0xfb460001, 0xfb470001, 0xfb480001, 0xfa490001, 0xf94a0001,
+       0xf84B0001, 0xf74c0001, 0xf64d0001, 0xf54e0001, 0xf44f0001,
+       0xf3500001, 0xf2510001, 0xf1520001, 0xf0530001, 0xef540001,
+       0xee550001, 0xed560001, 0xec570001, 0xeb580001, 0xea590001,
+       0xe95a0001, 0xe85b0001, 0xe75c0001, 0xe65d0001, 0xe55e0001,
+       0xe45f0001, 0xe3600001, 0xe2610001, 0xc3620001, 0xc2630001,
+       0xc1640001, 0x8b650001, 0x8a660001, 0x89670001, 0x88680001,
+       0x87690001, 0x866a0001, 0x856b0001, 0x846c0001, 0x676d0001,
+       0x666e0001, 0x656f0001, 0x64700001, 0x63710001, 0x62720001,
+       0x61730001, 0x60740001, 0x46750001, 0x45760001, 0x44770001,
+       0x43780001, 0x42790001, 0x417a0001, 0x407b0001, 0x407c0001,
+       0x407d0001, 0x407e0001, 0x407f0001
+};
+
+static const struct urtwn_bb_prog rtl8188eu_bb_prog = {
+       NELEM(rtl8188eu_bb_regs),
+       rtl8188eu_bb_regs,
+       rtl8188eu_bb_vals,
+       NELEM(rtl8188eu_agc_vals),
+       rtl8188eu_agc_vals
+};
+
 /*
  * RTL8188RU.
  */
@@ -1787,6 +1992,47 @@ static const struct urtwn_rf_prog rtl8188cu_rf_prog[] = {
        }
 };
 
+/*
+ * RTL8188EU.
+ */
+static const uint8_t rtl8188eu_rf_regs[] = {
+       0x00, 0x08, 0x18, 0x19, 0x1e, 0x1f, 0x2f, 0x3f, 0x42, 0x57,
+       0x58, 0x67, 0x83, 0xb0, 0xb1, 0xb2, 0xb4, 0xb6, 0xb7, 0xb8,
+       0xb9, 0xba, 0xbb, 0xbf, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+       0xc8, 0xc9, 0xca, 0xdf, 0xef, 0x51, 0x52, 0x53, 0x56,
+       0x35, 0x35, 0x35, 0x36, 0x36, 0x36, 0x36, 0xb6, 0x18, 0x5a,
+       0x19, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34,
+       0x34, 0x34, 0x00, 0x84, 0x86, 0x87, 0x8e, 0x8f, 0xef, 0x3b,
+       0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+       0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0xef, 0x00, 0x18, 0xfe, 0xfe,
+       0x1f, 0xfe, 0xfe, 0x1e, 0x1f, 0x00
+};
+
+static const uint32_t rtl8188eu_rf_vals[] = {
+       0x30000, 0x84000, 0x00407, 0x00012, 0x80009, 0x00880, 0x1a060,
+       0x00000, 0x060c0, 0xd0000, 0xbe180, 0x01552, 0x00000, 0xff8fc,
+       0x54400, 0xccc19, 0x43003, 0x4953e, 0x1c718, 0x060ff, 0x80001,
+       0x40000, 0x00400, 0xc0000, 0x02400, 0x00009, 0x40c91, 0x99999,
+       0x000a3, 0x88820, 0x76c06, 0x00000, 0x80000, 0x00180, 0x001a0,
+       0x6b27d, 0x7e49d, 0x00073, 0x51ff3, 0x00086, 0x00186,
+       0x00286, 0x01c25, 0x09c25, 0x11c25, 0x19c25, 0x48538, 0x00c07,
+       0x4bd00, 0x739d0, 0x0adf3, 0x09df0, 0x08ded, 0x07dea, 0x06de7,
+       0x054ee, 0x044eb, 0x034e8, 0x0246b, 0x01468, 0x0006d, 0x30159,
+       0x68200, 0x000ce, 0x48a00, 0x65540, 0x88000, 0x020a0, 0xf02b0,
+       0xef7b0, 0xd4fb0, 0xcf060, 0xb0090, 0xa0080, 0x90080, 0x8f780,
+       0x722b0, 0x6f7b0, 0x54fb0, 0x4f060, 0x30090, 0x20080, 0x10080,
+       0x0f780, 0x000a0, 0x10159, 0x0f407, 0x00000, 0x00000, 0x80003,
+       0x00000, 0x00000, 0x00001, 0x80000, 0x33e60
+};
+
+static const struct urtwn_rf_prog rtl8188eu_rf_prog[] = {
+       {
+               NELEM(rtl8188eu_rf_regs),
+               rtl8188eu_rf_regs,
+               rtl8188eu_rf_vals
+       }
+};
+
 /*
  * RTL8188RU.
  */
@@ -1826,6 +2072,10 @@ struct urtwn_txpwr {
        uint8_t pwr[3][28];
 };
 
+struct urtwn_r88e_txpwr {
+       uint8_t pwr[6][28];
+};
+
 /*
  * Per RF chain/group/rate Tx gain values.
  */
@@ -1894,3 +2144,44 @@ static const struct urtwn_txpwr rtl8188ru_txagc[] = {
        }
        } }
 };
+
+static const struct urtwn_r88e_txpwr rtl8188eu_txagc[] = {
+       { {     /* Chain 0. */
+       {       /* 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. */
+       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 3. */
+       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 4. */
+       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 5. */
+       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. */
+       }
+       } }
+};
index a6ce159..a51dcb3 100644 (file)
@@ -1,5 +1,5 @@
-# $FreeBSD: src/sys/modules/usb/urtwnfw/Makefile,v 1.1 2013/07/11 00:28:45 svnexp Exp $
+# $FreeBSD: head/sys/modules/usb/urtwnfw/Makefile 264912 2014-04-25 08:01:22Z kevlo $
 
-SUBDIR=        urtwnrtl8192cT urtwnrtl8192cU
+SUBDIR=        urtwnrtl8188eu urtwnrtl8192cT urtwnrtl8192cU
 
 .include <bsd.subdir.mk>
diff --git a/sys/bus/u4b/wlan/urtwnfw/urtwnrtl8188eu/Makefile b/sys/bus/u4b/wlan/urtwnfw/urtwnrtl8188eu/Makefile
new file mode 100644 (file)
index 0000000..f3c4fa3
--- /dev/null
@@ -0,0 +1,6 @@
+# $FreeBSD: head/sys/modules/usb/urtwnfw/urtwnrtl8188eu/Makefile 264912 2014-04-25 08:01:22Z kevlo $
+
+KMOD=  urtwn-rtl8188eufw
+IMG=   urtwn-rtl8188eufw
+
+.include <bsd.kmod.mk>
index 5c5e40c..74277df 100644 (file)
@@ -2352,30 +2352,44 @@ run.fw                                  optional runfw usb              \
        no-obj no-implicit-rule                                         \
        clean           "run.fw"
 bus/u4b/wlan/if_urtwn.c                        optional urtwn usb
-urtwn-rtl8192cfwT.c                    optional urtwnfw usb    \
+urtwn-rtl8188eufw.c                    optional urtwnfw usb            \
+       compile-with    "${AWK} -f $S/tools/fw_stub.awk urtwn-rtl8188eufw.fw:urtwn-rtl8188eufw:111 -murtwn-rtl8188eufw -c${.TARGET}" \
+       no-implicit-rule before-depend local                            \
+       clean           "urtwn-rtl8188eufw.c"
+urtwn-rtl8188eufw.fwo                  optional urtwnfw usb            \
+       dependency      "urtwn-rtl8188eufw.fw"                          \
+       compile-with    "${NORMAL_FWO}"                                 \
+       no-implicit-rule                                                \
+       clean           "urtwn-rtl8188eufw.fwo"
+urtwn-rtl8188eufw.fw                   optional urtwnfw usb            \
+       dependency      "$S/contrib/dev/urtwn/urtwn-rtl8188eufw.fw.uu"  \
+       compile-with    "${NORMAL_FW}"                                  \
+       no-obj no-implicit-rule                                         \
+       clean           "urtwn-rtl8188eufw.fw"
+urtwn-rtl8192cfwT.c                    optional urtwnfw usb            \
        compile-with    "${AWK} -f $S/tools/fw_stub.awk urtwn-rtl8192cfwT.fw:urtwn-rtl8192cfwT:111 -murtwn-rtl8192cfwT -c${.TARGET}" \
        no-implicit-rule before-depend local                            \
        clean           "urtwn-rtl8192cfwT.c"
-urtwn-rtl8192cfwT.fwo                  optional urtwnfw usb    \
+urtwn-rtl8192cfwT.fwo                  optional urtwnfw usb            \
        dependency      "urtwn-rtl8192cfwT.fw"                          \
        compile-with    "${NORMAL_FWO}"                                 \
        no-implicit-rule                                                \
        clean           "urtwn-rtl8192cfwT.fwo"
-urtwn-rtl8192cfwT.fw                   optional urtwnfw usb    \
+urtwn-rtl8192cfwT.fw                   optional urtwnfw usb            \
        dependency      "$S/contrib/dev/urtwn/urtwn-rtl8192cfwT.fw.uu"  \
        compile-with    "${NORMAL_FW}"                                  \
        no-obj no-implicit-rule                                         \
        clean           "urtwn-rtl8192cfwT.fw"
-urtwn-rtl8192cfwU.c                    optional urtwnfw usb    \
+urtwn-rtl8192cfwU.c                    optional urtwnfw usb            \
        compile-with    "${AWK} -f $S/tools/fw_stub.awk urtwn-rtl8192cfwU.fw:urtwn-rtl8192cfwU:111 -murtwn-rtl8192cfwU -c${.TARGET}" \
        no-implicit-rule before-depend local                            \
        clean           "urtwn-rtl8192cfwU.c"
-urtwn-rtl8192cfwU.fwo                  optional urtwnfw usb    \
+urtwn-rtl8192cfwU.fwo                  optional urtwnfw usb            \
        dependency      "urtwn-rtl8192cfwU.fw"                          \
        compile-with    "${NORMAL_FWO}"                                 \
        no-implicit-rule                                                \
        clean           "urtwn-rtl8192cfwU.fwo"
-urtwn-rtl8192cfwU.fw                   optional urtwnfw usb    \
+urtwn-rtl8192cfwU.fw                   optional urtwnfw usb            \
        dependency      "$S/contrib/dev/urtwn/urtwn-rtl8192cfwU.fw.uu"  \
        compile-with    "${NORMAL_FW}"                                  \
        no-obj no-implicit-rule                                         \
diff --git a/sys/contrib/dev/urtwn/urtwn-rtl8188eufw.fw.uu b/sys/contrib/dev/urtwn/urtwn-rtl8188eufw.fw.uu
new file mode 100644 (file)
index 0000000..1039230
--- /dev/null
@@ -0,0 +1,312 @@
+begin 644 urtwn-rtl8188eufw.fw.uu
+MX8@0``L``0`!(1$G,#8``"T'```````````````````"14X`````````````
+M````````P6\`````````````````````````````````````````````````
+M````````````H>8````````"5O<`````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+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
+L\.#_D(%&X+4''I`&DN!4''`+$D\+D(%)X`3P@`:0!I)T'/#DD(%(\"(`NXX`
+`
+end