If there's someone who wants to dig into this, but lack a card, I have
one I can send.

Claudio Jeker <cje...@diehard.n-r-g.com> wrote:

> Here is the start of mwx(4) a driver for the MediaTek MT7921 chip which
> shipped for example in Lenovo AMD T14 Gen 2 laptops.
> So far the firmware loading works reliably and also the various DMA ring
> operate. It is possible to send commands and get answers. The problem
> where I got stuck now for a while is that the scan command fails to scan
> more than channel 1 and switching channel puts the device into a broken
> state.
> 
> I kind of hit a wall and hope someone else would like to join and share
> the pain. To enable the driver add 'mwx* at pci?' to your kernel config.
> 
> -- 
> :wq Claudio
> 
> Index: files.pci
> ===================================================================
> RCS file: /cvs/src/sys/dev/pci/files.pci,v
> retrieving revision 1.359
> diff -u -p -r1.359 files.pci
> --- files.pci 31 Mar 2022 21:41:17 -0000      1.359
> +++ files.pci 5 Apr 2022 06:44:51 -0000
> @@ -850,5 +850,10 @@ file     dev/pci/igc_phy.c               igc
>  attach       com at pci with com_pci
>  file dev/pci/com_pci.c               com_pci
>  
> +# MediaTek MT7921E wifi
> +device       mwx: ifnet, wlan, firmload
> +attach       mwx at pci
> +file dev/pci/if_mwx.c                mwx
> +
>  include "dev/pci/files.agp"
>  include "dev/pci/drm/files.drm"
> Index: if_mwx.c
> ===================================================================
> RCS file: if_mwx.c
> diff -N if_mwx.c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ if_mwx.c  9 Dec 2022 17:03:39 -0000
> @@ -0,0 +1,3652 @@
> +/*
> + * Copyright (c) 2022 Claudio Jeker <clau...@openbsd.org>
> + * Copyright (c) 2021 MediaTek Inc.
> + * Copyright (c) 2021 Lorenzo Bianconi <lorenzo.biancon...@gmail.com>
> + * Copyright (c) 2017 Stefan Sperling <s...@openbsd.org>
> + * Copyright (c) 2016 Felix Fietkau <n...@nbd.name>
> + * Copyright (c) 2007-2010 Damien Bergamini <damien.bergam...@free.fr>
> + *
> + * Permission to use, copy, modify, and/or 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.
> + */
> +
> +#include "bpfilter.h"
> +
> +#include <sys/param.h>
> +#include <sys/systm.h>
> +#include <sys/atomic.h>
> +#include <sys/sockio.h>
> +
> +#include <machine/bus.h>
> +#include <machine/intr.h>
> +
> +#include <dev/pci/pcireg.h>
> +#include <dev/pci/pcivar.h>
> +#include <dev/pci/pcidevs.h>
> +
> +#if NBPFILTER > 0
> +#include <net/bpf.h>
> +#endif
> +#include <net/if.h>
> +#include <net/if_dl.h>
> +#include <net/if_media.h>
> +
> +#include <netinet/in.h>
> +#include <netinet/if_ether.h>
> +
> +#include <net80211/ieee80211_var.h>
> +#include <net80211/ieee80211_radiotap.h>
> +
> +#include <dev/pci/if_mwxreg.h>
> +
> +static const struct pci_matchid mwx_devices[] = {
> +     { PCI_VENDOR_MEDIATEK, PCI_PRODUCT_MEDIATEK_MT7921 },
> +     { PCI_VENDOR_MEDIATEK, PCI_PRODUCT_MEDIATEK_MT7921K },
> +};
> +
> +#define MWX_DEBUG    1
> +
> +#define      MT7921_ROM_PATCH        "mt7961_1_2_rom_patch.bin"
> +#define      MT7921_FIRMWARE_WM      "mt7961_1.bin"
> +
> +#if NBPFILTER > 0
> +struct mwx_rx_radiotap_header {
> +     struct ieee80211_radiotap_header        wr_ihdr;
> +     uint64_t                                wr_tsft;
> +     uint8_t                                 wr_flags;
> +     uint8_t                                 wr_rate;
> +     uint16_t                                wr_chan_freq;
> +     uint16_t                                wr_chan_flags;
> +     int8_t                                  wr_dbm_antsignal;
> +     int8_t                                  wr_dbm_antnoise;
> +} __packed;
> +
> +#define      MWX_RX_RADIOTAP_PRESENT                         \
> +     ((1 << IEEE80211_RADIOTAP_TSFT) |               \
> +     (1 << IEEE80211_RADIOTAP_FLAGS) |               \
> +     (1 << IEEE80211_RADIOTAP_RATE) |                \
> +     (1 << IEEE80211_RADIOTAP_CHANNEL) |             \
> +     (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) |       \
> +     (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE))
> +
> +struct mwx_tx_radiotap_header {
> +     struct ieee80211_radiotap_header        wt_ihdr;
> +     uint8_t                                 wt_flags;
> +     uint8_t                                 wt_rate;
> +     uint16_t                                wt_chan_freq;
> +     uint16_t                                wt_chan_flags;
> +} __packed;
> +
> +#define      MWX_TX_RADIOTAP_PRESENT                 \
> +     ((1 << IEEE80211_RADIOTAP_FLAGS) |      \
> +     (1 << IEEE80211_RADIOTAP_RATE) |        \
> +     (1 << IEEE80211_RADIOTAP_CHANNEL))
> +
> +#endif
> +
> +struct mwx_data {
> +     struct mbuf *md_data;
> +     bus_dmamap_t md_map;
> +};
> +
> +struct mwx_queue {
> +     uint32_t                mq_regbase;
> +     u_int                   mq_count;
> +     u_int                   mq_prod;
> +     u_int                   mq_cons;
> +
> +     struct mt76_desc        *mq_desc;
> +     struct mwx_data         *mq_data;
> +
> +     bus_dmamap_t            mq_map;
> +     bus_dma_segment_t       mq_seg;
> +     int                     mq_wakeme;
> +};
> +
> +struct mwx_hw_capa {
> +     int8_t          has_2ghz;
> +     int8_t          has_5ghz;
> +     int8_t          has_6ghz;
> +     uint8_t         antenna_mask;
> +     uint8_t         num_streams;
> +};
> +
> +struct mwx_node {
> +     struct ieee80211_node   ni;
> +     uint16_t                wcid;
> +     uint8_t                 hw_key_idx;     /* encryption key index */
> +     uint8_t                 hw_key_idx2;
> +};
> +
> +struct mwx_vif {
> +     uint8_t                 idx;
> +     uint8_t                 omac_idx;
> +     uint8_t                 band_idx;
> +     uint8_t                 wmm_idx;
> +     uint8_t                 scan_seq_num;
> +};
> +
> +struct mwx_softc {
> +     struct device           sc_dev;
> +     struct ieee80211com     sc_ic;
> +
> +     struct mwx_queue        sc_txq;
> +     struct mwx_queue        sc_txmcuq;
> +     struct mwx_queue        sc_txfwdlq;
> +
> +     struct mwx_queue        sc_rxq;
> +     struct mwx_queue        sc_rxmcuq;
> +     struct mwx_queue        sc_rxfwdlq;
> +
> +     bus_space_tag_t         sc_st;
> +     bus_space_handle_t      sc_memh;
> +     bus_size_t              sc_mems;
> +     bus_dma_tag_t           sc_dmat;
> +     pcitag_t                sc_tag;
> +     pci_chipset_tag_t       sc_pc;
> +     void                    *sc_ih;
> +
> +     int                     (*sc_newstate)(struct ieee80211com *,
> +                                 enum ieee80211_state, int);
> +
> +     struct task             sc_reset_task;
> +     u_int                   sc_flags;
> +#define MWX_FLAG_SCANNING            0x01
> +#define MWX_FLAG_BGSCAN                      0x02
> +     int8_t                  sc_resetting;
> +     int8_t                  sc_fw_loaded;
> +
> +#if NBPFILTER > 0
> +     caddr_t                 sc_drvbpf;
> +     union {
> +             struct mwx_rx_radiotap_header th;
> +             uint8_t pad[IEEE80211_RADIOTAP_HDRLEN];
> +     }                       sc_rxtapu;
> +     int                     sc_rxtap_len;
> +     union {
> +             struct mwx_tx_radiotap_header th;
> +             uint8_t pad[IEEE80211_RADIOTAP_HDRLEN];
> +     }                       sc_txtapu;
> +     int                     sc_txtap_len;
> +#define      sc_rxtap        sc_rxtapu.th
> +#define      sc_txtap        sc_txtapu.th
> +#endif
> +
> +     /* mcu */
> +     uint32_t                sc_mcu_seq;
> +     struct {
> +             struct mbuf     *mcu_m;
> +             uint32_t         mcu_cmd;
> +             uint32_t         mcu_int;
> +     }                       sc_mcu_wait[16];
> +     struct mwx_vif          sc_vif;
> +     uint8_t                 sc_scan_seq_num;
> +
> +     /* phy / hw */
> +     struct mwx_hw_capa      sc_capa;
> +     uint8_t                 sc_lladdr[ETHER_ADDR_LEN];
> +     char                    sc_alpha2[4]; /* regulatory-domain */
> +
> +     /* mac specific */
> +     uint32_t                sc_rxfilter;
> +
> +};
> +
> +const uint8_t mwx_channels_2ghz[] = {
> +     1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
> +};
> +
> +#define MWX_NUM_2GHZ_CHANNELS   nitems(mwx_channels_2ghz)
> +
> +const uint8_t mwx_channels_5ghz[] = {
> +     36, 40, 44, 48, 52, 56, 60, 64,
> +     100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
> +     149, 153, 157, 161, 165, 169, 173
> +};
> +
> +#define MWX_NUM_5GHZ_CHANNELS   nitems(mwx_channels_5ghz)
> +
> +const uint8_t mwx_channels_6ghz[] = {
> +     /* UNII-5 */
> +     1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57,
> +     61, 65, 69, 73, 77, 81, 85, 89, 93,
> +     /* UNII-6 */
> +     97, 101, 105, 109, 113, 117,
> +     /* UNII-7 */
> +     121, 125, 129, 133, 137, 141, 145, 149, 153, 157, 161, 165, 169,
> +     173, 177, 181, 185,
> +     /* UNII-8 */
> +     189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233
> +};
> +
> +#define MWX_NUM_6GHZ_CHANNELS   nitems(mwx_channels_6ghz)
> +
> +#define      DEVNAME(s)      ((s)->sc_dev.dv_xname)
> +#define      DEVDEBUG(x)     ((x)->sc_ic.ic_if.if_flags & IFF_DEBUG)
> +
> +#ifdef MWX_DEBUG
> +#define      DPRINTF(x...)           printf(x)
> +#else
> +#define      DPRINTF(x...)
> +#endif
> +
> +int  mwx_init(struct ifnet *);
> +void mwx_stop(struct ifnet *);
> +void mwx_watchdog(struct ifnet *);
> +void mwx_start(struct ifnet *);
> +int  mwx_ioctl(struct ifnet *, u_long, caddr_t);
> +
> +struct ieee80211_node *mwx_node_alloc(struct ieee80211com *);
> +int  mwx_media_change(struct ifnet *);
> +#if NBPFILTER > 0
> +void mwx_radiotap_attach(struct mwx_softc *);
> +#endif
> +
> +int  mwx_tx(struct mwx_softc *, struct mbuf *, struct ieee80211_node *);
> +void mwx_rx(struct mwx_softc *, struct mbuf *, struct mbuf_list *);
> +int  mwx_intr(void *);
> +int  mwx_preinit(struct mwx_softc *);
> +void mwx_attach_hook(struct device *);
> +int  mwx_match(struct device *, void *, void *);
> +void mwx_attach(struct device *, struct device *, void *);
> +int  mwx_activate(struct device *, int);
> +
> +void mwx_reset(struct mwx_softc *);
> +void mwx_reset_task(void *);
> +int  mwx_queue_alloc(struct mwx_softc *, struct mwx_queue *, int, uint32_t);
> +void mwx_queue_free(struct mwx_softc *, struct mwx_queue *);
> +void mwx_queue_reset(struct mwx_softc *, struct mwx_queue *);
> +int  mwx_buf_fill(struct mwx_softc *, struct mwx_data *, struct mt76_desc *);
> +int  mwx_queue_fill(struct mwx_softc *, struct mwx_queue *);
> +int  mwx_dma_alloc(struct mwx_softc *);
> +int  mwx_dma_reset(struct mwx_softc *, int);
> +void mwx_dma_free(struct mwx_softc *);
> +int  mwx_dma_tx_enqueue(struct mwx_softc *, struct mwx_queue *,
> +         struct mbuf *, uint32_t);
> +void mwx_dma_tx_cleanup(struct mwx_softc *, struct mwx_queue *);
> +void mwx_dma_tx_done(struct mwx_softc *);
> +void mwx_dma_rx_process(struct mwx_softc *, struct mbuf_list *);
> +void mwx_dma_rx_dequeue(struct mwx_softc *, struct mwx_queue *,
> +         struct mbuf_list *);
> +void mwx_dma_rx_done(struct mwx_softc *, struct mwx_queue *);
> +
> +int  mwx_mcu_send_msg(struct mwx_softc *, uint32_t, void *, size_t, int *);
> +int  mwx_mcu_send_wait(struct mwx_softc *, uint32_t, void *, size_t);
> +void mwx_mcu_rx_event(struct mwx_softc *, struct mbuf *);
> +int  mwx_mcu_wait_resp_int(struct mwx_softc *, uint32_t, int, uint32_t *);
> +int  mwx_mcu_wait_resp_msg(struct mwx_softc *, uint32_t, int,
> +         struct mbuf **);
> +
> +int          mt7921_dma_disable(struct mwx_softc *sc, int force);
> +void         mt7921_dma_enable(struct mwx_softc *sc);
> +int          mt7921_wfsys_reset(struct mwx_softc *sc);
> +uint32_t     mt7921_reg_addr(struct mwx_softc *, uint32_t);
> +int          mt7921_init_hardware(struct mwx_softc *);
> +int          mt7921_mcu_init(struct mwx_softc *);
> +int          mt7921_load_firmware(struct mwx_softc *);
> +int          mt7921_mac_wtbl_update(struct mwx_softc *, int);
> +void         mt7921_mac_init_band(struct mwx_softc *sc, uint32_t);
> +int          mt7921_mac_init(struct mwx_softc *);
> +int          mt7921_mcu_patch_sem_ctrl(struct mwx_softc *, int);
> +int          mt7921_mcu_init_download(struct mwx_softc *, uint32_t,
> +                 uint32_t, uint32_t);
> +int          mt7921_mcu_send_firmware(struct mwx_softc *, int,
> +                 u_char *, size_t, size_t);
> +int          mt7921_mcu_start_patch(struct mwx_softc *);
> +int          mt7921_mcu_start_firmware(struct mwx_softc *, uint32_t,
> +                 uint32_t);
> +int          mt7921_mcu_get_nic_capability(struct mwx_softc *);
> +int          mt7921_mcu_fw_log_2_host(struct mwx_softc *, uint8_t);
> +int          mt7921_mcu_set_eeprom(struct mwx_softc *);
> +int          mt7921_mcu_set_rts_thresh(struct mwx_softc *, uint32_t,
> +                 uint8_t);
> +int          mt7921_mcu_set_deep_sleep(struct mwx_softc *, int);
> +void         mt7921_mcu_low_power_event(struct mwx_softc *, struct mbuf *);
> +void         mt7921_mcu_tx_done_event(struct mwx_softc *, struct mbuf *);
> +void         mt7921_mcu_scan_event(struct mwx_softc *, struct mbuf *);
> +int          mt7921_hw_scan(struct mwx_softc *, int);
> +int          mt7921_hw_scan_cancel(struct mwx_softc *);
> +int          mt7921_mcu_set_mac_enable(struct mwx_softc *, int, int);
> +int          mt7921_mcu_set_channel_domain(struct mwx_softc *);
> +uint8_t              mt7921_mcu_chan_bw(struct ieee80211_channel *channel);
> +int          mt7921_mcu_set_chan_info(struct mwx_softc *, int);
> +void         mt7921_mac_reset_counters(struct mwx_softc *);
> +int          mt7921_mcu_uni_add_dev(struct mwx_softc *, struct mwx_vif *,
> +                 struct mwx_node *, int);
> +int          mt7921_mcu_set_sniffer(struct mwx_softc *, int);
> +int          mt7921_mcu_set_beacon_filter(struct mwx_softc *, int);
> +int          mt7921_mac_fill_rx(struct mwx_softc *, struct mbuf *,
> +                 struct ieee80211_rxinfo *);
> +
> +
> +static inline uint32_t
> +mwx_read(struct mwx_softc *sc, uint32_t reg)
> +{
> +     reg = mt7921_reg_addr(sc, reg);
> +     return bus_space_read_4(sc->sc_st, sc->sc_memh, reg);
> +}
> +
> +static inline void
> +mwx_write(struct mwx_softc *sc, uint32_t reg, uint32_t val)
> +{
> +     reg = mt7921_reg_addr(sc, reg);
> +     bus_space_write_4(sc->sc_st, sc->sc_memh, reg, val);
> +}
> +
> +static inline void
> +mwx_barrier(struct mwx_softc *sc)
> +{
> +     bus_space_barrier(sc->sc_st, sc->sc_memh, 0, sc->sc_mems,
> +         BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
> +}
> +
> +static inline uint32_t
> +mwx_rmw(struct mwx_softc *sc, uint32_t reg, uint32_t val, uint32_t mask)
> +{
> +     reg = mt7921_reg_addr(sc, reg);
> +     val |= bus_space_read_4(sc->sc_st, sc->sc_memh, reg) & ~mask;
> +     bus_space_write_4(sc->sc_st, sc->sc_memh, reg, val);
> +     return val;
> +}
> +
> +static inline uint32_t
> +mwx_set(struct mwx_softc *sc, uint32_t reg, uint32_t bits)
> +{
> +     return mwx_rmw(sc, reg, bits, 0);
> +}
> +
> +static inline uint32_t
> +mwx_clear(struct mwx_softc *sc, uint32_t reg, uint32_t bits)
> +{
> +     return mwx_rmw(sc, reg, 0, bits);
> +}
> +
> +static inline uint32_t
> +mwx_map_reg_l1(struct mwx_softc *sc, uint32_t reg)
> +{
> +     uint32_t offset = MT_HIF_REMAP_L1_GET_OFFSET(reg);
> +     uint32_t base = MT_HIF_REMAP_L1_GET_BASE(reg);
> +
> +     mwx_rmw(sc, MT_HIF_REMAP_L1, base, MT_HIF_REMAP_L1_MASK);
> +     mwx_barrier(sc);
> +
> +     return MT_HIF_REMAP_BASE_L1 + offset;
> +}
> +
> +/*
> + * Poll for timeout miliseconds or until register reg read the value val
> + * after applying the mask to the value read. Returns 0 on success ETIMEDOUT
> + * on failure.
> + */
> +int
> +mwx_poll(struct mwx_softc *sc, uint32_t reg, uint32_t val, uint32_t mask,
> +    int timeout)
> +{
> +     uint32_t cur;
> +
> +     reg = mt7921_reg_addr(sc, reg);
> +     timeout *= 100;
> +     do {
> +             cur = bus_space_read_4(sc->sc_st, sc->sc_memh, reg) & mask;
> +             if (cur == val)
> +                     return 0;
> +             delay(10);
> +     } while (timeout-- > 0);
> +
> +     DPRINTF("%s: poll timeout reg %x val %x mask %x cur %x\n",
> +         DEVNAME(sc), reg, val, mask, cur);
> +     return ETIMEDOUT;
> +}
> +
> +/*
> + * ifp specific functions
> + */
> +int
> +mwx_init(struct ifnet *ifp)
> +{
> +     struct mwx_softc *sc = ifp->if_softc;
> +     struct ieee80211com *ic = &sc->sc_ic;
> +     int rv;
> +
> +     if (!sc->sc_fw_loaded) {
> +             rv = mwx_preinit(sc);
> +             if (rv)
> +                     return rv;
> +     }
> +
> +     DPRINTF("%s: init\n", DEVNAME(sc));
> +     mt7921_mcu_set_deep_sleep(sc, 0);
> +
> +     rv = mt7921_mcu_set_mac_enable(sc, 0, 1);
> +     if (rv)
> +             return rv;
> +
> +     rv = mt7921_mcu_set_channel_domain(sc);
> +     if (rv)
> +             return rv;
> +
> +     rv = mt7921_mcu_set_chan_info(sc, MCU_EXT_CMD_SET_RX_PATH);
> +     if (rv)
> +             return rv;
> +
> +     /* XXX TODO
> +     rv = mt76_connac_mcu_set_rate_txpower(sc);
> +     if (rv)
> +             return rv;
> +     */
> +
> +     if (ic->ic_opmode == IEEE80211_M_MONITOR) {
> +             mt7921_mcu_set_sniffer(sc, 1);
> +             mt7921_mcu_set_beacon_filter(sc, 0);
> +             sc->sc_rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC;
> +             mwx_set(sc, MT_DMA_DCR0(0), MT_DMA_DCR0_RXD_G5_EN);
> +     } else {
> +             mt7921_mcu_set_sniffer(sc, 0);
> +             sc->sc_rxfilter |= MT_WF_RFCR_DROP_OTHER_UC;
> +             mwx_clear(sc, MT_DMA_DCR0(0), MT_DMA_DCR0_RXD_G5_EN);
> +     }
> +     mwx_write(sc, MT_WF_RFCR(0), sc->sc_rxfilter);
> +
> +     mt7921_mac_reset_counters(sc);
> +
> +     rv = mt7921_mcu_uni_add_dev(sc, &sc->sc_vif, (void *)ic->ic_bss, 1);
> +     if (rv)
> +             return rv;
> +
> +     ifp->if_flags |= IFF_RUNNING;
> +     ifq_clr_oactive(&ifp->if_snd);
> +
> +     if (ic->ic_opmode == IEEE80211_M_MONITOR) {
> +             ic->ic_bss->ni_chan = ic->ic_ibss_chan;
> +             ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
> +             return 0;
> +     }
> +
> +     ieee80211_begin_scan(ifp);
> +
> +     /*
> +      * ieee80211_begin_scan() ends up scheduling iwx_newstate_task().
> +      * Wait until the transition to SCAN state has completed.
> +      */
> +
> +     return 0;
> +}
> +
> +void
> +mwx_stop(struct ifnet *ifp)
> +{
> +     struct mwx_softc *sc = ifp->if_softc;
> +     struct ieee80211com *ic = &sc->sc_ic;
> +
> +     DPRINTF("%s: stop\n", DEVNAME(sc));
> +
> +     //XXX sc->sc_flags |= MWX_FLAG_SHUTDOWN;
> +     /* Cancel scheduled tasks and let any stale tasks finish up. */
> +     task_del(systq, &sc->sc_reset_task);
> +
> +        ifp->if_timer = 0;
> +     ifp->if_flags &= ~IFF_RUNNING;
> +     ifq_clr_oactive(&ifp->if_snd);
> +
> +     ieee80211_new_state(ic, IEEE80211_S_INIT, -1);  /* free all nodes */
> +
> +     mt7921_mcu_set_mac_enable(sc, 0, 0);
> +
> +     /* XXX anything more ??? */
> +     /* check out mt7921e_mac_reset, mt7921e_unregister_device and
> +        mt7921_pci_suspend
> +      */
> +}
> +
> +void
> +mwx_watchdog(struct ifnet *ifp)
> +{
> +     ifp->if_timer = 0;
> +     ieee80211_watchdog(ifp);
> +}
> +
> +void
> +mwx_start(struct ifnet *ifp)
> +{
> +     struct mwx_softc *sc = ifp->if_softc;
> +     struct ieee80211com *ic = &sc->sc_ic;
> +     struct ieee80211_node *ni;
> +     struct ether_header *eh;
> +     struct mbuf *m;
> +
> +     if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd))
> +             return;
> +
> +     for (;;) {
> +             /* XXX TODO handle oactive
> +                     ifq_set_oactive(&ifp->if_snd);
> +             */
> +
> +             /* need to send management frames even if we're not RUNning */
> +             m = mq_dequeue(&ic->ic_mgtq);
> +             if (m) {
> +                     ni = m->m_pkthdr.ph_cookie;
> +                     goto sendit;
> +             }
> +
> +             if (ic->ic_state != IEEE80211_S_RUN ||
> +                 (ic->ic_xflags & IEEE80211_F_TX_MGMT_ONLY))
> +                     break;
> +
> +             m = ifq_dequeue(&ifp->if_snd);
> +             if (!m)
> +                     break;
> +             if (m->m_len < sizeof (*eh) &&
> +                 (m = m_pullup(m, sizeof (*eh))) == NULL) {
> +                     ifp->if_oerrors++;
> +                     continue;
> +             }
> +#if NBPFILTER > 0
> +             if (ifp->if_bpf != NULL)
> +                     bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
> +#endif
> +             if ((m = ieee80211_encap(ifp, m, &ni)) == NULL) {
> +                     ifp->if_oerrors++;
> +                     continue;
> +             }
> +
> + sendit:
> +#if NBPFILTER > 0
> +             if (ic->ic_rawbpf != NULL)
> +                     bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT);
> +#endif
> +             if (mwx_tx(sc, m, ni) != 0) {
> +                     ieee80211_release_node(ic, ni);
> +                     ifp->if_oerrors++;
> +                     continue;
> +             }
> +
> +             if (ifp->if_flags & IFF_UP)
> +                     ifp->if_timer = 1;
> +     }
> +}
> +
> +int
> +mwx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
> +{
> +     int s, err = 0;
> +
> +     s = splnet();
> +     switch (cmd) {
> +     case SIOCSIFADDR:
> +             ifp->if_flags |= IFF_UP;
> +             /* FALLTHROUGH */
> +     case SIOCSIFFLAGS:
> +             if (ifp->if_flags & IFF_UP) {
> +                     if (!(ifp->if_flags & IFF_RUNNING)) {
> +                             mwx_stop(ifp);
> +                             err = mwx_init(ifp);
> +                     }
> +             } else {
> +                     if (ifp->if_flags & IFF_RUNNING)
> +                             mwx_stop(ifp);
> +             }
> +             break;
> +     default:
> +             err = ieee80211_ioctl(ifp, cmd, data);
> +             break;
> +     }
> +
> +     if (err == ENETRESET) {
> +             err = 0;
> +             if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
> +                 (IFF_UP | IFF_RUNNING)) {
> +                     mwx_stop(ifp);
> +                     err = mwx_init(ifp);
> +             }
> +     }
> +     splx(s);
> +     return err;
> +}
> +
> +int
> +mwx_media_change(struct ifnet *ifp)
> +{
> +     struct mwx_softc *sc = ifp->if_softc;
> +     struct ieee80211com *ic = &sc->sc_ic;
> +     int err;
> +
> +     err = ieee80211_media_change(ifp);
> +     if (err != ENETRESET)
> +             return err;
> +
> +     /* TODO lot more handling here */
> +     if (ic->ic_fixed_mcs != -1) {
> +             ;
> +     } else if (ic->ic_fixed_rate != -1) {
> +             ;
> +     }
> +     if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
> +         (IFF_UP | IFF_RUNNING)) {
> +             /* XXX could be a bit harsh */
> +             mwx_stop(ifp);
> +             err = mwx_init(ifp);
> +     }
> +     return err;
> +}
> +
> +/*
> + * net80211 specific functions.
> + */
> +
> +struct ieee80211_node *
> +mwx_node_alloc(struct ieee80211com *ic)
> +{
> +     return malloc(sizeof(struct mwx_node), M_DEVBUF, M_NOWAIT | M_ZERO);
> +}
> +
> +void
> +mwx_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, int isnew)
> +{
> +     struct mwx_softc *sc = ic->ic_softc;
> +     struct mwx_node *mn = (void *)ni;
> +     uint16_t wcid = 0;
> +
> +        if (isnew && ni->ni_associd != 0) {
> +             /* only interested in true associations */
> +             wcid = mn->wcid = IEEE80211_AID(ni->ni_associd);
> +
> +             /* init WCID table entry */
> +             /* XXX TODO */
> +     }
> +     printf("%s: new assoc isnew=%d addr=%s WCID=%d\n", DEVNAME(sc),
> +         isnew, ether_sprintf(ni->ni_macaddr), wcid);
> +
> +     /* XXX TODO rate handling here */
> +}
> +
> +#ifndef IEEE80211_STA_ONLY
> +void
> +mwx_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
> +{
> +#if 0
> +     struct mwx_softc *sc = ic->ic_softc;
> +     struct mwx_node *mn = (void *)ni;
> +     uint16_t wcid = mn->wcid;
> +
> +     /* TODO clear WCID */
> +#endif
> +}
> +#endif
> +
> +int
> +mwx_scan(struct mwx_softc *sc)
> +{
> +     struct ieee80211com *ic = &sc->sc_ic;
> +     struct ifnet *ifp = &ic->ic_if;
> +     int rv;
> +
> +     if (sc->sc_flags & MWX_FLAG_BGSCAN) {
> +             rv = mt7921_hw_scan_cancel(sc);
> +             if (rv) {
> +                     printf("%s: could not abort background scan\n",
> +                         DEVNAME(sc));
> +                     return rv;
> +             }
> +     }
> +
> +     rv = mt7921_hw_scan(sc, 0);
> +        if (rv) {
> +             printf("%s: could not initiate scan\n", DEVNAME(sc));
> +             return rv;
> +     }
> +
> +     /*
> +      * The current mode might have been fixed during association.
> +      * Ensure all channels get scanned.
> +      */
> +     if (IFM_MODE(ic->ic_media.ifm_cur->ifm_media) != IFM_AUTO)
> +             ieee80211_setmode(ic, IEEE80211_MODE_AUTO);
> +
> +     sc->sc_flags |= MWX_FLAG_SCANNING;
> +     if (ifp->if_flags & IFF_DEBUG)
> +             printf("%s: %s -> %s\n", ifp->if_xname,
> +                 ieee80211_state_name[ic->ic_state],
> +                 ieee80211_state_name[IEEE80211_S_SCAN]);
> +     if ((sc->sc_flags & MWX_FLAG_BGSCAN) == 0) {
> +             ieee80211_set_link_state(ic, LINK_STATE_DOWN);
> +             ieee80211_node_cleanup(ic, ic->ic_bss);
> +     }
> +     ic->ic_state = IEEE80211_S_SCAN;
> +
> +     return 0;
> +}
> +
> +int
> +mwx_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
> +{
> +     struct mwx_softc *sc = ic->ic_softc;
> +     enum ieee80211_state ostate;
> +     int rv;
> +
> +     ostate = ic->ic_state;
> +
> +printf("%s: %s %d -> %d\n", DEVNAME(sc), __func__, ostate, nstate);
> +
> +     switch (ostate) {
> +     case IEEE80211_S_RUN:
> +             mt7921_mcu_set_deep_sleep(sc, 1);
> +             break;
> +     case IEEE80211_S_SCAN:
> +             if (nstate == ostate) {
> +                     if (sc->sc_flags & MWX_FLAG_SCANNING)
> +                             return 0;
> +             }
> +             break;
> +     default:
> +             break;
> +     }
> +
> +     /* XXX TODO */
> +     switch (nstate) {
> +     case IEEE80211_S_INIT:
> +             break;
> +     case IEEE80211_S_SCAN:
> +             rv = mwx_scan(sc);
> +             if (rv)
> +                     /* XXX error handling */
> +                     return rv;
> +             return 0;
> +     case IEEE80211_S_AUTH:
> +     case IEEE80211_S_ASSOC:
> +             break;
> +     case IEEE80211_S_RUN:
> +             mt7921_mcu_set_deep_sleep(sc, 0);
> +             break;
> +     }
> +
> +     return sc->sc_newstate(ic, nstate, arg);
> +}
> +
> +#if NBPFILTER > 0
> +void
> +mwx_radiotap_attach(struct mwx_softc *sc)
> +{
> +     bpfattach(&sc->sc_drvbpf, &sc->sc_ic.ic_if, DLT_IEEE802_11_RADIO,
> +         sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN);
> +
> +     sc->sc_rxtap_len = sizeof sc->sc_rxtapu;
> +     sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
> +     sc->sc_rxtap.wr_ihdr.it_present = htole32(MWX_RX_RADIOTAP_PRESENT);
> +
> +     sc->sc_txtap_len = sizeof sc->sc_txtapu;
> +     sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
> +     sc->sc_txtap.wt_ihdr.it_present = htole32(MWX_TX_RADIOTAP_PRESENT);
> +}
> +#endif
> +
> +int
> +mwx_tx(struct mwx_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
> +{
> +     return ENOBUFS;
> +}
> +
> +void
> +mwx_rx(struct mwx_softc *sc, struct mbuf *m, struct mbuf_list *ml)
> +{
> +     struct ieee80211com *ic = &sc->sc_ic;
> +     struct ifnet *ifp = &ic->ic_if;
> +     struct ieee80211_node *ni;
> +     struct ieee80211_frame *wh;
> +     struct ieee80211_rxinfo rxi = { 0 };
> +
> +
> +     if (mt7921_mac_fill_rx(sc, m, &rxi) == -1) {
> +             ifp->if_ierrors++;
> +             m_freem(m);
> +             return;
> +     }
> +
> +     wh = mtod(m, struct ieee80211_frame *);
> +
> +#if NBPFILTER > 0
> +     if (__predict_false(sc->sc_drvbpf != NULL)) {
> +             struct mwx_rx_radiotap_header *tap = &sc->sc_rxtap;
> +             uint32_t tsf_lo, tsf_hi;
> +             /* get timestamp (low and high 32 bits) */
> +             tsf_hi = 0;
> +             tsf_lo = 0;
> +             tap->wr_tsft = htole64(((uint64_t)tsf_hi << 32) | tsf_lo);
> +             tap->wr_flags = 0;
> +             tap->wr_rate = 2;       /* XXX */
> +             tap->wr_chan_freq = 
> +                 htole16(ic->ic_channels[rxi.rxi_chan].ic_freq);
> +             tap->wr_chan_flags =
> +                 ic->ic_channels[rxi.rxi_chan].ic_flags;
> +             tap->wr_dbm_antsignal = 0;
> +             bpf_mtap_hdr(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m,
> +                 BPF_DIRECTION_IN);
> +     }
> +#endif
> +
> +     /* grab a reference to the source node */
> +     ni = ieee80211_find_rxnode(ic, wh);
> +
> +     /* send the frame to the 802.11 layer */
> +     /* TODO MAYBE rxi.rxi_rssi = rssi; */
> +     ieee80211_inputm(ifp, m, ni, &rxi, ml);
> +
> +     /* node is no longer needed */
> +     ieee80211_release_node(ic, ni);
> +}
> +
> +/*
> + * Driver specific functions.
> + */
> +int
> +mwx_intr(void *arg)
> +{
> +     struct mwx_softc *sc = arg;
> +     uint32_t intr, intr_sw;
> +     uint32_t mask = MT_INT_RX_DONE_ALL|MT_INT_TX_DONE_ALL|MT_INT_MCU_CMD;
> +
> +     mwx_write(sc, MT_WFDMA0_HOST_INT_ENA, 0);
> +        intr = mwx_read(sc, MT_WFDMA0_HOST_INT_STA);
> +     if (intr == 0) {
> +             mwx_write(sc, MT_WFDMA0_HOST_INT_ENA, mask);
> +             return 0;
> +     }
> +
> +     /* TODO power management */
> +//   mt76_connac_pm_ref(&dev->mphy, &dev->pm);
> +
> +     if (intr & ~mask)
> +             printf("%s: unhandled interupt %08x\n", DEVNAME(sc),
> +                 intr & ~mask);
> +     /* ack interrupts */
> +     intr &= mask;
> +     mwx_write(sc, MT_WFDMA0_HOST_INT_STA, intr);
> +
> +     if (intr & MT_INT_MCU_CMD) {
> +             intr_sw = mwx_read(sc, MT_MCU_CMD);
> +             /* ack MCU2HOST_SW_INT_STA */
> +             mwx_write(sc, MT_MCU_CMD, intr_sw);
> +             if (intr_sw & MT_MCU_CMD_WAKE_RX_PCIE)
> +                     intr |= MT_INT_RX_DONE_DATA;
> +     }
> +
> +     if (intr & MT_INT_TX_DONE_ALL)
> +             mwx_dma_tx_done(sc);
> +
> +     if (intr & MT_INT_RX_DONE_WM)
> +             mwx_dma_rx_done(sc, &sc->sc_rxfwdlq);
> +     if (intr & MT_INT_RX_DONE_WM2)
> +             mwx_dma_rx_done(sc, &sc->sc_rxmcuq);
> +     if (intr & MT_INT_RX_DONE_DATA)
> +             mwx_dma_rx_done(sc, &sc->sc_rxq);
> +
> +     /* TODO power management */
> +//   mt76_connac_pm_unref(&dev->mphy, &dev->pm);
> +
> +     mwx_write(sc, MT_WFDMA0_HOST_INT_ENA, mask);
> +
> +     return 1;
> +}
> +
> +int
> +mwx_preinit(struct mwx_softc *sc)
> +{
> +     struct ieee80211com *ic = &sc->sc_ic;
> +     struct ifnet *ifp = &ic->ic_if;
> +     int rv, i;
> +     uint8_t chan;
> +
> +     DPRINTF("%s: init\n", DEVNAME(sc));
> +
> +     if ((rv = mt7921_init_hardware(sc)) != 0)
> +             return rv;
> +
> +     if ((rv = mt7921_mcu_set_deep_sleep(sc, 1)) != 0)
> +             return rv;
> +
> +     ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
> +     ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g;
> +
> +     if (sc->sc_capa.has_2ghz) {
> +             for (i = 0; i < MWX_NUM_2GHZ_CHANNELS; i++) {
> +                     chan = mwx_channels_2ghz[i];
> +                     ic->ic_channels[chan].ic_freq =
> +                         ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ);
> +                     ic->ic_channels[chan].ic_flags =
> +                         IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
> +                         IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
> +                     /* TODO 11n and 11ac flags */
> +             }
> +
> +     }
> +     if (sc->sc_capa.has_5ghz) {
> +             ic->ic_sup_rates[IEEE80211_MODE_11A] =
> +                 ieee80211_std_rateset_11a;
> +             /* set supported .11a channels */
> +             for (i = 0; i < MWX_NUM_5GHZ_CHANNELS; i++) {
> +                     chan = mwx_channels_5ghz[i];
> +                     ic->ic_channels[chan].ic_freq =
> +                         ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ);
> +                     ic->ic_channels[chan].ic_flags = IEEE80211_CHAN_A;
> +                     /* TODO 11n and 11ac flags */
> +             }
> +     }
> +#ifdef NOTYET
> +     /* TODO support for 6GHz */
> +     if (sc->sc_capa.has_6ghz) {
> +             for (i = 0; i < MWX_NUM_6GHZ_CHANNELS; i++) {
> +             }
> +     }
> +#endif
> +
> +     /* Configure channel information obtained from firmware. */
> +     ieee80211_channel_init(ifp);
> +
> +     if (IEEE80211_ADDR_EQ(etheranyaddr, sc->sc_ic.ic_myaddr))
> +             IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_lladdr);
> +
> +        /* Configure MAC address. */
> +     rv = if_setlladdr(ifp, ic->ic_myaddr);
> +     if (rv)
> +             printf("%s: could not set MAC address (error %d)\n",
> +                 DEVNAME(sc), rv);
> +
> +        ieee80211_media_init(ifp, mwx_media_change, ieee80211_media_status);
> +
> +     sc->sc_fw_loaded = 1;
> +     return 0;
> +}
> +
> +void
> +mwx_attach_hook(struct device *self)
> +{
> +     struct mwx_softc *sc = (void *)self;
> +
> +     mwx_preinit(sc);
> +}
> +
> +int
> +mwx_match(struct device *parent, void *match __unused, void *aux)
> +{
> +     struct pci_attach_args *pa = aux;
> +
> +     return pci_matchbyid(pa, mwx_devices, nitems(mwx_devices));
> +}
> +
> +void
> +mwx_attach(struct device *parent, struct device *self, void *aux)
> +{
> +     struct mwx_softc *sc = (struct mwx_softc *)self;
> +     struct ieee80211com *ic = &sc->sc_ic;
> +     struct ifnet *ifp = &ic->ic_if;
> +     struct pci_attach_args *pa = aux;
> +     pci_intr_handle_t ih;
> +     pcireg_t memtype;
> +     uint32_t hwid, hwrev;
> +     int error;
> +
> +     sc->sc_pc = pa->pa_pc;
> +     sc->sc_tag = pa->pa_tag;
> +     sc->sc_dmat = pa->pa_dmat;
> +
> +     memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_MAPREG_START);
> +     if (pci_mapreg_map(pa, PCI_MAPREG_START, memtype, 0,
> +         &sc->sc_st, &sc->sc_memh, NULL, &sc->sc_mems, 0)) {
> +             printf("%s: can't map mem space\n", DEVNAME(sc));
> +             return;
> +     }
> +
> +     if (pci_intr_map_msix(pa, 0, &ih) &&
> +         pci_intr_map_msi(pa, &ih) &&
> +         pci_intr_map(pa, &ih)) {
> +             printf("%s: can't map interrupt\n", DEVNAME(sc));
> +             bus_space_unmap(sc->sc_st, sc->sc_memh, sc->sc_mems);
> +             return;
> +     }
> +
> +     hwid = mwx_read(sc, MT_HW_CHIPID) & 0xffff;
> +     hwrev = mwx_read(sc, MT_HW_REV) & 0xff;
> +
> +     printf(": %s, rev: %x.%x\n", pci_intr_string(pa->pa_pc, ih),
> +         hwid, hwrev);
> +
> +     mwx_write(sc, MT_WFDMA0_HOST_INT_ENA, 0);
> +     mwx_write(sc, MT_PCIE_MAC_INT_ENABLE, 0xff);
> +
> +     sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_NET,
> +         mwx_intr, sc, DEVNAME(sc));
> +
> +     if ((error = mwx_dma_alloc(sc)) != 0) {
> +             printf("%s: failed to allocate DMA resources %d\n",
> +                 DEVNAME(sc), error);
> +             goto fail;
> +     }
> +
> +     ic->ic_phytype = IEEE80211_T_OFDM;      /* not only, but not used */
> +     ic->ic_opmode = IEEE80211_M_STA;        /* default to BSS mode */
> +     ic->ic_state = IEEE80211_S_INIT;
> +
> +     /* Set device capabilities. */
> +     ic->ic_caps =
> +         IEEE80211_C_QOS | IEEE80211_C_TX_AMPDU | /* A-MPDU */
> +         IEEE80211_C_ADDBA_OFFLOAD | /* device sends ADDBA/DELBA frames */
> +         IEEE80211_C_WEP |           /* WEP */
> +         IEEE80211_C_RSN |           /* WPA/RSN */
> +         IEEE80211_C_SCANALL |       /* device scans all channels at once */
> +         IEEE80211_C_SCANALLBAND |   /* device scans all bands at once */
> +         IEEE80211_C_MONITOR |       /* monitor mode supported */
> +#ifndef IEEE80211_STA_ONLY
> +            IEEE80211_C_IBSS |               /* IBSS mode supported */
> +         IEEE80211_C_HOSTAP |        /* HostAP mode supported */
> +         IEEE80211_C_APPMGT |        /* HostAP power management */
> +#endif
> +         IEEE80211_C_SHSLOT |        /* short slot time supported */
> +         IEEE80211_C_SHPREAMBLE;     /* short preamble supported */
> +
> +     ic->ic_htcaps = IEEE80211_HTCAP_SGI20 | IEEE80211_HTCAP_SGI40;
> +     ic->ic_htcaps |= IEEE80211_HTCAP_CBW20_40;
> +     ic->ic_htcaps |=
> +         (IEEE80211_HTCAP_SMPS_DIS << IEEE80211_HTCAP_SMPS_SHIFT);
> +     ic->ic_htxcaps = 0;
> +     ic->ic_txbfcaps = 0;
> +     ic->ic_aselcaps = 0;
> +     ic->ic_ampdu_params = (IEEE80211_AMPDU_PARAM_SS_4 | 0x3 /* 64k */);
> +
> +     ic->ic_vhtcaps = IEEE80211_VHTCAP_MAX_MPDU_LENGTH_3895 |
> +         (IEEE80211_VHTCAP_MAX_AMPDU_LEN_64K <<
> +         IEEE80211_VHTCAP_MAX_AMPDU_LEN_SHIFT) |
> +         (IEEE80211_VHTCAP_CHAN_WIDTH_80 <<
> +         IEEE80211_VHTCAP_CHAN_WIDTH_SHIFT) | IEEE80211_VHTCAP_SGI80 |
> +         IEEE80211_VHTCAP_RX_ANT_PATTERN | IEEE80211_VHTCAP_TX_ANT_PATTERN;
> +
> +     /* IBSS channel undefined for now. */
> +     ic->ic_ibss_chan = &ic->ic_channels[1];
> +
> +        /* HW supports up to 288 STAs in HostAP and IBSS modes */
> +     ic->ic_max_aid = min(IEEE80211_AID_MAX, MWX_WCID_MAX);
> +
> +     //XXX TODO ic->ic_max_rssi = IWX_MAX_DBM - IWX_MIN_DBM;
> +
> +     ifp->if_softc = sc;
> +     ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
> +     ifp->if_ioctl = mwx_ioctl;
> +     ifp->if_start = mwx_start;
> +     ifp->if_watchdog = mwx_watchdog;
> +     memcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ);
> +
> +     if_attach(ifp);
> +     ieee80211_ifattach(ifp);
> +     ieee80211_media_init(ifp, mwx_media_change, ieee80211_media_status);
> +
> +#if NBPFILTER > 0
> +     mwx_radiotap_attach(sc);
> +#endif
> +
> +        ic->ic_node_alloc = mwx_node_alloc;
> +     ic->ic_newassoc = mwx_newassoc;
> +#ifndef IEEE80211_STA_ONLY
> +     ic->ic_node_leave = mwx_node_leave;
> +#endif
> +     /* TODO XXX
> +     ic->ic_bgscan_start = mwx_bgscan;
> +     ic->ic_bgscan_done = mwx_bgscan_done;
> +     ic->ic_set_key = mwx_set_key;
> +     ic->ic_delete_key = mwx_delete_key;
> +     */
> +
> +     /* Override 802.11 state transition machine. */
> +     sc->sc_newstate = ic->ic_newstate;
> +     ic->ic_newstate = mwx_newstate;
> +
> +     task_set(&sc->sc_reset_task, mwx_reset_task, sc);
> +
> +     /*
> +      * We cannot read the MAC address without loading the
> +      * firmware from disk. Postpone until mountroot is done.
> +      */
> +     config_mountroot(self, mwx_attach_hook);
> +
> +     return;
> +
> +fail:
> +     mwx_dma_free(sc);
> +     pci_intr_disestablish(pa->pa_pc, sc->sc_ih);
> +     bus_space_unmap(sc->sc_st, sc->sc_memh, sc->sc_mems);
> +     return;
> +}
> +
> +int
> +mwx_activate(struct device *self, int act)
> +{
> +     /* XXX TODO */
> +     return 0;
> +}
> +
> +struct cfdriver mwx_cd = {
> +     NULL, "mwx", DV_IFNET
> +};
> +
> +struct cfattach mwx_ca = {
> +     sizeof(struct mwx_softc), mwx_match, mwx_attach,
> +     NULL, mwx_activate
> +};
> +
> +void
> +mwx_reset(struct mwx_softc *sc)
> +{
> +     if (sc->sc_resetting)
> +             return;
> +     sc->sc_resetting = 1;
> +     task_add(systq, &sc->sc_reset_task);
> +}
> +
> +void
> +mwx_reset_task(void *arg)
> +{
> +     struct mwx_softc *sc = arg;
> +     struct ifnet *ifp = &sc->sc_ic.ic_if;
> +     int fatal = 0;
> +
> +        if (ifp->if_flags & IFF_RUNNING)
> +             mwx_stop(ifp);
> +
> +     if (!fatal && (ifp->if_flags & (IFF_UP | IFF_RUNNING)) == IFF_UP)
> +             mwx_init(ifp);
> +}
> +
> +int
> +mwx_queue_alloc(struct mwx_softc *sc, struct mwx_queue *q, int count,
> +    uint32_t regbase)
> +{
> +     int error, nsegs, i;
> +     bus_size_t size = count * sizeof(*q->mq_desc);
> +
> +     q->mq_regbase = regbase;
> +     q->mq_count = count;
> +
> +     error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
> +         BUS_DMA_NOWAIT, &q->mq_map);
> +     if (error != 0) {
> +             printf("%s: could not create desc DMA map\n", DEVNAME(sc));
> +             goto fail;
> +     }
> +
> +     error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &q->mq_seg,
> +         1, &nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO);
> +     if (error != 0) {
> +             printf("%s: could not allocate DMA memory\n", DEVNAME(sc));
> +             goto fail;
> +     }
> +
> +     error = bus_dmamem_map(sc->sc_dmat, &q->mq_seg, nsegs, size,
> +         (caddr_t *)&q->mq_desc, BUS_DMA_NOWAIT);
> +     if (error != 0) {
> +             printf("%s: can't map desc DMA memory\n", DEVNAME(sc));
> +             goto fail;
> +     }
> +
> +     error = bus_dmamap_load(sc->sc_dmat, q->mq_map, q->mq_desc,
> +         size, NULL, BUS_DMA_NOWAIT);
> +     if (error != 0) {
> +             printf("%s: could not load desc DMA map\n", DEVNAME(sc));
> +             goto fail;
> +     }
> +
> +     q->mq_data = mallocarray(count, sizeof(*q->mq_data),
> +         M_DEVBUF, M_NOWAIT | M_ZERO);
> +     if (q->mq_data == NULL) {
> +             printf("%s: could not allocate soft data\n", DEVNAME(sc));
> +             error = ENOMEM;
> +             goto fail;
> +     }
> +
> +     for (i = 0; i < count; i++) {
> +             error = bus_dmamap_create(sc->sc_dmat, MT_MAX_SIZE,
> +                 MT_MAX_SCATTER, MT_MAX_SIZE, 0, BUS_DMA_NOWAIT,
> +                 &q->mq_data[i].md_map);
> +             if (error != 0) {
> +                     printf("%s: could not create data DMA map\n",
> +                         DEVNAME(sc));
> +                     goto fail;
> +             }
> +     }
> +
> +     mwx_queue_reset(sc, q);
> +     return 0;
> +
> +fail:
> +     mwx_queue_free(sc, q);
> +     return error;
> +
> +}
> +
> +void
> +mwx_queue_free(struct mwx_softc *sc, struct mwx_queue *q)
> +{
> +     if (q->mq_data != NULL) {
> +             int i;
> +             for (i = 0; i < q->mq_count; i++) {
> +                     struct mwx_data  *md = &q->mq_data[i];
> +                     bus_dmamap_destroy(sc->sc_dmat, md->md_map);
> +                     m_freem(md->md_data);
> +             }
> +             free(q->mq_data, M_DEVBUF, q->mq_count * sizeof(*q->mq_data));
> +     }
> +
> +     if (q->mq_desc != NULL) {
> +             bus_dmamap_sync(sc->sc_dmat, q->mq_map, 0,
> +                     q->mq_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
> +             bus_dmamap_unload(sc->sc_dmat, q->mq_map);
> +     }
> +
> +     /*
> +      * XXX TODO this is probably not correct as a check, should use
> +      * some state variable bitfield to decide which steps need to be run.
> +      */
> +     if (q->mq_seg.ds_len != 0)
> +             bus_dmamem_unmap(sc->sc_dmat, (caddr_t)q->mq_desc,
> +                     q->mq_count * sizeof(*q->mq_desc));
> +     if (q->mq_map != NULL)
> +             bus_dmamem_free(sc->sc_dmat, &q->mq_seg, 1);
> +
> +     memset(q, 0, sizeof(*q));
> +}
> +
> +void
> +mwx_queue_reset(struct mwx_softc *sc, struct mwx_queue *q)
> +{
> +     int i;
> +     uint32_t dmaaddr;
> +     struct mwx_data *md;
> +
> +     /* clear descriptors */
> +     bus_dmamap_sync(sc->sc_dmat, q->mq_map, 0, q->mq_map->dm_mapsize,
> +         BUS_DMASYNC_PREWRITE);
> +
> +     for (i = 0; i < q->mq_count; i++) {
> +             q->mq_desc[i].buf0 = 0;
> +             q->mq_desc[i].buf1 = 0;
> +             q->mq_desc[i].info = 0;
> +             q->mq_desc[i].ctrl = htole32(MT_DMA_CTL_DMA_DONE);
> +     }
> +
> +     bus_dmamap_sync(sc->sc_dmat, q->mq_map, 0, q->mq_map->dm_mapsize,
> +         BUS_DMASYNC_POSTWRITE);
> +
> +     /* reset DMA registers */
> +     KASSERT(q->mq_map->dm_nsegs == 1);
> +     KASSERT(q->mq_map->dm_segs[0].ds_addr <= UINT32_MAX);
> +     dmaaddr = q->mq_map->dm_segs[0].ds_addr;
> +     mwx_write(sc, q->mq_regbase + MT_DMA_DESC_BASE, dmaaddr);
> +     mwx_write(sc, q->mq_regbase + MT_DMA_RING_SIZE, q->mq_count);
> +     mwx_write(sc, q->mq_regbase + MT_DMA_CPU_IDX, 0);
> +     mwx_write(sc, q->mq_regbase + MT_DMA_DMA_IDX, 0);
> +     q->mq_cons = 0;
> +     q->mq_prod = 0;
> +
> +     /* free buffers */
> +     for (i = 0; i < q->mq_count; i++) {
> +             md = &q->mq_data[i];
> +             if (md->md_data != NULL) {
> +                     bus_dmamap_sync(sc->sc_dmat, md->md_map, 0,
> +                         md->md_map->dm_mapsize,
> +                         BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
> +                     bus_dmamap_unload(sc->sc_dmat, md->md_map);
> +                     m_freem(md->md_data);
> +                     md->md_data = NULL;
> +             }
> +     }
> +}
> +
> +int
> +mwx_buf_fill(struct mwx_softc *sc, struct mwx_data *md, struct mt76_desc 
> *desc)
> +{
> +     struct mbuf *m;
> +     uint32_t buf0, len0, ctrl;
> +     int rv;
> +
> +     m = MCLGETL(NULL, M_DONTWAIT, MT_RX_BUF_SIZE);
> +     if (m == NULL)
> +             return (ENOMEM);
> +
> +     m->m_pkthdr.len = m->m_len = MT_RX_BUF_SIZE;
> +
> +     rv = bus_dmamap_load_mbuf(sc->sc_dmat, md->md_map, m,
> +          BUS_DMA_READ | BUS_DMA_NOWAIT);
> +     if (rv != 0) {
> +             printf("%s: could not load data, %d\n", DEVNAME(sc), rv);
> +             m_freem(m);
> +             return (rv);
> +     }
> +
> +     bus_dmamap_sync(sc->sc_dmat, md->md_map, 0,
> +         md->md_map->dm_mapsize, BUS_DMASYNC_PREREAD);
> +
> +     md->md_data = m;
> +
> +     KASSERT(md->md_map->dm_nsegs == 1);
> +     KASSERT(md->md_map->dm_segs[0].ds_addr <= UINT32_MAX);
> +     buf0 = md->md_map->dm_segs[0].ds_addr;
> +     len0 = md->md_map->dm_segs[0].ds_len;
> +     ctrl = MT_DMA_CTL_SD_LEN0(len0);
> +     ctrl |= MT_DMA_CTL_LAST_SEC0;
> +
> +     desc->buf0 = htole32(buf0);
> +     desc->buf1 = 0;
> +     desc->info = 0;
> +     desc->ctrl = htole32(ctrl);
> +
> +     return 0;
> +}
> +
> +int
> +mwx_queue_fill(struct mwx_softc *sc, struct mwx_queue *q)
> +{
> +     u_int idx, last;
> +     int rv;
> +
> +     last = (q->mq_count + q->mq_cons - 1) % q->mq_count;
> +     idx = q->mq_prod;
> +
> +     bus_dmamap_sync(sc->sc_dmat, q->mq_map, 0, q->mq_map->dm_mapsize,
> +         BUS_DMASYNC_PREWRITE);
> +
> +     while (idx != last) {
> +             rv = mwx_buf_fill(sc, &q->mq_data[idx], &q->mq_desc[idx]);
> +             if (rv != 0) {
> +                     printf("%s: could not fill data, slot %d err %d\n",
> +                         DEVNAME(sc), idx, rv);
> +                     return rv;
> +             }
> +             idx = (idx + 1) % q->mq_count;
> +     }
> +
> +     bus_dmamap_sync(sc->sc_dmat, q->mq_map, 0, q->mq_map->dm_mapsize,
> +         BUS_DMASYNC_POSTWRITE);
> +
> +     q->mq_prod = idx;
> +     mwx_write(sc, q->mq_regbase + MT_DMA_CPU_IDX, q->mq_prod);
> +
> +     return 0;
> +}
> +
> +int
> +mwx_dma_alloc(struct mwx_softc *sc)
> +{
> +     int rv;
> +
> +     /* Stop DMA engine and reset wfsys */
> +     if ((rv = mt7921_dma_disable(sc, 1)) != 0)
> +             return rv;
> +     if ((rv = mt7921_wfsys_reset(sc)) != 0)
> +             return rv;
> +
> +     /* TX queues */
> +     if ((rv = mwx_queue_alloc(sc, &sc->sc_txq, 256,
> +         MT_TX_DATA_RING_BASE)) != 0)
> +             return rv;
> +     if ((rv = mwx_queue_alloc(sc, &sc->sc_txmcuq, 16 /* XXX */,
> +         MT_TX_MCU_RING_BASE)) != 0)
> +             return rv;
> +     if ((rv = mwx_queue_alloc(sc, &sc->sc_txfwdlq, 16 /* XXX */,
> +         MT_TX_FWDL_RING_BASE)) != 0)
> +             return rv;
> +
> +     /* RX queues */
> +     if ((rv = mwx_queue_alloc(sc, &sc->sc_rxq, 256,
> +         MT_RX_DATA_RING_BASE)) != 0 ||
> +         (rv = mwx_queue_fill(sc, &sc->sc_rxq)) != 0)
> +             return rv;
> +     if ((rv = mwx_queue_alloc(sc, &sc->sc_rxmcuq, 16 /* XXX */,
> +         MT_RX_MCU_RING_BASE)) != 0 ||
> +         (rv = mwx_queue_fill(sc, &sc->sc_rxmcuq)) != 0)
> +             return rv;
> +     if ((rv = mwx_queue_alloc(sc, &sc->sc_rxfwdlq, 16 /* XXX */,
> +         MT_RX_FWDL_RING_BASE)) != 0 ||
> +         (rv = mwx_queue_fill(sc, &sc->sc_rxfwdlq)) != 0)
> +             return rv;
> +
> +     /* enable DMA engine */
> +     mt7921_dma_enable(sc);
> +
> +     return 0;
> +}
> +
> +int
> +mwx_dma_reset(struct mwx_softc *sc, int fullreset)
> +{
> +     int rv;
> +
> +     DPRINTF("%s: DMA reset\n", DEVNAME(sc));
> +
> +     if ((rv = mt7921_dma_disable(sc, fullreset)) != 0)
> +             return rv;
> +     if (fullreset)
> +             if ((rv = mt7921_wfsys_reset(sc)) != 0)
> +                     return rv;
> +
> +     /* TX queues */
> +     mwx_queue_reset(sc, &sc->sc_txq);
> +     mwx_queue_reset(sc, &sc->sc_txmcuq);
> +     mwx_queue_reset(sc, &sc->sc_txfwdlq);
> +
> +     /* RX queues */
> +     mwx_queue_reset(sc, &sc->sc_rxq);
> +     mwx_queue_reset(sc, &sc->sc_rxmcuq);
> +     mwx_queue_reset(sc, &sc->sc_rxfwdlq);
> +
> +     /* TDOD mt76_tx_status_check */
> +
> +     /* refill RX queues */
> +     if ((rv = mwx_queue_fill(sc, &sc->sc_rxq)) != 0 ||
> +         (rv = mwx_queue_fill(sc, &sc->sc_rxmcuq)) != 0 ||
> +         (rv = mwx_queue_fill(sc, &sc->sc_rxfwdlq)) != 0)
> +             return rv;
> +
> +     /* enable DMA engine */
> +     mt7921_dma_enable(sc);
> +
> +     return 0;
> +}
> +
> +void
> +mwx_dma_free(struct mwx_softc *sc)
> +{
> +     /* TX queues */
> +     mwx_queue_free(sc, &sc->sc_txq);
> +     mwx_queue_free(sc, &sc->sc_txmcuq);
> +     mwx_queue_free(sc, &sc->sc_txfwdlq);
> +
> +     /* RX queues */
> +     mwx_queue_free(sc, &sc->sc_rxq);
> +     mwx_queue_free(sc, &sc->sc_rxmcuq);
> +     mwx_queue_free(sc, &sc->sc_rxfwdlq);
> +}
> +
> +static inline int
> +mwx_dma_free_slots(struct mwx_queue *q)
> +{
> +     int free = q->mq_count - 1;
> +     free += q->mq_cons;
> +     free -= q->mq_prod;
> +     free %= q->mq_count;
> +     return free;
> +}
> +
> +int
> +mwx_dma_tx_enqueue(struct mwx_softc *sc, struct mwx_queue *q, struct mbuf *m,
> +    uint32_t tx_info)
> +{
> +     struct mwx_data *md;
> +     struct mt76_desc *desc;
> +     int i, nsegs, idx, rv;
> +
> +     idx = q->mq_prod;
> +     md = &q->mq_data[idx];
> +     desc = &q->mq_desc[idx];
> +
> +     rv = bus_dmamap_load_mbuf(sc->sc_dmat, md->md_map, m,
> +         BUS_DMA_WRITE | BUS_DMA_NOWAIT);
> +     if (rv == EFBIG && m_defrag(m, M_DONTWAIT) == 0)
> +             rv = bus_dmamap_load_mbuf(sc->sc_dmat, md->md_map, m,
> +                 BUS_DMA_WRITE | BUS_DMA_NOWAIT);
> +     if (rv != 0)
> +             return rv;
> +
> +     nsegs = md->md_map->dm_nsegs;
> +
> +     /* check if there is enough space */
> +     if ((nsegs + 1)/2 > mwx_dma_free_slots(q)) {
> +             bus_dmamap_unload(sc->sc_dmat, md->md_map);
> +             return EBUSY;
> +     }
> +
> +     bus_dmamap_sync(sc->sc_dmat, md->md_map, 0, md->md_map->dm_mapsize,
> +         BUS_DMASYNC_PREWRITE);
> +     md->md_data = m;
> +
> +     bus_dmamap_sync(sc->sc_dmat, q->mq_map, 0, q->mq_map->dm_mapsize,
> +         BUS_DMASYNC_PREWRITE);
> +     for (i = 0; i < nsegs; i += 2) {
> +             uint32_t        buf0, buf1 = 0;
> +             uint32_t        len0, len1 = 0, ctrl;
> +
> +             KASSERT(md->md_map->dm_segs[i].ds_addr <= UINT32_MAX);
> +             buf0 = md->md_map->dm_segs[i].ds_addr;
> +             len0 = md->md_map->dm_segs[i].ds_len;
> +             ctrl = MT_DMA_CTL_SD_LEN0(len0);
> +
> +             if (i < nsegs - 1) {
> +                     KASSERT(md->md_map->dm_segs[i + 1].ds_addr <=
> +                         UINT32_MAX);
> +                     buf1 = md->md_map->dm_segs[i + 1].ds_addr;
> +                     len1 = md->md_map->dm_segs[i + 1].ds_len;
> +                     ctrl |= MT_DMA_CTL_SD_LEN1(len1);
> +             }
> +
> +             if (i == nsegs - 1)
> +                     ctrl |= MT_DMA_CTL_LAST_SEC0;
> +             else if (i == nsegs - 2)
> +                     ctrl |= MT_DMA_CTL_LAST_SEC1;
> +
> +             desc->buf0 = htole32(buf0);
> +             desc->buf1 = htole32(buf1);
> +             desc->info = htole32(tx_info);
> +             desc->ctrl = htole32(ctrl);
> +
> +             idx = (idx + 1) % q->mq_count;
> +             KASSERT(idx != q->mq_cons);
> +             md = &q->mq_data[idx];
> +             desc = &q->mq_desc[idx];
> +     }
> +     bus_dmamap_sync(sc->sc_dmat, q->mq_map, 0, q->mq_map->dm_mapsize,
> +         BUS_DMASYNC_POSTWRITE);
> +
> +     q->mq_prod = idx;
> +
> +     mwx_write(sc, q->mq_regbase + MT_DMA_CPU_IDX, q->mq_prod);
> +
> +     return 0;
> +}
> +
> +void
> +mwx_dma_tx_cleanup(struct mwx_softc *sc, struct mwx_queue *q)
> +{
> +     struct mwx_data *md;
> +     struct mt76_desc *desc;
> +     int idx, last;
> +
> +     idx = q->mq_cons;
> +     last = mwx_read(sc, q->mq_regbase + MT_DMA_DMA_IDX);
> +
> +     if (idx == last)
> +             return;
> +
> +     bus_dmamap_sync(sc->sc_dmat, q->mq_map, 0, q->mq_map->dm_mapsize,
> +          BUS_DMASYNC_PREWRITE);
> +
> +     while (idx != last) {
> +             md = &q->mq_data[idx];
> +             desc = &q->mq_desc[idx];
> +
> +             if (md->md_data != NULL) {
> +                     bus_dmamap_sync(sc->sc_dmat, md->md_map, 0,
> +                         md->md_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
> +                     bus_dmamap_unload(sc->sc_dmat, md->md_map);
> +                     m_freem(md->md_data);
> +                     md->md_data = NULL;
> +             }
> +
> +             /* clear DMA desc just to be sure */
> +             desc->buf0 = 0;
> +             desc->buf1 = 0;
> +             desc->info = 0;
> +             desc->ctrl = htole32(MT_DMA_CTL_DMA_DONE);
> +
> +             idx = (idx + 1) % q->mq_count;
> +
> +             /* check if more data made it in */
> +             /* XXX should we actually do that? */
> +             if (idx == last)
> +                     last = mwx_read(sc, q->mq_regbase + MT_DMA_DMA_IDX);
> +     }
> +
> +     bus_dmamap_sync(sc->sc_dmat, q->mq_map, 0, q->mq_map->dm_mapsize,
> +         BUS_DMASYNC_POSTWRITE);
> +
> +     q->mq_cons = idx;
> +     if (q->mq_wakeme) {
> +             q->mq_wakeme = 0;
> +             wakeup(q);
> +     }
> +}
> +
> +void
> +mwx_dma_tx_done(struct mwx_softc *sc)
> +{
> +     mwx_dma_tx_cleanup(sc, &sc->sc_txq);
> +     mwx_dma_tx_cleanup(sc, &sc->sc_txmcuq);
> +     mwx_dma_tx_cleanup(sc, &sc->sc_txfwdlq);
> +
> +     /* XXX do we need to wakeup someone */
> +}
> +
> +/* XXX wrong place */
> +void
> +mwx_dma_rx_process(struct mwx_softc *sc, struct mbuf_list *ml)
> +{
> +     struct mbuf_list mlout = MBUF_LIST_INITIALIZER();
> +     struct mbuf *m;
> +     uint32_t *data, rxd, type, flag;
> +
> +     while ((m = ml_dequeue(ml)) != NULL) {
> +             data = mtod(m, uint32_t *);
> +             rxd = le32toh(data[0]);
> +
> +             type = MT_RXD0_PKT_TYPE_GET(rxd);
> +             flag = (rxd & MT_RXD0_PKT_FLAG_MASK) >> MT_RXD0_PKT_FLAG_SHIFT;
> +
> +             if (type == PKT_TYPE_RX_EVENT && flag == 0x1)
> +                     type = PKT_TYPE_NORMAL_MCU;
> +
> +             switch (type) {
> +             case PKT_TYPE_RX_EVENT:
> +                     mwx_mcu_rx_event(sc, m);
> +                     break;
> +#if TODO
> +             case PKT_TYPE_TXRX_NOTIFY:
> +                     mt7921_mac_tx_free(sc, m);
> +                     break;
> +             case PKT_TYPE_TXS:
> +                     for (rxd += 2; rxd + 8 <= end; rxd += 8)
> +                             mt7921_mac_add_txs(dev, rxd);
> +                     m_freem(m);
> +                     break;
> +#endif
> +             case PKT_TYPE_NORMAL_MCU:
> +             case PKT_TYPE_NORMAL:
> +                     mwx_rx(sc, m, &mlout);
> +                     break;
> +             default:
> +                     if (DEVDEBUG(sc))
> +                             printf("%s: recevied unknown pkt type %d\n",
> +                                 DEVNAME(sc), type);
> +                     m_freem(m);
> +                     break;
> +             }
> +     }
> +
> +     if_input(&sc->sc_ic.ic_if, &mlout);
> +}
> +
> +void
> +mwx_dma_rx_dequeue(struct mwx_softc *sc, struct mwx_queue *q,
> +    struct mbuf_list *ml)
> +{
> +     struct mwx_data *md;
> +     struct mt76_desc *desc;
> +     struct mbuf *m, *m0 = NULL, *mtail = NULL;
> +     int idx, last;
> +
> +     idx = q->mq_cons;
> +     last = mwx_read(sc, q->mq_regbase + MT_DMA_DMA_IDX);
> +
> +     if (idx == last)
> +             return;
> +
> +     bus_dmamap_sync(sc->sc_dmat, q->mq_map, 0, q->mq_map->dm_mapsize,
> +          BUS_DMASYNC_PREREAD);
> +
> +     while (idx != last) {
> +             uint32_t ctrl;
> +
> +             md = &q->mq_data[idx];
> +             desc = &q->mq_desc[idx];
> +
> +             bus_dmamap_sync(sc->sc_dmat, md->md_map, 0,
> +                 md->md_map->dm_mapsize, BUS_DMASYNC_POSTREAD);
> +             bus_dmamap_unload(sc->sc_dmat, md->md_map);
> +
> +             /* dequeue mbuf */
> +             m = md->md_data;
> +             md->md_data = NULL;
> +
> +             /* only buf0 is used on RX rings */
> +             ctrl = le32toh(desc->ctrl);
> +             m->m_len = MT_DNA_CTL_SD_GET_LEN0(ctrl);
> +
> +             if (m0 == NULL) {
> +                     m0 = mtail = m;
> +                     m0->m_pkthdr.len = m->m_len;
> +             } else {
> +                     mtail->m_next = m;
> +                     mtail = m;
> +                     m0->m_pkthdr.len += m->m_len;
> +             }
> +
> +             /* TODO handle desc->info */
> +
> +             /* check if this was the last mbuf of the chain */
> +             if (ctrl & MT_DMA_CTL_LAST_SEC0) {
> +                     ml_enqueue(ml, m0);
> +                     m0 = NULL;
> +                     mtail = NULL;
> +             }
> +
> +             idx = (idx + 1) % q->mq_count;
> +
> +             /* check if more data made it in */
> +             /* XXX should we actually do that? */
> +             if (idx == last)
> +                     last = mwx_read(sc, q->mq_regbase + MT_DMA_DMA_IDX);
> +     }
> +
> +     /* XXX make sure we don't have half processed data */
> +     KASSERT(m0 == NULL);
> +
> +     bus_dmamap_sync(sc->sc_dmat, q->mq_map, 0, q->mq_map->dm_mapsize,
> +         BUS_DMASYNC_POSTREAD);
> +
> +     q->mq_cons = idx;
> +}
> +
> +void
> +mwx_dma_rx_done(struct mwx_softc *sc, struct mwx_queue *q)
> +{
> +     struct mbuf_list ml = MBUF_LIST_INITIALIZER();
> +
> +     mwx_dma_rx_dequeue(sc, q, &ml);
> +
> +     if (ml_empty(&ml))
> +             return;
> +
> +     mwx_queue_fill(sc, q);  /* TODO what if it fails, run timer? */
> +
> +     mwx_dma_rx_process(sc, &ml);
> +}
> +
> +int
> +mwx_mcu_send_msg(struct mwx_softc *sc, uint32_t cmd, void *data, size_t len,
> +    int *seqp)
> +{
> +     const int headspace = sizeof(struct mt7921_mcu_txd);
> +     struct mbuf *m;
> +     struct mt7921_uni_txd *uni_txd;
> +     struct mt7921_mcu_txd *mcu_txd;
> +     struct mwx_queue *q;
> +     uint32_t *txd, val;
> +     int s, rv, txd_len, mcu_cmd = cmd & MCU_CMD_FIELD_ID_MASK;
> +     uint8_t seq;
> +
> +     /* Allocate mbuf with enough space */
> +     m = m_gethdr(MT_DATA, M_DONTWAIT);
> +     if (m == NULL)
> +             return ENOMEM;
> +     if (len + headspace > MHLEN) {
> +             m_clget(m, M_DONTWAIT, len + sizeof(*mcu_txd));
> +             if (!ISSET(m->m_flags, M_EXT)) {
> +                     m_freem(m);
> +                     return ENOMEM;
> +             }
> +     }
> +
> +     m_align(m, len + headspace);
> +     m->m_pkthdr.len = m->m_len = len + headspace;
> +     m_adj(m, headspace);
> +     if (len != 0)
> +             memcpy(mtod(m, caddr_t), data, len);
> +
> +     if (cmd == MCU_CMD_FW_SCATTER) {
> +             q = &sc->sc_txfwdlq;
> +             goto enqueue;
> +     }
> +
> +     seq = ++sc->sc_mcu_seq & 0x0f;
> +     if (seq == 0)
> +             seq = ++sc->sc_mcu_seq & 0x0f;
> +
> +     txd_len = cmd & MCU_CMD_FIELD_UNI ? sizeof(*uni_txd) : sizeof(*mcu_txd);
> +     m = m_prepend(m, txd_len, M_DONTWAIT);
> +     txd = mtod(m, uint32_t *);
> +     memset(txd, 0, txd_len);
> +
> +     val = (m->m_len & MT_TXD0_TX_BYTES_MASK) |
> +         MT_TX_TYPE_CMD | MT_TXD0_Q_IDX(MT_TX_MCU_PORT_RX_Q0);
> +     txd[0] = htole32(val);
> +
> +     val = MT_TXD1_LONG_FORMAT | MT_HDR_FORMAT_CMD;
> +     txd[1] = htole32(val);
> +
> +     if (cmd & MCU_CMD_FIELD_UNI) {
> +             uni_txd = (struct mt7921_uni_txd *)txd;
> +             uni_txd->len = htole16(len);
> +             uni_txd->option = MCU_CMD_UNI_EXT_ACK;
> +             uni_txd->cid = htole16(mcu_cmd);
> +             uni_txd->s2d_index = CMD_S2D_IDX_H2N;
> +             uni_txd->pkt_type = MCU_PKT_ID;
> +             uni_txd->seq = seq;
> +     } else {
> +             mcu_txd = (struct mt7921_mcu_txd *)txd;
> +             mcu_txd->len = htole16(len);
> +             mcu_txd->pq_id = htole16(MCU_PQ_ID(MT_TX_PORT_IDX_MCU,
> +                     MT_TX_MCU_PORT_RX_Q0));
> +             mcu_txd->pkt_type = MCU_PKT_ID;
> +             mcu_txd->seq = seq;
> +             mcu_txd->cid = mcu_cmd;
> +             mcu_txd->s2d_index = CMD_S2D_IDX_H2N;
> +             mcu_txd->ext_cid = MCU_GET_EXT_CMD(cmd);
> +
> +             if (mcu_txd->ext_cid || (cmd & MCU_CMD_FIELD_CE)) {
> +                     if (cmd & MCU_CMD_FIELD_QUERY)
> +                             mcu_txd->set_query = MCU_Q_QUERY;
> +                     else
> +                             mcu_txd->set_query = MCU_Q_SET;
> +                     mcu_txd->ext_cid_ack = !!mcu_txd->ext_cid;
> +             } else {
> +                     mcu_txd->set_query = MCU_Q_NA;
> +             }
> +     }
> +
> +     if (seqp != NULL)
> +             *seqp = seq;
> +     q = &sc->sc_txmcuq;
> +enqueue:
> +     s = splnet();
> +     while (1) {
> +             rv = mwx_dma_tx_enqueue(sc, q, m, 0);
> +             if (rv != EBUSY)
> +                     break;
> +             q->mq_wakeme = 1;
> +             tsleep_nsec(q, 0, "mwxq", MSEC_TO_NSEC(100));
> +     }
> +     splx(s);
> +     return rv;
> +}
> +
> +int
> +mwx_mcu_send_wait(struct mwx_softc *sc, uint32_t cmd, void *data, size_t len)
> +{
> +     int rv, seq;
> +
> +     rv = mwx_mcu_send_msg(sc, cmd, data, len, &seq);
> +     if (rv != 0)
> +             return rv;
> +     return mwx_mcu_wait_resp_int(sc, cmd, seq, NULL);
> +}
> +
> +void
> +mwx_mcu_rx_event(struct mwx_softc *sc, struct mbuf *m)
> +{
> +     struct mt7921_mcu_rxd *rxd;
> +     uint32_t cmd, mcu_int = 0;
> +     int len;
> +
> +     if ((m = m_pullup(m, sizeof(*rxd))) == NULL)
> +             return;
> +     rxd = mtod(m, struct mt7921_mcu_rxd *);
> +
> +     if (rxd->ext_eid == MCU_EXT_EVENT_RATE_REPORT) {
> +             printf("%s: MCU_EXT_EVENT_RATE_REPORT COMMAND\n", DEVNAME(sc));
> +             m_freem(m);
> +             return;
> +     }
> +
> +     len = sizeof(*rxd) - sizeof(rxd->rxd) + le16toh(rxd->len);
> +     /* make sure all the data is in one mbuf */
> +     if ((m = m_pullup(m, len)) == NULL) {
> +             printf("%s: mwx_mcu_rx_event m_pullup failed\n", DEVNAME(sc));
> +             return;
> +     }
> +     /* refetch after pullup */
> +     rxd = mtod(m, struct mt7921_mcu_rxd *);
> +     m_adj(m, sizeof(*rxd));
> +
> +     switch (rxd->eid) {
> +     case MCU_EVENT_SCHED_SCAN_DONE:
> +     case MCU_EVENT_SCAN_DONE:
> +             mt7921_mcu_scan_event(sc, m);
> +             return;
> +#if 0
> +     case MCU_EVENT_BSS_BEACON_LOSS:
> +             mt7921_mcu_connection_loss_event(dev, skb);
> +             break;
> +     case MCU_EVENT_BSS_ABSENCE:
> +             mt7921_mcu_bss_event(dev, skb);
> +             break;
> +     case MCU_EVENT_DBG_MSG:
> +             mt7921_mcu_debug_msg_event(dev, skb);
> +             break;
> +#endif
> +     case MCU_EVENT_COREDUMP:
> +             /* it makes little sense to write the coredump down */
> +             if (!sc->sc_resetting)
> +                     printf("%s: coredump event\n", DEVNAME(sc));
> +             mwx_reset(sc);
> +             return;
> +     case MCU_EVENT_LP_INFO:
> +             mt7921_mcu_low_power_event(sc, m);
> +             break;
> +     case MCU_EVENT_TX_DONE:
> +             mt7921_mcu_tx_done_event(sc, m);
> +             break;
> +     case 0x6:
> +             printf("%s: MAGIC COMMAND\n", DEVNAME(sc));
> +     default:
> +             if (rxd->seq == 0 || rxd->seq >= nitems(sc->sc_mcu_wait)) {
> +                     printf("%s: mcu rx bad seq %x\n", DEVNAME(sc),
> +                         rxd->seq);
> +                     m_freem(m);
> +                     break;
> +             }
> +
> +             cmd = sc->sc_mcu_wait[rxd->seq].mcu_cmd;
> +
> +             if (cmd == MCU_CMD_PATCH_SEM_CONTROL) {
> +                     /* XXX this is a terrible abuse */
> +                     m = m_prepend(m, sizeof(uint32_t), M_DONTWAIT);
> +                     mcu_int = *mtod(m, uint8_t *);
> +             } else if (cmd == MCU_EXT_CMD_THERMAL_CTRL) {
> +                     if (m->m_len < sizeof(uint32_t) * 2)
> +                             return;
> +                     mcu_int = le32toh(mtod(m, uint32_t *)[1]);
> +             } else if (cmd == MCU_EXT_CMD_EFUSE_ACCESS) {
> +                     //ret = mt7921_mcu_parse_eeprom(sc, m);
> +                     printf("%s: mcu resp no handled yet\n", DEVNAME(sc));
> +             } else if (cmd == MCU_UNI_CMD_DEV_INFO_UPDATE ||
> +                 cmd == MCU_UNI_CMD_BSS_INFO_UPDATE ||
> +                 cmd == MCU_UNI_CMD_STA_REC_UPDATE ||
> +                 cmd == MCU_UNI_CMD_HIF_CTRL ||
> +                 cmd == MCU_UNI_CMD_OFFLOAD ||
> +                 cmd == MCU_UNI_CMD_SUSPEND) {
> +                     struct mt7921_mcu_uni_event *event;
> +
> +                     if (m->m_len < sizeof(*event))
> +                             return;
> +                     event = mtod(m, struct mt7921_mcu_uni_event *);
> +                     mcu_int = le32toh(event->status);
> +             } else if (cmd == MCU_CE_QUERY_REG_READ) {
> +                     struct mt7921_mcu_reg_event *event;
> +
> +                     if (m->m_len < sizeof(*event))
> +                             return;
> +                     event = mtod(m, struct mt7921_mcu_reg_event *);
> +                     mcu_int = le32toh(event->val);
> +             }
> +
> +             sc->sc_mcu_wait[rxd->seq].mcu_int = mcu_int;
> +             sc->sc_mcu_wait[rxd->seq].mcu_m = m;
> +             wakeup(&sc->sc_mcu_wait[rxd->seq]);
> +             break;
> +     }
> +}
> +
> +int
> +mwx_mcu_wait_resp_int(struct mwx_softc *sc, uint32_t cmd, int seq,
> +    uint32_t *val)
> +{
> +     int rv;
> +
> +     KASSERT(seq < nitems(sc->sc_mcu_wait));
> +
> +     memset(&sc->sc_mcu_wait[seq], 0, sizeof(sc->sc_mcu_wait[0]));
> +     sc->sc_mcu_wait[seq].mcu_cmd = cmd;
> +
> +     rv = tsleep_nsec(&sc->sc_mcu_wait[seq], 0, "mwxwait", SEC_TO_NSEC(3));
> +     if (rv != 0) {
> +             printf("%s: command %x timeout\n", DEVNAME(sc), cmd);
> +             mwx_reset(sc);
> +             return rv;
> +     }
> +
> +     if (sc->sc_mcu_wait[seq].mcu_m != NULL) {
> +             m_freem(sc->sc_mcu_wait[seq].mcu_m);
> +             sc->sc_mcu_wait[seq].mcu_m = NULL;
> +     }
> +     if (val != NULL)
> +             *val = sc->sc_mcu_wait[seq].mcu_int;
> +     return 0;
> +}
> +
> +int
> +mwx_mcu_wait_resp_msg(struct mwx_softc *sc, uint32_t cmd, int seq,
> +    struct mbuf **mp)
> +{
> +     int rv;
> +
> +     KASSERT(seq < nitems(sc->sc_mcu_wait));
> +
> +     memset(&sc->sc_mcu_wait[seq], 0, sizeof(sc->sc_mcu_wait[0]));
> +     sc->sc_mcu_wait[seq].mcu_cmd = cmd;
> +
> +     rv = tsleep_nsec(&sc->sc_mcu_wait[seq], 0, "mwxwait", SEC_TO_NSEC(3));
> +     if (rv != 0) {
> +             printf("%s: command %x timeout\n", DEVNAME(sc), cmd);
> +             mwx_reset(sc);
> +             return rv;
> +     }
> +     if (sc->sc_mcu_wait[seq].mcu_m == NULL) {
> +             printf("%s: command respone missing\n", DEVNAME(sc));
> +             return ENOENT;
> +     }
> +     if (mp != NULL)
> +             *mp = sc->sc_mcu_wait[seq].mcu_m;
> +     else
> +             m_freem(sc->sc_mcu_wait[seq].mcu_m);
> +     sc->sc_mcu_wait[seq].mcu_m = NULL;
> +     return 0;
> +}
> +
> +int
> +mt7921_dma_disable(struct mwx_softc *sc, int force)
> +{
> +     /* disable WFDMA0 */
> +     mwx_clear(sc, MT_WFDMA0_GLO_CFG,
> +         MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN |
> +         MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN |
> +         MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
> +         MT_WFDMA0_GLO_CFG_OMIT_RX_INFO |
> +         MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
> +
> +     if (force) {
> +             /* reset */
> +             mwx_clear(sc, MT_WFDMA0_RST, MT_WFDMA0_RST_DMASHDL_ALL_RST |
> +                 MT_WFDMA0_RST_LOGIC_RST);
> +             mwx_set(sc, MT_WFDMA0_RST, MT_WFDMA0_RST_DMASHDL_ALL_RST |
> +                 MT_WFDMA0_RST_LOGIC_RST);
> +     }
> +
> +     /* disable dmashdl */
> +     mwx_clear(sc, MT_WFDMA0_GLO_CFG_EXT0, MT_WFDMA0_CSR_TX_DMASHDL_ENABLE);
> +     mwx_set(sc, MT_DMASHDL_SW_CONTROL, MT_DMASHDL_DMASHDL_BYPASS);
> +
> +     return mwx_poll(sc, MT_WFDMA0_GLO_CFG, 0,
> +         MT_WFDMA0_GLO_CFG_TX_DMA_BUSY | MT_WFDMA0_GLO_CFG_RX_DMA_BUSY,
> +         1000);
> +}
> +
> +void
> +mt7921_dma_enable(struct mwx_softc *sc)
> +{
> +#define PREFETCH(base, depth)   ((base) << 16 | (depth))
> +     /* configure perfetch settings */
> +     mwx_write(sc, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0, 0x4));
> +     mwx_write(sc, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x40, 0x4));
> +     mwx_write(sc, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x80, 0x4));
> +     mwx_write(sc, MT_WFDMA0_RX_RING4_EXT_CTRL, PREFETCH(0xc0, 0x4));
> +     mwx_write(sc, MT_WFDMA0_RX_RING5_EXT_CTRL, PREFETCH(0x100, 0x4));
> +
> +     mwx_write(sc, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x140, 0x4));
> +     mwx_write(sc, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x180, 0x4));
> +     mwx_write(sc, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x1c0, 0x4));
> +     mwx_write(sc, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x200, 0x4));
> +     mwx_write(sc, MT_WFDMA0_TX_RING4_EXT_CTRL, PREFETCH(0x240, 0x4));
> +     mwx_write(sc, MT_WFDMA0_TX_RING5_EXT_CTRL, PREFETCH(0x280, 0x4));
> +     mwx_write(sc, MT_WFDMA0_TX_RING6_EXT_CTRL, PREFETCH(0x2c0, 0x4));
> +     mwx_write(sc, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x340, 0x4));
> +     mwx_write(sc, MT_WFDMA0_TX_RING17_EXT_CTRL, PREFETCH(0x380, 0x4));
> +
> +     /* reset dma idx */
> +     mwx_write(sc, MT_WFDMA0_RST_DTX_PTR, ~0);
> +
> +     /* configure delay interrupt */
> +     mwx_write(sc, MT_WFDMA0_PRI_DLY_INT_CFG0, 0);
> +
> +     mwx_set(sc, MT_WFDMA0_GLO_CFG,
> +         MT_WFDMA0_GLO_CFG_TX_WB_DDONE |
> +         MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN |
> +         MT_WFDMA0_GLO_CFG_CLK_GAT_DIS |
> +         MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
> +         MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN |
> +         MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
> +
> +     mwx_barrier(sc);
> +     mwx_set(sc, MT_WFDMA0_GLO_CFG,
> +         MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN);
> +
> +     mwx_set(sc, MT_WFDMA_DUMMY_CR, MT_WFDMA_NEED_REINIT);
> +
> +     /* enable interrupts for TX/RX rings */
> +     mwx_write(sc, MT_WFDMA0_HOST_INT_ENA, MT_INT_RX_DONE_ALL |
> +         MT_INT_TX_DONE_ALL | MT_INT_MCU_CMD);
> +     mwx_set(sc, MT_MCU2HOST_SW_INT_ENA, MT_MCU_CMD_WAKE_RX_PCIE);
> +     mwx_write(sc, MT_PCIE_MAC_INT_ENABLE, 0xff);
> +}
> +
> +int
> +mt7921_wfsys_reset(struct mwx_softc *sc)
> +{
> +     DPRINTF("%s: WFSYS reset\n", DEVNAME(sc));
> +
> +     mwx_clear(sc, MT_WFSYS_SW_RST_B, WFSYS_SW_RST_B);
> +     delay(50 * 1000);
> +     mwx_set(sc, MT_WFSYS_SW_RST_B, WFSYS_SW_RST_B);
> +
> +     return mwx_poll(sc, MT_WFSYS_SW_RST_B, WFSYS_SW_INIT_DONE,
> +         WFSYS_SW_INIT_DONE, 500);
> +}
> +
> +/*
> + * To be honest this is ridiculous.
> + */
> +uint32_t
> +mt7921_reg_addr(struct mwx_softc *sc, uint32_t reg)
> +{
> +     static const struct {
> +             uint32_t phys;
> +             uint32_t mapped;
> +             uint32_t size;
> +     } fixed_map[] = {
> +     { 0x820d0000, 0x30000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */
> +     { 0x820ed000, 0x24800, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */
> +     { 0x820e4000, 0x21000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */
> +     { 0x820e7000, 0x21e00, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */
> +     { 0x820eb000, 0x24200, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */
> +     { 0x820e2000, 0x20800, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */
> +     { 0x820e3000, 0x20c00, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */
> +     { 0x820e5000, 0x21400, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */
> +     { 0x00400000, 0x80000, 0x10000 }, /* WF_MCU_SYSRAM */
> +     { 0x00410000, 0x90000, 0x10000 }, /* WF_MCU_SYSRAM (conf register) */
> +     { 0x40000000, 0x70000, 0x10000 }, /* WF_UMAC_SYSRAM */
> +     { 0x54000000, 0x02000, 0x1000 }, /* WFDMA PCIE0 MCU DMA0 */
> +     { 0x55000000, 0x03000, 0x1000 }, /* WFDMA PCIE0 MCU DMA1 */
> +     { 0x58000000, 0x06000, 0x1000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */
> +     { 0x59000000, 0x07000, 0x1000 }, /* WFDMA PCIE1 MCU DMA1 */
> +     { 0x7c000000, 0xf0000, 0x10000 }, /* CONN_INFRA */
> +     { 0x7c020000, 0xd0000, 0x10000 }, /* CONN_INFRA, WFDMA */
> +     { 0x7c060000, 0xe0000, 0x10000 }, /* CONN_INFRA, conn_host_csr_top */
> +     { 0x80020000, 0xb0000, 0x10000 }, /* WF_TOP_MISC_OFF */
> +     { 0x81020000, 0xc0000, 0x10000 }, /* WF_TOP_MISC_ON */
> +     { 0x820c0000, 0x08000, 0x4000 }, /* WF_UMAC_TOP (PLE) */
> +     { 0x820c8000, 0x0c000, 0x2000 }, /* WF_UMAC_TOP (PSE) */
> +     { 0x820cc000, 0x0e000, 0x1000 }, /* WF_UMAC_TOP (PP) */
> +     { 0x820cd000, 0x0f000, 0x1000 }, /* WF_MDP_TOP */
> +     { 0x820ce000, 0x21c00, 0x0200 }, /* WF_LMAC_TOP (WF_SEC) */
> +     { 0x820cf000, 0x22000, 0x1000 }, /* WF_LMAC_TOP (WF_PF) */
> +     { 0x820e0000, 0x20000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */
> +     { 0x820e1000, 0x20400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */
> +     { 0x820e9000, 0x23400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */
> +     { 0x820ea000, 0x24000, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */
> +     { 0x820ec000, 0x24600, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_INT) */
> +     { 0x820f0000, 0xa0000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */
> +     { 0x820f1000, 0xa0600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */
> +     { 0x820f2000, 0xa0800, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */
> +     { 0x820f3000, 0xa0c00, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */
> +     { 0x820f4000, 0xa1000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */
> +     { 0x820f5000, 0xa1400, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */
> +     { 0x820f7000, 0xa1e00, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */
> +     { 0x820f9000, 0xa3400, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */
> +     { 0x820fa000, 0xa4000, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */
> +     { 0x820fb000, 0xa4200, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */
> +     { 0x820fc000, 0xa4600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_INT) */
> +     { 0x820fd000, 0xa4800, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */
> +     };
> +     int i;
> +
> +     if (reg < 0x100000)
> +             return reg;
> +
> +     for (i = 0; i < nitems(fixed_map); i++) {
> +             uint32_t ofs;
> +
> +             if (reg < fixed_map[i].phys)
> +                     continue;
> +
> +             ofs = reg - fixed_map[i].phys;
> +             if (ofs > fixed_map[i].size)
> +                     continue;
> +
> +             return fixed_map[i].mapped + ofs;
> +     }
> +
> +     if ((reg >= 0x18000000 && reg < 0x18c00000) ||
> +         (reg >= 0x70000000 && reg < 0x78000000) ||
> +         (reg >= 0x7c000000 && reg < 0x7c400000))
> +             return mwx_map_reg_l1(sc, reg);
> +
> +     panic("%s: Access to currently unsupported address %08x\n",
> +        DEVNAME(sc), reg);
> +}
> +
> +int
> +mt7921_init_hardware(struct mwx_softc *sc)
> +{
> +     int rv;
> +
> +     /* reset dma */
> +     rv = mwx_dma_reset(sc, 1);
> +     if (rv != 0)
> +             return rv;
> +
> +     /*
> +      * force firmware operation mode into normal state,
> +      * which should be set before firmware download stage.
> +      */
> +     mwx_write(sc, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE);
> +     mwx_barrier(sc);
> +
> +     rv = mt7921_mcu_init(sc);
> +     if (rv != 0)
> +             goto fail;
> +     /* TODO override eeprom for systems with FDT */
> +     rv = mt7921_mcu_set_eeprom(sc);
> +     if (rv != 0)
> +             goto fail;
> +     rv = mt7921_mac_init(sc);
> +     if (rv != 0)
> +             goto fail;
> +
> +     /* MAYBE alloc beacon and mgmt frame wcid 0 here */
> +
> +     return 0;
> +
> + fail:
> +     /* reset dma */
> +     rv = mwx_dma_reset(sc, 1);
> +     if (rv != 0)
> +             return rv;
> +     return EAGAIN;
> +}
> +
> +int
> +mt7921_mcu_init(struct mwx_softc *sc)
> +{
> +     int rv;
> +
> +     /* this read is needed to make interrupts work */
> +     (void) mwx_read(sc, MT_TOP_LPCR_HOST_BAND0);
> +     mwx_write(sc, MT_TOP_LPCR_HOST_BAND0, MT_TOP_LPCR_HOST_DRV_OWN);
> +     if (mwx_poll(sc, MT_TOP_LPCR_HOST_BAND0, 0, MT_TOP_LPCR_HOST_FW_OWN,
> +         5000) != 0) {
> +         printf("%s: timeout for driver own\n", DEVNAME(sc));
> +         return EIO;
> +     }
> +
> +     if ((rv = mt7921_load_firmware(sc)) != 0)
> +             return rv;
> +
> +     if ((rv = mt7921_mcu_get_nic_capability(sc)) != 0)
> +             return rv;
> +     if ((rv = mt7921_mcu_fw_log_2_host(sc, 1)) != 0)
> +             return rv;
> +
> +     /* TODO mark MCU running */
> +
> +     return 0;
> +}
> +
> +static inline uint32_t
> +mt7921_get_data_mode(struct mwx_softc *sc, uint32_t info)
> +{
> +     uint32_t mode = DL_MODE_NEED_RSP;
> +
> +     if (info == PATCH_SEC_NOT_SUPPORT)
> +             return mode;
> +     switch (info & PATCH_SEC_ENC_TYPE_MASK) {
> +     case PATCH_SEC_ENC_TYPE_PLAIN:
> +             break;
> +     case PATCH_SEC_ENC_TYPE_AES:
> +             mode |= DL_MODE_ENCRYPT;
> +             mode |= (info << DL_MODE_KEY_IDX_SHIFT) & DL_MODE_KEY_IDX_MASK;
> +             mode |= DL_MODE_RESET_SEC_IV;
> +             break;
> +     case PATCH_SEC_ENC_TYPE_SCRAMBLE:
> +             mode |= DL_MODE_ENCRYPT;
> +             mode |= DL_CONFIG_ENCRY_MODE_SEL;
> +             mode |= DL_MODE_RESET_SEC_IV;
> +             break;
> +     default:
> +             printf("%s: encryption type not supported\n", DEVNAME(sc));
> +     }
> +     return mode;
> +}
> +
> +static inline uint32_t
> +mt7921_mcu_gen_dl_mode(uint8_t feature_set)
> +{
> +     uint32_t ret = DL_MODE_NEED_RSP;
> +
> +     if (feature_set & FW_FEATURE_SET_ENCRYPT)
> +             ret |= (DL_MODE_ENCRYPT | DL_MODE_RESET_SEC_IV);
> +     if (feature_set & FW_FEATURE_ENCRY_MODE)
> +             ret |= DL_CONFIG_ENCRY_MODE_SEL;
> +
> +     /* FW_FEATURE_SET_KEY_IDX_MASK == DL_MODE_KEY_IDX_MASK */
> +     ret |= feature_set & FW_FEATURE_SET_KEY_IDX_MASK;
> +
> +     return ret;
> +}
> +
> +
> +int
> +mt7921_load_firmware(struct mwx_softc *sc)
> +{
> +     struct mt7921_patch_hdr *hdr;
> +     struct mt7921_fw_trailer *fwhdr;
> +     u_char *buf, *fwbuf, *dl;
> +     size_t buflen, fwlen, offset = 0;
> +     uint32_t reg, override = 0, option = 0;
> +     int i, rv, sem;
> +
> +     reg = mwx_read(sc, MT_CONN_ON_MISC) & MT_TOP_MISC2_FW_N9_RDY;
> +     if (reg != 0) {
> +             DPRINTF("%s: firmware already downloaded\n", DEVNAME(sc));
> +             return 0;
> +     }
> +
> +     if ((rv = loadfirmware(MT7921_ROM_PATCH, &buf, &buflen)) != 0 ||
> +         (rv = loadfirmware(MT7921_FIRMWARE_WM, &fwbuf, &fwlen)) != 0) {
> +             printf("%s: loadfirmware error %d\n", DEVNAME(sc), rv);
> +             return rv;
> +     }
> +
> +     rv = mt7921_mcu_patch_sem_ctrl(sc, 1);
> +     if (rv != 0)
> +             return rv;
> +
> +     if (buflen < sizeof(*hdr)) {
> +             DPRINTF("%s: invalid firmware\n", DEVNAME(sc));
> +             rv = EINVAL;
> +             goto out;
> +     }
> +     hdr = (struct mt7921_patch_hdr *)buf;
> +     printf("%s: HW/SW version: 0x%x, build time: %.15s\n",
> +         DEVNAME(sc), be32toh(hdr->hw_sw_ver), hdr->build_date);
> +
> +     for (i = 0; i < be32toh(hdr->desc.n_region); i++) {
> +             struct mt7921_patch_sec *sec;
> +             uint32_t len, addr, mode, sec_info;
> +
> +             sec = (struct mt7921_patch_sec *)(buf + sizeof(*hdr) +
> +                 i * sizeof(*sec));
> +             if ((be32toh(sec->type) & PATCH_SEC_TYPE_MASK) !=
> +                 PATCH_SEC_TYPE_INFO) {
> +                     DPRINTF("%s: invalid firmware sector\n", DEVNAME(sc));
> +                     rv =  EINVAL;
> +                     goto out;
> +             }
> +
> +             addr = be32toh(sec->info.addr);
> +             len = be32toh(sec->info.len);
> +             dl = buf + be32toh(sec->offs);
> +             sec_info = be32toh(sec->info.sec_key_idx);
> +             mode = mt7921_get_data_mode(sc, sec_info);
> +
> +             rv = mt7921_mcu_init_download(sc, addr, len, mode);
> +             if (rv != 0) {
> +                     DPRINTF("%s: download request failed\n", DEVNAME(sc));
> +                     goto out;
> +             }
> +             rv = mt7921_mcu_send_firmware(sc, MCU_CMD_FW_SCATTER,
> +                 dl, len, 4096);
> +             if (rv != 0) {
> +                     DPRINTF("%s: failed to send patch\n", DEVNAME(sc));
> +                     goto out;
> +             }
> +     }
> +
> +     rv = mt7921_mcu_start_patch(sc);
> +     if (rv != 0) {
> +             printf("%s: patch start failed\n", DEVNAME(sc));
> +             goto fail;
> +     }
> +
> +out:
> +     sem = mt7921_mcu_patch_sem_ctrl(sc, 0);
> +     if (sem != 0)
> +             rv = sem;
> +     if (rv != 0)
> +             goto fail;
> +
> +     fwhdr = (struct mt7921_fw_trailer *)(fwbuf + fwlen - sizeof(*fwhdr));
> +     printf("%s: WM firmware version: %.10s, build time: %.15s\n",
> +         DEVNAME(sc), fwhdr->fw_ver, fwhdr->build_date);
> +
> +     for (i = 0; i < fwhdr->n_region; i++) {
> +             struct mt7921_fw_region *region;
> +             uint32_t len, addr, mode;
> +
> +             region = (struct mt7921_fw_region *)((u_char *)fwhdr -
> +                 (fwhdr->n_region - i) * sizeof(*region));
> +
> +             addr = le32toh(region->addr);
> +             len = le32toh(region->len);
> +             mode = mt7921_mcu_gen_dl_mode(region->feature_set);
> +
> +             if (region->feature_set & FW_FEATURE_OVERRIDE_ADDR)
> +                     override = addr;
> +
> +             rv = mt7921_mcu_init_download(sc, addr, len, mode);
> +             if (rv != 0) {
> +                     DPRINTF("%s: download request failed\n", DEVNAME(sc));
> +                     goto fail;
> +             }
> +
> +             rv = mt7921_mcu_send_firmware(sc, MCU_CMD_FW_SCATTER,
> +                 fwbuf + offset, len, 4096);
> +             if (rv != 0) {
> +                     DPRINTF("%s: failed to send firmware\n", DEVNAME(sc));
> +                     goto fail;
> +             }
> +             offset += len;
> +     }
> +
> +     if (override != 0)
> +             option |= FW_START_OVERRIDE;
> +
> +     rv = mt7921_mcu_start_firmware(sc, override, option);
> +     if (rv != 0) {
> +             DPRINTF("%s: firmware start failed\n", DEVNAME(sc));
> +             goto fail;
> +     }
> +
> +     /* XXX should not busy poll here */
> +     if (mwx_poll(sc, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY,
> +         MT_TOP_MISC2_FW_N9_RDY, 1500) != 0) {
> +             DPRINTF("%s: Timeout initializing firmware\n", DEVNAME(sc));
> +             return EIO;
> +     }
> +
> +     DPRINTF("%s: firmware loaded\n", DEVNAME(sc));
> +     rv = 0;
> +
> +fail:
> +     free(buf, M_DEVBUF, buflen);
> +     free(fwbuf, M_DEVBUF, fwlen);
> +     return rv;
> +}
> +
> +int
> +mt7921_mac_wtbl_update(struct mwx_softc *sc, int idx)
> +{
> +     mwx_rmw(sc, MT_WTBL_UPDATE,
> +         (idx & MT_WTBL_UPDATE_WLAN_IDX) | MT_WTBL_UPDATE_ADM_COUNT_CLEAR,
> +         MT_WTBL_UPDATE_WLAN_IDX);
> +
> +     return mwx_poll(sc, MT_WTBL_UPDATE, 0, MT_WTBL_UPDATE_BUSY, 5000);
> +}
> +
> +void
> +mt7921_mac_init_band(struct mwx_softc *sc, uint32_t band)
> +{
> +     mwx_rmw(sc, MT_TMAC_CTCR0(band), 0x3f, MT_TMAC_CTCR0_INS_DDLMT_REFTIME);
> +     mwx_set(sc, MT_TMAC_CTCR0(band),
> +         MT_TMAC_CTCR0_INS_DDLMT_VHT_SMPDU_EN |
> +         MT_TMAC_CTCR0_INS_DDLMT_EN);
> +
> +     mwx_set(sc, MT_WF_RMAC_MIB_TIME0(band), MT_WF_RMAC_MIB_RXTIME_EN);
> +     mwx_set(sc, MT_WF_RMAC_MIB_AIRTIME0(band), MT_WF_RMAC_MIB_RXTIME_EN);
> +
> +     /* enable MIB tx-rx time reporting */
> +     mwx_set(sc, MT_MIB_SCR1(band), MT_MIB_TXDUR_EN);
> +     mwx_set(sc, MT_MIB_SCR1(band), MT_MIB_RXDUR_EN);
> +
> +     mwx_rmw(sc, MT_DMA_DCR0(band),
> +         1536 << MT_DMA_DCR0_MAX_RX_LEN_SHIFT, MT_DMA_DCR0_MAX_RX_LEN_MASK);
> +     /* disable rx rate report by default due to hw issues */
> +     mwx_clear(sc, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN);
> +}
> +
> +int
> +mt7921_mac_init(struct mwx_softc *sc)
> +{
> +     int i;
> +
> +     mwx_rmw(sc, MT_MDP_DCR1, 1536 << MT_MDP_DCR1_MAX_RX_LEN_SHIFT,
> +         MT_MDP_DCR1_MAX_RX_LEN_MASK);
> +
> +     /* enable hardware de-agg */
> +     mwx_set(sc, MT_MDP_DCR0, MT_MDP_DCR0_DAMSDU_EN);
> +#if 0
> +     /* not enabled since our stack does not handle 802.3 frames */
> +     /* enable hardware rx header translation */
> +     mwx_set(sc, MT_MDP_DCR0, MT_MDP_DCR0_RX_HDR_TRANS_EN);
> +#endif
> +
> +     for (i = 0; i < MT7921_WTBL_SIZE; i++)
> +             mt7921_mac_wtbl_update(sc, i);
> +
> +     mt7921_mac_init_band(sc, 0);
> +     mt7921_mac_init_band(sc, 1);
> +
> +     sc->sc_rxfilter = mwx_read(sc, MT_WF_RFCR(0));
> +     return mt7921_mcu_set_rts_thresh(sc, 0x92b, 0);
> +}
> +
> +int
> +mt7921_mcu_patch_sem_ctrl(struct mwx_softc *sc, int semget)
> +{
> +#define      PATCH_SEM_RELEASE               0
> +#define PATCH_SEM_GET                        1
> +#define      PATCH_NOT_DL_SEM_FAIL           0
> +#define      PATCH_IS_DL                     1
> +#define      PATCH_NOT_DL_SEM_SUCCESS        2
> +#define      PATCH_REL_SEM_SUCCESS           3
> +
> +        uint32_t op = semget ? PATCH_SEM_GET : PATCH_SEM_RELEASE;
> +     struct {
> +             uint32_t op;
> +     } req = {
> +             .op = htole32(op),
> +     };
> +     int rv, seq, sem;
> +
> +     rv = mwx_mcu_send_msg(sc, MCU_CMD_PATCH_SEM_CONTROL,
> +         &req, sizeof(req), &seq);
> +     if (rv != 0)
> +             return rv;
> +
> +     rv = mwx_mcu_wait_resp_int(sc, MCU_CMD_PATCH_SEM_CONTROL, seq, &sem);
> +     if (rv != 0)
> +             return rv;
> +
> +     if (semget) {
> +             switch (sem) {
> +             case PATCH_IS_DL:
> +                     return -1;
> +             case PATCH_NOT_DL_SEM_SUCCESS:
> +                     return 0;
> +             default:
> +                     DPRINTF("%s: failed to %s patch semaphore\n",
> +                         DEVNAME(sc), "get");
> +                     return EAGAIN;
> +             }
> +     } else {
> +             switch (sem) {
> +             case PATCH_REL_SEM_SUCCESS:
> +                     return 0;
> +             default:
> +                     DPRINTF("%s: failed to %s patch semaphore\n",
> +                         DEVNAME(sc), "release");
> +                     return EAGAIN;
> +             }
> +     }
> +}
> +
> +int
> +mt7921_mcu_init_download(struct mwx_softc *sc, uint32_t addr,
> +    uint32_t len, uint32_t mode)
> +{
> +     struct {
> +             uint32_t addr;
> +             uint32_t len;
> +             uint32_t mode;
> +     } req = {
> +             .addr = htole32(addr),
> +             .len = htole32(len),
> +             .mode = htole32(mode),
> +     };
> +     int cmd;
> +
> +     if (addr == 0x200000 || addr == 0x900000)
> +             cmd = MCU_CMD_PATCH_START_REQ;
> +     else
> +             cmd = MCU_CMD_TARGET_ADDRESS_LEN_REQ;
> +
> +     return mwx_mcu_send_wait(sc, cmd, &req, sizeof(req));
> +}
> +
> +int
> +mt7921_mcu_send_firmware(struct mwx_softc *sc, int cmd, u_char *data,
> +    size_t len, size_t max_len)
> +{
> +     size_t cur_len;
> +     int rv;
> +
> +     while (len > 0) {
> +             cur_len = len;
> +             if (cur_len > max_len)
> +                     cur_len = max_len;
> +
> +             rv = mwx_mcu_send_msg(sc, cmd, data, cur_len, NULL);
> +             if (rv != 0)
> +                     return rv;
> +
> +             data += cur_len;
> +             len -= cur_len;
> +
> +             mwx_dma_tx_cleanup(sc, &sc->sc_txfwdlq);
> +     }
> +
> +     return 0;
> +}
> +
> +int
> +mt7921_mcu_start_patch(struct mwx_softc *sc)
> +{
> +     struct {
> +             uint8_t check_crc;
> +             uint8_t reserved[3];
> +     } req = {
> +             .check_crc = 0,
> +     };
> +
> +     return mwx_mcu_send_wait(sc, MCU_CMD_PATCH_FINISH_REQ, &req,
> +         sizeof(req));
> +}
> +
> +int
> +mt7921_mcu_start_firmware(struct mwx_softc *sc, uint32_t addr, uint32_t 
> option)
> +{
> +     struct {
> +             uint32_t option;
> +             uint32_t addr;
> +     } req = {
> +             .option = htole32(option),
> +             .addr = htole32(addr),
> +     };
> +
> +     return mwx_mcu_send_wait(sc, MCU_CMD_FW_START_REQ, &req, sizeof(req));
> +}
> +
> +int
> +mt7921_mcu_get_nic_capability(struct mwx_softc *sc)
> +{
> +     struct mt76_connac_cap_hdr {
> +             uint16_t        n_elements;
> +             uint16_t        pad;
> +     } __packed *hdr;
> +     struct tlv_hdr {
> +             uint32_t        type;
> +             uint32_t        len;
> +     } __packed *tlv;
> +     struct mt76_connac_phy_cap {
> +             uint8_t         ht;
> +             uint8_t         vht;
> +             uint8_t         _5g;
> +             uint8_t         max_bw;
> +             uint8_t         nss;
> +             uint8_t         dbdc;
> +             uint8_t         tx_ldpc;
> +             uint8_t         rx_ldpc;
> +             uint8_t         tx_stbc;
> +             uint8_t         rx_stbc;
> +             uint8_t         hw_path;
> +             uint8_t         he;
> +     } __packed *cap;
> +     struct mbuf *m;
> +     int rv, seq, count, i;
> +
> +     rv = mwx_mcu_send_msg(sc, MCU_CE_CMD_GET_NIC_CAPAB, NULL, 0, &seq);
> +     if (rv != 0)
> +             return rv;
> +
> +     rv = mwx_mcu_wait_resp_msg(sc, MCU_CE_CMD_GET_NIC_CAPAB, seq, &m);
> +     if (rv != 0)
> +             return rv;
> +
> +     /* the message was allready pulled up by mwx_mcu_rx_event() */
> +     if (m->m_len < sizeof(*hdr)) {
> +             printf("%s: GET_NIC_CAPAB response size error\n", DEVNAME(sc));
> +             return EINVAL;
> +     }
> +     hdr = mtod(m, struct mt76_connac_cap_hdr *);
> +     count = le16toh(hdr->n_elements);
> +     m_adj(m, sizeof(*hdr));
> +
> +     for (i = 0; i < count; i++) {
> +             uint32_t type, len;
> +
> +             if (m->m_len < sizeof(*tlv)) {
> +                     printf("%s: GET_NIC_CAPAB tlv size error\n",
> +                         DEVNAME(sc));
> +                     return EINVAL;
> +             }
> +
> +             tlv = mtod(m, struct tlv_hdr *);
> +             type = le32toh(tlv->type);
> +             len = le32toh(tlv->len);
> +             m_adj(m, sizeof(*tlv));
> +
> +             if (m->m_len < len)
> +                     break;
> +             switch (type) {
> +             case MT_NIC_CAP_6G:
> +                     sc->sc_capa.has_6ghz = 1;
> +                     break;
> +             case MT_NIC_CAP_MAC_ADDR:
> +                     if (len < ETHER_ADDR_LEN)
> +                             break;
> +                     memcpy(sc->sc_lladdr, mtod(m, caddr_t), ETHER_ADDR_LEN);
> +                     break;
> +             case MT_NIC_CAP_PHY:
> +                     if (len < sizeof(*cap))
> +                             break;
> +                     cap = mtod(m, struct mt76_connac_phy_cap *);
> +
> +                     sc->sc_capa.num_streams = cap->nss;
> +                     sc->sc_capa.antenna_mask = (1U << cap->nss) - 1;
> +                     sc->sc_capa.has_2ghz = cap->hw_path & 0x01;
> +                     sc->sc_capa.has_5ghz = cap->hw_path & 0x02;
> +                     break;
> +             case MT_NIC_CAP_TX_RESOURCE:
> +                     /* unused on PCIe devices */
> +                     break;
> +             }
> +             m_adj(m, len);
> +     }
> +
> +        printf("%s: address %s\n", DEVNAME(sc), 
> ether_sprintf(sc->sc_lladdr));
> +
> +     m_freem(m);
> +     return 0;
> +}
> +
> +int
> +mt7921_mcu_fw_log_2_host(struct mwx_softc *sc, uint8_t ctrl)
> +{
> +     struct {
> +             uint8_t ctrl;
> +             uint8_t pad[3];
> +     } req = {
> +             .ctrl = ctrl,
> +     };
> +
> +     return mwx_mcu_send_msg(sc, MCU_CE_CMD_FWLOG_2_HOST, &req,
> +         sizeof(req), NULL);
> +}
> +
> +int
> +mt7921_mcu_set_eeprom(struct mwx_softc *sc)
> +{
> +     struct req_hdr {
> +             uint8_t buffer_mode;
> +             uint8_t format;
> +             uint8_t pad[2];
> +     } req = {
> +             .buffer_mode = EE_MODE_EFUSE,
> +             .format = EE_FORMAT_WHOLE,
> +     };
> +
> +     return mwx_mcu_send_wait(sc, MCU_EXT_CMD_EFUSE_BUFFER_MODE, &req,
> +         sizeof(req));
> +}
> +
> +int
> +mt7921_mcu_set_rts_thresh(struct mwx_softc *sc, uint32_t val, uint8_t band)
> +{
> +     struct {
> +             uint8_t         prot_idx;
> +             uint8_t         band;
> +             uint8_t         rsv[2];
> +             uint32_t        len_thresh;
> +             uint32_t        pkt_thresh;
> +     } __packed req = {
> +             .prot_idx = 1,
> +             .band = band,
> +             .len_thresh = htole32(val),
> +             .pkt_thresh = htole32(0x2),
> +     };
> +
> +     return mwx_mcu_send_wait(sc, MCU_EXT_CMD_PROTECT_CTRL, &req,
> +         sizeof(req));
> +}
> +
> +int
> +mt7921_mcu_set_deep_sleep(struct mwx_softc *sc, int ena)
> +{
> +     struct mt76_connac_config req = {
> +             .resp_type = 0,
> +     };
> +
> +     DPRINTF("%s: %s deep sleep\n", DEVNAME(sc), ena ? "enable" : "disable");
> +     snprintf(req.data, sizeof(req.data), "KeepFullPwr %d", !ena);
> +     return mwx_mcu_send_msg(sc, MCU_CE_CMD_CHIP_CONFIG, &req,
> +          sizeof(req), NULL);
> +}
> +
> +#ifdef NOTYET
> +int
> +mt7921_sta_state_dp(struct mwx_softc *sc, enum ieee80211_state old_state,
> +    enum ieee80211_state new_state)
> +{
> +     if ((old_state == IEEE80211_STA_ASSOC &&
> +         new_state == IEEE80211_STA_AUTHORIZED) ||
> +         (old_state == IEEE80211_STA_NONE &&
> +         new_state == IEEE80211_STA_NOTEXIST))
> +             mt7921_mcu_set_deep_sleep(dev, 1);
> +     if ((old_state == IEEE80211_STA_NOTEXIST &&
> +         new_state == IEEE80211_STA_NONE) ||
> +         (old_state == IEEE80211_STA_AUTHORIZED &&
> +         new_state == IEEE80211_STA_ASSOC))
> +             mt7921_mcu_set_deep_sleep(dev, 0);
> +     return 0;
> +}
> +#endif
> +
> +void
> +mt7921_mcu_low_power_event(struct mwx_softc *sc, struct mbuf *m)
> +{
> +     struct mt7921_mcu_lp_event {
> +             uint8_t state;
> +             uint8_t reserved[3];
> +     } __packed *event;
> +
> +     if (m->m_len < sizeof(*event))
> +             return;
> +     event = mtod(m, struct mt7921_mcu_lp_event *);
> +     DPRINTF("%s: low power event state %d\n", DEVNAME(sc), event->state);
> +}
> +
> +void
> +mt7921_mcu_tx_done_event(struct mwx_softc *sc, struct mbuf *m)
> +{
> +     struct mt7921_mcu_tx_done_event {
> +             uint8_t         pid;
> +             uint8_t         status;
> +             uint16_t        seq;
> +             uint8_t         wlan_idx;
> +             uint8_t         tx_cnt;
> +             uint16_t        tx_rate;
> +             uint8_t         flag;
> +             uint8_t         tid;
> +             uint8_t         rsp_rate;
> +             uint8_t         mcs;
> +             uint8_t         bw;
> +             uint8_t         tx_pwr;
> +             uint8_t         reason;
> +             uint8_t         rsv0[1];
> +             uint32_t        delay;
> +             uint32_t        timestamp;
> +             uint32_t        applied_flag;
> +             uint8_t         txs[28];
> +             uint8_t         rsv1[32];
> +     } __packed *event;
> +
> +     if (m->m_len < sizeof(*event))
> +             return;
> +     event = mtod(m, struct mt7921_mcu_tx_done_event *);
> +     // TODO mt7921_mac_add_txs(dev, event->txs);
> +}
> +
> +void
> +mt7921_mcu_scan_event(struct mwx_softc *sc, struct mbuf *m)
> +{
> +     struct ieee80211com *ic = &sc->sc_ic;
> +
> +     if ((sc->sc_flags & (MWX_FLAG_SCANNING | MWX_FLAG_BGSCAN)) == 0)
> +             return;
> +
> +     sc->sc_flags &= ~(MWX_FLAG_SCANNING | MWX_FLAG_BGSCAN);
> +     ieee80211_end_scan(&ic->ic_if);
> +}
> +
> +int
> +mt7921_hw_scan(struct mwx_softc *sc, int bgscan)
> +{
> +     struct mt76_connac_hw_scan_req  *req;
> +     int n_ssids = 0;
> +     int rv;
> +
> +     req = malloc(sizeof(*req), M_DEVBUF, M_NOWAIT | M_ZERO);
> +     if (req == NULL)
> +             return ENOMEM;
> +
> +     sc->sc_flags |= MWX_FLAG_SCANNING;
> +
> +     sc->sc_scan_seq_num = (sc->sc_scan_seq_num + 1) & 0x7f;
> +
> +     req->seq_num = sc->sc_scan_seq_num /* | sc->sc_band_idx << 7 */;
> +     req->bss_idx = /* mvif->idx */ 0;
> +     req->scan_type = /* sreq->n_ssids ? 1 : */ 0;
> +     req->probe_req_num = /* sreq->n_ssids ? 2 : */ 0;
> +     req->version = 1;
> +
> +#ifdef NOTYET
> +     for (i = 0; i < sreq->n_ssids; i++) {
> +             if (!sreq->ssids[i].ssid_len)
> +                     continue;
> +             req->ssids[i].ssid_len = cpu_to_le32(sreq->ssids[i].ssid_len);
> +             memcpy(req->ssids[i].ssid, sreq->ssids[i].ssid,
> +                 sreq->ssids[i].ssid_len);
> +             n_ssids++;
> +     }
> +#endif
> +
> +     req->ssid_type = n_ssids ? 0x4 : 0x1;
> +     req->ssid_type_ext = n_ssids ? 1 : 0;
> +     req->ssids_num = n_ssids;
> +
> +#ifdef NOTYET
> +     if (sreq->n_channels <= 32) {
> +             req->channels_num = sreq->n_channels;
> +     } else {
> +             req->channels_num = 32
> +             if (sreq->n_channels - 32 <= 32)
> +                     req->ext_channels_num = sreq->n_channels - 32;
> +             else
> +                     req->ext_channels_num = 32;
> +     }
> +     for (i = 0; i < req->channels_num + req->ext_channels_num; i++) {
> +             struct mt76_connac_mcu_scan_channel *chan;
> +
> +             if (i >= 32)
> +                     chan = &req->ext_channels[i - 32];
> +             else
> +                     chan = &req->channels[i];
> +             switch (scan_list[i]->band) {
> +             case NL80211_BAND_2GHZ:
> +                     chan->band = 1;
> +                     break;
> +             case NL80211_BAND_6GHZ:
> +                     chan->band = 3;
> +                     break;
> +             default:
> +                     chan->band = 2;
> +                     break;
> +             }
> +             chan->channel_num = scan_list[i]->hw_value;
> +     }
> +#endif
> +     req->channel_type = /* sreq->n_channels ? 4 : */ 0;
> +
> +
> +#ifdef NOTYET
> +     if (sreq->ie_len > 0) {
> +             memcpy(req->ies, sreq->ie, sreq->ie_len);
> +             req->ies_len = cpu_to_le16(sreq->ie_len);
> +     }
> +#endif
> +
> +     req->scan_func |= SCAN_FUNC_SPLIT_SCAN;
> +
> +     /* wildcard BSSID */
> +     memset(req->bssid, 0xff, ETHER_ADDR_LEN);
> +#ifdef NOTYET
> +     if (sreq->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
> +             get_random_mask_addr(req->random_mac, sreq->mac_addr,
> +                 sreq->mac_addr_mask);
> +             req->scan_func |= SCAN_FUNC_RANDOM_MAC;
> +     }
> +#endif
> +
> +     rv = mwx_mcu_send_msg(sc, MCU_CE_CMD_START_HW_SCAN, req,
> +         sizeof(*req), NULL);
> +
> +     if (rv != 0)
> +             sc->sc_flags &= ~(MWX_FLAG_SCANNING | MWX_FLAG_BGSCAN);
> +
> +     free(req, M_DEVBUF, sizeof(*req));
> +     return rv;
> +}
> +
> +int
> +mt7921_hw_scan_cancel(struct mwx_softc *sc)
> +{
> +     struct {
> +             uint8_t         seq_num;
> +             uint8_t         is_ext_channel;
> +             uint8_t         rsv[2];
> +     } __packed req = {
> +             .seq_num = sc->sc_scan_seq_num,
> +     };
> +     int rv;
> +
> +     rv = mwx_mcu_send_msg(sc, MCU_CE_CMD_CANCEL_HW_SCAN, &req,
> +         sizeof(req), NULL);
> +     if (rv == 0)
> +             sc->sc_flags &= ~(MWX_FLAG_SCANNING | MWX_FLAG_BGSCAN);
> +     return rv;
> +}
> +
> +int
> +mt7921_mcu_set_mac_enable(struct mwx_softc *sc, int band, int enable)
> +{
> +     struct {
> +             uint8_t         enable;
> +             uint8_t         band;
> +             uint8_t         rsv[2];
> +     } __packed req = {
> +             .enable = enable,
> +             .band = band,
> +     };
> +
> +     return mwx_mcu_send_wait(sc, MCU_EXT_CMD_MAC_INIT_CTRL, &req,
> +         sizeof(req));
> +}
> +
> +int
> +mt7921_mcu_set_channel_domain(struct mwx_softc *sc)
> +{
> +     struct {
> +             uint8_t         alpha2[4]; /* regulatory_request.alpha2 */
> +             uint8_t         bw_2g;  /* BW_20_40M            0
> +                                      * BW_20M               1
> +                                      * BW_20_40_80M         2
> +                                      * BW_20_40_80_160M     3
> +                                      * BW_20_40_80_8080M    4
> +                                      */
> +             uint8_t         bw_5g;
> +             uint8_t         bw_6g;
> +             uint8_t         pad;
> +             uint8_t         n_2ch;
> +             uint8_t         n_5ch;
> +             uint8_t         n_6ch;
> +             uint8_t         pad2;
> +     } __packed *hdr;
> +     struct {
> +             uint16_t        hw_value;
> +             uint16_t        pad;
> +             uint16_t        flags;
> +     } __packed *channel;
> +     struct ieee80211com *ic = &sc->sc_ic;
> +     struct ieee80211_channel *chan;
> +     int i, len, rv;
> +     int n_2ch = 0, n_5ch = 0, n_6ch = 0;
> +
> +     len = sizeof(*hdr) + IEEE80211_CHAN_MAX * sizeof(channel);
> +     hdr = malloc(len, M_DEVBUF, M_NOWAIT | M_ZERO);
> +     if (hdr == NULL)
> +             return ENOMEM;
> +
> +     hdr->bw_2g = 0; /* BW_20_40M */
> +     hdr->bw_5g = 3; /* BW_20_40_80_160M */
> +     hdr->bw_6g = 3; /* BW_20_40_80_160M */
> +
> +     channel = (void *)(hdr + 1);
> +
> +     for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
> +             chan = &ic->ic_channels[i];
> +             if (!IEEE80211_IS_CHAN_2GHZ(chan))
> +                     continue;
> +
> +             channel->hw_value = htole16(ieee80211_chan2ieee(ic, chan));
> +             channel->flags = htole32(0);    /* XXX */
> +
> +             channel++;
> +             n_2ch++;
> +     }
> +     for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
> +             chan = &ic->ic_channels[i];
> +             if (!IEEE80211_IS_CHAN_5GHZ(chan))
> +                     continue;
> +
> +             channel->hw_value = htole16(ieee80211_chan2ieee(ic, chan));
> +             channel->flags = htole32(0);    /* XXX */
> +
> +             channel++;
> +             n_5ch++;
> +     }
> +#ifdef NOTYET
> +     /* 6GHz handling */
> +     for (i = 0; i <= IEEE80211_CHAN_MAX; i++) {
> +             chan = &ic->ic_channels[i];
> +             if (!IEEE80211_IS_CHAN_6GHZ(chan))
> +                     continue;
> +
> +             channel->hw_value = htole16(ieee80211_chan2ieee(ic, chan));
> +             channel->flags = htole32(0);    /* XXX */
> +
> +             channel++;
> +             n_6ch++;
> +     }
> +#endif
> +
> +     memcpy(hdr->alpha2, sc->sc_alpha2, sizeof(hdr->alpha2));
> +     hdr->n_2ch = n_2ch;
> +     hdr->n_5ch = n_5ch;
> +     hdr->n_6ch = n_6ch;
> +
> +     i = (caddr_t)channel - (caddr_t)hdr;
> +     rv = mwx_mcu_send_msg(sc, MCU_CE_CMD_SET_CHAN_DOMAIN, hdr, i, NULL);
> +     free(hdr, M_DEVBUF, len);
> +     return rv;
> +}
> +
> +uint8_t
> +mt7921_mcu_chan_bw(struct ieee80211_channel *channel)
> +{
> +     /*
> +      * following modes are not yet supported
> +      *  CMD_CBW_5MHZ, CMD_CBW_10MHZ, CMD_CBW_8080MHZ
> +      */
> +     if (channel->ic_xflags & IEEE80211_CHANX_160MHZ)
> +             return CMD_CBW_160MHZ;
> +     if (channel->ic_xflags & IEEE80211_CHANX_80MHZ)
> +             return CMD_CBW_80MHZ;
> +     if (channel->ic_flags & IEEE80211_CHAN_40MHZ)
> +             return CMD_CBW_40MHZ;
> +     return CMD_CBW_20MHZ;
> +}
> +
> +int
> +mt7921_mcu_set_chan_info(struct mwx_softc *sc, int cmd)
> +{
> +     struct ieee80211_channel *channel = sc->sc_ic.ic_ibss_chan;
> +     struct {
> +             uint8_t         control_ch;
> +             uint8_t         center_ch;
> +             uint8_t         bw;
> +             uint8_t         tx_streams_num;
> +             uint8_t         rx_streams;     /* mask or num */
> +             uint8_t         switch_reason;
> +             uint8_t         band_idx;
> +             uint8_t         center_ch2;     /* for 80+80 only */
> +             uint16_t        cac_case;
> +             uint8_t         channel_band;
> +             uint8_t         rsv0;
> +             uint32_t        outband_freq;
> +             uint8_t         txpower_drop;
> +             uint8_t         ap_bw;
> +             uint8_t         ap_center_ch;
> +             uint8_t         rsv1[57];
> +     } __packed req = {
> +             .control_ch = ieee80211_mhz2ieee(channel->ic_freq,
> +                 channel->ic_flags),
> +             .center_ch = ieee80211_mhz2ieee(channel->ic_freq,
> +                 channel->ic_flags),
> +             .tx_streams_num = sc->sc_capa.num_streams,
> +             .rx_streams = sc->sc_capa.antenna_mask,
> +             .band_idx = 0,  /* XXX 0 or 1 */
> +     };
> +
> +     req.bw = mt7921_mcu_chan_bw(channel);
> +
> +#ifdef NOTYET
> +     if (channel->ic_flags & IEEE80211_CHAN_6GHZ)
> +             req.channel_band = 2;
> +     else
> +#endif
> +     if (channel->ic_flags & IEEE80211_CHAN_5GHZ)
> +             req.channel_band = 1;
> +     else
> +             req.channel_band = 0;
> +
> +#ifdef NOTYET
> +     if (dev->mt76.hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
> +             req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
> +     else if ((chandef->chan->flags & IEEE80211_CHAN_RADAR) &&
> +              chandef->chan->dfs_state != NL80211_DFS_AVAILABLE)
> +             req.switch_reason = CH_SWITCH_DFS;
> +     else
> +#endif
> +             req.switch_reason = CH_SWITCH_NORMAL;
> +
> +     if (cmd == MCU_EXT_CMD_CHANNEL_SWITCH)
> +             req.rx_streams = sc->sc_capa.num_streams;
> +
> +#ifdef NOTYET
> +     if (chandef->width == NL80211_CHAN_WIDTH_80P80) {
> +             int freq2 = chandef->center_freq2;
> +             req.center_ch2 = ieee80211_frequency_to_channel(freq2);
> +     }
> +#endif
> +
> +     return mwx_mcu_send_wait(sc, cmd, &req, sizeof(req));
> +}
> +
> +void
> +mt7921_mac_reset_counters(struct mwx_softc *sc)
> +{
> +     int i;
> +
> +     for (i = 0; i < 4; i++) {
> +             mwx_read(sc, MT_TX_AGG_CNT(0, i));
> +             mwx_read(sc, MT_TX_AGG_CNT2(0, i));
> +     }
> +
> +     /* XXX TODO stats in softc */
> +
> +     /* reset airtime counters */
> +     mwx_read(sc, MT_MIB_SDR9(0));
> +     mwx_read(sc, MT_MIB_SDR36(0));
> +     mwx_read(sc, MT_MIB_SDR37(0));
> +     mwx_set(sc, MT_WF_RMAC_MIB_TIME0(0), MT_WF_RMAC_MIB_RXTIME_CLR);
> +     mwx_set(sc, MT_WF_RMAC_MIB_AIRTIME0(0), MT_WF_RMAC_MIB_RXTIME_CLR);
> +}
> +
> +int
> +mt7921_mcu_uni_add_dev(struct mwx_softc *sc, struct mwx_vif *mvif,
> +    struct mwx_node *mn, int enable)
> +{
> +     struct {
> +             struct {
> +                     uint8_t         omac_idx;
> +                     uint8_t         band_idx;
> +                     uint16_t        pad;
> +             } __packed hdr;
> +             struct req_tlv {
> +                     uint16_t        tag;
> +                     uint16_t        len;
> +                     uint8_t         active;
> +                     uint8_t         pad;
> +                     uint8_t         omac_addr[ETHER_ADDR_LEN];
> +             } __packed tlv;
> +     } dev_req = {
> +             .hdr = {
> +                     .omac_idx = mvif->omac_idx,
> +                     .band_idx = mvif->band_idx,
> +             },
> +             .tlv = {
> +                     .tag = htole16(DEV_INFO_ACTIVE),
> +                     .len = htole16(sizeof(struct req_tlv)),
> +                     .active = enable,
> +             },
> +     };
> +     struct {
> +             struct {
> +                     uint8_t         bss_idx;
> +                     uint8_t         pad[3];
> +             } __packed hdr;
> +             struct mt76_connac_bss_basic_tlv basic;
> +     } basic_req = {
> +             .hdr = {
> +                     .bss_idx = mvif->idx,
> +             },
> +             .basic = {
> +                     .tag = htole16(UNI_BSS_INFO_BASIC),
> +                     .len = htole16(sizeof(struct 
> mt76_connac_bss_basic_tlv)),
> +                     .omac_idx = mvif->omac_idx,
> +                     .band_idx = mvif->band_idx,
> +                     .wmm_idx = mvif->wmm_idx,
> +                     .active = enable,
> +                     .bmc_tx_wlan_idx = htole16(mn->wcid),
> +                     .sta_idx = htole16(mn->wcid),
> +                     .conn_state = 1,
> +             },
> +     };
> +     int rv, idx, cmd, len;
> +     void *data;
> +
> +     switch (sc->sc_ic.ic_opmode) {
> +     case IEEE80211_M_MONITOR:
> +     case IEEE80211_M_HOSTAP:
> +             basic_req.basic.conn_type = htole32(CONNECTION_INFRA_AP);
> +             break;
> +     case IEEE80211_M_STA:
> +             basic_req.basic.conn_type = htole32(CONNECTION_INFRA_STA);
> +             break;
> +     case IEEE80211_M_IBSS:
> +             basic_req.basic.conn_type = htole32(CONNECTION_IBSS_ADHOC);
> +             break;
> +     default:
> +             panic("%s: unknown operation mode", DEVNAME(sc));
> +     }
> +
> +     idx = mvif->omac_idx > EXT_BSSID_START ? HW_BSSID_0 : mvif->omac_idx;
> +     basic_req.basic.hw_bss_idx = idx;
> +
> +     memcpy(dev_req.tlv.omac_addr, sc->sc_lladdr, ETHER_ADDR_LEN);
> +
> +     if (enable) {
> +             cmd = MCU_UNI_CMD_DEV_INFO_UPDATE;
> +             data = &dev_req;
> +             len = sizeof(dev_req);
> +     } else {
> +             cmd = MCU_UNI_CMD_BSS_INFO_UPDATE;
> +             data = &basic_req;
> +             len = sizeof(basic_req);
> +     }
> +
> +printf("%s: %s cmd %x mvif idx %d omac %d band %d wmm %d\n", DEVNAME(sc), 
> __func__, cmd, mvif->idx, mvif->omac_idx, mvif->band_idx, mvif->wmm_idx);
> +     rv = mwx_mcu_send_wait(sc, cmd, data, len);
> +     if (rv < 0)
> +             return rv;
> +
> +     if (enable) {
> +             cmd = MCU_UNI_CMD_BSS_INFO_UPDATE;
> +             data = &basic_req;
> +             len = sizeof(basic_req);
> +     } else {
> +             cmd = MCU_UNI_CMD_DEV_INFO_UPDATE;
> +             data = &dev_req;
> +             len = sizeof(dev_req);
> +     }
> +
> +printf("%s: %s cmd %x wcid %d\n", DEVNAME(sc), __func__, cmd, mn->wcid);
> +     return mwx_mcu_send_wait(sc, cmd, data, len);
> +}
> +
> +int
> +mt7921_mcu_set_sniffer(struct mwx_softc *sc, int enable)
> +{
> +     struct {
> +             uint8_t         band_idx;
> +             uint8_t         pad[3];
> +             struct sniffer_enable_tlv {
> +                     uint16_t        tag;
> +                     uint16_t        len;
> +                     uint8_t         enable;
> +                     uint8_t         pad[3];
> +             }               enable;
> +     } req = {
> +             .band_idx = 0,
> +             .enable = {
> +                     .tag = htole16(0),
> +                     .len = htole16(sizeof(struct sniffer_enable_tlv)),
> +                     .enable = enable,
> +             },
> +     };
> +
> +     return mwx_mcu_send_wait(sc, MCU_UNI_CMD_SNIFFER, &req, sizeof(req));
> +}
> +
> +int
> +mt7921_mcu_set_beacon_filter(struct mwx_softc *sc, int enable)
> +{
> +#ifdef NOTYET
> +     int rv;
> +
> +     if (enable) {
> +             rv = mt7921_mcu_uni_bss_bcnft(dev, vif, true);
> +             if (rv)
> +                     return rv;
> +             vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
> +             ieee80211_hw_set(hw, CONNECTION_MONITOR);
> +             mt76_set(dev, MT_WF_RFCR(0), MT_WF_RFCR_DROP_OTHER_BEACON);
> +             return 0;
> +     }
> +
> +     rv = mt7921_mcu_set_bss_pm(dev, vif, false);
> +     if (rv)
> +             return rv;
> +     vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER;
> +     __clear_bit(IEEE80211_HW_CONNECTION_MONITOR, hw->flags);
> +     mt76_clear(dev, MT_WF_RFCR(0), MT_WF_RFCR_DROP_OTHER_BEACON);
> +#endif
> +     return 0;
> +} 
> +
> +int
> +mt7921_mac_fill_rx(struct mwx_softc *sc, struct mbuf *m,
> +    struct ieee80211_rxinfo *rxi)
> +{
> +     struct ieee80211com *ic = &sc->sc_ic;
> +     uint32_t *rxd, rxd0, rxd1, rxd2, rxd3, rxd4;
> +//   uint32_t mode = 0;
> +     uint16_t hdr_gap /*, seq_ctrl = 0, fc = 0 */;
> +     uint8_t chfreq, remove_pad /*, qos_ctl = 0, amsdu_info */;
> +     int idx, unicast, num_rxd = 6;
> +//   bool insert_ccmp_hdr = false;
> +
> +     if (m->m_len < num_rxd * sizeof(uint32_t))
> +             return -1;
> +
> +     rxd = mtod(m, uint32_t *);
> +     rxd0 = le32toh(rxd[0]);
> +     rxd1 = le32toh(rxd[1]);
> +     rxd2 = le32toh(rxd[2]);
> +     rxd3 = le32toh(rxd[3]);
> +     rxd4 = le32toh(rxd[4]);
> +
> +     if (rxd1 & MT_RXD1_NORMAL_BAND_IDX)
> +             return -1;
> +
> +     if (rxd2 & MT_RXD2_NORMAL_AMSDU_ERR)
> +             return -1;
> +
> +     if (rxd2 & MT_RXD2_NORMAL_HDR_TRANS)
> +             return -1;
> +
> +     /* ICV error or CCMP/BIP/WPI MIC error */
> +     if (rxd1 & MT_RXD1_NORMAL_ICV_ERR) {
> +             ic->ic_stats.is_rx_decryptcrc++;
> +             return -1;
> +     }
> +
> +     if (rxd1 & MT_RXD1_NORMAL_FCS_ERR)
> +             return -1;
> +
> +     if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR) {
> +             /* report MIC failures to net80211 for TKIP */
> +             ic->ic_stats.is_rx_locmicfail++;
> +             ieee80211_michael_mic_failure(ic, 0/* XXX */);
> +             return -1;
> +     }
> +
> +
> +     chfreq = (rxd3 & MT_RXD3_NORMAL_CH_FREQ_MASK) >>
> +         MT_RXD3_NORMAL_CH_FREQ_SHIFT;
> +     unicast = (rxd3 & MT_RXD3_NORMAL_ADDR_TYPE_MASK) == MT_RXD3_NORMAL_U2M;
> +     idx = rxd1 & MT_RXD1_NORMAL_WLAN_IDX_MASK;
> +
> +#if 0
> +     status->wcid = mt7921_rx_get_wcid(dev, idx, unicast);
> +     if (status->wcid) {
> +             struct mt7921_sta *msta;
> +
> +             msta = container_of(status->wcid, struct mt7921_sta, wcid);
> +             spin_lock_bh(&dev->sta_poll_lock);
> +             if (list_empty(&msta->poll_list))
> +                     list_add_tail(&msta->poll_list, &dev->sta_poll_list);
> +             spin_unlock_bh(&dev->sta_poll_lock);
> +     }
> +#endif
> +
> +#if NOTYET
> +     if ((rxd0 & (MT_RXD0_NORMAL_IP_SUM | MT_RXD0_NORMAL_UDP_TCP_SUM)) ==
> +         (MT_RXD0_NORMAL_IP_SUM | MT_RXD0_NORMAL_UDP_TCP_SUM)) {
> +             m->m_pkthdr.csum_flags = M_IPV4_CSUM_IN_OK |
> +                 M_TCP_CSUM_IN_OK | M_UDP_CSUM_IN_OK;
> +     }
> +#endif
> +
> +     if ((rxd1 & MT_RXD1_NORMAL_SEC_MODE_MASK) != 0 &&
> +         !(rxd1 & (MT_RXD1_NORMAL_CLM | MT_RXD1_NORMAL_CM))) {
> +             rxi->rxi_flags |= IEEE80211_RXI_HWDEC |
> +                 IEEE80211_RXI_HWDEC_IV_STRIPPED;
> +     }
> +
> +     remove_pad = (rxd2 & MT_RXD2_NORMAL_HDR_OFFSET_MASK) >>
> +         MT_RXD2_NORMAL_HDR_OFFSET_SHIFT;
> +
> +     if (rxd2 & MT_RXD2_NORMAL_MAX_LEN_ERROR)
> +             return -EINVAL;
> +
> +     rxd += 6;
> +
> +     if (rxd1 & MT_RXD1_NORMAL_GROUP_4)
> +             num_rxd += 4;
> +     if (rxd1 & MT_RXD1_NORMAL_GROUP_1)
> +             num_rxd += 4;
> +     if (rxd1 & MT_RXD1_NORMAL_GROUP_2)
> +             num_rxd += 2;
> +     if (rxd1 & MT_RXD1_NORMAL_GROUP_3)
> +             num_rxd += 2;
> +     if (rxd1 & MT_RXD1_NORMAL_GROUP_5)
> +             num_rxd += 18;
> +
> +     if (m->m_len < num_rxd * sizeof(uint32_t))
> +             return -1;
> +
> +#if 0
> +     if (rxd1 & MT_RXD1_NORMAL_GROUP_4) {
> +             uint32_t v0 = le32toh(rxd[0]);
> +             uint32_t v2 = le32toh(rxd[2]);
> +
> +             fc = cpu_to_le16(FIELD_GET(MT_RXD6_FRAME_CONTROL, v0));
> +             seq_ctrl = FIELD_GET(MT_RXD8_SEQ_CTRL, v2);
> +             qos_ctl = FIELD_GET(MT_RXD8_QOS_CTL, v2);
> +
> +             rxd += 4;
> +     }
> +
> +     if (rxd1 & MT_RXD1_NORMAL_GROUP_1) {
> +             u8 *data = (u8 *)rxd;
> +
> +             if (status->flag & RX_FLAG_DECRYPTED) {
> +                     switch (FIELD_GET(MT_RXD1_NORMAL_SEC_MODE, rxd1)) {
> +                     case MT_CIPHER_AES_CCMP:
> +                     case MT_CIPHER_CCMP_CCX:
> +                     case MT_CIPHER_CCMP_256:
> +                             insert_ccmp_hdr =
> +                                     (rxd2 & MT_RXD2_NORMAL_FRAG);
> +                             /* FALLTHROUGH */
> +                     case MT_CIPHER_TKIP:
> +                     case MT_CIPHER_TKIP_NO_MIC:
> +                     case MT_CIPHER_GCMP:
> +                     case MT_CIPHER_GCMP_256:
> +                             status->iv[0] = data[5];
> +                             status->iv[1] = data[4];
> +                             status->iv[2] = data[3];
> +                             status->iv[3] = data[2];
> +                             status->iv[4] = data[1];
> +                             status->iv[5] = data[0];
> +                             break;
> +                     default:
> +                             break;
> +                     }
> +             }
> +             rxd += 4;
> +     }
> +
> +     if (rxd1 & MT_RXD1_NORMAL_GROUP_2) {
> +             status->timestamp = le32_to_cpu(rxd[0]);
> +             status->flag |= RX_FLAG_MACTIME_START;
> +
> +             if (!(rxd2 & MT_RXD2_NORMAL_NON_AMPDU)) {
> +                     status->flag |= RX_FLAG_AMPDU_DETAILS;
> +
> +                     /* all subframes of an A-MPDU have the same timestamp */
> +                     if (phy->rx_ampdu_ts != status->timestamp) {
> +                             if (!++phy->ampdu_ref)
> +                                     phy->ampdu_ref++;
> +                     }
> +                     phy->rx_ampdu_ts = status->timestamp;
> +
> +                     status->ampdu_ref = phy->ampdu_ref;
> +             }
> +
> +             rxd += 2;
> +     }
> +
> +     /* RXD Group 3 - P-RXV */
> +     if (rxd1 & MT_RXD1_NORMAL_GROUP_3) {
> +             u8 stbc, gi;
> +             u32 v0, v1;
> +             bool cck;
> +
> +             rxv = rxd;
> +             rxd += 2;
> +
> +             v0 = le32_to_cpu(rxv[0]);
> +             v1 = le32_to_cpu(rxv[1]);
> +
> +             if (v0 & MT_PRXV_HT_AD_CODE)
> +                     status->enc_flags |= RX_ENC_FLAG_LDPC;
> +
> +             status->chains = mphy->antenna_mask;
> +             status->chain_signal[0] = to_rssi(MT_PRXV_RCPI0, v1);
> +             status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v1);
> +             status->chain_signal[2] = to_rssi(MT_PRXV_RCPI2, v1);
> +             status->chain_signal[3] = to_rssi(MT_PRXV_RCPI3, v1);
> +             status->signal = -128;
> +             for (i = 0; i < hweight8(mphy->antenna_mask); i++) {
> +                     if (!(status->chains & BIT(i)) ||
> +                         status->chain_signal[i] >= 0)
> +                             continue;
> +
> +                     status->signal = max(status->signal,
> +                                          status->chain_signal[i]);
> +             }
> +
> +             stbc = FIELD_GET(MT_PRXV_STBC, v0);
> +             gi = FIELD_GET(MT_PRXV_SGI, v0);
> +             cck = false;
> +
> +             idx = i = FIELD_GET(MT_PRXV_TX_RATE, v0);
> +             mode = FIELD_GET(MT_PRXV_TX_MODE, v0);
> +
> +             switch (mode) {
> +             case MT_PHY_TYPE_CCK:
> +                     cck = true;
> +                     fallthrough;
> +             case MT_PHY_TYPE_OFDM:
> +                     i = mt76_get_rate(&dev->mt76, sband, i, cck);
> +                     break;
> +             case MT_PHY_TYPE_HT_GF:
> +             case MT_PHY_TYPE_HT:
> +                     status->encoding = RX_ENC_HT;
> +                     if (i > 31)
> +                             return -EINVAL;
> +                     break;
> +             case MT_PHY_TYPE_VHT:
> +                     status->nss =
> +                             FIELD_GET(MT_PRXV_NSTS, v0) + 1;
> +                     status->encoding = RX_ENC_VHT;
> +                     if (i > 9)
> +                             return -EINVAL;
> +                     break;
> +             case MT_PHY_TYPE_HE_MU:
> +             case MT_PHY_TYPE_HE_SU:
> +             case MT_PHY_TYPE_HE_EXT_SU:
> +             case MT_PHY_TYPE_HE_TB:
> +                     status->nss =
> +                             FIELD_GET(MT_PRXV_NSTS, v0) + 1;
> +                     status->encoding = RX_ENC_HE;
> +                     i &= GENMASK(3, 0);
> +
> +                     if (gi <= NL80211_RATE_INFO_HE_GI_3_2)
> +                             status->he_gi = gi;
> +
> +                     status->he_dcm = !!(idx & MT_PRXV_TX_DCM);
> +                     break;
> +             default:
> +                     return -EINVAL;
> +             }
> +
> +             status->rate_idx = i;
> +
> +             switch (FIELD_GET(MT_PRXV_FRAME_MODE, v0)) {
> +             case IEEE80211_STA_RX_BW_20:
> +                     break;
> +             case IEEE80211_STA_RX_BW_40:
> +                     if (mode & MT_PHY_TYPE_HE_EXT_SU &&
> +                         (idx & MT_PRXV_TX_ER_SU_106T)) {
> +                             status->bw = RATE_INFO_BW_HE_RU;
> +                             status->he_ru =
> +                                     NL80211_RATE_INFO_HE_RU_ALLOC_106;
> +                     } else {
> +                             status->bw = RATE_INFO_BW_40;
> +                     }
> +                     break;
> +             case IEEE80211_STA_RX_BW_80:
> +                     status->bw = RATE_INFO_BW_80;
> +                     break;
> +             case IEEE80211_STA_RX_BW_160:
> +                     status->bw = RATE_INFO_BW_160;
> +                     break;
> +             default:
> +                     return -EINVAL;
> +             }
> +
> +             status->enc_flags |= RX_ENC_FLAG_STBC_MASK * stbc;
> +             if (mode < MT_PHY_TYPE_HE_SU && gi)
> +                     status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
> +
> +             if (rxd1 & MT_RXD1_NORMAL_GROUP_5) {
> +                     rxd += 18;
> +             }
> +     }
> +
> +     amsdu_info = FIELD_GET(MT_RXD4_NORMAL_PAYLOAD_FORMAT, rxd4);
> +     status->amsdu = !!amsdu_info;
> +     if (status->amsdu) {
> +             status->first_amsdu = amsdu_info == MT_RXD4_FIRST_AMSDU_FRAME;
> +             status->last_amsdu = amsdu_info == MT_RXD4_LAST_AMSDU_FRAME;
> +     }
> +#endif
> +
> +     hdr_gap = num_rxd * sizeof(uint32_t) + 2 * remove_pad;
> +     m_adj(m, hdr_gap);
> +#if 0
> +     if (status->amsdu) {
> +             memmove(skb->data + 2, skb->data,
> +                     ieee80211_get_hdrlen_from_skb(skb));
> +             skb_pull(skb, 2);
> +     }
> +
> +     struct ieee80211_hdr *hdr;
> +
> +     if (insert_ccmp_hdr) {
> +             u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1);
> +
> +             mt76_insert_ccmp_hdr(skb, key_id);
> +     }
> +
> +     hdr = mt76_skb_get_hdr(skb);
> +     fc = hdr->frame_control;
> +     if (ieee80211_is_data_qos(fc)) {
> +             seq_ctrl = le16_to_cpu(hdr->seq_ctrl);
> +             qos_ctl = *ieee80211_get_qos_ctl(hdr);
> +     }
> +
> +     if (!status->wcid || !ieee80211_is_data_qos(fc))
> +             return 0;
> +
> +     status->aggr = unicast && !ieee80211_is_qos_nullfunc(fc);
> +     status->seqno = IEEE80211_SEQ_TO_SN(seq_ctrl);
> +     status->qos_ctl = qos_ctl;
> +#endif
> +     rxi->rxi_chan = chfreq;
> +
> +     return 0;
> +}
> Index: if_mwxreg.h
> ===================================================================
> RCS file: if_mwxreg.h
> diff -N if_mwxreg.h
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ if_mwxreg.h       9 Dec 2022 17:09:39 -0000
> @@ -0,0 +1,1076 @@
> +/*
> + * Copyright (c) 2022 Claudio Jeker <clau...@openbsd.org>
> + * Copyright (C) 2021 MediaTek Inc.
> + * Copyright (C) 2016 Felix Fietkau <n...@nbd.name>
> + *
> + * Permission to use, copy, modify, and/or 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.
> + */
> +
> +/* MCU WFDMA1 */
> +#define      MT_MCU_WFDMA1_BASE              0x3000
> +
> +#define      MT_MDP_BASE                     0x820cd000
> +#define      MT_MDP_DCR0                     0x820cd000
> +#define      MT_MDP_DCR0_DAMSDU_EN           (1U << 15)
> +#define      MT_MDP_DCR0_RX_HDR_TRANS_EN     (1U << 19)
> +
> +#define      MT_MDP_DCR1                     0x820cd004
> +#define      MT_MDP_DCR1_MAX_RX_LEN_MASK     0x0000fff8
> +#define      MT_MDP_DCR1_MAX_RX_LEN_SHIFT    3
> +
> +/* TMAC: band 0 (0x21000), band 1 (0xa1000) */
> +#define      MT_BAND_BASE0           0x820f0000
> +#define      MT_BAND_BASE1           0x820e0000
> +#define      MT_BAND_OFF             (MT_BAND_BASE0 - MT_BAND_BASE1)
> +#define      MT_BAND_ADDR(_band, ofs)                \
> +     (MT_BAND_BASE0 - (_band) * MT_BAND_OFF + (ofs))
> +
> +#define      MT_TMAC_TCR0(_band)             MT_BAND_ADDR(_band, 0x4000)
> +#define      MT_TMAC_TCR0_TBTT_STOP_CTRL     (1U << 25)
> +
> +#define      MT_TMAC_CDTR(_band)             MT_BAND_ADDR(_band, 0x4090)
> +#define      MT_TMAC_ODTR(_band)             MT_BAND_ADDR(_band, 0x4094)
> +#define      MT_TIMEOUT_VAL_PLCP             0x0000ffff
> +#define      MT_TIMEOUT_VAL_CCA              0xffff0000
> +
> +#define      MT_TMAC_ICR0(_band)             MT_BAND_ADDR(_band, 0x40a4)
> +#define      MT_IFS_EIFS                     GENMASK(8, 0)
> +#define      MT_IFS_RIFS                     GENMASK(14, 10)
> +#define      MT_IFS_SIFS                     GENMASK(22, 16)
> +#define      MT_IFS_SLOT                     GENMASK(30, 24)
> +
> +#define      MT_TMAC_CTCR0(_band)                    MT_BAND_ADDR(_band, 
> 0x40f4)
> +#define      MT_TMAC_CTCR0_INS_DDLMT_REFTIME         0x0000003f
> +#define      MT_TMAC_CTCR0_INS_DDLMT_EN              (1U << 17)
> +#define      MT_TMAC_CTCR0_INS_DDLMT_VHT_SMPDU_EN    (1U << 18)
> +
> +#define      MT_TMAC_TRCR0(_band)            MT_BAND_ADDR(_band, 0x409c)
> +#define      MT_TMAC_TFCR0(_band)            MT_BAND_ADDR(_band, 0x41e0)
> +
> +#define      MT_DMA_DCR0(_band)              MT_BAND_ADDR(_band, 0x7000)
> +#define      MT_DMA_DCR0_MAX_RX_LEN_MASK     0x0000fff8
> +#define      MT_DMA_DCR0_MAX_RX_LEN_SHIFT    3
> +#define      MT_DMA_DCR0_RXD_G5_EN           (1U << 23)
> +
> +/* MIB: band 0(0x24800), band 1(0xa4800) */
> +#define      MT_MIB_SCR1(_band)              MT_BAND_ADDR(_band, 0xd004)
> +#define      MT_MIB_TXDUR_EN                 0x0100
> +#define      MT_MIB_RXDUR_EN                 0x0200
> +
> +#define      MT_MIB_SDR9(_band)              MT_BAND_ADDR(_band, 0xd02c)
> +#define      MT_MIB_SDR9_BUSY_MASK           0x00ffffff
> +
> +#define      MT_MIB_SDR36(_band)             MT_BAND_ADDR(_band, 0xd054)
> +#define      MT_MIB_SDR36_TXTIME_MASK        0x00ffffff
> +#define      MT_MIB_SDR37(_band)             MT_BAND_ADDR(_band, 0xd058)
> +#define      MT_MIB_SDR37_RXTIME_MASK        0x00ffffff
> +
> +#define      MT_TX_AGG_CNT(_band, n)         MT_BAND_ADDR(_band, 0xd7dc + 
> ((n) << 2))
> +#define      MT_TX_AGG_CNT2(_band, n)        MT_BAND_ADDR(_band, 0xd7ec + 
> ((n) << 2))
> +
> +#define      MT_WTBLON_TOP_BASE              0x820d4000
> +#define      MT_WTBLON_TOP_WDUCR             0x820d4200
> +#define      MT_WTBLON_TOP_WDUCR_GROUP       0x0007
> +
> +#define      MT_WTBL_UPDATE                  0x820d4230
> +#define      MT_WTBL_UPDATE_WLAN_IDX         0x000003ff
> +#define      MT_WTBL_UPDATE_ADM_COUNT_CLEAR  (1U << 12)
> +#define      MT_WTBL_UPDATE_BUSY             (1U << 31)
> +
> +#define      MT_WTBL_BASE                    0x820d8000
> +#define      MT_WTBL_LMAC_ID                 GENMASK(14, 8)
> +#define      MT_WTBL_LMAC_DW                 GENMASK(7, 2)
> +#define      MT_WTBL_LMAC_OFFS(_id, _dw)     (MT_WTBL_BASE | \
> +                                     FIELD_PREP(MT_WTBL_LMAC_ID, _id) | \
> +                                     FIELD_PREP(MT_WTBL_LMAC_DW, _dw))
> +
> +/* RMAC: band 0 (0x21400), band 1 (0xa1400) */
> +#define      MT_WF_RFCR(_base)               MT_BAND_ADDR(_base, 0x5000)
> +#define      MT_WF_RFCR_DROP_STBC_MULTI      0x00000001
> +#define      MT_WF_RFCR_DROP_FCSFAIL         0x00000002
> +#define      MT_WF_RFCR_DROP_VERSION         0x00000008
> +#define      MT_WF_RFCR_DROP_PROBEREQ        0x00000010
> +#define      MT_WF_RFCR_DROP_MCAST           0x00000020
> +#define      MT_WF_RFCR_DROP_BCAST           0x00000040
> +#define      MT_WF_RFCR_DROP_MCAST_FILTERED  0x00000080
> +#define      MT_WF_RFCR_DROP_A3_MAC          0x00000100
> +#define      MT_WF_RFCR_DROP_A3_BSSID        0x00000200
> +#define      MT_WF_RFCR_DROP_A2_BSSID        0x00000400
> +#define      MT_WF_RFCR_DROP_OTHER_BEACON    0x00000800
> +#define      MT_WF_RFCR_DROP_FRAME_REPORT    0x00001000
> +#define      MT_WF_RFCR_DROP_CTL_RSV         0x00002000
> +#define      MT_WF_RFCR_DROP_CTS             0x00004000
> +#define      MT_WF_RFCR_DROP_RTS             0x00008000
> +#define      MT_WF_RFCR_DROP_DUPLICATE       0x00010000
> +#define      MT_WF_RFCR_DROP_OTHER_BSS       0x00020000
> +#define      MT_WF_RFCR_DROP_OTHER_UC        0x00040000
> +#define      MT_WF_RFCR_DROP_OTHER_TIM       0x00080000
> +#define      MT_WF_RFCR_DROP_NDPA            0x00100000
> +#define      MT_WF_RFCR_DROP_UNWANTED_CTL    0x00200000
> +
> +#define      MT_WF_RFCR1(_band)              MT_BAND_ADDR(_band, 0x5004)
> +#define      MT_WF_RFCR1_DROP_ACK            (1U << 4)
> +#define      MT_WF_RFCR1_DROP_BF_POLL        (1U << 5)
> +#define      MT_WF_RFCR1_DROP_BA             (1U << 6)
> +#define      MT_WF_RFCR1_DROP_CFEND          (1U << 7)
> +#define      MT_WF_RFCR1_DROP_CFACK          (1U << 8)
> +
> +#define      MT_WF_RMAC_MIB_TIME0(_band)     MT_BAND_ADDR(_band, 0x53c4)
> +#define      MT_WF_RMAC_MIB_RXTIME_CLR       (1U << 31)
> +#define      MT_WF_RMAC_MIB_RXTIME_EN        (1U << 30)
> +
> +#define      MT_WF_RMAC_MIB_AIRTIME14(_band) MT_BAND_ADDR(_band, 0x53b8)
> +#define      MT_MIB_OBSSTIME_MASK            0x00ffffff
> +#define      MT_WF_RMAC_MIB_AIRTIME0(_band)  MT_BAND_ADDR(_band, 0x5380)
> +
> +/* WFDMA0 */
> +#define      MT_WFDMA0_BASE                  0xd4000
> +
> +#define      MT_WFDMA0_RST                   0xd4100
> +#define      MT_WFDMA0_RST_LOGIC_RST         (1U << 4)
> +#define      MT_WFDMA0_RST_DMASHDL_ALL_RST   (1U << 5)
> +
> +#define      MT_MCU_CMD                      0xd41f0
> +#define      MT_MCU_CMD_WAKE_RX_PCIE         (1U << 0)
> +#define      MT_MCU_CMD_STOP_DMA_FW_RELOAD   (1U << 1)
> +#define      MT_MCU_CMD_STOP_DMA             (1U << 2)
> +#define      MT_MCU_CMD_RESET_DONE           (1U << 3)
> +#define      MT_MCU_CMD_RECOVERY_DONE        (1U << 4)
> +#define      MT_MCU_CMD_NORMAL_STATE         (1U << 5)
> +#define      MT_MCU_CMD_ERROR_MASK           0x003e
> +
> +#define      MT_MCU2HOST_SW_INT_ENA          0xd41f4
> +
> +#define      MT_WFDMA0_HOST_INT_STA          0xd4200
> +#define      HOST_RX_DONE_INT_STS0           (1U << 0)       /* Rx mcu */
> +#define      HOST_RX_DONE_INT_STS2           (1U << 2)       /* Rx data */
> +#define      HOST_RX_DONE_INT_STS4           (1U << 22)      /* Rx mcu after 
> fw downloaded */
> +#define      HOST_TX_DONE_INT_STS16          (1U << 26)
> +#define      HOST_TX_DONE_INT_STS17          (1U << 27)      /* MCU tx done*/
> +
> +#define      MT_WFDMA0_HOST_INT_ENA          0xd4204
> +#define      HOST_RX_DONE_INT_ENA0           (1U << 0)
> +#define      HOST_RX_DONE_INT_ENA1           (1U << 1)
> +#define      HOST_RX_DONE_INT_ENA2           (1U << 2)
> +#define      HOST_RX_DONE_INT_ENA3           (1U << 3)
> +#define      HOST_TX_DONE_INT_ENA0           (1U << 4)
> +#define      HOST_TX_DONE_INT_ENA1           (1U << 5)
> +#define      HOST_TX_DONE_INT_ENA2           (1U << 6)
> +#define      HOST_TX_DONE_INT_ENA3           (1U << 7)
> +#define      HOST_TX_DONE_INT_ENA4           (1U << 8)
> +#define      HOST_TX_DONE_INT_ENA5           (1U << 9)
> +#define      HOST_TX_DONE_INT_ENA6           (1U << 10)
> +#define      HOST_TX_DONE_INT_ENA7           (1U << 11)
> +#define      HOST_TX_DONE_INT_ENA8           (1U << 12)
> +#define      HOST_TX_DONE_INT_ENA9           (1U << 13)
> +#define      HOST_TX_DONE_INT_ENA10          (1U << 14)
> +#define      HOST_TX_DONE_INT_ENA11          (1U << 15)
> +#define      HOST_TX_DONE_INT_ENA12          (1U << 16)
> +#define      HOST_TX_DONE_INT_ENA13          (1U << 17)
> +#define      HOST_TX_DONE_INT_ENA14          (1U << 18)
> +#define      HOST_RX_COHERENT_EN             (1U << 20)
> +#define      HOST_TX_COHERENT_EN             (1U << 21)
> +#define      HOST_RX_DONE_INT_ENA4           (1U << 22)
> +#define      HOST_RX_DONE_INT_ENA5           (1U << 23)
> +#define      HOST_TX_DONE_INT_ENA16          (1U << 26)
> +#define      HOST_TX_DONE_INT_ENA17          (1U << 27)
> +#define      MCU2HOST_SW_INT_ENA             (1U << 29)
> +#define      HOST_TX_DONE_INT_ENA18          (1U << 30)
> +
> +#define      MT_PCIE_MAC_BASE                0x10000
> +#define      MT_PCIE_MAC_INT_ENABLE          0x10188
> +
> +/* WFDMA interrupt */
> +#define      MT_INT_RX_DONE_DATA             HOST_RX_DONE_INT_ENA2
> +#define      MT_INT_RX_DONE_WM               HOST_RX_DONE_INT_ENA0
> +#define      MT_INT_RX_DONE_WM2              HOST_RX_DONE_INT_ENA4
> +#define      MT_INT_RX_DONE_ALL              (MT_INT_RX_DONE_DATA |  \
> +                                     MT_INT_RX_DONE_WM |     \
> +                                     MT_INT_RX_DONE_WM2)
> +
> +#define      MT_INT_TX_DONE_MCU_WM           HOST_TX_DONE_INT_ENA17
> +#define      MT_INT_TX_DONE_FWDL             HOST_TX_DONE_INT_ENA16
> +#define      MT_INT_TX_DONE_BAND0            HOST_TX_DONE_INT_ENA0
> +#define      MT_INT_MCU_CMD                  MCU2HOST_SW_INT_ENA
> +#define      MT_INT_TX0_TO_TX14              0x7fff0
> +
> +#define      MT_INT_TX_DONE_MCU              (MT_INT_TX_DONE_MCU_WM |        
> \
> +                                     MT_INT_TX_DONE_FWDL)
> +
> +#define      MT_INT_TX_DONE_ALL              (MT_INT_TX_DONE_MCU |           
> \
> +                                     MT_INT_TX0_TO_TX14)
> +
> +#define      MT_WFDMA0_GLO_CFG               0xd4208
> +#define      MT_WFDMA0_GLO_CFG_TX_DMA_EN     (1U << 0)
> +#define      MT_WFDMA0_GLO_CFG_TX_DMA_BUSY   (1U << 1)
> +#define      MT_WFDMA0_GLO_CFG_RX_DMA_EN     (1U << 2)
> +#define      MT_WFDMA0_GLO_CFG_RX_DMA_BUSY   (1U << 3)
> +#define      MT_WFDMA0_GLO_CFG_TX_WB_DDONE   (1U << 6)
> +#define      MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN            (1U << 12)
> +#define      MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN    (1U << 15)
> +#define      MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2            (1U << 21)
> +#define      MT_WFDMA0_GLO_CFG_OMIT_RX_INFO  (1U << 27)
> +#define      MT_WFDMA0_GLO_CFG_OMIT_TX_INFO  (1U << 28)
> +#define      MT_WFDMA0_GLO_CFG_CLK_GAT_DIS   (1U << 30)
> +
> +#define      MT_WFDMA0_RST_DTX_PTR           0xd420c
> +#define      MT_WFDMA0_GLO_CFG_EXT0          0xd42b0
> +#define      MT_WFDMA0_CSR_TX_DMASHDL_ENABLE (1U << 6)
> +#define      MT_WFDMA0_PRI_DLY_INT_CFG0      0xd42f0
> +
> +#define      MT_WFDMA0_TX_RING0_EXT_CTRL     0xd4600
> +#define      MT_WFDMA0_TX_RING1_EXT_CTRL     0xd4604
> +#define      MT_WFDMA0_TX_RING2_EXT_CTRL     0xd4608
> +#define      MT_WFDMA0_TX_RING3_EXT_CTRL     0xd460c
> +#define      MT_WFDMA0_TX_RING4_EXT_CTRL     0xd4610
> +#define      MT_WFDMA0_TX_RING5_EXT_CTRL     0xd4614
> +#define      MT_WFDMA0_TX_RING6_EXT_CTRL     0xd4618
> +#define      MT_WFDMA0_TX_RING16_EXT_CTRL    0xd4640
> +#define      MT_WFDMA0_TX_RING17_EXT_CTRL    0xd4644
> +
> +#define      MT_WFDMA0_RX_RING0_EXT_CTRL     0xd4680
> +#define      MT_WFDMA0_RX_RING1_EXT_CTRL     0xd4684
> +#define      MT_WFDMA0_RX_RING2_EXT_CTRL     0xd4688
> +#define      MT_WFDMA0_RX_RING3_EXT_CTRL     0xd468c
> +#define      MT_WFDMA0_RX_RING4_EXT_CTRL     0xd4690
> +#define      MT_WFDMA0_RX_RING5_EXT_CTRL     0xd4694
> +
> +#define      MT_TX_DATA_RING_BASE            0xd4300
> +#define      MT_TX_FWDL_RING_BASE            0xd4400
> +#define      MT_TX_MCU_RING_BASE             0xd4410
> +#define      MT_RX_DATA_RING_BASE            0xd4520
> +#define      MT_RX_MCU_RING_BASE             0xd4540
> +#define      MT_RX_FWDL_RING_BASE            0xd4500
> +
> +#define      MT_PCIE_MAC_BASE                0x10000
> +#define      MT_PCIE_MAC_INT_ENABLE          0x10188
> +
> +#define      MT_INFRA_CFG_BASE               0xfe000
> +#define      MT_HIF_REMAP_L1                 0xfe24c
> +#define      MT_HIF_REMAP_L1_MASK            0x0000ffff
> +#define      MT_HIF_REMAP_L1_GET_OFFSET(x)   ((x) & 0xffff)
> +#define      MT_HIF_REMAP_L1_GET_BASE(x)     ((x >> 16) & 0xffff)
> +#define      MT_HIF_REMAP_BASE_L1            0x40000
> +
> +#define      MT_SWDEF_BASE                   0x41f200
> +#define      MT_SWDEF_MODE                   0x41f23c
> +#define      MT_SWDEF_NORMAL_MODE            0
> +#define      MT_SWDEF_ICAP_MODE              1
> +#define      MT_SWDEF_SPECTRUM_MODE          2
> +
> +#define      MT_DMASHDL_SW_CONTROL           0xd6004
> +#define      MT_DMASHDL_DMASHDL_BYPASS       (1U << 28)
> +#define      MT_DMASHDL_OPTIONAL             0xd6008
> +#define      MT_DMASHDL_PAGE                 0xd600c
> +#define      MT_DMASHDL_REFILL               0xd6010
> +#define      MT_DMASHDL_PKT_MAX_SIZE         0xd601c
> +#define      MT_DMASHDL_PKT_MAX_SIZE_PLE     0x00000fff
> +#define      MT_DMASHDL_PKT_MAX_SIZE_PSE     0x0fff0000
> +
> +#define      MT_CONN_ON_MISC                 0x7c0600f0
> +#define      MT_TOP_MISC2_FW_N9_RDY          0x3
> +
> +#define      MT_WFSYS_SW_RST_B               0x18000140
> +#define      WFSYS_SW_RST_B                  (1U << 0)
> +#define      WFSYS_SW_INIT_DONE              (1U << 4)
> +
> +#define      MT_TOP_BASE                     0x18060000
> +#define      MT_TOP_LPCR_HOST_BAND0          0x18060010
> +#define      MT_TOP_LPCR_HOST_FW_OWN         0x0001
> +#define      MT_TOP_LPCR_HOST_DRV_OWN        0x0002
> +
> +#define      MT_MCU_WPDMA0_BASE              0x54000000
> +#define      MT_WFDMA_DUMMY_CR               0x54000120
> +#define      MT_WFDMA_NEED_REINIT            (1U << 1)
> +
> +#define      MT_HW_CHIPID                    0x70010200
> +#define      MT_HW_REV                       0x70010204
> +
> +#define      MT_DMA_DESC_BASE                0
> +#define      MT_DMA_RING_SIZE                4
> +#define      MT_DMA_CPU_IDX                  8
> +#define      MT_DMA_DMA_IDX                  12
> +
> +#define      MT_DMA_CTL_SD_LEN_MASK          0x00003fff
> +#define      MT_DMA_CTL_SD_LEN0_SHIFT        16
> +#define      MT_DMA_CTL_LAST_SEC1            (1U << 14)
> +#define      MT_DMA_CTL_BURST                (1U << 15)
> +#define      MT_DMA_CTL_LAST_SEC0            (1U << 30)
> +#define      MT_DMA_CTL_DMA_DONE             (1U << 31)
> +#define      MT_DMA_CTL_SD_LEN1(x)           ((x) & MT_DMA_CTL_SD_LEN_MASK)
> +#define      MT_DMA_CTL_SD_LEN0(x)           \
> +         (((x) & MT_DMA_CTL_SD_LEN_MASK) << MT_DMA_CTL_SD_LEN0_SHIFT)
> +#define      MT_DMA_CTL_SD_GET_LEN1(c)       ((c) & MT_DMA_CTL_SD_LEN_MASK)
> +#define      MT_DNA_CTL_SD_GET_LEN0(c)       \
> +         (((c) >> MT_DMA_CTL_SD_LEN0_SHIFT) & MT_DMA_CTL_SD_LEN_MASK)
> +
> +#define      MT7921_MCU_INIT_RETRY_COUNT     10
> +
> +/* it seems most of the txqueues are unused, actually all but 0 */
> +enum mt76_txq_id {
> +     MT_TXQ_VO,
> +     MT_TXQ_VI,
> +     MT_TXQ_BE,
> +     MT_TXQ_BK,
> +     MT_TXQ_PSD,
> +     MT_TXQ_BEACON,
> +     MT_TXQ_CAB,
> +     __MT_TXQ_MAX
> +};
> +
> +enum mt76_mcuq_id {
> +     MT_MCUQ_WM,
> +     MT_MCUQ_WA,
> +     MT_MCUQ_FWDL,
> +     __MT_MCUQ_MAX
> +};
> +
> +enum mt76_rxq_id {
> +     MT_RXQ_MAIN,
> +     MT_RXQ_MCU,
> +     MT_RXQ_MCU_WA,
> +     MT_RXQ_EXT,
> +     MT_RXQ_EXT_WA,
> +     __MT_RXQ_MAX
> +};
> +
> +#define      MT7921_MAX_INTERFACES   4
> +#define      MT7921_MAX_WMM_SETS     4
> +#define      MT7921_WTBL_SIZE        20
> +#define      MT7921_WTBL_RESERVED    (MT7921_WTBL_SIZE - 1)
> +#define      MT7921_WTBL_STA         (MT7921_WTBL_RESERVED - 
> MT7921_MAX_INTERFACES)
> +
> +#define      MT_RX_BUF_SIZE          2048
> +#define      MT_MAX_SCATTER          32
> +#define      MT_MAX_SIZE             MT_DMA_CTL_SD_LEN_MASK
> +
> +#define      MWX_WCID_MAX            288
> +
> +struct mt76_desc {
> +     volatile uint32_t       buf0;
> +     volatile uint32_t       ctrl;
> +     volatile uint32_t       buf1;
> +     volatile uint32_t       info;
> +} __packed __aligned(4);
> +
> +#define      MCU_Q_QUERY                             0
> +#define      MCU_Q_SET                               1
> +#define      MCU_Q_RESERVED                          2
> +#define      MCU_Q_NA                                3
> +
> +#define      CMD_S2D_IDX_H2N                         0
> +#define      CMD_S2D_IDX_C2N                         1
> +#define      CMD_S2D_IDX_H2C                         2
> +#define      CMD_S2D_IDX_H2N_AND_H2C                 3
> +
> +#define      MCU_CMD_ACK                             0x01
> +#define      MCU_CMD_UNI                             0x02
> +#define      MCU_CMD_QUERY                           0x04
> +#define      MCU_CMD_UNI_EXT_ACK     (MCU_CMD_ACK | MCU_CMD_UNI | 
> MCU_CMD_QUERY)
> +
> +#define      MCU_CMD_FIELD_ID_MASK                   0x000000ff
> +#define      MCU_CMD_FIELD_EXT_ID_MASK               0x0000ff00
> +
> +#define      MCU_CMD_FIELD_QUERY                     (1U << 16)
> +#define      MCU_CMD_FIELD_UNI                       (1U << 17)
> +#define      MCU_CMD_FIELD_CE                        (1U << 18)
> +#define      MCU_CMD_FIELD_WA                        (1U << 19)
> +
> +#define      MCU_CMD_TARGET_ADDRESS_LEN_REQ          0x00000001
> +#define      MCU_CMD_FW_START_REQ                    0x00000002
> +#define      MCU_CMD_INIT_ACCESS_REG                 0x00000003
> +#define      MCU_CMD_NIC_POWER_CTRL                  0x00000004
> +#define      MCU_CMD_PATCH_START_REQ                 0x00000005
> +#define      MCU_CMD_PATCH_FINISH_REQ                0x00000007
> +#define      MCU_CMD_PATCH_SEM_CONTROL               0x00000010
> +#define      MCU_CMD_WA_PARAM                        0x000000c4
> +#define      MCU_CMD_EXT_CID                         0x000000ed
> +#define      MCU_CMD_FW_SCATTER                      0x000000ee
> +#define      MCU_CMD_RESTART_DL_REQ                  0x000000ef
> +
> +/* MCU_EXT_CMD use MCU_CMD_EXT_CID as FIELD_ID plus FIELD_EXT_ID */
> +#define      MCU_EXT_CMD_EFUSE_ACCESS                0x000001ed
> +#define      MCU_EXT_CMD_RF_REG_ACCESS               0x000002ed
> +#define      MCU_EXT_CMD_RF_TEST                     0x000004ed
> +#define      MCU_EXT_CMD_PM_STATE_CTRL               0x000007ed
> +#define      MCU_EXT_CMD_CHANNEL_SWITCH              0x000008ed
> +#define      MCU_EXT_CMD_SET_TX_POWER_CTRL           0x000011ed
> +#define      MCU_EXT_CMD_FW_LOG_2_HOST               0x000013ed
> +#define      MCU_EXT_CMD_TXBF_ACTION                 0x00001eed
> +#define      MCU_EXT_CMD_EFUSE_BUFFER_MODE           0x000021ed
> +#define      MCU_EXT_CMD_THERMAL_PROT                0x000023ed
> +#define      MCU_EXT_CMD_STA_REC_UPDATE              0x000025ed
> +#define      MCU_EXT_CMD_BSS_INFO_UPDATE             0x000026ed
> +#define      MCU_EXT_CMD_EDCA_UPDATE                 0x000027ed
> +#define      MCU_EXT_CMD_DEV_INFO_UPDATE             0x00002Aed
> +#define      MCU_EXT_CMD_THERMAL_CTRL                0x00002ced
> +#define      MCU_EXT_CMD_WTBL_UPDATE                 0x000032ed
> +#define      MCU_EXT_CMD_SET_DRR_CTRL                0x000036ed
> +#define      MCU_EXT_CMD_SET_RDD_CTRL                0x00003aed
> +#define      MCU_EXT_CMD_ATE_CTRL                    0x00003ded
> +#define      MCU_EXT_CMD_PROTECT_CTRL                0x00003eed
> +#define      MCU_EXT_CMD_DBDC_CTRL                   0x000045ed
> +#define      MCU_EXT_CMD_MAC_INIT_CTRL               0x000046ed
> +#define      MCU_EXT_CMD_RX_HDR_TRANS                0x000047ed
> +#define      MCU_EXT_CMD_MUAR_UPDATE                 0x000048ed
> +#define      MCU_EXT_CMD_BCN_OFFLOAD                 0x000049ed
> +#define      MCU_EXT_CMD_RX_AIRTIME_CTRL             0x00004aed
> +#define      MCU_EXT_CMD_SET_RX_PATH                 0x00004eed
> +#define      MCU_EXT_CMD_EFUSE_FREE_BLOCK            0x00004fed
> +#define      MCU_EXT_CMD_TX_POWER_FEATURE_CTRL       0x000058ed
> +#define      MCU_EXT_CMD_RXDCOC_CAL                  0x000059ed
> +#define      MCU_EXT_CMD_GET_MIB_INFO                0x00005aed
> +#define      MCU_EXT_CMD_TXDPD_CAL                   0x000060ed
> +#define      MCU_EXT_CMD_CAL_CACHE                   0x000067ed
> +#define      MCU_EXT_CMD_SET_RADAR_TH                0x00007ced
> +#define      MCU_EXT_CMD_SET_RDD_PATTERN             0x00007ded
> +#define      MCU_EXT_CMD_MWDS_SUPPORT                0x000080ed
> +#define      MCU_EXT_CMD_SET_SER_TRIGGER             0x000081ed
> +#define      MCU_EXT_CMD_SCS_CTRL                    0x000082ed
> +#define      MCU_EXT_CMD_TWT_AGRT_UPDATE             0x000094ed
> +#define      MCU_EXT_CMD_FW_DBG_CTRL                 0x000095ed
> +#define      MCU_EXT_CMD_SET_RDD_TH                  0x00009ded
> +#define      MCU_EXT_CMD_MURU_CTRL                   0x00009fed
> +#define      MCU_EXT_CMD_SET_SPR                     0x0000a8ed
> +#define      MCU_EXT_CMD_GROUP_PRE_CAL_INFO          0x0000abed
> +#define      MCU_EXT_CMD_DPD_PRE_CAL_INFO            0x0000aced
> +#define      MCU_EXT_CMD_PHY_STAT_INFO               0x0000aded
> +
> +#define      MCU_GET_EXT_CMD(x)              (((x) & 
> MCU_CMD_FIELD_EXT_ID_MASK) >> 8)
> +
> +#define      MCU_UNI_CMD_DEV_INFO_UPDATE             0x00020001
> +#define      MCU_UNI_CMD_BSS_INFO_UPDATE             0x00020002
> +#define      MCU_UNI_CMD_STA_REC_UPDATE              0x00020003
> +#define      MCU_UNI_CMD_SUSPEND                     0x00020005
> +#define      MCU_UNI_CMD_OFFLOAD                     0x00020006
> +#define      MCU_UNI_CMD_HIF_CTRL                    0x00020007
> +#define      MCU_UNI_CMD_SNIFFER                     0x00020024
> +
> +#define      UNI_BSS_INFO_BASIC                      0
> +#define      UNI_BSS_INFO_RLM                        2
> +#define      UNI_BSS_INFO_BSS_COLOR                  4
> +#define      UNI_BSS_INFO_HE_BASIC                   5
> +#define      UNI_BSS_INFO_BCN_CONTENT                7
> +#define      UNI_BSS_INFO_QBSS                       15
> +#define      UNI_BSS_INFO_UAPSD                      19
> +#define      UNI_BSS_INFO_PS                         21
> +#define      UNI_BSS_INFO_BCNFT                      22
> +
> +/* offload mcu commands */
> +#define      MCU_CE_CMD_TEST_CTRL                    0x00040001
> +#define      MCU_CE_CMD_START_HW_SCAN                0x00040003
> +#define      MCU_CE_CMD_SET_PS_PROFILE               0x00040005
> +#define      MCU_CE_CMD_SET_CHAN_DOMAIN              0x0004000f
> +#define      MCU_CE_CMD_SET_BSS_CONNECTED            0x00040016
> +#define      MCU_CE_CMD_SET_BSS_ABORT                0x00040017
> +#define      MCU_CE_CMD_CANCEL_HW_SCAN               0x0004001b
> +#define      MCU_CE_CMD_SET_ROC                      0x0004001d
> +#define      MCU_CE_CMD_SET_P2P_OPPPS                0x00040033
> +#define      MCU_CE_CMD_SET_RATE_TX_POWER            0x0004005d
> +#define      MCU_CE_CMD_SCHED_SCAN_ENABLE            0x00040061
> +#define      MCU_CE_CMD_SCHED_SCAN_REQ               0x00040062
> +#define      MCU_CE_CMD_GET_NIC_CAPAB                0x0004008a
> +#define      MCU_CE_CMD_SET_MU_EDCA_PARMS            0x000400b0
> +#define      MCU_CE_CMD_REG_WRITE                    0x000400c0
> +#define      MCU_CE_CMD_REG_READ                     0x000400c0
> +#define      MCU_CE_CMD_CHIP_CONFIG                  0x000400ca
> +#define      MCU_CE_CMD_FWLOG_2_HOST                 0x000400c5
> +#define      MCU_CE_CMD_GET_WTBL                     0x000400cd
> +#define      MCU_CE_CMD_GET_TXPWR                    0x000400d0
> +#define      MCU_CE_QUERY_REG_READ   (MCU_CE_CMD_REG_READ | 
> MCU_CMD_FIELD_QUERY)
> +
> +/* event commands */
> +#define      MCU_EVENT_TARGET_ADDRESS_LEN            0x01
> +#define      MCU_EVENT_FW_START                      0x01
> +#define      MCU_EVENT_GENERIC                       0x01
> +#define      MCU_EVENT_ACCESS_REG                    0x02
> +#define      MCU_EVENT_MT_PATCH_SEM                  0x04
> +#define      MCU_EVENT_REG_ACCESS                    0x05
> +#define      MCU_EVENT_LP_INFO                       0x07
> +#define      MCU_EVENT_SCAN_DONE                     0x0d
> +#define      MCU_EVENT_TX_DONE                       0x0f
> +#define      MCU_EVENT_ROC                           0x10
> +#define      MCU_EVENT_BSS_ABSENCE                   0x11
> +#define      MCU_EVENT_BSS_BEACON_LOSS               0x13
> +#define      MCU_EVENT_CH_PRIVILEGE                  0x18
> +#define      MCU_EVENT_SCHED_SCAN_DONE               0x23
> +#define      MCU_EVENT_DBG_MSG                       0x27
> +#define      MCU_EVENT_TXPWR                         0xd0
> +#define      MCU_EVENT_EXT                           0xed
> +#define      MCU_EVENT_RESTART_DL                    0xef
> +#define      MCU_EVENT_COREDUMP                      0xf0
> +
> +/* extended event commands */
> +#define      MCU_EXT_EVENT_PS_SYNC                   0x5
> +#define      MCU_EXT_EVENT_FW_LOG_2_HOST             0x13
> +#define      MCU_EXT_EVENT_THERMAL_PROTECT           0x22
> +#define      MCU_EXT_EVENT_ASSERT_DUMP               0x23
> +#define      MCU_EXT_EVENT_RDD_REPORT                0x3a
> +#define      MCU_EXT_EVENT_CSA_NOTIFY                0x4f
> +#define      MCU_EXT_EVENT_BCC_NOTIFY                0x75
> +#define      MCU_EXT_EVENT_RATE_REPORT               0x87
> +#define      MCU_EXT_EVENT_MURU_CTRL                 0x9f
> +
> +#define      MCU_PQ_ID(p, q)                         (((p) << 15) | ((q) << 
> 10))
> +#define      MCU_PKT_ID                              0xa0
> +
> +/* values for MT_TXD0_PKT_FMT */
> +#define      MT_TX_TYPE_CT                           (0 << 23)
> +#define      MT_TX_TYPE_SF                           (1 << 23)
> +#define      MT_TX_TYPE_CMD                          (2 << 23)
> +#define      MT_TX_TYPE_FW                           (3 << 23)
> +
> +/* values for port idx */
> +#define      MT_TX_PORT_IDX_LMAC                     0
> +#define      MT_TX_PORT_IDX_MCU                      1
> +
> +/* valus for EEPROM command */
> +#define      EE_MODE_EFUSE           0
> +#define      EE_MODE_BUFFER          1
> +
> +#define      EE_FORMAT_BIN           0
> +#define      EE_FORMAT_WHOLE         1
> +#define      EE_FORMAT_MULTIPLE      2
> +
> +
> +/* values for MT_TXD0_Q_IDX */
> +#define      MT_TX_MCU_PORT_RX_Q0                    0x20
> +#define      MT_TX_MCU_PORT_RX_Q1                    0x21
> +#define      MT_TX_MCU_PORT_RX_Q2                    0x22
> +#define      MT_TX_MCU_PORT_RX_Q3                    0x23
> +#define      MT_TX_MCU_PORT_RX_FWDL                  0x3e
> +
> +#define      MT_TXD0_Q_IDX_MASK                      0xfe000000
> +#define      MT_TXD0_Q_IDX(x)                (((x) << 25) & 
> MT_TXD0_Q_IDX_MASK)
> +#define      MT_TXD0_PKT_FMT                         0x01800000
> +#define      MT_TXD0_ETH_TYPE_OFFSET                 0x007f0000
> +#define      MT_TXD0_TX_BYTES_MASK                   0x0000ffff
> +
> +/* values for MT_TXD1_HDR_FORMAT */
> +#define      MT_HDR_FORMAT_802_3                     (0 << 16)
> +#define      MT_HDR_FORMAT_CMD                       (1 << 16)
> +#define      MT_HDR_FORMAT_802_11                    (2 << 16)
> +#define      MT_HDR_FORMAT_802_11_EXT                (3 << 16)
> +
> +#define      MT_TXD1_LONG_FORMAT                     (1U << 31)
> +#define      MT_TXD1_TGID                            (1U << 30)
> +#define      MT_TXD1_OWN_MAC                         GENMASK(29, 24)
> +#define      MT_TXD1_AMSDU                           (1U << 23)
> +#define      MT_TXD1_TID                             GENMASK(22, 20)
> +#define      MT_TXD1_HDR_PAD                         GENMASK(19, 18)
> +#define      MT_TXD1_HDR_FORMAT                      0x00030000
> +#define      MT_TXD1_HDR_INFO                        GENMASK(15, 11)
> +#define      MT_TXD1_ETH_802_3                       (1U << 15)
> +#define      MT_TXD1_VTA                             (1U << 10)
> +#define      MT_TXD1_WLAN_IDX_MASK                   GENMASK(9, 0)
> +
> +#define      MT_RXD0_LENGTH_MASK                     0x0000ffff
> +#define      MT_RXD0_PKT_FLAG_MASK                   0x000f0000
> +#define      MT_RXD0_PKT_FLAG_SHIFT                  16
> +#define      MT_RXD0_NORMAL_ETH_TYPE_OFS             0x007f0000
> +#define      MT_RXD0_NORMAL_IP_SUM                   (1U << 23)
> +#define      MT_RXD0_NORMAL_UDP_TCP_SUM              (1U << 24)
> +#define      MT_RXD0_PKT_TYPE_MASK                   0xf8000000
> +#define      MT_RXD0_PKT_TYPE_SHIFT                  27
> +#define      MT_RXD0_PKT_TYPE_GET(x)         \
> +         (((x) & MT_RXD0_PKT_TYPE_MASK) >> MT_RXD0_PKT_TYPE_SHIFT)
> +
> +/* RXD DW1 */
> +#define MT_RXD1_NORMAL_WLAN_IDX_MASK         0x000003ff
> +#define MT_RXD1_NORMAL_GROUP_1                       (1U << 11)
> +#define MT_RXD1_NORMAL_GROUP_2                       (1U << 12)
> +#define MT_RXD1_NORMAL_GROUP_3                       (1U << 13)
> +#define MT_RXD1_NORMAL_GROUP_4                       (1U << 14)
> +#define MT_RXD1_NORMAL_GROUP_5                       (1U << 15)
> +#define MT_RXD1_NORMAL_SEC_MODE_MASK         0x001f0000
> +#define MT_RXD1_NORMAL_SEC_MODE_SHIFT                16
> +#define MT_RXD1_NORMAL_KEY_ID_MASK           0x00600000
> +#define MT_RXD1_NORMAL_CM                    (1U << 23)
> +#define MT_RXD1_NORMAL_CLM                   (1U << 24)
> +#define MT_RXD1_NORMAL_ICV_ERR                       (1U << 25)
> +#define MT_RXD1_NORMAL_TKIP_MIC_ERR          (1U << 26)
> +#define MT_RXD1_NORMAL_FCS_ERR                       (1U << 27)
> +#define MT_RXD1_NORMAL_BAND_IDX                      (1U << 28)
> +#define MT_RXD1_NORMAL_SPP_EN                        (1U << 29)
> +#define MT_RXD1_NORMAL_ADD_OM                        (1U << 30)
> +#define MT_RXD1_NORMAL_SEC_DONE                      (1U << 31)
> +
> +/* RXD DW2 */
> +#define MT_RXD2_NORMAL_BSSID                 0x0000003f
> +#define MT_RXD2_NORMAL_CO_ANT                        (1U << 6)
> +#define MT_RXD2_NORMAL_BF_CQI                        (1U << 7)
> +#define MT_RXD2_NORMAL_MAC_HDR_LEN           0x00001f00
> +#define MT_RXD2_NORMAL_HDR_TRANS             (1U << 13)
> +#define MT_RXD2_NORMAL_HDR_OFFSET_MASK               0x0000c000
> +#define MT_RXD2_NORMAL_HDR_OFFSET_SHIFT              14
> +#define MT_RXD2_NORMAL_TID                   0x000f0000
> +#define MT_RXD2_NORMAL_MU_BAR                        (1U << 21)
> +#define MT_RXD2_NORMAL_SW_BIT                        (1U << 22)
> +#define MT_RXD2_NORMAL_AMSDU_ERR             (1U << 23)
> +#define MT_RXD2_NORMAL_MAX_LEN_ERROR         (1U << 24)
> +#define MT_RXD2_NORMAL_HDR_TRANS_ERROR               (1U << 25)
> +#define MT_RXD2_NORMAL_INT_FRAME             (1U << 26)
> +#define MT_RXD2_NORMAL_FRAG                  (1U << 27)
> +#define MT_RXD2_NORMAL_NULL_FRAME            (1U << 28)
> +#define MT_RXD2_NORMAL_NDATA                 (1U << 29)
> +#define MT_RXD2_NORMAL_NON_AMPDU             (1U << 30)
> +#define MT_RXD2_NORMAL_BF_REPORT             (1U << 31)
> +
> +/* RXD DW3 */
> +#define MT_RXD3_NORMAL_RXV_SEQ_MASK          0x000000ff
> +#define MT_RXD3_NORMAL_CH_FREQ_MASK          0x0000ff00
> +#define MT_RXD3_NORMAL_CH_FREQ_SHIFT         8
> +#define MT_RXD3_NORMAL_ADDR_TYPE_MASK                0x00030000
> +#define MT_RXD3_NORMAL_U2M                   (1U << 16)
> +#define MT_RXD3_NORMAL_HTC_VLD                       (1U << 0)
> +#define MT_RXD3_NORMAL_TSF_COMPARE_LOSS              (1U << 19)
> +#define MT_RXD3_NORMAL_BEACON_MC             (1U << 20)
> +#define MT_RXD3_NORMAL_BEACON_UC             (1U << 21)
> +#define MT_RXD3_NORMAL_AMSDU                 (1U << 22)
> +#define MT_RXD3_NORMAL_MESH                  (1U << 23)
> +#define MT_RXD3_NORMAL_MHCP                  (1U << 24)
> +#define MT_RXD3_NORMAL_NO_INFO_WB            (1U << 25)
> +#define MT_RXD3_NORMAL_DISABLE_RX_HDR_TRANS  (1U << 26)
> +#define MT_RXD3_NORMAL_POWER_SAVE_STAT               (1U << 27)
> +#define MT_RXD3_NORMAL_MORE                  (1U << 28)
> +#define MT_RXD3_NORMAL_UNWANT                        (1U << 29)
> +#define MT_RXD3_NORMAL_RX_DROP                       (1U << 30)
> +#define MT_RXD3_NORMAL_VLAN2ETH                      (1U << 31)
> +
> +/* RXD DW4 */
> +#define MT_RXD4_NORMAL_PAYLOAD_FORMAT                0x00000003
> +#define MT_RXD4_FIRST_AMSDU_FRAME            0x3
> +#define MT_RXD4_MID_AMSDU_FRAME                      0x2
> +#define MT_RXD4_LAST_AMSDU_FRAME             0x1
> +#define MT_RXD4_NORMAL_PATTERN_DROP          (1U << 9)
> +#define MT_RXD4_NORMAL_CLS                   (1U << 10)
> +#define MT_RXD4_NORMAL_OFLD             GENMASK(12, 11)
> +#define MT_RXD4_NORMAL_MAGIC_PKT             (1U << 13)
> +#define MT_RXD4_NORMAL_WOL              GENMASK(18, 14)
> +#define MT_RXD4_NORMAL_CLS_BITMAP       GENMASK(28, 19)
> +#define MT_RXD3_NORMAL_PF_MODE                       (1U << 99)
> +#define MT_RXD3_NORMAL_PF_STS           GENMASK(31, 30)
> +
> +#define      PKT_TYPE_TXS                            0
> +#define      PKT_TYPE_TXRXV                          1
> +#define      PKT_TYPE_NORMAL                         2
> +#define      PKT_TYPE_RX_DUP_RFB                     3
> +#define      PKT_TYPE_RX_TMR                         4
> +#define      PKT_TYPE_RETRIEVE                       5
> +#define      PKT_TYPE_TXRX_NOTIFY                    6
> +#define      PKT_TYPE_RX_EVENT                       7
> +#define      PKT_TYPE_NORMAL_MCU                     8
> +
> +struct mt7921_mcu_txd {
> +     uint32_t        txd[8];
> +
> +     uint16_t        len;
> +     uint16_t        pq_id;
> +
> +     uint8_t         cid;
> +     uint8_t         pkt_type;
> +     uint8_t         set_query; /* FW don't care */
> +     uint8_t         seq;
> +
> +     uint8_t         uc_d2b0_rev;
> +     uint8_t         ext_cid;
> +     uint8_t         s2d_index;
> +     uint8_t         ext_cid_ack;
> +
> +     uint32_t        reserved[5];
> +} __packed __aligned(4);
> +
> +/**
> + * struct mt7921_uni_txd - mcu command descriptor for firmware v3
> + * @txd: hardware descriptor
> + * @len: total length not including txd
> + * @cid: command identifier
> + * @pkt_type: must be 0xa0 (cmd packet by long format)
> + * @frag_n: fragment number
> + * @seq: sequence number
> + * @checksum: 0 mean there is no checksum
> + * @s2d_index: index for command source and destination
> + *  Definition                       | value | note
> + *  CMD_S2D_IDX_H2N          | 0x00  | command from HOST to WM
> + *  CMD_S2D_IDX_C2N          | 0x01  | command from WA to WM
> + *  CMD_S2D_IDX_H2C          | 0x02  | command from HOST to WA
> + *  CMD_S2D_IDX_H2N_AND_H2C  | 0x03  | command from HOST to WA and WM
> + *
> + * @option: command option
> + *  BIT[0]:  UNI_CMD_OPT_BIT_ACK
> + *           set to 1 to request a fw reply
> + *           if UNI_CMD_OPT_BIT_0_ACK is set and UNI_CMD_OPT_BIT_2_SET_QUERY
> + *           is set, mcu firmware will send response event EID = 0x01
> + *           (UNI_EVENT_ID_CMD_RESULT) to the host.
> + *  BIT[1]:  UNI_CMD_OPT_BIT_UNI_CMD
> + *           0: original command
> + *           1: unified command
> + *  BIT[2]:  UNI_CMD_OPT_BIT_SET_QUERY
> + *           0: QUERY command
> + *           1: SET command
> + */
> +struct mt7921_uni_txd {
> +     uint32_t        txd[8];
> +
> +     /* DW1 */
> +     uint16_t        len;
> +     uint16_t        cid;
> +
> +     /* DW2 */
> +     uint8_t         reserved;
> +     uint8_t         pkt_type;
> +     uint8_t         frag_n;
> +     uint8_t         seq;
> +
> +     /* DW3 */
> +     uint16_t        checksum;
> +     uint8_t         s2d_index;
> +     uint8_t         option;
> +
> +     /* DW4 */
> +     uint8_t         reserved2[4];
> +} __packed __aligned(4);
> +
> +
> +struct mt7921_mcu_rxd {
> +     uint32_t        rxd[6];
> +     uint16_t        len;            /* includes hdr but without rxd[6] */
> +     uint16_t        pkt_type_id;
> +     uint8_t         eid;
> +     uint8_t         seq;
> +     uint16_t        pad0;
> +     uint8_t         ext_eid;
> +     uint8_t         pad1[2];
> +     uint8_t         s2d_index;
> +} __packed;
> +
> +struct mt7921_mcu_uni_event {
> +     uint8_t         cid;
> +     uint8_t         pad[3];
> +     uint32_t        status; /* 0: success, others: fail */
> +} __packed;
> +
> +struct mt7921_mcu_reg_event {
> +     uint32_t        reg;
> +     uint32_t        val;
> +} __packed;
> +
> +struct mt76_connac_config {
> +     uint16_t        id;
> +     uint8_t         type;
> +     uint8_t         resp_type;
> +     uint16_t        data_size;
> +     uint16_t        resv;
> +     uint8_t         data[320];
> +};
> +
> +struct mt76_connac_bss_basic_tlv {
> +     uint16_t        tag;
> +     uint16_t        len;
> +     uint8_t         active;
> +     uint8_t         omac_idx;
> +     uint8_t         hw_bss_idx;
> +     uint8_t         band_idx;
> +     uint32_t        conn_type;
> +     uint8_t         conn_state;
> +     uint8_t         wmm_idx;
> +     uint8_t         bssid[ETHER_ADDR_LEN];
> +     uint16_t        bmc_tx_wlan_idx;
> +     uint16_t        bcn_interval;
> +     uint8_t         dtim_period;
> +     uint8_t         phymode;        /* bit(0): A
> +                                      * bit(1): B
> +                                      * bit(2): G
> +                                      * bit(3): GN
> +                                      * bit(4): AN
> +                                      * bit(5): AC
> +                                      * bit(6): AX2
> +                                      * bit(7): AX5
> +                                      * bit(8): AX6
> +                                      */
> +     uint16_t        sta_idx;
> +     uint16_t        nonht_basic_phy;
> +     uint8_t         phymode_ext; /* bit(0) AX_6G */
> +     uint8_t         pad[1];
> +} __packed;
> +
> +struct mt76_connac_mcu_scan_ssid {
> +     uint32_t        ssid_len;
> +     uint8_t         ssid[IEEE80211_NWID_LEN];
> +} __packed;
> +
> +struct mt76_connac_mcu_scan_channel {
> +     uint8_t          band;  /* 1: 2.4GHz
> +                              * 2: 5.0GHz
> +                              * Others: Reserved
> +                              */
> +     uint8_t          channel_num;
> +} __packed;
> +
> +struct mt76_connac_mcu_scan_match {
> +     uint32_t        rssi_th;
> +     uint8_t         ssid[IEEE80211_NWID_LEN];
> +     uint8_t         ssid_len;
> +     uint8_t         rsv[3];
> +} __packed;
> +
> +#define      SCAN_FUNC_RANDOM_MAC            0x1
> +#define      SCAN_FUNC_SPLIT_SCAN            0x20
> +#define      MT76_HW_SCAN_IE_LEN             600
> +
> +struct mt76_connac_hw_scan_req {
> +     uint8_t         seq_num;
> +     uint8_t         bss_idx;
> +     uint8_t         scan_type;      /* 0: PASSIVE SCAN
> +                                      * 1: ACTIVE SCAN
> +                                      */
> +     uint8_t         ssid_type;      /* 0x1 wildcard SSID
> +                                      * 0x2 P2P wildcard SSID
> +                                      * 0x4 specified SSID + wildcard SSID
> +                                      * 0x4 + ssid_type_ext 0x1
> +                                      *     specified SSID only
> +                                      */
> +     uint8_t         ssids_num;
> +     uint8_t         probe_req_num;  /* Number of probe request per SSID */
> +     uint8_t         scan_func;      /* 0x1 Enable random MAC scan
> +                                      * 0x2 Disable DBDC scan type 1~3.
> +                                      * 0x4 Use DBDC scan type 3
> +                                      *    (dedicated one RF to scan).
> +                                      */
> +     uint8_t         version;        /* 0: Not support fields after ies.
> +                                      * 1: Support fields after ies.
> +                                      */
> +     struct mt76_connac_mcu_scan_ssid ssids[4];
> +     uint16_t        probe_delay_time;
> +     uint16_t        channel_dwell_time;     /* channel Dwell interval */
> +     uint16_t        timeout_value;
> +     uint8_t         channel_type;   /* 0: Full channels
> +                                      * 1: Only 2.4GHz channels
> +                                      * 2: Only 5GHz channels
> +                                      * 3: P2P social channels only
> +                                      *     (channel #1, #6 and #11)
> +                                      * 4: Specified channels
> +                                      * Others: Reserved
> +                                      */
> +     uint8_t         channels_num;   /* valid when channel_type is 4 */
> +     /* valid when channels_num is set */
> +     struct mt76_connac_mcu_scan_channel channels[32];
> +     uint16_t        ies_len;
> +     uint8_t         ies[MT76_HW_SCAN_IE_LEN];
> +     /* following fields are valid if version > 0 */
> +     uint8_t         ext_channels_num;
> +     uint8_t         ext_ssids_num;
> +     uint16_t        channel_min_dwell_time;
> +     struct mt76_connac_mcu_scan_channel ext_channels[32];
> +     struct mt76_connac_mcu_scan_ssid ext_ssids[6];
> +     uint8_t         bssid[ETHER_ADDR_LEN];
> +     /* valid when BIT(1) in scan_func is set. */
> +     uint8_t         random_mac[ETHER_ADDR_LEN];
> +     uint8_t         pad[63];
> +     uint8_t         ssid_type_ext;
> +} __packed;
> +
> +#define      MT76_HW_SCAN_DONE_MAX_CHANNEL_NUM               64
> +
> +struct mt76_connac_hw_scan_done {
> +     uint8_t         seq_num;
> +     uint8_t         sparse_channel_num;
> +     struct mt76_connac_mcu_scan_channel sparse_channel;
> +     uint8_t         complete_channel_num;
> +     uint8_t         current_state;
> +     uint8_t         version;
> +     uint8_t         pad;
> +     uint32_t        beacon_scan_num;
> +     uint8_t         pno_enabled;
> +     uint8_t         pad2[3];
> +     uint8_t         sparse_channel_valid_num;
> +     uint8_t         pad3[3];
> +     uint8_t         channel_num[MT76_HW_SCAN_DONE_MAX_CHANNEL_NUM];
> +     /* idle format for channel_idle_time
> +      * 0: first bytes: idle time(ms) 2nd byte: dwell time(ms)
> +      * 1: first bytes: idle time(8ms) 2nd byte: dwell time(8ms)
> +      * 2: dwell time (16us)
> +      */
> +     uint16_t        channel_idle_time[MT76_HW_SCAN_DONE_MAX_CHANNEL_NUM];
> +     /* beacon and probe response count */
> +     uint8_t         beacon_probe_num[MT76_HW_SCAN_DONE_MAX_CHANNEL_NUM];
> +     uint8_t         mdrdy_count[MT76_HW_SCAN_DONE_MAX_CHANNEL_NUM];
> +     uint32_t        beacon_2g_num;
> +     uint32_t        beacon_5g_num;
> +} __packed;
> +
> +struct mt7921_patch_hdr {
> +     char            build_date[16];
> +     char            platform[4];
> +     uint32_t        hw_sw_ver;
> +     uint32_t        patch_ver;
> +     uint16_t        checksum;
> +     uint16_t        reserved;
> +     struct {
> +             uint32_t        patch_ver;
> +             uint32_t        subsys;
> +             uint32_t        feature;
> +             uint32_t        n_region;
> +             uint32_t        crc;
> +             uint32_t        reserved[11];
> +     } desc;
> +} __packed;
> +
> +struct mt7921_patch_sec {
> +     uint32_t        type;
> +     uint32_t        offs;
> +     uint32_t        size;
> +     union {
> +             uint32_t                spec[13];
> +             struct {
> +                     uint32_t        addr;
> +                     uint32_t        len;
> +                     uint32_t        sec_key_idx;
> +                     uint32_t        align_len;
> +                     uint32_t        reserved[9];
> +             } info;
> +     };
> +} __packed;
> +
> +struct mt7921_fw_trailer {
> +     uint8_t         chip_id;
> +     uint8_t         eco_code;
> +     uint8_t         n_region;
> +     uint8_t         format_ver;
> +     uint8_t         format_flag;
> +     uint8_t         reserved[2];
> +     char            fw_ver[10];
> +     char            build_date[15];
> +     uint32_t        crc;
> +} __packed;
> +
> +struct mt7921_fw_region {
> +     uint32_t        decomp_crc;
> +     uint32_t        decomp_len;
> +     uint32_t        decomp_blk_sz;
> +     uint8_t         reserved[4];
> +     uint32_t        addr;
> +     uint32_t        len;
> +     uint8_t         feature_set;
> +     uint8_t         reserved1[15];
> +} __packed;
> +
> +#define      FW_START_OVERRIDE               0x01
> +#define      FW_START_WORKING_PDA_CR4        0x02
> +
> +#define      FW_FEATURE_SET_ENCRYPT          0x1
> +#define      FW_FEATURE_SET_KEY_IDX_MASK     0x06
> +#define      FW_FEATURE_ENCRY_MODE           0x10
> +#define      FW_FEATURE_OVERRIDE_ADDR        0x20
> +
> +#define      PATCH_SEC_NOT_SUPPORT           0xffffffff
> +#define      PATCH_SEC_TYPE_MASK             0x0000ffff
> +#define      PATCH_SEC_TYPE_INFO             0x2
> +
> +#define      PATCH_SEC_ENC_TYPE_MASK         0xff000000
> +#define      PATCH_SEC_ENC_TYPE_SHIFT        24
> +#define      PATCH_SEC_ENC_TYPE_PLAIN        (0x00 << 
> PATCH_SEC_ENC_TYPE_SHIFT)
> +#define      PATCH_SEC_ENC_TYPE_AES          (0x01 << 
> PATCH_SEC_ENC_TYPE_SHIFT)
> +#define      PATCH_SEC_ENC_TYPE_SCRAMBLE     (0x02 << 
> PATCH_SEC_ENC_TYPE_SHIFT)
> +#define      PATCH_SEC_ENC_SCRAMBLE_INFO_MASK        0xffff
> +#define      PATCH_SEC_ENC_AES_KEY_MASK              0xff
> +
> +#define      DL_MODE_ENCRYPT                 0x01
> +#define      DL_MODE_KEY_IDX_MASK            0x06
> +#define      DL_MODE_KEY_IDX_SHIFT           1
> +#define      DL_MODE_RESET_SEC_IV            0x08
> +#define      DL_MODE_WORKING_PDA_CR4         0x10
> +#define      DL_CONFIG_ENCRY_MODE_SEL        0x40
> +#define      DL_MODE_NEED_RSP                0x80000000
> +
> +/* defines for mt7921_mcu_get_nic_capability */
> +enum {
> +     MT_NIC_CAP_TX_RESOURCE,
> +     MT_NIC_CAP_TX_EFUSE_ADDR,
> +     MT_NIC_CAP_COEX,
> +     MT_NIC_CAP_SINGLE_SKU,
> +     MT_NIC_CAP_CSUM_OFFLOAD,
> +     MT_NIC_CAP_HW_VER,
> +     MT_NIC_CAP_SW_VER,
> +     MT_NIC_CAP_MAC_ADDR,
> +     MT_NIC_CAP_PHY,
> +     MT_NIC_CAP_MAC,
> +     MT_NIC_CAP_FRAME_BUF,
> +     MT_NIC_CAP_BEAM_FORM,
> +     MT_NIC_CAP_LOCATION,
> +     MT_NIC_CAP_MUMIMO,
> +     MT_NIC_CAP_BUFFER_MODE_INFO,
> +     MT_NIC_CAP_HW_ADIE_VERSION = 0x14,
> +     MT_NIC_CAP_ANTSWP = 0x16,
> +     MT_NIC_CAP_WFDMA_REALLOC,
> +     MT_NIC_CAP_6G,
> +};
> +
> +/* defines for channel bandwidth */
> +enum {
> +     CMD_CBW_20MHZ,
> +     CMD_CBW_40MHZ,
> +     CMD_CBW_80MHZ,
> +     CMD_CBW_160MHZ,
> +     CMD_CBW_10MHZ,
> +     CMD_CBW_5MHZ,
> +     CMD_CBW_8080MHZ,
> +};
> +
> +/* defines for channel switch reason */
> +enum {
> +     CH_SWITCH_NORMAL = 0,
> +     CH_SWITCH_SCAN = 3,
> +     CH_SWITCH_MCC = 4,
> +     CH_SWITCH_DFS = 5,
> +     CH_SWITCH_BACKGROUND_SCAN_START = 6,
> +     CH_SWITCH_BACKGROUND_SCAN_RUNNING = 7,
> +     CH_SWITCH_BACKGROUND_SCAN_STOP = 8,
> +     CH_SWITCH_SCAN_BYPASS_DPD = 9
> +};
> +
> +enum {
> +     HW_BSSID_0 = 0x0,
> +     HW_BSSID_1,
> +     HW_BSSID_2,
> +     HW_BSSID_3,
> +     HW_BSSID_MAX = HW_BSSID_3,
> +     EXT_BSSID_START = 0x10,
> +     EXT_BSSID_1,
> +     EXT_BSSID_15 = 0x1f,
> +     EXT_BSSID_MAX = EXT_BSSID_15,
> +     REPEATER_BSSID_START = 0x20,
> +     REPEATER_BSSID_MAX = 0x3f,
> +};
> +
> +
> +#define      STA_TYPE_STA                    (1U << 0)
> +#define      STA_TYPE_AP                     (1U << 1)
> +#define      STA_TYPE_ADHOC                  (1U << 2)
> +#define      STA_TYPE_WDS                    (1U << 4)
> +#define      STA_TYPE_BC                     (1U << 5)
> +
> +#define      NETWORK_INFRA                   (1U << 16)
> +#define      NETWORK_P2P                     (1U << 17)
> +#define      NETWORK_IBSS                    (1U << 18)
> +#define      NETWORK_WDS                     (1U << 21)
> +
> +#define      CONNECTION_INFRA_STA            (STA_TYPE_STA | NETWORK_INFRA)
> +#define      CONNECTION_INFRA_AP             (STA_TYPE_AP | NETWORK_INFRA)
> +#define      CONNECTION_P2P_GC               (STA_TYPE_STA | NETWORK_P2P)
> +#define      CONNECTION_P2P_GO               (STA_TYPE_AP | NETWORK_P2P)
> +#define      CONNECTION_IBSS_ADHOC           (STA_TYPE_ADHOC | NETWORK_IBSS)
> +#define      CONNECTION_WDS                  (STA_TYPE_WDS | NETWORK_WDS)
> +#define      CONNECTION_INFRA_BC             (STA_TYPE_BC | NETWORK_INFRA)
> +
> +#define      DEV_INFO_ACTIVE                 0
> 


Reply via email to