Page MenuHomeFreeBSD

D45179.diff
No OneTemporary

D45179.diff

diff --git a/sbin/devd/devd.conf b/sbin/devd/devd.conf
--- a/sbin/devd/devd.conf
+++ b/sbin/devd/devd.conf
@@ -18,7 +18,7 @@
# Setup some shorthand for regex that we use later in the file.
#XXX Yes, this is gross -- imp
set wifi-driver-regex
- "(ath|ath[0-9]+k|bwi|bwn|ipw|iwlwifi|iwi|iwm|iwn|malo|mwl|mt79|otus|\
+ "(ath|ath[0-9]+k|bwi|bwn|ipw|iwlwifi|iwi|iwm|iwn|malo|mwl|mt79|mtw|otus|\
ral|rsu|rtw|rtwn|rum|run|uath|upgt|ural|urtw|wpi|wtap|zyd)[0-9]+";
};
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -338,6 +338,7 @@
msdosfs.4 \
msk.4 \
mtio.4 \
+ mtw.4 \
multicast.4 \
muge.4 \
mvs.4 \
diff --git a/share/man/man4/mtw.4 b/share/man/man4/mtw.4
new file mode 100644
--- /dev/null
+++ b/share/man/man4/mtw.4
@@ -0,0 +1,74 @@
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2024 Jesper Schmitz Mouridsen <jsm@freebsd.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd May 13, 2024
+.Dt MTW 4
+.Os
+.Sh NAME
+.Nm if_mtw
+.Nd "Mediatek MT7601U"
+.Ed
+.Sh DESCRIPTION
+This module provides support for Mediatek MT7601U with the firmware from net/mt7601u-firmware-kmod
+
+.Sh HARDWARE
+The
+.Nm
+driver supports Mediatek MT7601U
+based USB wireless network adapters including (but not tested):
+.Pp
+.Bl -column -compact
+.It
+ASUS USB-N10 v2
+.It
+D-Link DWA-127 rev B1
+.It
+Edimax EW-7711UAn v2
+.It
+Foxconn WFU03
+.It
+Tenda U2
+.It
+Tenda W311MI v2
+.It
+TP-LINK TL-WN727N v4
+.It
+Yealink WF40
+.El
+.Sh SEE ALSO
+.Xr usb 4
+.Sh BUGS
+The
+.Nm
+only works in station mode and monitor mode. The firmware does not always reinitialize when reloading the module, or when rebooting, without first unplugging the device.
+.Sh History
+The mtw driver first appeared in OpenBSD 7.1. The mtw driver was ported to FreeBSD in FreeBSD 15.0.
+.Sh AUTHORS
+.An -nosplit
+The mtw driver was written by
+.An James Hastings Aq Mt hastings@openbsd.org
+ported to FreeBSD by
+.An Jesper Schmitz Mouridsen Aq Mt jsm@FreeBSD.org
diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs
--- a/sys/dev/usb/usbdevs
+++ b/sys/dev/usb/usbdevs
@@ -1906,6 +1906,7 @@
product EDIMAX RTL8192SU_1 0x7611 RTL8192SU
product EDIMAX RTL8192SU_2 0x7612 RTL8192SU
product EDIMAX EW7622UMN 0x7622 EW-7622UMn
+product EDIMAX MT7601U 0x7710 MT7601U
product EDIMAX RT2870_1 0x7711 RT2870
product EDIMAX EW7717 0x7717 EW-7717
product EDIMAX EW7718 0x7718 EW-7718
@@ -4100,7 +4101,7 @@
product RALINK RT5370 0x5370 RT5370
product RALINK RT5372 0x5372 RT5372
product RALINK RT5572 0x5572 RT5572
-product RALINK RT7601 0x7601 RT7601
+product RALINK MT7601U 0x7601 MT7601 Mediatek Wireless Adpater
product RALINK RT8070 0x8070 RT8070
product RALINK RT2570_3 0x9020 RT2500USB Wireless Adapter
product RALINK RT2573_2 0x9021 RT2501USB Wireless Adapter
diff --git a/sys/dev/usb/wlan/if_mtw.c b/sys/dev/usb/wlan/if_mtw.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/usb/wlan/if_mtw.c
@@ -0,0 +1,4668 @@
+/*-
+ * Copyright (c) 2008-2010 Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2013-2014 Kevin Lo
+ * Copyright (c) 2021 James Hastings
+ * Ported to FreeBSD by Jesper Schmitz Mouridsen jsm@FreeBSD.org
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * MediaTek MT7601U 802.11b/g/n WLAN.
+ */
+
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/eventhandler.h>
+#include <sys/firmware.h>
+#include <sys/kdb.h>
+#include <sys/kernel.h>
+#include <sys/linker.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+
+#include <net/bpf.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_var.h>
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_ratectl.h>
+#include <net80211/ieee80211_regdomain.h>
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include "usbdevs.h"
+
+#define USB_DEBUG_VAR mtw_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_msctest.h>
+
+#include "if_mtwreg.h"
+#include "if_mtwvar.h"
+
+#define MTW_DEBUG
+
+#ifdef MTW_DEBUG
+int mtw_debug;
+static SYSCTL_NODE(_hw_usb, OID_AUTO, mtw, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
+ "USB mtw");
+SYSCTL_INT(_hw_usb_mtw, OID_AUTO, debug, CTLFLAG_RWTUN, &mtw_debug, 0,
+ "mtw debug level");
+
+enum {
+ MTW_DEBUG_XMIT = 0x00000001, /* basic xmit operation */
+ MTW_DEBUG_XMIT_DESC = 0x00000002, /* xmit descriptors */
+ MTW_DEBUG_RECV = 0x00000004, /* basic recv operation */
+ MTW_DEBUG_RECV_DESC = 0x00000008, /* recv descriptors */
+ MTW_DEBUG_STATE = 0x00000010, /* 802.11 state transitions */
+ MTW_DEBUG_RATE = 0x00000020, /* rate adaptation */
+ MTW_DEBUG_USB = 0x00000040, /* usb requests */
+ MTW_DEBUG_FIRMWARE = 0x00000080, /* firmware(9) loading debug */
+ MTW_DEBUG_BEACON = 0x00000100, /* beacon handling */
+ MTW_DEBUG_INTR = 0x00000200, /* ISR */
+ MTW_DEBUG_TEMP = 0x00000400, /* temperature calibration */
+ MTW_DEBUG_ROM = 0x00000800, /* various ROM info */
+ MTW_DEBUG_KEY = 0x00001000, /* crypto keys management */
+ MTW_DEBUG_TXPWR = 0x00002000, /* dump Tx power values */
+ MTW_DEBUG_RSSI = 0x00004000, /* dump RSSI lookups */
+ MTW_DEBUG_RESET = 0x00008000, /* initialization progress */
+ MTW_DEBUG_CALIB = 0x00010000, /* calibration progress */
+ MTW_DEBUG_CMD = 0x00020000, /* command queue */
+ MTW_DEBUG_ANY = 0xffffffff
+};
+
+#define MTW_DPRINTF(_sc, _m, ...) \
+ do { \
+ if (mtw_debug & (_m)) \
+ device_printf((_sc)->sc_dev, __VA_ARGS__); \
+ } while (0)
+
+#else
+#define MTW_DPRINTF(_sc, _m, ...) \
+ do { \
+ (void)_sc; \
+ } while (0)
+#endif
+
+#define IEEE80211_HAS_ADDR4(wh) IEEE80211_IS_DSTODS(wh)
+
+/* NB: "11" is the maximum number of padding bytes needed for Tx */
+#define MTW_MAX_TXSZ \
+ (sizeof(struct mtw_txd) + sizeof(struct mtw_txwi) + MCLBYTES + 11)
+
+/*
+ * Because of LOR in mtw_key_delete(), use atomic instead.
+ * '& MTW_CMDQ_MASQ' is to loop cmdq[].
+ */
+#define MTW_CMDQ_GET(c) (atomic_fetchadd_32((c), 1) & MTW_CMDQ_MASQ)
+
+static const STRUCT_USB_HOST_ID mtw_devs[] = {
+#define MTW_DEV(v, p) \
+ { \
+ USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) \
+ }
+ MTW_DEV(EDIMAX, MT7601U),
+ MTW_DEV(RALINK, MT7601U),
+ MTW_DEV(XIAOMI, MT7601U)
+};
+#undef MTW_DEV
+
+static device_probe_t mtw_match;
+static device_attach_t mtw_attach;
+static device_detach_t mtw_detach;
+
+static usb_callback_t mtw_bulk_rx_callback;
+static usb_callback_t mtw_bulk_tx_callback0;
+static usb_callback_t mtw_bulk_tx_callback1;
+static usb_callback_t mtw_bulk_tx_callback2;
+static usb_callback_t mtw_bulk_tx_callback3;
+static usb_callback_t mtw_bulk_tx_callback4;
+static usb_callback_t mtw_bulk_tx_callback5;
+static usb_callback_t mtw_fw_callback;
+
+static void mtw_autoinst(void *, struct usb_device *, struct usb_attach_arg *);
+static int mtw_driver_loaded(struct module *, int, void *);
+static void mtw_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error,
+ u_int index);
+static struct ieee80211vap *mtw_vap_create(struct ieee80211com *,
+ const char[IFNAMSIZ], int, enum ieee80211_opmode, int,
+ const uint8_t[IEEE80211_ADDR_LEN], const uint8_t[IEEE80211_ADDR_LEN]);
+static void mtw_vap_delete(struct ieee80211vap *);
+static void mtw_cmdq_cb(void *, int);
+static void mtw_setup_tx_list(struct mtw_softc *, struct mtw_endpoint_queue *);
+static void mtw_unsetup_tx_list(struct mtw_softc *,
+ struct mtw_endpoint_queue *);
+static void mtw_load_microcode(void *arg);
+
+static usb_error_t mtw_do_request(struct mtw_softc *,
+ struct usb_device_request *, void *);
+static int mtw_read(struct mtw_softc *, uint16_t, uint32_t *);
+static int mtw_read_region_1(struct mtw_softc *, uint16_t, uint8_t *, int);
+static int mtw_write_2(struct mtw_softc *, uint16_t, uint16_t);
+static int mtw_write(struct mtw_softc *, uint16_t, uint32_t);
+static int mtw_write_region_1(struct mtw_softc *, uint16_t, uint8_t *, int);
+static int mtw_set_region_4(struct mtw_softc *, uint16_t, uint32_t, int);
+static int mtw_efuse_read_2(struct mtw_softc *, uint16_t, uint16_t *);
+static int mtw_bbp_read(struct mtw_softc *, uint8_t, uint8_t *);
+static int mtw_bbp_write(struct mtw_softc *, uint8_t, uint8_t);
+static int mtw_mcu_cmd(struct mtw_softc *sc, uint8_t cmd, void *buf, int len);
+static void mtw_get_txpower(struct mtw_softc *);
+static int mtw_read_eeprom(struct mtw_softc *);
+static struct ieee80211_node *mtw_node_alloc(struct ieee80211vap *,
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static int mtw_media_change(if_t);
+static int mtw_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int mtw_wme_update(struct ieee80211com *);
+static void mtw_key_set_cb(void *);
+static int mtw_key_set(struct ieee80211vap *, struct ieee80211_key *);
+static void mtw_key_delete_cb(void *);
+static int mtw_key_delete(struct ieee80211vap *, struct ieee80211_key *);
+static void mtw_ratectl_to(void *);
+static void mtw_ratectl_cb(void *, int);
+static void mtw_drain_fifo(void *);
+static void mtw_iter_func(void *, struct ieee80211_node *);
+static void mtw_newassoc_cb(void *);
+static void mtw_newassoc(struct ieee80211_node *, int);
+static int mtw_mcu_radio(struct mtw_softc *sc, int func, uint32_t val);
+static void mtw_recv_mgmt(struct ieee80211_node *, struct mbuf *, int,
+ const struct ieee80211_rx_stats *, int, int);
+static void mtw_rx_frame(struct mtw_softc *, struct mbuf *, uint32_t);
+static void mtw_tx_free(struct mtw_endpoint_queue *pq, struct mtw_tx_data *,
+ int);
+static void mtw_set_tx_desc(struct mtw_softc *, struct mtw_tx_data *);
+static int mtw_tx(struct mtw_softc *, struct mbuf *, struct ieee80211_node *);
+static int mtw_tx_mgt(struct mtw_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int mtw_sendprot(struct mtw_softc *, const struct mbuf *,
+ struct ieee80211_node *, int, int);
+static int mtw_tx_param(struct mtw_softc *, struct mbuf *,
+ struct ieee80211_node *, const struct ieee80211_bpf_params *);
+static int mtw_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static int mtw_transmit(struct ieee80211com *, struct mbuf *);
+static void mtw_start(struct mtw_softc *);
+static void mtw_parent(struct ieee80211com *);
+static void mtw_select_chan_group(struct mtw_softc *, int);
+
+static int mtw_set_chan(struct mtw_softc *, struct ieee80211_channel *);
+static void mtw_set_channel(struct ieee80211com *);
+static void mtw_getradiocaps(struct ieee80211com *, int, int *,
+ struct ieee80211_channel[]);
+static void mtw_scan_start(struct ieee80211com *);
+static void mtw_scan_end(struct ieee80211com *);
+static void mtw_update_beacon(struct ieee80211vap *, int);
+static void mtw_update_beacon_cb(void *);
+static void mtw_updateprot(struct ieee80211com *);
+static void mtw_updateprot_cb(void *);
+static void mtw_usb_timeout_cb(void *);
+static int mtw_reset(struct mtw_softc *sc);
+static void mtw_enable_tsf_sync(struct mtw_softc *);
+
+
+static void mtw_enable_mrr(struct mtw_softc *);
+static void mtw_set_txpreamble(struct mtw_softc *);
+static void mtw_set_basicrates(struct mtw_softc *);
+static void mtw_set_leds(struct mtw_softc *, uint16_t);
+static void mtw_set_bssid(struct mtw_softc *, const uint8_t *);
+static void mtw_set_macaddr(struct mtw_softc *, const uint8_t *);
+static void mtw_updateslot(struct ieee80211com *);
+static void mtw_updateslot_cb(void *);
+static void mtw_update_mcast(struct ieee80211com *);
+static int8_t mtw_rssi2dbm(struct mtw_softc *, uint8_t, uint8_t);
+static void mtw_update_promisc_locked(struct mtw_softc *);
+static void mtw_update_promisc(struct ieee80211com *);
+static int mtw_txrx_enable(struct mtw_softc *);
+static void mtw_init_locked(struct mtw_softc *);
+static void mtw_stop(void *);
+static void mtw_delay(struct mtw_softc *, u_int);
+static void mtw_update_chw(struct ieee80211com *ic);
+static int mtw_ampdu_enable(struct ieee80211_node *ni,
+ struct ieee80211_tx_ampdu *tap);
+
+static eventhandler_tag mtw_etag;
+
+static const struct {
+ uint8_t reg;
+ uint8_t val;
+} mt7601_rf_bank0[] = { MT7601_BANK0_RF },
+ mt7601_rf_bank4[] = { MT7601_BANK4_RF },
+ mt7601_rf_bank5[] = { MT7601_BANK5_RF };
+static const struct {
+ uint32_t reg;
+ uint32_t val;
+} mt7601_def_mac[] = { MT7601_DEF_MAC };
+static const struct {
+ uint8_t reg;
+ uint8_t val;
+} mt7601_def_bbp[] = { MT7601_DEF_BBP };
+
+
+static const struct {
+ u_int chan;
+ uint8_t r17, r18, r19, r20;
+} mt7601_rf_chan[] = { MT7601_RF_CHAN };
+
+
+static const struct usb_config mtw_config[MTW_N_XFER] = {
+ [MTW_BULK_RX] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .bufsize = MTW_MAX_RXSZ,
+ .flags = {.pipe_bof = 1,
+ .short_xfer_ok = 1,},
+ .callback = mtw_bulk_rx_callback,
+ },
+ [MTW_BULK_TX_BE] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = MTW_MAX_TXSZ,
+ .flags = {.pipe_bof = 1,
+ .force_short_xfer = 0,},
+ .callback = mtw_bulk_tx_callback0,
+ .timeout = 5000, /* ms */
+ },
+ [MTW_BULK_TX_BK] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = MTW_MAX_TXSZ,
+ .flags = {.pipe_bof = 1,
+ .force_short_xfer = 1,},
+ .callback = mtw_bulk_tx_callback1,
+ .timeout = 5000, /* ms */
+ },
+ [MTW_BULK_TX_VI] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = MTW_MAX_TXSZ,
+ .flags = {.pipe_bof = 1,
+ .force_short_xfer = 1,},
+ .callback = mtw_bulk_tx_callback2,
+ .timeout = 5000, /* ms */
+ },
+ [MTW_BULK_TX_VO] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = MTW_MAX_TXSZ,
+ .flags = {.pipe_bof = 1,
+ .force_short_xfer = 1,},
+ .callback = mtw_bulk_tx_callback3,
+ .timeout = 5000, /* ms */
+ },
+ [MTW_BULK_TX_HCCA] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = MTW_MAX_TXSZ,
+ .flags = {.pipe_bof = 1,
+ .force_short_xfer = 1, .no_pipe_ok = 1,},
+ .callback = mtw_bulk_tx_callback4,
+ .timeout = 5000, /* ms */
+ },
+ [MTW_BULK_TX_PRIO] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = MTW_MAX_TXSZ,
+ .flags = {.pipe_bof = 1,
+ .force_short_xfer = 1, .no_pipe_ok = 1,},
+ .callback = mtw_bulk_tx_callback5,
+ .timeout = 5000, /* ms */
+ },
+
+ [MTW_BULK_FW_CMD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = MTW_MAX_TXSZ,
+ .flags = {.pipe_bof = 1,
+ .force_short_xfer = 1, .no_pipe_ok = 1,},
+ .callback = mtw_fw_callback,
+
+ },
+
+ [MTW_BULK_RAW_TX] = {
+ .type = UE_BULK,
+ .ep_index = 0,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .bufsize = MTW_MAX_TXSZ,
+ .flags = {.pipe_bof = 1,
+ .force_short_xfer = 1, .no_pipe_ok = 1,},
+ .callback = mtw_bulk_tx_callback0,
+ .timeout = 5000, /* ms */
+ },
+
+};
+static uint8_t mtw_wme_ac_xfer_map[4] = {
+ [WME_AC_BE] = MTW_BULK_TX_BE,
+ [WME_AC_BK] = MTW_BULK_TX_BK,
+ [WME_AC_VI] = MTW_BULK_TX_VI,
+ [WME_AC_VO] = MTW_BULK_TX_VO,
+};
+static void
+mtw_autoinst(void *arg, struct usb_device *udev, struct usb_attach_arg *uaa)
+{
+ struct usb_interface *iface;
+ struct usb_interface_descriptor *id;
+
+ if (uaa->dev_state != UAA_DEV_READY)
+ return;
+
+ iface = usbd_get_iface(udev, 0);
+ if (iface == NULL)
+ return;
+ id = iface->idesc;
+ if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
+ return;
+ if (usbd_lookup_id_by_uaa(mtw_devs, sizeof(mtw_devs), uaa))
+ return;
+
+ if (usb_msc_eject(udev, 0, MSC_EJECT_STOPUNIT) == 0)
+ uaa->dev_state = UAA_DEV_EJECTING;
+}
+
+static int
+mtw_driver_loaded(struct module *mod, int what, void *arg)
+{
+ switch (what) {
+ case MOD_LOAD:
+ mtw_etag = EVENTHANDLER_REGISTER(usb_dev_configured,
+ mtw_autoinst, NULL, EVENTHANDLER_PRI_ANY);
+ break;
+ case MOD_UNLOAD:
+ EVENTHANDLER_DEREGISTER(usb_dev_configured, mtw_etag);
+ break;
+ default:
+ return (EOPNOTSUPP);
+ }
+ return (0);
+}
+
+static const char *
+mtw_get_rf(int rev)
+{
+ switch (rev) {
+ case MT7601_RF_7601:
+ return ("MT7601");
+ case MT7610_RF_7610:
+ return ("MT7610");
+ case MT7612_RF_7612:
+ return ("MT7612");
+ }
+ return ("unknown");
+}
+static int
+mtw_wlan_enable(struct mtw_softc *sc, int enable)
+{
+ uint32_t tmp;
+ int error = 0;
+
+ if (enable) {
+ mtw_read(sc, MTW_WLAN_CTRL, &tmp);
+ if (sc->asic_ver == 0x7612)
+ tmp &= ~0xfffff000;
+
+ tmp &= ~MTW_WLAN_CLK_EN;
+ tmp |= MTW_WLAN_EN;
+ mtw_write(sc, MTW_WLAN_CTRL, tmp);
+ mtw_delay(sc, 2);
+
+ tmp |= MTW_WLAN_CLK_EN;
+ if (sc->asic_ver == 0x7612) {
+ tmp |= (MTW_WLAN_RESET | MTW_WLAN_RESET_RF);
+ }
+ mtw_write(sc, MTW_WLAN_CTRL, tmp);
+ mtw_delay(sc, 2);
+
+ mtw_read(sc, MTW_OSC_CTRL, &tmp);
+ tmp |= MTW_OSC_EN;
+ mtw_write(sc, MTW_OSC_CTRL, tmp);
+ tmp |= MTW_OSC_CAL_REQ;
+ mtw_write(sc, MTW_OSC_CTRL, tmp);
+ } else {
+ mtw_read(sc, MTW_WLAN_CTRL, &tmp);
+ tmp &= ~(MTW_WLAN_CLK_EN | MTW_WLAN_EN);
+ mtw_write(sc, MTW_WLAN_CTRL, tmp);
+
+ mtw_read(sc, MTW_OSC_CTRL, &tmp);
+ tmp &= ~MTW_OSC_EN;
+ mtw_write(sc, MTW_OSC_CTRL, tmp);
+ }
+ return (error);
+}
+
+static int
+mtw_read_cfg(struct mtw_softc *sc, uint16_t reg, uint32_t *val)
+{
+ usb_device_request_t req;
+ uint32_t tmp;
+ uint16_t actlen;
+ int error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = MTW_READ_CFG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 4);
+ error = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, &tmp, 0,
+ &actlen, 1000);
+
+ if (error == 0)
+ *val = le32toh(tmp);
+ else
+ *val = 0xffffffff;
+ return (error);
+}
+
+static int
+mtw_match(device_t self)
+{
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->usb_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != 0)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != 0)
+ return (ENXIO);
+
+ return (usbd_lookup_id_by_uaa(mtw_devs, sizeof(mtw_devs), uaa));
+}
+
+static int
+mtw_attach(device_t self)
+{
+ struct mtw_softc *sc = device_get_softc(self);
+ struct usb_attach_arg *uaa = device_get_ivars(self);
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint32_t ver;
+ int i, ret;
+ // uint32_t tmp;
+ uint8_t iface_index;
+ int ntries, error;
+
+ device_set_usb_desc(self);
+ sc->sc_udev = uaa->device;
+ sc->sc_dev = self;
+ sc->sc_sent = 0;
+
+ mtx_init(&sc->sc_mtx,
+ device_get_nameunit(sc->sc_dev),
+ MTX_NETWORK_LOCK, MTX_DEF);
+
+ iface_index = 0;
+
+ error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
+ mtw_config, MTW_N_XFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(sc->sc_dev,
+ "could not allocate USB transfers, "
+ "err=%s\n",
+ usbd_errstr(error));
+ goto detach;
+ }
+ for (i = 0; i < 7; i++) {
+ sc->txd_fw[i] = (struct mtw_txd_fw *)
+ malloc(sizeof(struct mtw_txd_fw),
+ M_USBDEV, M_NOWAIT | M_ZERO);
+ }
+ MTW_LOCK(sc);
+ sc->sc_idx = 0;
+ mbufq_init(&sc->sc_snd, ifqmaxlen);
+
+ /*enable WLAN core */
+ if ((error = mtw_wlan_enable(sc, 1)) != 0) {
+ device_printf(sc->sc_dev, "could not enable WLAN core\n");
+ return (ENXIO);
+ }
+
+ /* wait for the chip to settle */
+ DELAY(100);
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (mtw_read(sc, MTW_ASIC_VER, &ver) != 0) {
+ goto detach;
+ }
+ if (ver != 0 && ver != 0xffffffff)
+ break;
+ DELAY(10);
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev,
+ "timeout waiting for NIC to initialize\n");
+ goto detach;
+ }
+ sc->asic_ver = ver >> 16;
+ sc->asic_rev = ver & 0xffff;
+ DELAY(100);
+ if (sc->asic_ver != 0x7601) {
+ device_printf(sc->sc_dev,
+ "Your revision 0x04%x is not supported yet\n",
+ sc->asic_rev);
+ goto detach;
+ }
+
+ mtw_load_microcode(sc);
+ ret = msleep(&sc->fwloading, &sc->sc_mtx, 0, "fwload", 3 * hz);
+ if (ret == EWOULDBLOCK || sc->fwloading != 1) {
+ device_printf(sc->sc_dev,
+ "timeout waiting for MCU to initialize\n");
+ goto detach;
+ }
+
+ sc->sc_srom_read = mtw_efuse_read_2;
+ /* retrieve RF rev. no and various other things from EEPROM */
+ mtw_read_eeprom(sc);
+
+ device_printf(sc->sc_dev,
+ "MAC/BBP RT%04X (rev 0x%04X), RF %s (MIMO %dT%dR), address %s\n",
+ sc->asic_ver, sc->mac_rev, mtw_get_rf(sc->rf_rev), sc->ntxchains,
+ sc->nrxchains, ether_sprintf(ic->ic_macaddr));
+ DELAY(100);
+
+ //mtw_set_leds(sc,5);
+ // mtw_mcu_radio(sc,0x31,0);
+ MTW_UNLOCK(sc);
+
+
+ ic->ic_softc = sc;
+ ic->ic_name = device_get_nameunit(self);
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
+
+ ic->ic_caps = IEEE80211_C_STA | /* station mode supported */
+ IEEE80211_C_MONITOR | /* monitor mode supported */
+ IEEE80211_C_IBSS |
+ IEEE80211_C_HOSTAP |
+ IEEE80211_C_WDS | /* 4-address traffic works */
+ IEEE80211_C_MBSS |
+ IEEE80211_C_SHPREAMBLE | /* short preamble supported */
+ IEEE80211_C_SHSLOT | /* short slot time supported */
+ IEEE80211_C_WME | /* WME */
+ IEEE80211_C_WPA; /* WPA1|WPA2(RSN) */
+ device_printf(sc->sc_dev, "[HT] Enabling 802.11n\n");
+ ic->ic_htcaps = IEEE80211_HTC_HT;
+
+ ic->ic_rxstream = sc->nrxchains;
+ ic->ic_txstream = sc->ntxchains;
+
+ ic->ic_cryptocaps = IEEE80211_CRYPTO_WEP | IEEE80211_CRYPTO_AES_CCM |
+ IEEE80211_CRYPTO_AES_OCB | IEEE80211_CRYPTO_TKIP |
+ IEEE80211_CRYPTO_TKIPMIC;
+
+ ic->ic_flags |= IEEE80211_F_DATAPAD;
+ ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS;
+
+ mtw_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
+ ic->ic_channels);
+
+ ieee80211_ifattach(ic);
+
+ ic->ic_scan_start = mtw_scan_start;
+ ic->ic_scan_end = mtw_scan_end;
+ ic->ic_set_channel = mtw_set_channel;
+ ic->ic_getradiocaps = mtw_getradiocaps;
+ ic->ic_node_alloc = mtw_node_alloc;
+ ic->ic_newassoc = mtw_newassoc;
+ ic->ic_update_mcast = mtw_update_mcast;
+ ic->ic_updateslot = mtw_updateslot;
+ ic->ic_wme.wme_update = mtw_wme_update;
+ ic->ic_raw_xmit = mtw_raw_xmit;
+ ic->ic_update_promisc = mtw_update_promisc;
+ ic->ic_vap_create = mtw_vap_create;
+ ic->ic_vap_delete = mtw_vap_delete;
+ ic->ic_transmit = mtw_transmit;
+ ic->ic_parent = mtw_parent;
+
+ ic->ic_update_chw = mtw_update_chw;
+ ic->ic_ampdu_enable = mtw_ampdu_enable;
+
+ ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr,
+ sizeof(sc->sc_txtap), MTW_TX_RADIOTAP_PRESENT,
+ &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
+ MTW_RX_RADIOTAP_PRESENT);
+ TASK_INIT(&sc->cmdq_task, 0, mtw_cmdq_cb, sc);
+ TASK_INIT(&sc->ratectl_task, 0, mtw_ratectl_cb, sc);
+ usb_callout_init_mtx(&sc->ratectl_ch, &sc->sc_mtx, 0);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+
+ return (0);
+
+detach:
+ MTW_UNLOCK(sc);
+ mtw_detach(self);
+ return (ENXIO);
+}
+
+static void
+mtw_drain_mbufq(struct mtw_softc *sc)
+{
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+
+ MTW_LOCK_ASSERT(sc, MA_OWNED);
+ while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m->m_pkthdr.rcvif = NULL;
+ ieee80211_free_node(ni);
+ m_freem(m);
+ }
+}
+
+static int
+mtw_detach(device_t self)
+{
+ struct mtw_softc *sc = device_get_softc(self);
+ struct ieee80211com *ic = &sc->sc_ic;
+ int i;
+ MTW_LOCK(sc);
+ mtw_reset(sc);
+ DELAY(10000);
+ sc->sc_detached = 1;
+ MTW_UNLOCK(sc);
+
+
+ /* stop all USB transfers */
+ for (i = 0; i < MTW_N_XFER; i++)
+ usbd_transfer_drain(sc->sc_xfer[i]);
+
+ MTW_LOCK(sc);
+ sc->ratectl_run = MTW_RATECTL_OFF;
+ sc->cmdq_run = sc->cmdq_key_set = MTW_CMDQ_ABORT;
+
+ /* free TX list, if any */
+ for (i = 1; i < 7; i++)
+ mtw_unsetup_tx_list(sc, &sc->sc_epq[i]);
+
+ /* Free TX queue */
+ mtw_drain_mbufq(sc);
+ MTW_UNLOCK(sc);
+ if (sc->sc_ic.ic_softc == sc) {
+ /* dr_ain tasks */
+ usb_callout_drain(&sc->ratectl_ch);
+ ieee80211_draintask(ic, &sc->cmdq_task);
+ ieee80211_draintask(ic, &sc->ratectl_task);
+ ieee80211_ifdetach(ic);
+ }
+ for (i = 0; i < 7; i++) {
+ free(sc->txd_fw[i], M_USBDEV);
+ }
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static struct ieee80211vap *
+mtw_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
+ enum ieee80211_opmode opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct mtw_softc *sc = ic->ic_softc;
+ struct mtw_vap *rvp;
+ struct ieee80211vap *vap;
+ int i;
+
+ if (sc->rvp_cnt >= MTW_VAP_MAX) {
+ device_printf(sc->sc_dev, "number of VAPs maxed out\n");
+ return (NULL);
+ }
+
+ switch (opmode) {
+ case IEEE80211_M_STA:
+ /* enable s/w bmiss handling for sta mode */
+ flags |= IEEE80211_CLONE_NOBEACONS;
+ /* fall though */
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_MONITOR:
+ case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_MBSS:
+ /* other than WDS vaps, only one at a time */
+ if (!TAILQ_EMPTY(&ic->ic_vaps))
+ return (NULL);
+ break;
+ case IEEE80211_M_WDS:
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+ if (vap->iv_opmode != IEEE80211_M_HOSTAP)
+ continue;
+ /* WDS vap's always share the local mac address. */
+ flags &= ~IEEE80211_CLONE_BSSID;
+ break;
+ }
+ if (vap == NULL) {
+ device_printf(sc->sc_dev,
+ "wds only supported in ap mode\n");
+ return (NULL);
+ }
+ break;
+ default:
+ device_printf(sc->sc_dev, "unknown opmode %d\n", opmode);
+ return (NULL);
+ }
+
+ rvp = malloc(sizeof(struct mtw_vap), M_80211_VAP, M_WAITOK | M_ZERO);
+ vap = &rvp->vap;
+
+ if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid) !=
+ 0) {
+ /* out of memory */
+ free(rvp, M_80211_VAP);
+ return (NULL);
+ }
+
+ vap->iv_update_beacon = mtw_update_beacon;
+ vap->iv_max_aid = MTW_WCID_MAX;
+
+ /*
+ * The linux rt2800 driver limits 1 stream devices to a 32KB
+ * RX AMPDU.
+ */
+ if (ic->ic_rxstream > 1)
+ vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K;
+ else
+ vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K;
+ vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_2; /* 2uS */
+
+ /*
+ * To delete the right key from h/w, we need wcid.
+ * Luckily, there is unused space in ieee80211_key{}, wk_pad,
+ * and matching wcid will be written into there. So, cast
+ * some spells to remove 'const' from ieee80211_key{}
+ */
+ vap->iv_key_delete = (void *)mtw_key_delete;
+ vap->iv_key_set = (void *)mtw_key_set;
+
+ // override state transition machine
+ rvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = mtw_newstate;
+ if (opmode == IEEE80211_M_IBSS) {
+ rvp->recv_mgmt = vap->iv_recv_mgmt;
+ vap->iv_recv_mgmt = mtw_recv_mgmt;
+ }
+
+ ieee80211_ratectl_init(vap);
+ ieee80211_ratectl_setinterval(vap, 1000); // 1 second
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, mtw_media_change, ieee80211_media_status,
+ mac);
+
+ /* make sure id is always unique */
+ for (i = 0; i < MTW_VAP_MAX; i++) {
+ if ((sc->rvp_bmap & 1 << i) == 0) {
+ sc->rvp_bmap |= 1 << i;
+ rvp->rvp_id = i;
+ break;
+ }
+ }
+ if (sc->rvp_cnt++ == 0)
+ ic->ic_opmode = opmode;
+
+ if (opmode == IEEE80211_M_HOSTAP)
+ sc->cmdq_run = MTW_CMDQ_GO;
+
+ MTW_DPRINTF(sc, MTW_DEBUG_STATE, "rvp_id=%d bmap=%x rvp_cnt=%d\n",
+ rvp->rvp_id, sc->rvp_bmap, sc->rvp_cnt);
+
+ return (vap);
+}
+
+static void
+mtw_vap_delete(struct ieee80211vap *vap)
+{
+ struct mtw_vap *rvp = MTW_VAP(vap);
+ struct ieee80211com *ic;
+ struct mtw_softc *sc;
+ uint8_t rvp_id;
+
+ if (vap == NULL)
+ return;
+
+ ic = vap->iv_ic;
+ sc = ic->ic_softc;
+
+ MTW_LOCK(sc);
+ m_freem(rvp->beacon_mbuf);
+ rvp->beacon_mbuf = NULL;
+
+ rvp_id = rvp->rvp_id;
+ sc->ratectl_run &= ~(1 << rvp_id);
+ sc->rvp_bmap &= ~(1 << rvp_id);
+ mtw_set_region_4(sc, MTW_SKEY(rvp_id, 0), 0, 256);
+ mtw_set_region_4(sc, (0x7800 + (rvp_id) * 512), 0, 512);
+ --sc->rvp_cnt;
+
+ MTW_DPRINTF(sc, MTW_DEBUG_STATE,
+ "vap=%p rvp_id=%d bmap=%x rvp_cnt=%d\n", vap, rvp_id, sc->rvp_bmap,
+ sc->rvp_cnt);
+
+ MTW_UNLOCK(sc);
+
+ ieee80211_ratectl_deinit(vap);
+ ieee80211_vap_detach(vap);
+ free(rvp, M_80211_VAP);
+}
+
+/*
+ * There are numbers of functions need to be called in context thread.
+ * Rather than creating taskqueue event for each of those functions,
+ * here is all-for-one taskqueue callback function. This function
+ * guarantees deferred functions are executed in the same order they
+ * were enqueued.
+ * '& MTW_CMDQ_MASQ' is to loop cmdq[].
+ */
+static void
+mtw_cmdq_cb(void *arg, int pending)
+{
+ struct mtw_softc *sc = arg;
+ uint8_t i;
+ /* call cmdq[].func locked */
+ MTW_LOCK(sc);
+ for (i = sc->cmdq_exec; sc->cmdq[i].func && pending;
+ i = sc->cmdq_exec, pending--) {
+ MTW_DPRINTF(sc, MTW_DEBUG_CMD, "cmdq_exec=%d pending=%d\n", i,
+ pending);
+ if (sc->cmdq_run == MTW_CMDQ_GO) {
+ /*
+ * If arg0 is NULL, callback func needs more
+ * than one arg. So, pass ptr to cmdq struct.
+ */
+ if (sc->cmdq[i].arg0)
+ sc->cmdq[i].func(sc->cmdq[i].arg0);
+ else
+ sc->cmdq[i].func(&sc->cmdq[i]);
+ }
+ sc->cmdq[i].arg0 = NULL;
+ sc->cmdq[i].func = NULL;
+ sc->cmdq_exec++;
+ sc->cmdq_exec &= MTW_CMDQ_MASQ;
+ }
+ MTW_UNLOCK(sc);
+}
+
+static void
+mtw_setup_tx_list(struct mtw_softc *sc, struct mtw_endpoint_queue *pq)
+{
+ struct mtw_tx_data *data;
+
+ memset(pq, 0, sizeof(*pq));
+
+ STAILQ_INIT(&pq->tx_qh);
+ STAILQ_INIT(&pq->tx_fh);
+
+ for (data = &pq->tx_data[0]; data < &pq->tx_data[MTW_TX_RING_COUNT];
+ data++) {
+ data->sc = sc;
+ STAILQ_INSERT_TAIL(&pq->tx_fh, data, next);
+ }
+ pq->tx_nfree = MTW_TX_RING_COUNT;
+}
+
+static void
+mtw_unsetup_tx_list(struct mtw_softc *sc, struct mtw_endpoint_queue *pq)
+{
+ struct mtw_tx_data *data;
+ /* make sure any subsequent use of the queues will fail */
+ pq->tx_nfree = 0;
+
+ STAILQ_INIT(&pq->tx_fh);
+ STAILQ_INIT(&pq->tx_qh);
+
+ /* free up all node references and mbufs */
+ for (data = &pq->tx_data[0]; data < &pq->tx_data[MTW_TX_RING_COUNT];
+ data++) {
+ if (data->m != NULL) {
+ m_freem(data->m);
+ data->m = NULL;
+ }
+ if (data->ni != NULL) {
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ }
+}
+
+static int
+mtw_write_ivb(struct mtw_softc *sc, void *buf, uint16_t len)
+{
+ usb_device_request_t req;
+ uint16_t actlen;
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = MTW_RESET;
+ USETW(req.wValue, 0x12);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ int error = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, buf,
+ 0, &actlen, 1000);
+
+ return (error);
+}
+
+static int
+mtw_write_cfg(struct mtw_softc *sc, uint16_t reg, uint32_t val)
+{
+ usb_device_request_t req;
+ int error;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = MTW_WRITE_CFG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 4);
+ val = htole32(val);
+ error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, &val);
+ return (error);
+}
+
+static int
+mtw_usb_dma_write(struct mtw_softc *sc, uint32_t val)
+{
+ // if (sc->asic_ver == 0x7612)
+ // return mtw_write_cfg(sc, MTW_USB_U3DMA_CFG, val);
+ // else
+ return (mtw_write(sc, MTW_USB_DMA_CFG, val));
+}
+
+static void
+mtw_ucode_setup(struct mtw_softc *sc)
+{
+
+ mtw_usb_dma_write(sc, (MTW_USB_TX_EN | MTW_USB_RX_EN));
+ mtw_write(sc, MTW_FCE_PSE_CTRL, 1);
+ mtw_write(sc, MTW_TX_CPU_FCE_BASE, 0x400230);
+ mtw_write(sc, MTW_TX_CPU_FCE_MAX_COUNT, 1);
+ mtw_write(sc, MTW_MCU_FW_IDX, 1);
+ mtw_write(sc, MTW_FCE_PDMA, 0x44);
+ mtw_write(sc, MTW_FCE_SKIP_FS, 3);
+}
+static int
+mtw_ucode_write(struct mtw_softc *sc, const uint8_t *fw, const uint8_t *ivb,
+ int32_t len, uint32_t offset)
+{
+
+ // struct usb_attach_arg *uaa = device_get_ivars(sc->sc_dev);
+#if 0 // firmware not tested
+
+ if (sc->asic_ver == 0x7612 && offset >= 0x90000)
+ blksz = 0x800; /* MT7612 ROM Patch */
+
+ xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (xfer == NULL) {
+ error = ENOMEM;
+ goto fail;
+ }
+ buf = usbd_alloc_buffer(xfer, blksz + 12);
+ if (buf == NULL) {
+ error = ENOMEM;
+ goto fail;
+ }
+#endif
+
+
+
+ int mlen;
+ int idx = 0;
+
+ mlen = 0x2000;
+
+ while (len > 0) {
+
+ if (len < 0x2000 && len > 0) {
+ mlen = len;
+ }
+
+ sc->txd_fw[idx]->len = htole16(mlen);
+ sc->txd_fw[idx]->flags = htole16(MTW_TXD_DATA | MTW_TXD_MCU);
+
+ memcpy(&sc->txd_fw[idx]->fw, fw, mlen);
+ // memcpy(&txd[1], fw, mlen);
+ // memset(&txd[1] + mlen, 0, MTW_DMA_PAD);
+ // mtw_write_cfg(sc, MTW_MCU_DMA_ADDR, offset
+ //+sent); 1mtw_write_cfg(sc, MTW_MCU_DMA_LEN, (mlen << 16));
+
+ // sc->sc_fw_data[idx]->len=htole16(mlen);
+
+ // memcpy(tmpbuf,fw,mlen);
+ // memset(tmpbuf+mlen,0,MTW_DMA_PAD);
+ // memcpy(sc->sc_fw_data[idx].buf, fw, mlen);
+
+ fw += mlen;
+ len -= mlen;
+ // sent+=mlen;
+ idx++;
+ }
+ sc->sc_sent = 0;
+ memcpy(sc->sc_ivb_1, ivb, MTW_MCU_IVB_LEN);
+
+ usbd_transfer_start(sc->sc_xfer[7]);
+
+ return (0);
+}
+
+static void
+mtw_load_microcode(void *arg)
+{
+
+ struct mtw_softc *sc = (struct mtw_softc *)arg;
+ const struct mtw_ucode_hdr *hdr;
+ // onst struct mtw_ucode *fw = NULL;
+ const char *fwname;
+ size_t size;
+ int error = 0;
+ uint32_t tmp, iofs = 0x40;
+ // int ntries;
+ int dlen, ilen;
+ device_printf(sc->sc_dev, "version:0x%hx\n", sc->asic_ver);
+ /* is firmware already running? */
+ mtw_read_cfg(sc, MTW_MCU_DMA_ADDR, &tmp);
+ if (tmp == MTW_MCU_READY) {
+ //return;
+ }
+ if (sc->asic_ver == 0x7612) {
+ fwname = "mtw-mt7662u_rom_patch";
+
+ const struct firmware *firmware = firmware_get(fwname);
+ if (firmware == NULL) {
+ device_printf(sc->sc_dev,
+ "failed loadfirmware of file %s (error %d)\n",
+ fwname, error);
+ return;
+ }
+ size = firmware->datasize;
+
+ const struct mtw_ucode *fw = (const struct mtw_ucode *)
+ firmware->data;
+ hdr = (const struct mtw_ucode_hdr *)&fw->hdr;
+ // memcpy(fw,(const unsigned char*)firmware->data +
+ // 0x1e,size-0x1e);
+ ilen = size - 0x1e;
+
+ mtw_ucode_setup(sc);
+
+ if ((error = mtw_ucode_write(sc, firmware->data, fw->ivb, ilen,
+ 0x90000)) != 0) {
+ goto fail;
+ }
+ mtw_usb_dma_write(sc, 0x00e41814);
+ }
+
+ fwname = "mt7601u_fw";
+ iofs = 0x40;
+ // dofs = 0;
+ if (sc->asic_ver == 0x7612) {
+ fwname = "mtw-mt7662u";
+ iofs = 0x80040;
+ // dofs = 0x110800;
+ } else if (sc->asic_ver == 0x7610) {
+ fwname = "mt7610u";
+ // dofs = 0x80000;
+ }
+ MTW_UNLOCK(sc);
+ const struct firmware *firmware = firmware_get(fwname);
+
+ if (firmware == NULL) {
+ device_printf(sc->sc_dev,
+ "failed loadfirmware of file %s (error %d)\n", fwname,
+ error);
+ MTW_LOCK(sc);
+ return;
+ }
+ MTW_LOCK(sc);
+ size = firmware->datasize;
+ MTW_DPRINTF(sc, MTW_DEBUG_FIRMWARE, "firmware size:%zu\n", size);
+ const struct mtw_ucode *fw = (const struct mtw_ucode *)firmware->data;
+
+ if (size < sizeof(struct mtw_ucode_hdr)) {
+ device_printf(sc->sc_dev, "firmware header too short\n");
+ goto fail;
+ }
+
+ hdr = (const struct mtw_ucode_hdr *)&fw->hdr;
+
+ if (size < sizeof(struct mtw_ucode_hdr) + le32toh(hdr->ilm_len) +
+ le32toh(hdr->dlm_len)) {
+ device_printf(sc->sc_dev, "firmware payload too short\n");
+ goto fail;
+ }
+
+ ilen = le32toh(hdr->ilm_len) - MTW_MCU_IVB_LEN;
+ dlen = le32toh(hdr->dlm_len);
+
+ if (ilen > size || dlen > size) {
+ device_printf(sc->sc_dev, "firmware payload too large\n");
+ goto fail;
+ }
+
+ mtw_write(sc, MTW_FCE_PDMA, 0);
+ mtw_write(sc, MTW_FCE_PSE_CTRL, 0);
+ mtw_ucode_setup(sc);
+
+ if ((error = mtw_ucode_write(sc, fw->data, fw->ivb, ilen, iofs)) != 0)
+ device_printf(sc->sc_dev, "Could not write ucode errro=%d\n",
+ error);
+
+ device_printf(sc->sc_dev, "loaded firmware ver %d.%d\n",
+ le16toh(hdr->build_ver), le16toh(hdr->fw_ver));
+
+ return;
+fail:
+ return;
+}
+static usb_error_t
+mtw_do_request(struct mtw_softc *sc, struct usb_device_request *req, void *data)
+{
+ usb_error_t err;
+ int ntries = 5;
+
+ MTW_LOCK_ASSERT(sc, MA_OWNED);
+
+ while (ntries--) {
+ err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, req, data,
+ 0, NULL, 2000); // ms seconds
+ if (err == 0)
+ break;
+ MTW_DPRINTF(sc, MTW_DEBUG_USB,
+ "Control request failed, %s (retrying)\n",
+ usbd_errstr(err));
+ mtw_delay(sc, 10);
+ }
+ return (err);
+}
+
+static int
+mtw_read(struct mtw_softc *sc, uint16_t reg, uint32_t *val)
+{
+ uint32_t tmp;
+ int error;
+
+ error = mtw_read_region_1(sc, reg, (uint8_t *)&tmp, sizeof tmp);
+ if (error == 0)
+ *val = le32toh(tmp);
+ else
+ *val = 0xffffffff;
+ return (error);
+}
+
+static int
+mtw_read_region_1(struct mtw_softc *sc, uint16_t reg, uint8_t *buf, int len)
+{
+ usb_device_request_t req;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = MTW_READ_REGION_1;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, len);
+
+ return (mtw_do_request(sc, &req, buf));
+}
+
+static int
+mtw_write_2(struct mtw_softc *sc, uint16_t reg, uint16_t val)
+{
+
+ usb_device_request_t req;
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = MTW_WRITE_2;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL));
+}
+
+static int
+mtw_write(struct mtw_softc *sc, uint16_t reg, uint32_t val)
+{
+
+ int error;
+
+ if ((error = mtw_write_2(sc, reg, val & 0xffff)) == 0) {
+
+ error = mtw_write_2(sc, reg + 2, val >> 16);
+ }
+
+ return (error);
+}
+
+static int
+mtw_write_region_1(struct mtw_softc *sc, uint16_t reg, uint8_t *buf, int len)
+{
+
+ usb_device_request_t req;
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = MTW_WRITE_REGION_1;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, len);
+ return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, buf));
+}
+
+static int
+mtw_set_region_4(struct mtw_softc *sc, uint16_t reg, uint32_t val, int count)
+{
+ int i, error = 0;
+
+ KASSERT((count & 3) == 0, ("mte_set_region_4: Invalid data length.\n"));
+ for (i = 0; i < count && error == 0; i += 4)
+ error = mtw_write(sc, reg + i, val);
+ return (error);
+}
+
+static int
+mtw_efuse_read_2(struct mtw_softc *sc, uint16_t addr, uint16_t *val)
+{
+
+ uint32_t tmp;
+ uint16_t reg;
+ int error, ntries;
+
+ if ((error = mtw_read(sc, MTW_EFUSE_CTRL, &tmp)) != 0)
+ return (error);
+
+ addr *= 2;
+ /*
+ * Read one 16-byte block into registers EFUSE_DATA[0-3]:
+ * DATA0: 3 2 1 0
+ * DATA1: 7 6 5 4
+ * DATA2: B A 9 8
+ * DATA3: F E D C
+ */
+ tmp &= ~(MTW_EFSROM_MODE_MASK | MTW_EFSROM_AIN_MASK);
+ tmp |= (addr & ~0xf) << MTW_EFSROM_AIN_SHIFT | MTW_EFSROM_KICK;
+ mtw_write(sc, MTW_EFUSE_CTRL, tmp);
+ for (ntries = 0; ntries < 100; ntries++) {
+ if ((error = mtw_read(sc, MTW_EFUSE_CTRL, &tmp)) != 0)
+ return (error);
+ if (!(tmp & MTW_EFSROM_KICK))
+ break;
+ DELAY(2);
+ }
+ if (ntries == 100)
+ return (ETIMEDOUT);
+
+ if ((tmp & MTW_EFUSE_AOUT_MASK) == MTW_EFUSE_AOUT_MASK) {
+ *val = 0xffff; // address not found
+ return (0);
+ }
+// determine to which 32-bit register our 16-bit word belongs
+ reg = MTW_EFUSE_DATA0 + (addr & 0xc);
+ if ((error = mtw_read(sc, reg, &tmp)) != 0)
+ return (error);
+
+ *val = (addr & 2) ? tmp >> 16 : tmp & 0xffff;
+ return (0);
+}
+
+static __inline int
+mtw_srom_read(struct mtw_softc *sc, uint16_t addr, uint16_t *val)
+{
+ /* either eFUSE ROM or EEPROM */
+ return (sc->sc_srom_read(sc, addr, val));
+}
+
+static int
+mtw_bbp_read(struct mtw_softc *sc, uint8_t reg, uint8_t *val)
+{
+ uint32_t tmp;
+ int ntries, error;
+
+ for (ntries = 0; ntries < 10; ntries++) {
+ if ((error = mtw_read(sc, MTW_BBP_CSR, &tmp)) != 0)
+ return (error);
+ if (!(tmp & MTW_BBP_CSR_KICK))
+ break;
+ }
+ if (ntries == 10)
+ return (ETIMEDOUT);
+
+ tmp = MTW_BBP_CSR_READ | MTW_BBP_CSR_KICK | reg << 8;
+ if ((error = mtw_write(sc, MTW_BBP_CSR, tmp)) != 0)
+ return (error);
+
+ for (ntries = 0; ntries < 10; ntries++) {
+ if ((error = mtw_read(sc, MTW_BBP_CSR, &tmp)) != 0)
+ return (error);
+ if (!(tmp & MTW_BBP_CSR_KICK))
+ break;
+ }
+ if (ntries == 10)
+ return (ETIMEDOUT);
+
+ *val = tmp & 0xff;
+ return (0);
+}
+
+static int
+mtw_bbp_write(struct mtw_softc *sc, uint8_t reg, uint8_t val)
+{
+ uint32_t tmp;
+ int ntries, error;
+
+ for (ntries = 0; ntries < 10; ntries++) {
+ if ((error = mtw_read(sc, MTW_BBP_CSR, &tmp)) != 0)
+ return (error);
+ if (!(tmp & MTW_BBP_CSR_KICK))
+ break;
+ }
+ if (ntries == 10)
+ return (ETIMEDOUT);
+
+ tmp = MTW_BBP_CSR_KICK | reg << 8 | val;
+ return (mtw_write(sc, MTW_BBP_CSR, tmp));
+}
+
+static int
+mtw_mcu_cmd(struct mtw_softc *sc, u_int8_t cmd, void *buf, int len)
+{
+ sc->sc_idx = 0;
+ sc->txd_fw[sc->sc_idx]->len = htole16(
+ len + 8);
+ sc->txd_fw[sc->sc_idx]->flags = htole16(MTW_TXD_CMD | MTW_TXD_MCU |
+ (cmd & 0x1f) << MTW_TXD_CMD_SHIFT | (0 & 0xf));
+
+ memset(&sc->txd_fw[sc->sc_idx]->fw, 0, 2004);
+ memcpy(&sc->txd_fw[sc->sc_idx]->fw, buf, len);
+ usbd_transfer_start(sc->sc_xfer[7]);
+ return (0);
+}
+
+/*
+ * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word.
+ * Used to adjust per-rate Tx power registers.
+ */
+static __inline uint32_t
+b4inc(uint32_t b32, int8_t delta)
+{
+ int8_t i, b4;
+
+ for (i = 0; i < 8; i++) {
+ b4 = b32 & 0xf;
+ b4 += delta;
+ if (b4 < 0)
+ b4 = 0;
+ else if (b4 > 0xf)
+ b4 = 0xf;
+ b32 = b32 >> 4 | b4 << 28;
+ }
+ return (b32);
+}
+static void
+mtw_get_txpower(struct mtw_softc *sc)
+{
+ uint16_t val;
+ int i;
+
+ /* Read power settings for 2GHz channels. */
+ for (i = 0; i < 14; i += 2) {
+ mtw_srom_read(sc, MTW_EEPROM_PWR2GHZ_BASE1 + i / 2, &val);
+ sc->txpow1[i + 0] = (int8_t)(val & 0xff);
+ sc->txpow1[i + 1] = (int8_t)(val >> 8);
+ mtw_srom_read(sc, MTW_EEPROM_PWR2GHZ_BASE2 + i / 2, &val);
+ sc->txpow2[i + 0] = (int8_t)(val & 0xff);
+ sc->txpow2[i + 1] = (int8_t)(val >> 8);
+ }
+ /* Fix broken Tx power entries. */
+ for (i = 0; i < 14; i++) {
+ if (sc->txpow1[i] < 0 || sc->txpow1[i] > 27)
+ sc->txpow1[i] = 5;
+ if (sc->txpow2[i] < 0 || sc->txpow2[i] > 27)
+ sc->txpow2[i] = 5;
+ MTW_DPRINTF(sc, MTW_DEBUG_TXPWR,
+ "chan %d: power1=%d, power2=%d\n", mt7601_rf_chan[i].chan,
+ sc->txpow1[i], sc->txpow2[i]);
+ }
+}
+
+struct ieee80211_node *
+mtw_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ return (malloc(sizeof(struct mtw_node), M_80211_NODE,
+ M_NOWAIT | M_ZERO));
+}
+static int
+mtw_read_eeprom(struct mtw_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ int8_t delta_2ghz, delta_5ghz;
+ uint16_t val;
+ int ridx, ant;
+
+ sc->sc_srom_read = mtw_efuse_read_2;
+
+ /* read RF information */
+ mtw_srom_read(sc, MTW_EEPROM_CHIPID, &val);
+ sc->rf_rev = val;
+ mtw_srom_read(sc, MTW_EEPROM_ANTENNA, &val);
+ sc->ntxchains = (val >> 4) & 0xf;
+ sc->nrxchains = val & 0xf;
+ MTW_DPRINTF(sc, MTW_DEBUG_ROM, "EEPROM RF rev=0x%02x chains=%dT%dR\n",
+ sc->rf_rev, sc->ntxchains, sc->nrxchains);
+
+ /* read ROM version */
+ mtw_srom_read(sc, MTW_EEPROM_VERSION, &val);
+ MTW_DPRINTF(sc, MTW_DEBUG_ROM, "EEPROM rev=%d, FAE=%d\n", val & 0xff,
+ val >> 8);
+
+ /* read MAC address */
+ mtw_srom_read(sc, MTW_EEPROM_MAC01, &val);
+ ic->ic_macaddr[0] = val & 0xff;
+ ic->ic_macaddr[1] = val >> 8;
+ mtw_srom_read(sc, MTW_EEPROM_MAC23, &val);
+ ic->ic_macaddr[2] = val & 0xff;
+ ic->ic_macaddr[3] = val >> 8;
+ mtw_srom_read(sc, MTW_EEPROM_MAC45, &val);
+ ic->ic_macaddr[4] = val & 0xff;
+ ic->ic_macaddr[5] = val >> 8;
+#if 0
+ printf("eFUSE ROM\n00: ");
+ for (int i = 0; i < 256; i++) {
+ if (((i % 8) == 0) && i > 0)
+ printf("\n%02x: ", i);
+ mtw_srom_read(sc, i, &val);
+ printf(" %04x", val);
+ }
+ printf("\n");
+#endif
+ /* check if RF supports automatic Tx access gain control */
+ mtw_srom_read(sc, MTW_EEPROM_CONFIG, &val);
+ device_printf(sc->sc_dev, "EEPROM CFG 0x%04x\n", val);
+ if ((val & 0xff) != 0xff) {
+ sc->ext_5ghz_lna = (val >> 3) & 1;
+ sc->ext_2ghz_lna = (val >> 2) & 1;
+ /* check if RF supports automatic Tx access gain control */
+ sc->calib_2ghz = sc->calib_5ghz = (val >> 1) & 1;
+ /* check if we have a hardware radio switch */
+ sc->rfswitch = val & 1;
+ }
+
+ /* read RF frequency offset from EEPROM */
+ mtw_srom_read(sc, MTW_EEPROM_FREQ_OFFSET, &val);
+ if ((val & 0xff) != 0xff)
+ sc->rf_freq_offset = val;
+ else
+ sc->rf_freq_offset = 0;
+ MTW_DPRINTF(sc, MTW_DEBUG_ROM, "frequency offset 0x%x\n",
+ sc->rf_freq_offset);
+
+ /* Read Tx power settings. */
+ mtw_get_txpower(sc);
+
+ /* read Tx power compensation for each Tx rate */
+ mtw_srom_read(sc, MTW_EEPROM_DELTAPWR, &val);
+ delta_2ghz = delta_5ghz = 0;
+ if ((val & 0xff) != 0xff && (val & 0x80)) {
+ delta_2ghz = val & 0xf;
+ if (!(val & 0x40)) /* negative number */
+ delta_2ghz = -delta_2ghz;
+ }
+ val >>= 8;
+ if ((val & 0xff) != 0xff && (val & 0x80)) {
+ delta_5ghz = val & 0xf;
+ if (!(val & 0x40)) /* negative number */
+ delta_5ghz = -delta_5ghz;
+ }
+ MTW_DPRINTF(sc, MTW_DEBUG_ROM | MTW_DEBUG_TXPWR,
+ "power compensation=%d (2GHz), %d (5GHz)\n", delta_2ghz,
+ delta_5ghz);
+
+ for (ridx = 0; ridx < 5; ridx++) {
+ uint32_t reg;
+
+ mtw_srom_read(sc, MTW_EEPROM_RPWR + ridx * 2, &val);
+ reg = val;
+ mtw_srom_read(sc, MTW_EEPROM_RPWR + ridx * 2 + 1, &val);
+ reg |= (uint32_t)val << 16;
+
+ sc->txpow20mhz[ridx] = reg;
+ sc->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz);
+ sc->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz);
+
+ MTW_DPRINTF(sc, MTW_DEBUG_ROM | MTW_DEBUG_TXPWR,
+ "ridx %d: power 20MHz=0x%08x, 40MHz/2GHz=0x%08x, "
+ "40MHz/5GHz=0x%08x\n",
+ ridx, sc->txpow20mhz[ridx], sc->txpow40mhz_2ghz[ridx],
+ sc->txpow40mhz_5ghz[ridx]);
+ }
+
+ /* read RSSI offsets and LNA gains from EEPROM */
+ val = 0;
+ mtw_srom_read(sc, MTW_EEPROM_RSSI1_2GHZ, &val);
+ sc->rssi_2ghz[0] = val & 0xff; /* Ant A */
+ sc->rssi_2ghz[1] = val >> 8; /* Ant B */
+ mtw_srom_read(sc, MTW_EEPROM_RSSI2_2GHZ, &val);
+ /*
+ * On RT3070 chips (limited to 2 Rx chains), this ROM
+ * field contains the Tx mixer gain for the 2GHz band.
+ */
+ if ((val & 0xff) != 0xff)
+ sc->txmixgain_2ghz = val & 0x7;
+ MTW_DPRINTF(sc, MTW_DEBUG_ROM, "tx mixer gain=%u (2GHz)\n",
+ sc->txmixgain_2ghz);
+ sc->lna[2] = val >> 8; /* channel group 2 */
+ mtw_srom_read(sc, MTW_EEPROM_RSSI1_5GHZ, &val);
+ sc->rssi_5ghz[0] = val & 0xff; /* Ant A */
+ sc->rssi_5ghz[1] = val >> 8; /* Ant B */
+ mtw_srom_read(sc, MTW_EEPROM_RSSI2_5GHZ, &val);
+ sc->rssi_5ghz[2] = val & 0xff; /* Ant C */
+
+ sc->lna[3] = val >> 8; /* channel group 3 */
+
+ mtw_srom_read(sc, MTW_EEPROM_LNA, &val);
+ sc->lna[0] = val & 0xff; /* channel group 0 */
+ sc->lna[1] = val >> 8; /* channel group 1 */
+ MTW_DPRINTF(sc, MTW_DEBUG_ROM, "LNA0 0x%x\n", sc->lna[0]);
+
+ /* fix broken 5GHz LNA entries */
+ if (sc->lna[2] == 0 || sc->lna[2] == 0xff) {
+ MTW_DPRINTF(sc, MTW_DEBUG_ROM,
+ "invalid LNA for channel group %d\n", 2);
+ sc->lna[2] = sc->lna[1];
+ }
+ if (sc->lna[3] == 0 || sc->lna[3] == 0xff) {
+ MTW_DPRINTF(sc, MTW_DEBUG_ROM,
+ "invalid LNA for channel group %d\n", 3);
+ sc->lna[3] = sc->lna[1];
+ }
+
+ /* fix broken RSSI offset entries */
+ for (ant = 0; ant < 3; ant++) {
+ if (sc->rssi_2ghz[ant] < -10 || sc->rssi_2ghz[ant] > 10) {
+ MTW_DPRINTF(sc, MTW_DEBUG_ROM,
+ "invalid RSSI%d offset: %d (2GHz)\n", ant + 1,
+ sc->rssi_2ghz[ant]);
+ sc->rssi_2ghz[ant] = 0;
+ }
+ if (sc->rssi_5ghz[ant] < -10 || sc->rssi_5ghz[ant] > 10) {
+ MTW_DPRINTF(sc, MTW_DEBUG_ROM,
+ "invalid RSSI%d offset: %d (5GHz)\n", ant + 1,
+ sc->rssi_5ghz[ant]);
+ sc->rssi_5ghz[ant] = 0;
+ }
+ }
+ return (0);
+}
+static int
+mtw_media_change(if_t ifp)
+{
+ struct ieee80211vap *vap = if_getsoftc(ifp);
+ struct ieee80211com *ic = vap->iv_ic;
+ const struct ieee80211_txparam *tp;
+ struct mtw_softc *sc = ic->ic_softc;
+ uint8_t rate, ridx;
+
+ MTW_LOCK(sc);
+ ieee80211_media_change(ifp);
+ //tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
+ tp = &vap->iv_txparms[ic->ic_curmode];
+ if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ struct ieee80211_node *ni;
+ struct mtw_node *rn;
+ /* XXX TODO: methodize with MCS rates */
+ rate =
+ ic->ic_sup_rates[ic->ic_curmode].rs_rates[tp->ucastrate] &
+ IEEE80211_RATE_VAL;
+ for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++) {
+ if (rt2860_rates[ridx].rate == rate)
+ break;
+ }
+ ni = ieee80211_ref_node(vap->iv_bss);
+ rn = MTW_NODE(ni);
+ rn->fix_ridx = ridx;
+
+ MTW_DPRINTF(sc, MTW_DEBUG_RATE, "rate=%d, fix_ridx=%d\n", rate,
+ rn->fix_ridx);
+ ieee80211_free_node(ni);
+ }
+ MTW_UNLOCK(sc);
+
+ return (0);
+}
+
+void
+mtw_set_leds(struct mtw_softc *sc, uint16_t which)
+{
+ struct mtw_mcu_cmd_8 cmd;
+ cmd.func = htole32(0x1);
+ cmd.val = htole32(which);
+ mtw_mcu_cmd(sc, CMD_LED_MODE, &cmd, sizeof(struct mtw_mcu_cmd_8));
+}
+static void
+mtw_abort_tsf_sync(struct mtw_softc *sc)
+{
+ uint32_t tmp;
+
+ mtw_read(sc, MTW_BCN_TIME_CFG, &tmp);
+ tmp &= ~(MTW_BCN_TX_EN | MTW_TSF_TIMER_EN | MTW_TBTT_TIMER_EN);
+ mtw_write(sc, MTW_BCN_TIME_CFG, tmp);
+}
+static int
+mtw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ const struct ieee80211_txparam *tp;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct mtw_softc *sc = ic->ic_softc;
+ struct mtw_vap *rvp = MTW_VAP(vap);
+ enum ieee80211_state ostate;
+ uint32_t sta[3];
+ uint8_t ratectl = 0;
+ uint8_t restart_ratectl = 0;
+ uint8_t bid = 1 << rvp->rvp_id;
+
+
+ ostate = vap->iv_state;
+ MTW_DPRINTF(sc, MTW_DEBUG_STATE, "%s -> %s\n",
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
+ IEEE80211_UNLOCK(ic);
+ MTW_LOCK(sc);
+ ratectl = sc->ratectl_run; /* remember current state */
+ usb_callout_stop(&sc->ratectl_ch);
+ sc->ratectl_run = MTW_RATECTL_OFF;
+ if (ostate == IEEE80211_S_RUN) {
+ /* turn link LED off */
+ }
+
+ switch (nstate) {
+ case IEEE80211_S_INIT:
+ restart_ratectl = 1;
+ if (ostate != IEEE80211_S_RUN)
+ break;
+
+ ratectl &= ~bid;
+ sc->runbmap &= ~bid;
+
+ /* abort TSF synchronization if there is no vap running */
+ if (--sc->running == 0)
+ mtw_abort_tsf_sync(sc);
+ break;
+
+ case IEEE80211_S_RUN:
+ if (!(sc->runbmap & bid)) {
+ if (sc->running++)
+ restart_ratectl = 1;
+ sc->runbmap |= bid;
+ }
+
+ m_freem(rvp->beacon_mbuf);
+ rvp->beacon_mbuf = NULL;
+
+ switch (vap->iv_opmode) {
+ case IEEE80211_M_HOSTAP:
+ case IEEE80211_M_MBSS:
+ sc->ap_running |= bid;
+ ic->ic_opmode = vap->iv_opmode;
+ mtw_update_beacon_cb(vap);
+ break;
+ case IEEE80211_M_IBSS:
+ sc->adhoc_running |= bid;
+ if (!sc->ap_running)
+ ic->ic_opmode = vap->iv_opmode;
+ mtw_update_beacon_cb(vap);
+ break;
+ case IEEE80211_M_STA:
+ sc->sta_running |= bid;
+ if (!sc->ap_running && !sc->adhoc_running)
+ ic->ic_opmode = vap->iv_opmode;
+
+ /* read statistic counters (clear on read) */
+ mtw_read_region_1(sc, MTW_TX_STA_CNT0, (uint8_t *)sta,
+ sizeof sta);
+
+ break;
+ default:
+ ic->ic_opmode = vap->iv_opmode;
+ break;
+ }
+
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
+ struct ieee80211_node *ni;
+
+ if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) {
+ MTW_UNLOCK(sc);
+ IEEE80211_LOCK(ic);
+ return (-1);
+ }
+ mtw_updateslot(ic);
+ mtw_enable_mrr(sc);
+ mtw_set_txpreamble(sc);
+ mtw_set_basicrates(sc);
+ ni = ieee80211_ref_node(vap->iv_bss);
+ IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid);
+ mtw_set_bssid(sc, sc->sc_bssid);
+ ieee80211_free_node(ni);
+ mtw_enable_tsf_sync(sc);
+
+ /* enable automatic rate adaptation */
+ tp = &vap->iv_txparms[ieee80211_chan2mode(
+ ic->ic_curchan)];
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
+ ratectl |= bid;
+ } else {
+ mtw_enable_tsf_sync(sc);
+ }
+
+ break;
+ default:
+ MTW_DPRINTF(sc, MTW_DEBUG_STATE, "undefined state\n");
+ break;
+ }
+
+ /* restart amrr for running VAPs */
+ if ((sc->ratectl_run = ratectl) && restart_ratectl) {
+ usb_callout_reset(&sc->ratectl_ch, hz, mtw_ratectl_to, sc);
+ }
+ MTW_UNLOCK(sc);
+ IEEE80211_LOCK(ic);
+ return (rvp->newstate(vap, nstate, arg));
+}
+
+static int
+mtw_wme_update(struct ieee80211com *ic)
+{
+ struct chanAccParams chp;
+ struct mtw_softc *sc = ic->ic_softc;
+ const struct wmeParams *ac;
+ int aci, error = 0;
+ ieee80211_wme_ic_getparams(ic, &chp);
+ ac = chp.cap_wmeParams;
+
+ MTW_LOCK(sc);
+ /* update MAC TX configuration registers */
+ for (aci = 0; aci < WME_NUM_AC; aci++) {
+ error = mtw_write(sc, MTW_EDCA_AC_CFG(aci),
+ ac[aci].wmep_logcwmax << 16 | ac[aci].wmep_logcwmin << 12 |
+ ac[aci].wmep_aifsn << 8 | ac[aci].wmep_txopLimit);
+ if (error)
+ goto err;
+ }
+
+ /* update SCH/DMA registers too */
+ error = mtw_write(sc, MTW_WMM_AIFSN_CFG,
+ ac[WME_AC_VO].wmep_aifsn << 12 | ac[WME_AC_VI].wmep_aifsn << 8 |
+ ac[WME_AC_BK].wmep_aifsn << 4 | ac[WME_AC_BE].wmep_aifsn);
+ if (error)
+ goto err;
+ error = mtw_write(sc, MTW_WMM_CWMIN_CFG,
+ ac[WME_AC_VO].wmep_logcwmin << 12 |
+ ac[WME_AC_VI].wmep_logcwmin << 8 |
+ ac[WME_AC_BK].wmep_logcwmin << 4 | ac[WME_AC_BE].wmep_logcwmin);
+ if (error)
+ goto err;
+ error = mtw_write(sc, MTW_WMM_CWMAX_CFG,
+ ac[WME_AC_VO].wmep_logcwmax << 12 |
+ ac[WME_AC_VI].wmep_logcwmax << 8 |
+ ac[WME_AC_BK].wmep_logcwmax << 4 | ac[WME_AC_BE].wmep_logcwmax);
+ if (error)
+ goto err;
+ error = mtw_write(sc, MTW_WMM_TXOP0_CFG,
+ ac[WME_AC_BK].wmep_txopLimit << 16 | ac[WME_AC_BE].wmep_txopLimit);
+ if (error)
+ goto err;
+ error = mtw_write(sc, MTW_WMM_TXOP1_CFG,
+ ac[WME_AC_VO].wmep_txopLimit << 16 | ac[WME_AC_VI].wmep_txopLimit);
+
+err:
+ MTW_UNLOCK(sc);
+ if (error)
+ MTW_DPRINTF(sc, MTW_DEBUG_USB, "WME update failed\n");
+
+ return (error);
+}
+
+static int
+mtw_key_set(struct ieee80211vap *vap, struct ieee80211_key *k)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct mtw_softc *sc = ic->ic_softc;
+ uint32_t i;
+
+ i = MTW_CMDQ_GET(&sc->cmdq_store);
+ MTW_DPRINTF(sc, MTW_DEBUG_KEY, "cmdq_store=%d\n", i);
+ sc->cmdq[i].func = mtw_key_set_cb;
+ sc->cmdq[i].arg0 = NULL;
+ sc->cmdq[i].arg1 = vap;
+ sc->cmdq[i].k = k;
+ IEEE80211_ADDR_COPY(sc->cmdq[i].mac, k->wk_macaddr);
+ ieee80211_runtask(ic, &sc->cmdq_task);
+
+ /*
+ * To make sure key will be set when hostapd
+ * calls iv_key_set() before if_init().
+ */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ MTW_LOCK(sc);
+ sc->cmdq_key_set = MTW_CMDQ_GO;
+ MTW_UNLOCK(sc);
+ }
+
+ return (1);
+}
+static void
+mtw_key_set_cb(void *arg)
+{
+ struct mtw_cmdq *cmdq = arg;
+ struct ieee80211vap *vap = cmdq->arg1;
+ struct ieee80211_key *k = cmdq->k;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct mtw_softc *sc = ic->ic_softc;
+ struct ieee80211_node *ni;
+ u_int cipher = k->wk_cipher->ic_cipher;
+ uint32_t attr;
+ uint16_t base;
+ uint8_t mode, wcid, iv[8];
+ MTW_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP)
+ ni = ieee80211_find_vap_node(&ic->ic_sta, vap, cmdq->mac);
+ else
+ ni = vap->iv_bss;
+
+ /* map net80211 cipher to RT2860 security mode */
+ switch (cipher) {
+ case IEEE80211_CIPHER_WEP:
+ if (k->wk_keylen < 8)
+ mode = MTW_MODE_WEP40;
+ else
+ mode = MTW_MODE_WEP104;
+ break;
+ case IEEE80211_CIPHER_TKIP:
+ mode = MTW_MODE_TKIP;
+ break;
+ case IEEE80211_CIPHER_AES_CCM:
+ mode = MTW_MODE_AES_CCMP;
+ break;
+ default:
+ MTW_DPRINTF(sc, MTW_DEBUG_KEY, "undefined case\n");
+ return;
+ }
+
+ if (k->wk_flags & IEEE80211_KEY_GROUP) {
+ wcid = 0; /* NB: update WCID0 for group keys */
+ base = MTW_SKEY(0, k->wk_keyix);
+ } else {
+ wcid = (ni != NULL) ? MTW_AID2WCID(ni->ni_associd) : 0;
+ base = MTW_PKEY(wcid);
+ }
+
+ if (cipher == IEEE80211_CIPHER_TKIP) {
+ mtw_write_region_1(sc, base, k->wk_key, 16);
+ mtw_write_region_1(sc, base + 16, &k->wk_key[24], 8);
+ mtw_write_region_1(sc, base + 24, &k->wk_key[16], 8);
+ } else {
+ /* roundup len to 16-bit: XXX fix write_region_1() instead */
+ mtw_write_region_1(sc, base, k->wk_key,
+ (k->wk_keylen + 1) & ~1);
+ }
+
+ if (!(k->wk_flags & IEEE80211_KEY_GROUP) ||
+ (k->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))) {
+ /* set initial packet number in IV+EIV */
+ if (cipher == IEEE80211_CIPHER_WEP) {
+ memset(iv, 0, sizeof iv);
+ iv[3] = vap->iv_def_txkey << 6;
+ } else {
+ if (cipher == IEEE80211_CIPHER_TKIP) {
+ iv[0] = k->wk_keytsc >> 8;
+ iv[1] = (iv[0] | 0x20) & 0x7f;
+ iv[2] = k->wk_keytsc;
+ } else { //CCMP
+ iv[0] = k->wk_keytsc;
+ iv[1] = k->wk_keytsc >> 8;
+ iv[2] = 0;
+ }
+ iv[3] = k->wk_keyix << 6 | IEEE80211_WEP_EXTIV;
+ iv[4] = k->wk_keytsc >> 16;
+ iv[5] = k->wk_keytsc >> 24;
+ iv[6] = k->wk_keytsc >> 32;
+ iv[7] = k->wk_keytsc >> 40;
+ }
+ mtw_write_region_1(sc, MTW_IVEIV(wcid), iv, 8);
+ }
+
+ if (k->wk_flags & IEEE80211_KEY_GROUP) {
+ /* install group key */
+ mtw_read(sc, MTW_SKEY_MODE_0_7, &attr);
+ attr &= ~(0xf << (k->wk_keyix * 4));
+ attr |= mode << (k->wk_keyix * 4);
+ mtw_write(sc, MTW_SKEY_MODE_0_7, attr);
+
+ if (cipher & (IEEE80211_CIPHER_WEP)) {
+ mtw_read(sc, MTW_WCID_ATTR(wcid + 1), &attr);
+ attr = (attr & ~0xf) | (mode << 1);
+ mtw_write(sc, MTW_WCID_ATTR(wcid + 1), attr);
+
+ mtw_set_region_4(sc, MTW_IVEIV(0), 0, 4);
+
+ mtw_read(sc, MTW_WCID_ATTR(wcid), &attr);
+ attr = (attr & ~0xf) | (mode << 1);
+ mtw_write(sc, MTW_WCID_ATTR(wcid), attr);
+ }
+ } else {
+ /* install pairwise key */
+ mtw_read(sc, MTW_WCID_ATTR(wcid), &attr);
+ attr = (attr & ~0xf) | (mode << 1) | MTW_RX_PKEY_EN;
+ mtw_write(sc, MTW_WCID_ATTR(wcid), attr);
+ }
+ k->wk_pad = wcid;
+}
+
+/*
+ * If wlan is destroyed without being brought down i.e. without
+ * wlan down or wpa_cli terminate, this function is called after
+ * vap is gone. Don't refer it.
+ */
+static void
+mtw_key_delete_cb(void *arg)
+{
+ struct mtw_cmdq *cmdq = arg;
+ struct mtw_softc *sc = cmdq->arg1;
+ struct ieee80211_key *k = &cmdq->key;
+ uint32_t attr;
+ uint8_t wcid;
+
+ MTW_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (k->wk_flags & IEEE80211_KEY_GROUP) {
+ /* remove group key */
+ MTW_DPRINTF(sc, MTW_DEBUG_KEY, "removing group key\n");
+ mtw_read(sc, MTW_SKEY_MODE_0_7, &attr);
+ attr &= ~(0xf << (k->wk_keyix * 4));
+ mtw_write(sc, MTW_SKEY_MODE_0_7, attr);
+ } else {
+ /* remove pairwise key */
+ MTW_DPRINTF(sc, MTW_DEBUG_KEY, "removing key for wcid %x\n",
+ k->wk_pad);
+ /* matching wcid was written to wk_pad in mtw_key_set() */
+ wcid = k->wk_pad;
+ mtw_read(sc, MTW_WCID_ATTR(wcid), &attr);
+ attr &= ~0xf;
+ mtw_write(sc, MTW_WCID_ATTR(wcid), attr);
+ }
+
+ k->wk_pad = 0;
+}
+
+/*
+ * return 0 on error
+ */
+static int
+mtw_key_delete(struct ieee80211vap *vap, struct ieee80211_key *k)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct mtw_softc *sc = ic->ic_softc;
+ struct ieee80211_key *k0;
+ uint32_t i;
+
+ /*
+ * When called back, key might be gone. So, make a copy
+ * of some values need to delete keys before deferring.
+ * But, because of LOR with node lock, cannot use lock here.
+ * So, use atomic instead.
+ */
+ i = MTW_CMDQ_GET(&sc->cmdq_store);
+ MTW_DPRINTF(sc, MTW_DEBUG_KEY, "cmdq_store=%d\n", i);
+ sc->cmdq[i].func = mtw_key_delete_cb;
+ sc->cmdq[i].arg0 = NULL;
+ sc->cmdq[i].arg1 = sc;
+ k0 = &sc->cmdq[i].key;
+ k0->wk_flags = k->wk_flags;
+ k0->wk_keyix = k->wk_keyix;
+ /* matching wcid was written to wk_pad in mtw_key_set() */
+ k0->wk_pad = k->wk_pad;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+ return (1); /* return fake success */
+}
+
+static void
+mtw_ratectl_to(void *arg)
+{
+ struct mtw_softc *sc = arg;
+ /* do it in a process context, so it can go sleep */
+ ieee80211_runtask(&sc->sc_ic, &sc->ratectl_task);
+ /* next timeout will be rescheduled in the callback task */
+}
+
+/* ARGSUSED */
+static void
+mtw_ratectl_cb(void *arg, int pending)
+{
+
+ struct mtw_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+
+ if (vap == NULL)
+ return;
+
+ ieee80211_iterate_nodes(&ic->ic_sta, mtw_iter_func, sc);
+
+ usb_callout_reset(&sc->ratectl_ch, hz, mtw_ratectl_to, sc);
+
+
+}
+
+static void
+mtw_drain_fifo(void *arg)
+{
+ struct mtw_softc *sc = arg;
+ uint32_t stat;
+ uint16_t(*wstat)[3];
+ uint8_t wcid, mcs, pid;
+ int8_t retry;
+
+ MTW_LOCK_ASSERT(sc, MA_OWNED);
+
+ for (;;) {
+ /* drain Tx status FIFO (maxsize = 16) */
+ mtw_read(sc, MTW_TX_STAT_FIFO, &stat);
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "tx stat 0x%08x\n", stat);
+ if (!(stat & MTW_TXQ_VLD))
+ break;
+
+ wcid = (stat >> MTW_TXQ_WCID_SHIFT) & 0xff;
+
+ /* if no ACK was requested, no feedback is available */
+ if (!(stat & MTW_TXQ_ACKREQ) || wcid > MTW_WCID_MAX ||
+ wcid == 0)
+ continue;
+
+ /*
+ * Even though each stat is Tx-complete-status like format,
+ * the device can poll stats. Because there is no guarantee
+ * that the referring node is still around when read the stats.
+ * So that, if we use ieee80211_ratectl_tx_update(), we will
+ * have hard time not to refer already freed node.
+ *
+ * To eliminate such page faults, we poll stats in softc.
+ * Then, update the rates later with
+ * ieee80211_ratectl_tx_update().
+ */
+ wstat = &(sc->wcid_stats[wcid]);
+ (*wstat)[MTW_TXCNT]++;
+ if (stat & MTW_TXQ_OK)
+ (*wstat)[MTW_SUCCESS]++;
+ else
+ counter_u64_add(sc->sc_ic.ic_oerrors, 1);
+ /*
+ * Check if there were retries, ie if the Tx success rate is
+ * different from the requested rate. Note that it works only
+ * because we do not allow rate fallback from OFDM to CCK.
+ */
+ mcs = (stat >> MTW_TXQ_MCS_SHIFT) & 0x7f;
+ pid = (stat >> MTW_TXQ_PID_SHIFT) & 0xf;
+ if ((retry = pid - 1 - mcs) > 0) {
+ (*wstat)[MTW_TXCNT] += retry;
+ (*wstat)[MTW_RETRY] += retry;
+ }
+ }
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "count=%d\n", sc->fifo_cnt);
+
+ sc->fifo_cnt = 0;
+}
+
+static void
+mtw_iter_func(void *arg, struct ieee80211_node *ni)
+{
+ struct mtw_softc *sc = arg;
+ MTW_LOCK(sc);
+ struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct mtw_node *rn = MTW_NODE(ni);
+ uint32_t sta[3];
+ uint16_t(*wstat)[3];
+ int error, ridx;
+
+
+ /* Check for special case */
+ if (sc->rvp_cnt <= 1 && vap->iv_opmode == IEEE80211_M_STA &&
+ ni != vap->iv_bss)
+ goto fail;
+
+ txs->flags = IEEE80211_RATECTL_TX_STATS_NODE |
+ IEEE80211_RATECTL_TX_STATS_RETRIES;
+ txs->ni = ni;
+ if (sc->rvp_cnt <= 1 &&
+ (vap->iv_opmode == IEEE80211_M_IBSS ||
+ vap->iv_opmode == IEEE80211_M_STA)) {
+ /*
+ * read statistic counters (clear on read) and update AMRR state
+ */
+ error = mtw_read_region_1(sc, MTW_TX_STA_CNT0, (uint8_t *)sta,
+ sizeof sta);
+ MTW_DPRINTF(sc, MTW_DEBUG_RATE, "error:%d\n", error);
+ if (error != 0)
+ goto fail;
+
+ /* count failed TX as errors */
+ if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS,
+ le32toh(sta[0]) & 0xffff);
+
+ txs->nretries = (le32toh(sta[1]) >> 16);
+ txs->nsuccess = (le32toh(sta[1]) & 0xffff);
+ /* nretries??? */
+ txs->nframes = txs->nsuccess + (le32toh(sta[0]) & 0xffff);
+
+ MTW_DPRINTF(sc, MTW_DEBUG_RATE,
+ "retrycnt=%d success=%d failcnt=%d\n", txs->nretries,
+ txs->nsuccess, le32toh(sta[0]) & 0xffff);
+ } else {
+ wstat = &(sc->wcid_stats[MTW_AID2WCID(ni->ni_associd)]);
+
+ if (wstat == &(sc->wcid_stats[0]) ||
+ wstat > &(sc->wcid_stats[MTW_WCID_MAX]))
+ goto fail;
+
+ txs->nretries = (*wstat)[MTW_RETRY];
+ txs->nsuccess = (*wstat)[MTW_SUCCESS];
+ txs->nframes = (*wstat)[MTW_TXCNT];
+ MTW_DPRINTF(sc, MTW_DEBUG_RATE,
+ "wstat retrycnt=%d txcnt=%d success=%d\n", txs->nretries,
+ txs->nframes, txs->nsuccess);
+
+ memset(wstat, 0, sizeof(*wstat));
+ }
+
+ ieee80211_ratectl_tx_update(vap, txs);
+ ieee80211_ratectl_rate(ni, NULL, 0);
+ /* XXX TODO: methodize with MCS rates */
+ for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++) {
+ MTW_DPRINTF(sc, MTW_DEBUG_RATE, "ni_txrate=0x%x\n",
+ ni->ni_txrate);
+ if (rt2860_rates[ridx].rate == ni->ni_txrate) {
+ break;
+ }
+ }
+ rn->amrr_ridx = ridx;
+fail:
+ MTW_UNLOCK(sc);
+
+ MTW_DPRINTF(sc, MTW_DEBUG_RATE, "rate=%d, ridx=%d\n",
+ ni->ni_txrate, rn->amrr_ridx);
+}
+
+static void
+mtw_newassoc_cb(void *arg)
+{
+ struct mtw_cmdq *cmdq = arg;
+ struct ieee80211_node *ni = cmdq->arg1;
+ struct mtw_softc *sc = ni->ni_vap->iv_ic->ic_softc;
+
+ uint8_t wcid = cmdq->wcid;
+
+ MTW_LOCK_ASSERT(sc, MA_OWNED);
+
+ mtw_write_region_1(sc, MTW_WCID_ENTRY(wcid), ni->ni_macaddr,
+ IEEE80211_ADDR_LEN);
+
+ memset(&(sc->wcid_stats[wcid]), 0, sizeof(sc->wcid_stats[wcid]));
+}
+
+static void
+mtw_newassoc(struct ieee80211_node *ni, int isnew)
+{
+
+ struct mtw_node *mn = MTW_NODE(ni);
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct mtw_softc *sc = ic->ic_softc;
+
+ uint8_t rate;
+ uint8_t ridx;
+ uint8_t wcid;
+ //int i;
+ // int i,j;
+ wcid = MTW_AID2WCID(ni->ni_associd);
+
+ if (wcid > MTW_WCID_MAX) {
+ device_printf(sc->sc_dev, "wcid=%d out of range\n", wcid);
+ return;
+ }
+
+ /* only interested in true associations */
+ if (isnew && ni->ni_associd != 0) {
+ /*
+ * This function could is called though timeout function.
+ * Need to deferggxr.
+ */
+
+ uint32_t cnt = MTW_CMDQ_GET(&sc->cmdq_store);
+ MTW_DPRINTF(sc, MTW_DEBUG_STATE, "cmdq_store=%d\n", cnt);
+ sc->cmdq[cnt].func = mtw_newassoc_cb;
+ sc->cmdq[cnt].arg0 = NULL;
+ sc->cmdq[cnt].arg1 = ni;
+ sc->cmdq[cnt].wcid = wcid;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+ }
+
+ MTW_DPRINTF(sc, MTW_DEBUG_STATE,
+ "new assoc isnew=%d associd=%x addr=%s\n", isnew, ni->ni_associd,
+ ether_sprintf(ni->ni_macaddr));
+ rate = vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)].mgmtrate;
+ /* XXX TODO: methodize with MCS rates */
+ for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++)
+ if (rt2860_rates[ridx].rate == rate)
+ break;
+ mn->mgt_ridx = ridx;
+ MTW_DPRINTF(sc, MTW_DEBUG_STATE | MTW_DEBUG_RATE,
+ "rate=%d, ctl_ridx=%d\n", rate, ridx);
+ MTW_LOCK(sc);
+ if (sc->ratectl_run != MTW_RATECTL_OFF) {
+ usb_callout_reset(&sc->ratectl_ch, hz, &mtw_ratectl_to, sc);
+ }
+ MTW_UNLOCK(sc);
+
+}
+
+/*
+ * Return the Rx chain with the highest RSSI for a given frame.
+ */
+static __inline uint8_t
+mtw_maxrssi_chain(struct mtw_softc *sc, const struct mtw_rxwi *rxwi)
+{
+ uint8_t rxchain = 0;
+
+ if (sc->nrxchains > 1) {
+ if (rxwi->rssi[1] > rxwi->rssi[rxchain])
+ rxchain = 1;
+ if (sc->nrxchains > 2)
+ if (rxwi->rssi[2] > rxwi->rssi[rxchain])
+ rxchain = 2;
+ }
+ return (rxchain);
+}
+static void
+mtw_get_tsf(struct mtw_softc *sc, uint64_t *buf)
+{
+ mtw_read_region_1(sc, MTW_TSF_TIMER_DW0, (uint8_t *)buf, sizeof(*buf));
+}
+
+static void
+mtw_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype,
+ const struct ieee80211_rx_stats *rxs, int rssi, int nf)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct mtw_softc *sc = vap->iv_ic->ic_softc;
+ struct mtw_vap *rvp = MTW_VAP(vap);
+ uint64_t ni_tstamp, rx_tstamp;
+
+ rvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf);
+
+ if (vap->iv_state == IEEE80211_S_RUN &&
+ (subtype == IEEE80211_FC0_SUBTYPE_BEACON ||
+ subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) {
+ ni_tstamp = le64toh(ni->ni_tstamp.tsf);
+ MTW_LOCK(sc);
+ mtw_get_tsf(sc, &rx_tstamp);
+ MTW_UNLOCK(sc);
+ rx_tstamp = le64toh(rx_tstamp);
+
+ if (ni_tstamp >= rx_tstamp) {
+ MTW_DPRINTF(sc, MTW_DEBUG_RECV | MTW_DEBUG_BEACON,
+ "ibss merge, tsf %ju tstamp %ju\n",
+ (uintmax_t)rx_tstamp, (uintmax_t)ni_tstamp);
+ (void)ieee80211_ibss_merge(ni);
+ }
+ }
+}
+static void
+mtw_rx_frame(struct mtw_softc *sc, struct mbuf *m, uint32_t dmalen)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
+ struct epoch_tracker et;
+
+ struct mtw_rxwi *rxwi;
+ uint32_t flags;
+ uint16_t len, rxwisize;
+ uint8_t ant, rssi;
+ int8_t nf;
+
+ rxwisize = sizeof(struct mtw_rxwi);
+
+ if (__predict_false(
+ dmalen < rxwisize + sizeof(struct ieee80211_frame_ack))) {
+ MTW_DPRINTF(sc, MTW_DEBUG_RECV,
+ "payload is too short: dma length %u < %zu\n", dmalen,
+ rxwisize + sizeof(struct ieee80211_frame_ack));
+ goto fail;
+ }
+
+ rxwi = mtod(m, struct mtw_rxwi *);
+ len = le16toh(rxwi->len) & 0xfff;
+ flags = le32toh(rxwi->flags);
+ if (__predict_false(len > dmalen - rxwisize)) {
+ MTW_DPRINTF(sc, MTW_DEBUG_RECV, "bad RXWI length %u > %u\n",
+ len, dmalen);
+ goto fail;
+ }
+
+ if (__predict_false(flags & (MTW_RX_CRCERR | MTW_RX_ICVERR))) {
+ MTW_DPRINTF(sc, MTW_DEBUG_RECV, "%s error.\n",
+ (flags & MTW_RX_CRCERR) ? "CRC" : "ICV");
+ goto fail;
+ }
+
+ if (flags & MTW_RX_L2PAD) {
+ MTW_DPRINTF(sc, MTW_DEBUG_RECV,
+ "received RT2860_RX_L2PAD frame\n");
+ len += 2;
+ }
+
+ m->m_data += rxwisize;
+ m->m_pkthdr.len = m->m_len = len;
+
+ wh = mtod(m, struct ieee80211_frame *);
+ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
+ wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
+ m->m_flags |= M_WEP;
+ }
+
+ if (len >= sizeof(struct ieee80211_frame_min)) {
+ ni = ieee80211_find_rxnode(ic,
+ mtod(m, struct ieee80211_frame_min *));
+ } else
+ ni = NULL;
+
+ if (ni && ni->ni_flags & IEEE80211_NODE_HT) {
+ m->m_flags |= M_AMPDU;
+ }
+
+ if (__predict_false(flags & MTW_RX_MICERR)) {
+ /* report MIC failures to net80211 for TKIP */
+ if (ni != NULL)
+ ieee80211_notify_michael_failure(ni->ni_vap, wh,
+ rxwi->keyidx);
+ MTW_DPRINTF(sc, MTW_DEBUG_RECV,
+ "MIC error. Someone is lying.\n");
+ goto fail;
+ }
+
+ ant = mtw_maxrssi_chain(sc, rxwi);
+ rssi = rxwi->rssi[ant];
+ nf = mtw_rssi2dbm(sc, rssi, ant);
+
+ if (__predict_false(ieee80211_radiotap_active(ic))) {
+ struct mtw_rx_radiotap_header *tap = &sc->sc_rxtap;
+ uint16_t phy;
+
+ tap->wr_flags = 0;
+ if (flags & MTW_RX_L2PAD)
+ tap->wr_flags |= IEEE80211_RADIOTAP_F_DATAPAD;
+ tap->wr_antsignal = rssi;
+ tap->wr_antenna = ant;
+ tap->wr_dbm_antsignal = mtw_rssi2dbm(sc, rssi, ant);
+ tap->wr_rate = 2; /* in case it can't be found below */
+ //MTW_LOCK(sc);
+
+ // MTW_UNLOCK(sc);
+ phy = le16toh(rxwi->phy);
+ switch (phy >> MT7601_PHY_SHIFT) {
+ case MTW_PHY_CCK:
+ switch ((phy & MTW_PHY_MCS) & ~MTW_PHY_SHPRE) {
+ case 0:
+ tap->wr_rate = 2;
+ break;
+ case 1:
+ tap->wr_rate = 4;
+ break;
+ case 2:
+ tap->wr_rate = 11;
+ break;
+ case 3:
+ tap->wr_rate = 22;
+ break;
+ }
+ if (phy & MTW_PHY_SHPRE)
+ tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+ break;
+ case MTW_PHY_OFDM:
+ switch (phy & MTW_PHY_MCS) {
+ case 0:
+ tap->wr_rate = 12;
+ break;
+ case 1:
+ tap->wr_rate = 18;
+ break;
+ case 2:
+ tap->wr_rate = 24;
+ break;
+ case 3:
+ tap->wr_rate = 36;
+ break;
+ case 4:
+ tap->wr_rate = 48;
+ break;
+ case 5:
+ tap->wr_rate = 72;
+ break;
+ case 6:
+ tap->wr_rate = 96;
+ break;
+ case 7:
+ tap->wr_rate = 108;
+ break;
+ }
+ break;
+ }
+ }
+
+ NET_EPOCH_ENTER(et);
+ if (ni != NULL) {
+ (void)ieee80211_input(ni, m, rssi, nf);
+ ieee80211_free_node(ni);
+ } else {
+ (void)ieee80211_input_all(ic, m, rssi, nf);
+ }
+ NET_EPOCH_EXIT(et);
+
+ return;
+
+fail:
+ m_freem(m);
+ counter_u64_add(ic->ic_ierrors, 1);
+}
+
+static void
+mtw_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct mtw_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct mbuf *m = NULL;
+ struct mbuf *m0;
+ uint32_t dmalen, mbuf_len;
+ uint16_t rxwisize;
+ int xferlen;
+
+ rxwisize = sizeof(struct mtw_rxwi);
+
+ usbd_xfer_status(xfer, &xferlen, NULL, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ MTW_DPRINTF(sc, MTW_DEBUG_RECV, "rx done, actlen=%d\n",
+ xferlen);
+ if (xferlen < (int)(sizeof(uint32_t) + rxwisize +
+ sizeof(struct mtw_rxd))) {
+ MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB,
+ "xfer too short %d %d\n", xferlen,
+ (int)(sizeof(uint32_t) + rxwisize +
+ sizeof(struct mtw_rxd)));
+ goto tr_setup;
+ }
+
+ m = sc->rx_m;
+ sc->rx_m = NULL;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+ tr_setup:
+
+ if (sc->rx_m == NULL) {
+ sc->rx_m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR,
+ MTW_MAX_RXSZ);
+ }
+ if (sc->rx_m == NULL) {
+ MTW_DPRINTF(sc,
+ MTW_DEBUG_RECV | MTW_DEBUG_RECV_DESC |
+ MTW_DEBUG_USB,
+ "could not allocate mbuf - idle with stall\n");
+ counter_u64_add(ic->ic_ierrors, 1);
+ usbd_xfer_set_stall(xfer);
+ usbd_xfer_set_frames(xfer, 0);
+ } else {
+ /*
+ * Directly loading a mbuf cluster into DMA to
+ * save some data copying. This works because
+ * there is only one cluster.
+ */
+ usbd_xfer_set_frame_data(xfer, 0,
+ mtod(sc->rx_m, caddr_t), MTW_MAX_RXSZ);
+ usbd_xfer_set_frames(xfer, 1);
+ }
+ usbd_transfer_submit(xfer);
+ break;
+
+ default: /* Error */
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB,
+ "USB transfer error, %s\n", usbd_errstr(error));
+
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ if (error == USB_ERR_TIMEOUT)
+ device_printf(sc->sc_dev, "device timeout %s\n",
+ __func__);
+ counter_u64_add(ic->ic_ierrors, 1);
+ goto tr_setup;
+ }
+ if (sc->rx_m != NULL) {
+ m_freem(sc->rx_m);
+ sc->rx_m = NULL;
+ }
+ break;
+ }
+
+ if (m == NULL)
+ return;
+
+ /* inputting all the frames must be last */
+
+ MTW_UNLOCK(sc);
+
+ m->m_pkthdr.len = m->m_len = xferlen;
+
+ /* HW can aggregate multiple 802.11 frames in a single USB xfer */
+ for (;;) {
+ dmalen = le32toh(*mtod(m, uint32_t *)) & 0xffff;
+
+ if ((dmalen >= (uint32_t)-8) || (dmalen == 0) ||
+ ((dmalen & 3) != 0)) {
+ MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB,
+ "bad DMA length %u\n", dmalen);
+ break;
+ }
+ if ((dmalen + 8) > (uint32_t)xferlen) {
+ MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB,
+ "bad DMA length %u > %d\n", dmalen + 8, xferlen);
+ break;
+ }
+
+ /* If it is the last one or a single frame, we won't copy. */
+ if ((xferlen -= dmalen + 8) <= 8) {
+ /* trim 32-bit DMA-len header */
+ m->m_data += 4;
+ m->m_pkthdr.len = m->m_len -= 4;
+ mtw_rx_frame(sc, m, dmalen);
+ m = NULL; /* don't free source buffer */
+ break;
+ }
+
+ mbuf_len = dmalen + sizeof(struct mtw_rxd);
+ if (__predict_false(mbuf_len > MCLBYTES)) {
+ MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB,
+ "payload is too big: mbuf_len %u\n", mbuf_len);
+ counter_u64_add(ic->ic_ierrors, 1);
+ break;
+ }
+
+ /* copy aggregated frames to another mbuf */
+ m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+ if (__predict_false(m0 == NULL)) {
+ MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC,
+ "could not allocate mbuf\n");
+ counter_u64_add(ic->ic_ierrors, 1);
+ break;
+ }
+ m_copydata(m, 4 /* skip 32-bit DMA-len header */, mbuf_len,
+ mtod(m0, caddr_t));
+ m0->m_pkthdr.len = m0->m_len = mbuf_len;
+ mtw_rx_frame(sc, m0, dmalen);
+
+ /* update data ptr */
+ m->m_data += mbuf_len + 4;
+ m->m_pkthdr.len = m->m_len -= mbuf_len + 4;
+ }
+
+ /* make sure we free the source buffer, if any */
+ m_freem(m);
+
+#ifdef IEEE80211_SUPPORT_SUPERG
+ ieee80211_ff_age_all(ic, 100);
+#endif
+ MTW_LOCK(sc);
+}
+
+static void
+mtw_tx_free(struct mtw_endpoint_queue *pq, struct mtw_tx_data *data, int txerr)
+{
+
+ ieee80211_tx_complete(data->ni, data->m, txerr);
+ data->m = NULL;
+ data->ni = NULL;
+
+ STAILQ_INSERT_TAIL(&pq->tx_fh, data, next);
+ pq->tx_nfree++;
+}
+static void
+mtw_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error, u_int index)
+{
+ struct mtw_softc *sc = usbd_xfer_softc(xfer);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct mtw_tx_data *data;
+ struct ieee80211vap *vap = NULL;
+ struct usb_page_cache *pc;
+ struct mtw_endpoint_queue *pq = &sc->sc_epq[index];
+ struct mbuf *m;
+ usb_frlength_t size;
+ int actlen;
+ int sumlen;
+ usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB,
+ "transfer complete: %d bytes @ index %d\n", actlen, index);
+
+ data = usbd_xfer_get_priv(xfer);
+ mtw_tx_free(pq, data, 0);
+ usbd_xfer_set_priv(xfer, NULL);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+ tr_setup:
+ data = STAILQ_FIRST(&pq->tx_qh);
+ if (data == NULL)
+ break;
+
+ STAILQ_REMOVE_HEAD(&pq->tx_qh, next);
+
+ m = data->m;
+
+ size = sizeof(data->desc);
+ if ((m->m_pkthdr.len + size + 3 + 8) > MTW_MAX_TXSZ) {
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT_DESC | MTW_DEBUG_USB,
+ "data overflow, %u bytes\n", m->m_pkthdr.len);
+ mtw_tx_free(pq, data, 1);
+ goto tr_setup;
+ }
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_in(pc, 0, &data->desc, size);
+ usbd_m_copy_in(pc, size, m, 0, m->m_pkthdr.len);
+ size += m->m_pkthdr.len;
+ /*
+ * Align end on a 4-byte boundary, pad 8 bytes (CRC +
+ * 4-byte padding), and be sure to zero those trailing
+ * bytes:
+ */
+ usbd_frame_zero(pc, size, ((-size) & 3) + MTW_DMA_PAD);
+ size += ((-size) & 3) + MTW_DMA_PAD;
+
+ vap = data->ni->ni_vap;
+ if (ieee80211_radiotap_active_vap(vap)) {
+ const struct ieee80211_frame *wh;
+ struct mtw_tx_radiotap_header *tap = &sc->sc_txtap;
+ struct mtw_txwi *txwi =
+ (struct mtw_txwi *)(&data->desc +
+ sizeof(struct mtw_txd));
+ int has_l2pad;
+
+ wh = mtod(m, struct ieee80211_frame *);
+ has_l2pad = IEEE80211_HAS_ADDR4(wh) !=
+ IEEE80211_QOS_HAS_SEQ(wh);
+
+ tap->wt_flags = 0;
+ tap->wt_rate = rt2860_rates[data->ridx].rate;
+ tap->wt_hwqueue = index;
+ if (le16toh(txwi->phy) & MTW_PHY_SHPRE)
+ tap->wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+ if (has_l2pad)
+ tap->wt_flags |= IEEE80211_RADIOTAP_F_DATAPAD;
+
+ ieee80211_radiotap_tx(vap, m);
+ }
+
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB,
+ "sending frame len=%u/%u @ index %d\n", m->m_pkthdr.len,
+ size, index);
+
+ usbd_xfer_set_frame_len(xfer, 0, size);
+ usbd_xfer_set_priv(xfer, data);
+ usbd_transfer_submit(xfer);
+ mtw_start(sc);
+
+ break;
+
+ default:
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB,
+ "USB transfer error, %s\n", usbd_errstr(error));
+
+ data = usbd_xfer_get_priv(xfer);
+
+ if (data != NULL) {
+ if (data->ni != NULL)
+ vap = data->ni->ni_vap;
+ mtw_tx_free(pq, data, error);
+ usbd_xfer_set_priv(xfer, NULL);
+ }
+
+ if (vap == NULL)
+ vap = TAILQ_FIRST(&ic->ic_vaps);
+
+ if (error != USB_ERR_CANCELLED) {
+ if (error == USB_ERR_TIMEOUT) {
+ device_printf(sc->sc_dev, "device timeout %s\n",
+ __func__);
+ uint32_t i = MTW_CMDQ_GET(&sc->cmdq_store);
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB,
+ "cmdq_store=%d\n", i);
+ sc->cmdq[i].func = mtw_usb_timeout_cb;
+ sc->cmdq[i].arg0 = vap;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+ }
+
+ /*
+ * Try to clear stall first, also if other
+ * errors occur, hence clearing stall
+ * introduces a 50 ms delay:
+ */
+ usbd_xfer_set_stall(xfer);
+ goto tr_setup;
+ }
+ break;
+ }
+#ifdef IEEE80211_SUPPORT_SUPERG
+ /* XXX TODO: make this deferred rather than unlock/relock */
+ /* XXX TODO: should only do the QoS AC this belongs to */
+ if (pq->tx_nfree >= RUN_TX_RING_COUNT) {
+ RUN_UNLOCK(sc);
+ ieee80211_ff_flush_all(ic);
+ RUN_LOCK(sc);
+ }
+#endif
+}
+
+static void
+mtw_fw_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct mtw_softc *sc = usbd_xfer_softc(xfer);
+
+ int actlen;
+ int ntries, tmp;
+ // struct mtw_txd *data;
+
+ usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+ // data = usbd_xfer_get_priv(xfer);
+ usbd_xfer_set_priv(xfer, NULL);
+ switch (USB_GET_STATE(xfer)) {
+
+ case USB_ST_TRANSFERRED:
+ sc->sc_sent += actlen;
+ memset(sc->txd_fw[sc->sc_idx], 0, actlen);
+
+ if (actlen < 0x2000 && sc->sc_idx == 0) {
+ return;
+ }
+ if (sc->sc_idx == 5) {
+
+ if ((error = mtw_write_ivb(sc, sc->sc_ivb_1,
+ MTW_MCU_IVB_LEN)) != 0) {
+ device_printf(sc->sc_dev,
+ "Could not write ivb error: %d\n", error);
+ }
+
+ mtw_delay(sc, 10);
+ for (ntries = 0; ntries < 100; ntries++) {
+ if ((error = mtw_read_cfg(sc, MTW_MCU_DMA_ADDR,
+ &tmp)) != 0) {
+ device_printf(sc->sc_dev,
+ "Could not read cfg error: %d\n", error);
+
+ }
+ if (tmp == MTW_MCU_READY) {
+ MTW_DPRINTF(sc, MTW_DEBUG_FIRMWARE,
+ "mcu reaady %d\n", tmp);
+ sc->fwloading = 1;
+ break;
+ }
+
+ mtw_delay(sc, 10);
+ }
+ if (ntries == 100)
+ sc->fwloading = 0;
+ wakeup(&sc->fwloading);
+ return;
+ }
+
+ if (actlen == 0x2000) {
+ sc->sc_idx++;
+ DELAY(1000);
+ }
+
+ case USB_ST_SETUP: {
+ int dlen = 0;
+ dlen = sc->txd_fw[sc->sc_idx]->len;
+
+ mtw_write_cfg(sc, MTW_MCU_DMA_ADDR, 0x40 + sc->sc_sent);
+ mtw_write_cfg(sc, MTW_MCU_DMA_LEN, (dlen << 16));
+
+ usbd_xfer_set_frame_len(xfer, 0, dlen);
+ usbd_xfer_set_frame_data(xfer, 0, sc->txd_fw[sc->sc_idx], dlen);
+
+ // usbd_xfer_set_priv(xfer,sc->txd[sc->sc_idx]);
+ usbd_transfer_submit(xfer);
+ break;
+
+ default: /* Error */
+ device_printf(sc->sc_dev, "%s:%d %s\n", __FILE__, __LINE__,
+ usbd_errstr(error));
+ sc->fwloading = 0;
+ wakeup(&sc->fwloading);
+ /*
+ * Print error message and clear stall
+ * for example.
+ */
+ break;
+ }
+ /*
+ * Here it is safe to do something without the private
+ * USB mutex locked.
+ */
+ }
+ return;
+}
+static void
+mtw_bulk_tx_callback0(struct usb_xfer *xfer, usb_error_t error)
+{
+ mtw_bulk_tx_callbackN(xfer, error, 0);
+}
+
+static void
+mtw_bulk_tx_callback1(struct usb_xfer *xfer, usb_error_t error)
+{
+
+
+ mtw_bulk_tx_callbackN(xfer, error, 1);
+}
+
+static void
+mtw_bulk_tx_callback2(struct usb_xfer *xfer, usb_error_t error)
+{
+ mtw_bulk_tx_callbackN(xfer, error, 2);
+}
+
+static void
+mtw_bulk_tx_callback3(struct usb_xfer *xfer, usb_error_t error)
+{
+ mtw_bulk_tx_callbackN(xfer, error, 3);
+}
+
+static void
+mtw_bulk_tx_callback4(struct usb_xfer *xfer, usb_error_t error)
+{
+ mtw_bulk_tx_callbackN(xfer, error, 4);
+}
+
+static void
+mtw_bulk_tx_callback5(struct usb_xfer *xfer, usb_error_t error)
+{
+ mtw_bulk_tx_callbackN(xfer, error, 5);
+}
+
+static void
+mtw_set_tx_desc(struct mtw_softc *sc, struct mtw_tx_data *data)
+{
+ struct mbuf *m = data->m;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = data->ni->ni_vap;
+ struct ieee80211_frame *wh;
+ struct mtw_txd *txd;
+ struct mtw_txwi *txwi;
+ uint16_t xferlen, txwisize;
+ uint16_t mcs;
+ uint8_t ridx = data->ridx;
+ uint8_t pad;
+
+ /* get MCS code from rate index */
+ mcs = rt2860_rates[ridx].mcs;
+
+ txwisize = sizeof(*txwi);
+ xferlen = txwisize + m->m_pkthdr.len;
+
+ /* roundup to 32-bit alignment */
+ xferlen = (xferlen + 3) & ~3;
+
+ txd = (struct mtw_txd *)&data->desc;
+ txd->len = htole16(xferlen);
+
+ wh = mtod(m, struct ieee80211_frame *);
+
+ /*
+ * Ether both are true or both are false, the header
+ * are nicely aligned to 32-bit. So, no L2 padding.
+ */
+ if (IEEE80211_HAS_ADDR4(wh) == IEEE80211_QOS_HAS_SEQ(wh))
+ pad = 0;
+ else
+ pad = 2;
+
+ /* setup TX Wireless Information */
+ txwi = (struct mtw_txwi *)(txd + 1);
+ txwi->len = htole16(m->m_pkthdr.len - pad);
+ if (rt2860_rates[ridx].phy == IEEE80211_T_DS) {
+ mcs |= MTW_PHY_CCK;
+ if (ridx != MTW_RIDX_CCK1 &&
+ (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
+ mcs |= MTW_PHY_SHPRE;
+ } else if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM) {
+ mcs |= MTW_PHY_OFDM;
+ } else if (rt2860_rates[ridx].phy == IEEE80211_T_HT) {
+ /* XXX TODO: [adrian] set short preamble for MCS? */
+ mcs |= MTW_PHY_HT; /* Mixed, not greenfield */
+ }
+ txwi->phy = htole16(mcs);
+
+ /* check if RTS/CTS or CTS-to-self protection is required */
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+ ((m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) ||
+ ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ rt2860_rates[ridx].phy == IEEE80211_T_OFDM) ||
+ ((ic->ic_htprotmode == IEEE80211_PROT_RTSCTS) &&
+ rt2860_rates[ridx].phy == IEEE80211_T_HT)))
+ txwi->txop |= MTW_TX_TXOP_HT;
+ else
+ txwi->txop |= MTW_TX_TXOP_BACKOFF;
+
+}
+
+/* This function must be called locked */
+static int
+mtw_tx(struct mtw_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_frame *wh;
+
+
+ //const struct ieee80211_txparam *tp = ni->ni_txparms;
+ struct mtw_node *rn = MTW_NODE(ni);
+ struct mtw_tx_data *data;
+ struct mtw_txd *txd;
+ struct mtw_txwi *txwi;
+ uint16_t qos;
+ uint16_t dur;
+ uint16_t qid;
+ uint8_t type;
+ uint8_t tid;
+ uint16_t ridx;
+ uint8_t ctl_ridx;
+ uint16_t qflags;
+ uint8_t xflags = 0;
+
+ int hasqos;
+
+ MTW_LOCK_ASSERT(sc, MA_OWNED);
+
+ wh = mtod(m, struct ieee80211_frame *);
+ const struct ieee80211_txparam *tp = ni->ni_txparms;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+
+ qflags = htole16(MTW_TXD_DATA | MTW_TXD_80211 |
+ MTW_TXD_WLAN | MTW_TXD_QSEL_HCCA);
+
+ if ((hasqos = IEEE80211_QOS_HAS_SEQ(wh))) {
+ uint8_t *frm;
+ frm = ieee80211_getqos(wh);
+
+
+ //device_printf(sc->sc_dev,"JSS:frm:%d",*frm);
+ qos = le16toh(*(const uint16_t *)frm);
+ tid = ieee80211_gettid(wh);
+ qid = TID_TO_WME_AC(tid);
+ qflags |= MTW_TXD_QSEL_EDCA;
+ } else {
+ qos = 0;
+ tid = 0;
+ qid = WME_AC_BE;
+ }
+ if (type & IEEE80211_FC0_TYPE_MGT) {
+ qid = 0;
+ }
+
+ if (type != IEEE80211_FC0_TYPE_DATA)
+ qflags |= htole16(MTW_TXD_WIV);
+
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
+ type != IEEE80211_FC0_TYPE_DATA || m->m_flags & M_EAPOL) {
+ /* XXX TODO: methodize for 11n; use MCS0 for 11NA/11NG */
+ ridx = (ic->ic_curmode == IEEE80211_MODE_11A
+ || ic->ic_curmode == IEEE80211_MODE_11NA) ?
+ MTW_RIDX_OFDM6 : MTW_RIDX_CCK1;
+ if (type == IEEE80211_MODE_11NG) {
+ ridx = 12;
+ }
+ ctl_ridx = rt2860_rates[ridx].ctl_ridx;
+ } else {
+ if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ ridx = rn->fix_ridx;
+
+ } else {
+ ridx = rn->amrr_ridx;
+ ctl_ridx = rt2860_rates[ridx].ctl_ridx;
+ }
+ }
+
+ if (hasqos)
+ xflags = 0;
+ else
+ xflags = MTW_TX_NSEQ;
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+ (!hasqos ||
+ (qos & IEEE80211_QOS_ACKPOLICY) !=
+ IEEE80211_QOS_ACKPOLICY_NOACK)) {
+ xflags |= MTW_TX_ACK;
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+ dur = rt2860_rates[ctl_ridx].sp_ack_dur;
+ else
+ dur = rt2860_rates[ctl_ridx].lp_ack_dur;
+ USETW(wh->i_dur, dur);
+ }
+ /* reserve slots for mgmt packets, just in case */
+ if (sc->sc_epq[qid].tx_nfree < 3) {
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "tx ring %d is full\n", qid);
+ return (-1);
+ }
+
+ data = STAILQ_FIRST(&sc->sc_epq[qid].tx_fh);
+ STAILQ_REMOVE_HEAD(&sc->sc_epq[qid].tx_fh, next);
+ sc->sc_epq[qid].tx_nfree--;
+
+ txd = (struct mtw_txd *)&data->desc;
+ txd->flags = qflags;
+
+ txwi = (struct mtw_txwi *)(txd + 1);
+ txwi->xflags = xflags;
+ txwi->wcid = (type == IEEE80211_FC0_TYPE_DATA) ?
+
+ MTW_AID2WCID(ni->ni_associd) :
+ 0xff;
+
+ /* clear leftover garbage bits */
+ txwi->flags = 0;
+ txwi->txop = 0;
+
+ data->m = m;
+ data->ni = ni;
+ data->ridx = ridx;
+
+ mtw_set_tx_desc(sc, data);
+
+ /*
+ * The chip keeps track of 2 kind of Tx stats,
+ * * TX_STAT_FIFO, for per WCID stats, and
+ * * TX_STA_CNT0 for all-TX-in-one stats.
+ *
+ * To use FIFO stats, we need to store MCS into the driver-private
+ * PacketID field. So that, we can tell whose stats when we read them.
+ * We add 1 to the MCS because setting the PacketID field to 0 means
+ * that we don't want feedback in TX_STAT_FIFO.
+ * And, that's what we want for STA mode, since TX_STA_CNT0 does the
+ * job.
+ *
+ * FIFO stats doesn't count Tx with WCID 0xff, so we do this in
+ * run_tx().
+ */
+
+ if (sc->rvp_cnt > 1 || vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_MBSS) {
+
+ /*
+ * Unlike PCI based devices, we don't get any interrupt from
+ * USB devices, so we simulate FIFO-is-full interrupt here.
+ * Ralink recommends to drain FIFO stats every 100 ms, but 16
+ * slots quickly get fulled. To prevent overflow, increment a
+ * counter on every FIFO stat request, so we know how many slots
+ * are left. We do this only in HOSTAP or multiple vap mode
+ * since FIFO stats are used only in those modes. We just drain
+ * stats. AMRR gets updated every 1 sec by run_ratectl_cb() via
+ * callout. Call it early. Otherwise overflow.
+ */
+ if (sc->fifo_cnt++ == 10) {
+ /*
+ * With multiple vaps or if_bridge, if_start() is called
+ * with a non-sleepable lock, tcpinp. So, need to defer.
+ */
+ uint32_t i = MTW_CMDQ_GET(&sc->cmdq_store);
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "cmdq_store=%d\n", i);
+ sc->cmdq[i].func = mtw_drain_fifo;
+ sc->cmdq[i].arg0 = sc;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+ }
+ }
+
+ STAILQ_INSERT_TAIL(&sc->sc_epq[qid].tx_qh, data, next);
+ usbd_transfer_start(sc->sc_xfer[mtw_wme_ac_xfer_map[qid]]);
+
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT,
+ "sending data frame len=%d rate=%d qid=%d\n",
+ m->m_pkthdr.len +
+ (int)(sizeof(struct mtw_txd) + sizeof(struct mtw_txwi)),
+ rt2860_rates[ridx].rate, qid);
+
+ return (0);
+ }
+
+static int
+mtw_tx_mgt(struct mtw_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct mtw_node *rn = MTW_NODE(ni);
+ struct mtw_tx_data *data;
+ struct ieee80211_frame *wh;
+ struct mtw_txd *txd;
+ struct mtw_txwi *txwi;
+ uint8_t type;
+ uint16_t dur;
+ uint8_t ridx = rn->mgt_ridx;
+ uint8_t xflags = 0;
+ uint8_t wflags = 0;
+
+ MTW_LOCK_ASSERT(sc, MA_OWNED);
+
+ wh = mtod(m, struct ieee80211_frame *);
+
+ /* tell hardware to add timestamp for probe responses */
+ if ((wh->i_fc[0] &
+ (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
+ (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP))
+ wflags |= MTW_TX_TS;
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ xflags |= MTW_TX_ACK;
+
+ dur = ieee80211_ack_duration(ic->ic_rt, rt2860_rates[ridx].rate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
+ USETW(wh->i_dur, dur);
+ }
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ if (sc->sc_epq[0].tx_nfree == 0)
+ /* let caller free mbuf */
+ return (EIO);
+ data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh);
+ STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next);
+ sc->sc_epq[0].tx_nfree--;
+
+ txd = (struct mtw_txd *)&data->desc;
+ txd->flags = htole16(
+ MTW_TXD_DATA | MTW_TXD_80211 | MTW_TXD_WLAN | MTW_TXD_QSEL_EDCA);
+ if (type != IEEE80211_FC0_TYPE_DATA)
+ txd->flags |= htole16(MTW_TXD_WIV);
+
+ txwi = (struct mtw_txwi *)(txd + 1);
+ txwi->wcid = 0xff;
+ txwi->xflags = xflags;
+ txwi->flags = wflags;
+
+ txwi->txop = 0; /* clear leftover garbage bits */
+
+ data->m = m;
+ data->ni = ni;
+ data->ridx = ridx;
+
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "sending mgt frame len=%d rate=%d\n",
+ m->m_pkthdr.len +
+ (int)(sizeof(struct mtw_txd) + sizeof(struct mtw_txwi)),
+ rt2860_rates[ridx].rate);
+
+ STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next);
+
+ usbd_transfer_start(sc->sc_xfer[MTW_BULK_TX_BE]);
+
+ return (0);
+}
+
+static int
+mtw_sendprot(struct mtw_softc *sc, const struct mbuf *m,
+ struct ieee80211_node *ni, int prot, int rate)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct mtw_tx_data *data;
+ struct mtw_txd *txd;
+ struct mtw_txwi *txwi;
+ struct mbuf *mprot;
+ int ridx;
+ int protrate;
+ uint8_t wflags = 0;
+ uint8_t xflags = 0;
+
+ MTW_LOCK_ASSERT(sc, MA_OWNED);
+
+ /* check that there are free slots before allocating the mbuf */
+ if (sc->sc_epq[0].tx_nfree == 0)
+ /* let caller free mbuf */
+ return (ENOBUFS);
+
+ mprot = ieee80211_alloc_prot(ni, m, rate, prot);
+ if (mprot == NULL) {
+ if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1);
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "could not allocate mbuf\n");
+ return (ENOBUFS);
+ }
+
+ protrate = ieee80211_ctl_rate(ic->ic_rt, rate);
+ wflags = MTW_TX_FRAG;
+ xflags = 0;
+ if (prot == IEEE80211_PROT_RTSCTS)
+ xflags |= MTW_TX_ACK;
+
+ data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh);
+ STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next);
+ sc->sc_epq[0].tx_nfree--;
+
+ txd = (struct mtw_txd *)&data->desc;
+ txd->flags = RT2860_TX_QSEL_EDCA;
+ txwi = (struct mtw_txwi *)(txd + 1);
+ txwi->wcid = 0xff;
+ txwi->flags = wflags;
+ txwi->xflags = xflags;
+ txwi->txop = 0; /* clear leftover garbage bits */
+
+ data->m = mprot;
+ data->ni = ieee80211_ref_node(ni);
+
+ /* XXX TODO: methodize with MCS rates */
+ for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++)
+ if (rt2860_rates[ridx].rate == protrate)
+ break;
+ data->ridx = ridx;
+
+ mtw_set_tx_desc(sc, data);
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "sending prot len=%u rate=%u\n",
+ m->m_pkthdr.len, rate);
+
+ STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next);
+
+ usbd_transfer_start(sc->sc_xfer[0]);
+
+ return (0);
+}
+
+static int
+mtw_tx_param(struct mtw_softc *sc, struct mbuf *m, struct ieee80211_node *ni,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct mtw_tx_data *data;
+ struct mtw_txd *txd;
+ struct mtw_txwi *txwi;
+ uint8_t ridx;
+ uint8_t rate;
+ uint8_t opflags = 0;
+ uint8_t xflags = 0;
+ int error;
+
+ MTW_LOCK_ASSERT(sc, MA_OWNED);
+
+ KASSERT(params != NULL, ("no raw xmit params"));
+
+ rate = params->ibp_rate0;
+ if (!ieee80211_isratevalid(ic->ic_rt, rate)) {
+ /* let caller free mbuf */
+ return (EINVAL);
+ }
+
+ if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
+ xflags |= MTW_TX_ACK;
+ if (params->ibp_flags & (IEEE80211_BPF_RTS | IEEE80211_BPF_CTS)) {
+ error = mtw_sendprot(sc, m, ni,
+ params->ibp_flags & IEEE80211_BPF_RTS ?
+ IEEE80211_PROT_RTSCTS :
+ IEEE80211_PROT_CTSONLY,
+ rate);
+ if (error) {
+ device_printf(sc->sc_dev, "%s:%d %d\n", __FILE__,
+ __LINE__, error);
+ return (error);
+ }
+ opflags |= MTW_TX_TXOP_SIFS;
+ }
+
+ if (sc->sc_epq[0].tx_nfree == 0) {
+ /* let caller free mbuf */
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT,
+ "sending raw frame, but tx ring is full\n");
+ return (EIO);
+ }
+ data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh);
+ STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next);
+ sc->sc_epq[0].tx_nfree--;
+
+ txd = (struct mtw_txd *)&data->desc;
+ txd->flags = htole16(
+ MTW_TXD_DATA | MTW_TXD_80211 | MTW_TXD_WLAN | MTW_TXD_QSEL_EDCA);
+ // txd->flags = htole16(MTW_TXD_QSEL_EDCA);
+ txwi = (struct mtw_txwi *)(txd + 1);
+ txwi->wcid = 0xff;
+ txwi->xflags = xflags;
+ txwi->txop = opflags;
+ txwi->flags = 0; /* clear leftover garbage bits */
+
+ data->m = m;
+ data->ni = ni;
+ /* XXX TODO: methodize with MCS rates */
+ for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++)
+ if (rt2860_rates[ridx].rate == rate)
+ break;
+ data->ridx = ridx;
+
+ mtw_set_tx_desc(sc, data);
+
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "sending raw frame len=%u rate=%u\n",
+ m->m_pkthdr.len, rate);
+
+ STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next);
+
+ usbd_transfer_start(sc->sc_xfer[MTW_BULK_RAW_TX]);
+
+ return (0);
+}
+
+static int
+mtw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct mtw_softc *sc = ni->ni_ic->ic_softc;
+ int error = 0;
+ MTW_LOCK(sc);
+ /* prevent management frames from being sent if we're not ready */
+ if (!(sc->sc_flags & MTW_RUNNING)) {
+ error = ENETDOWN;
+ goto done;
+ }
+
+ if (params == NULL) {
+ /* tx mgt packet */
+ if ((error = mtw_tx_mgt(sc, m, ni)) != 0) {
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "mgt tx failed\n");
+ goto done;
+ }
+ } else {
+ /* tx raw packet with param */
+ if ((error = mtw_tx_param(sc, m, ni, params)) != 0) {
+ MTW_DPRINTF(sc, MTW_DEBUG_XMIT,
+ "tx with param failed\n");
+ goto done;
+ }
+ }
+
+done:
+
+ MTW_UNLOCK(sc);
+
+ if (error != 0) {
+ if (m != NULL)
+ m_freem(m);
+ }
+
+ return (error);
+}
+
+static int
+mtw_transmit(struct ieee80211com *ic, struct mbuf *m)
+{
+ struct mtw_softc *sc = ic->ic_softc;
+ int error;
+ MTW_LOCK(sc);
+ if ((sc->sc_flags & MTW_RUNNING) == 0) {
+ MTW_UNLOCK(sc);
+ return (ENXIO);
+ }
+ error = mbufq_enqueue(&sc->sc_snd, m);
+ if (error) {
+ MTW_UNLOCK(sc);
+ return (error);
+ }
+ mtw_start(sc);
+ MTW_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+mtw_start(struct mtw_softc *sc)
+{
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ MTW_LOCK_ASSERT(sc, MA_OWNED);
+
+ if ((sc->sc_flags & MTW_RUNNING) == 0) {
+
+ return;
+ }
+ while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) {
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ if (mtw_tx(sc, m, ni) != 0) {
+ mbufq_prepend(&sc->sc_snd, m);
+ break;
+ }
+ }
+}
+
+static void
+mtw_parent(struct ieee80211com *ic)
+{
+
+ struct mtw_softc *sc = ic->ic_softc;
+
+ MTW_LOCK(sc);
+ if (sc->sc_detached) {
+ MTW_UNLOCK(sc);
+ return;
+ }
+
+ if (!(sc->sc_flags & MTW_RUNNING) && ic->ic_nrunning > 0) {
+ mtw_init_locked(sc);
+ MTW_UNLOCK(sc);
+ ieee80211_start_all(ic);
+ return;
+ }
+ if (!(sc->sc_flags & MTW_RUNNING) && ic->ic_nrunning > 0) {
+ mtw_update_promisc_locked(sc);
+ MTW_UNLOCK(sc);
+ return;
+ }
+ if ((sc->sc_flags & MTW_RUNNING) && sc->rvp_cnt <= 1 &&
+ ic->ic_nrunning == 0) {
+ mtw_stop(sc);
+ MTW_UNLOCK(sc);
+ return;
+ }
+ return;
+}
+
+static void
+mt7601_set_agc(struct mtw_softc *sc, uint8_t agc)
+{
+ uint8_t bbp;
+
+ mtw_bbp_write(sc, 66, agc);
+ mtw_bbp_write(sc, 195, 0x87);
+ bbp = (agc & 0xf0) | 0x08;
+ mtw_bbp_write(sc, 196, bbp);
+}
+
+static int
+mtw_mcu_calibrate(struct mtw_softc *sc, int func, uint32_t val)
+{
+ struct mtw_mcu_cmd_8 cmd;
+
+ cmd.func = htole32(func);
+ cmd.val = htole32(val);
+ return (mtw_mcu_cmd(sc, 31, &cmd, sizeof(struct mtw_mcu_cmd_8)));
+}
+
+static int
+mtw_rf_write(struct mtw_softc *sc, uint8_t bank, uint8_t reg, uint8_t val)
+{
+ uint32_t tmp;
+ int error, ntries, shift;
+
+ for (ntries = 0; ntries < 10; ntries++) {
+ if ((error = mtw_read(sc, MTW_RF_CSR, &tmp)) != 0)
+ return (error);
+ if (!(tmp & MTW_RF_CSR_KICK))
+ break;
+ }
+ if (ntries == 10)
+ return (ETIMEDOUT);
+
+ if (sc->asic_ver == 0x7601)
+ shift = MT7601_BANK_SHIFT;
+ else
+ shift = MT7610_BANK_SHIFT;
+
+ tmp = MTW_RF_CSR_WRITE | MTW_RF_CSR_KICK | (bank & 0xf) << shift |
+ reg << 8 | val;
+ return (mtw_write(sc, MTW_RF_CSR, tmp));
+}
+
+void
+mtw_select_chan_group(struct mtw_softc *sc, int group)
+{
+ uint32_t tmp;
+ uint8_t bbp;
+
+ /* Tx band 20MHz 2G */
+ mtw_read(sc, MTW_TX_BAND_CFG, &tmp);
+ tmp &= ~(
+ MTW_TX_BAND_SEL_2G | MTW_TX_BAND_SEL_5G | MTW_TX_BAND_UPPER_40M);
+ tmp |= (group == 0) ? MTW_TX_BAND_SEL_2G : MTW_TX_BAND_SEL_5G;
+ mtw_write(sc, MTW_TX_BAND_CFG, tmp);
+
+ /* select 20 MHz bandwidth */
+ mtw_bbp_read(sc, 4, &bbp);
+ bbp &= ~0x18;
+ bbp |= 0x40;
+ mtw_bbp_write(sc, 4, bbp);
+
+ /* calibrate BBP */
+ mtw_bbp_write(sc, 69, 0x12);
+ mtw_bbp_write(sc, 91, 0x07);
+ mtw_bbp_write(sc, 195, 0x23);
+ mtw_bbp_write(sc, 196, 0x17);
+ mtw_bbp_write(sc, 195, 0x24);
+ mtw_bbp_write(sc, 196, 0x06);
+ mtw_bbp_write(sc, 195, 0x81);
+ mtw_bbp_write(sc, 196, 0x12);
+ mtw_bbp_write(sc, 195, 0x83);
+ mtw_bbp_write(sc, 196, 0x17);
+ mtw_rf_write(sc, 5, 8, 0x00);
+ // mtw_mcu_calibrate(sc, 0x6, 0x10001);
+
+ /* set initial AGC value */
+ mt7601_set_agc(sc, 0x14);
+}
+
+static int
+mtw_rf_read(struct mtw_softc *sc, uint8_t bank, uint8_t reg, uint8_t *val)
+{
+ uint32_t tmp;
+ int error, ntries, shift;
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if ((error = mtw_read(sc, MTW_RF_CSR, &tmp)) != 0)
+ return (error);
+ if (!(tmp & MTW_RF_CSR_KICK))
+ break;
+ }
+ if (ntries == 100)
+ return (ETIMEDOUT);
+
+ if (sc->asic_ver == 0x7601)
+ shift = MT7601_BANK_SHIFT;
+ else
+ shift = MT7610_BANK_SHIFT;
+
+ tmp = MTW_RF_CSR_KICK | (bank & 0xf) << shift | reg << 8;
+ if ((error = mtw_write(sc, MTW_RF_CSR, tmp)) != 0)
+ return (error);
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if ((error = mtw_read(sc, MTW_RF_CSR, &tmp)) != 0)
+ return (error);
+ if (!(tmp & MTW_RF_CSR_KICK))
+ break;
+ }
+ if (ntries == 100)
+ return (ETIMEDOUT);
+
+ *val = tmp & 0xff;
+ return (0);
+}
+static void
+mt7601_set_chan(struct mtw_softc *sc, u_int chan)
+{
+ uint32_t tmp;
+ uint8_t bbp, rf, txpow1;
+ int i;
+ /* find the settings for this channel */
+ for (i = 0; mt7601_rf_chan[i].chan != chan; i++)
+ ;
+
+ mtw_rf_write(sc, 0, 17, mt7601_rf_chan[i].r17);
+ mtw_rf_write(sc, 0, 18, mt7601_rf_chan[i].r18);
+ mtw_rf_write(sc, 0, 19, mt7601_rf_chan[i].r19);
+ mtw_rf_write(sc, 0, 20, mt7601_rf_chan[i].r20);
+
+ /* use Tx power values from EEPROM */
+ txpow1 = sc->txpow1[i];
+
+ /* Tx automatic level control */
+ mtw_read(sc, MTW_TX_ALC_CFG0, &tmp);
+ tmp &= ~0x3f3f;
+ tmp |= (txpow1 & 0x3f);
+ mtw_write(sc, MTW_TX_ALC_CFG0, tmp);
+
+ /* LNA */
+ mtw_bbp_write(sc, 62, 0x37 - sc->lna[0]);
+ mtw_bbp_write(sc, 63, 0x37 - sc->lna[0]);
+ mtw_bbp_write(sc, 64, 0x37 - sc->lna[0]);
+
+ /* VCO calibration */
+ mtw_rf_write(sc, 0, 4, 0x0a);
+ mtw_rf_write(sc, 0, 5, 0x20);
+ mtw_rf_read(sc, 0, 4, &rf);
+ mtw_rf_write(sc, 0, 4, rf | 0x80);
+
+ /* select 20 MHz bandwidth */
+ mtw_bbp_read(sc, 4, &bbp);
+ bbp &= ~0x18;
+ bbp |= 0x40;
+ mtw_bbp_write(sc, 4, bbp);
+ mtw_bbp_write(sc, 178, 0xff);
+}
+
+static int
+mtw_set_chan(struct mtw_softc *sc, struct ieee80211_channel *c)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ u_int chan, group;
+
+ chan = ieee80211_chan2ieee(ic, c);
+ if (chan == 0 || chan == IEEE80211_CHAN_ANY)
+ return (EINVAL);
+
+ /* determine channel group */
+ if (chan <= 14)
+ group = 0;
+ else if (chan <= 64)
+ group = 1;
+ else if (chan <= 128)
+ group = 2;
+ else
+ group = 3;
+
+ if (group != sc->sc_chan_group || !sc->sc_bw_calibrated)
+ mtw_select_chan_group(sc, group);
+
+ sc->sc_chan_group = group;
+
+ /* chipset specific */
+ if (sc->asic_ver == 0x7601)
+ mt7601_set_chan(sc, chan);
+
+ DELAY(1000);
+ return (0);
+}
+
+static void
+mtw_set_channel(struct ieee80211com *ic)
+{
+ struct mtw_softc *sc = ic->ic_softc;
+
+ MTW_LOCK(sc);
+ mtw_set_chan(sc, ic->ic_curchan);
+ MTW_UNLOCK(sc);
+
+ return;
+}
+
+static void
+mtw_getradiocaps(struct ieee80211com *ic, int maxchans, int *nchans,
+ struct ieee80211_channel chans[])
+{
+ // struct mtw_softc *sc = ic->ic_softc;
+ uint8_t bands[IEEE80211_MODE_BYTES];
+
+ memset(bands, 0, sizeof(bands));
+ setbit(bands, IEEE80211_MODE_11B);
+ setbit(bands, IEEE80211_MODE_11G);
+ setbit(bands, IEEE80211_MODE_11NG);
+
+ /* Note: for now, only support HT20 channels */
+ ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, bands, 0);
+}
+
+static void
+mtw_scan_start(struct ieee80211com *ic)
+{
+ struct mtw_softc *sc = ic->ic_softc;
+ MTW_LOCK(sc);
+ /* abort TSF synchronization */
+ mtw_abort_tsf_sync(sc);
+ mtw_set_bssid(sc, ieee80211broadcastaddr);
+
+ MTW_UNLOCK(sc);
+
+ return;
+}
+
+static void
+mtw_scan_end(struct ieee80211com *ic)
+{
+ struct mtw_softc *sc = ic->ic_softc;
+
+ MTW_LOCK(sc);
+
+ mtw_enable_tsf_sync(sc);
+ mtw_set_bssid(sc, sc->sc_bssid);
+
+ MTW_UNLOCK(sc);
+
+ return;
+}
+
+/*
+ * Could be called from ieee80211_node_timeout()
+ * (non-sleepable thread)
+ */
+static void
+mtw_update_beacon(struct ieee80211vap *vap, int item)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off;
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct mtw_softc *sc = ic->ic_softc;
+ struct mtw_vap *rvp = MTW_VAP(vap);
+ int mcast = 0;
+ uint32_t i;
+
+ switch (item) {
+ case IEEE80211_BEACON_ERP:
+ mtw_updateslot(ic);
+ break;
+ case IEEE80211_BEACON_HTINFO:
+ mtw_updateprot(ic);
+ break;
+ case IEEE80211_BEACON_TIM:
+ mcast = 1; /*TODO*/
+ break;
+ default:
+ break;
+ }
+
+ setbit(bo->bo_flags, item);
+ if (rvp->beacon_mbuf == NULL) {
+ rvp->beacon_mbuf = ieee80211_beacon_alloc(ni);
+ if (rvp->beacon_mbuf == NULL)
+ return;
+ }
+ ieee80211_beacon_update(ni, rvp->beacon_mbuf, mcast);
+
+ i = MTW_CMDQ_GET(&sc->cmdq_store);
+ MTW_DPRINTF(sc, MTW_DEBUG_BEACON, "cmdq_store=%d\n", i);
+ sc->cmdq[i].func = mtw_update_beacon_cb;
+ sc->cmdq[i].arg0 = vap;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+
+ return;
+}
+
+static void
+mtw_update_beacon_cb(void *arg)
+{
+
+ struct ieee80211vap *vap = arg;
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct mtw_vap *rvp = MTW_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct mtw_softc *sc = ic->ic_softc;
+ struct mtw_txwi txwi;
+ struct mbuf *m;
+ uint16_t txwisize;
+ uint8_t ridx;
+ if (ni->ni_chan == IEEE80211_CHAN_ANYC)
+ return;
+ if (ic->ic_bsschan == IEEE80211_CHAN_ANYC)
+ return;
+
+ /*
+ * No need to call ieee80211_beacon_update(), mtw_update_beacon()
+ * is taking care of appropriate calls.
+ */
+ if (rvp->beacon_mbuf == NULL) {
+ rvp->beacon_mbuf = ieee80211_beacon_alloc(ni);
+ if (rvp->beacon_mbuf == NULL)
+ return;
+ }
+ m = rvp->beacon_mbuf;
+
+ memset(&txwi, 0, sizeof(txwi));
+ txwi.wcid = 0xff;
+ txwi.len = htole16(m->m_pkthdr.len);
+
+ /* send beacons at the lowest available rate */
+ ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? MTW_RIDX_OFDM6 :
+ MTW_RIDX_CCK1;
+ txwi.phy = htole16(rt2860_rates[ridx].mcs);
+ if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM)
+ txwi.phy |= htole16(MTW_PHY_OFDM);
+ txwi.txop = MTW_TX_TXOP_HT;
+ txwi.flags = MTW_TX_TS;
+ txwi.xflags = MTW_TX_NSEQ;
+
+ txwisize = sizeof(txwi);
+ mtw_write_region_1(sc, MTW_BCN_BASE, (uint8_t *)&txwi, txwisize);
+ mtw_write_region_1(sc, MTW_BCN_BASE + txwisize, mtod(m, uint8_t *),
+ (m->m_pkthdr.len + 1) & ~1);
+}
+
+static void
+mtw_updateprot(struct ieee80211com *ic)
+{
+ struct mtw_softc *sc = ic->ic_softc;
+ uint32_t i;
+
+ i = MTW_CMDQ_GET(&sc->cmdq_store);
+ MTW_DPRINTF(sc, MTW_DEBUG_BEACON, "test cmdq_store=%d\n", i);
+ sc->cmdq[i].func = mtw_updateprot_cb;
+ sc->cmdq[i].arg0 = ic;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+}
+
+static void
+mtw_updateprot_cb(void *arg)
+{
+
+ struct ieee80211com *ic = arg;
+ struct mtw_softc *sc = ic->ic_softc;
+ uint32_t tmp;
+
+ tmp = RT2860_RTSTH_EN | RT2860_PROT_NAV_SHORT | RT2860_TXOP_ALLOW_ALL;
+ /* setup protection frame rate (MCS code) */
+ tmp |= (ic->ic_curmode == IEEE80211_MODE_11A) ?
+ rt2860_rates[MTW_RIDX_OFDM6].mcs | MTW_PHY_OFDM :
+ rt2860_rates[MTW_RIDX_CCK11].mcs;
+
+ /* CCK frames don't require protection */
+ mtw_write(sc, MTW_CCK_PROT_CFG, tmp);
+ if (ic->ic_flags & IEEE80211_F_USEPROT) {
+ if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+ tmp |= RT2860_PROT_CTRL_RTS_CTS;
+ else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
+ tmp |= RT2860_PROT_CTRL_CTS;
+ }
+ mtw_write(sc, MTW_OFDM_PROT_CFG, tmp);
+}
+
+static void
+mtw_usb_timeout_cb(void *arg)
+{
+ struct ieee80211vap *vap = arg;
+ struct mtw_softc *sc = vap->iv_ic->ic_softc;
+
+ MTW_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (vap->iv_state == IEEE80211_S_SCAN) {
+ MTW_DPRINTF(sc, MTW_DEBUG_USB | MTW_DEBUG_STATE,
+ "timeout caused by scan\n");
+ /* cancel bgscan */
+ ieee80211_cancel_scan(vap);
+ } else {
+ MTW_DPRINTF(sc, MTW_DEBUG_USB | MTW_DEBUG_STATE,
+ "timeout by unknown cause\n");
+ }
+}
+static int mtw_reset(struct mtw_softc *sc)
+{
+
+ usb_device_request_t req;
+ uint16_t tmp;
+ uint16_t actlen;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = MTW_RESET;
+ USETW(req.wValue, 1);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx,
+ &req, &tmp, 0, &actlen, 1000));
+
+}
+
+
+static void
+mtw_update_promisc_locked(struct mtw_softc *sc)
+{
+
+ uint32_t tmp;
+
+ mtw_read(sc, MTW_RX_FILTR_CFG, &tmp);
+
+ tmp |= MTW_DROP_UC_NOME;
+ if (sc->sc_ic.ic_promisc > 0)
+ tmp &= ~MTW_DROP_UC_NOME;
+
+ mtw_write(sc, MTW_RX_FILTR_CFG, tmp);
+
+ MTW_DPRINTF(sc, MTW_DEBUG_RECV, "%s promiscuous mode\n",
+ (sc->sc_ic.ic_promisc > 0) ? "entering" : "leaving");
+}
+
+static void
+mtw_update_promisc(struct ieee80211com *ic)
+{
+ struct mtw_softc *sc = ic->ic_softc;
+
+ if ((sc->sc_flags & MTW_RUNNING) == 0)
+ return;
+
+ MTW_LOCK(sc);
+ mtw_update_promisc_locked(sc);
+ MTW_UNLOCK(sc);
+}
+
+static void
+mtw_enable_tsf_sync(struct mtw_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ uint32_t tmp;
+ int error;
+ mtw_read(sc, MTW_BCN_TIME_CFG, &tmp);
+ tmp &= ~0x1fffff;
+ tmp |= vap->iv_bss->ni_intval * 16;
+ tmp |= MTW_TSF_TIMER_EN | MTW_TBTT_TIMER_EN;
+
+ /* local TSF is always updated with remote TSF on beacon reception */
+ tmp |= 1 << MTW_TSF_SYNC_MODE_SHIFT;
+ error = mtw_write(sc, MTW_BCN_TIME_CFG, tmp);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "enable_tsf_sync failed error:%d\n",
+ error);
+ }
+ return;
+}
+
+static void
+mtw_enable_mrr(struct mtw_softc *sc)
+{
+#define CCK(mcs) (mcs)
+
+#define OFDM(mcs) (1 << 3 | (mcs))
+ mtw_write(sc, MTW_LG_FBK_CFG0,
+ OFDM(6) << 28 | /* 54->48 */
+ OFDM(5) << 24 | /* 48->36 */
+ OFDM(4) << 20 | /* 36->24 */
+ OFDM(3) << 16 | /* 24->18 */
+ OFDM(2) << 12 | /* 18->12 */
+ OFDM(1) << 8 | /* 12-> 9 */
+ OFDM(0) << 4 | /* 9-> 6 */
+ OFDM(0)); /* 6-> 6 */
+
+ mtw_write(sc, MTW_LG_FBK_CFG1,
+ CCK(2) << 12 | /* 11->5.5 */
+ CCK(1) << 8 | /* 5.5-> 2 */
+ CCK(0) << 4 | /* 2-> 1 */
+ CCK(0)); /* 1-> 1 */
+#undef OFDM
+#undef CCK
+}
+
+static void
+mtw_set_txpreamble(struct mtw_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint32_t tmp;
+
+ mtw_read(sc, MTW_AUTO_RSP_CFG, &tmp);
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+ tmp |= MTW_CCK_SHORT_EN;
+ else
+ tmp &= ~MTW_CCK_SHORT_EN;
+ mtw_write(sc, MTW_AUTO_RSP_CFG, tmp);
+}
+
+static void
+mtw_set_basicrates(struct mtw_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ /* set basic rates mask */
+ if (ic->ic_curmode == IEEE80211_MODE_11B)
+ mtw_write(sc, MTW_LEGACY_BASIC_RATE, 0x003);
+ else if (ic->ic_curmode == IEEE80211_MODE_11A)
+ mtw_write(sc, MTW_LEGACY_BASIC_RATE, 0x150);
+ else /* 11g */
+ mtw_write(sc, MTW_LEGACY_BASIC_RATE, 0x17f);
+}
+
+static void
+mtw_set_bssid(struct mtw_softc *sc, const uint8_t *bssid)
+{
+ mtw_write(sc, MTW_MAC_BSSID_DW0,
+ bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24);
+ mtw_write(sc, MTW_MAC_BSSID_DW1, bssid[4] | bssid[5] << 8);
+}
+
+static void
+mtw_set_macaddr(struct mtw_softc *sc, const uint8_t *addr)
+{
+ mtw_write(sc, MTW_MAC_ADDR_DW0,
+ addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24);
+ mtw_write(sc, MTW_MAC_ADDR_DW1, addr[4] | addr[5] << 8 | 0xff << 16);
+}
+
+static void
+mtw_updateslot(struct ieee80211com *ic)
+{
+
+ struct mtw_softc *sc = ic->ic_softc;
+ uint32_t i;
+
+ i = MTW_CMDQ_GET(&sc->cmdq_store);
+ MTW_DPRINTF(sc, MTW_DEBUG_BEACON, "cmdq_store=%d\n", i);
+ sc->cmdq[i].func = mtw_updateslot_cb;
+ sc->cmdq[i].arg0 = ic;
+ ieee80211_runtask(ic, &sc->cmdq_task);
+
+ return;
+}
+
+/* ARGSUSED */
+static void
+mtw_updateslot_cb(void *arg)
+{
+ struct ieee80211com *ic = arg;
+ struct mtw_softc *sc = ic->ic_softc;
+ uint32_t tmp;
+ mtw_read(sc, MTW_BKOFF_SLOT_CFG, &tmp);
+ tmp &= ~0xff;
+ tmp |= IEEE80211_GET_SLOTTIME(ic);
+ mtw_write(sc, MTW_BKOFF_SLOT_CFG, tmp);
+}
+
+static void
+mtw_update_mcast(struct ieee80211com *ic)
+{
+}
+
+static int8_t
+mtw_rssi2dbm(struct mtw_softc *sc, uint8_t rssi, uint8_t rxchain)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_channel *c = ic->ic_curchan;
+ int delta;
+
+ if (IEEE80211_IS_CHAN_5GHZ(c)) {
+ u_int chan = ieee80211_chan2ieee(ic, c);
+ delta = sc->rssi_5ghz[rxchain];
+
+ /* determine channel group */
+ if (chan <= 64)
+ delta -= sc->lna[1];
+ else if (chan <= 128)
+ delta -= sc->lna[2];
+ else
+ delta -= sc->lna[3];
+ } else
+ delta = sc->rssi_2ghz[rxchain] - sc->lna[0];
+
+ return (-12 - delta - rssi);
+}
+static int
+mt7601_bbp_init(struct mtw_softc *sc)
+{
+ uint8_t bbp;
+ int i, error, ntries;
+
+ /* wait for BBP to wake up */
+ for (ntries = 0; ntries < 20; ntries++) {
+ if ((error = mtw_bbp_read(sc, 0, &bbp)) != 0)
+ return (error);
+ if (bbp != 0 && bbp != 0xff)
+ break;
+ }
+
+ if (ntries == 20)
+ return (ETIMEDOUT);
+
+ mtw_bbp_read(sc, 3, &bbp);
+ mtw_bbp_write(sc, 3, 0);
+ mtw_bbp_read(sc, 105, &bbp);
+ mtw_bbp_write(sc, 105, 0);
+
+ /* initialize BBP registers to default values */
+ for (i = 0; i < nitems(mt7601_def_bbp); i++) {
+ if ((error = mtw_bbp_write(sc, mt7601_def_bbp[i].reg,
+ mt7601_def_bbp[i].val)) != 0)
+ return (error);
+ }
+
+ sc->sc_bw_calibrated = 0;
+
+ return (0);
+}
+
+static int
+mt7601_rf_init(struct mtw_softc *sc)
+{
+ int i, error;
+
+ /* RF bank 0 */
+ for (i = 0; i < nitems(mt7601_rf_bank0); i++) {
+ error = mtw_rf_write(sc, 0, mt7601_rf_bank0[i].reg,
+ mt7601_rf_bank0[i].val);
+ if (error != 0)
+ return (error);
+ }
+ /* RF bank 4 */
+ for (i = 0; i < nitems(mt7601_rf_bank4); i++) {
+ error = mtw_rf_write(sc, 4, mt7601_rf_bank4[i].reg,
+ mt7601_rf_bank4[i].val);
+ if (error != 0)
+ return (error);
+ }
+ /* RF bank 5 */
+ for (i = 0; i < nitems(mt7601_rf_bank5); i++) {
+ error = mtw_rf_write(sc, 5, mt7601_rf_bank5[i].reg,
+ mt7601_rf_bank5[i].val);
+ if (error != 0)
+ return (error);
+ }
+ return (0);
+}
+
+static int
+mtw_txrx_enable(struct mtw_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint32_t tmp;
+ int error, ntries;
+ mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_MAC_TX_EN);
+ for (ntries = 0; ntries < 200; ntries++) {
+ if ((error = mtw_read(sc, MTW_WPDMA_GLO_CFG, &tmp)) != 0) {
+ return (error);
+ }
+ if ((tmp & (MTW_TX_DMA_BUSY | MTW_RX_DMA_BUSY)) == 0)
+ break;
+ mtw_delay(sc, 50);
+ }
+ if (ntries == 200) {
+ return (ETIMEDOUT);
+ }
+
+ DELAY(50);
+
+ tmp |= MTW_RX_DMA_EN | MTW_TX_DMA_EN | MTW_TX_WB_DDONE;
+ mtw_write(sc, MTW_WPDMA_GLO_CFG, tmp);
+
+ /* enable Rx bulk aggregation (set timeout and limit) */
+ tmp = MTW_USB_TX_EN | MTW_USB_RX_EN | MTW_USB_RX_AGG_EN |
+ MTW_USB_RX_AGG_TO(128) | MTW_USB_RX_AGG_LMT(2);
+ mtw_write(sc, MTW_USB_DMA_CFG, tmp);
+
+ /* set Rx filter */
+ tmp = MTW_DROP_CRC_ERR | MTW_DROP_PHY_ERR;
+ if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ tmp |= MTW_DROP_UC_NOME | MTW_DROP_DUPL | MTW_DROP_CTS |
+ MTW_DROP_BA | MTW_DROP_ACK | MTW_DROP_VER_ERR |
+ MTW_DROP_CTRL_RSV | MTW_DROP_CFACK | MTW_DROP_CFEND;
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ tmp |= MTW_DROP_RTS | MTW_DROP_PSPOLL;
+ }
+ mtw_write(sc, MTW_RX_FILTR_CFG, tmp);
+
+ mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_MAC_RX_EN | MTW_MAC_TX_EN);
+ return (0);
+}
+static int
+mt7601_rxdc_cal(struct mtw_softc *sc)
+{
+ uint32_t tmp;
+ uint8_t bbp;
+ int ntries;
+
+ mtw_read(sc, MTW_MAC_SYS_CTRL, &tmp);
+ mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_MAC_RX_EN);
+ mtw_bbp_write(sc, 158, 0x8d);
+ mtw_bbp_write(sc, 159, 0xfc);
+ mtw_bbp_write(sc, 158, 0x8c);
+ mtw_bbp_write(sc, 159, 0x4c);
+
+ for (ntries = 0; ntries < 20; ntries++) {
+ DELAY(300);
+ mtw_bbp_write(sc, 158, 0x8c);
+ mtw_bbp_read(sc, 159, &bbp);
+ if (bbp == 0x0c)
+ break;
+ }
+
+ if (ntries == 20)
+ return (ETIMEDOUT);
+
+ mtw_write(sc, MTW_MAC_SYS_CTRL, 0);
+ mtw_bbp_write(sc, 158, 0x8d);
+ mtw_bbp_write(sc, 159, 0xe0);
+ mtw_write(sc, MTW_MAC_SYS_CTRL, tmp);
+ return (0);
+}
+
+static int
+mt7601_r49_read(struct mtw_softc *sc, uint8_t flag, int8_t *val)
+{
+ uint8_t bbp;
+
+ mtw_bbp_read(sc, 47, &bbp);
+ bbp = 0x90;
+ mtw_bbp_write(sc, 47, bbp);
+ bbp &= ~0x0f;
+ bbp |= flag;
+ mtw_bbp_write(sc, 47, bbp);
+ return (mtw_bbp_read(sc, 49, val));
+}
+
+static int
+mt7601_rf_temperature(struct mtw_softc *sc, int8_t *val)
+{
+ uint32_t rfb, rfs;
+ uint8_t bbp;
+ int ntries;
+
+ mtw_read(sc, MTW_RF_BYPASS0, &rfb);
+ mtw_read(sc, MTW_RF_SETTING0, &rfs);
+ mtw_write(sc, MTW_RF_BYPASS0, 0);
+ mtw_write(sc, MTW_RF_SETTING0, 0x10);
+ mtw_write(sc, MTW_RF_BYPASS0, 0x10);
+
+ mtw_bbp_read(sc, 47, &bbp);
+ bbp &= ~0x7f;
+ bbp |= 0x10;
+ mtw_bbp_write(sc, 47, bbp);
+
+ mtw_bbp_write(sc, 22, 0x40);
+
+ for (ntries = 0; ntries < 10; ntries++) {
+ mtw_bbp_read(sc, 47, &bbp);
+ if ((bbp & 0x10) == 0)
+ break;
+ }
+ if (ntries == 10)
+ return (ETIMEDOUT);
+
+ mt7601_r49_read(sc, MT7601_R47_TEMP, val);
+
+ mtw_bbp_write(sc, 22, 0);
+
+ mtw_bbp_read(sc, 21, &bbp);
+ bbp |= 0x02;
+ mtw_bbp_write(sc, 21, bbp);
+ bbp &= ~0x02;
+ mtw_bbp_write(sc, 21, bbp);
+
+ mtw_write(sc, MTW_RF_BYPASS0, 0);
+ mtw_write(sc, MTW_RF_SETTING0, rfs);
+ mtw_write(sc, MTW_RF_BYPASS0, rfb);
+ return (0);
+}
+
+static int
+mt7601_rf_setup(struct mtw_softc *sc)
+{
+ uint32_t tmp;
+ uint8_t rf;
+ int error;
+
+ if (sc->sc_rf_calibrated)
+ return (0);
+
+ /* init RF registers */
+ if ((error = mt7601_rf_init(sc)) != 0)
+ return (error);
+
+ /* init frequency offset */
+ mtw_rf_write(sc, 0, 12, sc->rf_freq_offset);
+ mtw_rf_read(sc, 0, 12, &rf);
+
+ /* read temperature */
+ mt7601_rf_temperature(sc, &rf);
+ sc->bbp_temp = rf;
+ device_printf(sc->sc_dev, "BBP temp 0x%x\n", rf);
+
+ mtw_rf_read(sc, 0, 7, &rf);
+ if ((error = mtw_mcu_calibrate(sc, 0x1, 0)) != 0)
+ return (error);
+ mtw_delay(sc, 100);
+ mtw_rf_read(sc, 0, 7, &rf);
+
+ /* Calibrate VCO RF 0/4 */
+ mtw_rf_write(sc, 0, 4, 0x0a);
+ mtw_rf_write(sc, 0, 4, 0x20);
+ mtw_rf_read(sc, 0, 4, &rf);
+ mtw_rf_write(sc, 0, 4, rf | 0x80);
+
+ if ((error = mtw_mcu_calibrate(sc, 0x9, 0)) != 0)
+ return (error);
+ if ((error = mt7601_rxdc_cal(sc)) != 0)
+ return (error);
+ if ((error = mtw_mcu_calibrate(sc, 0x6, 1)) != 0)
+ return (error);
+ if ((error = mtw_mcu_calibrate(sc, 0x6, 0)) != 0)
+ return (error);
+ if ((error = mtw_mcu_calibrate(sc, 0x4, 0)) != 0)
+ return (error);
+ if ((error = mtw_mcu_calibrate(sc, 0x5, 0)) != 0)
+ return (error);
+
+ mtw_read(sc, MTW_LDO_CFG0, &tmp);
+ tmp &= ~(1 << 4);
+ tmp |= (1 << 2);
+ mtw_write(sc, MTW_LDO_CFG0, tmp);
+
+ if ((error = mtw_mcu_calibrate(sc, 0x8, 0)) != 0)
+ return (error);
+ if ((error = mt7601_rxdc_cal(sc)) != 0)
+ return (error);
+
+ sc->sc_rf_calibrated = 1;
+ return (0);
+}
+
+static void
+mtw_set_txrts(struct mtw_softc *sc)
+{
+ uint32_t tmp;
+
+ /* set RTS threshold */
+ mtw_read(sc, MTW_TX_RTS_CFG, &tmp);
+ tmp &= ~0xffff00;
+ tmp |= 0x1000 << MTW_RTS_THRES_SHIFT;
+ mtw_write(sc, MTW_TX_RTS_CFG, tmp);
+}
+static int
+mtw_mcu_radio(struct mtw_softc *sc, int func, uint32_t val)
+{
+ struct mtw_mcu_cmd_16 cmd;
+
+ cmd.r1 = htole32(func);
+ cmd.r2 = htole32(val);
+ cmd.r3 = 0;
+ cmd.r4 = 0;
+ return (mtw_mcu_cmd(sc, 8, &val, sizeof(struct mtw_mcu_cmd_16)));
+}
+static void
+mtw_init_locked(struct mtw_softc *sc)
+{
+
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ uint32_t tmp;
+ int i, error, ridx, ntries;
+ if (ic->ic_nrunning > 1)
+ return;
+ mtw_stop(sc);
+
+ for (i = 0; i != MTW_EP_QUEUES; i++)
+ mtw_setup_tx_list(sc, &sc->sc_epq[i]);
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if ((error = mtw_read(sc, MTW_WPDMA_GLO_CFG, &tmp)) != 0)
+ goto fail;
+ if ((tmp & (MTW_TX_DMA_BUSY | MTW_RX_DMA_BUSY)) == 0)
+ break;
+ DELAY(1000);
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "timeout waiting for DMA engine\n");
+ error = ETIMEDOUT;
+ goto fail;
+ }
+ tmp &= 0xff0;
+ tmp |= MTW_TX_WB_DDONE;
+ mtw_write(sc, MTW_WPDMA_GLO_CFG, tmp);
+
+ mtw_set_leds(sc, MTW_LED_MODE_ON);
+ /* reset MAC and baseband */
+ mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_BBP_HRST | MTW_MAC_SRST);
+ mtw_write(sc, MTW_USB_DMA_CFG, 0);
+ mtw_write(sc, MTW_MAC_SYS_CTRL, 0);
+
+ /* init MAC values */
+ if (sc->asic_ver == 0x7601) {
+ for (i = 0; i < nitems(mt7601_def_mac); i++)
+ mtw_write(sc, mt7601_def_mac[i].reg,
+ mt7601_def_mac[i].val);
+ }
+
+ /* wait while MAC is busy */
+ for (ntries = 0; ntries < 100; ntries++) {
+ if ((error = mtw_read(sc, MTW_MAC_STATUS_REG, &tmp)) != 0)
+ goto fail;
+ if (!(tmp & (MTW_RX_STATUS_BUSY | MTW_TX_STATUS_BUSY)))
+ break;
+ DELAY(1000);
+ }
+ if (ntries == 100) {
+ error = ETIMEDOUT;
+ goto fail;
+ }
+
+ /* set MAC address */
+
+ mtw_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr);
+
+ /* clear WCID attribute table */
+ mtw_set_region_4(sc, MTW_WCID_ATTR(0), 1, 8 * 32);
+
+ mtw_write(sc, 0x1648, 0x00830083);
+ mtw_read(sc, MTW_FCE_L2_STUFF, &tmp);
+ tmp &= ~MTW_L2S_WR_MPDU_LEN_EN;
+ mtw_write(sc, MTW_FCE_L2_STUFF, tmp);
+
+ /* RTS config */
+ mtw_set_txrts(sc);
+
+ /* clear Host to MCU mailbox */
+ mtw_write(sc, MTW_BBP_CSR, 0);
+ mtw_write(sc, MTW_H2M_MAILBOX, 0);
+
+ /* clear RX WCID search table */
+ mtw_set_region_4(sc, MTW_WCID_ENTRY(0), 0xffffffff, 512);
+
+ /* abort TSF synchronization */
+ mtw_abort_tsf_sync(sc);
+
+ mtw_read(sc, MTW_US_CYC_CNT, &tmp);
+ tmp = (tmp & ~0xff);
+ if (sc->asic_ver == 0x7601)
+ tmp |= 0x1e;
+ mtw_write(sc, MTW_US_CYC_CNT, tmp);
+
+ /* clear shared key table */
+ mtw_set_region_4(sc, MTW_SKEY(0, 0), 0, 8 * 32);
+
+ /* clear IV/EIV table */
+ mtw_set_region_4(sc, MTW_IVEIV(0), 0, 8 * 32);
+
+ /* clear shared key mode */
+ mtw_write(sc, MTW_SKEY_MODE_0_7, 0);
+ mtw_write(sc, MTW_SKEY_MODE_8_15, 0);
+
+ /* txop truncation */
+ mtw_write(sc, MTW_TXOP_CTRL_CFG, 0x0000583f);
+
+ /* init Tx power for all Tx rates */
+ for (ridx = 0; ridx < 5; ridx++) {
+ if (sc->txpow20mhz[ridx] == 0xffffffff)
+ continue;
+ mtw_write(sc, MTW_TX_PWR_CFG(ridx), sc->txpow20mhz[ridx]);
+ }
+ mtw_write(sc, MTW_TX_PWR_CFG7, 0);
+ mtw_write(sc, MTW_TX_PWR_CFG9, 0);
+
+ mtw_read(sc, MTW_CMB_CTRL, &tmp);
+ tmp &= ~(1 << 18 | 1 << 14);
+ mtw_write(sc, MTW_CMB_CTRL, tmp);
+
+ /* clear USB DMA */
+ mtw_write(sc, MTW_USB_DMA_CFG,
+ MTW_USB_TX_EN | MTW_USB_RX_EN | MTW_USB_RX_AGG_EN |
+ MTW_USB_TX_CLEAR | MTW_USB_TXOP_HALT | MTW_USB_RX_WL_DROP);
+ mtw_delay(sc, 50);
+ mtw_read(sc, MTW_USB_DMA_CFG, &tmp);
+ tmp &= ~(MTW_USB_TX_CLEAR | MTW_USB_TXOP_HALT | MTW_USB_RX_WL_DROP);
+ mtw_write(sc, MTW_USB_DMA_CFG, tmp);
+
+ /* enable radio */
+ mtw_mcu_radio(sc, 0x31, 0);
+
+ /* init RF registers */
+ if (sc->asic_ver == 0x7601)
+ mt7601_rf_init(sc);
+
+ /* init baseband registers */
+ if (sc->asic_ver == 0x7601)
+ error = mt7601_bbp_init(sc);
+
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not initialize BBP\n");
+ goto fail;
+ }
+
+ /* setup and calibrate RF */
+ error = mt7601_rf_setup(sc);
+
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not initialize RF\n");
+ goto fail;
+ }
+
+ /* select default channel */
+ mtw_set_chan(sc, ic->ic_curchan);
+
+ /* setup initial protection mode */
+ mtw_updateprot_cb(ic);
+
+ sc->sc_flags |= MTW_RUNNING;
+ sc->cmdq_run = MTW_CMDQ_GO;
+ for (i = 0; i != MTW_N_XFER; i++)
+ usbd_xfer_set_stall(sc->sc_xfer[i]);
+
+ usbd_transfer_start(sc->sc_xfer[MTW_BULK_RX]);
+
+ error = mtw_txrx_enable(sc);
+ if (error != 0) {
+ goto fail;
+ }
+
+ return;
+
+fail:
+
+ mtw_stop(sc);
+ return;
+}
+
+static void
+mtw_stop(void *arg)
+{
+ struct mtw_softc *sc = (struct mtw_softc *)arg;
+ uint32_t tmp;
+ int i, ntries, error;
+
+ MTW_LOCK_ASSERT(sc, MA_OWNED);
+
+ sc->sc_flags &= ~MTW_RUNNING;
+
+ sc->ratectl_run = MTW_RATECTL_OFF;
+ sc->cmdq_run = sc->cmdq_key_set;
+
+ MTW_UNLOCK(sc);
+
+ for (i = 0; i < MTW_N_XFER; i++)
+ usbd_transfer_drain(sc->sc_xfer[i]);
+
+ MTW_LOCK(sc);
+
+ mtw_drain_mbufq(sc);
+
+ if (sc->rx_m != NULL) {
+ m_free(sc->rx_m);
+ sc->rx_m = NULL;
+ }
+
+ /* Disable Tx/Rx DMA. */
+ mtw_read(sc, MTW_WPDMA_GLO_CFG, &tmp);
+ tmp &= ~(MTW_RX_DMA_EN | MTW_TX_DMA_EN);
+ mtw_write(sc, MTW_WPDMA_GLO_CFG, tmp);
+ // mtw_usb_dma_write(sc, 0);
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (mtw_read(sc, MTW_WPDMA_GLO_CFG, &tmp) != 0)
+ break;
+ if ((tmp & (MTW_TX_DMA_BUSY | MTW_RX_DMA_BUSY)) == 0)
+ break;
+ DELAY(10);
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "timeout waiting for DMA engine\n");
+ }
+
+ /* stop MAC Tx/Rx */
+ mtw_read(sc, MTW_MAC_SYS_CTRL, &tmp);
+ tmp &= ~(MTW_MAC_RX_EN | MTW_MAC_TX_EN);
+ mtw_write(sc, MTW_MAC_SYS_CTRL, tmp);
+
+ /* disable RTS retry */
+ mtw_read(sc, MTW_TX_RTS_CFG, &tmp);
+ tmp &= ~0xff;
+ mtw_write(sc, MTW_TX_RTS_CFG, tmp);
+
+ /* US_CYC_CFG */
+ mtw_read(sc, MTW_US_CYC_CNT, &tmp);
+ tmp = (tmp & ~0xff);
+ mtw_write(sc, MTW_US_CYC_CNT, tmp);
+
+ /* stop PBF */
+ mtw_read(sc, MTW_PBF_CFG, &tmp);
+ tmp &= ~0x3;
+ mtw_write(sc, MTW_PBF_CFG, tmp);
+
+ /* wait for pending Tx to complete */
+ for (ntries = 0; ntries < 100; ntries++) {
+ if ((error = mtw_read(sc, MTW_TXRXQ_PCNT, &tmp)) != 0)
+ break;
+ if ((tmp & MTW_TX2Q_PCNT_MASK) == 0)
+ break;
+ }
+
+}
+
+static void
+mtw_delay(struct mtw_softc *sc, u_int ms)
+{
+ usb_pause_mtx(mtx_owned(&sc->sc_mtx) ? &sc->sc_mtx : NULL,
+ USB_MS_TO_TICKS(ms));
+}
+
+static void
+mtw_update_chw(struct ieee80211com *ic)
+{
+
+ printf("%s: TODO\n", __func__);
+}
+
+static int
+mtw_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
+{
+
+ /* For now, no A-MPDU TX support in the driver */
+ return (0);
+}
+
+static device_method_t mtw_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mtw_match),
+ DEVMETHOD(device_attach, mtw_attach),
+ DEVMETHOD(device_detach, mtw_detach), DEVMETHOD_END
+};
+
+static driver_t mtw_driver = { .name = "mtw",
+ .methods = mtw_methods,
+ .size = sizeof(struct mtw_softc) };
+
+DRIVER_MODULE(mtw, uhub, mtw_driver, mtw_driver_loaded, NULL);
+MODULE_DEPEND(mtw, wlan, 1, 1, 1);
+MODULE_DEPEND(mtw, usb, 1, 1, 1);
+MODULE_DEPEND(mtw, firmware, 1, 1, 1);
+MODULE_VERSION(mtw, 1);
+USB_PNP_HOST_INFO(mtw_devs);
diff --git a/sys/dev/usb/wlan/if_mtwreg.h b/sys/dev/usb/wlan/if_mtwreg.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/usb/wlan/if_mtwreg.h
@@ -0,0 +1,1439 @@
+/* $OpenBSD: mtwreg.h,v 1.2 2022/07/27 06:41:04 hastings Exp $ */
+/*
+ * Copyright (c) 2007 Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2021 James Hastings
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define MTW_ASIC_VER 0x0000
+#define MTW_CMB_CTRL 0x0020
+#define MTW_EFUSE_CTRL 0x0024
+#define MTW_EFUSE_DATA0 0x0028
+#define MTW_EFUSE_DATA1 0x002c
+#define MTW_EFUSE_DATA2 0x0030
+#define MTW_EFUSE_DATA3 0x0034
+#define MTW_OSC_CTRL 0x0038
+#define MTW_COEX_CFG0 0x0040
+#define MTW_PLL_CTRL 0x0050
+#define MTW_LDO_CFG0 0x006c
+#define MTW_LDO_CFG1 0x0070
+#define MTW_WLAN_CTRL 0x0080
+
+/* SCH/DMA registers */
+#define MTW_INT_STATUS 0x0200
+#define RT2860_INT_MASK 0x0204
+#define MTW_WPDMA_GLO_CFG 0x0208
+#define RT2860_WPDMA_RST_IDX 0x020c
+#define RT2860_DELAY_INT_CFG 0x0210
+#define MTW_WMM_AIFSN_CFG 0x0214
+#define MTW_WMM_CWMIN_CFG 0x0218
+#define MTW_WMM_CWMAX_CFG 0x021c
+#define MTW_WMM_TXOP0_CFG 0x0220
+#define MTW_WMM_TXOP1_CFG 0x0224
+#define RT2860_GPIO_CTRL 0x0228
+#define RT2860_MCU_CMD_REG 0x022c
+#define MTW_MCU_DMA_ADDR 0x0230
+#define MTW_MCU_DMA_LEN 0x0234
+#define MTW_USB_DMA_CFG 0x0238
+#define RT2860_TX_BASE_PTR(qid) (0x0230 + (qid) * 16)
+#define RT2860_TX_MAX_CNT(qid) (0x0234 + (qid) * 16)
+#define RT2860_TX_CTX_IDX(qid) (0x0238 + (qid) * 16)
+#define RT2860_TX_DTX_IDX(qid) (0x023c + (qid) * 16)
+#define MTW_TSO_CTRL 0x0250
+#define MTW_HDR_TRANS_CTRL 0x0260
+#define RT2860_RX_BASE_PTR 0x0290
+#define RT2860_RX_MAX_CNT 0x0294
+#define RT2860_RX_CALC_IDX 0x0298
+#define RT2860_FS_DRX_IDX 0x029c
+#define MTW_US_CYC_CNT 0x02a4
+
+#define MTW_TX_RING_BASE 0x0300
+#define MTW_RX_RING_BASE 0x03c0
+
+/* Packet Buffer registers */
+#define MTW_SYS_CTRL 0x0400
+#define MTW_PBF_CFG 0x0404
+#define MTW_TX_MAX_PCNT 0x0408
+#define MTW_RX_MAX_PCNT 0x040c
+#define MTW_PBF_CTRL 0x0410
+#define RT2860_BUF_CTRL 0x0410
+#define RT2860_MCU_INT_STA 0x0414
+#define RT2860_MCU_INT_ENA 0x0418
+#define RT2860_TXQ_IO(qid) (0x041c + (qid) * 4)
+#define MTW_BCN_OFFSET0 0x041c
+#define MTW_BCN_OFFSET1 0x0420
+#define MTW_BCN_OFFSET2 0x0424
+#define MTW_BCN_OFFSET3 0x0428
+#define RT2860_RX0Q_IO 0x0424
+#define MTW_RXQ_STA 0x0430
+#define MTW_TXQ_STA 0x0434
+#define MTW_TXRXQ_PCNT 0x0438
+
+/* RF registers */
+#define MTW_RF_CSR 0x0500
+#define MTW_RF_BYPASS0 0x0504
+#define MTW_RF_BYPASS1 0x0508
+#define MTW_RF_SETTING0 0x050C
+#define MTW_RF_MISC 0x0518
+#define MTW_RF_DATA_WR 0x0524
+#define MTW_RF_CTRL 0x0528
+#define MTW_RF_DATA_RD 0x052c
+
+/* MCU registers */
+#define MTW_MCU_RESET_CTL 0x070c
+#define MTW_MCU_INT_LEVEL 0x0718
+#define MTW_MCU_COM_REG0 0x0730
+#define MTW_MCU_COM_REG1 0x0734
+#define MTW_MCU_COM_REG2 0x0738
+#define MTW_MCU_COM_REG3 0x073c
+#define MTW_FCE_PSE_CTRL 0x0800
+#define MTW_FCE_PARAMETERS 0x0804
+#define MTW_FCE_CSO 0x0808
+#define MTW_FCE_L2_STUFF 0x080c
+#define MTW_FCE_WLAN_FLOW_CTRL 0x0824
+#define MTW_TX_CPU_FCE_BASE 0x09a0
+#define MTW_TX_CPU_FCE_MAX_COUNT 0x09a4
+#define MTW_MCU_FW_IDX 0x09a8
+#define MTW_FCE_PDMA 0x09c4
+#define MTW_FCE_SKIP_FS 0x0a6c
+
+/* MAC registers */
+#define MTW_MAC_VER_ID 0x1000
+#define MTW_MAC_SYS_CTRL 0x1004
+#define MTW_MAC_ADDR_DW0 0x1008
+#define MTW_MAC_ADDR_DW1 0x100c
+#define MTW_MAC_BSSID_DW0 0x1010
+#define MTW_MAC_BSSID_DW1 0x1014
+#define MTW_MAX_LEN_CFG 0x1018
+#define MTW_BBP_CSR 0x101c
+#define MTW_LED_CFG 0x102c
+#define MTW_AMPDU_MAX_LEN_20M1S 0x1030
+#define MTW_AMPDU_MAX_LEN_20M2S 0x1034
+#define MTW_AMPDU_MAX_LEN_40M1S 0x1038
+#define MTW_AMPDU_MAX_LEN_40M2S 0x103c
+#define MTW_AMPDU_MAX_LEN 0x1040
+
+/* MAC Timing control registers */
+#define MTW_XIFS_TIME_CFG 0x1100
+#define MTW_BKOFF_SLOT_CFG 0x1104
+#define RT2860_NAV_TIME_CFG 0x1108
+#define RT2860_CH_TIME_CFG 0x110c
+#define RT2860_PBF_LIFE_TIMER 0x1110
+#define MTW_BCN_TIME_CFG 0x1114
+#define MTW_TBTT_SYNC_CFG 0x1118
+#define MTW_TSF_TIMER_DW0 0x111c
+#define MTW_TSF_TIMER_DW1 0x1120
+#define RT2860_TBTT_TIMER 0x1124
+#define MTW_INT_TIMER_CFG 0x1128
+#define RT2860_INT_TIMER_EN 0x112c
+#define RT2860_CH_IDLE_TIME 0x1130
+
+/* MAC Power Save configuration registers */
+#define MTW_MAC_STATUS_REG 0x1200
+#define MTW_PWR_PIN_CFG 0x1204
+#define MTW_AUTO_WAKEUP_CFG 0x1208
+#define MTW_AUX_CLK_CFG 0x120c
+#define MTW_BBP_PA_MODE_CFG0 0x1214
+#define MTW_BBP_PA_MODE_CFG1 0x1218
+#define MTW_RF_PA_MODE_CFG0 0x121c
+#define MTW_RF_PA_MODE_CFG1 0x1220
+#define MTW_RF_PA_MODE_ADJ0 0x1228
+#define MTW_RF_PA_MODE_ADJ1 0x122c
+#define MTW_DACCLK_EN_DLY_CFG 0x1264 /* MT7612 */
+
+/* MAC TX configuration registers */
+#define MTW_EDCA_AC_CFG(aci) (0x1300 + (aci) * 4)
+#define MTW_EDCA_TID_AC_MAP 0x1310
+#define MTW_TX_PWR_CFG(ridx) (0x1314 + (ridx) * 4)
+#define MTW_TX_PIN_CFG 0x1328
+#define MTW_TX_BAND_CFG 0x132c
+#define MTW_TX_SW_CFG0 0x1330
+#define MTW_TX_SW_CFG1 0x1334
+#define MTW_TX_SW_CFG2 0x1338
+#define RT2860_TXOP_THRES_CFG 0x133c
+#define MTW_TXOP_CTRL_CFG 0x1340
+#define MTW_TX_RTS_CFG 0x1344
+#define MTW_TX_TIMEOUT_CFG 0x1348
+#define MTW_TX_RETRY_CFG 0x134c
+#define MTW_TX_LINK_CFG 0x1350
+#define MTW_HT_FBK_CFG0 0x1354
+#define MTW_HT_FBK_CFG1 0x1358
+#define MTW_LG_FBK_CFG0 0x135c
+#define MTW_LG_FBK_CFG1 0x1360
+#define MTW_CCK_PROT_CFG 0x1364
+#define MTW_OFDM_PROT_CFG 0x1368
+#define MTW_MM20_PROT_CFG 0x136c
+#define MTW_MM40_PROT_CFG 0x1370
+#define MTW_GF20_PROT_CFG 0x1374
+#define MTW_GF40_PROT_CFG 0x1378
+#define RT2860_EXP_CTS_TIME 0x137c
+#define MTW_EXP_ACK_TIME 0x1380
+#define MTW_TX_PWR_CFG5 0x1384
+#define MTW_TX_PWR_CFG6 0x1388
+#define MTW_TX_PWR_EXT_CFG(ridx) (0x1390 + (ridx) * 4)
+#define MTW_TX0_RF_GAIN_CORR 0x13a0
+#define MTW_TX1_RF_GAIN_CORR 0x13a4
+#define MTW_TX0_RF_GAIN_ATTEN 0x13a8
+#define MTW_TX_ALC_CFG3 0x13ac
+#define MTW_TX_ALC_CFG0 0x13b0
+#define MTW_TX_ALC_CFG1 0x13b4
+#define MTW_TX_ALC_CFG4 0x13c0
+#define MTW_TX_ALC_VGA3 0x13c8
+#define MTW_TX_PWR_CFG7 0x13d4
+#define MTW_TX_PWR_CFG8 0x13d8
+#define MTW_TX_PWR_CFG9 0x13dc
+#define MTW_VHT20_PROT_CFG 0x13e0
+#define MTW_VHT40_PROT_CFG 0x13e4
+#define MTW_VHT80_PROT_CFG 0x13e8
+#define MTW_TX_PIFS_CFG 0x13ec /* MT761X */
+
+/* MAC RX configuration registers */
+#define MTW_RX_FILTR_CFG 0x1400
+#define MTW_AUTO_RSP_CFG 0x1404
+#define MTW_LEGACY_BASIC_RATE 0x1408
+#define MTW_HT_BASIC_RATE 0x140c
+#define MTW_HT_CTRL_CFG 0x1410
+#define RT2860_SIFS_COST_CFG 0x1414
+#define RT2860_RX_PARSER_CFG 0x1418
+
+/* MAC Security configuration registers */
+#define RT2860_TX_SEC_CNT0 0x1500
+#define RT2860_RX_SEC_CNT0 0x1504
+#define RT2860_CCMP_FC_MUTE 0x1508
+#define MTW_PN_PAD_MODE 0x150c /* MT761X */
+
+/* MAC HCCA/PSMP configuration registers */
+#define MTW_TXOP_HLDR_ADDR0 0x1600
+#define MTW_TXOP_HLDR_ADDR1 0x1604
+#define MTW_TXOP_HLDR_ET 0x1608
+#define RT2860_QOS_CFPOLL_RA_DW0 0x160c
+#define RT2860_QOS_CFPOLL_A1_DW1 0x1610
+#define RT2860_QOS_CFPOLL_QC 0x1614
+#define MTW_PROT_AUTO_TX_CFG 0x1648
+
+/* MAC Statistics Counters */
+#define MTW_RX_STA_CNT0 0x1700
+#define MTW_RX_STA_CNT1 0x1704
+#define MTW_RX_STA_CNT2 0x1708
+#define MTW_TX_STA_CNT0 0x170c
+#define MTW_TX_STA_CNT1 0x1710
+#define MTW_TX_STA_CNT2 0x1714
+#define MTW_TX_STAT_FIFO 0x1718
+
+/* RX WCID search table */
+#define MTW_WCID_ENTRY(wcid) (0x1800 + (wcid) * 8)
+
+/* MT761x Baseband */
+#define MTW_BBP_CORE(x) (0x2000 + (x) * 4)
+#define MTW_BBP_IBI(x) (0x2100 + (x) * 4)
+#define MTW_BBP_AGC(x) (0x2300 + (x) * 4)
+#define MTW_BBP_TXC(x) (0x2400 + (x) * 4)
+#define MTW_BBP_RXC(x) (0x2500 + (x) * 4)
+#define MTW_BBP_TXQ(x) (0x2600 + (x) * 4)
+#define MTW_BBP_TXBE(x) (0x2700 + (x) * 4)
+#define MTW_BBP_RXFE(x) (0x2800 + (x) * 4)
+#define MTW_BBP_RXO(x) (0x2900 + (x) * 4)
+#define MTW_BBP_DFS(x) (0x2a00 + (x) * 4)
+#define MTW_BBP_TR(x) (0x2b00 + (x) * 4)
+#define MTW_BBP_CAL(x) (0x2c00 + (x) * 4)
+#define MTW_BBP_DSC(x) (0x2e00 + (x) * 4)
+#define MTW_BBP_PFMU(x) (0x2f00 + (x) * 4)
+
+#define MTW_SKEY_MODE_16_23 0x7008
+#define MTW_SKEY_MODE_24_31 0x700c
+#define MTW_H2M_MAILBOX 0x7010
+
+/* Pair-wise key table */
+#define MTW_PKEY(wcid) (0x8000 + (wcid) * 32)
+
+/* USB 3.0 DMA */
+#define MTW_USB_U3DMA_CFG 0x9018
+
+/* IV/EIV table */
+#define MTW_IVEIV(wcid) (0xa000 + (wcid) * 8)
+
+/* WCID attribute table */
+#define MTW_WCID_ATTR(wcid) (0xa800 + (wcid) * 4)
+
+/* Shared Key Table */
+#define MTW_SKEY(vap, kidx) ((vap & 8) ? MTW_SKEY_1(vap, kidx) : \
+ MTW_SKEY_0(vap, kidx))
+#define MTW_SKEY_0(vap, kidx) (0xac00 + (4 * (vap) + (kidx)) * 32)
+#define MTW_SKEY_1(vap, kidx) (0xb400 + (4 * ((vap) & 7) + (kidx)) * 32)
+
+/* Shared Key Mode */
+#define MTW_SKEY_MODE_0_7 0xb000
+#define MTW_SKEY_MODE_8_15 0xb004
+
+/* Shared Key Mode */
+#define MTW_SKEY_MODE_BASE 0xb000
+
+/* Beacon */
+#define MTW_BCN_BASE 0xc000
+
+/* possible flags for register CMB_CTRL 0x0020 */
+#define MTW_PLL_LD (1U << 23)
+#define MTW_XTAL_RDY (1U << 22)
+
+/* possible flags for register EFUSE_CTRL 0x0024 */
+#define MTW_SEL_EFUSE (1U << 31)
+#define MTW_EFSROM_KICK (1U << 30)
+#define MTW_EFSROM_AIN_MASK 0x03ff0000
+#define MTW_EFSROM_AIN_SHIFT 16
+#define MTW_EFSROM_MODE_MASK 0x000000c0
+#define MTW_EFUSE_AOUT_MASK 0x0000003f
+
+/* possible flags for register OSC_CTRL 0x0038 */
+#define MTW_OSC_EN (1U << 31)
+#define MTW_OSC_CAL_REQ (1U << 30)
+#define MTW_OSC_CLK_32K_VLD (1U << 29)
+#define MTW_OSC_CAL_ACK (1U << 28)
+#define MTW_OSC_CAL_CNT (0xfff << 16)
+#define MTW_OSC_REF_CYCLE 0x1fff
+
+/* possible flags for register WLAN_CTRL 0x0080 */
+#define MTW_GPIO_OUT_OE_ALL (0xff << 24)
+#define MTW_GPIO_OUT_ALL (0xff << 16)
+#define MTW_GPIO_IN_ALL (0xff << 8)
+#define MTW_THERM_CKEN (1U << 9)
+#define MTW_THERM_RST (1U << 8)
+#define MTW_INV_TR_SW0 (1U << 6)
+#define MTW_FRC_WL_ANT_SET (1U << 5)
+#define MTW_PCIE_APP0_CLK_REQ (1U << 4)
+#define MTW_WLAN_RESET (1U << 3)
+#define MTW_WLAN_RESET_RF (1U << 2)
+#define MTW_WLAN_CLK_EN (1U << 1)
+#define MTW_WLAN_EN (1U << 0)
+
+/* possible flags for registers INT_STATUS/INT_MASK 0x0200 */
+#define RT2860_TX_COHERENT (1 << 17)
+#define RT2860_RX_COHERENT (1 << 16)
+#define RT2860_MAC_INT_4 (1 << 15)
+#define RT2860_MAC_INT_3 (1 << 14)
+#define RT2860_MAC_INT_2 (1 << 13)
+#define RT2860_MAC_INT_1 (1 << 12)
+#define RT2860_MAC_INT_0 (1 << 11)
+#define RT2860_TX_RX_COHERENT (1 << 10)
+#define RT2860_MCU_CMD_INT (1 << 9)
+#define RT2860_TX_DONE_INT5 (1 << 8)
+#define RT2860_TX_DONE_INT4 (1 << 7)
+#define RT2860_TX_DONE_INT3 (1 << 6)
+#define RT2860_TX_DONE_INT2 (1 << 5)
+#define RT2860_TX_DONE_INT1 (1 << 4)
+#define RT2860_TX_DONE_INT0 (1 << 3)
+#define RT2860_RX_DONE_INT (1 << 2)
+#define RT2860_TX_DLY_INT (1 << 1)
+#define RT2860_RX_DLY_INT (1 << 0)
+
+/* possible flags for register WPDMA_GLO_CFG 0x0208 */
+#define MTW_HDR_SEG_LEN_SHIFT 8
+#define MTW_BIG_ENDIAN (1 << 7)
+#define MTW_TX_WB_DDONE (1 << 6)
+#define MTW_WPDMA_BT_SIZE_SHIFT 4
+#define MTW_WPDMA_BT_SIZE16 0
+#define MTW_WPDMA_BT_SIZE32 1
+#define MTW_WPDMA_BT_SIZE64 2
+#define MTW_WPDMA_BT_SIZE128 3
+#define MTW_RX_DMA_BUSY (1 << 3)
+#define MTW_RX_DMA_EN (1 << 2)
+#define MTW_TX_DMA_BUSY (1 << 1)
+#define MTW_TX_DMA_EN (1 << 0)
+
+/* possible flags for register DELAY_INT_CFG */
+#define RT2860_TXDLY_INT_EN (1U << 31)
+#define RT2860_TXMAX_PINT_SHIFT 24
+#define RT2860_TXMAX_PTIME_SHIFT 16
+#define RT2860_RXDLY_INT_EN (1U << 15)
+#define RT2860_RXMAX_PINT_SHIFT 8
+#define RT2860_RXMAX_PTIME_SHIFT 0
+
+/* possible flags for register GPIO_CTRL */
+#define RT2860_GPIO_D_SHIFT 8
+#define RT2860_GPIO_O_SHIFT 0
+
+/* possible flags for register MCU_DMA_ADDR 0x0230 */
+#define MTW_MCU_READY (1U << 0)
+
+/* possible flags for register USB_DMA_CFG 0x0238 */
+#define MTW_USB_TX_BUSY (1U << 31)
+#define MTW_USB_RX_BUSY (1U << 30)
+#define MTW_USB_EPOUT_VLD_SHIFT 24
+#define MTW_USB_RX_WL_DROP (1U << 25)
+#define MTW_USB_TX_EN (1U << 23)
+#define MTW_USB_RX_EN (1U << 22)
+#define MTW_USB_RX_AGG_EN (1U << 21)
+#define MTW_USB_TXOP_HALT (1U << 20)
+#define MTW_USB_TX_CLEAR (1U << 19)
+#define MTW_USB_PHY_WD_EN (1U << 16)
+#define MTW_USB_PHY_MAN_RST (1U << 15)
+#define MTW_USB_RX_AGG_LMT(x) ((x) << 8) /* in unit of 1KB */
+#define MTW_USB_RX_AGG_TO(x) ((x) & 0xff) /* in unit of 33ns */
+
+/* possible flags for register US_CYC_CNT 0x02a4 */
+#define RT2860_TEST_EN (1 << 24)
+#define RT2860_TEST_SEL_SHIFT 16
+#define RT2860_BT_MODE_EN (1 << 8)
+#define RT2860_US_CYC_CNT_SHIFT 0
+
+/* possible flags for register PBF_CFG 0x0404 */
+#define MTW_PBF_CFG_RX_DROP (1 << 8)
+#define MTW_PBF_CFG_RX0Q_EN (1 << 4)
+#define MTW_PBF_CFG_TX3Q_EN (1 << 3)
+#define MTW_PBF_CFG_TX2Q_EN (1 << 2)
+#define MTW_PBF_CFG_TX1Q_EN (1 << 1)
+#define MTW_PBF_CFG_TX0Q_EN (1 << 0)
+
+/* possible flags for register BUF_CTRL 0x0410 */
+#define RT2860_WRITE_TXQ(qid) (1 << (11 - (qid)))
+#define RT2860_NULL0_KICK (1 << 7)
+#define RT2860_NULL1_KICK (1 << 6)
+#define RT2860_BUF_RESET (1 << 5)
+#define RT2860_READ_TXQ(qid) (1 << (3 - (qid))
+#define RT2860_READ_RX0Q (1 << 0)
+
+/* possible flags for registers MCU_INT_STA/MCU_INT_ENA */
+#define RT2860_MCU_MAC_INT_8 (1 << 24)
+#define RT2860_MCU_MAC_INT_7 (1 << 23)
+#define RT2860_MCU_MAC_INT_6 (1 << 22)
+#define RT2860_MCU_MAC_INT_4 (1 << 20)
+#define RT2860_MCU_MAC_INT_3 (1 << 19)
+#define RT2860_MCU_MAC_INT_2 (1 << 18)
+#define RT2860_MCU_MAC_INT_1 (1 << 17)
+#define RT2860_MCU_MAC_INT_0 (1 << 16)
+#define RT2860_DTX0_INT (1 << 11)
+#define RT2860_DTX1_INT (1 << 10)
+#define RT2860_DTX2_INT (1 << 9)
+#define RT2860_DRX0_INT (1 << 8)
+#define RT2860_HCMD_INT (1 << 7)
+#define RT2860_N0TX_INT (1 << 6)
+#define RT2860_N1TX_INT (1 << 5)
+#define RT2860_BCNTX_INT (1 << 4)
+#define RT2860_MTX0_INT (1 << 3)
+#define RT2860_MTX1_INT (1 << 2)
+#define RT2860_MTX2_INT (1 << 1)
+#define RT2860_MRX0_INT (1 << 0)
+
+/* possible flags for register TXRXQ_PCNT 0x0438 */
+#define MTW_RX0Q_PCNT_MASK 0xff000000
+#define MTW_TX2Q_PCNT_MASK 0x00ff0000
+#define MTW_TX1Q_PCNT_MASK 0x0000ff00
+#define MTW_TX0Q_PCNT_MASK 0x000000ff
+
+/* possible flags for register RF_CSR_CFG 0x0500 */
+#define MTW_RF_CSR_KICK (1U << 31)
+#define MTW_RF_CSR_WRITE (1U << 30)
+#define MT7610_BANK_SHIFT 15
+#define MT7601_BANK_SHIFT 14
+
+/* possible flags for register FCE_L2_STUFF 0x080c */
+#define MTW_L2S_WR_MPDU_LEN_EN (1 << 4)
+
+/* possible flag for register DEBUG_INDEX */
+#define RT5592_SEL_XTAL (1U << 31)
+
+/* possible flags for register MAC_SYS_CTRL 0x1004 */
+#define MTW_RX_TS_EN (1 << 7)
+#define MTW_WLAN_HALT_EN (1 << 6)
+#define MTW_PBF_LOOP_EN (1 << 5)
+#define MTW_CONT_TX_TEST (1 << 4)
+#define MTW_MAC_RX_EN (1 << 3)
+#define MTW_MAC_TX_EN (1 << 2)
+#define MTW_BBP_HRST (1 << 1)
+#define MTW_MAC_SRST (1 << 0)
+
+/* possible flags for register MAC_BSSID_DW1 0x100c */
+#define RT2860_MULTI_BCN_NUM_SHIFT 18
+#define RT2860_MULTI_BSSID_MODE_SHIFT 16
+
+/* possible flags for register MAX_LEN_CFG 0x1018 */
+#define RT2860_MIN_MPDU_LEN_SHIFT 16
+#define RT2860_MAX_PSDU_LEN_SHIFT 12
+#define RT2860_MAX_PSDU_LEN8K 0
+#define RT2860_MAX_PSDU_LEN16K 1
+#define RT2860_MAX_PSDU_LEN32K 2
+#define RT2860_MAX_PSDU_LEN64K 3
+#define RT2860_MAX_MPDU_LEN_SHIFT 0
+
+/* possible flags for registers BBP_CSR_CFG 0x101c */
+#define MTW_BBP_CSR_KICK (1 << 17)
+#define MTW_BBP_CSR_READ (1 << 16)
+#define MTW_BBP_ADDR_SHIFT 8
+#define MTW_BBP_DATA_SHIFT 0
+
+/* possible flags for register LED_CFG */
+#define MTW_LED_MODE_ON 0
+#define MTW_LED_MODE_DIM 1
+#define MTW_LED_MODE_BLINK_TX 2
+#define MTW_LED_MODE_SLOW_BLINK 3
+
+/* possible flags for register XIFS_TIME_CFG 0x1100 */
+#define MTW_BB_RXEND_EN (1 << 29)
+#define MTW_EIFS_TIME_SHIFT 20
+#define MTW_OFDM_XIFS_TIME_SHIFT 16
+#define MTW_OFDM_SIFS_TIME_SHIFT 8
+#define MTW_CCK_SIFS_TIME_SHIFT 0
+
+/* possible flags for register BKOFF_SLOT_CFG 0x1104 */
+#define MTW_CC_DELAY_TIME_SHIFT 8
+#define MTW_SLOT_TIME 0
+
+/* possible flags for register NAV_TIME_CFG */
+#define RT2860_NAV_UPD (1U << 31)
+#define RT2860_NAV_UPD_VAL_SHIFT 16
+#define RT2860_NAV_CLR_EN (1U << 15)
+#define RT2860_NAV_TIMER_SHIFT 0
+
+/* possible flags for register CH_TIME_CFG */
+#define RT2860_EIFS_AS_CH_BUSY (1 << 4)
+#define RT2860_NAV_AS_CH_BUSY (1 << 3)
+#define RT2860_RX_AS_CH_BUSY (1 << 2)
+#define RT2860_TX_AS_CH_BUSY (1 << 1)
+#define RT2860_CH_STA_TIMER_EN (1 << 0)
+
+/* possible values for register BCN_TIME_CFG 0x1114 */
+#define MTW_TSF_INS_COMP_SHIFT 24
+#define MTW_BCN_TX_EN (1 << 20)
+#define MTW_TBTT_TIMER_EN (1 << 19)
+#define MTW_TSF_SYNC_MODE_SHIFT 17
+#define MTW_TSF_SYNC_MODE_DIS 0
+#define MTW_TSF_SYNC_MODE_STA 1
+#define MTW_TSF_SYNC_MODE_IBSS 2
+#define MTW_TSF_SYNC_MODE_HOSTAP 3
+#define MTW_TSF_TIMER_EN (1 << 16)
+#define MTW_BCN_INTVAL_SHIFT 0
+
+/* possible flags for register TBTT_SYNC_CFG 0x1118 */
+#define RT2860_BCN_CWMIN_SHIFT 20
+#define RT2860_BCN_AIFSN_SHIFT 16
+#define RT2860_BCN_EXP_WIN_SHIFT 8
+#define RT2860_TBTT_ADJUST_SHIFT 0
+
+/* possible flags for register INT_TIMER_CFG 0x1128 */
+#define RT2860_GP_TIMER_SHIFT 16
+#define RT2860_PRE_TBTT_TIMER_SHIFT 0
+
+/* possible flags for register INT_TIMER_EN */
+#define RT2860_GP_TIMER_EN (1 << 1)
+#define RT2860_PRE_TBTT_INT_EN (1 << 0)
+
+/* possible flags for register MAC_STATUS_REG 0x1200 */
+#define MTW_RX_STATUS_BUSY (1 << 1)
+#define MTW_TX_STATUS_BUSY (1 << 0)
+
+/* possible flags for register PWR_PIN_CFG 0x1204 */
+#define RT2860_IO_ADDA_PD (1 << 3)
+#define RT2860_IO_PLL_PD (1 << 2)
+#define RT2860_IO_RA_PE (1 << 1)
+#define RT2860_IO_RF_PE (1 << 0)
+
+/* possible flags for register AUTO_WAKEUP_CFG 0x1208 */
+#define MTW_AUTO_WAKEUP_EN (1 << 15)
+#define MTW_SLEEP_TBTT_NUM_SHIFT 8
+#define MTW_WAKEUP_LEAD_TIME_SHIFT 0
+
+/* possible flags for register TX_PIN_CFG 0x1328 */
+#define RT2860_TRSW_POL (1U << 19)
+#define RT2860_TRSW_EN (1U << 18)
+#define RT2860_RFTR_POL (1U << 17)
+#define RT2860_RFTR_EN (1U << 16)
+#define RT2860_LNA_PE_G1_POL (1U << 15)
+#define RT2860_LNA_PE_A1_POL (1U << 14)
+#define RT2860_LNA_PE_G0_POL (1U << 13)
+#define RT2860_LNA_PE_A0_POL (1U << 12)
+#define RT2860_LNA_PE_G1_EN (1U << 11)
+#define RT2860_LNA_PE_A1_EN (1U << 10)
+#define RT2860_LNA_PE1_EN (RT2860_LNA_PE_A1_EN | RT2860_LNA_PE_G1_EN)
+#define RT2860_LNA_PE_G0_EN (1U << 9)
+#define RT2860_LNA_PE_A0_EN (1U << 8)
+#define RT2860_LNA_PE0_EN (RT2860_LNA_PE_A0_EN | RT2860_LNA_PE_G0_EN)
+#define RT2860_PA_PE_G1_POL (1U << 7)
+#define RT2860_PA_PE_A1_POL (1U << 6)
+#define RT2860_PA_PE_G0_POL (1U << 5)
+#define RT2860_PA_PE_A0_POL (1U << 4)
+#define RT2860_PA_PE_G1_EN (1U << 3)
+#define RT2860_PA_PE_A1_EN (1U << 2)
+#define RT2860_PA_PE_G0_EN (1U << 1)
+#define RT2860_PA_PE_A0_EN (1U << 0)
+
+/* possible flags for register TX_BAND_CFG 0x132c */
+#define MTW_TX_BAND_SEL_2G (1 << 2)
+#define MTW_TX_BAND_SEL_5G (1 << 1)
+#define MTW_TX_BAND_UPPER_40M (1 << 0)
+
+/* possible flags for register TX_SW_CFG0 0x1330 */
+#define RT2860_DLY_RFTR_EN_SHIFT 24
+#define RT2860_DLY_TRSW_EN_SHIFT 16
+#define RT2860_DLY_PAPE_EN_SHIFT 8
+#define RT2860_DLY_TXPE_EN_SHIFT 0
+
+/* possible flags for register TX_SW_CFG1 0x1334 */
+#define RT2860_DLY_RFTR_DIS_SHIFT 16
+#define RT2860_DLY_TRSW_DIS_SHIFT 8
+#define RT2860_DLY_PAPE_DIS SHIFT 0
+
+/* possible flags for register TX_SW_CFG2 0x1338 */
+#define RT2860_DLY_LNA_EN_SHIFT 24
+#define RT2860_DLY_LNA_DIS_SHIFT 16
+#define RT2860_DLY_DAC_EN_SHIFT 8
+#define RT2860_DLY_DAC_DIS_SHIFT 0
+
+/* possible flags for register TXOP_THRES_CFG 0x133c */
+#define RT2860_TXOP_REM_THRES_SHIFT 24
+#define RT2860_CF_END_THRES_SHIFT 16
+#define RT2860_RDG_IN_THRES 8
+#define RT2860_RDG_OUT_THRES 0
+
+/* possible flags for register TXOP_CTRL_CFG 0x1340 */
+#define MTW_TXOP_ED_CCA_EN (1 << 20)
+#define MTW_EXT_CW_MIN_SHIFT 16
+#define MTW_EXT_CCA_DLY_SHIFT 8
+#define MTW_EXT_CCA_EN (1 << 7)
+#define MTW_LSIG_TXOP_EN (1 << 6)
+#define MTW_TXOP_TRUN_EN_MIMOPS (1 << 4)
+#define MTW_TXOP_TRUN_EN_TXOP (1 << 3)
+#define MTW_TXOP_TRUN_EN_RATE (1 << 2)
+#define MTW_TXOP_TRUN_EN_AC (1 << 1)
+#define MTW_TXOP_TRUN_EN_TIMEOUT (1 << 0)
+
+/* possible flags for register TX_RTS_CFG 0x1344 */
+#define MTW_RTS_FBK_EN (1 << 24)
+#define MTW_RTS_THRES_SHIFT 8
+#define MTW_RTS_RTY_LIMIT_SHIFT 0
+
+/* possible flags for register TX_TIMEOUT_CFG 0x1348 */
+#define MTW_TXOP_TIMEOUT_SHIFT 16
+#define MTW_RX_ACK_TIMEOUT_SHIFT 8
+#define MTW_MPDU_LIFE_TIME_SHIFT 4
+
+/* possible flags for register TX_RETRY_CFG 0x134c */
+#define MTW_TX_AUTOFB_EN (1 << 30)
+#define MTW_AGG_RTY_MODE_TIMER (1 << 29)
+#define MTW_NAG_RTY_MODE_TIMER (1 << 28)
+#define MTW_LONG_RTY_THRES_SHIFT 16
+#define MTW_LONG_RTY_LIMIT_SHIFT 8
+#define MTW_SHORT_RTY_LIMIT_SHIFT 0
+
+/* possible flags for register TX_LINK_CFG 0x1350 */
+#define MTW_REMOTE_MFS_SHIFT 24
+#define MTW_REMOTE_MFB_SHIFT 16
+#define MTW_TX_CFACK_EN (1 << 12)
+#define MTW_TX_RDG_EN (1 << 11)
+#define MTW_TX_MRQ_EN (1 << 10)
+#define MTW_REMOTE_UMFS_EN (1 << 9)
+#define MTW_TX_MFB_EN (1 << 8)
+#define MTW_REMOTE_MFB_LT_SHIFT 0
+
+/* possible flags for registers *_PROT_CFG */
+#define RT2860_RTSTH_EN (1 << 26)
+#define RT2860_TXOP_ALLOW_GF40 (1 << 25)
+#define RT2860_TXOP_ALLOW_GF20 (1 << 24)
+#define RT2860_TXOP_ALLOW_MM40 (1 << 23)
+#define RT2860_TXOP_ALLOW_MM20 (1 << 22)
+#define RT2860_TXOP_ALLOW_OFDM (1 << 21)
+#define RT2860_TXOP_ALLOW_CCK (1 << 20)
+#define RT2860_TXOP_ALLOW_ALL (0x3f << 20)
+#define RT2860_PROT_NAV_SHORT (1 << 18)
+#define RT2860_PROT_NAV_LONG (2 << 18)
+#define RT2860_PROT_CTRL_RTS_CTS (1 << 16)
+#define RT2860_PROT_CTRL_CTS (2 << 16)
+
+/* possible flags for registers EXP_{CTS,ACK}_TIME */
+#define RT2860_EXP_OFDM_TIME_SHIFT 16
+#define RT2860_EXP_CCK_TIME_SHIFT 0
+
+/* possible flags for register RX_FILTR_CFG 0x1400 */
+#define MTW_DROP_CTRL_RSV (1 << 16)
+#define MTW_DROP_BAR (1 << 15)
+#define MTW_DROP_BA (1 << 14)
+#define MTW_DROP_PSPOLL (1 << 13)
+#define MTW_DROP_RTS (1 << 12)
+#define MTW_DROP_CTS (1 << 11)
+#define MTW_DROP_ACK (1 << 10)
+#define MTW_DROP_CFEND (1 << 9)
+#define MTW_DROP_CFACK (1 << 8)
+#define MTW_DROP_DUPL (1 << 7)
+#define MTW_DROP_BC (1 << 6)
+#define MTW_DROP_MC (1 << 5)
+#define MTW_DROP_VER_ERR (1 << 4)
+#define MTW_DROP_NOT_MYBSS (1 << 3)
+#define MTW_DROP_UC_NOME (1 << 2)
+#define MTW_DROP_PHY_ERR (1 << 1)
+#define MTW_DROP_CRC_ERR (1 << 0)
+
+/* possible flags for register AUTO_RSP_CFG 0x1404 */
+#define MTW_CTRL_PWR_BIT (1 << 7)
+#define MTW_BAC_ACK_POLICY (1 << 6)
+#define MTW_CCK_SHORT_EN (1 << 4)
+#define MTW_CTS_40M_REF_EN (1 << 3)
+#define MTW_CTS_40M_MODE_EN (1 << 2)
+#define MTW_BAC_ACKPOLICY_EN (1 << 1)
+#define MTW_AUTO_RSP_EN (1 << 0)
+
+/* possible flags for register SIFS_COST_CFG */
+#define RT2860_OFDM_SIFS_COST_SHIFT 8
+#define RT2860_CCK_SIFS_COST_SHIFT 0
+
+/* possible flags for register TXOP_HLDR_ET 0x1608 */
+#define MTW_TXOP_ETM1_EN (1 << 25)
+#define MTW_TXOP_ETM0_EN (1 << 24)
+#define MTW_TXOP_ETM_THRES_SHIFT 16
+#define MTW_TXOP_ETO_EN (1 << 8)
+#define MTW_TXOP_ETO_THRES_SHIFT 1
+#define MTW_PER_RX_RST_EN (1 << 0)
+
+/* possible flags for register TX_STAT_FIFO 0x1718 */
+#define MTW_TXQ_MCS_SHIFT 16
+#define MTW_TXQ_WCID_SHIFT 8
+#define MTW_TXQ_ACKREQ (1 << 7)
+#define MTW_TXQ_AGG (1 << 6)
+#define MTW_TXQ_OK (1 << 5)
+#define MTW_TXQ_PID_SHIFT 1
+#define MTW_TXQ_VLD (1 << 0)
+
+/* possible flags for register TX_STAT_FIFO_EXT 0x1798 */
+#define MTW_TXQ_PKTID_SHIFT 8
+#define MTW_TXQ_RETRY_SHIFT 0
+
+/* possible flags for register WCID_ATTR 0xa800 */
+#define MTW_MODE_NOSEC 0
+#define MTW_MODE_WEP40 1
+#define MTW_MODE_WEP104 2
+#define MTW_MODE_TKIP 3
+#define MTW_MODE_AES_CCMP 4
+#define MTW_MODE_CKIP40 5
+#define MTW_MODE_CKIP104 6
+#define MTW_MODE_CKIP128 7
+#define MTW_RX_PKEY_EN (1 << 0)
+
+/* possible flags for MT7601 BBP register 47 */
+#define MT7601_R47_MASK 0x07
+#define MT7601_R47_TSSI (0 << 0)
+#define MT7601_R47_PKT (1 << 0)
+#define MT7601_R47_TXRATE (1 << 1)
+#define MT7601_R47_TEMP (1 << 2)
+
+#define MTW_RXQ_WLAN 0
+#define MTW_RXQ_MCU 1
+#define MTW_TXQ_MCU 5
+
+enum mtw_phy_mode {
+ MTW_PHY_CCK,
+ MTW_PHY_OFDM,
+ MTW_PHY_HT,
+ MTW_PHY_HT_GF,
+ MTW_PHY_VHT,
+};
+
+/* RT2860 TX descriptor */
+struct rt2860_txd {
+ uint32_t sdp0; /* Segment Data Pointer 0 */
+ uint16_t sdl1; /* Segment Data Length 1 */
+#define RT2860_TX_BURST (1 << 15)
+#define RT2860_TX_LS1 (1 << 14) /* SDP1 is the last segment */
+
+ uint16_t sdl0; /* Segment Data Length 0 */
+#define RT2860_TX_DDONE (1 << 15)
+#define RT2860_TX_LS0 (1 << 14) /* SDP0 is the last segment */
+
+ uint32_t sdp1; /* Segment Data Pointer 1 */
+ uint8_t reserved[3];
+ uint8_t flags;
+#define RT2860_TX_QSEL_SHIFT 1
+#define RT2860_TX_QSEL_MGMT (0 << 1)
+#define RT2860_TX_QSEL_HCCA (1 << 1)
+#define RT2860_TX_QSEL_EDCA (2 << 1)
+#define RT2860_TX_WIV (1 << 0)
+} __packed;
+
+/* TX descriptor */
+struct mtw_txd {
+ uint16_t len;
+ uint16_t flags;
+#define MTW_TXD_CMD (1 << 14)
+#define MTW_TXD_DATA (0 << 14)
+#define MTW_TXD_MCU (2 << 11)
+#define MTW_TXD_WLAN (0 << 11)
+#define MTW_TXD_QSEL_EDCA (2 << 9)
+#define MTW_TXD_QSEL_HCCA (1 << 9)
+#define MTW_TXD_QSEL_MGMT (0 << 9)
+#define MTW_TXD_WIV (1 << 8)
+#define MTW_TXD_CMD_SHIFT 4
+#define MTW_TXD_80211 (1 << 3)
+} __packed;
+struct mtw_txd_fw {
+ uint16_t len;
+ uint16_t flags;
+uint8_t fw[0x2004];
+} __packed;
+/* TX Wireless Information */
+struct mtw_txwi {
+ uint8_t flags;
+#define MTW_TX_MPDU_DSITY_SHIFT 5
+#define MTW_TX_AMPDU (1 << 4)
+#define MTW_TX_TS (1 << 3)
+#define MTW_TX_CFACK (1 << 2)
+#define MTW_TX_MMPS (1 << 1)
+#define MTW_TX_FRAG (1 << 0)
+
+ uint8_t txop;
+#define MTW_TX_TXOP_HT 0
+#define MTW_TX_TXOP_PIFS 1
+#define MTW_TX_TXOP_SIFS 2
+#define MTW_TX_TXOP_BACKOFF 3
+
+ uint16_t phy;
+#define MT7650_PHY_MODE 0xe000
+#define MT7601_PHY_MODE 0xc000
+#define MT7601_PHY_SHIFT 14
+#define MT7650_PHY_SHIFT 13
+#define MT7650_PHY_SGI (1 << 9)
+#define MT7601_PHY_SGI (1 << 8)
+#define MTW_PHY_BW20 (0 << 7)
+#define MTW_PHY_BW40 (1 << 7)
+#define MTW_PHY_BW80 (2 << 7)
+#define MTW_PHY_BW160 (3 << 7)
+#define MTW_PHY_LDPC (1 << 6)
+#define MTW_PHY_MCS 0x3f
+#define MTW_PHY_SHPRE (1 << 3)
+
+ uint8_t xflags;
+#define MTW_TX_BAWINSIZE_SHIFT 2
+#define MTW_TX_NSEQ (1 << 1)
+#define MTW_TX_ACK (1 << 0)
+
+ uint8_t wcid; /* Wireless Client ID */
+ uint16_t len;
+#define MTW_TX_PID_SHIFT 12
+
+ uint32_t iv;
+ uint32_t eiv;
+ uint32_t reserved1;
+} __packed;
+
+/* RT2860 RX descriptor */
+struct rt2860_rxd {
+ uint32_t sdp0;
+ uint16_t sdl1; /* unused */
+ uint16_t sdl0;
+#define MTW_RX_DDONE (1 << 15)
+#define MTW_RX_LS0 (1 << 14)
+
+ uint32_t sdp1; /* unused */
+ uint32_t flags;
+#define MTW_RX_DEC (1 << 16)
+#define MTW_RX_AMPDU (1 << 15)
+#define MTW_RX_L2PAD (1 << 14)
+#define MTW_RX_RSSI (1 << 13)
+#define MTW_RX_HTC (1 << 12)
+#define MTW_RX_AMSDU (1 << 11)
+#define MTW_RX_MICERR (1 << 10)
+#define MTW_RX_ICVERR (1 << 9)
+#define MTW_RX_CRCERR (1 << 8)
+#define MTW_RX_MYBSS (1 << 7)
+#define MTW_RX_BC (1 << 6)
+#define MTW_RX_MC (1 << 5)
+#define MTW_RX_UC2ME (1 << 4)
+#define MTW_RX_FRAG (1 << 3)
+#define MTW_RX_NULL (1 << 2)
+#define MTW_RX_DATA (1 << 1)
+#define MTW_RX_BA (1 << 0)
+} __packed;
+
+/* RX descriptor */
+struct mtw_rxd {
+ uint16_t len;
+#define MTW_RXD_SELF_GEN (1 << 15)
+#define MTW_RXD_LEN 0x3fff
+
+ uint16_t flags;
+} __packed;
+
+/* RX Wireless Information */
+struct mtw_rxwi {
+ uint32_t flags;
+ uint8_t wcid;
+ uint8_t keyidx;
+#define MTW_RX_UDF_SHIFT 5
+#define MTW_RX_BSS_IDX_SHIFT 2
+
+ uint16_t len;
+#define MTW_RX_TID_SHIFT 12
+
+ uint16_t seq;
+ uint16_t phy;
+ uint8_t rssi[4];
+ uint32_t reserved1;
+ uint32_t reserved2;
+ uint32_t reserved3;
+} __packed __aligned(4);
+
+/* MCU Command */
+struct mtw_mcu_cmd_8 {
+ uint32_t func;
+ uint32_t val;
+} __packed __aligned(4);
+
+struct mtw_mcu_cmd_16 {
+ uint32_t r1;
+ uint32_t r2;
+ uint32_t r3;
+ uint32_t r4;
+} __packed __aligned(4);
+
+#define MTW_DMA_PAD 4
+
+/* first DMA segment contains TXWI + 802.11 header + 32-bit padding */
+#define MTW_TXWI_DMASZ \
+ (sizeof (struct mtw_txwi) + \
+ sizeof (struct ieee80211_htframe) + \
+ sizeof (uint16_t))
+
+#define MT7601_RF_7601 0x7601 /* 1T1R */
+#define MT7610_RF_7610 0x7610 /* 1T1R */
+#define MT7612_RF_7612 0x7612 /* 2T2R */
+
+#define MTW_CONFIG_NO 1
+
+/* USB vendor request */
+#define MTW_RESET 0x1
+#define MTW_WRITE_2 0x2
+#define MTW_WRITE_REGION_1 0x6
+#define MTW_READ_REGION_1 0x7
+#define MTW_EEPROM_READ 0x9
+#define MTW_WRITE_CFG 0x46
+#define MTW_READ_CFG 0x47
+
+/* eFUSE ROM */
+#define MTW_EEPROM_CHIPID 0x00
+#define MTW_EEPROM_VERSION 0x01
+#define MTW_EEPROM_MAC01 0x02
+#define MTW_EEPROM_MAC23 0x03
+#define MTW_EEPROM_MAC45 0x04
+#define MTW_EEPROM_ANTENNA 0x1a
+#define MTW_EEPROM_CONFIG 0x1b
+#define MTW_EEPROM_COUNTRY 0x1c
+#define MTW_EEPROM_FREQ_OFFSET 0x1d
+#define MTW_EEPROM_LED1 0x1e
+#define MTW_EEPROM_LED2 0x1f
+#define MTW_EEPROM_LED3 0x20
+#define MTW_EEPROM_LNA 0x22
+#define MTW_EEPROM_RSSI1_2GHZ 0x23
+#define MTW_EEPROM_RSSI2_2GHZ 0x24
+#define MTW_EEPROM_RSSI1_5GHZ 0x25
+#define MTW_EEPROM_RSSI2_5GHZ 0x26
+#define MTW_EEPROM_DELTAPWR 0x28
+#define MTW_EEPROM_PWR2GHZ_BASE1 0x29
+#define MTW_EEPROM_PWR2GHZ_BASE2 0x30
+#define MTW_EEPROM_TSSI1_2GHZ 0x37
+#define MTW_EEPROM_TSSI2_2GHZ 0x38
+#define MTW_EEPROM_TSSI3_2GHZ 0x39
+#define MTW_EEPROM_TSSI4_2GHZ 0x3a
+#define MTW_EEPROM_TSSI5_2GHZ 0x3b
+#define MTW_EEPROM_PWR5GHZ_BASE1 0x3c
+#define MTW_NIC_CONF2 0x42
+#define MTW_EEPROM_PWR5GHZ_BASE2 0x53
+#define MTW_TXPWR_EXT_PA_5G 0x54
+#define MTW_TXPWR_START_2G_0 0x56
+#define MTW_TXPWR_START_2G_1 0x5c
+#define MTW_TXPWR_START_5G_0 0x62
+#define RT2860_EEPROM_TSSI1_5GHZ 0x6a
+#define RT2860_EEPROM_TSSI2_5GHZ 0x6b
+#define RT2860_EEPROM_TSSI3_5GHZ 0x6c
+#define RT2860_EEPROM_TSSI4_5GHZ 0x6d
+#define RT2860_EEPROM_TSSI5_5GHZ 0x6e
+#define MTW_TX_TSSI_SLOPE 0x6e
+#define MTW_EEPROM_RPWR 0x6f
+
+/* led related */
+#define CMD_LED_MODE 0x10
+#define CMD_MODE_ON 0x0
+static const struct rt2860_rate {
+ uint8_t rate;
+ uint8_t mcs;
+ enum ieee80211_phytype phy;
+ uint8_t ctl_ridx;
+ uint16_t sp_ack_dur;
+ uint16_t lp_ack_dur;
+} rt2860_rates[] = {
+ { 2, 0, IEEE80211_T_DS, 0, 314, 314 },
+ { 4, 1, IEEE80211_T_DS, 1, 258, 162 },
+ { 11, 2, IEEE80211_T_DS, 2, 223, 127 },
+ { 22, 3, IEEE80211_T_DS, 3, 213, 117 },
+ { 12, 0, IEEE80211_T_OFDM, 4, 60, 60 },
+ { 18, 1, IEEE80211_T_OFDM, 4, 52, 52 },
+ { 24, 2, IEEE80211_T_OFDM, 6, 48, 48 },
+ { 36, 3, IEEE80211_T_OFDM, 6, 44, 44 },
+ { 48, 4, IEEE80211_T_OFDM, 8, 44, 44 },
+ { 72, 5, IEEE80211_T_OFDM, 8, 40, 40 },
+ { 96, 6, IEEE80211_T_OFDM, 8, 40, 40 },
+ { 108, 7, IEEE80211_T_OFDM, 8, 40, 40 },
+ { 0x80, 0, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x81, 1, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x82, 2, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x83, 3, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x84, 4, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x85, 5, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x86, 6, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x87, 7, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x88, 8, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x89, 9, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x8a, 10, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x8b, 11, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x8c, 12, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x8d, 13, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x8e, 14, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x8f, 15, IEEE80211_T_HT, 4, 60, 60 },
+
+ /* MCS - 3 streams */
+ { 0x90, 16, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x91, 17, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x92, 18, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x93, 19, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x94, 20, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x95, 21, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x96, 22, IEEE80211_T_HT, 4, 60, 60 },
+ { 0x97, 23, IEEE80211_T_HT, 4, 60, 60 }
+};
+/* These are indexes into the above rt2860_rates[] array */
+#define MTW_RIDX_CCK1 0
+#define MTW_RIDX_CCK11 3
+#define MTW_RIDX_OFDM6 4
+#define MTW_RIDX_MCS0 12
+#define MTW_RIDX_MAX 36
+
+#define MT7601_RF_CHAN \
+ { 1, 0x99, 0x99, 0x09, 0x50 }, \
+ { 2, 0x46, 0x44, 0x0a, 0x50 }, \
+ { 3, 0xec, 0xee, 0x0a, 0x50 }, \
+ { 4, 0x99, 0x99, 0x0b, 0x50 }, \
+ { 5, 0x46, 0x44, 0x08, 0x51 }, \
+ { 6, 0xec, 0xee, 0x08, 0x51 }, \
+ { 7, 0x99, 0x99, 0x09, 0x51 }, \
+ { 8, 0x46, 0x44, 0x0a, 0x51 }, \
+ { 9, 0xec, 0xee, 0x0a, 0x51 }, \
+ { 10, 0x99, 0x99, 0x0b, 0x51 }, \
+ { 11, 0x46, 0x44, 0x08, 0x52 }, \
+ { 12, 0xec, 0xee, 0x08, 0x52 }, \
+ { 13, 0x99, 0x99, 0x09, 0x52 }, \
+ { 14, 0x33, 0x33, 0x0b, 0x52 }
+
+/*
+ * Default values for MAC registers.
+ */
+#define MT7601_DEF_MAC \
+ { MTW_BCN_OFFSET0, 0x18100800 }, \
+ { MTW_BCN_OFFSET1, 0x38302820 }, \
+ { MTW_BCN_OFFSET2, 0x58504840 }, \
+ { MTW_BCN_OFFSET3, 0x78706860 }, \
+ { MTW_MAC_SYS_CTRL, 0x0000000c }, \
+ { MTW_MAX_LEN_CFG, 0x000a3fff }, \
+ { MTW_AMPDU_MAX_LEN_20M1S, 0x77777777 }, \
+ { MTW_AMPDU_MAX_LEN_20M2S, 0x77777777 }, \
+ { MTW_AMPDU_MAX_LEN_40M1S, 0x77777777 }, \
+ { MTW_AMPDU_MAX_LEN_40M2S, 0x77777777 }, \
+ { MTW_XIFS_TIME_CFG, 0x33a41010 }, \
+ { MTW_BKOFF_SLOT_CFG, 0x00000209 }, \
+ { MTW_TBTT_SYNC_CFG, 0x00422010 }, \
+ { MTW_INT_TIMER_CFG, 0x00000000 }, \
+ { MTW_PWR_PIN_CFG, 0x00000000 }, \
+ { MTW_AUTO_WAKEUP_CFG, 0x00000014 }, \
+ { MTW_EDCA_AC_CFG(0), 0x000a4360 }, \
+ { MTW_EDCA_AC_CFG(1), 0x000a4700 }, \
+ { MTW_EDCA_AC_CFG(2), 0x00043338 }, \
+ { MTW_EDCA_AC_CFG(3), 0x0003222f }, \
+ { MTW_TX_PIN_CFG, 0x33150f0f }, \
+ { MTW_TX_BAND_CFG, 0x00000005 }, \
+ { MTW_TX_SW_CFG0, 0x00000402 }, \
+ { MTW_TX_SW_CFG1, 0x00000000 }, \
+ { MTW_TX_SW_CFG2, 0x00000000 }, \
+ { MTW_TXOP_CTRL_CFG, 0x0000583f }, \
+ { MTW_TX_RTS_CFG, 0x01100020 }, \
+ { MTW_TX_TIMEOUT_CFG, 0x000a2090 }, \
+ { MTW_TX_RETRY_CFG, 0x47d01f0f }, \
+ { MTW_TX_LINK_CFG, 0x007f1820 }, \
+ { MTW_HT_FBK_CFG1, 0xedcba980 }, \
+ { MTW_CCK_PROT_CFG, 0x07f40000 }, \
+ { MTW_OFDM_PROT_CFG, 0x07f60000 }, \
+ { MTW_MM20_PROT_CFG, 0x01750003 }, \
+ { MTW_MM40_PROT_CFG, 0x03f50003 }, \
+ { MTW_GF20_PROT_CFG, 0x01750003 }, \
+ { MTW_GF40_PROT_CFG, 0x03f50003 }, \
+ { MTW_EXP_ACK_TIME, 0x002400ca }, \
+ { MTW_TX_PWR_CFG5, 0x00000000 }, \
+ { MTW_TX_PWR_CFG6, 0x01010101 }, \
+ { MTW_TX0_RF_GAIN_CORR, 0x003b0005 }, \
+ { MTW_TX1_RF_GAIN_CORR, 0x00000000 }, \
+ { MTW_TX0_RF_GAIN_ATTEN, 0x00006969 }, \
+ { MTW_TX_ALC_CFG3, 0x6c6c6c6c }, \
+ { MTW_TX_ALC_CFG0, 0x2f2f0005 }, \
+ { MTW_TX_ALC_CFG4, 0x00000400 }, \
+ { MTW_TX_ALC_VGA3, 0x00060006 }, \
+ { MTW_RX_FILTR_CFG, 0x00015f97 }, \
+ { MTW_AUTO_RSP_CFG, 0x00000003 }, \
+ { MTW_LEGACY_BASIC_RATE, 0x0000015f }, \
+ { MTW_HT_BASIC_RATE, 0x00008003 }, \
+ { MTW_RX_MAX_PCNT, 0x0000009f }, \
+ { MTW_WPDMA_GLO_CFG, 0x00000030 }, \
+ { MTW_WMM_AIFSN_CFG, 0x00002273 }, \
+ { MTW_WMM_CWMIN_CFG, 0x00002344 }, \
+ { MTW_WMM_CWMAX_CFG, 0x000034aa }, \
+ { MTW_TSO_CTRL, 0x00000000 }, \
+ { MTW_SYS_CTRL, 0x00080c00 }, \
+ { MTW_FCE_PSE_CTRL, 0x00000001 }, \
+ { MTW_AUX_CLK_CFG, 0x00000000 }, \
+ { MTW_BBP_PA_MODE_CFG0, 0x010055ff }, \
+ { MTW_BBP_PA_MODE_CFG1, 0x00550055 }, \
+ { MTW_RF_PA_MODE_CFG0, 0x010055ff }, \
+ { MTW_RF_PA_MODE_CFG1, 0x00550055 }, \
+ { 0x0a38, 0x00000000 }, \
+ { MTW_BBP_CSR, 0x00000000 }, \
+ { MTW_PBF_CFG, 0x7f723c1f }
+
+/*
+ * Default values for Baseband registers
+ */
+#define MT7601_DEF_BBP \
+ { 1, 0x04 }, \
+ { 4, 0x40 }, \
+ { 20, 0x06 }, \
+ { 31, 0x08 }, \
+ { 178, 0xff }, \
+ { 66, 0x14 }, \
+ { 68, 0x8b }, \
+ { 69, 0x12 }, \
+ { 70, 0x09 }, \
+ { 73, 0x11 }, \
+ { 75, 0x60 }, \
+ { 76, 0x44 }, \
+ { 84, 0x9a }, \
+ { 86, 0x38 }, \
+ { 91, 0x07 }, \
+ { 92, 0x02 }, \
+ { 99, 0x50 }, \
+ { 101, 0x00 }, \
+ { 103, 0xc0 }, \
+ { 104, 0x92 }, \
+ { 105, 0x3c }, \
+ { 106, 0x03 }, \
+ { 128, 0x12 }, \
+ { 142, 0x04 }, \
+ { 143, 0x37 }, \
+ { 142, 0x03 }, \
+ { 143, 0x99 }, \
+ { 160, 0xeb }, \
+ { 161, 0xc4 }, \
+ { 162, 0x77 }, \
+ { 163, 0xf9 }, \
+ { 164, 0x88 }, \
+ { 165, 0x80 }, \
+ { 166, 0xff }, \
+ { 167, 0xe4 }, \
+ { 195, 0x00 }, \
+ { 196, 0x00 }, \
+ { 195, 0x01 }, \
+ { 196, 0x04 }, \
+ { 195, 0x02 }, \
+ { 196, 0x20 }, \
+ { 195, 0x03 }, \
+ { 196, 0x0a }, \
+ { 195, 0x06 }, \
+ { 196, 0x16 }, \
+ { 195, 0x07 }, \
+ { 196, 0x05 }, \
+ { 195, 0x08 }, \
+ { 196, 0x37 }, \
+ { 195, 0x0a }, \
+ { 196, 0x15 }, \
+ { 195, 0x0b }, \
+ { 196, 0x17 }, \
+ { 195, 0x0c }, \
+ { 196, 0x06 }, \
+ { 195, 0x0d }, \
+ { 196, 0x09 }, \
+ { 195, 0x0e }, \
+ { 196, 0x05 }, \
+ { 195, 0x0f }, \
+ { 196, 0x09 }, \
+ { 195, 0x10 }, \
+ { 196, 0x20 }, \
+ { 195, 0x20 }, \
+ { 196, 0x17 }, \
+ { 195, 0x21 }, \
+ { 196, 0x06 }, \
+ { 195, 0x22 }, \
+ { 196, 0x09 }, \
+ { 195, 0x23 }, \
+ { 196, 0x17 }, \
+ { 195, 0x24 }, \
+ { 196, 0x06 }, \
+ { 195, 0x25 }, \
+ { 196, 0x09 }, \
+ { 195, 0x26 }, \
+ { 196, 0x17 }, \
+ { 195, 0x27 }, \
+ { 196, 0x06 }, \
+ { 195, 0x28 }, \
+ { 196, 0x09 }, \
+ { 195, 0x29 }, \
+ { 196, 0x05 }, \
+ { 195, 0x2a }, \
+ { 196, 0x09 }, \
+ { 195, 0x80 }, \
+ { 196, 0x8b }, \
+ { 195, 0x81 }, \
+ { 196, 0x12 }, \
+ { 195, 0x82 }, \
+ { 196, 0x09 }, \
+ { 195, 0x83 }, \
+ { 196, 0x17 }, \
+ { 195, 0x84 }, \
+ { 196, 0x11 }, \
+ { 195, 0x85 }, \
+ { 196, 0x00 }, \
+ { 195, 0x86 }, \
+ { 196, 0x00 }, \
+ { 195, 0x87 }, \
+ { 196, 0x18 }, \
+ { 195, 0x88 }, \
+ { 196, 0x60 }, \
+ { 195, 0x89 }, \
+ { 196, 0x44 }, \
+ { 195, 0x8a }, \
+ { 196, 0x8b }, \
+ { 195, 0x8b }, \
+ { 196, 0x8b }, \
+ { 195, 0x8c }, \
+ { 196, 0x8b }, \
+ { 195, 0x8d }, \
+ { 196, 0x8b }, \
+ { 195, 0x8e }, \
+ { 196, 0x09 }, \
+ { 195, 0x8f }, \
+ { 196, 0x09 }, \
+ { 195, 0x90 }, \
+ { 196, 0x09 }, \
+ { 195, 0x91 }, \
+ { 196, 0x09 }, \
+ { 195, 0x92 }, \
+ { 196, 0x11 }, \
+ { 195, 0x93 }, \
+ { 196, 0x11 }, \
+ { 195, 0x94 }, \
+ { 196, 0x11 }, \
+ { 195, 0x95 }, \
+ { 196, 0x11 }, \
+ { 47, 0x80 }, \
+ { 60, 0x80 }, \
+ { 150, 0xd2 }, \
+ { 151, 0x32 }, \
+ { 152, 0x23 }, \
+ { 153, 0x41 }, \
+ { 154, 0x00 }, \
+ { 155, 0x4f }, \
+ { 253, 0x7e }, \
+ { 195, 0x30 }, \
+ { 196, 0x32 }, \
+ { 195, 0x31 }, \
+ { 196, 0x23 }, \
+ { 195, 0x32 }, \
+ { 196, 0x45 }, \
+ { 195, 0x35 }, \
+ { 196, 0x4a }, \
+ { 195, 0x36 }, \
+ { 196, 0x5a }, \
+ { 195, 0x37 }, \
+ { 196, 0x5a }
+
+/*
+ * Default values for RF registers
+ */
+#define MT7601_BANK0_RF \
+ { 0, 0x02 }, \
+ { 1, 0x01 }, \
+ { 2, 0x11 }, \
+ { 3, 0xff }, \
+ { 4, 0x0a }, \
+ { 5, 0x20 }, \
+ { 6, 0x00 }, \
+ { 7, 0x00 }, \
+ { 8, 0x00 }, \
+ { 9, 0x00 }, \
+ { 10, 0x00 }, \
+ { 11, 0x21 }, \
+ { 13, 0x00 }, \
+ { 14, 0x7c }, \
+ { 15, 0x22 }, \
+ { 16, 0x80 }, \
+ { 17, 0x99 }, \
+ { 18, 0x99 }, \
+ { 19, 0x09 }, \
+ { 20, 0x50 }, \
+ { 21, 0xb0 }, \
+ { 22, 0x00 }, \
+ { 23, 0xc5 }, \
+ { 24, 0xfc }, \
+ { 25, 0x40 }, \
+ { 26, 0x4d }, \
+ { 27, 0x02 }, \
+ { 28, 0x72 }, \
+ { 29, 0x01 }, \
+ { 30, 0x00 }, \
+ { 31, 0x00 }, \
+ { 32, 0x00 }, \
+ { 33, 0x00 }, \
+ { 34, 0x23 }, \
+ { 35, 0x01 }, \
+ { 36, 0x00 }, \
+ { 37, 0x00 }, \
+ { 38, 0x00 }, \
+ { 39, 0x20 }, \
+ { 40, 0x00 }, \
+ { 41, 0xd0 }, \
+ { 42, 0x1b }, \
+ { 43, 0x02 }, \
+ { 44, 0x00 }
+
+#define MT7601_BANK4_RF \
+ { 0, 0x01 }, \
+ { 1, 0x00 }, \
+ { 2, 0x00 }, \
+ { 3, 0x00 }, \
+ { 4, 0x00 }, \
+ { 5, 0x08 }, \
+ { 6, 0x00 }, \
+ { 7, 0x5b }, \
+ { 8, 0x52 }, \
+ { 9, 0xb6 }, \
+ { 10, 0x57 }, \
+ { 11, 0x33 }, \
+ { 12, 0x22 }, \
+ { 13, 0x3d }, \
+ { 14, 0x3e }, \
+ { 15, 0x13 }, \
+ { 16, 0x22 }, \
+ { 17, 0x23 }, \
+ { 18, 0x02 }, \
+ { 19, 0xa4 }, \
+ { 20, 0x01 }, \
+ { 21, 0x12 }, \
+ { 22, 0x80 }, \
+ { 23, 0xb3 }, \
+ { 24, 0x00 }, \
+ { 25, 0x00 }, \
+ { 26, 0x00 }, \
+ { 27, 0x00 }, \
+ { 28, 0x18 }, \
+ { 29, 0xee }, \
+ { 30, 0x6b }, \
+ { 31, 0x31 }, \
+ { 32, 0x5d }, \
+ { 33, 0x00 }, \
+ { 34, 0x96 }, \
+ { 35, 0x55 }, \
+ { 36, 0x08 }, \
+ { 37, 0xbb }, \
+ { 38, 0xb3 }, \
+ { 39, 0xb3 }, \
+ { 40, 0x03 }, \
+ { 41, 0x00 }, \
+ { 42, 0x00 }, \
+ { 43, 0xc5 }, \
+ { 44, 0xc5 }, \
+ { 45, 0xc5 }, \
+ { 46, 0x07 }, \
+ { 47, 0xa8 }, \
+ { 48, 0xef }, \
+ { 49, 0x1a }, \
+ { 54, 0x07 }, \
+ { 55, 0xa7 }, \
+ { 56, 0xcc }, \
+ { 57, 0x14 }, \
+ { 58, 0x07 }, \
+ { 59, 0xa8 }, \
+ { 60, 0xd7 }, \
+ { 61, 0x10 }, \
+ { 62, 0x1c }, \
+ { 63, 0x00 }
+
+#define MT7601_BANK5_RF \
+ { 0, 0x47 }, \
+ { 1, 0x00 }, \
+ { 2, 0x00 }, \
+ { 3, 0x08 }, \
+ { 4, 0x04 }, \
+ { 5, 0x20 }, \
+ { 6, 0x3a }, \
+ { 7, 0x3a }, \
+ { 8, 0x00 }, \
+ { 9, 0x00 }, \
+ { 10, 0x10 }, \
+ { 11, 0x10 }, \
+ { 12, 0x10 }, \
+ { 13, 0x10 }, \
+ { 14, 0x10 }, \
+ { 15, 0x20 }, \
+ { 16, 0x22 }, \
+ { 17, 0x7c }, \
+ { 18, 0x00 }, \
+ { 19, 0x00 }, \
+ { 20, 0x00 }, \
+ { 21, 0xf1 }, \
+ { 22, 0x11 }, \
+ { 23, 0x02 }, \
+ { 24, 0x41 }, \
+ { 25, 0x20 }, \
+ { 26, 0x00 }, \
+ { 27, 0xd7 }, \
+ { 28, 0xa2 }, \
+ { 29, 0x20 }, \
+ { 30, 0x49 }, \
+ { 31, 0x20 }, \
+ { 32, 0x04 }, \
+ { 33, 0xf1 }, \
+ { 34, 0xa1 }, \
+ { 35, 0x01 }, \
+ { 41, 0x00 }, \
+ { 42, 0x00 }, \
+ { 43, 0x00 }, \
+ { 44, 0x00 }, \
+ { 45, 0x00 }, \
+ { 46, 0x00 }, \
+ { 47, 0x00 }, \
+ { 48, 0x00 }, \
+ { 49, 0x00 }, \
+ { 50, 0x00 }, \
+ { 51, 0x00 }, \
+ { 52, 0x00 }, \
+ { 53, 0x00 }, \
+ { 54, 0x00 }, \
+ { 55, 0x00 }, \
+ { 56, 0x00 }, \
+ { 57, 0x00 }, \
+ { 58, 0x31 }, \
+ { 59, 0x31 }, \
+ { 60, 0x0a }, \
+ { 61, 0x02 }, \
+ { 62, 0x00 }, \
+ { 63, 0x00 }
+union mtw_stats {
+ uint32_t raw;
+ struct {
+ uint16_t fail;
+ uint16_t pad;
+ } error;
+ struct {
+ uint16_t success;
+ uint16_t retry;
+ } tx;
+} __aligned(4);
diff --git a/sys/dev/usb/wlan/if_mtwvar.h b/sys/dev/usb/wlan/if_mtwvar.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/usb/wlan/if_mtwvar.h
@@ -0,0 +1,387 @@
+/* $OpenBSD: if_mtwvar.h,v 1.1 2021/12/20 13:59:02 hastings Exp $ */
+/*
+ * Copyright (c) 2008,2009 Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define MTW_MAX_RXSZ \
+ 4096
+#if 0
+ (sizeof (uint32_t) + \
+ sizeof (struct mtw_rxwi) + \
+ sizeof (uint16_t) + \
+ MCLBYTES + \
+ sizeof (struct mtw_rxd))
+#endif
+
+#define MTW_TX_TIMEOUT 5000 /* ms */
+#define MTW_VAP_MAX 8
+#define MTW_RX_RING_COUNT 1
+#define MTW_TX_RING_COUNT 32
+
+#define MTW_RXQ_COUNT 2
+#define MTW_TXQ_COUNT 6
+
+#define MTW_WCID_MAX 64
+#define MTW_AID2WCID(aid) (1 + ((aid) & 0x7))
+
+struct mtw_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint64_t wr_tsf;
+ uint8_t wr_flags;
+ uint8_t wr_rate;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ uint8_t wr_dbm_antsignal;
+ uint8_t wr_antenna;
+ uint8_t wr_antsignal;
+} __packed;
+#define MTW_RATECTL_OFF 0
+#define MTW_RX_RADIOTAP_PRESENT \
+ (1 << IEEE80211_RADIOTAP_FLAGS | \
+ 1 << IEEE80211_RADIOTAP_RATE | \
+ 1 << IEEE80211_RADIOTAP_CHANNEL | \
+ 1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL | \
+ 1 << IEEE80211_RADIOTAP_ANTENNA | \
+ 1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)
+struct mtw_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;
+ uint8_t wt_hwqueue;
+} __packed;
+
+#define MTW_TX_RADIOTAP_PRESENT \
+ (1 << IEEE80211_RADIOTAP_FLAGS | \
+ 1 << IEEE80211_RADIOTAP_RATE | \
+ 1 << IEEE80211_RADIOTAP_CHANNEL)
+
+struct mtw_softc;
+
+struct mtw_fw_data {
+ uint16_t len;
+ uint16_t flags;
+
+ uint8_t *buf;
+ uint32_t buflen;
+
+};
+struct mtw_tx_desc {
+ uint32_t flags;
+#define RT2573_TX_BURST (1 << 0)
+#define RT2573_TX_VALID (1 << 1)
+#define RT2573_TX_MORE_FRAG (1 << 2)
+#define RT2573_TX_NEED_ACK (1 << 3)
+#define RT2573_TX_TIMESTAMP (1 << 4)
+#define RT2573_TX_OFDM (1 << 5)
+#define RT2573_TX_IFS_SIFS (1 << 6)
+#define RT2573_TX_LONG_RETRY (1 << 7)
+#define RT2573_TX_TKIPMIC (1 << 8)
+#define RT2573_TX_KEY_PAIR (1 << 9)
+#define RT2573_TX_KEY_ID(id) (((id) & 0x3f) << 10)
+#define RT2573_TX_CIP_MODE(m) ((m) << 29)
+
+ uint16_t wme;
+#define RT2573_QID(v) (v)
+#define RT2573_AIFSN(v) ((v) << 4)
+#define RT2573_LOGCWMIN(v) ((v) << 8)
+#define RT2573_LOGCWMAX(v) ((v) << 12)
+
+ uint8_t hdrlen;
+ uint8_t xflags;
+#define RT2573_TX_HWSEQ (1 << 4)
+
+ uint8_t plcp_signal;
+ uint8_t plcp_service;
+#define RT2573_PLCP_LENGEXT 0x80
+
+ uint8_t plcp_length_lo;
+ uint8_t plcp_length_hi;
+
+ uint32_t iv;
+ uint32_t eiv;
+
+ uint8_t offset;
+ uint8_t qid;
+ uint8_t txpower;
+#define RT2573_DEFAULT_TXPOWER 0
+
+ uint8_t reserved;
+} __packed;
+
+struct mtw_tx_data {
+ STAILQ_ENTRY(mtw_tx_data) next;
+ struct mbuf *m;
+ struct mtw_softc *sc;
+ struct usbd_xfer *xfer;
+ uint8_t qid;
+ uint8_t ridx;
+ uint32_t buflen;
+ //struct mtw_tx_desc desc;
+ struct ieee80211_node *ni;
+ //struct mtw_txd desc;
+ uint8_t desc[sizeof(struct mtw_txd)+sizeof(struct mtw_txwi)];
+
+};
+
+struct mtw_rx_data {
+ STAILQ_ENTRY(mtw_rx_data) next;
+ struct mtw_softc *sc;
+ struct usbd_xfer *xfer;
+
+ uint8_t *buf;
+};
+
+struct mtw_tx_ring {
+ struct mtw_tx_data data[MTW_TX_RING_COUNT];
+ struct usbd_pipe *pipeh;
+ int cur;
+ int queued;
+ uint8_t pipe_no;
+};
+
+struct mtw_rx_ring {
+ struct mtw_rx_data data[MTW_RX_RING_COUNT];
+ struct usbd_pipe *pipeh;
+ uint8_t pipe_no;
+};
+
+struct mtw_vap {
+ struct ieee80211vap vap;
+ struct mbuf *beacon_mbuf;
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ void (*recv_mgmt)(struct ieee80211_node *,
+ struct mbuf *, int,
+ const struct ieee80211_rx_stats *,
+ int, int);
+
+ uint8_t rvp_id;
+};
+#define MTW_VAP(vap) ((struct mtw_vap *)(vap))
+struct mtw_host_cmd {
+ void (*cb)(struct mtw_softc *, void *);
+ uint8_t data[256];
+};
+
+struct mtw_cmd_newstate {
+ enum ieee80211_state state;
+ int arg;
+};
+
+struct mtw_cmd_key {
+ struct ieee80211_key key;
+ struct ieee80211_node *ni;
+};
+
+#define MTW_HOST_CMD_RING_COUNT 32
+struct mtw_host_cmd_ring {
+ struct mtw_host_cmd cmd[MTW_HOST_CMD_RING_COUNT];
+ int cur;
+ int next;
+ int queued;
+};
+
+
+
+struct mtw_node {
+ struct ieee80211_node ni;
+ uint8_t mgt_ridx;
+ uint8_t amrr_ridx;
+ uint8_t fix_ridx;
+
+};
+#define MTW_NODE(ni) ((struct mtw_node *)(ni))
+
+struct mtw_mcu_tx {
+ struct mtw_softc *sc;
+ struct usbd_xfer *xfer;
+ struct usbd_pipe *pipeh;
+ uint8_t pipe_no;
+ uint8_t *buf;
+ int8_t seq;
+};
+
+#define MTW_MCU_IVB_LEN 0x40
+struct mtw_ucode_hdr {
+ uint32_t ilm_len;
+ uint32_t dlm_len;
+ uint16_t build_ver;
+ uint16_t fw_ver;
+ uint8_t pad[4];
+ char build_time[16];
+} __packed;
+
+struct mtw_ucode {
+ struct mtw_ucode_hdr hdr;
+ uint8_t ivb[MTW_MCU_IVB_LEN];
+ uint8_t data[];
+} __packed;
+
+STAILQ_HEAD(mtw_tx_data_head, mtw_tx_data);
+struct mtw_endpoint_queue {
+ struct mtw_tx_data tx_data[MTW_TX_RING_COUNT];
+ struct mtw_tx_data_head tx_qh;
+ struct mtw_tx_data_head tx_fh;
+ uint32_t tx_nfree;
+};
+
+struct mtw_cmdq {
+ void *arg0;
+ void *arg1;
+ void (*func)(void *);
+ struct ieee80211_key *k;
+ struct ieee80211_key key;
+ uint8_t mac[IEEE80211_ADDR_LEN];
+ uint8_t wcid;
+};
+enum {
+ MTW_BULK_RX, /* = WME_AC_BK */
+ //MTW_BULK_RX1,
+ MTW_BULK_TX_BE, /* = WME_AC_BE */
+ MTW_BULK_TX_VI, /* = WME_AC_VI */
+ MTW_BULK_TX_VO, /* = WME_AC_VO */
+ MTW_BULK_TX_HCCA,
+ MTW_BULK_TX_PRIO,
+ MTW_BULK_TX_BK,
+ MTW_BULK_FW_CMD,
+ MTW_BULK_RAW_TX,
+ MTW_N_XFER,
+};
+#define MTW_TXCNT 0
+#define MTW_SUCCESS 1
+#define MTW_RETRY 2
+#define MTW_EP_QUEUES 6
+#define MTW_FLAG_FWLOAD_NEEDED 0x01
+#define MTW_RUNNING 0x02
+struct mtw_softc {
+ device_t sc_dev;
+ int sc_idx;
+ struct ieee80211com sc_ic;
+ struct ieee80211_ratectl_tx_stats sc_txs;
+ int (*sc_newstate)(struct ieee80211com *,
+ enum ieee80211_state, int);
+ int (*sc_srom_read)(struct mtw_softc *,
+ uint16_t, uint16_t *);
+#define MTW_CMDQ_MAX 16
+#define MTW_CMDQ_MASQ (MTW_CMDQ_MAX - 1)
+#define MTW_CMDQ_ABORT 0
+#define MTW_CMDQ_GO 1
+ struct mbuf *rx_m;
+ uint8_t runbmap;
+ uint8_t running;
+ uint8_t ap_running;
+ uint8_t adhoc_running;
+ uint8_t sta_running;
+ uint8_t fwloading;
+ uint16_t wcid_stats[MTW_WCID_MAX + 1][3];
+ struct mbufq sc_snd;
+ uint8_t cmdq_exec;
+ uint8_t fifo_cnt;
+ uint32_t sc_flags;
+ uint8_t rvp_cnt;
+ uint8_t cmdq_run;
+ uint8_t rvp_bmap;
+ struct mtw_cmdq cmdq[MTW_CMDQ_MAX];
+ struct task cmdq_task;
+ uint8_t cmdq_mtw;
+ uint8_t cmdq_key_set;
+ struct usb_device *sc_udev;
+ struct usb_interface *sc_iface;
+ uint32_t cmdq_store;
+ struct mtx sc_mtx;
+ uint32_t sc_mcu_xferlen;
+ struct usb_xfer *sc_xfer[MTW_N_XFER];
+ uint16_t asic_ver;
+ uint16_t asic_rev;
+ uint16_t mac_ver;
+ uint16_t mac_rev;
+ uint16_t rf_rev;
+ int ridx;
+ int amrr_ridx;
+ uint8_t freq;
+ uint8_t ntxchains;
+ uint8_t nrxchains;
+
+ struct mtw_txd_fw *txd_fw[7];
+ int sc_sent;
+ uint8_t sc_ivb_1[MTW_MCU_IVB_LEN];
+ struct mtw_endpoint_queue sc_epq[MTW_BULK_RX];
+ uint8_t rfswitch;
+ uint8_t ext_2ghz_lna;
+ uint8_t ext_5ghz_lna;
+ uint8_t calib_2ghz;
+ uint8_t calib_5ghz;
+ uint8_t txmixgain_2ghz;
+ uint8_t txmixgain_5ghz;
+ int8_t txpow1[54];
+ int8_t txpow2[54];
+ int8_t txpow3[54];
+ int8_t rssi_2ghz[3];
+ int8_t rssi_5ghz[3];
+ uint8_t lna[4];
+
+ uint8_t leds;
+ uint16_t led[3];
+ uint32_t txpow20mhz[5];
+ uint32_t txpow40mhz_2ghz[5];
+ uint32_t txpow40mhz_5ghz[5];
+
+ int8_t bbp_temp;
+ uint8_t rf_freq_offset;
+ uint32_t rf_pa_mode[2];
+ int sc_rf_calibrated;
+ int sc_bw_calibrated;
+ int sc_chan_group;
+
+
+
+
+ uint8_t cmd_seq;
+ uint8_t sc_detached;
+ struct mtw_tx_ring sc_mcu;
+ struct mtw_rx_ring rxq[MTW_RXQ_COUNT];
+ struct mtw_tx_ring txq[MTW_TXQ_COUNT];
+ struct task ratectl_task;
+ struct usb_callout ratectl_ch;
+ uint8_t ratectl_run;
+ //struct mtw_host_cmd_ring cmdq;
+ uint8_t qfullmsk;
+ int sc_tx_timer;
+
+ uint8_t sc_bssid[IEEE80211_ADDR_LEN];
+
+ union {
+ struct mtw_rx_radiotap_header th;
+ uint8_t pad[64];
+ } sc_rxtapu;
+#define sc_rxtap sc_rxtapu.th
+ int sc_rxtap_len;
+
+ union {
+ struct mtw_tx_radiotap_header th;
+ uint8_t pad[64];
+ uint8_t wt_hwqueue;
+
+ } sc_txtapu;
+#define sc_txtap sc_txtapu.th
+ int sc_txtap_len;
+ int sc_key_tasks;
+};
+#define MTW_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define MTW_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define MTW_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t)
diff --git a/sys/modules/usb/Makefile b/sys/modules/usb/Makefile
--- a/sys/modules/usb/Makefile
+++ b/sys/modules/usb/Makefile
@@ -45,7 +45,7 @@
SUBDIR = usb
SUBDIR += ${_dwc_otg} ehci ${_musb} ohci uhci xhci ${_uss820dci} \
${_atmegadci} ${_avr32dci} ${_rsu} ${_rsufw} ${_bcm2838_xhci}
-SUBDIR += ${_rum} ${_run} ${_runfw} ${_uath} upgt usie ural ${_zyd} ${_urtw}
+SUBDIR += mtw ${_rum} ${_run} ${_runfw} ${_uath} upgt usie ural ${_zyd} ${_urtw}
SUBDIR += atp cfumass uhid uhid_snes ukbd ums udbp uep wmt wsp ugold uled \
usbhid
SUBDIR += ucom u3g uark ubsa ubser uchcom ucycom ufoma uftdi ugensa uipaq ulpt \
diff --git a/sys/modules/usb/mtw/Makefile b/sys/modules/usb/mtw/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/usb/mtw/Makefile
@@ -0,0 +1,9 @@
+S= ${SRCTOP}/sys
+
+.PATH: $S/dev/usb/wlan
+
+KMOD= if_mtw
+SRCS= opt_bus.h opt_usb.h opt_wlan.h device_if.h bus_if.h usb_if.h \
+ usbdevs.h if_mtw.c
+DEBUG_FLAGS=-g
+.include <bsd.kmod.mk>

File Metadata

Mime Type
text/plain
Expires
Thu, Jan 9, 8:47 PM (9 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15735731
Default Alt Text
D45179.diff (176 KB)

Event Timeline