root/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_mtw.c
/*-
 * Copyright (c) 2008-2010 Damien Bergamini <damien.bergamini@free.fr>
 * Copyright (c) 2013-2014 Kevin Lo
 * Copyright (c) 2021 James Hastings
 * Ported to FreeBSD by Jesper Schmitz Mouridsen jsm@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
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * MediaTek MT7601U 802.11b/g/n WLAN.
 */

#include "opt_wlan.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/eventhandler.h>
#include <sys/firmware.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
#include <sys/linker.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>

#include <net/bpf.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if_var.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_radiotap.h>
#include <net80211/ieee80211_ratectl.h>
#include <net80211/ieee80211_regdomain.h>
#ifdef  IEEE80211_SUPPORT_SUPERG
#include <net80211/ieee80211_superg.h>
#endif
#include <netinet/if_ether.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>

#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>

#include "usbdevs.h"

#define USB_DEBUG_VAR mtw_debug
#include <dev/usb/usb_debug.h>
#ifndef __HAIKU__
#include <dev/usb/usb_msctest.h>
#endif

#include "if_mtwreg.h"
#include "if_mtwvar.h"

#define MTW_DEBUG

#ifdef MTW_DEBUG
int mtw_debug;
static SYSCTL_NODE(_hw_usb, OID_AUTO, mtw, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
    "USB mtw");
SYSCTL_INT(_hw_usb_mtw, OID_AUTO, debug, CTLFLAG_RWTUN, &mtw_debug, 0,
    "mtw debug level");

enum {
        MTW_DEBUG_XMIT = 0x00000001,      /* basic xmit operation */
        MTW_DEBUG_XMIT_DESC = 0x00000002, /* xmit descriptors */
        MTW_DEBUG_RECV = 0x00000004,      /* basic recv operation */
        MTW_DEBUG_RECV_DESC = 0x00000008, /* recv descriptors */
        MTW_DEBUG_STATE = 0x00000010,     /* 802.11 state transitions */
        MTW_DEBUG_RATE = 0x00000020,      /* rate adaptation */
        MTW_DEBUG_USB = 0x00000040,       /* usb requests */
        MTW_DEBUG_FIRMWARE = 0x00000080,  /* firmware(9) loading debug */
        MTW_DEBUG_BEACON = 0x00000100,    /* beacon handling */
        MTW_DEBUG_INTR = 0x00000200,      /* ISR */
        MTW_DEBUG_TEMP = 0x00000400,      /* temperature calibration */
        MTW_DEBUG_ROM = 0x00000800,       /* various ROM info */
        MTW_DEBUG_KEY = 0x00001000,       /* crypto keys management */
        MTW_DEBUG_TXPWR = 0x00002000,     /* dump Tx power values */
        MTW_DEBUG_RSSI = 0x00004000,      /* dump RSSI lookups */
        MTW_DEBUG_RESET = 0x00008000,     /* initialization progress */
        MTW_DEBUG_CALIB = 0x00010000,     /* calibration progress */
        MTW_DEBUG_CMD = 0x00020000,       /* command queue */
        MTW_DEBUG_ANY = 0xffffffff
};

#define MTW_DPRINTF(_sc, _m, ...)                                  \
        do {                                                       \
                if (mtw_debug & (_m))                              \
                        device_printf((_sc)->sc_dev, __VA_ARGS__); \
        } while (0)

#else
#define MTW_DPRINTF(_sc, _m, ...) \
        do {                      \
                (void)_sc;        \
        } while (0)
#endif

#define IEEE80211_HAS_ADDR4(wh) IEEE80211_IS_DSTODS(wh)

/* NB: "11" is the maximum number of padding bytes needed for Tx */
#define MTW_MAX_TXSZ \
        (sizeof(struct mtw_txd) + sizeof(struct mtw_txwi) + MCLBYTES + 11)

/*
 * Because of LOR in mtw_key_delete(), use atomic instead.
 * '& MTW_CMDQ_MASQ' is to loop cmdq[].
 */
#define MTW_CMDQ_GET(c) (atomic_fetchadd_32((c), 1) & MTW_CMDQ_MASQ)

static const STRUCT_USB_HOST_ID mtw_devs[] = {
#define MTW_DEV(v, p)                                         \
        {                                                     \
                USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) \
        }
        MTW_DEV(EDIMAX, MT7601U),
        MTW_DEV(RALINK, MT7601U),
        MTW_DEV(XIAOMI, MT7601U)
};
#undef MTW_DEV

static device_probe_t mtw_match;
static device_attach_t mtw_attach;
static device_detach_t mtw_detach;

static usb_callback_t mtw_bulk_rx_callback;
static usb_callback_t mtw_bulk_tx_callback0;
static usb_callback_t mtw_bulk_tx_callback1;
static usb_callback_t mtw_bulk_tx_callback2;
static usb_callback_t mtw_bulk_tx_callback3;
static usb_callback_t mtw_bulk_tx_callback4;
static usb_callback_t mtw_bulk_tx_callback5;
static usb_callback_t mtw_fw_callback;

static void mtw_autoinst(void *, struct usb_device *, struct usb_attach_arg *);
static int mtw_driver_loaded(struct module *, int, void *);
static void mtw_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error,
                                  u_int index);
static struct ieee80211vap *mtw_vap_create(struct ieee80211com *,
        const char[IFNAMSIZ], int, enum ieee80211_opmode, int,
        const uint8_t[IEEE80211_ADDR_LEN], const uint8_t[IEEE80211_ADDR_LEN]);
static void mtw_vap_delete(struct ieee80211vap *);
static void mtw_cmdq_cb(void *, int);
static void mtw_setup_tx_list(struct mtw_softc *, struct mtw_endpoint_queue *);
static void mtw_unsetup_tx_list(struct mtw_softc *,
                                struct mtw_endpoint_queue *);
static void mtw_load_microcode(void *arg);

static usb_error_t mtw_do_request(struct mtw_softc *,
                                  struct usb_device_request *, void *);
static int mtw_read(struct mtw_softc *, uint16_t, uint32_t *);
static int mtw_read_region_1(struct mtw_softc *, uint16_t, uint8_t *, int);
static int mtw_write_2(struct mtw_softc *, uint16_t, uint16_t);
static int mtw_write(struct mtw_softc *, uint16_t, uint32_t);
static int mtw_write_region_1(struct mtw_softc *, uint16_t, uint8_t *, int);
static int mtw_set_region_4(struct mtw_softc *, uint16_t, uint32_t, int);
static int mtw_efuse_read_2(struct mtw_softc *, uint16_t, uint16_t *);
static int mtw_bbp_read(struct mtw_softc *, uint8_t, uint8_t *);
static int mtw_bbp_write(struct mtw_softc *, uint8_t, uint8_t);
static int mtw_mcu_cmd(struct mtw_softc *sc, uint8_t cmd, void *buf, int len);
static void mtw_get_txpower(struct mtw_softc *);
static int mtw_read_eeprom(struct mtw_softc *);
static struct ieee80211_node *mtw_node_alloc(struct ieee80211vap *,
    const uint8_t mac[IEEE80211_ADDR_LEN]);
static int mtw_media_change(if_t);
static int mtw_newstate(struct ieee80211vap *, enum ieee80211_state, int);
static int mtw_wme_update(struct ieee80211com *);
static void mtw_key_set_cb(void *);
static int mtw_key_set(struct ieee80211vap *, struct ieee80211_key *);
static void mtw_key_delete_cb(void *);
static int mtw_key_delete(struct ieee80211vap *, struct ieee80211_key *);
static void mtw_ratectl_to(void *);
static void mtw_ratectl_cb(void *, int);
static void mtw_drain_fifo(void *);
static void mtw_iter_func(void *, struct ieee80211_node *);
static void mtw_newassoc_cb(void *);
static void mtw_newassoc(struct ieee80211_node *, int);
static int mtw_mcu_radio(struct mtw_softc *sc, int func, uint32_t val);
static void mtw_recv_mgmt(struct ieee80211_node *, struct mbuf *, int,
    const struct ieee80211_rx_stats *, int, int);
static void mtw_rx_frame(struct mtw_softc *, struct mbuf *, uint32_t);
static void mtw_tx_free(struct mtw_endpoint_queue *pq, struct mtw_tx_data *,
    int);
static void mtw_set_tx_desc(struct mtw_softc *, struct mtw_tx_data *);
static int mtw_tx(struct mtw_softc *, struct mbuf *, struct ieee80211_node *);
static int mtw_tx_mgt(struct mtw_softc *, struct mbuf *,
    struct ieee80211_node *);
static int mtw_sendprot(struct mtw_softc *, const struct mbuf *,
    struct ieee80211_node *, int, int);
static int mtw_tx_param(struct mtw_softc *, struct mbuf *,
    struct ieee80211_node *, const struct ieee80211_bpf_params *);
static int mtw_raw_xmit(struct ieee80211_node *, struct mbuf *,
    const struct ieee80211_bpf_params *);
static int mtw_transmit(struct ieee80211com *, struct mbuf *);
static void mtw_start(struct mtw_softc *);
static void mtw_parent(struct ieee80211com *);
static void mtw_select_chan_group(struct mtw_softc *, int);

static int mtw_set_chan(struct mtw_softc *, struct ieee80211_channel *);
static void mtw_set_channel(struct ieee80211com *);
static void mtw_getradiocaps(struct ieee80211com *, int, int *,
    struct ieee80211_channel[]);
static void mtw_scan_start(struct ieee80211com *);
static void mtw_scan_end(struct ieee80211com *);
static void mtw_update_beacon(struct ieee80211vap *, int);
static void mtw_update_beacon_cb(void *);
static void mtw_updateprot(struct ieee80211com *);
static void mtw_updateprot_cb(void *);
static void mtw_usb_timeout_cb(void *);
static int mtw_reset(struct mtw_softc *sc);
static void mtw_enable_tsf_sync(struct mtw_softc *);


static void mtw_enable_mrr(struct mtw_softc *);
static void mtw_set_txpreamble(struct mtw_softc *);
static void mtw_set_basicrates(struct mtw_softc *);
static void mtw_set_leds(struct mtw_softc *, uint16_t);
static void mtw_set_bssid(struct mtw_softc *, const uint8_t *);
static void mtw_set_macaddr(struct mtw_softc *, const uint8_t *);
static void mtw_updateslot(struct ieee80211com *);
static void mtw_updateslot_cb(void *);
static void mtw_update_mcast(struct ieee80211com *);
static int8_t mtw_rssi2dbm(struct mtw_softc *, uint8_t, uint8_t);
static void mtw_update_promisc_locked(struct mtw_softc *);
static void mtw_update_promisc(struct ieee80211com *);
static int mtw_txrx_enable(struct mtw_softc *);
static void mtw_init_locked(struct mtw_softc *);
static void mtw_stop(void *);
static void mtw_delay(struct mtw_softc *, u_int);
static void mtw_update_chw(struct ieee80211com *ic);
static int mtw_ampdu_enable(struct ieee80211_node *ni,
    struct ieee80211_tx_ampdu *tap);

static eventhandler_tag mtw_etag;

static const struct {
        uint8_t reg;
        uint8_t val;
} mt7601_rf_bank0[] = { MT7601_BANK0_RF },
  mt7601_rf_bank4[] = { MT7601_BANK4_RF },
  mt7601_rf_bank5[] = { MT7601_BANK5_RF };
static const struct {
        uint32_t reg;
        uint32_t val;
} mt7601_def_mac[] = { MT7601_DEF_MAC };
static const struct {
        uint8_t reg;
        uint8_t val;
} mt7601_def_bbp[] = { MT7601_DEF_BBP };


static const struct {
        u_int chan;
        uint8_t r17, r18, r19, r20;
} mt7601_rf_chan[] = { MT7601_RF_CHAN };


static const struct usb_config mtw_config[MTW_N_XFER] = {
        [MTW_BULK_RX] = {
                .type = UE_BULK,
                .endpoint = UE_ADDR_ANY,
                .direction = UE_DIR_IN,
                .bufsize = MTW_MAX_RXSZ,
                .flags = {.pipe_bof = 1,
                          .short_xfer_ok = 1,},
                .callback = mtw_bulk_rx_callback,
        },
        [MTW_BULK_TX_BE] = {
                .type = UE_BULK,
                .endpoint = UE_ADDR_ANY,
                .direction = UE_DIR_OUT,
                .bufsize = MTW_MAX_TXSZ,
                .flags = {.pipe_bof = 1,
                          .force_short_xfer = 0,},
                .callback = mtw_bulk_tx_callback0,
                .timeout = 5000,        /* ms */
    },
        [MTW_BULK_TX_BK] = {
                .type = UE_BULK,
                .endpoint = UE_ADDR_ANY,
                .direction = UE_DIR_OUT,
                .bufsize = MTW_MAX_TXSZ,
                .flags = {.pipe_bof = 1,
                          .force_short_xfer = 1,},
                .callback = mtw_bulk_tx_callback1,
                .timeout = 5000,        /* ms */
        },
        [MTW_BULK_TX_VI] = {
            .type = UE_BULK,
            .endpoint = UE_ADDR_ANY,
            .direction = UE_DIR_OUT,
            .bufsize = MTW_MAX_TXSZ,
            .flags = {.pipe_bof = 1,
                      .force_short_xfer = 1,},
            .callback = mtw_bulk_tx_callback2,
            .timeout = 5000,    /* ms */
        },
        [MTW_BULK_TX_VO] = {
                .type = UE_BULK,
                .endpoint = UE_ADDR_ANY,
                .direction = UE_DIR_OUT,
                .bufsize = MTW_MAX_TXSZ,
                .flags = {.pipe_bof = 1,
                          .force_short_xfer = 1,},
                .callback = mtw_bulk_tx_callback3,
                .timeout = 5000,        /* ms */
    },
        [MTW_BULK_TX_HCCA] = {
                .type = UE_BULK,
                .endpoint = UE_ADDR_ANY,
                .direction = UE_DIR_OUT,
                .bufsize = MTW_MAX_TXSZ,
                .flags = {.pipe_bof = 1,
                          .force_short_xfer = 1, .no_pipe_ok = 1,},
                .callback = mtw_bulk_tx_callback4,
                .timeout = 5000,        /* ms */
    },
        [MTW_BULK_TX_PRIO] = {
                .type = UE_BULK,
                .endpoint = UE_ADDR_ANY,
                .direction = UE_DIR_OUT,
                .bufsize = MTW_MAX_TXSZ,
                .flags = {.pipe_bof = 1,
                          .force_short_xfer = 1, .no_pipe_ok = 1,},
                .callback = mtw_bulk_tx_callback5,
                .timeout = 5000,        /* ms */
        },

        [MTW_BULK_FW_CMD] = {
                .type = UE_BULK,
                .endpoint = UE_ADDR_ANY,
                .direction = UE_DIR_OUT,
                .bufsize = 0x2c44,
                .flags = {.pipe_bof = 1,
                          .force_short_xfer = 1, .no_pipe_ok = 1,},
                .callback = mtw_fw_callback,

        },

        [MTW_BULK_RAW_TX] = {
                .type = UE_BULK,
                .ep_index = 0,
                .endpoint = UE_ADDR_ANY,
                .direction = UE_DIR_OUT,
                .bufsize = MTW_MAX_TXSZ,
                .flags = {.pipe_bof = 1,
                          .force_short_xfer = 1, .no_pipe_ok = 1,},
                .callback = mtw_bulk_tx_callback0,
                .timeout = 5000,        /* ms */
        },

};
static uint8_t mtw_wme_ac_xfer_map[4] = {
        [WME_AC_BE] = MTW_BULK_TX_BE,
        [WME_AC_BK] = MTW_BULK_TX_BK,
        [WME_AC_VI] = MTW_BULK_TX_VI,
        [WME_AC_VO] = MTW_BULK_TX_VO,
};
static void
mtw_autoinst(void *arg, struct usb_device *udev, struct usb_attach_arg *uaa)
{
#ifndef __HAIKU__
        struct usb_interface *iface;
        struct usb_interface_descriptor *id;

        if (uaa->dev_state != UAA_DEV_READY)
                return;

        iface = usbd_get_iface(udev, 0);
        if (iface == NULL)
                return;
        id = iface->idesc;
        if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
                return;
        if (usbd_lookup_id_by_uaa(mtw_devs, sizeof(mtw_devs), uaa))
                return;

        if (usb_msc_eject(udev, 0, MSC_EJECT_STOPUNIT) == 0)
                uaa->dev_state = UAA_DEV_EJECTING;
#endif
}

static int
mtw_driver_loaded(struct module *mod, int what, void *arg)
{
        switch (what) {
        case MOD_LOAD:
                mtw_etag = EVENTHANDLER_REGISTER(usb_dev_configured,
                    mtw_autoinst, NULL, EVENTHANDLER_PRI_ANY);
                break;
        case MOD_UNLOAD:
                EVENTHANDLER_DEREGISTER(usb_dev_configured, mtw_etag);
                break;
        default:
                return (EOPNOTSUPP);
        }
        return (0);
}

static const char *
mtw_get_rf(int rev)
{
        switch (rev) {
        case MT7601_RF_7601:
                return ("MT7601");
        case MT7610_RF_7610:
                return ("MT7610");
        case MT7612_RF_7612:
                return ("MT7612");
        }
        return ("unknown");
}
static int
mtw_wlan_enable(struct mtw_softc *sc, int enable)
{
        uint32_t tmp;
        int error = 0;

        if (enable) {
                mtw_read(sc, MTW_WLAN_CTRL, &tmp);
                if (sc->asic_ver == 0x7612)
                        tmp &= ~0xfffff000;

                tmp &= ~MTW_WLAN_CLK_EN;
                tmp |= MTW_WLAN_EN;
                mtw_write(sc, MTW_WLAN_CTRL, tmp);
                mtw_delay(sc, 2);

                tmp |= MTW_WLAN_CLK_EN;
                if (sc->asic_ver == 0x7612) {
                        tmp |= (MTW_WLAN_RESET | MTW_WLAN_RESET_RF);
                }
                mtw_write(sc, MTW_WLAN_CTRL, tmp);
                mtw_delay(sc, 2);

                mtw_read(sc, MTW_OSC_CTRL, &tmp);
                tmp |= MTW_OSC_EN;
                mtw_write(sc, MTW_OSC_CTRL, tmp);
                tmp |= MTW_OSC_CAL_REQ;
                mtw_write(sc, MTW_OSC_CTRL, tmp);
        } else {
                mtw_read(sc, MTW_WLAN_CTRL, &tmp);
                tmp &= ~(MTW_WLAN_CLK_EN | MTW_WLAN_EN);
                mtw_write(sc, MTW_WLAN_CTRL, tmp);

                mtw_read(sc, MTW_OSC_CTRL, &tmp);
                tmp &= ~MTW_OSC_EN;
                mtw_write(sc, MTW_OSC_CTRL, tmp);
        }
        return (error);
}

static int
mtw_read_cfg(struct mtw_softc *sc, uint16_t reg, uint32_t *val)
{
        usb_device_request_t req;
        uint32_t tmp;
        uint16_t actlen;
        int error;

        req.bmRequestType = UT_READ_VENDOR_DEVICE;
        req.bRequest = MTW_READ_CFG;
        USETW(req.wValue, 0);
        USETW(req.wIndex, reg);
        USETW(req.wLength, 4);
        error = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, &tmp, 0,
            &actlen, 1000);

        if (error == 0)
                *val = le32toh(tmp);
        else
                *val = 0xffffffff;
        return (error);
}

static int
mtw_match(device_t self)
{
        struct usb_attach_arg *uaa = device_get_ivars(self);

        if (uaa->usb_mode != USB_MODE_HOST)
                return (ENXIO);
        if (uaa->info.bConfigIndex != 0)
                return (ENXIO);
        if (uaa->info.bIfaceIndex != 0)
                return (ENXIO);

        return (usbd_lookup_id_by_uaa(mtw_devs, sizeof(mtw_devs), uaa));
}

static int
mtw_attach(device_t self)
{
        struct mtw_softc *sc = device_get_softc(self);
        struct usb_attach_arg *uaa = device_get_ivars(self);
        struct ieee80211com *ic = &sc->sc_ic;
        uint32_t ver;
        int i, ret;
        uint32_t tmp;
        uint8_t iface_index;
        int ntries, error;

        device_set_usb_desc(self);
        sc->sc_udev = uaa->device;
        sc->sc_dev = self;
        sc->sc_sent = 0;

        mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev),
                 MTX_NETWORK_LOCK, MTX_DEF);

        iface_index = 0;

        error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
            mtw_config, MTW_N_XFER, sc, &sc->sc_mtx);
        if (error) {
                device_printf(sc->sc_dev,
                    "could not allocate USB transfers, "
                    "err=%s\n",
                    usbd_errstr(error));
                goto detach;
        }
        for (i = 0; i < 4; i++) {
                sc->txd_fw[i] = (struct mtw_txd_fw *)
                    malloc(sizeof(struct mtw_txd_fw),
                        M_USBDEV, M_NOWAIT | M_ZERO);
        }
        MTW_LOCK(sc);
        sc->sc_idx = 0;
        mbufq_init(&sc->sc_snd, ifqmaxlen);

        /*enable WLAN core */
        if ((error = mtw_wlan_enable(sc, 1)) != 0) {
                device_printf(sc->sc_dev, "could not enable WLAN core\n");
                return (ENXIO);
        }

        /* wait for the chip to settle */
        DELAY(100);
        for (ntries = 0; ntries < 100; ntries++) {
                if (mtw_read(sc, MTW_ASIC_VER, &ver) != 0) {
                        goto detach;
                }
                if (ver != 0 && ver != 0xffffffff)
                        break;
                DELAY(10);
        }
        if (ntries == 100) {
                device_printf(sc->sc_dev,
                    "timeout waiting for NIC to initialize\n");
                goto detach;
        }
        sc->asic_ver = ver >> 16;
        sc->asic_rev = ver & 0xffff;
        DELAY(100);
        if (sc->asic_ver != 0x7601) {
                device_printf(sc->sc_dev,
                    "Your revision 0x04%x is not supported yet\n",
                 sc->asic_rev);
                goto detach;
        }


        if (mtw_read(sc, MTW_MAC_VER_ID, &tmp) != 0)
                goto detach;
        sc->mac_rev = tmp & 0xffff;

        mtw_load_microcode(sc);
        ret = msleep(&sc->fwloading, &sc->sc_mtx, 0, "fwload", 3 * hz);
        if (ret == EWOULDBLOCK || sc->fwloading != 1) {
                device_printf(sc->sc_dev,
                    "timeout waiting for MCU to initialize\n");
                goto detach;
        }

        sc->sc_srom_read = mtw_efuse_read_2;
        /* retrieve RF rev. no and various other things from EEPROM */
        mtw_read_eeprom(sc);

        device_printf(sc->sc_dev,
            "MAC/BBP RT%04X (rev 0x%04X), RF %s (MIMO %dT%dR), address %s\n",
            sc->asic_ver, sc->mac_rev, mtw_get_rf(sc->rf_rev), sc->ntxchains,
            sc->nrxchains, ether_sprintf(ic->ic_macaddr));
        DELAY(100);

        //mtw_set_leds(sc,5);
        // mtw_mcu_radio(sc,0x31,0);
        MTW_UNLOCK(sc);


        ic->ic_softc = sc;
        ic->ic_name = device_get_nameunit(self);
        ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
        ic->ic_opmode = IEEE80211_M_STA;   /* default to BSS mode */

        ic->ic_caps = IEEE80211_C_STA | /* station mode supported */
                IEEE80211_C_MONITOR |   /* monitor mode supported */
                IEEE80211_C_IBSS |
                IEEE80211_C_HOSTAP |
                IEEE80211_C_WDS | /* 4-address traffic works */
                IEEE80211_C_MBSS |
                IEEE80211_C_SHPREAMBLE | /* short preamble supported */
                IEEE80211_C_SHSLOT |     /* short slot time supported */
                IEEE80211_C_WME |             /* WME */
                IEEE80211_C_WPA;             /* WPA1|WPA2(RSN) */
            device_printf(sc->sc_dev, "[HT] Enabling 802.11n\n");
            ic->ic_htcaps =       IEEE80211_HTC_HT
            | IEEE80211_HTC_AMPDU
            | IEEE80211_HTC_AMSDU
            | IEEE80211_HTCAP_MAXAMSDU_3839
            | IEEE80211_HTCAP_SMPS_OFF;

        ic->ic_rxstream = sc->nrxchains;
        ic->ic_txstream = sc->ntxchains;

        ic->ic_cryptocaps = IEEE80211_CRYPTO_WEP | IEEE80211_CRYPTO_AES_CCM |
            IEEE80211_CRYPTO_AES_OCB | IEEE80211_CRYPTO_TKIP |
            IEEE80211_CRYPTO_TKIPMIC;

        ic->ic_flags |= IEEE80211_F_DATAPAD;
        ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;

        mtw_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
            ic->ic_channels);

        ieee80211_ifattach(ic);

        ic->ic_scan_start = mtw_scan_start;
        ic->ic_scan_end = mtw_scan_end;
        ic->ic_set_channel = mtw_set_channel;
        ic->ic_getradiocaps = mtw_getradiocaps;
        ic->ic_node_alloc = mtw_node_alloc;
        ic->ic_newassoc = mtw_newassoc;
        ic->ic_update_mcast = mtw_update_mcast;
        ic->ic_updateslot = mtw_updateslot;
        ic->ic_wme.wme_update = mtw_wme_update;
        ic->ic_raw_xmit = mtw_raw_xmit;
        ic->ic_update_promisc = mtw_update_promisc;
        ic->ic_vap_create = mtw_vap_create;
        ic->ic_vap_delete = mtw_vap_delete;
        ic->ic_transmit = mtw_transmit;
        ic->ic_parent = mtw_parent;

        ic->ic_update_chw = mtw_update_chw;
        ic->ic_ampdu_enable = mtw_ampdu_enable;

        ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr,
            sizeof(sc->sc_txtap), MTW_TX_RADIOTAP_PRESENT,
            &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
            MTW_RX_RADIOTAP_PRESENT);
        TASK_INIT(&sc->cmdq_task, 0, mtw_cmdq_cb, sc);
        TASK_INIT(&sc->ratectl_task, 0, mtw_ratectl_cb, sc);
        usb_callout_init_mtx(&sc->ratectl_ch, &sc->sc_mtx, 0);

        if (bootverbose)
                ieee80211_announce(ic);

        return (0);

detach:
        MTW_UNLOCK(sc);
        mtw_detach(self);
        return (ENXIO);
}

static void
mtw_drain_mbufq(struct mtw_softc *sc)
{
        struct mbuf *m;
        struct ieee80211_node *ni;

        MTW_LOCK_ASSERT(sc, MA_OWNED);
        while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
                ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
                m->m_pkthdr.rcvif = NULL;
                ieee80211_free_node(ni);
                m_freem(m);
        }
}

static int
mtw_detach(device_t self)
{
        struct mtw_softc *sc = device_get_softc(self);
        struct ieee80211com *ic = &sc->sc_ic;
        int i;
        MTW_LOCK(sc);
        mtw_reset(sc);
        DELAY(10000);
        sc->sc_detached = 1;
        MTW_UNLOCK(sc);


        /* stop all USB transfers */
        for (i = 0; i < MTW_N_XFER; i++)
                usbd_transfer_drain(sc->sc_xfer[i]);

        MTW_LOCK(sc);
        sc->ratectl_run = MTW_RATECTL_OFF;
        sc->cmdq_run = sc->cmdq_key_set = MTW_CMDQ_ABORT;

        /* free TX list, if any */
        if (ic->ic_nrunning > 0)
                for (i = 0; i < MTW_EP_QUEUES; i++)
                        mtw_unsetup_tx_list(sc, &sc->sc_epq[i]);

        /* Free TX queue */
        mtw_drain_mbufq(sc);
        MTW_UNLOCK(sc);
        if (sc->sc_ic.ic_softc == sc) {
                /* drain tasks */
                usb_callout_drain(&sc->ratectl_ch);
                ieee80211_draintask(ic, &sc->cmdq_task);
                ieee80211_draintask(ic, &sc->ratectl_task);
                ieee80211_ifdetach(ic);
        }
        for (i = 0; i < 4; i++) {
                free(sc->txd_fw[i], M_USBDEV);
        }
#ifndef __HAIKU__
        firmware_unregister("/mediatek/mt7601u");
#endif
        mtx_destroy(&sc->sc_mtx);

        return (0);
}

static struct ieee80211vap *
mtw_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
    enum ieee80211_opmode opmode, int flags,
    const uint8_t bssid[IEEE80211_ADDR_LEN],
    const uint8_t mac[IEEE80211_ADDR_LEN])
{
        struct mtw_softc *sc = ic->ic_softc;
        struct mtw_vap *rvp;
        struct ieee80211vap *vap;
        int i;

        if (sc->rvp_cnt >= MTW_VAP_MAX) {
                device_printf(sc->sc_dev, "number of VAPs maxed out\n");
                return (NULL);
        }

        switch (opmode) {
        case IEEE80211_M_STA:
                /* enable s/w bmiss handling for sta mode */
                flags |= IEEE80211_CLONE_NOBEACONS;
                /* fall though */
        case IEEE80211_M_IBSS:
        case IEEE80211_M_MONITOR:
        case IEEE80211_M_HOSTAP:
        case IEEE80211_M_MBSS:
                /* other than WDS vaps, only one at a time */
                if (!TAILQ_EMPTY(&ic->ic_vaps))
                        return (NULL);
                break;
        case IEEE80211_M_WDS:
                TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
                        if (vap->iv_opmode != IEEE80211_M_HOSTAP)
                                continue;
                        /* WDS vap's always share the local mac address. */
                        flags &= ~IEEE80211_CLONE_BSSID;
                        break;
                }
                if (vap == NULL) {
                        device_printf(sc->sc_dev,
                            "wds only supported in ap mode\n");
                        return (NULL);
                }
                break;
        default:
                device_printf(sc->sc_dev, "unknown opmode %d\n", opmode);
                return (NULL);
        }

        rvp = malloc(sizeof(struct mtw_vap), M_80211_VAP, M_WAITOK | M_ZERO);
        vap = &rvp->vap;

        if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid) !=
            0) {
                /* out of memory */
                free(rvp, M_80211_VAP);
                return (NULL);
        }

        vap->iv_update_beacon = mtw_update_beacon;
        vap->iv_max_aid = MTW_WCID_MAX;

        /*
         * The linux rt2800 driver limits 1 stream devices to a 32KB
         * RX AMPDU.
         */
        if (ic->ic_rxstream > 1)
                vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K;
        else
                vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K;
        vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_2; /* 2uS */

        /*
         * To delete the right key from h/w, we need wcid.
         * Luckily, there is unused space in ieee80211_key{}, wk_pad,
         * and matching wcid will be written into there. So, cast
         * some spells to remove 'const' from ieee80211_key{}
         */
        vap->iv_key_delete = (void *)mtw_key_delete;
        vap->iv_key_set = (void *)mtw_key_set;

        // override state transition machine
        rvp->newstate = vap->iv_newstate;
        vap->iv_newstate = mtw_newstate;
        if (opmode == IEEE80211_M_IBSS) {
                rvp->recv_mgmt = vap->iv_recv_mgmt;
                vap->iv_recv_mgmt = mtw_recv_mgmt;
        }

        ieee80211_ratectl_init(vap);
        ieee80211_ratectl_setinterval(vap, 1000); // 1 second

        /* complete setup */
        ieee80211_vap_attach(vap, mtw_media_change, ieee80211_media_status,
            mac);

        /* make sure id is always unique */
        for (i = 0; i < MTW_VAP_MAX; i++) {
                if ((sc->rvp_bmap & 1 << i) == 0) {
                        sc->rvp_bmap |= 1 << i;
                        rvp->rvp_id = i;
                        break;
                }
        }
        if (sc->rvp_cnt++ == 0)
                ic->ic_opmode = opmode;

        if (opmode == IEEE80211_M_HOSTAP)
                sc->cmdq_run = MTW_CMDQ_GO;

        MTW_DPRINTF(sc, MTW_DEBUG_STATE, "rvp_id=%d bmap=%x rvp_cnt=%d\n",
            rvp->rvp_id, sc->rvp_bmap, sc->rvp_cnt);

        return (vap);
}

static void
mtw_vap_delete(struct ieee80211vap *vap)
{
        struct mtw_vap *rvp = MTW_VAP(vap);
        struct ieee80211com *ic;
        struct mtw_softc *sc;
        uint8_t rvp_id;

        if (vap == NULL)
                return;

        ic = vap->iv_ic;
        sc = ic->ic_softc;

        MTW_LOCK(sc);
        m_freem(rvp->beacon_mbuf);
        rvp->beacon_mbuf = NULL;

        rvp_id = rvp->rvp_id;
        sc->ratectl_run &= ~(1 << rvp_id);
        sc->rvp_bmap &= ~(1 << rvp_id);
        mtw_set_region_4(sc, MTW_SKEY(rvp_id, 0), 0, 256);
        mtw_set_region_4(sc, (0x7800 + (rvp_id) * 512), 0, 512);
        --sc->rvp_cnt;

        MTW_DPRINTF(sc, MTW_DEBUG_STATE,
            "vap=%p rvp_id=%d bmap=%x rvp_cnt=%d\n", vap, rvp_id, sc->rvp_bmap,
            sc->rvp_cnt);

        MTW_UNLOCK(sc);

        ieee80211_ratectl_deinit(vap);
        ieee80211_vap_detach(vap);
        free(rvp, M_80211_VAP);
}

/*
 * There are numbers of functions need to be called in context thread.
 * Rather than creating taskqueue event for each of those functions,
 * here is all-for-one taskqueue callback function. This function
 * guarantees deferred functions are executed in the same order they
 * were enqueued.
 * '& MTW_CMDQ_MASQ' is to loop cmdq[].
 */
static void
mtw_cmdq_cb(void *arg, int pending)
{
        struct mtw_softc *sc = arg;
        uint8_t i;
        /* call cmdq[].func locked */
        MTW_LOCK(sc);
        for (i = sc->cmdq_exec; sc->cmdq[i].func && pending;
             i = sc->cmdq_exec, pending--) {
                MTW_DPRINTF(sc, MTW_DEBUG_CMD, "cmdq_exec=%d pending=%d\n", i,
                    pending);
                if (sc->cmdq_run == MTW_CMDQ_GO) {
                        /*
                         * If arg0 is NULL, callback func needs more
                         * than one arg. So, pass ptr to cmdq struct.
                         */
                        if (sc->cmdq[i].arg0)
                                sc->cmdq[i].func(sc->cmdq[i].arg0);
                        else
                                sc->cmdq[i].func(&sc->cmdq[i]);
                }
                sc->cmdq[i].arg0 = NULL;
                sc->cmdq[i].func = NULL;
                sc->cmdq_exec++;
                sc->cmdq_exec &= MTW_CMDQ_MASQ;
        }
        MTW_UNLOCK(sc);
}

static void
mtw_setup_tx_list(struct mtw_softc *sc, struct mtw_endpoint_queue *pq)
{
        struct mtw_tx_data *data;

        memset(pq, 0, sizeof(*pq));

        STAILQ_INIT(&pq->tx_qh);
        STAILQ_INIT(&pq->tx_fh);

        for (data = &pq->tx_data[0]; data < &pq->tx_data[MTW_TX_RING_COUNT];
             data++) {
                data->sc = sc;
                STAILQ_INSERT_TAIL(&pq->tx_fh, data, next);
        }
        pq->tx_nfree = MTW_TX_RING_COUNT;
}

static void
mtw_unsetup_tx_list(struct mtw_softc *sc, struct mtw_endpoint_queue *pq)
{
        struct mtw_tx_data *data;
        /* make sure any subsequent use of the queues will fail */
        pq->tx_nfree = 0;

        STAILQ_INIT(&pq->tx_fh);
        STAILQ_INIT(&pq->tx_qh);

        /* free up all node references and mbufs */
        for (data = &pq->tx_data[0]; data < &pq->tx_data[MTW_TX_RING_COUNT];
             data++) {
                if (data->m != NULL) {
                        m_freem(data->m);
                        data->m = NULL;
                }
                if (data->ni != NULL) {
                        ieee80211_free_node(data->ni);
                        data->ni = NULL;
                }
        }
}

static int
mtw_write_ivb(struct mtw_softc *sc, void *buf, uint16_t len)
{
        usb_device_request_t req;
        uint16_t actlen;
        req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
        req.bRequest = MTW_RESET;
        USETW(req.wValue, 0x12);
        USETW(req.wIndex, 0);
        USETW(req.wLength, len);

        int error = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, buf,
            0, &actlen, 1000);

        return (error);
}

static int
mtw_write_cfg(struct mtw_softc *sc, uint16_t reg, uint32_t val)
{
        usb_device_request_t req;
        int error;

        req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
        req.bRequest = MTW_WRITE_CFG;
        USETW(req.wValue, 0);
        USETW(req.wIndex, reg);
        USETW(req.wLength, 4);
        val = htole32(val);
        error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, &val);
        return (error);
}

static int
mtw_usb_dma_write(struct mtw_softc *sc, uint32_t val)
{
        // if (sc->asic_ver == 0x7612)
        //              return mtw_write_cfg(sc, MTW_USB_U3DMA_CFG, val);
        //      else
        return (mtw_write(sc, MTW_USB_DMA_CFG, val));
}

static void
mtw_ucode_setup(struct mtw_softc *sc)
{

        mtw_usb_dma_write(sc, (MTW_USB_TX_EN | MTW_USB_RX_EN));
        mtw_write(sc, MTW_FCE_PSE_CTRL, 1);
        mtw_write(sc, MTW_TX_CPU_FCE_BASE, 0x400230);
        mtw_write(sc, MTW_TX_CPU_FCE_MAX_COUNT, 1);
        mtw_write(sc, MTW_MCU_FW_IDX, 1);
        mtw_write(sc, MTW_FCE_PDMA, 0x44);
        mtw_write(sc, MTW_FCE_SKIP_FS, 3);
}
static int
mtw_ucode_write(struct mtw_softc *sc, const uint8_t *fw, const uint8_t *ivb,
    int32_t len, uint32_t offset)
{

        // struct usb_attach_arg *uaa = device_get_ivars(sc->sc_dev);
#if 0 // firmware not tested

        if (sc->asic_ver == 0x7612 && offset >= 0x90000)
                blksz = 0x800; /* MT7612 ROM Patch */

        xfer = usbd_alloc_xfer(sc->sc_udev);
        if (xfer == NULL) {
                error = ENOMEM;
                goto fail;
        }
        buf = usbd_alloc_buffer(xfer, blksz + 12);
        if (buf == NULL) {
                error = ENOMEM;
                goto fail;
        }
#endif



        int mlen;
        int idx = 0;

        mlen = 0x2c44;

        while (len > 0) {

                if (len < 0x2c44 && len > 0) {
                        mlen = len;
                }

                sc->txd_fw[idx]->len = htole16(mlen);
                sc->txd_fw[idx]->flags = htole16(MTW_TXD_DATA | MTW_TXD_MCU);

                memcpy(&sc->txd_fw[idx]->fw, fw, mlen);
                // memcpy(&txd[1], fw, mlen);
                //      memset(&txd[1]  + mlen, 0, MTW_DMA_PAD);
                //              mtw_write_cfg(sc, MTW_MCU_DMA_ADDR, offset
                //+sent); 1mtw_write_cfg(sc, MTW_MCU_DMA_LEN, (mlen << 16));

                //        sc->sc_fw_data[idx]->len=htole16(mlen);

                // memcpy(tmpbuf,fw,mlen);
                // memset(tmpbuf+mlen,0,MTW_DMA_PAD);
                // memcpy(sc->sc_fw_data[idx].buf, fw, mlen);

                fw += mlen;
                len -= mlen;
                // sent+=mlen;
                idx++;
        }
        sc->sc_sent = 0;
        memcpy(sc->sc_ivb_1, ivb, MTW_MCU_IVB_LEN);

        usbd_transfer_start(sc->sc_xfer[7]);

        return (0);
}

static void
mtw_load_microcode(void *arg)
{

        struct mtw_softc *sc = (struct mtw_softc *)arg;
        const struct mtw_ucode_hdr *hdr;
        // onst struct mtw_ucode *fw = NULL;
        const char *fwname;
        size_t size;
        int error = 0;
        uint32_t tmp, iofs = 0x40;
        //      int ntries;
        int dlen, ilen;
        device_printf(sc->sc_dev, "version:0x%hx\n", sc->asic_ver);
        /* is firmware already running? */
        mtw_read_cfg(sc, MTW_MCU_DMA_ADDR, &tmp);
        if (tmp == MTW_MCU_READY) {
                return;
        }
        if (sc->asic_ver == 0x7612) {
                fwname = "mtw-mt7662u_rom_patch";

                const struct firmware *firmware = firmware_get_flags(fwname,FIRMWARE_GET_NOWARN);
                if (firmware == NULL) {
                        device_printf(sc->sc_dev,
                            "failed loadfirmware of file %s (error %d)\n",
                            fwname, error);
                        return;
                }
                size = firmware->datasize;

                const struct mtw_ucode *fw = (const struct mtw_ucode *)
                                                 firmware->data;
                hdr = (const struct mtw_ucode_hdr *)&fw->hdr;
                // memcpy(fw,(const unsigned char*)firmware->data +
                // 0x1e,size-0x1e);
                ilen = size - 0x1e;

                mtw_ucode_setup(sc);

                if ((error = mtw_ucode_write(sc, firmware->data, fw->ivb, ilen,
                         0x90000)) != 0) {
                        goto fail;
                }
                mtw_usb_dma_write(sc, 0x00e41814);
        }

        fwname = "/mediatek/mt7601u.bin";
        iofs = 0x40;
        // dofs = 0;
        if (sc->asic_ver == 0x7612) {
                fwname = "mtw-mt7662u";
                iofs = 0x80040;
                //      dofs = 0x110800;
        } else if (sc->asic_ver == 0x7610) {
                fwname = "mt7610u";
                // dofs = 0x80000;
        }
        MTW_UNLOCK(sc);
        const struct firmware *firmware = firmware_get_flags(fwname, FIRMWARE_GET_NOWARN);

        if (firmware == NULL) {
                device_printf(sc->sc_dev,
                    "failed loadfirmware of file %s (error %d)\n", fwname,
                    error);
                MTW_LOCK(sc);
                return;
        }
        MTW_LOCK(sc);
        size = firmware->datasize;
        MTW_DPRINTF(sc, MTW_DEBUG_FIRMWARE, "firmware size:%zu\n", size);
        const struct mtw_ucode *fw = (const struct mtw_ucode *)firmware->data;

        if (size < sizeof(struct mtw_ucode_hdr)) {
                device_printf(sc->sc_dev, "firmware header too short\n");
                goto fail;
        }

        hdr = (const struct mtw_ucode_hdr *)&fw->hdr;

        if (size < sizeof(struct mtw_ucode_hdr) + le32toh(hdr->ilm_len) +
                le32toh(hdr->dlm_len)) {
                device_printf(sc->sc_dev, "firmware payload too short\n");
                goto fail;
        }

        ilen = le32toh(hdr->ilm_len) - MTW_MCU_IVB_LEN;
        dlen = le32toh(hdr->dlm_len);

        if (ilen > size || dlen > size) {
                device_printf(sc->sc_dev, "firmware payload too large\n");
                goto fail;
        }

        mtw_write(sc, MTW_FCE_PDMA, 0);
        mtw_write(sc, MTW_FCE_PSE_CTRL, 0);
        mtw_ucode_setup(sc);

        if ((error = mtw_ucode_write(sc, fw->data, fw->ivb, ilen, iofs)) != 0)
                device_printf(sc->sc_dev, "Could not write ucode errro=%d\n",
                    error);

        device_printf(sc->sc_dev, "loaded firmware ver %.8x %.8x %s\n",
            le32toh(hdr->fw_ver), le32toh(hdr->build_ver), hdr->build_time);

#ifndef __HAIKU__
        return;
fail:
        return;
#else
fail:
        firmware_put(firmware, 0);
        return;
#endif
}
static usb_error_t
mtw_do_request(struct mtw_softc *sc, struct usb_device_request *req, void *data)
{
        usb_error_t err;
        int ntries = 5;

        MTW_LOCK_ASSERT(sc, MA_OWNED);

        while (ntries--) {
                err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, req, data,
                    0, NULL, 2000); // ms seconds
                if (err == 0)
                        break;
                MTW_DPRINTF(sc, MTW_DEBUG_USB,
                    "Control request failed, %s (retrying)\n",
                    usbd_errstr(err));
                mtw_delay(sc, 10);
        }
        return (err);
}

static int
mtw_read(struct mtw_softc *sc, uint16_t reg, uint32_t *val)
{
        uint32_t tmp;
        int error;

        error = mtw_read_region_1(sc, reg, (uint8_t *)&tmp, sizeof tmp);
        if (error == 0)
                *val = le32toh(tmp);
        else
                *val = 0xffffffff;
        return (error);
}

static int
mtw_read_region_1(struct mtw_softc *sc, uint16_t reg, uint8_t *buf, int len)
{
        usb_device_request_t req;

        req.bmRequestType = UT_READ_VENDOR_DEVICE;
        req.bRequest = MTW_READ_REGION_1;
        USETW(req.wValue, 0);
        USETW(req.wIndex, reg);
        USETW(req.wLength, len);

        return (mtw_do_request(sc, &req, buf));
}

static int
mtw_write_2(struct mtw_softc *sc, uint16_t reg, uint16_t val)
{

        usb_device_request_t req;
        req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
        req.bRequest = MTW_WRITE_2;
        USETW(req.wValue, val);
        USETW(req.wIndex, reg);
        USETW(req.wLength, 0);
        return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL));
}

static int
mtw_write(struct mtw_softc *sc, uint16_t reg, uint32_t val)
{

        int error;

        if ((error = mtw_write_2(sc, reg, val & 0xffff)) == 0) {

                error = mtw_write_2(sc, reg + 2, val >> 16);
        }

        return (error);
}

static int
mtw_write_region_1(struct mtw_softc *sc, uint16_t reg, uint8_t *buf, int len)
{

        usb_device_request_t req;
        req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
        req.bRequest = MTW_WRITE_REGION_1;
        USETW(req.wValue, 0);
        USETW(req.wIndex, reg);
        USETW(req.wLength, len);
        return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, buf));
}

static int
mtw_set_region_4(struct mtw_softc *sc, uint16_t reg, uint32_t val, int count)
{
        int i, error = 0;

        KASSERT((count & 3) == 0, ("mte_set_region_4: Invalid data length.\n"));
        for (i = 0; i < count && error == 0; i += 4)
                error = mtw_write(sc, reg + i, val);
        return (error);
}

static int
mtw_efuse_read_2(struct mtw_softc *sc, uint16_t addr, uint16_t *val)
{

        uint32_t tmp;
        uint16_t reg;
        int error, ntries;

        if ((error = mtw_read(sc, MTW_EFUSE_CTRL, &tmp)) != 0)
                return (error);

        addr *= 2;
        /*
         * Read one 16-byte block into registers EFUSE_DATA[0-3]:
         * DATA0: 3 2 1 0
         * DATA1: 7 6 5 4
         * DATA2: B A 9 8
         * DATA3: F E D C
         */
        tmp &= ~(MTW_EFSROM_MODE_MASK | MTW_EFSROM_AIN_MASK);
        tmp |= (addr & ~0xf) << MTW_EFSROM_AIN_SHIFT | MTW_EFSROM_KICK;
        mtw_write(sc, MTW_EFUSE_CTRL, tmp);
        for (ntries = 0; ntries < 100; ntries++) {
                if ((error = mtw_read(sc, MTW_EFUSE_CTRL, &tmp)) != 0)
                        return (error);
                if (!(tmp & MTW_EFSROM_KICK))
                        break;
                DELAY(2);
        }
        if (ntries == 100)
                return (ETIMEDOUT);

        if ((tmp & MTW_EFUSE_AOUT_MASK) == MTW_EFUSE_AOUT_MASK) {
                *val = 0xffff; // address not found
                return (0);
        }
// determine to which 32-bit register our 16-bit word belongs
        reg = MTW_EFUSE_DATA0 + (addr & 0xc);
        if ((error = mtw_read(sc, reg, &tmp)) != 0)
                return (error);

        *val = (addr & 2) ? tmp >> 16 : tmp & 0xffff;
        return (0);
}

static __inline int
mtw_srom_read(struct mtw_softc *sc, uint16_t addr, uint16_t *val)
{
        /* either eFUSE ROM or EEPROM */
        return (sc->sc_srom_read(sc, addr, val));
}

static int
mtw_bbp_read(struct mtw_softc *sc, uint8_t reg, uint8_t *val)
{
        uint32_t tmp;
        int ntries, error;

        for (ntries = 0; ntries < 10; ntries++) {
                if ((error = mtw_read(sc, MTW_BBP_CSR, &tmp)) != 0)
                        return (error);
                if (!(tmp & MTW_BBP_CSR_KICK))
                        break;
        }
        if (ntries == 10)
                return (ETIMEDOUT);

        tmp = MTW_BBP_CSR_READ | MTW_BBP_CSR_KICK | reg << 8;
        if ((error = mtw_write(sc, MTW_BBP_CSR, tmp)) != 0)
                return (error);

        for (ntries = 0; ntries < 10; ntries++) {
                if ((error = mtw_read(sc, MTW_BBP_CSR, &tmp)) != 0)
                        return (error);
                if (!(tmp & MTW_BBP_CSR_KICK))
                        break;
        }
        if (ntries == 10)
                return (ETIMEDOUT);

        *val = tmp & 0xff;
        return (0);
}

static int
mtw_bbp_write(struct mtw_softc *sc, uint8_t reg, uint8_t val)
{
        uint32_t tmp;
        int ntries, error;

        for (ntries = 0; ntries < 10; ntries++) {
                if ((error = mtw_read(sc, MTW_BBP_CSR, &tmp)) != 0)
                        return (error);
                if (!(tmp & MTW_BBP_CSR_KICK))
                        break;
        }
        if (ntries == 10)
                return (ETIMEDOUT);

        tmp = MTW_BBP_CSR_KICK | reg << 8 | val;
        return (mtw_write(sc, MTW_BBP_CSR, tmp));
}

static int
mtw_mcu_cmd(struct mtw_softc *sc, u_int8_t cmd, void *buf, int len)
{
        sc->sc_idx = 0;
        sc->txd_fw[sc->sc_idx]->len = htole16(
            len + 8);
        sc->txd_fw[sc->sc_idx]->flags = htole16(MTW_TXD_CMD | MTW_TXD_MCU |
            (cmd & 0x1f) << MTW_TXD_CMD_SHIFT | (0 & 0xf));

        memset(&sc->txd_fw[sc->sc_idx]->fw, 0, 2004);
        memcpy(&sc->txd_fw[sc->sc_idx]->fw, buf, len);
        usbd_transfer_start(sc->sc_xfer[7]);
        return (0);
}

/*
 * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word.
 * Used to adjust per-rate Tx power registers.
 */
static __inline uint32_t
b4inc(uint32_t b32, int8_t delta)
{
        int8_t i, b4;

        for (i = 0; i < 8; i++) {
                b4 = b32 & 0xf;
                b4 += delta;
                if (b4 < 0)
                        b4 = 0;
                else if (b4 > 0xf)
                        b4 = 0xf;
                b32 = b32 >> 4 | b4 << 28;
        }
        return (b32);
}
static void
mtw_get_txpower(struct mtw_softc *sc)
{
        uint16_t val;
        int i;

        /* Read power settings for 2GHz channels. */
        for (i = 0; i < 14; i += 2) {
                mtw_srom_read(sc, MTW_EEPROM_PWR2GHZ_BASE1 + i / 2, &val);
                sc->txpow1[i + 0] = (int8_t)(val & 0xff);
                sc->txpow1[i + 1] = (int8_t)(val >> 8);
                mtw_srom_read(sc, MTW_EEPROM_PWR2GHZ_BASE2 + i / 2, &val);
                sc->txpow2[i + 0] = (int8_t)(val & 0xff);
                sc->txpow2[i + 1] = (int8_t)(val >> 8);
        }
        /* Fix broken Tx power entries. */
        for (i = 0; i < 14; i++) {
                if (sc->txpow1[i] < 0 || sc->txpow1[i] > 27)
                        sc->txpow1[i] = 5;
                if (sc->txpow2[i] < 0 || sc->txpow2[i] > 27)
                        sc->txpow2[i] = 5;
                MTW_DPRINTF(sc, MTW_DEBUG_TXPWR,
                "chan %d: power1=%d, power2=%d\n", mt7601_rf_chan[i].chan,
                sc->txpow1[i], sc->txpow2[i]);
        }
}

struct ieee80211_node *
mtw_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
{
        return (malloc(sizeof(struct mtw_node), M_80211_NODE,
                M_NOWAIT | M_ZERO));
}
static int
mtw_read_eeprom(struct mtw_softc *sc)
{
        struct ieee80211com *ic = &sc->sc_ic;
        int8_t delta_2ghz, delta_5ghz;
        uint16_t val;
        int ridx, ant;

        sc->sc_srom_read = mtw_efuse_read_2;

        /* read RF information */
        mtw_srom_read(sc, MTW_EEPROM_CHIPID, &val);
        sc->rf_rev = val;
        mtw_srom_read(sc, MTW_EEPROM_ANTENNA, &val);
        sc->ntxchains = (val >> 4) & 0xf;
        sc->nrxchains = val & 0xf;
        MTW_DPRINTF(sc, MTW_DEBUG_ROM, "EEPROM RF rev=0x%02x chains=%dT%dR\n",
            sc->rf_rev, sc->ntxchains, sc->nrxchains);

        /* read ROM version */
        mtw_srom_read(sc, MTW_EEPROM_VERSION, &val);
        MTW_DPRINTF(sc, MTW_DEBUG_ROM, "EEPROM rev=%d, FAE=%d\n", val & 0xff,
            val >> 8);

        /* read MAC address */
        mtw_srom_read(sc, MTW_EEPROM_MAC01, &val);
        ic->ic_macaddr[0] = val & 0xff;
        ic->ic_macaddr[1] = val >> 8;
        mtw_srom_read(sc, MTW_EEPROM_MAC23, &val);
        ic->ic_macaddr[2] = val & 0xff;
        ic->ic_macaddr[3] = val >> 8;
        mtw_srom_read(sc, MTW_EEPROM_MAC45, &val);
        ic->ic_macaddr[4] = val & 0xff;
        ic->ic_macaddr[5] = val >> 8;
#if 0
        printf("eFUSE ROM\n00: ");
        for (int i = 0; i < 256; i++) {
                if (((i % 8) == 0) && i > 0)
                        printf("\n%02x: ", i);
                mtw_srom_read(sc, i, &val);
                printf(" %04x", val);
        }
        printf("\n");
#endif
        /* check if RF supports automatic Tx access gain control */
        mtw_srom_read(sc, MTW_EEPROM_CONFIG, &val);
        device_printf(sc->sc_dev, "EEPROM CFG 0x%04x\n", val);
        if ((val & 0xff) != 0xff) {
                sc->ext_5ghz_lna = (val >> 3) & 1;
                sc->ext_2ghz_lna = (val >> 2) & 1;
                /* check if RF supports automatic Tx access gain control */
                sc->calib_2ghz = sc->calib_5ghz = (val >> 1) & 1;
                /* check if we have a hardware radio switch */
                sc->rfswitch = val & 1;
        }

        /* read RF frequency offset from EEPROM */
        mtw_srom_read(sc, MTW_EEPROM_FREQ_OFFSET, &val);
        if ((val & 0xff) != 0xff)
                sc->rf_freq_offset = val;
        else
                sc->rf_freq_offset = 0;
        MTW_DPRINTF(sc, MTW_DEBUG_ROM, "frequency offset 0x%x\n",
            sc->rf_freq_offset);

        /* Read Tx power settings. */
        mtw_get_txpower(sc);

        /* read Tx power compensation for each Tx rate */
        mtw_srom_read(sc, MTW_EEPROM_DELTAPWR, &val);
        delta_2ghz = delta_5ghz = 0;
        if ((val & 0xff) != 0xff && (val & 0x80)) {
                delta_2ghz = val & 0xf;
                if (!(val & 0x40)) /* negative number */
                        delta_2ghz = -delta_2ghz;
        }
        val >>= 8;
        if ((val & 0xff) != 0xff && (val & 0x80)) {
                delta_5ghz = val & 0xf;
                if (!(val & 0x40)) /* negative number */
                        delta_5ghz = -delta_5ghz;
        }
        MTW_DPRINTF(sc, MTW_DEBUG_ROM | MTW_DEBUG_TXPWR,
            "power compensation=%d (2GHz), %d (5GHz)\n", delta_2ghz,
            delta_5ghz);

        for (ridx = 0; ridx < 5; ridx++) {
                uint32_t reg;

                mtw_srom_read(sc, MTW_EEPROM_RPWR + ridx * 2, &val);
                reg = val;
                mtw_srom_read(sc, MTW_EEPROM_RPWR + ridx * 2 + 1, &val);
                reg |= (uint32_t)val << 16;

                sc->txpow20mhz[ridx] = reg;
                sc->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz);
                sc->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz);

                MTW_DPRINTF(sc, MTW_DEBUG_ROM | MTW_DEBUG_TXPWR,
                    "ridx %d: power 20MHz=0x%08x, 40MHz/2GHz=0x%08x, "
                    "40MHz/5GHz=0x%08x\n",
                    ridx, sc->txpow20mhz[ridx], sc->txpow40mhz_2ghz[ridx],
                    sc->txpow40mhz_5ghz[ridx]);
        }

        /* read RSSI offsets and LNA gains from EEPROM */
        val = 0;
        mtw_srom_read(sc, MTW_EEPROM_RSSI1_2GHZ, &val);
        sc->rssi_2ghz[0] = val & 0xff; /* Ant A */
        sc->rssi_2ghz[1] = val >> 8;   /* Ant B */
        mtw_srom_read(sc, MTW_EEPROM_RSSI2_2GHZ, &val);
        /*
         * On RT3070 chips (limited to 2 Rx chains), this ROM
         * field contains the Tx mixer gain for the 2GHz band.
         */
        if ((val & 0xff) != 0xff)
                sc->txmixgain_2ghz = val & 0x7;
        MTW_DPRINTF(sc, MTW_DEBUG_ROM, "tx mixer gain=%u (2GHz)\n",
            sc->txmixgain_2ghz);
        sc->lna[2] = val >> 8; /* channel group 2 */
        mtw_srom_read(sc, MTW_EEPROM_RSSI1_5GHZ, &val);
        sc->rssi_5ghz[0] = val & 0xff; /* Ant A */
        sc->rssi_5ghz[1] = val >> 8;   /* Ant B */
        mtw_srom_read(sc, MTW_EEPROM_RSSI2_5GHZ, &val);
        sc->rssi_5ghz[2] = val & 0xff; /* Ant C */

        sc->lna[3] = val >> 8; /* channel group 3 */

        mtw_srom_read(sc, MTW_EEPROM_LNA, &val);
        sc->lna[0] = val & 0xff; /* channel group 0 */
        sc->lna[1] = val >> 8;   /* channel group 1 */
        MTW_DPRINTF(sc, MTW_DEBUG_ROM, "LNA0 0x%x\n", sc->lna[0]);

        /* fix broken 5GHz LNA entries */
        if (sc->lna[2] == 0 || sc->lna[2] == 0xff) {
                MTW_DPRINTF(sc, MTW_DEBUG_ROM,
                    "invalid LNA for channel group %d\n", 2);
                sc->lna[2] = sc->lna[1];
        }
        if (sc->lna[3] == 0 || sc->lna[3] == 0xff) {
                MTW_DPRINTF(sc, MTW_DEBUG_ROM,
                    "invalid LNA for channel group %d\n", 3);
                sc->lna[3] = sc->lna[1];
        }

        /* fix broken RSSI offset entries */
        for (ant = 0; ant < 3; ant++) {
                if (sc->rssi_2ghz[ant] < -10 || sc->rssi_2ghz[ant] > 10) {
                        MTW_DPRINTF(sc, MTW_DEBUG_ROM,
                            "invalid RSSI%d offset: %d (2GHz)\n", ant + 1,
                            sc->rssi_2ghz[ant]);
                        sc->rssi_2ghz[ant] = 0;
                }
                if (sc->rssi_5ghz[ant] < -10 || sc->rssi_5ghz[ant] > 10) {
                        MTW_DPRINTF(sc, MTW_DEBUG_ROM,
                            "invalid RSSI%d offset: %d (5GHz)\n", ant + 1,
                            sc->rssi_5ghz[ant]);
                        sc->rssi_5ghz[ant] = 0;
                }
        }
        return (0);
}
static int
mtw_media_change(if_t ifp)
{
        struct ieee80211vap *vap = if_getsoftc(ifp);
        struct ieee80211com *ic = vap->iv_ic;
        const struct ieee80211_txparam *tp;
        struct mtw_softc *sc = ic->ic_softc;
        uint8_t rate, ridx;

        MTW_LOCK(sc);
        ieee80211_media_change(ifp);
        //tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
        tp = &vap->iv_txparms[ic->ic_curmode];
        if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
                struct ieee80211_node *ni;
                struct mtw_node *rn;
                /* XXX TODO: methodize with MCS rates */
                rate =
                    ic->ic_sup_rates[ic->ic_curmode].rs_rates[tp->ucastrate] &
                    IEEE80211_RATE_VAL;
                for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++) {
                        if (rt2860_rates[ridx].rate == rate)
                                break;
                }
                ni = ieee80211_ref_node(vap->iv_bss);
                rn = MTW_NODE(ni);
                rn->fix_ridx = ridx;

                MTW_DPRINTF(sc, MTW_DEBUG_RATE, "rate=%d, fix_ridx=%d\n", rate,
                    rn->fix_ridx);
                ieee80211_free_node(ni);
        }
        MTW_UNLOCK(sc);

        return (0);
}

void
mtw_set_leds(struct mtw_softc *sc, uint16_t which)
{
        struct mtw_mcu_cmd_8 cmd;
        cmd.func = htole32(0x1);
        cmd.val = htole32(which);
        mtw_mcu_cmd(sc, CMD_LED_MODE, &cmd, sizeof(struct mtw_mcu_cmd_8));
}
static void
mtw_abort_tsf_sync(struct mtw_softc *sc)
{
        uint32_t tmp;

        mtw_read(sc, MTW_BCN_TIME_CFG, &tmp);
        tmp &= ~(MTW_BCN_TX_EN | MTW_TSF_TIMER_EN | MTW_TBTT_TIMER_EN);
        mtw_write(sc, MTW_BCN_TIME_CFG, tmp);
}
static int
mtw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
        const struct ieee80211_txparam *tp;
        struct ieee80211com *ic = vap->iv_ic;
        struct mtw_softc *sc = ic->ic_softc;
        struct mtw_vap *rvp = MTW_VAP(vap);
        enum ieee80211_state ostate;
        uint32_t sta[3];
        uint8_t ratectl = 0;
        uint8_t restart_ratectl = 0;
        uint8_t bid = 1 << rvp->rvp_id;


        ostate = vap->iv_state;
        MTW_DPRINTF(sc, MTW_DEBUG_STATE, "%s -> %s\n",
            ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
        IEEE80211_UNLOCK(ic);
        MTW_LOCK(sc);
        ratectl = sc->ratectl_run; /* remember current state */
        usb_callout_stop(&sc->ratectl_ch);
        sc->ratectl_run = MTW_RATECTL_OFF;
        if (ostate == IEEE80211_S_RUN) {
                /* turn link LED off */
        }

        switch (nstate) {
        case IEEE80211_S_INIT:
        restart_ratectl = 1;
                if (ostate != IEEE80211_S_RUN)
                        break;

                ratectl &= ~bid;
                sc->runbmap &= ~bid;

                /* abort TSF synchronization if there is no vap running  */
                if (--sc->running == 0)
                        mtw_abort_tsf_sync(sc);
                break;

        case IEEE80211_S_RUN:
                if (!(sc->runbmap & bid)) {
                        if (sc->running++)
                                restart_ratectl = 1;
                        sc->runbmap |= bid;
                }

                m_freem(rvp->beacon_mbuf);
                rvp->beacon_mbuf = NULL;

                switch (vap->iv_opmode) {
                case IEEE80211_M_HOSTAP:
                case IEEE80211_M_MBSS:
                        sc->ap_running |= bid;
                        ic->ic_opmode = vap->iv_opmode;
                        mtw_update_beacon_cb(vap);
                        break;
                case IEEE80211_M_IBSS:
                        sc->adhoc_running |= bid;
                        if (!sc->ap_running)
                                ic->ic_opmode = vap->iv_opmode;
                        mtw_update_beacon_cb(vap);
                        break;
                case IEEE80211_M_STA:
                        sc->sta_running |= bid;
                        if (!sc->ap_running && !sc->adhoc_running)
                                ic->ic_opmode = vap->iv_opmode;

                        /* read statistic counters (clear on read) */
                        mtw_read_region_1(sc, MTW_TX_STA_CNT0, (uint8_t *)sta,
                            sizeof sta);

                        break;
                default:
                        ic->ic_opmode = vap->iv_opmode;
                        break;
                }

                if (vap->iv_opmode != IEEE80211_M_MONITOR) {
                        struct ieee80211_node *ni;

                        if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) {
                                MTW_UNLOCK(sc);
                                IEEE80211_LOCK(ic);
                                return (-1);
                        }
                        mtw_updateslot(ic);
                        mtw_enable_mrr(sc);
                        mtw_set_txpreamble(sc);
                        mtw_set_basicrates(sc);
                        ni = ieee80211_ref_node(vap->iv_bss);
                        IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid);
                        mtw_set_bssid(sc, sc->sc_bssid);
                        ieee80211_free_node(ni);
                        mtw_enable_tsf_sync(sc);

                        /* enable automatic rate adaptation */
                        tp = &vap->iv_txparms[ieee80211_chan2mode(
                            ic->ic_curchan)];
                        if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
                                ratectl |= bid;
                } else {
                mtw_enable_tsf_sync(sc);
                }

                break;
        default:
                MTW_DPRINTF(sc, MTW_DEBUG_STATE, "undefined state\n");
                break;
        }

        /* restart amrr for running VAPs */
        if ((sc->ratectl_run = ratectl) && restart_ratectl) {
                usb_callout_reset(&sc->ratectl_ch, hz, mtw_ratectl_to, sc);
        }
        MTW_UNLOCK(sc);
        IEEE80211_LOCK(ic);
        return (rvp->newstate(vap, nstate, arg));
}

static int
mtw_wme_update(struct ieee80211com *ic)
{
        struct chanAccParams chp;
        struct mtw_softc *sc = ic->ic_softc;
        const struct wmeParams *ac;
        int aci, error = 0;
        ieee80211_wme_ic_getparams(ic, &chp);
        ac = chp.cap_wmeParams;

        MTW_LOCK(sc);
        /* update MAC TX configuration registers */
        for (aci = 0; aci < WME_NUM_AC; aci++) {
                error = mtw_write(sc, MTW_EDCA_AC_CFG(aci),
                    ac[aci].wmep_logcwmax << 16 | ac[aci].wmep_logcwmin << 12 |
                        ac[aci].wmep_aifsn << 8 | ac[aci].wmep_txopLimit);
                if (error)
                        goto err;
        }

        /* update SCH/DMA registers too */
        error = mtw_write(sc, MTW_WMM_AIFSN_CFG,
            ac[WME_AC_VO].wmep_aifsn << 12 | ac[WME_AC_VI].wmep_aifsn << 8 |
                ac[WME_AC_BK].wmep_aifsn << 4 | ac[WME_AC_BE].wmep_aifsn);
        if (error)
                goto err;
        error = mtw_write(sc, MTW_WMM_CWMIN_CFG,
            ac[WME_AC_VO].wmep_logcwmin << 12 |
                ac[WME_AC_VI].wmep_logcwmin << 8 |
                ac[WME_AC_BK].wmep_logcwmin << 4 | ac[WME_AC_BE].wmep_logcwmin);
        if (error)
                goto err;
        error = mtw_write(sc, MTW_WMM_CWMAX_CFG,
            ac[WME_AC_VO].wmep_logcwmax << 12 |
                ac[WME_AC_VI].wmep_logcwmax << 8 |
                ac[WME_AC_BK].wmep_logcwmax << 4 | ac[WME_AC_BE].wmep_logcwmax);
        if (error)
                goto err;
        error = mtw_write(sc, MTW_WMM_TXOP0_CFG,
            ac[WME_AC_BK].wmep_txopLimit << 16 | ac[WME_AC_BE].wmep_txopLimit);
        if (error)
                goto err;
        error = mtw_write(sc, MTW_WMM_TXOP1_CFG,
            ac[WME_AC_VO].wmep_txopLimit << 16 | ac[WME_AC_VI].wmep_txopLimit);

err:
        MTW_UNLOCK(sc);
        if (error)
                MTW_DPRINTF(sc, MTW_DEBUG_USB, "WME update failed\n");

        return (error);
}

static int
mtw_key_set(struct ieee80211vap *vap, struct ieee80211_key *k)
{
        struct ieee80211com *ic = vap->iv_ic;
        struct mtw_softc *sc = ic->ic_softc;
        uint32_t i;

        i = MTW_CMDQ_GET(&sc->cmdq_store);
        MTW_DPRINTF(sc, MTW_DEBUG_KEY, "cmdq_store=%d\n", i);
        sc->cmdq[i].func = mtw_key_set_cb;
        sc->cmdq[i].arg0 = NULL;
        sc->cmdq[i].arg1 = vap;
        sc->cmdq[i].k = k;
        IEEE80211_ADDR_COPY(sc->cmdq[i].mac, k->wk_macaddr);
        ieee80211_runtask(ic, &sc->cmdq_task);

        /*
         * To make sure key will be set when hostapd
         * calls iv_key_set() before if_init().
         */
        if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
                MTW_LOCK(sc);
                sc->cmdq_key_set = MTW_CMDQ_GO;
                MTW_UNLOCK(sc);
        }

        return (1);
}
static void
mtw_key_set_cb(void *arg)
{
        struct mtw_cmdq *cmdq = arg;
        struct ieee80211vap *vap = cmdq->arg1;
        struct ieee80211_key *k = cmdq->k;
        struct ieee80211com *ic = vap->iv_ic;
        struct mtw_softc *sc = ic->ic_softc;
        struct ieee80211_node *ni;
        u_int cipher = k->wk_cipher->ic_cipher;
        uint32_t attr;
        uint16_t base;
        uint8_t mode, wcid, iv[8];
        MTW_LOCK_ASSERT(sc, MA_OWNED);

        if (vap->iv_opmode == IEEE80211_M_HOSTAP)
                ni = ieee80211_find_vap_node(&ic->ic_sta, vap, cmdq->mac);
        else
                ni = vap->iv_bss;

        /* map net80211 cipher to RT2860 security mode */
        switch (cipher) {
        case IEEE80211_CIPHER_WEP:
                if (k->wk_keylen < 8)
                        mode = MTW_MODE_WEP40;
                else
                        mode = MTW_MODE_WEP104;
                break;
        case IEEE80211_CIPHER_TKIP:
                mode = MTW_MODE_TKIP;
                break;
        case IEEE80211_CIPHER_AES_CCM:
                mode = MTW_MODE_AES_CCMP;
                break;
        default:
                MTW_DPRINTF(sc, MTW_DEBUG_KEY, "undefined case\n");
                return;
        }

        if (k->wk_flags & IEEE80211_KEY_GROUP) {
                wcid = 0; /* NB: update WCID0 for group keys */
                base = MTW_SKEY(0, k->wk_keyix);
        } else {
                wcid = (ni != NULL) ? MTW_AID2WCID(ni->ni_associd) : 0;
                base = MTW_PKEY(wcid);
        }

        if (cipher == IEEE80211_CIPHER_TKIP) {
                mtw_write_region_1(sc, base, k->wk_key, 16);
                mtw_write_region_1(sc, base + 16, &k->wk_key[24], 8);
                mtw_write_region_1(sc, base + 24, &k->wk_key[16], 8);
        } else {
                /* roundup len to 16-bit: XXX fix write_region_1() instead */
                mtw_write_region_1(sc, base, k->wk_key,
                    (k->wk_keylen + 1) & ~1);
        }

        if (!(k->wk_flags & IEEE80211_KEY_GROUP) ||
            (k->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))) {
                /* set initial packet number in IV+EIV */
                if (cipher == IEEE80211_CIPHER_WEP) {
                        memset(iv, 0, sizeof iv);
                        iv[3] = vap->iv_def_txkey << 6;
                } else {
                        if (cipher == IEEE80211_CIPHER_TKIP) {
                                iv[0] = k->wk_keytsc >> 8;
                                iv[1] = (iv[0] | 0x20) & 0x7f;
                                iv[2] = k->wk_keytsc;
                        } else  { //CCMP
                                iv[0] = k->wk_keytsc;
                                iv[1] = k->wk_keytsc >> 8;
                                iv[2] = 0;
                        }
                        iv[3] = k->wk_keyix << 6 | IEEE80211_WEP_EXTIV;
                        iv[4] = k->wk_keytsc >> 16;
                        iv[5] = k->wk_keytsc >> 24;
                        iv[6] = k->wk_keytsc >> 32;
                        iv[7] = k->wk_keytsc >> 40;
                }
                mtw_write_region_1(sc, MTW_IVEIV(wcid), iv, 8);
        }

        if (k->wk_flags & IEEE80211_KEY_GROUP) {
                /* install group key */
                mtw_read(sc, MTW_SKEY_MODE_0_7, &attr);
                attr &= ~(0xf << (k->wk_keyix * 4));
                attr |= mode << (k->wk_keyix * 4);
                mtw_write(sc, MTW_SKEY_MODE_0_7, attr);

                if (cipher & (IEEE80211_CIPHER_WEP)) {
                        mtw_read(sc, MTW_WCID_ATTR(wcid + 1), &attr);
                        attr = (attr & ~0xf) | (mode << 1);
                        mtw_write(sc, MTW_WCID_ATTR(wcid + 1), attr);

                        mtw_set_region_4(sc, MTW_IVEIV(0), 0, 4);

                        mtw_read(sc, MTW_WCID_ATTR(wcid), &attr);
                        attr = (attr & ~0xf) | (mode << 1);
                        mtw_write(sc, MTW_WCID_ATTR(wcid), attr);
                }
        } else {
                /* install pairwise key */
                mtw_read(sc, MTW_WCID_ATTR(wcid), &attr);
                attr = (attr & ~0xf) | (mode << 1) | MTW_RX_PKEY_EN;
                mtw_write(sc, MTW_WCID_ATTR(wcid), attr);
        }
        k->wk_pad = wcid;
}

/*
 * If wlan is destroyed without being brought down i.e. without
 * wlan down or wpa_cli terminate, this function is called after
 * vap is gone. Don't refer it.
 */
static void
mtw_key_delete_cb(void *arg)
{
        struct mtw_cmdq *cmdq = arg;
        struct mtw_softc *sc = cmdq->arg1;
        struct ieee80211_key *k = &cmdq->key;
        uint32_t attr;
        uint8_t wcid;

        MTW_LOCK_ASSERT(sc, MA_OWNED);

        if (k->wk_flags & IEEE80211_KEY_GROUP) {
                /* remove group key */
                MTW_DPRINTF(sc, MTW_DEBUG_KEY, "removing group key\n");
                mtw_read(sc, MTW_SKEY_MODE_0_7, &attr);
                attr &= ~(0xf << (k->wk_keyix * 4));
                mtw_write(sc, MTW_SKEY_MODE_0_7, attr);
        } else {
                /* remove pairwise key */
                MTW_DPRINTF(sc, MTW_DEBUG_KEY, "removing key for wcid %x\n",
                    k->wk_pad);
                /* matching wcid was written to wk_pad in mtw_key_set() */
                wcid = k->wk_pad;
                mtw_read(sc, MTW_WCID_ATTR(wcid), &attr);
                attr &= ~0xf;
                mtw_write(sc, MTW_WCID_ATTR(wcid), attr);
        }

        k->wk_pad = 0;
}

/*
 * return 0 on error
 */
static int
mtw_key_delete(struct ieee80211vap *vap, struct ieee80211_key *k)
{
        struct ieee80211com *ic = vap->iv_ic;
        struct mtw_softc *sc = ic->ic_softc;
        struct ieee80211_key *k0;
        uint32_t i;
        if (sc->sc_flags & MTW_RUNNING)
                return (1);

        /*
         * When called back, key might be gone. So, make a copy
         * of some values need to delete keys before deferring.
         * But, because of LOR with node lock, cannot use lock here.
         * So, use atomic instead.
         */
        i = MTW_CMDQ_GET(&sc->cmdq_store);
        MTW_DPRINTF(sc, MTW_DEBUG_KEY, "cmdq_store=%d\n", i);
        sc->cmdq[i].func = mtw_key_delete_cb;
        sc->cmdq[i].arg0 = NULL;
        sc->cmdq[i].arg1 = sc;
        k0 = &sc->cmdq[i].key;
        k0->wk_flags = k->wk_flags;
        k0->wk_keyix = k->wk_keyix;
        /* matching wcid was written to wk_pad in mtw_key_set() */
        k0->wk_pad = k->wk_pad;
        ieee80211_runtask(ic, &sc->cmdq_task);
        return (1); /* return fake success */
}

static void
mtw_ratectl_to(void *arg)
{
        struct mtw_softc *sc = arg;
        /* do it in a process context, so it can go sleep */
        ieee80211_runtask(&sc->sc_ic, &sc->ratectl_task);
        /* next timeout will be rescheduled in the callback task */
}

/* ARGSUSED */
static void
mtw_ratectl_cb(void *arg, int pending)
{

        struct mtw_softc *sc = arg;
        struct ieee80211com *ic = &sc->sc_ic;
        struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);

        if (vap == NULL)
                return;

        ieee80211_iterate_nodes(&ic->ic_sta, mtw_iter_func, sc);

        usb_callout_reset(&sc->ratectl_ch, hz, mtw_ratectl_to, sc);


}

static void
mtw_drain_fifo(void *arg)
{
        struct mtw_softc *sc = arg;
        uint32_t stat;
        uint16_t(*wstat)[3];
        uint8_t wcid, mcs, pid;
        int8_t retry;

        MTW_LOCK_ASSERT(sc, MA_OWNED);

        for (;;) {
                /* drain Tx status FIFO (maxsize = 16) */
                mtw_read(sc, MTW_TX_STAT_FIFO, &stat);
                MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "tx stat 0x%08x\n", stat);
                if (!(stat & MTW_TXQ_VLD))
                        break;

                wcid = (stat >> MTW_TXQ_WCID_SHIFT) & 0xff;

                /* if no ACK was requested, no feedback is available */
                if (!(stat & MTW_TXQ_ACKREQ) || wcid > MTW_WCID_MAX ||
                    wcid == 0)
                        continue;

                /*
                 * Even though each stat is Tx-complete-status like format,
                 * the device can poll stats. Because there is no guarantee
                 * that the referring node is still around when read the stats.
                 * So that, if we use ieee80211_ratectl_tx_update(), we will
                 * have hard time not to refer already freed node.
                 *
                 * To eliminate such page faults, we poll stats in softc.
                 * Then, update the rates later with
                 * ieee80211_ratectl_tx_update().
                 */
                wstat = &(sc->wcid_stats[wcid]);
                (*wstat)[MTW_TXCNT]++;
                if (stat & MTW_TXQ_OK)
                        (*wstat)[MTW_SUCCESS]++;
                else
                        counter_u64_add(sc->sc_ic.ic_oerrors, 1);
                /*
                 * Check if there were retries, ie if the Tx success rate is
                 * different from the requested rate. Note that it works only
                 * because we do not allow rate fallback from OFDM to CCK.
                 */
                mcs = (stat >> MTW_TXQ_MCS_SHIFT) & 0x7f;
                pid = (stat >> MTW_TXQ_PID_SHIFT) & 0xf;
                if ((retry = pid - 1 - mcs) > 0) {
                        (*wstat)[MTW_TXCNT] += retry;
                        (*wstat)[MTW_RETRY] += retry;
                }
        }
        MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "count=%d\n", sc->fifo_cnt);

        sc->fifo_cnt = 0;
}

static void
mtw_iter_func(void *arg, struct ieee80211_node *ni)
{
        struct mtw_softc *sc = arg;
        MTW_LOCK(sc);
        struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs;
        struct ieee80211vap *vap = ni->ni_vap;
        struct mtw_node *rn = MTW_NODE(ni);
        uint32_t sta[3];
        uint16_t(*wstat)[3];
        int error, ridx;
        uint8_t txrate = 0;

        /* Check for special case */
        if (sc->rvp_cnt <= 1 && vap->iv_opmode == IEEE80211_M_STA &&
            ni != vap->iv_bss)
                goto fail;

        txs->flags = IEEE80211_RATECTL_TX_STATS_NODE |
            IEEE80211_RATECTL_TX_STATS_RETRIES;
        txs->ni = ni;
        if (sc->rvp_cnt <= 1 &&
            (vap->iv_opmode == IEEE80211_M_IBSS ||
                vap->iv_opmode == IEEE80211_M_STA)) {
                /*
                 * read statistic counters (clear on read) and update AMRR state
                 */
                error = mtw_read_region_1(sc, MTW_TX_STA_CNT0, (uint8_t *)sta,
                    sizeof sta);
                MTW_DPRINTF(sc, MTW_DEBUG_RATE, "error:%d\n", error);
                if (error != 0)
                        goto fail;

                /* count failed TX as errors */
                if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS,
                    le32toh(sta[0]) & 0xffff);

                txs->nretries = (le32toh(sta[1]) >> 16);
                txs->nsuccess = (le32toh(sta[1]) & 0xffff);
                /* nretries??? */
                txs->nframes = txs->nsuccess + (le32toh(sta[0]) & 0xffff);

                MTW_DPRINTF(sc, MTW_DEBUG_RATE,
                    "retrycnt=%d success=%d failcnt=%d\n", txs->nretries,
                    txs->nsuccess, le32toh(sta[0]) & 0xffff);
        } else {
                wstat = &(sc->wcid_stats[MTW_AID2WCID(ni->ni_associd)]);

                if (wstat == &(sc->wcid_stats[0]) ||
                    wstat > &(sc->wcid_stats[MTW_WCID_MAX]))
                        goto fail;

                txs->nretries = (*wstat)[MTW_RETRY];
                txs->nsuccess = (*wstat)[MTW_SUCCESS];
                txs->nframes = (*wstat)[MTW_TXCNT];
                MTW_DPRINTF(sc, MTW_DEBUG_RATE,
                    "wstat retrycnt=%d txcnt=%d success=%d\n", txs->nretries,
                    txs->nframes, txs->nsuccess);

                memset(wstat, 0, sizeof(*wstat));
        }

        ieee80211_ratectl_tx_update(vap, txs);
        ieee80211_ratectl_rate(ni, NULL, 0);
        txrate = ieee80211_node_get_txrate_dot11rate(ni);

        /* XXX TODO: methodize with MCS rates */
        for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++) {
                MTW_DPRINTF(sc, MTW_DEBUG_RATE, "ni_txrate=0x%x\n",
                             txrate);
                if (rt2860_rates[ridx].rate == txrate) {
                        break;
                }
        }
        rn->amrr_ridx = ridx;
fail:
        MTW_UNLOCK(sc);

        MTW_DPRINTF(sc, MTW_DEBUG_RATE, "rate=%d, ridx=%d\n",
                    txrate, rn->amrr_ridx);
}

static void
mtw_newassoc_cb(void *arg)
{
        struct mtw_cmdq *cmdq = arg;
        struct ieee80211_node *ni = cmdq->arg1;
        struct mtw_softc *sc = ni->ni_vap->iv_ic->ic_softc;

        uint8_t wcid = cmdq->wcid;

        MTW_LOCK_ASSERT(sc, MA_OWNED);

        mtw_write_region_1(sc, MTW_WCID_ENTRY(wcid), ni->ni_macaddr,
            IEEE80211_ADDR_LEN);

        memset(&(sc->wcid_stats[wcid]), 0, sizeof(sc->wcid_stats[wcid]));
}

static void
mtw_newassoc(struct ieee80211_node *ni, int isnew)
{

        struct mtw_node *mn = MTW_NODE(ni);
        struct ieee80211vap *vap = ni->ni_vap;
        struct ieee80211com *ic = vap->iv_ic;
        struct mtw_softc *sc = ic->ic_softc;

        uint8_t rate;
        uint8_t ridx;
        uint8_t wcid;
        //int i;
        // int i,j;
        wcid = MTW_AID2WCID(ni->ni_associd);

        if (wcid > MTW_WCID_MAX) {
                device_printf(sc->sc_dev, "wcid=%d out of range\n", wcid);
                return;
        }

        /* only interested in true associations */
        if (isnew && ni->ni_associd != 0) {
                /*
                 * This function could is called though timeout function.
                 * Need to deferggxr.
                 */

                uint32_t cnt = MTW_CMDQ_GET(&sc->cmdq_store);
                MTW_DPRINTF(sc, MTW_DEBUG_STATE, "cmdq_store=%d\n", cnt);
                sc->cmdq[cnt].func = mtw_newassoc_cb;
                sc->cmdq[cnt].arg0 = NULL;
                sc->cmdq[cnt].arg1 = ni;
                sc->cmdq[cnt].wcid = wcid;
                ieee80211_runtask(ic, &sc->cmdq_task);
        }

        MTW_DPRINTF(sc, MTW_DEBUG_STATE,
            "new assoc isnew=%d associd=%x addr=%s\n", isnew, ni->ni_associd,
            ether_sprintf(ni->ni_macaddr));
        rate = vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)].mgmtrate;
        /* XXX TODO: methodize with MCS rates */
        for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++)
                if (rt2860_rates[ridx].rate == rate)
                        break;
        mn->mgt_ridx = ridx;
        MTW_DPRINTF(sc, MTW_DEBUG_STATE | MTW_DEBUG_RATE,
            "rate=%d, ctl_ridx=%d\n", rate, ridx);
        MTW_LOCK(sc);
        if (sc->ratectl_run != MTW_RATECTL_OFF) {
                usb_callout_reset(&sc->ratectl_ch, hz, &mtw_ratectl_to, sc);
        }
        MTW_UNLOCK(sc);

}

/*
 * Return the Rx chain with the highest RSSI for a given frame.
 */
static __inline uint8_t
mtw_maxrssi_chain(struct mtw_softc *sc, const struct mtw_rxwi *rxwi)
{
        uint8_t rxchain = 0;

        if (sc->nrxchains > 1) {
                if (rxwi->rssi[1] > rxwi->rssi[rxchain])
                        rxchain = 1;
                if (sc->nrxchains > 2)
                        if (rxwi->rssi[2] > rxwi->rssi[rxchain])
                                rxchain = 2;
        }
        return (rxchain);
}
static void
mtw_get_tsf(struct mtw_softc *sc, uint64_t *buf)
{
        mtw_read_region_1(sc, MTW_TSF_TIMER_DW0, (uint8_t *)buf, sizeof(*buf));
}

static void
mtw_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype,
    const struct ieee80211_rx_stats *rxs, int rssi, int nf)
{
        struct ieee80211vap *vap = ni->ni_vap;
        struct mtw_softc *sc = vap->iv_ic->ic_softc;
        struct mtw_vap *rvp = MTW_VAP(vap);
        uint64_t ni_tstamp, rx_tstamp;

        rvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf);

        if (vap->iv_state == IEEE80211_S_RUN &&
            (subtype == IEEE80211_FC0_SUBTYPE_BEACON ||
                subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) {
                ni_tstamp = le64toh(ni->ni_tstamp.tsf);
                MTW_LOCK(sc);
                mtw_get_tsf(sc, &rx_tstamp);
                MTW_UNLOCK(sc);
                rx_tstamp = le64toh(rx_tstamp);

                if (ni_tstamp >= rx_tstamp) {
                        MTW_DPRINTF(sc, MTW_DEBUG_RECV | MTW_DEBUG_BEACON,
                            "ibss merge, tsf %ju tstamp %ju\n",
                            (uintmax_t)rx_tstamp, (uintmax_t)ni_tstamp);
                        (void)ieee80211_ibss_merge(ni);
                }
        }
}
static void
mtw_rx_frame(struct mtw_softc *sc, struct mbuf *m, uint32_t dmalen)
{
        struct ieee80211com *ic = &sc->sc_ic;
        struct ieee80211_frame *wh;
        struct ieee80211_node *ni;
        struct epoch_tracker et;

        struct mtw_rxwi *rxwi;
        uint32_t flags;
        uint16_t len, rxwisize;
        uint8_t ant, rssi;
        int8_t nf;

        rxwisize = sizeof(struct mtw_rxwi);

        if (__predict_false(
                dmalen < rxwisize + sizeof(struct ieee80211_frame_ack))) {
                MTW_DPRINTF(sc, MTW_DEBUG_RECV,
                    "payload is too short: dma length %u < %zu\n", dmalen,
                    rxwisize + sizeof(struct ieee80211_frame_ack));
                goto fail;
        }

        rxwi = mtod(m, struct mtw_rxwi *);
        len = le16toh(rxwi->len) & 0xfff;
        flags = le32toh(rxwi->flags);
        if (__predict_false(len > dmalen - rxwisize)) {
                MTW_DPRINTF(sc, MTW_DEBUG_RECV, "bad RXWI length %u > %u\n",
                    len, dmalen);
                goto fail;
        }

        if (__predict_false(flags & (MTW_RX_CRCERR | MTW_RX_ICVERR))) {
                MTW_DPRINTF(sc, MTW_DEBUG_RECV, "%s error.\n",
                    (flags & MTW_RX_CRCERR) ? "CRC" : "ICV");
                goto fail;
        }

        if (flags & MTW_RX_L2PAD) {
                MTW_DPRINTF(sc, MTW_DEBUG_RECV,
                    "received RT2860_RX_L2PAD frame\n");
                len += 2;
        }

        m->m_data += rxwisize;
        m->m_pkthdr.len = m->m_len = len;

        wh = mtod(m, struct ieee80211_frame *);
        if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
                wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
                m->m_flags |= M_WEP;
        }

        if (len >= sizeof(struct ieee80211_frame_min)) {
                ni = ieee80211_find_rxnode(ic,
                    mtod(m, struct ieee80211_frame_min *));
        } else
                ni = NULL;

        if (ni && ni->ni_flags & IEEE80211_NODE_HT) {
                m->m_flags |= M_AMPDU;
        }

        if (__predict_false(flags & MTW_RX_MICERR)) {
                /* report MIC failures to net80211 for TKIP */
                if (ni != NULL)
                        ieee80211_notify_michael_failure(ni->ni_vap, wh,
                            rxwi->keyidx);
                MTW_DPRINTF(sc, MTW_DEBUG_RECV,
                    "MIC error. Someone is lying.\n");
                goto fail;
        }

        ant = mtw_maxrssi_chain(sc, rxwi);
        rssi = rxwi->rssi[ant];
        nf = mtw_rssi2dbm(sc, rssi, ant);

        if (__predict_false(ieee80211_radiotap_active(ic))) {
                struct mtw_rx_radiotap_header *tap = &sc->sc_rxtap;
                uint16_t phy;

                tap->wr_flags = 0;
                if (flags & MTW_RX_L2PAD)
                        tap->wr_flags |= IEEE80211_RADIOTAP_F_DATAPAD;
                tap->wr_antsignal = rssi;
                tap->wr_antenna = ant;
                tap->wr_dbm_antsignal = mtw_rssi2dbm(sc, rssi, ant);
                tap->wr_rate = 2; /* in case it can't be found below */
                //MTW_LOCK(sc);

        //      MTW_UNLOCK(sc);
                phy = le16toh(rxwi->phy);
                switch (phy >> MT7601_PHY_SHIFT) {
                case MTW_PHY_CCK:
                        switch ((phy & MTW_PHY_MCS) & ~MTW_PHY_SHPRE) {
                        case 0:
                                tap->wr_rate = 2;
                                break;
                        case 1:
                                tap->wr_rate = 4;
                                break;
                        case 2:
                                tap->wr_rate = 11;
                                break;
                        case 3:
                                tap->wr_rate = 22;
                                break;
                        }
                        if (phy & MTW_PHY_SHPRE)
                                tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
                        break;
                case MTW_PHY_OFDM:
                        switch (phy & MTW_PHY_MCS) {
                        case 0:
                                tap->wr_rate = 12;
                                break;
                        case 1:
                                tap->wr_rate = 18;
                                break;
                        case 2:
                                tap->wr_rate = 24;
                                break;
                        case 3:
                                tap->wr_rate = 36;
                                break;
                        case 4:
                                tap->wr_rate = 48;
                                break;
                        case 5:
                                tap->wr_rate = 72;
                                break;
                        case 6:
                                tap->wr_rate = 96;
                                break;
                        case 7:
                                tap->wr_rate = 108;
                                break;
                        }
                        break;
                }
        }

        NET_EPOCH_ENTER(et);
        if (ni != NULL) {
                (void)ieee80211_input(ni, m, rssi, nf);
                ieee80211_free_node(ni);
        } else {
                (void)ieee80211_input_all(ic, m, rssi, nf);
        }
        NET_EPOCH_EXIT(et);

        return;

fail:
        m_freem(m);
        counter_u64_add(ic->ic_ierrors, 1);
}

static void
mtw_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error)
{
        struct mtw_softc *sc = usbd_xfer_softc(xfer);
        struct ieee80211com *ic = &sc->sc_ic;
        struct mbuf *m = NULL;
        struct mbuf *m0;
        uint32_t dmalen, mbuf_len;
        uint16_t rxwisize;
        int xferlen;

        rxwisize = sizeof(struct mtw_rxwi);

        usbd_xfer_status(xfer, &xferlen, NULL, NULL, NULL);

        switch (USB_GET_STATE(xfer)) {
        case USB_ST_TRANSFERRED:
                MTW_DPRINTF(sc, MTW_DEBUG_RECV, "rx done, actlen=%d\n",
                    xferlen);
                if (xferlen < (int)(sizeof(uint32_t) + rxwisize +
                                  sizeof(struct mtw_rxd))) {
                        MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB,
                            "xfer too short %d %d\n", xferlen,
                            (int)(sizeof(uint32_t) + rxwisize +
                                sizeof(struct mtw_rxd)));
                        goto tr_setup;
                }

                m = sc->rx_m;
                sc->rx_m = NULL;

                /* FALLTHROUGH */
        case USB_ST_SETUP:
        tr_setup:

                if (sc->rx_m == NULL) {
                        sc->rx_m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR,
                            MTW_MAX_RXSZ);
                }
                if (sc->rx_m == NULL) {
                        MTW_DPRINTF(sc,
                            MTW_DEBUG_RECV | MTW_DEBUG_RECV_DESC |
                                MTW_DEBUG_USB,
                            "could not allocate mbuf - idle with stall\n");
                        counter_u64_add(ic->ic_ierrors, 1);
                        usbd_xfer_set_stall(xfer);
                        usbd_xfer_set_frames(xfer, 0);
                } else {
                        /*
                         * Directly loading a mbuf cluster into DMA to
                         * save some data copying. This works because
                         * there is only one cluster.
                         */
                        usbd_xfer_set_frame_data(xfer, 0,
                            mtod(sc->rx_m, caddr_t), MTW_MAX_RXSZ);
                        usbd_xfer_set_frames(xfer, 1);
                }
                usbd_transfer_submit(xfer);
                break;

        default: /* Error */
                MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB,
                    "USB transfer error, %s\n", usbd_errstr(error));

                if (error != USB_ERR_CANCELLED) {
                        /* try to clear stall first */
                        usbd_xfer_set_stall(xfer);
                        if (error == USB_ERR_TIMEOUT)
                                device_printf(sc->sc_dev, "device timeout %s\n",
                                    __func__);
                        counter_u64_add(ic->ic_ierrors, 1);
                        goto tr_setup;
                }
                if (sc->rx_m != NULL) {
                        m_freem(sc->rx_m);
                        sc->rx_m = NULL;
                }
                break;
        }

        if (m == NULL)
                return;

        /* inputting all the frames must be last */

        MTW_UNLOCK(sc);

        m->m_pkthdr.len = m->m_len = xferlen;

        /* HW can aggregate multiple 802.11 frames in a single USB xfer */
        for (;;) {
                dmalen = le32toh(*mtod(m, uint32_t *)) & 0xffff;

                if ((dmalen >= (uint32_t)-8) || (dmalen == 0) ||
                    ((dmalen & 3) != 0)) {
                        MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB,
                            "bad DMA length %u\n", dmalen);
                        break;
                }
                if ((dmalen + 8) > (uint32_t)xferlen) {
                        MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB,
                            "bad DMA length %u > %d\n", dmalen + 8, xferlen);
                        break;
                }

                /* If it is the last one or a single frame, we won't copy. */
                if ((xferlen -= dmalen + 8) <= 8) {
                        /* trim 32-bit DMA-len header */
                        m->m_data += 4;
                        m->m_pkthdr.len = m->m_len -= 4;
                        mtw_rx_frame(sc, m, dmalen);
                        m = NULL; /* don't free source buffer */
                        break;
                }

                mbuf_len = dmalen + sizeof(struct mtw_rxd);
                if (__predict_false(mbuf_len > MCLBYTES)) {
                        MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB,
                            "payload is too big: mbuf_len %u\n", mbuf_len);
                        counter_u64_add(ic->ic_ierrors, 1);
                        break;
                }

                /* copy aggregated frames to another mbuf */
                m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
                if (__predict_false(m0 == NULL)) {
                        MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC,
                            "could not allocate mbuf\n");
                        counter_u64_add(ic->ic_ierrors, 1);
                        break;
                }
                m_copydata(m, 4 /* skip 32-bit DMA-len header */, mbuf_len,
                    mtod(m0, caddr_t));
                m0->m_pkthdr.len = m0->m_len = mbuf_len;
                mtw_rx_frame(sc, m0, dmalen);

                /* update data ptr */
                m->m_data += mbuf_len + 4;
                m->m_pkthdr.len = m->m_len -= mbuf_len + 4;
        }

        /* make sure we free the source buffer, if any */
        m_freem(m);

#ifdef IEEE80211_SUPPORT_SUPERG
        ieee80211_ff_age_all(ic, 100);
#endif
        MTW_LOCK(sc);
}

static void
mtw_tx_free(struct mtw_endpoint_queue *pq, struct mtw_tx_data *data, int txerr)
{

        ieee80211_tx_complete(data->ni, data->m, txerr);
        data->m = NULL;
        data->ni = NULL;

        STAILQ_INSERT_TAIL(&pq->tx_fh, data, next);
        pq->tx_nfree++;
}
static void
mtw_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error, u_int index)
{
        struct mtw_softc *sc = usbd_xfer_softc(xfer);
        struct ieee80211com *ic = &sc->sc_ic;
        struct mtw_tx_data *data;
        struct ieee80211vap *vap = NULL;
        struct usb_page_cache *pc;
        struct mtw_endpoint_queue *pq = &sc->sc_epq[index];
        struct mbuf *m;
        usb_frlength_t size;
        int actlen;
        int sumlen;
        usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);

        switch (USB_GET_STATE(xfer)) {
        case USB_ST_TRANSFERRED:
                MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB,
                    "transfer complete: %d bytes @ index %d\n", actlen, index);

                data = usbd_xfer_get_priv(xfer);
                mtw_tx_free(pq, data, 0);
                usbd_xfer_set_priv(xfer, NULL);

                /* FALLTHROUGH */
        case USB_ST_SETUP:
        tr_setup:
                data = STAILQ_FIRST(&pq->tx_qh);
                if (data == NULL)
                        break;

                STAILQ_REMOVE_HEAD(&pq->tx_qh, next);

                m = data->m;

                size = sizeof(data->desc);
                if ((m->m_pkthdr.len + size + 3 + 8) > MTW_MAX_TXSZ) {
                        MTW_DPRINTF(sc, MTW_DEBUG_XMIT_DESC | MTW_DEBUG_USB,
                            "data overflow, %u bytes\n", m->m_pkthdr.len);
                        mtw_tx_free(pq, data, 1);
                        goto tr_setup;
                }

                pc = usbd_xfer_get_frame(xfer, 0);
                usbd_copy_in(pc, 0, &data->desc, size);
                usbd_m_copy_in(pc, size, m, 0, m->m_pkthdr.len);
                size += m->m_pkthdr.len;
                /*
                 * Align end on a 4-byte boundary, pad 8 bytes (CRC +
                 * 4-byte padding), and be sure to zero those trailing
                 * bytes:
                 */
                usbd_frame_zero(pc, size, ((-size) & 3) + MTW_DMA_PAD);
                size += ((-size) & 3) + MTW_DMA_PAD;

                vap = data->ni->ni_vap;
                if (ieee80211_radiotap_active_vap(vap)) {
                        const struct ieee80211_frame *wh;
                        struct mtw_tx_radiotap_header *tap = &sc->sc_txtap;
                        struct mtw_txwi *txwi =
                            (struct mtw_txwi *)(&data->desc +
                                sizeof(struct mtw_txd));
                        int has_l2pad;

                        wh = mtod(m, struct ieee80211_frame *);
                        has_l2pad = IEEE80211_HAS_ADDR4(wh) !=
                            IEEE80211_QOS_HAS_SEQ(wh);

                        tap->wt_flags = 0;
                        tap->wt_rate = rt2860_rates[data->ridx].rate;
                        tap->wt_hwqueue = index;
                        if (le16toh(txwi->phy) & MTW_PHY_SHPRE)
                                tap->wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
                        if (has_l2pad)
                                tap->wt_flags |= IEEE80211_RADIOTAP_F_DATAPAD;

                        ieee80211_radiotap_tx(vap, m);
                }

                MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB,
                    "sending frame len=%u/%u @ index %d\n", m->m_pkthdr.len,
                    size, index);

                usbd_xfer_set_frame_len(xfer, 0, size);
                usbd_xfer_set_priv(xfer, data);
                usbd_transfer_submit(xfer);
                mtw_start(sc);

                break;

        default:
                MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB,
                    "USB transfer error, %s\n", usbd_errstr(error));

                data = usbd_xfer_get_priv(xfer);

                if (data != NULL) {
                        if (data->ni != NULL)
                                vap = data->ni->ni_vap;
                        mtw_tx_free(pq, data, error);
                        usbd_xfer_set_priv(xfer, NULL);
                }

                if (vap == NULL)
                        vap = TAILQ_FIRST(&ic->ic_vaps);

                if (error != USB_ERR_CANCELLED) {
                        if (error == USB_ERR_TIMEOUT) {
                                device_printf(sc->sc_dev, "device timeout %s\n",
                                    __func__);
                                uint32_t i = MTW_CMDQ_GET(&sc->cmdq_store);
                                MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB,
                                    "cmdq_store=%d\n", i);
                                sc->cmdq[i].func = mtw_usb_timeout_cb;
                                sc->cmdq[i].arg0 = vap;
                                ieee80211_runtask(ic, &sc->cmdq_task);
                        }

                        /*
                         * Try to clear stall first, also if other
                         * errors occur, hence clearing stall
                         * introduces a 50 ms delay:
                         */
                        usbd_xfer_set_stall(xfer);
                        goto tr_setup;
                }
                break;
        }
#ifdef IEEE80211_SUPPORT_SUPERG
        /* XXX TODO: make this deferred rather than unlock/relock */
        /* XXX TODO: should only do the QoS AC this belongs to */
        if (pq->tx_nfree >= MTW_TX_RING_COUNT) {
                MTW_UNLOCK(sc);
                ieee80211_ff_flush_all(ic);
                MTW_LOCK(sc);
        }
#endif
}

static void
mtw_fw_callback(struct usb_xfer *xfer, usb_error_t error)
{
        struct mtw_softc *sc = usbd_xfer_softc(xfer);

        int actlen;
        int ntries, tmp;
        // struct mtw_txd *data;

        usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
        // data = usbd_xfer_get_priv(xfer);
        usbd_xfer_set_priv(xfer, NULL);
        switch (USB_GET_STATE(xfer)) {

        case USB_ST_TRANSFERRED:
                sc->sc_sent += actlen;
                memset(sc->txd_fw[sc->sc_idx], 0, actlen);

                if (actlen < 0x2c44 && sc->sc_idx == 0) {
                        return;
                }
                if (sc->sc_idx == 3) {

                        if ((error = mtw_write_ivb(sc, sc->sc_ivb_1,
                                    MTW_MCU_IVB_LEN)) != 0) {
                                device_printf(sc->sc_dev,
                                    "Could not write ivb error:  %d\n", error);
                        }

                        mtw_delay(sc, 10);
                        for (ntries = 0; ntries < 100; ntries++) {
                                if ((error = mtw_read_cfg(sc, MTW_MCU_DMA_ADDR,
                                         &tmp)) != 0) {
                                        device_printf(sc->sc_dev,
                                    "Could not read cfg error:  %d\n", error);

                                }
                                if (tmp == MTW_MCU_READY) {
                                        MTW_DPRINTF(sc, MTW_DEBUG_FIRMWARE,
                                            "mcu reaady %d\n", tmp);
                                        sc->fwloading = 1;
                                        break;
                                }

                                mtw_delay(sc, 10);
                        }
                        if (ntries == 100)
                                sc->fwloading = 0;
                        wakeup(&sc->fwloading);
                        return;
                }

                if (actlen == 0x2c44) {
                        sc->sc_idx++;
                        DELAY(1000);
                }

        case USB_ST_SETUP: {
                int dlen = 0;
                dlen = sc->txd_fw[sc->sc_idx]->len;

                mtw_write_cfg(sc, MTW_MCU_DMA_ADDR, 0x40 + sc->sc_sent);
                mtw_write_cfg(sc, MTW_MCU_DMA_LEN, (dlen << 16));

                usbd_xfer_set_frame_len(xfer, 0, dlen);
                usbd_xfer_set_frame_data(xfer, 0, sc->txd_fw[sc->sc_idx], dlen);

                // usbd_xfer_set_priv(xfer,sc->txd[sc->sc_idx]);
                usbd_transfer_submit(xfer);
                break;

        default: /* Error */
                device_printf(sc->sc_dev, "%s:%d %s\n", __FILE__, __LINE__,
                    usbd_errstr(error));
                sc->fwloading = 0;
                wakeup(&sc->fwloading);
                /*
                 * Print error message and clear stall
                 * for example.
                 */
                break;
        }
                /*
                 * Here it is safe to do something without the private
                 * USB mutex    locked.
                 */
        }
        return;
}
static void
mtw_bulk_tx_callback0(struct usb_xfer *xfer, usb_error_t error)
{
        mtw_bulk_tx_callbackN(xfer, error, 0);
}

static void
mtw_bulk_tx_callback1(struct usb_xfer *xfer, usb_error_t error)
{


        mtw_bulk_tx_callbackN(xfer, error, 1);
}

static void
mtw_bulk_tx_callback2(struct usb_xfer *xfer, usb_error_t error)
{
        mtw_bulk_tx_callbackN(xfer, error, 2);
}

static void
mtw_bulk_tx_callback3(struct usb_xfer *xfer, usb_error_t error)
{
        mtw_bulk_tx_callbackN(xfer, error, 3);
}

static void
mtw_bulk_tx_callback4(struct usb_xfer *xfer, usb_error_t error)
{
        mtw_bulk_tx_callbackN(xfer, error, 4);
}

static void
mtw_bulk_tx_callback5(struct usb_xfer *xfer, usb_error_t error)
{
        mtw_bulk_tx_callbackN(xfer, error, 5);
}

static void
mtw_set_tx_desc(struct mtw_softc *sc, struct mtw_tx_data *data)
{
        struct mbuf *m = data->m;
        struct ieee80211com *ic = &sc->sc_ic;
        struct ieee80211vap *vap = data->ni->ni_vap;
        struct ieee80211_frame *wh;
        struct mtw_txd *txd;
        struct mtw_txwi *txwi;
        uint16_t xferlen, txwisize;
        uint16_t mcs;
        uint8_t ridx = data->ridx;
        uint8_t pad;

        /* get MCS code from rate index */
        mcs = rt2860_rates[ridx].mcs;

        txwisize = sizeof(*txwi);
        xferlen = txwisize + m->m_pkthdr.len;

        /* roundup to 32-bit alignment */
        xferlen = (xferlen + 3) & ~3;

        txd = (struct mtw_txd *)&data->desc;
        txd->len = htole16(xferlen);

        wh = mtod(m, struct ieee80211_frame *);

        /*
         * Ether both are true or both are false, the header
         * are nicely aligned to 32-bit. So, no L2 padding.
         */
        if (IEEE80211_HAS_ADDR4(wh) == IEEE80211_QOS_HAS_SEQ(wh))
                pad = 0;
        else
                pad = 2;

        /* setup TX Wireless Information */
        txwi = (struct mtw_txwi *)(txd + 1);
        txwi->len = htole16(m->m_pkthdr.len - pad);
        if (rt2860_rates[ridx].phy == IEEE80211_T_DS) {
                mcs |=  MTW_PHY_CCK;
                if (ridx != MTW_RIDX_CCK1 &&
                    (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
                        mcs |=  MTW_PHY_SHPRE;
        } else if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM) {
                mcs |=  MTW_PHY_OFDM;
        } else if (rt2860_rates[ridx].phy == IEEE80211_T_HT) {
                /* XXX TODO: [adrian] set short preamble for MCS? */
                mcs |=  MTW_PHY_HT; /* Mixed, not greenfield */
        }
        txwi->phy = htole16(mcs);

        /* check if RTS/CTS or CTS-to-self protection is required */
        if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
            ((m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) ||
             ((ic->ic_flags & IEEE80211_F_USEPROT) &&
              rt2860_rates[ridx].phy == IEEE80211_T_OFDM) ||
             ((ic->ic_htprotmode == IEEE80211_PROT_RTSCTS) &&
              rt2860_rates[ridx].phy == IEEE80211_T_HT)))
                txwi->txop |= MTW_TX_TXOP_HT;
        else
                txwi->txop |=   MTW_TX_TXOP_BACKOFF;

}

/* This function must be called locked */
static int
mtw_tx(struct mtw_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
{
        struct ieee80211com *ic = &sc->sc_ic;
        struct ieee80211vap *vap = ni->ni_vap;
        struct ieee80211_frame *wh;


        //const struct ieee80211_txparam *tp = ni->ni_txparms;
        struct mtw_node *rn = MTW_NODE(ni);
        struct mtw_tx_data *data;
        struct mtw_txd *txd;
        struct mtw_txwi *txwi;
        uint16_t qos;
        uint16_t dur;
        uint16_t qid;
        uint8_t type;
        uint8_t tid;
        uint16_t ridx;
        uint8_t ctl_ridx;
        uint16_t qflags;
        uint8_t xflags = 0;

        int hasqos;

        MTW_LOCK_ASSERT(sc, MA_OWNED);

        wh = mtod(m, struct ieee80211_frame *);
        const struct ieee80211_txparam *tp = ni->ni_txparms;
        type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;

        qflags = htole16(MTW_TXD_DATA | MTW_TXD_80211 |
                         MTW_TXD_WLAN | MTW_TXD_QSEL_HCCA);

        if ((hasqos = IEEE80211_QOS_HAS_SEQ(wh))) {
                uint8_t *frm;
                frm = ieee80211_getqos(wh);


                //device_printf(sc->sc_dev,"JSS:frm:%d",*frm);
                qos = le16toh(*(const uint16_t *)frm);
                tid = ieee80211_gettid(wh);
                qid = TID_TO_WME_AC(tid);
                qflags |= MTW_TXD_QSEL_EDCA;
        } else {
                qos = 0;
                tid = 0;
                qid = WME_AC_BE;
        }
        if (type & IEEE80211_FC0_TYPE_MGT) {
                qid = 0;
        }

        if (type != IEEE80211_FC0_TYPE_DATA)
                qflags |= htole16(MTW_TXD_WIV);

        if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
            type != IEEE80211_FC0_TYPE_DATA || m->m_flags & M_EAPOL) {
                /* XXX TODO: methodize for 11n; use MCS0 for 11NA/11NG */
                ridx = (ic->ic_curmode == IEEE80211_MODE_11A
                        || ic->ic_curmode == IEEE80211_MODE_11NA) ?
                        MTW_RIDX_OFDM6 : MTW_RIDX_CCK1;
                if (type == IEEE80211_MODE_11NG) {
                        ridx = 12;
                }
                ctl_ridx = rt2860_rates[ridx].ctl_ridx;
        } else {
                if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
                        ridx = rn->fix_ridx;

                } else {
                        ridx = rn->amrr_ridx;
                        ctl_ridx = rt2860_rates[ridx].ctl_ridx;
                }
        }

        if (hasqos)
                xflags = 0;
        else
                xflags = MTW_TX_NSEQ;

        if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
            (!hasqos ||
                (qos & IEEE80211_QOS_ACKPOLICY) !=
                    IEEE80211_QOS_ACKPOLICY_NOACK)) {
                xflags |= MTW_TX_ACK;
                if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
                        dur = rt2860_rates[ctl_ridx].sp_ack_dur;
                else
                        dur = rt2860_rates[ctl_ridx].lp_ack_dur;
                USETW(wh->i_dur, dur);
        }
        /* reserve slots for mgmt packets, just in case */
        if (sc->sc_epq[qid].tx_nfree < 3) {
                MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "tx ring %d is full\n", qid);
                return (-1);
        }

        data = STAILQ_FIRST(&sc->sc_epq[qid].tx_fh);
        STAILQ_REMOVE_HEAD(&sc->sc_epq[qid].tx_fh, next);
        sc->sc_epq[qid].tx_nfree--;

        txd = (struct mtw_txd *)&data->desc;
        txd->flags = qflags;

        txwi = (struct mtw_txwi *)(txd + 1);
        txwi->xflags = xflags;
        txwi->wcid = (type == IEEE80211_FC0_TYPE_DATA) ?

           MTW_AID2WCID(ni->ni_associd) :
            0xff;

        /* clear leftover garbage bits */
        txwi->flags = 0;
        txwi->txop = 0;

        data->m = m;
        data->ni = ni;
        data->ridx = ridx;

        mtw_set_tx_desc(sc, data);

        /*
         * The chip keeps track of 2 kind of Tx stats,
         *  * TX_STAT_FIFO, for per WCID stats, and
         *  * TX_STA_CNT0 for all-TX-in-one stats.
         *
         * To use FIFO stats, we need to store MCS into the driver-private
         * PacketID field. So that, we can tell whose stats when we read them.
         * We add 1 to the MCS because setting the PacketID field to 0 means
         * that we don't want feedback in TX_STAT_FIFO.
         * And, that's what we want for STA mode, since TX_STA_CNT0 does the
         * job.
         *
         * FIFO stats doesn't count Tx with WCID 0xff, so we do this in
         * run_tx().
         */

        if (sc->rvp_cnt > 1 || vap->iv_opmode == IEEE80211_M_HOSTAP ||
            vap->iv_opmode == IEEE80211_M_MBSS) {

                /*
                 * Unlike PCI based devices, we don't get any interrupt from
                 * USB devices, so we simulate FIFO-is-full interrupt here.
                 * Ralink recommends to drain FIFO stats every 100 ms, but 16
                 * slots quickly get fulled. To prevent overflow, increment a
                 * counter on every FIFO stat request, so we know how many slots
                 * are left. We do this only in HOSTAP or multiple vap mode
                 * since FIFO stats are used only in those modes. We just drain
                 * stats. AMRR gets updated every 1 sec by run_ratectl_cb() via
                 * callout. Call it early. Otherwise overflow.
                 */
                if (sc->fifo_cnt++ == 10) {
                        /*
                         * With multiple vaps or if_bridge, if_start() is called
                         * with a non-sleepable lock, tcpinp. So, need to defer.
                         */
                        uint32_t i = MTW_CMDQ_GET(&sc->cmdq_store);
                        MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "cmdq_store=%d\n", i);
                        sc->cmdq[i].func = mtw_drain_fifo;
                        sc->cmdq[i].arg0 = sc;
                        ieee80211_runtask(ic, &sc->cmdq_task);
                }
        }

        STAILQ_INSERT_TAIL(&sc->sc_epq[qid].tx_qh, data, next);
        usbd_transfer_start(sc->sc_xfer[mtw_wme_ac_xfer_map[qid]]);

        MTW_DPRINTF(sc, MTW_DEBUG_XMIT,
            "sending data frame len=%d rate=%d qid=%d\n",
            m->m_pkthdr.len +
                (int)(sizeof(struct mtw_txd) + sizeof(struct mtw_txwi)),
            rt2860_rates[ridx].rate, qid);

        return (0);
        }

static int
mtw_tx_mgt(struct mtw_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
{
        struct ieee80211com *ic = &sc->sc_ic;
        struct mtw_node *rn = MTW_NODE(ni);
        struct mtw_tx_data *data;
        struct ieee80211_frame *wh;
        struct mtw_txd *txd;
        struct mtw_txwi *txwi;
        uint8_t type;
        uint16_t dur;
        uint8_t ridx = rn->mgt_ridx;
        uint8_t xflags = 0;
        uint8_t wflags = 0;

        MTW_LOCK_ASSERT(sc, MA_OWNED);

        wh = mtod(m, struct ieee80211_frame *);

        /* tell hardware to add timestamp for probe responses  */
        if ((wh->i_fc[0] &
                (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
            (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP))
                wflags |= MTW_TX_TS;
        if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
                xflags |= MTW_TX_ACK;

                dur = ieee80211_ack_duration(ic->ic_rt, rt2860_rates[ridx].rate,
                    ic->ic_flags & IEEE80211_F_SHPREAMBLE);
                USETW(wh->i_dur, dur);
        }
        type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
        if (sc->sc_epq[0].tx_nfree == 0)
                /* let caller free mbuf */
                return (EIO);
        data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh);
        STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next);
        sc->sc_epq[0].tx_nfree--;

        txd = (struct mtw_txd *)&data->desc;
        txd->flags = htole16(
            MTW_TXD_DATA | MTW_TXD_80211 | MTW_TXD_WLAN | MTW_TXD_QSEL_EDCA);
        if (type != IEEE80211_FC0_TYPE_DATA)
                txd->flags |= htole16(MTW_TXD_WIV);

        txwi = (struct mtw_txwi *)(txd + 1);
        txwi->wcid = 0xff;
        txwi->xflags = xflags;
        txwi->flags = wflags;

        txwi->txop = 0; /* clear leftover garbage bits */

        data->m = m;
        data->ni = ni;
        data->ridx = ridx;

        MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "sending mgt frame len=%d rate=%d\n",
            m->m_pkthdr.len +
                (int)(sizeof(struct mtw_txd) + sizeof(struct mtw_txwi)),
            rt2860_rates[ridx].rate);

        STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next);

        usbd_transfer_start(sc->sc_xfer[MTW_BULK_TX_BE]);

        return (0);
}

static int
mtw_sendprot(struct mtw_softc *sc, const struct mbuf *m,
    struct ieee80211_node *ni, int prot, int rate)
{
        struct ieee80211com *ic = ni->ni_ic;
        struct mtw_tx_data *data;
        struct mtw_txd *txd;
        struct mtw_txwi *txwi;
        struct mbuf *mprot;
        int ridx;
        int protrate;
        uint8_t wflags = 0;
        uint8_t xflags = 0;

        MTW_LOCK_ASSERT(sc, MA_OWNED);

        /* check that there are free slots before allocating the mbuf */
        if (sc->sc_epq[0].tx_nfree == 0)
                /* let caller free mbuf */
                return (ENOBUFS);

        mprot = ieee80211_alloc_prot(ni, m, rate, prot);
        if (mprot == NULL) {
                if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1);
                MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "could not allocate mbuf\n");
                return (ENOBUFS);
        }

        protrate = ieee80211_ctl_rate(ic->ic_rt, rate);
        wflags = MTW_TX_FRAG;
        xflags = 0;
        if (prot == IEEE80211_PROT_RTSCTS)
                xflags |= MTW_TX_ACK;

        data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh);
        STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next);
        sc->sc_epq[0].tx_nfree--;

        txd = (struct mtw_txd *)&data->desc;
        txd->flags = RT2860_TX_QSEL_EDCA;
        txwi = (struct mtw_txwi *)(txd + 1);
        txwi->wcid = 0xff;
        txwi->flags = wflags;
        txwi->xflags = xflags;
        txwi->txop = 0; /* clear leftover garbage bits */

        data->m = mprot;
        data->ni = ieee80211_ref_node(ni);

        /* XXX TODO: methodize with MCS rates */
        for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++)
                if (rt2860_rates[ridx].rate == protrate)
                        break;
        data->ridx = ridx;

        mtw_set_tx_desc(sc, data);
        MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "sending prot len=%u rate=%u\n",
            m->m_pkthdr.len, rate);

        STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next);

        usbd_transfer_start(sc->sc_xfer[0]);

        return (0);
}

static int
mtw_tx_param(struct mtw_softc *sc, struct mbuf *m, struct ieee80211_node *ni,
    const struct ieee80211_bpf_params *params)
{
        struct ieee80211com *ic = ni->ni_ic;
        struct mtw_tx_data *data;
        struct mtw_txd *txd;
        struct mtw_txwi *txwi;
        uint8_t ridx;
        uint8_t rate;
        uint8_t opflags = 0;
        uint8_t xflags = 0;
        int error;

        MTW_LOCK_ASSERT(sc, MA_OWNED);

        KASSERT(params != NULL, ("no raw xmit params"));

        rate = params->ibp_rate0;
        if (!ieee80211_isratevalid(ic->ic_rt, rate)) {
                /* let caller free mbuf */
                return (EINVAL);
        }

        if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
                xflags |= MTW_TX_ACK;
        if (params->ibp_flags & (IEEE80211_BPF_RTS | IEEE80211_BPF_CTS)) {
                error = mtw_sendprot(sc, m, ni,
                    params->ibp_flags & IEEE80211_BPF_RTS ?
                        IEEE80211_PROT_RTSCTS :
                        IEEE80211_PROT_CTSONLY,
                    rate);
                if (error) {
                        device_printf(sc->sc_dev, "%s:%d %d\n", __FILE__,
                            __LINE__, error);
                        return (error);
                }
                opflags |=  MTW_TX_TXOP_SIFS;
        }

        if (sc->sc_epq[0].tx_nfree == 0) {
                /* let caller free mbuf */
                MTW_DPRINTF(sc, MTW_DEBUG_XMIT,
                    "sending raw frame, but tx ring is full\n");
                return (EIO);
        }
        data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh);
        STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next);
        sc->sc_epq[0].tx_nfree--;

        txd = (struct mtw_txd *)&data->desc;
        txd->flags = htole16(
            MTW_TXD_DATA | MTW_TXD_80211 | MTW_TXD_WLAN | MTW_TXD_QSEL_EDCA);
        // txd->flags = htole16(MTW_TXD_QSEL_EDCA);
        txwi = (struct mtw_txwi *)(txd + 1);
        txwi->wcid = 0xff;
        txwi->xflags = xflags;
        txwi->txop = opflags;
        txwi->flags = 0; /* clear leftover garbage bits */

        data->m = m;
        data->ni = ni;
        /* XXX TODO: methodize with MCS rates */
        for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++)
                if (rt2860_rates[ridx].rate == rate)
                        break;
        data->ridx = ridx;

        mtw_set_tx_desc(sc, data);

        MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "sending raw frame len=%u rate=%u\n",
            m->m_pkthdr.len, rate);

        STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next);

        usbd_transfer_start(sc->sc_xfer[MTW_BULK_RAW_TX]);

        return (0);
}

static int
mtw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
    const struct ieee80211_bpf_params *params)
{
        struct mtw_softc *sc = ni->ni_ic->ic_softc;
        int error = 0;
        MTW_LOCK(sc);
        /* prevent management frames from being sent if we're not ready */
        if (!(sc->sc_flags & MTW_RUNNING)) {
                error = ENETDOWN;
                goto done;
        }

        if (params == NULL) {
                /* tx mgt packet */
                if ((error = mtw_tx_mgt(sc, m, ni)) != 0) {
                        MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "mgt tx failed\n");
                        goto done;
                }
        } else {
                /* tx raw packet with param */
                if ((error = mtw_tx_param(sc, m, ni, params)) != 0) {
                        MTW_DPRINTF(sc, MTW_DEBUG_XMIT,
                            "tx with param failed\n");
                        goto done;
                }
        }

done:

        MTW_UNLOCK(sc);

        if (error != 0) {
                if (m != NULL)
                        m_freem(m);
        }

        return (error);
}

static int
mtw_transmit(struct ieee80211com *ic, struct mbuf *m)
{
        struct mtw_softc *sc = ic->ic_softc;
        int error;
        MTW_LOCK(sc);
        if ((sc->sc_flags & MTW_RUNNING) == 0) {
                MTW_UNLOCK(sc);
                return (ENXIO);
        }
        error = mbufq_enqueue(&sc->sc_snd, m);
        if (error) {
                MTW_UNLOCK(sc);
                return (error);
        }
        mtw_start(sc);
        MTW_UNLOCK(sc);

        return (0);
}

static void
mtw_start(struct mtw_softc *sc)
{
        struct ieee80211_node *ni;
        struct mbuf *m;

        MTW_LOCK_ASSERT(sc, MA_OWNED);

        if ((sc->sc_flags & MTW_RUNNING) == 0) {

                return;
        }
        while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
                ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
                if (mtw_tx(sc, m, ni) != 0) {
                        mbufq_prepend(&sc->sc_snd, m);
                        break;
                }
        }
}

static void
mtw_parent(struct ieee80211com *ic)
{

        struct mtw_softc *sc = ic->ic_softc;

        MTW_LOCK(sc);
        if (sc->sc_detached) {
                MTW_UNLOCK(sc);
                return;
        }

        if (!(sc->sc_flags & MTW_RUNNING) && ic->ic_nrunning > 0) {
                mtw_init_locked(sc);
                MTW_UNLOCK(sc);
                ieee80211_start_all(ic);
                return;
        }
        if (!(sc->sc_flags & MTW_RUNNING) && ic->ic_nrunning > 0) {
                mtw_update_promisc_locked(sc);
                MTW_UNLOCK(sc);
                return;
        }
        if ((sc->sc_flags & MTW_RUNNING) && sc->rvp_cnt <= 1 &&
            ic->ic_nrunning == 0) {
                mtw_stop(sc);
                MTW_UNLOCK(sc);
                return;
        }
        return;
}

static void
mt7601_set_agc(struct mtw_softc *sc, uint8_t agc)
{
        uint8_t bbp;

        mtw_bbp_write(sc, 66, agc);
        mtw_bbp_write(sc, 195, 0x87);
        bbp = (agc & 0xf0) | 0x08;
        mtw_bbp_write(sc, 196, bbp);
}

static int
mtw_mcu_calibrate(struct mtw_softc *sc, int func, uint32_t val)
{
        struct mtw_mcu_cmd_8 cmd;

        cmd.func = htole32(func);
        cmd.val = htole32(val);
        return (mtw_mcu_cmd(sc, 31, &cmd, sizeof(struct mtw_mcu_cmd_8)));
}

static int
mtw_rf_write(struct mtw_softc *sc, uint8_t bank, uint8_t reg, uint8_t val)
{
        uint32_t tmp;
        int error, ntries, shift;

        for (ntries = 0; ntries < 10; ntries++) {
                if ((error = mtw_read(sc, MTW_RF_CSR, &tmp)) != 0)
                        return (error);
                if (!(tmp & MTW_RF_CSR_KICK))
                        break;
        }
        if (ntries == 10)
                return (ETIMEDOUT);

        if (sc->asic_ver == 0x7601)
                shift = MT7601_BANK_SHIFT;
        else
                shift = MT7610_BANK_SHIFT;

        tmp = MTW_RF_CSR_WRITE | MTW_RF_CSR_KICK | (bank & 0xf) << shift |
            reg << 8 | val;
        return (mtw_write(sc, MTW_RF_CSR, tmp));
}

void
mtw_select_chan_group(struct mtw_softc *sc, int group)
{
        uint32_t tmp;
        uint8_t bbp;

        /* Tx band 20MHz 2G */
        mtw_read(sc, MTW_TX_BAND_CFG, &tmp);
        tmp &= ~(
            MTW_TX_BAND_SEL_2G | MTW_TX_BAND_SEL_5G | MTW_TX_BAND_UPPER_40M);
        tmp |= (group == 0) ? MTW_TX_BAND_SEL_2G : MTW_TX_BAND_SEL_5G;
        mtw_write(sc, MTW_TX_BAND_CFG, tmp);

        /* select 20 MHz bandwidth */
        mtw_bbp_read(sc, 4, &bbp);
        bbp &= ~0x18;
        bbp |= 0x40;
        mtw_bbp_write(sc, 4, bbp);

        /* calibrate BBP */
        mtw_bbp_write(sc, 69, 0x12);
        mtw_bbp_write(sc, 91, 0x07);
        mtw_bbp_write(sc, 195, 0x23);
        mtw_bbp_write(sc, 196, 0x17);
        mtw_bbp_write(sc, 195, 0x24);
        mtw_bbp_write(sc, 196, 0x06);
        mtw_bbp_write(sc, 195, 0x81);
        mtw_bbp_write(sc, 196, 0x12);
        mtw_bbp_write(sc, 195, 0x83);
        mtw_bbp_write(sc, 196, 0x17);
        mtw_rf_write(sc, 5, 8, 0x00);
        // mtw_mcu_calibrate(sc, 0x6, 0x10001);

        /* set initial AGC value */
        mt7601_set_agc(sc, 0x14);
}

static int
mtw_rf_read(struct mtw_softc *sc, uint8_t bank, uint8_t reg, uint8_t *val)
{
        uint32_t tmp;
        int error, ntries, shift;

        for (ntries = 0; ntries < 100; ntries++) {
                if ((error = mtw_read(sc, MTW_RF_CSR, &tmp)) != 0)
                        return (error);
                if (!(tmp & MTW_RF_CSR_KICK))
                        break;
        }
        if (ntries == 100)
                return (ETIMEDOUT);

        if (sc->asic_ver == 0x7601)
                shift = MT7601_BANK_SHIFT;
        else
                shift = MT7610_BANK_SHIFT;

        tmp = MTW_RF_CSR_KICK | (bank & 0xf) << shift | reg << 8;
        if ((error = mtw_write(sc, MTW_RF_CSR, tmp)) != 0)
                return (error);

        for (ntries = 0; ntries < 100; ntries++) {
                if ((error = mtw_read(sc, MTW_RF_CSR, &tmp)) != 0)
                        return (error);
                if (!(tmp & MTW_RF_CSR_KICK))
                        break;
        }
        if (ntries == 100)
                return (ETIMEDOUT);

        *val = tmp & 0xff;
        return (0);
}
static void
mt7601_set_chan(struct mtw_softc *sc, u_int chan)
{
        uint32_t tmp;
        uint8_t bbp, rf, txpow1;
        int i;
        /* find the settings for this channel */
        for (i = 0; mt7601_rf_chan[i].chan != chan; i++)
                ;

        mtw_rf_write(sc, 0, 17, mt7601_rf_chan[i].r17);
        mtw_rf_write(sc, 0, 18, mt7601_rf_chan[i].r18);
        mtw_rf_write(sc, 0, 19, mt7601_rf_chan[i].r19);
        mtw_rf_write(sc, 0, 20, mt7601_rf_chan[i].r20);

        /* use Tx power values from EEPROM */
        txpow1 = sc->txpow1[i];

        /* Tx automatic level control */
        mtw_read(sc, MTW_TX_ALC_CFG0, &tmp);
        tmp &= ~0x3f3f;
        tmp |= (txpow1 & 0x3f);
        mtw_write(sc, MTW_TX_ALC_CFG0, tmp);

        /* LNA */
        mtw_bbp_write(sc, 62, 0x37 - sc->lna[0]);
        mtw_bbp_write(sc, 63, 0x37 - sc->lna[0]);
        mtw_bbp_write(sc, 64, 0x37 - sc->lna[0]);

        /* VCO calibration */
        mtw_rf_write(sc, 0, 4, 0x0a);
        mtw_rf_write(sc, 0, 5, 0x20);
        mtw_rf_read(sc, 0, 4, &rf);
        mtw_rf_write(sc, 0, 4, rf | 0x80);

        /* select 20 MHz bandwidth */
        mtw_bbp_read(sc, 4, &bbp);
        bbp &= ~0x18;
        bbp |= 0x40;
        mtw_bbp_write(sc, 4, bbp);
        mtw_bbp_write(sc, 178, 0xff);
}

static int
mtw_set_chan(struct mtw_softc *sc, struct ieee80211_channel *c)
{
        struct ieee80211com *ic = &sc->sc_ic;
        u_int chan, group;

        chan = ieee80211_chan2ieee(ic, c);
        if (chan == 0 || chan == IEEE80211_CHAN_ANY)
                return (EINVAL);

        /* determine channel group */
        if (chan <= 14)
                group = 0;
        else if (chan <= 64)
                group = 1;
        else if (chan <= 128)
                group = 2;
        else
                group = 3;

        if (group != sc->sc_chan_group || !sc->sc_bw_calibrated)
                mtw_select_chan_group(sc, group);

        sc->sc_chan_group = group;

        /* chipset specific */
        if (sc->asic_ver == 0x7601)
                mt7601_set_chan(sc, chan);

        DELAY(1000);
        return (0);
}

static void
mtw_set_channel(struct ieee80211com *ic)
{
        struct mtw_softc *sc = ic->ic_softc;

        MTW_LOCK(sc);
        mtw_set_chan(sc, ic->ic_curchan);
        MTW_UNLOCK(sc);

        return;
}

static void
mtw_getradiocaps(struct ieee80211com *ic, int maxchans, int *nchans,
    struct ieee80211_channel chans[])
{
        // struct mtw_softc *sc = ic->ic_softc;
        uint8_t bands[IEEE80211_MODE_BYTES];

        memset(bands, 0, sizeof(bands));
        setbit(bands, IEEE80211_MODE_11B);
        setbit(bands, IEEE80211_MODE_11G);
        setbit(bands, IEEE80211_MODE_11NG);

        /* Note: for now, only support HT20 channels */
        ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, bands, 0);
}

static void
mtw_scan_start(struct ieee80211com *ic)
{
        struct mtw_softc *sc = ic->ic_softc;
        MTW_LOCK(sc);
        /* abort TSF synchronization */
        mtw_abort_tsf_sync(sc);
        mtw_set_bssid(sc, ieee80211broadcastaddr);

        MTW_UNLOCK(sc);

        return;
}

static void
mtw_scan_end(struct ieee80211com *ic)
{
        struct mtw_softc *sc = ic->ic_softc;

        MTW_LOCK(sc);

        mtw_enable_tsf_sync(sc);
        mtw_set_bssid(sc, sc->sc_bssid);

        MTW_UNLOCK(sc);

        return;
}

/*
 * Could be called from ieee80211_node_timeout()
 * (non-sleepable thread)
 */
static void
mtw_update_beacon(struct ieee80211vap *vap, int item)
{
        struct ieee80211com *ic = vap->iv_ic;
        struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off;
        struct ieee80211_node *ni = vap->iv_bss;
        struct mtw_softc *sc = ic->ic_softc;
        struct mtw_vap *rvp = MTW_VAP(vap);
        int mcast = 0;
        uint32_t i;

        switch (item) {
        case IEEE80211_BEACON_ERP:
                mtw_updateslot(ic);
                break;
        case IEEE80211_BEACON_HTINFO:
                mtw_updateprot(ic);
                break;
        case IEEE80211_BEACON_TIM:
                mcast = 1; /*TODO*/
                break;
        default:
                break;
        }

        setbit(bo->bo_flags, item);
        if (rvp->beacon_mbuf == NULL) {
                rvp->beacon_mbuf = ieee80211_beacon_alloc(ni);
                if (rvp->beacon_mbuf == NULL)
                        return;
        }
        ieee80211_beacon_update(ni, rvp->beacon_mbuf, mcast);

        i = MTW_CMDQ_GET(&sc->cmdq_store);
        MTW_DPRINTF(sc, MTW_DEBUG_BEACON, "cmdq_store=%d\n", i);
        sc->cmdq[i].func = mtw_update_beacon_cb;
        sc->cmdq[i].arg0 = vap;
        ieee80211_runtask(ic, &sc->cmdq_task);

        return;
}

static void
mtw_update_beacon_cb(void *arg)
{

        struct ieee80211vap *vap = arg;
        struct ieee80211_node *ni = vap->iv_bss;
        struct mtw_vap *rvp = MTW_VAP(vap);
        struct ieee80211com *ic = vap->iv_ic;
        struct mtw_softc *sc = ic->ic_softc;
        struct mtw_txwi txwi;
        struct mbuf *m;
        uint16_t txwisize;
        uint8_t ridx;
        if (ni->ni_chan == IEEE80211_CHAN_ANYC)
                return;
        if (ic->ic_bsschan == IEEE80211_CHAN_ANYC)
                return;

        /*
         * No need to call ieee80211_beacon_update(), mtw_update_beacon()
         * is taking care of appropriate calls.
         */
        if (rvp->beacon_mbuf == NULL) {
                rvp->beacon_mbuf = ieee80211_beacon_alloc(ni);
                if (rvp->beacon_mbuf == NULL)
                        return;
        }
        m = rvp->beacon_mbuf;

        memset(&txwi, 0, sizeof(txwi));
        txwi.wcid = 0xff;
        txwi.len = htole16(m->m_pkthdr.len);

        /* send beacons at the lowest available rate */
        ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? MTW_RIDX_OFDM6 :
                                                        MTW_RIDX_CCK1;
        txwi.phy = htole16(rt2860_rates[ridx].mcs);
        if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM)
                txwi.phy |= htole16(MTW_PHY_OFDM);
        txwi.txop = MTW_TX_TXOP_HT;
        txwi.flags = MTW_TX_TS;
        txwi.xflags = MTW_TX_NSEQ;

        txwisize =  sizeof(txwi);
        mtw_write_region_1(sc, MTW_BCN_BASE, (uint8_t *)&txwi, txwisize);
        mtw_write_region_1(sc, MTW_BCN_BASE + txwisize, mtod(m, uint8_t *),
            (m->m_pkthdr.len + 1) & ~1);
}

static void
mtw_updateprot(struct ieee80211com *ic)
{
        struct mtw_softc *sc = ic->ic_softc;
        uint32_t i;

        i = MTW_CMDQ_GET(&sc->cmdq_store);
        MTW_DPRINTF(sc, MTW_DEBUG_BEACON, "test cmdq_store=%d\n", i);
        sc->cmdq[i].func = mtw_updateprot_cb;
        sc->cmdq[i].arg0 = ic;
        ieee80211_runtask(ic, &sc->cmdq_task);
}

static void
mtw_updateprot_cb(void *arg)
{

        struct ieee80211com *ic = arg;
        struct mtw_softc *sc = ic->ic_softc;
        uint32_t tmp;

        tmp = RT2860_RTSTH_EN | RT2860_PROT_NAV_SHORT | RT2860_TXOP_ALLOW_ALL;
        /* setup protection frame rate (MCS code) */
        tmp |= (ic->ic_curmode == IEEE80211_MODE_11A) ?
            rt2860_rates[MTW_RIDX_OFDM6].mcs | MTW_PHY_OFDM :
            rt2860_rates[MTW_RIDX_CCK11].mcs;

        /* CCK frames don't require protection */
        mtw_write(sc, MTW_CCK_PROT_CFG, tmp);
        if (ic->ic_flags & IEEE80211_F_USEPROT) {
                if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
                        tmp |= RT2860_PROT_CTRL_RTS_CTS;
                else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
                        tmp |= RT2860_PROT_CTRL_CTS;
        }
        mtw_write(sc, MTW_OFDM_PROT_CFG, tmp);
}

static void
mtw_usb_timeout_cb(void *arg)
{
        struct ieee80211vap *vap = arg;
        struct mtw_softc *sc = vap->iv_ic->ic_softc;

        MTW_LOCK_ASSERT(sc, MA_OWNED);

        if (vap->iv_state == IEEE80211_S_SCAN) {
                MTW_DPRINTF(sc, MTW_DEBUG_USB | MTW_DEBUG_STATE,
                    "timeout caused by scan\n");
                /* cancel bgscan */
                ieee80211_cancel_scan(vap);
        } else {
                MTW_DPRINTF(sc, MTW_DEBUG_USB | MTW_DEBUG_STATE,
                    "timeout by unknown cause\n");
        }
}
static int mtw_reset(struct mtw_softc *sc)
{

        usb_device_request_t req;
        uint16_t tmp;
        uint16_t actlen;

        req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
        req.bRequest = MTW_RESET;
        USETW(req.wValue, 1);
        USETW(req.wIndex, 0);
        USETW(req.wLength, 0);
        return (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx,
                                 &req, &tmp, 0, &actlen, 1000));

}


static void
mtw_update_promisc_locked(struct mtw_softc *sc)
{

        uint32_t tmp;

        mtw_read(sc, MTW_RX_FILTR_CFG, &tmp);

        tmp |= MTW_DROP_UC_NOME;
        if (sc->sc_ic.ic_promisc > 0)
                tmp &= ~MTW_DROP_UC_NOME;

        mtw_write(sc, MTW_RX_FILTR_CFG, tmp);

        MTW_DPRINTF(sc, MTW_DEBUG_RECV, "%s promiscuous mode\n",
            (sc->sc_ic.ic_promisc > 0) ? "entering" : "leaving");
}

static void
mtw_update_promisc(struct ieee80211com *ic)
{
        struct mtw_softc *sc = ic->ic_softc;

        if ((sc->sc_flags & MTW_RUNNING) == 0)
                return;

        MTW_LOCK(sc);
        mtw_update_promisc_locked(sc);
        MTW_UNLOCK(sc);
}

static void
mtw_enable_tsf_sync(struct mtw_softc *sc)
{
        struct ieee80211com *ic = &sc->sc_ic;
        struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
        uint32_t tmp;
        int error;
        mtw_read(sc, MTW_BCN_TIME_CFG, &tmp);
        tmp &= ~0x1fffff;
        tmp |= vap->iv_bss->ni_intval * 16;
        tmp |= MTW_TSF_TIMER_EN | MTW_TBTT_TIMER_EN;

        /* local TSF is always updated with remote TSF on beacon reception */
        tmp |= 1 << MTW_TSF_SYNC_MODE_SHIFT;
        error = mtw_write(sc, MTW_BCN_TIME_CFG, tmp);
        if (error != 0) {
                device_printf(sc->sc_dev, "enable_tsf_sync failed error:%d\n",
                    error);
        }
        return;
}

static void
mtw_enable_mrr(struct mtw_softc *sc)
{
#define CCK(mcs) (mcs)

#define OFDM(mcs) (1 << 3 | (mcs))
        mtw_write(sc, MTW_LG_FBK_CFG0,
            OFDM(6) << 28 |     /* 54->48 */
                OFDM(5) << 24 | /* 48->36 */
                OFDM(4) << 20 | /* 36->24 */
                OFDM(3) << 16 | /* 24->18 */
                OFDM(2) << 12 | /* 18->12 */
                OFDM(1) << 8 |  /* 12-> 9 */
                OFDM(0) << 4 |  /*  9-> 6 */
                OFDM(0));       /*  6-> 6 */

        mtw_write(sc, MTW_LG_FBK_CFG1,
            CCK(2) << 12 |    /* 11->5.5 */
                CCK(1) << 8 | /* 5.5-> 2 */
                CCK(0) << 4 | /*   2-> 1 */
                CCK(0));      /*   1-> 1 */
#undef OFDM
#undef CCK
}

static void
mtw_set_txpreamble(struct mtw_softc *sc)
{
        struct ieee80211com *ic = &sc->sc_ic;
        uint32_t tmp;

        mtw_read(sc, MTW_AUTO_RSP_CFG, &tmp);
        if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
                tmp |= MTW_CCK_SHORT_EN;
        else
                tmp &= ~MTW_CCK_SHORT_EN;
        mtw_write(sc, MTW_AUTO_RSP_CFG, tmp);
}

static void
mtw_set_basicrates(struct mtw_softc *sc)
{
        struct ieee80211com *ic = &sc->sc_ic;

        /* set basic rates mask */
        if (ic->ic_curmode == IEEE80211_MODE_11B)
                mtw_write(sc, MTW_LEGACY_BASIC_RATE, 0x003);
        else if (ic->ic_curmode == IEEE80211_MODE_11A)
                mtw_write(sc, MTW_LEGACY_BASIC_RATE, 0x150);
        else /* 11g */
                mtw_write(sc, MTW_LEGACY_BASIC_RATE, 0x17f);
}

static void
mtw_set_bssid(struct mtw_softc *sc, const uint8_t *bssid)
{
        mtw_write(sc, MTW_MAC_BSSID_DW0,
            bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24);
        mtw_write(sc, MTW_MAC_BSSID_DW1, bssid[4] | bssid[5] << 8);
}

static void
mtw_set_macaddr(struct mtw_softc *sc, const uint8_t *addr)
{
        mtw_write(sc, MTW_MAC_ADDR_DW0,
            addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24);
        mtw_write(sc, MTW_MAC_ADDR_DW1, addr[4] | addr[5] << 8 | 0xff << 16);
}

static void
mtw_updateslot(struct ieee80211com *ic)
{

        struct mtw_softc *sc = ic->ic_softc;
        uint32_t i;

        i = MTW_CMDQ_GET(&sc->cmdq_store);
        MTW_DPRINTF(sc, MTW_DEBUG_BEACON, "cmdq_store=%d\n", i);
        sc->cmdq[i].func = mtw_updateslot_cb;
        sc->cmdq[i].arg0 = ic;
        ieee80211_runtask(ic, &sc->cmdq_task);

        return;
}

/* ARGSUSED */
static void
mtw_updateslot_cb(void *arg)
{
  struct ieee80211com *ic = arg;
        struct mtw_softc *sc = ic->ic_softc;
        uint32_t tmp;
        mtw_read(sc, MTW_BKOFF_SLOT_CFG, &tmp);
        tmp &= ~0xff;
        tmp |= IEEE80211_GET_SLOTTIME(ic);
        mtw_write(sc, MTW_BKOFF_SLOT_CFG, tmp);
}

static void
mtw_update_mcast(struct ieee80211com *ic)
{
}

static int8_t
mtw_rssi2dbm(struct mtw_softc *sc, uint8_t rssi, uint8_t rxchain)
{
        struct ieee80211com *ic = &sc->sc_ic;
        struct ieee80211_channel *c = ic->ic_curchan;
        int delta;

        if (IEEE80211_IS_CHAN_5GHZ(c)) {
                u_int chan = ieee80211_chan2ieee(ic, c);
                delta = sc->rssi_5ghz[rxchain];

                /* determine channel group */
                if (chan <= 64)
                        delta -= sc->lna[1];
                else if (chan <= 128)
                        delta -= sc->lna[2];
                else
                        delta -= sc->lna[3];
        } else
                delta = sc->rssi_2ghz[rxchain] - sc->lna[0];

        return (-12 - delta - rssi);
}
static int
mt7601_bbp_init(struct mtw_softc *sc)
{
        uint8_t bbp;
        int i, error, ntries;

        /* wait for BBP to wake up */
        for (ntries = 0; ntries < 20; ntries++) {
                if ((error = mtw_bbp_read(sc, 0, &bbp)) != 0)
                        return (error);
                if (bbp != 0 && bbp != 0xff)
                        break;
        }

        if (ntries == 20)
                return (ETIMEDOUT);

        mtw_bbp_read(sc, 3, &bbp);
        mtw_bbp_write(sc, 3, 0);
        mtw_bbp_read(sc, 105, &bbp);
        mtw_bbp_write(sc, 105, 0);

        /* initialize BBP registers to default values */
        for (i = 0; i < nitems(mt7601_def_bbp); i++) {
                if ((error = mtw_bbp_write(sc, mt7601_def_bbp[i].reg,
                         mt7601_def_bbp[i].val)) != 0)
                        return (error);
        }

        sc->sc_bw_calibrated = 0;

        return (0);
}

static int
mt7601_rf_init(struct mtw_softc *sc)
{
        int i, error;

        /* RF bank 0 */
        for (i = 0; i < nitems(mt7601_rf_bank0); i++) {
                error = mtw_rf_write(sc, 0, mt7601_rf_bank0[i].reg,
                    mt7601_rf_bank0[i].val);
                if (error != 0)
                        return (error);
        }
        /* RF bank 4 */
        for (i = 0; i < nitems(mt7601_rf_bank4); i++) {
                error = mtw_rf_write(sc, 4, mt7601_rf_bank4[i].reg,
                    mt7601_rf_bank4[i].val);
                if (error != 0)
                        return (error);
        }
        /* RF bank 5 */
        for (i = 0; i < nitems(mt7601_rf_bank5); i++) {
                error = mtw_rf_write(sc, 5, mt7601_rf_bank5[i].reg,
                    mt7601_rf_bank5[i].val);
                if (error != 0)
                        return (error);
        }
        return (0);
}

static int
mtw_txrx_enable(struct mtw_softc *sc)
{
        struct ieee80211com *ic = &sc->sc_ic;
        uint32_t tmp;
        int error, ntries;
        mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_MAC_TX_EN);
        for (ntries = 0; ntries < 200; ntries++) {
                if ((error = mtw_read(sc, MTW_WPDMA_GLO_CFG, &tmp)) != 0) {
                        return (error);
                }
                if ((tmp & (MTW_TX_DMA_BUSY | MTW_RX_DMA_BUSY)) == 0)
                        break;
                mtw_delay(sc, 50);
        }
        if (ntries == 200) {
                return (ETIMEDOUT);
        }

        DELAY(50);

        tmp |= MTW_RX_DMA_EN | MTW_TX_DMA_EN | MTW_TX_WB_DDONE;
        mtw_write(sc, MTW_WPDMA_GLO_CFG, tmp);

        /* enable Rx bulk aggregation (set timeout and limit) */
        tmp = MTW_USB_TX_EN | MTW_USB_RX_EN | MTW_USB_RX_AGG_EN |
            MTW_USB_RX_AGG_TO(128) | MTW_USB_RX_AGG_LMT(2);
        mtw_write(sc, MTW_USB_DMA_CFG, tmp);

        /* set Rx filter */
        tmp = MTW_DROP_CRC_ERR | MTW_DROP_PHY_ERR;
        if (ic->ic_opmode != IEEE80211_M_MONITOR) {
                tmp |= MTW_DROP_UC_NOME | MTW_DROP_DUPL | MTW_DROP_CTS |
                    MTW_DROP_BA | MTW_DROP_ACK | MTW_DROP_VER_ERR |
                    MTW_DROP_CTRL_RSV | MTW_DROP_CFACK | MTW_DROP_CFEND;
                if (ic->ic_opmode == IEEE80211_M_STA)
                        tmp |= MTW_DROP_RTS | MTW_DROP_PSPOLL;
        }
        mtw_write(sc, MTW_RX_FILTR_CFG, tmp);

        mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_MAC_RX_EN | MTW_MAC_TX_EN);
        return (0);
}
static int
mt7601_rxdc_cal(struct mtw_softc *sc)
{
        uint32_t tmp;
        uint8_t bbp;
        int ntries;

        mtw_read(sc, MTW_MAC_SYS_CTRL, &tmp);
        mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_MAC_RX_EN);
        mtw_bbp_write(sc, 158, 0x8d);
        mtw_bbp_write(sc, 159, 0xfc);
        mtw_bbp_write(sc, 158, 0x8c);
        mtw_bbp_write(sc, 159, 0x4c);

        for (ntries = 0; ntries < 20; ntries++) {
                DELAY(300);
                mtw_bbp_write(sc, 158, 0x8c);
                mtw_bbp_read(sc, 159, &bbp);
                if (bbp == 0x0c)
                        break;
        }

        if (ntries == 20)
                return (ETIMEDOUT);

        mtw_write(sc, MTW_MAC_SYS_CTRL, 0);
        mtw_bbp_write(sc, 158, 0x8d);
        mtw_bbp_write(sc, 159, 0xe0);
        mtw_write(sc, MTW_MAC_SYS_CTRL, tmp);
        return (0);
}

static int
mt7601_r49_read(struct mtw_softc *sc, uint8_t flag, int8_t *val)
{
        uint8_t bbp;

        mtw_bbp_read(sc, 47, &bbp);
        bbp = 0x90;
        mtw_bbp_write(sc, 47, bbp);
        bbp &= ~0x0f;
        bbp |= flag;
        mtw_bbp_write(sc, 47, bbp);
        return (mtw_bbp_read(sc, 49, val));
}

static int
mt7601_rf_temperature(struct mtw_softc *sc, int8_t *val)
{
        uint32_t rfb, rfs;
        uint8_t bbp;
        int ntries;

        mtw_read(sc, MTW_RF_BYPASS0, &rfb);
        mtw_read(sc, MTW_RF_SETTING0, &rfs);
        mtw_write(sc, MTW_RF_BYPASS0, 0);
        mtw_write(sc, MTW_RF_SETTING0, 0x10);
        mtw_write(sc, MTW_RF_BYPASS0, 0x10);

        mtw_bbp_read(sc, 47, &bbp);
        bbp &= ~0x7f;
        bbp |= 0x10;
        mtw_bbp_write(sc, 47, bbp);

        mtw_bbp_write(sc, 22, 0x40);

        for (ntries = 0; ntries < 10; ntries++) {
                mtw_bbp_read(sc, 47, &bbp);
                if ((bbp & 0x10) == 0)
                        break;
        }
        if (ntries == 10)
                return (ETIMEDOUT);

        mt7601_r49_read(sc, MT7601_R47_TEMP, val);

        mtw_bbp_write(sc, 22, 0);

        mtw_bbp_read(sc, 21, &bbp);
        bbp |= 0x02;
        mtw_bbp_write(sc, 21, bbp);
        bbp &= ~0x02;
        mtw_bbp_write(sc, 21, bbp);

        mtw_write(sc, MTW_RF_BYPASS0, 0);
        mtw_write(sc, MTW_RF_SETTING0, rfs);
        mtw_write(sc, MTW_RF_BYPASS0, rfb);
        return (0);
}

static int
mt7601_rf_setup(struct mtw_softc *sc)
{
        uint32_t tmp;
        uint8_t rf;
        int error;

        if (sc->sc_rf_calibrated)
                return (0);

        /* init RF registers */
        if ((error = mt7601_rf_init(sc)) != 0)
                return (error);

        /* init frequency offset */
        mtw_rf_write(sc, 0, 12, sc->rf_freq_offset);
        mtw_rf_read(sc, 0, 12, &rf);

        /* read temperature */
        mt7601_rf_temperature(sc, &rf);
        sc->bbp_temp = rf;
        device_printf(sc->sc_dev, "BBP temp 0x%x\n", rf);

        mtw_rf_read(sc, 0, 7, &rf);
        if ((error = mtw_mcu_calibrate(sc, 0x1, 0)) != 0)
                return (error);
        mtw_delay(sc, 100);
        mtw_rf_read(sc, 0, 7, &rf);

        /* Calibrate VCO RF 0/4 */
        mtw_rf_write(sc, 0, 4, 0x0a);
        mtw_rf_write(sc, 0, 4, 0x20);
        mtw_rf_read(sc, 0, 4, &rf);
        mtw_rf_write(sc, 0, 4, rf | 0x80);

        if ((error = mtw_mcu_calibrate(sc, 0x9, 0)) != 0)
                return (error);
        if ((error = mt7601_rxdc_cal(sc)) != 0)
                return (error);
        if ((error = mtw_mcu_calibrate(sc, 0x6, 1)) != 0)
                return (error);
        if ((error = mtw_mcu_calibrate(sc, 0x6, 0)) != 0)
                return (error);
        if ((error = mtw_mcu_calibrate(sc, 0x4, 0)) != 0)
                return (error);
        if ((error = mtw_mcu_calibrate(sc, 0x5, 0)) != 0)
                return (error);

        mtw_read(sc, MTW_LDO_CFG0, &tmp);
        tmp &= ~(1 << 4);
        tmp |= (1 << 2);
        mtw_write(sc, MTW_LDO_CFG0, tmp);

        if ((error = mtw_mcu_calibrate(sc, 0x8, 0)) != 0)
                return (error);
        if ((error = mt7601_rxdc_cal(sc)) != 0)
                return (error);

        sc->sc_rf_calibrated = 1;
        return (0);
}

static void
mtw_set_txrts(struct mtw_softc *sc)
{
        uint32_t tmp;

        /* set RTS threshold */
        mtw_read(sc, MTW_TX_RTS_CFG, &tmp);
        tmp &= ~0xffff00;
        tmp |= 0x1000 << MTW_RTS_THRES_SHIFT;
        mtw_write(sc, MTW_TX_RTS_CFG, tmp);
}
static int
mtw_mcu_radio(struct mtw_softc *sc, int func, uint32_t val)
{
        struct mtw_mcu_cmd_16 cmd;

        cmd.r1 = htole32(func);
        cmd.r2 = htole32(val);
        cmd.r3 = 0;
        cmd.r4 = 0;
        return (mtw_mcu_cmd(sc, 20, &cmd, sizeof(struct mtw_mcu_cmd_16)));
}
static void
mtw_init_locked(struct mtw_softc *sc)
{

        struct ieee80211com *ic = &sc->sc_ic;
        struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
        uint32_t tmp;
        int i, error, ridx, ntries;
        if (ic->ic_nrunning > 1)
                return;
        mtw_stop(sc);

        for (i = 0; i != MTW_EP_QUEUES; i++)
                mtw_setup_tx_list(sc, &sc->sc_epq[i]);

        for (ntries = 0; ntries < 100; ntries++) {
                if ((error = mtw_read(sc, MTW_WPDMA_GLO_CFG, &tmp)) != 0)
                        goto fail;
                if ((tmp & (MTW_TX_DMA_BUSY | MTW_RX_DMA_BUSY)) == 0)
                        break;
                DELAY(1000);
        }
        if (ntries == 100) {
                device_printf(sc->sc_dev, "timeout waiting for DMA engine\n");
                error = ETIMEDOUT;
                goto fail;
        }
        tmp &= 0xff0;
        tmp |= MTW_TX_WB_DDONE;
        mtw_write(sc, MTW_WPDMA_GLO_CFG, tmp);

        mtw_set_leds(sc, MTW_LED_MODE_ON);
        /* reset MAC and baseband */
        mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_BBP_HRST | MTW_MAC_SRST);
        mtw_write(sc, MTW_USB_DMA_CFG, 0);
        mtw_write(sc, MTW_MAC_SYS_CTRL, 0);

        /* init MAC values */
        if (sc->asic_ver == 0x7601) {
                for (i = 0; i < nitems(mt7601_def_mac); i++)
                        mtw_write(sc, mt7601_def_mac[i].reg,
                            mt7601_def_mac[i].val);
        }

        /* wait while MAC is busy */
        for (ntries = 0; ntries < 100; ntries++) {
                if ((error = mtw_read(sc, MTW_MAC_STATUS_REG, &tmp)) != 0)
                        goto fail;
                if (!(tmp & (MTW_RX_STATUS_BUSY | MTW_TX_STATUS_BUSY)))
                        break;
                DELAY(1000);
        }
        if (ntries == 100) {
                error = ETIMEDOUT;
                goto fail;
        }

        /* set MAC address */

        mtw_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr);

        /* clear WCID attribute table */
        mtw_set_region_4(sc, MTW_WCID_ATTR(0), 1, 8 * 32);

        mtw_write(sc, 0x1648, 0x00830083);
        mtw_read(sc, MTW_FCE_L2_STUFF, &tmp);
        tmp &= ~MTW_L2S_WR_MPDU_LEN_EN;
        mtw_write(sc, MTW_FCE_L2_STUFF, tmp);

        /* RTS config */
        mtw_set_txrts(sc);

        /* clear Host to MCU mailbox */
        mtw_write(sc, MTW_BBP_CSR, 0);
        mtw_write(sc, MTW_H2M_MAILBOX, 0);

        /* clear RX WCID search table */
        mtw_set_region_4(sc, MTW_WCID_ENTRY(0), 0xffffffff, 512);

        /* abort TSF synchronization */
        mtw_abort_tsf_sync(sc);

        mtw_read(sc, MTW_US_CYC_CNT, &tmp);
        tmp = (tmp & ~0xff);
        if (sc->asic_ver == 0x7601)
                tmp |= 0x1e;
        mtw_write(sc, MTW_US_CYC_CNT, tmp);

        /* clear shared key table */
        mtw_set_region_4(sc, MTW_SKEY(0, 0), 0, 8 * 32);

        /* clear IV/EIV table */
        mtw_set_region_4(sc, MTW_IVEIV(0), 0, 8 * 32);

        /* clear shared key mode */
        mtw_write(sc, MTW_SKEY_MODE_0_7, 0);
        mtw_write(sc, MTW_SKEY_MODE_8_15, 0);

        /* txop truncation */
        mtw_write(sc, MTW_TXOP_CTRL_CFG, 0x0000583f);

        /* init Tx power for all Tx rates */
        for (ridx = 0; ridx < 5; ridx++) {
                if (sc->txpow20mhz[ridx] == 0xffffffff)
                        continue;
                mtw_write(sc, MTW_TX_PWR_CFG(ridx), sc->txpow20mhz[ridx]);
        }
        mtw_write(sc, MTW_TX_PWR_CFG7, 0);
        mtw_write(sc, MTW_TX_PWR_CFG9, 0);

        mtw_read(sc, MTW_CMB_CTRL, &tmp);
        tmp &= ~(1 << 18 | 1 << 14);
        mtw_write(sc, MTW_CMB_CTRL, tmp);

        /* clear USB DMA */
        mtw_write(sc, MTW_USB_DMA_CFG,
            MTW_USB_TX_EN | MTW_USB_RX_EN | MTW_USB_RX_AGG_EN |
                MTW_USB_TX_CLEAR | MTW_USB_TXOP_HALT | MTW_USB_RX_WL_DROP);
        mtw_delay(sc, 50);
        mtw_read(sc, MTW_USB_DMA_CFG, &tmp);
        tmp &= ~(MTW_USB_TX_CLEAR | MTW_USB_TXOP_HALT | MTW_USB_RX_WL_DROP);
        mtw_write(sc, MTW_USB_DMA_CFG, tmp);

        /* enable radio */
        mtw_mcu_radio(sc, 0x31, 0);

        /* init RF registers */
        if (sc->asic_ver == 0x7601)
                mt7601_rf_init(sc);

        /* init baseband registers */
        if (sc->asic_ver == 0x7601)
                error = mt7601_bbp_init(sc);

        if (error != 0) {
                device_printf(sc->sc_dev, "could not initialize BBP\n");
                goto fail;
        }

        /* setup and calibrate RF */
        error = mt7601_rf_setup(sc);

        if (error != 0) {
                device_printf(sc->sc_dev, "could not initialize RF\n");
                goto fail;
        }

        /* select default channel */
        mtw_set_chan(sc, ic->ic_curchan);

        /* setup initial protection mode */
        mtw_updateprot_cb(ic);

        sc->sc_flags |= MTW_RUNNING;
        sc->cmdq_run = MTW_CMDQ_GO;
        for (i = 0; i != MTW_N_XFER; i++)
                usbd_xfer_set_stall(sc->sc_xfer[i]);

        usbd_transfer_start(sc->sc_xfer[MTW_BULK_RX]);

        error = mtw_txrx_enable(sc);
        if (error != 0) {
                goto fail;
        }

        return;

fail:

        mtw_stop(sc);
        return;
}

static void
mtw_stop(void *arg)
{
        struct mtw_softc *sc = (struct mtw_softc *)arg;
        uint32_t tmp;
        int i, ntries, error;

        MTW_LOCK_ASSERT(sc, MA_OWNED);

        sc->sc_flags &= ~MTW_RUNNING;

        sc->ratectl_run = MTW_RATECTL_OFF;
        sc->cmdq_run = sc->cmdq_key_set;

        MTW_UNLOCK(sc);

        for (i = 0; i < MTW_N_XFER; i++)
                usbd_transfer_drain(sc->sc_xfer[i]);

        MTW_LOCK(sc);

        mtw_drain_mbufq(sc);

        if (sc->rx_m != NULL) {
                m_free(sc->rx_m);
                sc->rx_m = NULL;
        }

        /* Disable Tx/Rx DMA. */
        mtw_read(sc, MTW_WPDMA_GLO_CFG, &tmp);
        tmp &= ~(MTW_RX_DMA_EN | MTW_TX_DMA_EN);
        mtw_write(sc, MTW_WPDMA_GLO_CFG, tmp);
        // mtw_usb_dma_write(sc, 0);

        for (ntries = 0; ntries < 100; ntries++) {
                if (mtw_read(sc, MTW_WPDMA_GLO_CFG, &tmp) != 0)
                        break;
                if ((tmp & (MTW_TX_DMA_BUSY | MTW_RX_DMA_BUSY)) == 0)
                        break;
                DELAY(10);
        }
        if (ntries == 100) {
                device_printf(sc->sc_dev, "timeout waiting for DMA engine\n");
        }

        /* stop MAC Tx/Rx */
        mtw_read(sc, MTW_MAC_SYS_CTRL, &tmp);
        tmp &= ~(MTW_MAC_RX_EN | MTW_MAC_TX_EN);
        mtw_write(sc, MTW_MAC_SYS_CTRL, tmp);

        /* disable RTS retry */
        mtw_read(sc, MTW_TX_RTS_CFG, &tmp);
        tmp &= ~0xff;
        mtw_write(sc, MTW_TX_RTS_CFG, tmp);

        /* US_CYC_CFG */
        mtw_read(sc, MTW_US_CYC_CNT, &tmp);
        tmp = (tmp & ~0xff);
        mtw_write(sc, MTW_US_CYC_CNT, tmp);

        /* stop PBF */
        mtw_read(sc, MTW_PBF_CFG, &tmp);
        tmp &= ~0x3;
        mtw_write(sc, MTW_PBF_CFG, tmp);

        /* wait for pending Tx to complete */
        for (ntries = 0; ntries < 100; ntries++) {
                if ((error = mtw_read(sc, MTW_TXRXQ_PCNT, &tmp)) != 0)
                        break;
                if ((tmp & MTW_TX2Q_PCNT_MASK) == 0)
                        break;
        }

}

static void
mtw_delay(struct mtw_softc *sc, u_int ms)
{
        usb_pause_mtx(mtx_owned(&sc->sc_mtx) ? &sc->sc_mtx : NULL,
            USB_MS_TO_TICKS(ms));
}

static void
mtw_update_chw(struct ieee80211com *ic)
{

        printf("%s: TODO\n", __func__);
}

static int
mtw_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
{

        /* For now, no A-MPDU TX support in the driver */
        return (0);
}

static device_method_t mtw_methods[] = {
        /* Device interface */
        DEVMETHOD(device_probe, mtw_match),
        DEVMETHOD(device_attach, mtw_attach),
        DEVMETHOD(device_detach, mtw_detach), DEVMETHOD_END
};

static driver_t mtw_driver = { .name = "mtw",
        .methods = mtw_methods,
        .size = sizeof(struct mtw_softc) };

DRIVER_MODULE(mtw, uhub, mtw_driver, mtw_driver_loaded, NULL);
MODULE_DEPEND(mtw, wlan, 1, 1, 1);
MODULE_DEPEND(mtw, usb, 1, 1, 1);
MODULE_DEPEND(mtw, firmware, 1, 1, 1);
MODULE_VERSION(mtw, 1);
USB_PNP_HOST_INFO(mtw_devs);