Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F137924396
D45179.id148709.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
178 KB
Referenced Files
None
Subscribers
None
D45179.id148709.diff
View Options
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 reinitialize when reloading the module without 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,4728 @@
+/*-
+ * 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 void mtw_reset_livelock(struct mtw_softc *);
+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_SWAMSDUTX | /* Do software A-MSDU TX */
+ IEEE80211_C_FF | /* Atheros fast-frames */
+ IEEE80211_C_WME | // 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;
+
+
+ sc->sc_detached = 1;
+
+
+ /* 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
+
+ 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);
+}
+
+// Read 16-bit from eFUSE ROM for RT3xxx.
+#if 0
+static int
+mtw_eeprom_read_2(struct mtw_softc *sc, uint16_t addr, uint16_t *val)
+{
+ usb_device_request_t req;
+ uint16_t tmp;
+ int error;
+
+ addr *= 2;
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = MTW_EEPROM_READ;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, addr);
+ USETW(req.wLength, sizeof(tmp));
+
+ error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, &tmp);
+ if (error == 0)
+ *val = le16toh(tmp);
+ else
+ *val = 0xffff;
+ return (error);
+}
+#endif
+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;
+
+
+//usb_caallout_reset(&sc->ratectl_ch,hz,mtw_ratectl_to,sc);
+ /* 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 */
+
+ //struct ieee80211com *ic = &sc->sc_ic;
+// ieee80211_iterate_nodes(&ic->ic_sta, mtw_iter_func, sc);
+}
+
+/* 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;
+#if 0
+ if (sc->rvp_cnt > 1 || vap->iv_opmode != IEEE80211_M_STA) {
+ /*
+ * mtw_reset_livelock() doesn't do anything with AMRR,
+ * but Ralink wants us to call it every 1 sec. So, we
+ * piggyback here rather than creating another callout.
+ * Livelock may occur only in HOSTAP or IBSS mode
+ * (when h/w is sending beacons).
+ */
+ MTW_LOCK(sc);
+ mtw_reset_livelock(sc);
+ /* just in case, there are some stats to drain */
+ mtw_drain_fifo(sc);
+ MTW_UNLOCK(sc);
+ }
+#endif
+
+ 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 %s %d\n", ni->ni_txrate,
+ rn->amrr_ridx, __func__,__LINE__);
+}
+
+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--;
+
+ //device_printf(sc->sc_dev,"%d ridx 0x%x\n",__LINE__,ridx);
+ 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) :
+ 0x1; // was 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);
+ //device_printf(sc->sc_dev,"jsm:mtw_wme_ac_xfer_map[qid]:%d qid:%d\n",mtw_wme_ac_xfer_map[qid],qid);
+ usbd_transfer_start(sc->sc_xfer[mtw_wme_ac_xfer_map[qid]]);
+ //device_printf(sc->sc_dev,"%d ridx 0x%x\n",__LINE__,ridx);
+
+ 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)) {
+ //device_printf(sc->sc_dev, "%s:%d 0x%d\n", __FILE__, __LINE__,
+ // 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/*, NET80211_CBW_FLAG_HT40*/,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_RUN &&
+ vap->iv_opmode != IEEE80211_M_STA)
+ mtw_reset_livelock(sc);
+ else 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 void
+mtw_reset_livelock(struct mtw_softc *sc)
+{
+#if 0
+ uint32_t tmp;
+
+ MTW_LOCK_ASSERT(sc, MA_OWNED);
+
+ /*
+ * In IBSS or HostAP modes (when the hardware sends beacons), the MAC
+ * can run into a livelock and start sending CTS-to-self frames like
+ * crazy if protection is enabled. Reset MAC/BBP for a while
+ */
+ mtw_read(sc, MTW_DEBUG, &tmp);
+ MTW_DPRINTF(sc, MTW_DEBUG_RESET, "debug reg %08x\n", tmp);
+ if ((tmp & (1 << 29)) && (tmp & (1 << 7 | 1 << 5))) {
+ MTW_DPRINTF(sc, MTW_DEBUG_RESET,
+ "CTS-to-self livelock detected\n");
+ mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_MAC_SRST);
+ mtw_delay(sc, 1);
+ mtw_write(sc, MTW_MAC_SYS_CTRL,
+ MTW_MAC_RX_EN | MTW_MAC_TX_EN);
+ }
+#endif
+}
+
+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)
+{
+#if 0
+ 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);
+#endif
+}
+
+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;
+ //device_printf(sc->sc_dev,"jsm %d %s \n",__LINE__,__func__);
+ 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
Details
Attached
Mime Type
text/plain
Expires
Fri, Nov 28, 7:35 AM (8 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
26277543
Default Alt Text
D45179.id148709.diff (178 KB)
Attached To
Mode
D45179: mt7601U: Importing if_mtw from OpenBSD
Attached
Detach File
Event Timeline
Log In to Comment