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 +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd May 13, 2024 +.Dt MTW 4 +.Os +.Sh NAME +.Nm if_mtw +.Nd "Mediatek MT7601U" +.Ed +.Sh DESCRIPTION +This module provides support for Mediatek MT7601U with the firmware from net/mt7601u-firmware-kmod + +.Sh HARDWARE +The +.Nm +driver supports Mediatek MT7601U +based USB wireless network adapters including (but not tested): +.Pp +.Bl -column -compact +.It +ASUS USB-N10 v2 +.It +D-Link DWA-127 rev B1 +.It +Edimax EW-7711UAn v2 +.It +Foxconn WFU03 +.It +Tenda U2 +.It +Tenda W311MI v2 +.It +TP-LINK TL-WN727N v4 +.It +Yealink WF40 +.El +.Sh SEE ALSO +.Xr usb 4 +.Sh BUGS +The +.Nm +only works in station mode and monitor mode. The firmware does not always reinitialize when reloading the module, or when rebooting, without first unplugging the device. +.Sh History +The mtw driver first appeared in OpenBSD 7.1. The mtw driver was ported to FreeBSD in FreeBSD 15.0. +.Sh AUTHORS +.An -nosplit +The mtw driver was written by +.An James Hastings Aq Mt hastings@openbsd.org +ported to FreeBSD by +.An Jesper Schmitz Mouridsen Aq Mt jsm@FreeBSD.org diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs --- a/sys/dev/usb/usbdevs +++ b/sys/dev/usb/usbdevs @@ -1906,6 +1906,7 @@ product EDIMAX RTL8192SU_1 0x7611 RTL8192SU product EDIMAX RTL8192SU_2 0x7612 RTL8192SU product EDIMAX EW7622UMN 0x7622 EW-7622UMn +product EDIMAX MT7601U 0x7710 MT7601U product EDIMAX RT2870_1 0x7711 RT2870 product EDIMAX EW7717 0x7717 EW-7717 product EDIMAX EW7718 0x7718 EW-7718 @@ -4100,7 +4101,7 @@ product RALINK RT5370 0x5370 RT5370 product RALINK RT5372 0x5372 RT5372 product RALINK RT5572 0x5572 RT5572 -product RALINK RT7601 0x7601 RT7601 +product RALINK MT7601U 0x7601 MT7601 Mediatek Wireless Adpater product RALINK RT8070 0x8070 RT8070 product RALINK RT2570_3 0x9020 RT2500USB Wireless Adapter product RALINK RT2573_2 0x9021 RT2501USB Wireless Adapter diff --git a/sys/dev/usb/wlan/if_mtw.c b/sys/dev/usb/wlan/if_mtw.c new file mode 100644 --- /dev/null +++ b/sys/dev/usb/wlan/if_mtw.c @@ -0,0 +1,4672 @@ +/*- + * Copyright (c) 2008-2010 Damien Bergamini + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "usbdevs.h" + +#define USB_DEBUG_VAR mtw_debug +#include +#include + +#include "if_mtwreg.h" +#include "if_mtwvar.h" + +#define MTW_DEBUG + +#ifdef MTW_DEBUG +int mtw_debug; +static SYSCTL_NODE(_hw_usb, OID_AUTO, mtw, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, + "USB mtw"); +SYSCTL_INT(_hw_usb_mtw, OID_AUTO, debug, CTLFLAG_RWTUN, &mtw_debug, 0, + "mtw debug level"); + +enum { + MTW_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ + MTW_DEBUG_XMIT_DESC = 0x00000002, /* xmit descriptors */ + MTW_DEBUG_RECV = 0x00000004, /* basic recv operation */ + MTW_DEBUG_RECV_DESC = 0x00000008, /* recv descriptors */ + MTW_DEBUG_STATE = 0x00000010, /* 802.11 state transitions */ + MTW_DEBUG_RATE = 0x00000020, /* rate adaptation */ + MTW_DEBUG_USB = 0x00000040, /* usb requests */ + MTW_DEBUG_FIRMWARE = 0x00000080, /* firmware(9) loading debug */ + MTW_DEBUG_BEACON = 0x00000100, /* beacon handling */ + MTW_DEBUG_INTR = 0x00000200, /* ISR */ + MTW_DEBUG_TEMP = 0x00000400, /* temperature calibration */ + MTW_DEBUG_ROM = 0x00000800, /* various ROM info */ + MTW_DEBUG_KEY = 0x00001000, /* crypto keys management */ + MTW_DEBUG_TXPWR = 0x00002000, /* dump Tx power values */ + MTW_DEBUG_RSSI = 0x00004000, /* dump RSSI lookups */ + MTW_DEBUG_RESET = 0x00008000, /* initialization progress */ + MTW_DEBUG_CALIB = 0x00010000, /* calibration progress */ + MTW_DEBUG_CMD = 0x00020000, /* command queue */ + MTW_DEBUG_ANY = 0xffffffff +}; + +#define MTW_DPRINTF(_sc, _m, ...) \ + do { \ + if (mtw_debug & (_m)) \ + device_printf((_sc)->sc_dev, __VA_ARGS__); \ + } while (0) + +#else +#define MTW_DPRINTF(_sc, _m, ...) \ + do { \ + (void)_sc; \ + } while (0) +#endif + +#define IEEE80211_HAS_ADDR4(wh) IEEE80211_IS_DSTODS(wh) + +/* NB: "11" is the maximum number of padding bytes needed for Tx */ +#define MTW_MAX_TXSZ \ + (sizeof(struct mtw_txd) + sizeof(struct mtw_txwi) + MCLBYTES + 11) + +/* + * Because of LOR in mtw_key_delete(), use atomic instead. + * '& MTW_CMDQ_MASQ' is to loop cmdq[]. + */ +#define MTW_CMDQ_GET(c) (atomic_fetchadd_32((c), 1) & MTW_CMDQ_MASQ) + +static const STRUCT_USB_HOST_ID mtw_devs[] = { +#define MTW_DEV(v, p) \ + { \ + USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) \ + } + MTW_DEV(EDIMAX, MT7601U), + MTW_DEV(RALINK, MT7601U), + MTW_DEV(XIAOMI, MT7601U) +}; +#undef MTW_DEV + +static device_probe_t mtw_match; +static device_attach_t mtw_attach; +static device_detach_t mtw_detach; + +static usb_callback_t mtw_bulk_rx_callback; +static usb_callback_t mtw_bulk_tx_callback0; +static usb_callback_t mtw_bulk_tx_callback1; +static usb_callback_t mtw_bulk_tx_callback2; +static usb_callback_t mtw_bulk_tx_callback3; +static usb_callback_t mtw_bulk_tx_callback4; +static usb_callback_t mtw_bulk_tx_callback5; +static usb_callback_t mtw_fw_callback; + +static void mtw_autoinst(void *, struct usb_device *, struct usb_attach_arg *); +static int mtw_driver_loaded(struct module *, int, void *); +static void mtw_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error, + u_int index); +static struct ieee80211vap *mtw_vap_create(struct ieee80211com *, + const char[IFNAMSIZ], int, enum ieee80211_opmode, int, + const uint8_t[IEEE80211_ADDR_LEN], const uint8_t[IEEE80211_ADDR_LEN]); +static void mtw_vap_delete(struct ieee80211vap *); +static void mtw_cmdq_cb(void *, int); +static void mtw_setup_tx_list(struct mtw_softc *, struct mtw_endpoint_queue *); +static void mtw_unsetup_tx_list(struct mtw_softc *, + struct mtw_endpoint_queue *); +static void mtw_load_microcode(void *arg); + +static usb_error_t mtw_do_request(struct mtw_softc *, + struct usb_device_request *, void *); +static int mtw_read(struct mtw_softc *, uint16_t, uint32_t *); +static int mtw_read_region_1(struct mtw_softc *, uint16_t, uint8_t *, int); +static int mtw_write_2(struct mtw_softc *, uint16_t, uint16_t); +static int mtw_write(struct mtw_softc *, uint16_t, uint32_t); +static int mtw_write_region_1(struct mtw_softc *, uint16_t, uint8_t *, int); +static int mtw_set_region_4(struct mtw_softc *, uint16_t, uint32_t, int); +static int mtw_efuse_read_2(struct mtw_softc *, uint16_t, uint16_t *); +static int mtw_bbp_read(struct mtw_softc *, uint8_t, uint8_t *); +static int mtw_bbp_write(struct mtw_softc *, uint8_t, uint8_t); +static int mtw_mcu_cmd(struct mtw_softc *sc, uint8_t cmd, void *buf, int len); +static void mtw_get_txpower(struct mtw_softc *); +static int mtw_read_eeprom(struct mtw_softc *); +static struct ieee80211_node *mtw_node_alloc(struct ieee80211vap *, + const uint8_t mac[IEEE80211_ADDR_LEN]); +static int mtw_media_change(if_t); +static int mtw_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static int mtw_wme_update(struct ieee80211com *); +static void mtw_key_set_cb(void *); +static int mtw_key_set(struct ieee80211vap *, struct ieee80211_key *); +static void mtw_key_delete_cb(void *); +static int mtw_key_delete(struct ieee80211vap *, struct ieee80211_key *); +static void mtw_ratectl_to(void *); +static void mtw_ratectl_cb(void *, int); +static void mtw_drain_fifo(void *); +static void mtw_iter_func(void *, struct ieee80211_node *); +static void mtw_newassoc_cb(void *); +static void mtw_newassoc(struct ieee80211_node *, int); +static int mtw_mcu_radio(struct mtw_softc *sc, int func, uint32_t val); +static void mtw_recv_mgmt(struct ieee80211_node *, struct mbuf *, int, + const struct ieee80211_rx_stats *, int, int); +static void mtw_rx_frame(struct mtw_softc *, struct mbuf *, uint32_t); +static void mtw_tx_free(struct mtw_endpoint_queue *pq, struct mtw_tx_data *, + int); +static void mtw_set_tx_desc(struct mtw_softc *, struct mtw_tx_data *); +static int mtw_tx(struct mtw_softc *, struct mbuf *, struct ieee80211_node *); +static int mtw_tx_mgt(struct mtw_softc *, struct mbuf *, + struct ieee80211_node *); +static int mtw_sendprot(struct mtw_softc *, const struct mbuf *, + struct ieee80211_node *, int, int); +static int mtw_tx_param(struct mtw_softc *, struct mbuf *, + struct ieee80211_node *, const struct ieee80211_bpf_params *); +static int mtw_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static int mtw_transmit(struct ieee80211com *, struct mbuf *); +static void mtw_start(struct mtw_softc *); +static void mtw_parent(struct ieee80211com *); +static void mtw_select_chan_group(struct mtw_softc *, int); + +static int mtw_set_chan(struct mtw_softc *, struct ieee80211_channel *); +static void mtw_set_channel(struct ieee80211com *); +static void mtw_getradiocaps(struct ieee80211com *, int, int *, + struct ieee80211_channel[]); +static void mtw_scan_start(struct ieee80211com *); +static void mtw_scan_end(struct ieee80211com *); +static void mtw_update_beacon(struct ieee80211vap *, int); +static void mtw_update_beacon_cb(void *); +static void mtw_updateprot(struct ieee80211com *); +static void mtw_updateprot_cb(void *); +static void mtw_usb_timeout_cb(void *); +static int mtw_reset(struct mtw_softc *sc); +static void mtw_enable_tsf_sync(struct mtw_softc *); + + +static void mtw_enable_mrr(struct mtw_softc *); +static void mtw_set_txpreamble(struct mtw_softc *); +static void mtw_set_basicrates(struct mtw_softc *); +static void mtw_set_leds(struct mtw_softc *, uint16_t); +static void mtw_set_bssid(struct mtw_softc *, const uint8_t *); +static void mtw_set_macaddr(struct mtw_softc *, const uint8_t *); +static void mtw_updateslot(struct ieee80211com *); +static void mtw_updateslot_cb(void *); +static void mtw_update_mcast(struct ieee80211com *); +static int8_t mtw_rssi2dbm(struct mtw_softc *, uint8_t, uint8_t); +static void mtw_update_promisc_locked(struct mtw_softc *); +static void mtw_update_promisc(struct ieee80211com *); +static int mtw_txrx_enable(struct mtw_softc *); +static void mtw_init_locked(struct mtw_softc *); +static void mtw_stop(void *); +static void mtw_delay(struct mtw_softc *, u_int); +static void mtw_update_chw(struct ieee80211com *ic); +static int mtw_ampdu_enable(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap); + +static eventhandler_tag mtw_etag; + +static const struct { + uint8_t reg; + uint8_t val; +} mt7601_rf_bank0[] = { MT7601_BANK0_RF }, + mt7601_rf_bank4[] = { MT7601_BANK4_RF }, + mt7601_rf_bank5[] = { MT7601_BANK5_RF }; +static const struct { + uint32_t reg; + uint32_t val; +} mt7601_def_mac[] = { MT7601_DEF_MAC }; +static const struct { + uint8_t reg; + uint8_t val; +} mt7601_def_bbp[] = { MT7601_DEF_BBP }; + + +static const struct { + u_int chan; + uint8_t r17, r18, r19, r20; +} mt7601_rf_chan[] = { MT7601_RF_CHAN }; + + +static const struct usb_config mtw_config[MTW_N_XFER] = { + [MTW_BULK_RX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = MTW_MAX_RXSZ, + .flags = {.pipe_bof = 1, + .short_xfer_ok = 1,}, + .callback = mtw_bulk_rx_callback, + }, + [MTW_BULK_TX_BE] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = MTW_MAX_TXSZ, + .flags = {.pipe_bof = 1, + .force_short_xfer = 0,}, + .callback = mtw_bulk_tx_callback0, + .timeout = 5000, /* ms */ + }, + [MTW_BULK_TX_BK] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = MTW_MAX_TXSZ, + .flags = {.pipe_bof = 1, + .force_short_xfer = 1,}, + .callback = mtw_bulk_tx_callback1, + .timeout = 5000, /* ms */ + }, + [MTW_BULK_TX_VI] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = MTW_MAX_TXSZ, + .flags = {.pipe_bof = 1, + .force_short_xfer = 1,}, + .callback = mtw_bulk_tx_callback2, + .timeout = 5000, /* ms */ + }, + [MTW_BULK_TX_VO] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = MTW_MAX_TXSZ, + .flags = {.pipe_bof = 1, + .force_short_xfer = 1,}, + .callback = mtw_bulk_tx_callback3, + .timeout = 5000, /* ms */ + }, + [MTW_BULK_TX_HCCA] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = MTW_MAX_TXSZ, + .flags = {.pipe_bof = 1, + .force_short_xfer = 1, .no_pipe_ok = 1,}, + .callback = mtw_bulk_tx_callback4, + .timeout = 5000, /* ms */ + }, + [MTW_BULK_TX_PRIO] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = MTW_MAX_TXSZ, + .flags = {.pipe_bof = 1, + .force_short_xfer = 1, .no_pipe_ok = 1,}, + .callback = mtw_bulk_tx_callback5, + .timeout = 5000, /* ms */ + }, + + [MTW_BULK_FW_CMD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = MTW_MAX_TXSZ, + .flags = {.pipe_bof = 1, + .force_short_xfer = 1, .no_pipe_ok = 1,}, + .callback = mtw_fw_callback, + + }, + + [MTW_BULK_RAW_TX] = { + .type = UE_BULK, + .ep_index = 0, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = MTW_MAX_TXSZ, + .flags = {.pipe_bof = 1, + .force_short_xfer = 1, .no_pipe_ok = 1,}, + .callback = mtw_bulk_tx_callback0, + .timeout = 5000, /* ms */ + }, + +}; +static uint8_t mtw_wme_ac_xfer_map[4] = { + [WME_AC_BE] = MTW_BULK_TX_BE, + [WME_AC_BK] = MTW_BULK_TX_BK, + [WME_AC_VI] = MTW_BULK_TX_VI, + [WME_AC_VO] = MTW_BULK_TX_VO, +}; +static void +mtw_autoinst(void *arg, struct usb_device *udev, struct usb_attach_arg *uaa) +{ + struct usb_interface *iface; + struct usb_interface_descriptor *id; + + if (uaa->dev_state != UAA_DEV_READY) + return; + + iface = usbd_get_iface(udev, 0); + if (iface == NULL) + return; + id = iface->idesc; + if (id == NULL || id->bInterfaceClass != UICLASS_MASS) + return; + if (usbd_lookup_id_by_uaa(mtw_devs, sizeof(mtw_devs), uaa)) + return; + + if (usb_msc_eject(udev, 0, MSC_EJECT_STOPUNIT) == 0) + uaa->dev_state = UAA_DEV_EJECTING; +} + +static int +mtw_driver_loaded(struct module *mod, int what, void *arg) +{ + switch (what) { + case MOD_LOAD: + mtw_etag = EVENTHANDLER_REGISTER(usb_dev_configured, + mtw_autoinst, NULL, EVENTHANDLER_PRI_ANY); + break; + case MOD_UNLOAD: + EVENTHANDLER_DEREGISTER(usb_dev_configured, mtw_etag); + break; + default: + return (EOPNOTSUPP); + } + return (0); +} + +static const char * +mtw_get_rf(int rev) +{ + switch (rev) { + case MT7601_RF_7601: + return ("MT7601"); + case MT7610_RF_7610: + return ("MT7610"); + case MT7612_RF_7612: + return ("MT7612"); + } + return ("unknown"); +} +static int +mtw_wlan_enable(struct mtw_softc *sc, int enable) +{ + uint32_t tmp; + int error = 0; + + if (enable) { + mtw_read(sc, MTW_WLAN_CTRL, &tmp); + if (sc->asic_ver == 0x7612) + tmp &= ~0xfffff000; + + tmp &= ~MTW_WLAN_CLK_EN; + tmp |= MTW_WLAN_EN; + mtw_write(sc, MTW_WLAN_CTRL, tmp); + mtw_delay(sc, 2); + + tmp |= MTW_WLAN_CLK_EN; + if (sc->asic_ver == 0x7612) { + tmp |= (MTW_WLAN_RESET | MTW_WLAN_RESET_RF); + } + mtw_write(sc, MTW_WLAN_CTRL, tmp); + mtw_delay(sc, 2); + + mtw_read(sc, MTW_OSC_CTRL, &tmp); + tmp |= MTW_OSC_EN; + mtw_write(sc, MTW_OSC_CTRL, tmp); + tmp |= MTW_OSC_CAL_REQ; + mtw_write(sc, MTW_OSC_CTRL, tmp); + } else { + mtw_read(sc, MTW_WLAN_CTRL, &tmp); + tmp &= ~(MTW_WLAN_CLK_EN | MTW_WLAN_EN); + mtw_write(sc, MTW_WLAN_CTRL, tmp); + + mtw_read(sc, MTW_OSC_CTRL, &tmp); + tmp &= ~MTW_OSC_EN; + mtw_write(sc, MTW_OSC_CTRL, tmp); + } + return (error); +} + +static int +mtw_read_cfg(struct mtw_softc *sc, uint16_t reg, uint32_t *val) +{ + usb_device_request_t req; + uint32_t tmp; + uint16_t actlen; + int error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = MTW_READ_CFG; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, 4); + error = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, &tmp, 0, + &actlen, 1000); + + if (error == 0) + *val = le32toh(tmp); + else + *val = 0xffffffff; + return (error); +} + +static int +mtw_match(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != 0) + return (ENXIO); + if (uaa->info.bIfaceIndex != 0) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(mtw_devs, sizeof(mtw_devs), uaa)); +} + +static int +mtw_attach(device_t self) +{ + struct mtw_softc *sc = device_get_softc(self); + struct usb_attach_arg *uaa = device_get_ivars(self); + struct ieee80211com *ic = &sc->sc_ic; + uint32_t ver; + int i, ret; + // uint32_t tmp; + uint8_t iface_index; + int ntries, error; + + device_set_usb_desc(self); + sc->sc_udev = uaa->device; + sc->sc_dev = self; + sc->sc_sent = 0; + + mtx_init(&sc->sc_mtx, + device_get_nameunit(sc->sc_dev), + MTX_NETWORK_LOCK, MTX_DEF); + + iface_index = 0; + + error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, + mtw_config, MTW_N_XFER, sc, &sc->sc_mtx); + if (error) { + device_printf(sc->sc_dev, + "could not allocate USB transfers, " + "err=%s\n", + usbd_errstr(error)); + goto detach; + } + for (i = 0; i < 7; i++) { + sc->txd_fw[i] = (struct mtw_txd_fw *) + malloc(sizeof(struct mtw_txd_fw), + M_USBDEV, M_NOWAIT | M_ZERO); + } + MTW_LOCK(sc); + sc->sc_idx = 0; + mbufq_init(&sc->sc_snd, ifqmaxlen); + + /*enable WLAN core */ + if ((error = mtw_wlan_enable(sc, 1)) != 0) { + device_printf(sc->sc_dev, "could not enable WLAN core\n"); + return (ENXIO); + } + + /* wait for the chip to settle */ + DELAY(100); + for (ntries = 0; ntries < 100; ntries++) { + if (mtw_read(sc, MTW_ASIC_VER, &ver) != 0) { + goto detach; + } + if (ver != 0 && ver != 0xffffffff) + break; + DELAY(10); + } + if (ntries == 100) { + device_printf(sc->sc_dev, + "timeout waiting for NIC to initialize\n"); + goto detach; + } + sc->asic_ver = ver >> 16; + sc->asic_rev = ver & 0xffff; + DELAY(100); + if (sc->asic_ver != 0x7601) { + device_printf(sc->sc_dev, + "Your revision 0x04%x is not supported yet\n", + sc->asic_rev); + goto detach; + } + + mtw_load_microcode(sc); + ret = msleep(&sc->fwloading, &sc->sc_mtx, 0, "fwload", 3 * hz); + if (ret == EWOULDBLOCK || sc->fwloading != 1) { + device_printf(sc->sc_dev, + "timeout waiting for MCU to initialize\n"); + goto detach; + } + + sc->sc_srom_read = mtw_efuse_read_2; + /* retrieve RF rev. no and various other things from EEPROM */ + mtw_read_eeprom(sc); + + device_printf(sc->sc_dev, + "MAC/BBP RT%04X (rev 0x%04X), RF %s (MIMO %dT%dR), address %s\n", + sc->asic_ver, sc->mac_rev, mtw_get_rf(sc->rf_rev), sc->ntxchains, + sc->nrxchains, ether_sprintf(ic->ic_macaddr)); + DELAY(100); + + //mtw_set_leds(sc,5); + // mtw_mcu_radio(sc,0x31,0); + MTW_UNLOCK(sc); + + + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(self); + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ + + ic->ic_caps = IEEE80211_C_STA | /* station mode supported */ + IEEE80211_C_MONITOR | /* monitor mode supported */ + IEEE80211_C_IBSS | + IEEE80211_C_HOSTAP | + IEEE80211_C_WDS | /* 4-address traffic works */ + IEEE80211_C_MBSS | + IEEE80211_C_SHPREAMBLE | /* short preamble supported */ + IEEE80211_C_SHSLOT | /* short slot time supported */ + IEEE80211_C_WME | /* WME */ + IEEE80211_C_WPA; /* WPA1|WPA2(RSN) */ + device_printf(sc->sc_dev, "[HT] Enabling 802.11n\n"); + ic->ic_htcaps = IEEE80211_HTC_HT + | IEEE80211_HTC_AMPDU + | IEEE80211_HTC_AMSDU + | IEEE80211_HTCAP_MAXAMSDU_3839 + | IEEE80211_HTCAP_SMPS_OFF; + + ic->ic_rxstream = sc->nrxchains; + ic->ic_txstream = sc->ntxchains; + + ic->ic_cryptocaps = IEEE80211_CRYPTO_WEP | IEEE80211_CRYPTO_AES_CCM | + IEEE80211_CRYPTO_AES_OCB | IEEE80211_CRYPTO_TKIP | + IEEE80211_CRYPTO_TKIPMIC; + + ic->ic_flags |= IEEE80211_F_DATAPAD; + ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS; + + mtw_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, + ic->ic_channels); + + ieee80211_ifattach(ic); + + ic->ic_scan_start = mtw_scan_start; + ic->ic_scan_end = mtw_scan_end; + ic->ic_set_channel = mtw_set_channel; + ic->ic_getradiocaps = mtw_getradiocaps; + ic->ic_node_alloc = mtw_node_alloc; + ic->ic_newassoc = mtw_newassoc; + ic->ic_update_mcast = mtw_update_mcast; + ic->ic_updateslot = mtw_updateslot; + ic->ic_wme.wme_update = mtw_wme_update; + ic->ic_raw_xmit = mtw_raw_xmit; + ic->ic_update_promisc = mtw_update_promisc; + ic->ic_vap_create = mtw_vap_create; + ic->ic_vap_delete = mtw_vap_delete; + ic->ic_transmit = mtw_transmit; + ic->ic_parent = mtw_parent; + + ic->ic_update_chw = mtw_update_chw; + ic->ic_ampdu_enable = mtw_ampdu_enable; + + ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, + sizeof(sc->sc_txtap), MTW_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + MTW_RX_RADIOTAP_PRESENT); + TASK_INIT(&sc->cmdq_task, 0, mtw_cmdq_cb, sc); + TASK_INIT(&sc->ratectl_task, 0, mtw_ratectl_cb, sc); + usb_callout_init_mtx(&sc->ratectl_ch, &sc->sc_mtx, 0); + + if (bootverbose) + ieee80211_announce(ic); + + return (0); + +detach: + MTW_UNLOCK(sc); + mtw_detach(self); + return (ENXIO); +} + +static void +mtw_drain_mbufq(struct mtw_softc *sc) +{ + struct mbuf *m; + struct ieee80211_node *ni; + + MTW_LOCK_ASSERT(sc, MA_OWNED); + while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + m->m_pkthdr.rcvif = NULL; + ieee80211_free_node(ni); + m_freem(m); + } +} + +static int +mtw_detach(device_t self) +{ + struct mtw_softc *sc = device_get_softc(self); + struct ieee80211com *ic = &sc->sc_ic; + int i; + MTW_LOCK(sc); + mtw_reset(sc); + DELAY(10000); + sc->sc_detached = 1; + MTW_UNLOCK(sc); + + + /* stop all USB transfers */ + for (i = 0; i < MTW_N_XFER; i++) + usbd_transfer_drain(sc->sc_xfer[i]); + + MTW_LOCK(sc); + sc->ratectl_run = MTW_RATECTL_OFF; + sc->cmdq_run = sc->cmdq_key_set = MTW_CMDQ_ABORT; + + /* free TX list, if any */ + for (i = 1; i < 7; i++) + mtw_unsetup_tx_list(sc, &sc->sc_epq[i]); + + /* Free TX queue */ + mtw_drain_mbufq(sc); + MTW_UNLOCK(sc); + if (sc->sc_ic.ic_softc == sc) { + /* dr_ain tasks */ + usb_callout_drain(&sc->ratectl_ch); + ieee80211_draintask(ic, &sc->cmdq_task); + ieee80211_draintask(ic, &sc->ratectl_task); + ieee80211_ifdetach(ic); + } + for (i = 0; i < 7; i++) { + free(sc->txd_fw[i], M_USBDEV); + } + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static struct ieee80211vap * +mtw_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, + enum ieee80211_opmode opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct mtw_softc *sc = ic->ic_softc; + struct mtw_vap *rvp; + struct ieee80211vap *vap; + int i; + + if (sc->rvp_cnt >= MTW_VAP_MAX) { + device_printf(sc->sc_dev, "number of VAPs maxed out\n"); + return (NULL); + } + + switch (opmode) { + case IEEE80211_M_STA: + /* enable s/w bmiss handling for sta mode */ + flags |= IEEE80211_CLONE_NOBEACONS; + /* fall though */ + case IEEE80211_M_IBSS: + case IEEE80211_M_MONITOR: + case IEEE80211_M_HOSTAP: + case IEEE80211_M_MBSS: + /* other than WDS vaps, only one at a time */ + if (!TAILQ_EMPTY(&ic->ic_vaps)) + return (NULL); + break; + case IEEE80211_M_WDS: + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + if (vap->iv_opmode != IEEE80211_M_HOSTAP) + continue; + /* WDS vap's always share the local mac address. */ + flags &= ~IEEE80211_CLONE_BSSID; + break; + } + if (vap == NULL) { + device_printf(sc->sc_dev, + "wds only supported in ap mode\n"); + return (NULL); + } + break; + default: + device_printf(sc->sc_dev, "unknown opmode %d\n", opmode); + return (NULL); + } + + rvp = malloc(sizeof(struct mtw_vap), M_80211_VAP, M_WAITOK | M_ZERO); + vap = &rvp->vap; + + if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid) != + 0) { + /* out of memory */ + free(rvp, M_80211_VAP); + return (NULL); + } + + vap->iv_update_beacon = mtw_update_beacon; + vap->iv_max_aid = MTW_WCID_MAX; + + /* + * The linux rt2800 driver limits 1 stream devices to a 32KB + * RX AMPDU. + */ + if (ic->ic_rxstream > 1) + vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K; + else + vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K; + vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_2; /* 2uS */ + + /* + * To delete the right key from h/w, we need wcid. + * Luckily, there is unused space in ieee80211_key{}, wk_pad, + * and matching wcid will be written into there. So, cast + * some spells to remove 'const' from ieee80211_key{} + */ + vap->iv_key_delete = (void *)mtw_key_delete; + vap->iv_key_set = (void *)mtw_key_set; + + // override state transition machine + rvp->newstate = vap->iv_newstate; + vap->iv_newstate = mtw_newstate; + if (opmode == IEEE80211_M_IBSS) { + rvp->recv_mgmt = vap->iv_recv_mgmt; + vap->iv_recv_mgmt = mtw_recv_mgmt; + } + + ieee80211_ratectl_init(vap); + ieee80211_ratectl_setinterval(vap, 1000); // 1 second + + /* complete setup */ + ieee80211_vap_attach(vap, mtw_media_change, ieee80211_media_status, + mac); + + /* make sure id is always unique */ + for (i = 0; i < MTW_VAP_MAX; i++) { + if ((sc->rvp_bmap & 1 << i) == 0) { + sc->rvp_bmap |= 1 << i; + rvp->rvp_id = i; + break; + } + } + if (sc->rvp_cnt++ == 0) + ic->ic_opmode = opmode; + + if (opmode == IEEE80211_M_HOSTAP) + sc->cmdq_run = MTW_CMDQ_GO; + + MTW_DPRINTF(sc, MTW_DEBUG_STATE, "rvp_id=%d bmap=%x rvp_cnt=%d\n", + rvp->rvp_id, sc->rvp_bmap, sc->rvp_cnt); + + return (vap); +} + +static void +mtw_vap_delete(struct ieee80211vap *vap) +{ + struct mtw_vap *rvp = MTW_VAP(vap); + struct ieee80211com *ic; + struct mtw_softc *sc; + uint8_t rvp_id; + + if (vap == NULL) + return; + + ic = vap->iv_ic; + sc = ic->ic_softc; + + MTW_LOCK(sc); + m_freem(rvp->beacon_mbuf); + rvp->beacon_mbuf = NULL; + + rvp_id = rvp->rvp_id; + sc->ratectl_run &= ~(1 << rvp_id); + sc->rvp_bmap &= ~(1 << rvp_id); + mtw_set_region_4(sc, MTW_SKEY(rvp_id, 0), 0, 256); + mtw_set_region_4(sc, (0x7800 + (rvp_id) * 512), 0, 512); + --sc->rvp_cnt; + + MTW_DPRINTF(sc, MTW_DEBUG_STATE, + "vap=%p rvp_id=%d bmap=%x rvp_cnt=%d\n", vap, rvp_id, sc->rvp_bmap, + sc->rvp_cnt); + + MTW_UNLOCK(sc); + + ieee80211_ratectl_deinit(vap); + ieee80211_vap_detach(vap); + free(rvp, M_80211_VAP); +} + +/* + * There are numbers of functions need to be called in context thread. + * Rather than creating taskqueue event for each of those functions, + * here is all-for-one taskqueue callback function. This function + * guarantees deferred functions are executed in the same order they + * were enqueued. + * '& MTW_CMDQ_MASQ' is to loop cmdq[]. + */ +static void +mtw_cmdq_cb(void *arg, int pending) +{ + struct mtw_softc *sc = arg; + uint8_t i; + /* call cmdq[].func locked */ + MTW_LOCK(sc); + for (i = sc->cmdq_exec; sc->cmdq[i].func && pending; + i = sc->cmdq_exec, pending--) { + MTW_DPRINTF(sc, MTW_DEBUG_CMD, "cmdq_exec=%d pending=%d\n", i, + pending); + if (sc->cmdq_run == MTW_CMDQ_GO) { + /* + * If arg0 is NULL, callback func needs more + * than one arg. So, pass ptr to cmdq struct. + */ + if (sc->cmdq[i].arg0) + sc->cmdq[i].func(sc->cmdq[i].arg0); + else + sc->cmdq[i].func(&sc->cmdq[i]); + } + sc->cmdq[i].arg0 = NULL; + sc->cmdq[i].func = NULL; + sc->cmdq_exec++; + sc->cmdq_exec &= MTW_CMDQ_MASQ; + } + MTW_UNLOCK(sc); +} + +static void +mtw_setup_tx_list(struct mtw_softc *sc, struct mtw_endpoint_queue *pq) +{ + struct mtw_tx_data *data; + + memset(pq, 0, sizeof(*pq)); + + STAILQ_INIT(&pq->tx_qh); + STAILQ_INIT(&pq->tx_fh); + + for (data = &pq->tx_data[0]; data < &pq->tx_data[MTW_TX_RING_COUNT]; + data++) { + data->sc = sc; + STAILQ_INSERT_TAIL(&pq->tx_fh, data, next); + } + pq->tx_nfree = MTW_TX_RING_COUNT; +} + +static void +mtw_unsetup_tx_list(struct mtw_softc *sc, struct mtw_endpoint_queue *pq) +{ + struct mtw_tx_data *data; + /* make sure any subsequent use of the queues will fail */ + pq->tx_nfree = 0; + + STAILQ_INIT(&pq->tx_fh); + STAILQ_INIT(&pq->tx_qh); + + /* free up all node references and mbufs */ + for (data = &pq->tx_data[0]; data < &pq->tx_data[MTW_TX_RING_COUNT]; + data++) { + if (data->m != NULL) { + m_freem(data->m); + data->m = NULL; + } + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; + } + } +} + +static int +mtw_write_ivb(struct mtw_softc *sc, void *buf, uint16_t len) +{ + usb_device_request_t req; + uint16_t actlen; + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = MTW_RESET; + USETW(req.wValue, 0x12); + USETW(req.wIndex, 0); + USETW(req.wLength, len); + + int error = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, buf, + 0, &actlen, 1000); + + return (error); +} + +static int +mtw_write_cfg(struct mtw_softc *sc, uint16_t reg, uint32_t val) +{ + usb_device_request_t req; + int error; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = MTW_WRITE_CFG; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, 4); + val = htole32(val); + error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, &val); + return (error); +} + +static int +mtw_usb_dma_write(struct mtw_softc *sc, uint32_t val) +{ + // if (sc->asic_ver == 0x7612) + // return mtw_write_cfg(sc, MTW_USB_U3DMA_CFG, val); + // else + return (mtw_write(sc, MTW_USB_DMA_CFG, val)); +} + +static void +mtw_ucode_setup(struct mtw_softc *sc) +{ + + mtw_usb_dma_write(sc, (MTW_USB_TX_EN | MTW_USB_RX_EN)); + mtw_write(sc, MTW_FCE_PSE_CTRL, 1); + mtw_write(sc, MTW_TX_CPU_FCE_BASE, 0x400230); + mtw_write(sc, MTW_TX_CPU_FCE_MAX_COUNT, 1); + mtw_write(sc, MTW_MCU_FW_IDX, 1); + mtw_write(sc, MTW_FCE_PDMA, 0x44); + mtw_write(sc, MTW_FCE_SKIP_FS, 3); +} +static int +mtw_ucode_write(struct mtw_softc *sc, const uint8_t *fw, const uint8_t *ivb, + int32_t len, uint32_t offset) +{ + + // struct usb_attach_arg *uaa = device_get_ivars(sc->sc_dev); +#if 0 // firmware not tested + + if (sc->asic_ver == 0x7612 && offset >= 0x90000) + blksz = 0x800; /* MT7612 ROM Patch */ + + xfer = usbd_alloc_xfer(sc->sc_udev); + if (xfer == NULL) { + error = ENOMEM; + goto fail; + } + buf = usbd_alloc_buffer(xfer, blksz + 12); + if (buf == NULL) { + error = ENOMEM; + goto fail; + } +#endif + + + + int mlen; + int idx = 0; + + mlen = 0x2000; + + while (len > 0) { + + if (len < 0x2000 && len > 0) { + mlen = len; + } + + sc->txd_fw[idx]->len = htole16(mlen); + sc->txd_fw[idx]->flags = htole16(MTW_TXD_DATA | MTW_TXD_MCU); + + memcpy(&sc->txd_fw[idx]->fw, fw, mlen); + // memcpy(&txd[1], fw, mlen); + // memset(&txd[1] + mlen, 0, MTW_DMA_PAD); + // mtw_write_cfg(sc, MTW_MCU_DMA_ADDR, offset + //+sent); 1mtw_write_cfg(sc, MTW_MCU_DMA_LEN, (mlen << 16)); + + // sc->sc_fw_data[idx]->len=htole16(mlen); + + // memcpy(tmpbuf,fw,mlen); + // memset(tmpbuf+mlen,0,MTW_DMA_PAD); + // memcpy(sc->sc_fw_data[idx].buf, fw, mlen); + + fw += mlen; + len -= mlen; + // sent+=mlen; + idx++; + } + sc->sc_sent = 0; + memcpy(sc->sc_ivb_1, ivb, MTW_MCU_IVB_LEN); + + usbd_transfer_start(sc->sc_xfer[7]); + + return (0); +} + +static void +mtw_load_microcode(void *arg) +{ + + struct mtw_softc *sc = (struct mtw_softc *)arg; + const struct mtw_ucode_hdr *hdr; + // onst struct mtw_ucode *fw = NULL; + const char *fwname; + size_t size; + int error = 0; + uint32_t tmp, iofs = 0x40; + // int ntries; + int dlen, ilen; + device_printf(sc->sc_dev, "version:0x%hx\n", sc->asic_ver); + /* is firmware already running? */ + mtw_read_cfg(sc, MTW_MCU_DMA_ADDR, &tmp); + if (tmp == MTW_MCU_READY) { + //return; + } + if (sc->asic_ver == 0x7612) { + fwname = "mtw-mt7662u_rom_patch"; + + const struct firmware *firmware = firmware_get(fwname); + if (firmware == NULL) { + device_printf(sc->sc_dev, + "failed loadfirmware of file %s (error %d)\n", + fwname, error); + return; + } + size = firmware->datasize; + + const struct mtw_ucode *fw = (const struct mtw_ucode *) + firmware->data; + hdr = (const struct mtw_ucode_hdr *)&fw->hdr; + // memcpy(fw,(const unsigned char*)firmware->data + + // 0x1e,size-0x1e); + ilen = size - 0x1e; + + mtw_ucode_setup(sc); + + if ((error = mtw_ucode_write(sc, firmware->data, fw->ivb, ilen, + 0x90000)) != 0) { + goto fail; + } + mtw_usb_dma_write(sc, 0x00e41814); + } + + fwname = "mt7601u_fw"; + iofs = 0x40; + // dofs = 0; + if (sc->asic_ver == 0x7612) { + fwname = "mtw-mt7662u"; + iofs = 0x80040; + // dofs = 0x110800; + } else if (sc->asic_ver == 0x7610) { + fwname = "mt7610u"; + // dofs = 0x80000; + } + MTW_UNLOCK(sc); + const struct firmware *firmware = firmware_get(fwname); + + if (firmware == NULL) { + device_printf(sc->sc_dev, + "failed loadfirmware of file %s (error %d)\n", fwname, + error); + MTW_LOCK(sc); + return; + } + MTW_LOCK(sc); + size = firmware->datasize; + MTW_DPRINTF(sc, MTW_DEBUG_FIRMWARE, "firmware size:%zu\n", size); + const struct mtw_ucode *fw = (const struct mtw_ucode *)firmware->data; + + if (size < sizeof(struct mtw_ucode_hdr)) { + device_printf(sc->sc_dev, "firmware header too short\n"); + goto fail; + } + + hdr = (const struct mtw_ucode_hdr *)&fw->hdr; + + if (size < sizeof(struct mtw_ucode_hdr) + le32toh(hdr->ilm_len) + + le32toh(hdr->dlm_len)) { + device_printf(sc->sc_dev, "firmware payload too short\n"); + goto fail; + } + + ilen = le32toh(hdr->ilm_len) - MTW_MCU_IVB_LEN; + dlen = le32toh(hdr->dlm_len); + + if (ilen > size || dlen > size) { + device_printf(sc->sc_dev, "firmware payload too large\n"); + goto fail; + } + + mtw_write(sc, MTW_FCE_PDMA, 0); + mtw_write(sc, MTW_FCE_PSE_CTRL, 0); + mtw_ucode_setup(sc); + + if ((error = mtw_ucode_write(sc, fw->data, fw->ivb, ilen, iofs)) != 0) + device_printf(sc->sc_dev, "Could not write ucode errro=%d\n", + error); + + device_printf(sc->sc_dev, "loaded firmware ver %d.%d\n", + le16toh(hdr->build_ver), le16toh(hdr->fw_ver)); + + return; +fail: + return; +} +static usb_error_t +mtw_do_request(struct mtw_softc *sc, struct usb_device_request *req, void *data) +{ + usb_error_t err; + int ntries = 5; + + MTW_LOCK_ASSERT(sc, MA_OWNED); + + while (ntries--) { + err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, req, data, + 0, NULL, 2000); // ms seconds + if (err == 0) + break; + MTW_DPRINTF(sc, MTW_DEBUG_USB, + "Control request failed, %s (retrying)\n", + usbd_errstr(err)); + mtw_delay(sc, 10); + } + return (err); +} + +static int +mtw_read(struct mtw_softc *sc, uint16_t reg, uint32_t *val) +{ + uint32_t tmp; + int error; + + error = mtw_read_region_1(sc, reg, (uint8_t *)&tmp, sizeof tmp); + if (error == 0) + *val = le32toh(tmp); + else + *val = 0xffffffff; + return (error); +} + +static int +mtw_read_region_1(struct mtw_softc *sc, uint16_t reg, uint8_t *buf, int len) +{ + usb_device_request_t req; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = MTW_READ_REGION_1; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + return (mtw_do_request(sc, &req, buf)); +} + +static int +mtw_write_2(struct mtw_softc *sc, uint16_t reg, uint16_t val) +{ + + usb_device_request_t req; + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = MTW_WRITE_2; + USETW(req.wValue, val); + USETW(req.wIndex, reg); + USETW(req.wLength, 0); + return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL)); +} + +static int +mtw_write(struct mtw_softc *sc, uint16_t reg, uint32_t val) +{ + + int error; + + if ((error = mtw_write_2(sc, reg, val & 0xffff)) == 0) { + + error = mtw_write_2(sc, reg + 2, val >> 16); + } + + return (error); +} + +static int +mtw_write_region_1(struct mtw_softc *sc, uint16_t reg, uint8_t *buf, int len) +{ + + usb_device_request_t req; + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = MTW_WRITE_REGION_1; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, buf)); +} + +static int +mtw_set_region_4(struct mtw_softc *sc, uint16_t reg, uint32_t val, int count) +{ + int i, error = 0; + + KASSERT((count & 3) == 0, ("mte_set_region_4: Invalid data length.\n")); + for (i = 0; i < count && error == 0; i += 4) + error = mtw_write(sc, reg + i, val); + return (error); +} + +static int +mtw_efuse_read_2(struct mtw_softc *sc, uint16_t addr, uint16_t *val) +{ + + uint32_t tmp; + uint16_t reg; + int error, ntries; + + if ((error = mtw_read(sc, MTW_EFUSE_CTRL, &tmp)) != 0) + return (error); + + addr *= 2; + /* + * Read one 16-byte block into registers EFUSE_DATA[0-3]: + * DATA0: 3 2 1 0 + * DATA1: 7 6 5 4 + * DATA2: B A 9 8 + * DATA3: F E D C + */ + tmp &= ~(MTW_EFSROM_MODE_MASK | MTW_EFSROM_AIN_MASK); + tmp |= (addr & ~0xf) << MTW_EFSROM_AIN_SHIFT | MTW_EFSROM_KICK; + mtw_write(sc, MTW_EFUSE_CTRL, tmp); + for (ntries = 0; ntries < 100; ntries++) { + if ((error = mtw_read(sc, MTW_EFUSE_CTRL, &tmp)) != 0) + return (error); + if (!(tmp & MTW_EFSROM_KICK)) + break; + DELAY(2); + } + if (ntries == 100) + return (ETIMEDOUT); + + if ((tmp & MTW_EFUSE_AOUT_MASK) == MTW_EFUSE_AOUT_MASK) { + *val = 0xffff; // address not found + return (0); + } +// determine to which 32-bit register our 16-bit word belongs + reg = MTW_EFUSE_DATA0 + (addr & 0xc); + if ((error = mtw_read(sc, reg, &tmp)) != 0) + return (error); + + *val = (addr & 2) ? tmp >> 16 : tmp & 0xffff; + return (0); +} + +static __inline int +mtw_srom_read(struct mtw_softc *sc, uint16_t addr, uint16_t *val) +{ + /* either eFUSE ROM or EEPROM */ + return (sc->sc_srom_read(sc, addr, val)); +} + +static int +mtw_bbp_read(struct mtw_softc *sc, uint8_t reg, uint8_t *val) +{ + uint32_t tmp; + int ntries, error; + + for (ntries = 0; ntries < 10; ntries++) { + if ((error = mtw_read(sc, MTW_BBP_CSR, &tmp)) != 0) + return (error); + if (!(tmp & MTW_BBP_CSR_KICK)) + break; + } + if (ntries == 10) + return (ETIMEDOUT); + + tmp = MTW_BBP_CSR_READ | MTW_BBP_CSR_KICK | reg << 8; + if ((error = mtw_write(sc, MTW_BBP_CSR, tmp)) != 0) + return (error); + + for (ntries = 0; ntries < 10; ntries++) { + if ((error = mtw_read(sc, MTW_BBP_CSR, &tmp)) != 0) + return (error); + if (!(tmp & MTW_BBP_CSR_KICK)) + break; + } + if (ntries == 10) + return (ETIMEDOUT); + + *val = tmp & 0xff; + return (0); +} + +static int +mtw_bbp_write(struct mtw_softc *sc, uint8_t reg, uint8_t val) +{ + uint32_t tmp; + int ntries, error; + + for (ntries = 0; ntries < 10; ntries++) { + if ((error = mtw_read(sc, MTW_BBP_CSR, &tmp)) != 0) + return (error); + if (!(tmp & MTW_BBP_CSR_KICK)) + break; + } + if (ntries == 10) + return (ETIMEDOUT); + + tmp = MTW_BBP_CSR_KICK | reg << 8 | val; + return (mtw_write(sc, MTW_BBP_CSR, tmp)); +} + +static int +mtw_mcu_cmd(struct mtw_softc *sc, u_int8_t cmd, void *buf, int len) +{ + sc->sc_idx = 0; + sc->txd_fw[sc->sc_idx]->len = htole16( + len + 8); + sc->txd_fw[sc->sc_idx]->flags = htole16(MTW_TXD_CMD | MTW_TXD_MCU | + (cmd & 0x1f) << MTW_TXD_CMD_SHIFT | (0 & 0xf)); + + memset(&sc->txd_fw[sc->sc_idx]->fw, 0, 2004); + memcpy(&sc->txd_fw[sc->sc_idx]->fw, buf, len); + usbd_transfer_start(sc->sc_xfer[7]); + return (0); +} + +/* + * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word. + * Used to adjust per-rate Tx power registers. + */ +static __inline uint32_t +b4inc(uint32_t b32, int8_t delta) +{ + int8_t i, b4; + + for (i = 0; i < 8; i++) { + b4 = b32 & 0xf; + b4 += delta; + if (b4 < 0) + b4 = 0; + else if (b4 > 0xf) + b4 = 0xf; + b32 = b32 >> 4 | b4 << 28; + } + return (b32); +} +static void +mtw_get_txpower(struct mtw_softc *sc) +{ + uint16_t val; + int i; + + /* Read power settings for 2GHz channels. */ + for (i = 0; i < 14; i += 2) { + mtw_srom_read(sc, MTW_EEPROM_PWR2GHZ_BASE1 + i / 2, &val); + sc->txpow1[i + 0] = (int8_t)(val & 0xff); + sc->txpow1[i + 1] = (int8_t)(val >> 8); + mtw_srom_read(sc, MTW_EEPROM_PWR2GHZ_BASE2 + i / 2, &val); + sc->txpow2[i + 0] = (int8_t)(val & 0xff); + sc->txpow2[i + 1] = (int8_t)(val >> 8); + } + /* Fix broken Tx power entries. */ + for (i = 0; i < 14; i++) { + if (sc->txpow1[i] < 0 || sc->txpow1[i] > 27) + sc->txpow1[i] = 5; + if (sc->txpow2[i] < 0 || sc->txpow2[i] > 27) + sc->txpow2[i] = 5; + MTW_DPRINTF(sc, MTW_DEBUG_TXPWR, + "chan %d: power1=%d, power2=%d\n", mt7601_rf_chan[i].chan, + sc->txpow1[i], sc->txpow2[i]); + } +} + +struct ieee80211_node * +mtw_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + return (malloc(sizeof(struct mtw_node), M_80211_NODE, + M_NOWAIT | M_ZERO)); +} +static int +mtw_read_eeprom(struct mtw_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + int8_t delta_2ghz, delta_5ghz; + uint16_t val; + int ridx, ant; + + sc->sc_srom_read = mtw_efuse_read_2; + + /* read RF information */ + mtw_srom_read(sc, MTW_EEPROM_CHIPID, &val); + sc->rf_rev = val; + mtw_srom_read(sc, MTW_EEPROM_ANTENNA, &val); + sc->ntxchains = (val >> 4) & 0xf; + sc->nrxchains = val & 0xf; + MTW_DPRINTF(sc, MTW_DEBUG_ROM, "EEPROM RF rev=0x%02x chains=%dT%dR\n", + sc->rf_rev, sc->ntxchains, sc->nrxchains); + + /* read ROM version */ + mtw_srom_read(sc, MTW_EEPROM_VERSION, &val); + MTW_DPRINTF(sc, MTW_DEBUG_ROM, "EEPROM rev=%d, FAE=%d\n", val & 0xff, + val >> 8); + + /* read MAC address */ + mtw_srom_read(sc, MTW_EEPROM_MAC01, &val); + ic->ic_macaddr[0] = val & 0xff; + ic->ic_macaddr[1] = val >> 8; + mtw_srom_read(sc, MTW_EEPROM_MAC23, &val); + ic->ic_macaddr[2] = val & 0xff; + ic->ic_macaddr[3] = val >> 8; + mtw_srom_read(sc, MTW_EEPROM_MAC45, &val); + ic->ic_macaddr[4] = val & 0xff; + ic->ic_macaddr[5] = val >> 8; +#if 0 + printf("eFUSE ROM\n00: "); + for (int i = 0; i < 256; i++) { + if (((i % 8) == 0) && i > 0) + printf("\n%02x: ", i); + mtw_srom_read(sc, i, &val); + printf(" %04x", val); + } + printf("\n"); +#endif + /* check if RF supports automatic Tx access gain control */ + mtw_srom_read(sc, MTW_EEPROM_CONFIG, &val); + device_printf(sc->sc_dev, "EEPROM CFG 0x%04x\n", val); + if ((val & 0xff) != 0xff) { + sc->ext_5ghz_lna = (val >> 3) & 1; + sc->ext_2ghz_lna = (val >> 2) & 1; + /* check if RF supports automatic Tx access gain control */ + sc->calib_2ghz = sc->calib_5ghz = (val >> 1) & 1; + /* check if we have a hardware radio switch */ + sc->rfswitch = val & 1; + } + + /* read RF frequency offset from EEPROM */ + mtw_srom_read(sc, MTW_EEPROM_FREQ_OFFSET, &val); + if ((val & 0xff) != 0xff) + sc->rf_freq_offset = val; + else + sc->rf_freq_offset = 0; + MTW_DPRINTF(sc, MTW_DEBUG_ROM, "frequency offset 0x%x\n", + sc->rf_freq_offset); + + /* Read Tx power settings. */ + mtw_get_txpower(sc); + + /* read Tx power compensation for each Tx rate */ + mtw_srom_read(sc, MTW_EEPROM_DELTAPWR, &val); + delta_2ghz = delta_5ghz = 0; + if ((val & 0xff) != 0xff && (val & 0x80)) { + delta_2ghz = val & 0xf; + if (!(val & 0x40)) /* negative number */ + delta_2ghz = -delta_2ghz; + } + val >>= 8; + if ((val & 0xff) != 0xff && (val & 0x80)) { + delta_5ghz = val & 0xf; + if (!(val & 0x40)) /* negative number */ + delta_5ghz = -delta_5ghz; + } + MTW_DPRINTF(sc, MTW_DEBUG_ROM | MTW_DEBUG_TXPWR, + "power compensation=%d (2GHz), %d (5GHz)\n", delta_2ghz, + delta_5ghz); + + for (ridx = 0; ridx < 5; ridx++) { + uint32_t reg; + + mtw_srom_read(sc, MTW_EEPROM_RPWR + ridx * 2, &val); + reg = val; + mtw_srom_read(sc, MTW_EEPROM_RPWR + ridx * 2 + 1, &val); + reg |= (uint32_t)val << 16; + + sc->txpow20mhz[ridx] = reg; + sc->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz); + sc->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz); + + MTW_DPRINTF(sc, MTW_DEBUG_ROM | MTW_DEBUG_TXPWR, + "ridx %d: power 20MHz=0x%08x, 40MHz/2GHz=0x%08x, " + "40MHz/5GHz=0x%08x\n", + ridx, sc->txpow20mhz[ridx], sc->txpow40mhz_2ghz[ridx], + sc->txpow40mhz_5ghz[ridx]); + } + + /* read RSSI offsets and LNA gains from EEPROM */ + val = 0; + mtw_srom_read(sc, MTW_EEPROM_RSSI1_2GHZ, &val); + sc->rssi_2ghz[0] = val & 0xff; /* Ant A */ + sc->rssi_2ghz[1] = val >> 8; /* Ant B */ + mtw_srom_read(sc, MTW_EEPROM_RSSI2_2GHZ, &val); + /* + * On RT3070 chips (limited to 2 Rx chains), this ROM + * field contains the Tx mixer gain for the 2GHz band. + */ + if ((val & 0xff) != 0xff) + sc->txmixgain_2ghz = val & 0x7; + MTW_DPRINTF(sc, MTW_DEBUG_ROM, "tx mixer gain=%u (2GHz)\n", + sc->txmixgain_2ghz); + sc->lna[2] = val >> 8; /* channel group 2 */ + mtw_srom_read(sc, MTW_EEPROM_RSSI1_5GHZ, &val); + sc->rssi_5ghz[0] = val & 0xff; /* Ant A */ + sc->rssi_5ghz[1] = val >> 8; /* Ant B */ + mtw_srom_read(sc, MTW_EEPROM_RSSI2_5GHZ, &val); + sc->rssi_5ghz[2] = val & 0xff; /* Ant C */ + + sc->lna[3] = val >> 8; /* channel group 3 */ + + mtw_srom_read(sc, MTW_EEPROM_LNA, &val); + sc->lna[0] = val & 0xff; /* channel group 0 */ + sc->lna[1] = val >> 8; /* channel group 1 */ + MTW_DPRINTF(sc, MTW_DEBUG_ROM, "LNA0 0x%x\n", sc->lna[0]); + + /* fix broken 5GHz LNA entries */ + if (sc->lna[2] == 0 || sc->lna[2] == 0xff) { + MTW_DPRINTF(sc, MTW_DEBUG_ROM, + "invalid LNA for channel group %d\n", 2); + sc->lna[2] = sc->lna[1]; + } + if (sc->lna[3] == 0 || sc->lna[3] == 0xff) { + MTW_DPRINTF(sc, MTW_DEBUG_ROM, + "invalid LNA for channel group %d\n", 3); + sc->lna[3] = sc->lna[1]; + } + + /* fix broken RSSI offset entries */ + for (ant = 0; ant < 3; ant++) { + if (sc->rssi_2ghz[ant] < -10 || sc->rssi_2ghz[ant] > 10) { + MTW_DPRINTF(sc, MTW_DEBUG_ROM, + "invalid RSSI%d offset: %d (2GHz)\n", ant + 1, + sc->rssi_2ghz[ant]); + sc->rssi_2ghz[ant] = 0; + } + if (sc->rssi_5ghz[ant] < -10 || sc->rssi_5ghz[ant] > 10) { + MTW_DPRINTF(sc, MTW_DEBUG_ROM, + "invalid RSSI%d offset: %d (5GHz)\n", ant + 1, + sc->rssi_5ghz[ant]); + sc->rssi_5ghz[ant] = 0; + } + } + return (0); +} +static int +mtw_media_change(if_t ifp) +{ + struct ieee80211vap *vap = if_getsoftc(ifp); + struct ieee80211com *ic = vap->iv_ic; + const struct ieee80211_txparam *tp; + struct mtw_softc *sc = ic->ic_softc; + uint8_t rate, ridx; + + MTW_LOCK(sc); + ieee80211_media_change(ifp); + //tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + tp = &vap->iv_txparms[ic->ic_curmode]; + if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { + struct ieee80211_node *ni; + struct mtw_node *rn; + /* XXX TODO: methodize with MCS rates */ + rate = + ic->ic_sup_rates[ic->ic_curmode].rs_rates[tp->ucastrate] & + IEEE80211_RATE_VAL; + for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++) { + if (rt2860_rates[ridx].rate == rate) + break; + } + ni = ieee80211_ref_node(vap->iv_bss); + rn = MTW_NODE(ni); + rn->fix_ridx = ridx; + + MTW_DPRINTF(sc, MTW_DEBUG_RATE, "rate=%d, fix_ridx=%d\n", rate, + rn->fix_ridx); + ieee80211_free_node(ni); + } + MTW_UNLOCK(sc); + + return (0); +} + +void +mtw_set_leds(struct mtw_softc *sc, uint16_t which) +{ + struct mtw_mcu_cmd_8 cmd; + cmd.func = htole32(0x1); + cmd.val = htole32(which); + mtw_mcu_cmd(sc, CMD_LED_MODE, &cmd, sizeof(struct mtw_mcu_cmd_8)); +} +static void +mtw_abort_tsf_sync(struct mtw_softc *sc) +{ + uint32_t tmp; + + mtw_read(sc, MTW_BCN_TIME_CFG, &tmp); + tmp &= ~(MTW_BCN_TX_EN | MTW_TSF_TIMER_EN | MTW_TBTT_TIMER_EN); + mtw_write(sc, MTW_BCN_TIME_CFG, tmp); +} +static int +mtw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + const struct ieee80211_txparam *tp; + struct ieee80211com *ic = vap->iv_ic; + struct mtw_softc *sc = ic->ic_softc; + struct mtw_vap *rvp = MTW_VAP(vap); + enum ieee80211_state ostate; + uint32_t sta[3]; + uint8_t ratectl = 0; + uint8_t restart_ratectl = 0; + uint8_t bid = 1 << rvp->rvp_id; + + + ostate = vap->iv_state; + MTW_DPRINTF(sc, MTW_DEBUG_STATE, "%s -> %s\n", + ieee80211_state_name[ostate], ieee80211_state_name[nstate]); + IEEE80211_UNLOCK(ic); + MTW_LOCK(sc); + ratectl = sc->ratectl_run; /* remember current state */ + usb_callout_stop(&sc->ratectl_ch); + sc->ratectl_run = MTW_RATECTL_OFF; + if (ostate == IEEE80211_S_RUN) { + /* turn link LED off */ + } + + switch (nstate) { + case IEEE80211_S_INIT: + restart_ratectl = 1; + if (ostate != IEEE80211_S_RUN) + break; + + ratectl &= ~bid; + sc->runbmap &= ~bid; + + /* abort TSF synchronization if there is no vap running */ + if (--sc->running == 0) + mtw_abort_tsf_sync(sc); + break; + + case IEEE80211_S_RUN: + if (!(sc->runbmap & bid)) { + if (sc->running++) + restart_ratectl = 1; + sc->runbmap |= bid; + } + + m_freem(rvp->beacon_mbuf); + rvp->beacon_mbuf = NULL; + + switch (vap->iv_opmode) { + case IEEE80211_M_HOSTAP: + case IEEE80211_M_MBSS: + sc->ap_running |= bid; + ic->ic_opmode = vap->iv_opmode; + mtw_update_beacon_cb(vap); + break; + case IEEE80211_M_IBSS: + sc->adhoc_running |= bid; + if (!sc->ap_running) + ic->ic_opmode = vap->iv_opmode; + mtw_update_beacon_cb(vap); + break; + case IEEE80211_M_STA: + sc->sta_running |= bid; + if (!sc->ap_running && !sc->adhoc_running) + ic->ic_opmode = vap->iv_opmode; + + /* read statistic counters (clear on read) */ + mtw_read_region_1(sc, MTW_TX_STA_CNT0, (uint8_t *)sta, + sizeof sta); + + break; + default: + ic->ic_opmode = vap->iv_opmode; + break; + } + + if (vap->iv_opmode != IEEE80211_M_MONITOR) { + struct ieee80211_node *ni; + + if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) { + MTW_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (-1); + } + mtw_updateslot(ic); + mtw_enable_mrr(sc); + mtw_set_txpreamble(sc); + mtw_set_basicrates(sc); + ni = ieee80211_ref_node(vap->iv_bss); + IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); + mtw_set_bssid(sc, sc->sc_bssid); + ieee80211_free_node(ni); + mtw_enable_tsf_sync(sc); + + /* enable automatic rate adaptation */ + tp = &vap->iv_txparms[ieee80211_chan2mode( + ic->ic_curchan)]; + if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) + ratectl |= bid; + } else { + mtw_enable_tsf_sync(sc); + } + + break; + default: + MTW_DPRINTF(sc, MTW_DEBUG_STATE, "undefined state\n"); + break; + } + + /* restart amrr for running VAPs */ + if ((sc->ratectl_run = ratectl) && restart_ratectl) { + usb_callout_reset(&sc->ratectl_ch, hz, mtw_ratectl_to, sc); + } + MTW_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (rvp->newstate(vap, nstate, arg)); +} + +static int +mtw_wme_update(struct ieee80211com *ic) +{ + struct chanAccParams chp; + struct mtw_softc *sc = ic->ic_softc; + const struct wmeParams *ac; + int aci, error = 0; + ieee80211_wme_ic_getparams(ic, &chp); + ac = chp.cap_wmeParams; + + MTW_LOCK(sc); + /* update MAC TX configuration registers */ + for (aci = 0; aci < WME_NUM_AC; aci++) { + error = mtw_write(sc, MTW_EDCA_AC_CFG(aci), + ac[aci].wmep_logcwmax << 16 | ac[aci].wmep_logcwmin << 12 | + ac[aci].wmep_aifsn << 8 | ac[aci].wmep_txopLimit); + if (error) + goto err; + } + + /* update SCH/DMA registers too */ + error = mtw_write(sc, MTW_WMM_AIFSN_CFG, + ac[WME_AC_VO].wmep_aifsn << 12 | ac[WME_AC_VI].wmep_aifsn << 8 | + ac[WME_AC_BK].wmep_aifsn << 4 | ac[WME_AC_BE].wmep_aifsn); + if (error) + goto err; + error = mtw_write(sc, MTW_WMM_CWMIN_CFG, + ac[WME_AC_VO].wmep_logcwmin << 12 | + ac[WME_AC_VI].wmep_logcwmin << 8 | + ac[WME_AC_BK].wmep_logcwmin << 4 | ac[WME_AC_BE].wmep_logcwmin); + if (error) + goto err; + error = mtw_write(sc, MTW_WMM_CWMAX_CFG, + ac[WME_AC_VO].wmep_logcwmax << 12 | + ac[WME_AC_VI].wmep_logcwmax << 8 | + ac[WME_AC_BK].wmep_logcwmax << 4 | ac[WME_AC_BE].wmep_logcwmax); + if (error) + goto err; + error = mtw_write(sc, MTW_WMM_TXOP0_CFG, + ac[WME_AC_BK].wmep_txopLimit << 16 | ac[WME_AC_BE].wmep_txopLimit); + if (error) + goto err; + error = mtw_write(sc, MTW_WMM_TXOP1_CFG, + ac[WME_AC_VO].wmep_txopLimit << 16 | ac[WME_AC_VI].wmep_txopLimit); + +err: + MTW_UNLOCK(sc); + if (error) + MTW_DPRINTF(sc, MTW_DEBUG_USB, "WME update failed\n"); + + return (error); +} + +static int +mtw_key_set(struct ieee80211vap *vap, struct ieee80211_key *k) +{ + struct ieee80211com *ic = vap->iv_ic; + struct mtw_softc *sc = ic->ic_softc; + uint32_t i; + + i = MTW_CMDQ_GET(&sc->cmdq_store); + MTW_DPRINTF(sc, MTW_DEBUG_KEY, "cmdq_store=%d\n", i); + sc->cmdq[i].func = mtw_key_set_cb; + sc->cmdq[i].arg0 = NULL; + sc->cmdq[i].arg1 = vap; + sc->cmdq[i].k = k; + IEEE80211_ADDR_COPY(sc->cmdq[i].mac, k->wk_macaddr); + ieee80211_runtask(ic, &sc->cmdq_task); + + /* + * To make sure key will be set when hostapd + * calls iv_key_set() before if_init(). + */ + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { + MTW_LOCK(sc); + sc->cmdq_key_set = MTW_CMDQ_GO; + MTW_UNLOCK(sc); + } + + return (1); +} +static void +mtw_key_set_cb(void *arg) +{ + struct mtw_cmdq *cmdq = arg; + struct ieee80211vap *vap = cmdq->arg1; + struct ieee80211_key *k = cmdq->k; + struct ieee80211com *ic = vap->iv_ic; + struct mtw_softc *sc = ic->ic_softc; + struct ieee80211_node *ni; + u_int cipher = k->wk_cipher->ic_cipher; + uint32_t attr; + uint16_t base; + uint8_t mode, wcid, iv[8]; + MTW_LOCK_ASSERT(sc, MA_OWNED); + + if (vap->iv_opmode == IEEE80211_M_HOSTAP) + ni = ieee80211_find_vap_node(&ic->ic_sta, vap, cmdq->mac); + else + ni = vap->iv_bss; + + /* map net80211 cipher to RT2860 security mode */ + switch (cipher) { + case IEEE80211_CIPHER_WEP: + if (k->wk_keylen < 8) + mode = MTW_MODE_WEP40; + else + mode = MTW_MODE_WEP104; + break; + case IEEE80211_CIPHER_TKIP: + mode = MTW_MODE_TKIP; + break; + case IEEE80211_CIPHER_AES_CCM: + mode = MTW_MODE_AES_CCMP; + break; + default: + MTW_DPRINTF(sc, MTW_DEBUG_KEY, "undefined case\n"); + return; + } + + if (k->wk_flags & IEEE80211_KEY_GROUP) { + wcid = 0; /* NB: update WCID0 for group keys */ + base = MTW_SKEY(0, k->wk_keyix); + } else { + wcid = (ni != NULL) ? MTW_AID2WCID(ni->ni_associd) : 0; + base = MTW_PKEY(wcid); + } + + if (cipher == IEEE80211_CIPHER_TKIP) { + mtw_write_region_1(sc, base, k->wk_key, 16); + mtw_write_region_1(sc, base + 16, &k->wk_key[24], 8); + mtw_write_region_1(sc, base + 24, &k->wk_key[16], 8); + } else { + /* roundup len to 16-bit: XXX fix write_region_1() instead */ + mtw_write_region_1(sc, base, k->wk_key, + (k->wk_keylen + 1) & ~1); + } + + if (!(k->wk_flags & IEEE80211_KEY_GROUP) || + (k->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))) { + /* set initial packet number in IV+EIV */ + if (cipher == IEEE80211_CIPHER_WEP) { + memset(iv, 0, sizeof iv); + iv[3] = vap->iv_def_txkey << 6; + } else { + if (cipher == IEEE80211_CIPHER_TKIP) { + iv[0] = k->wk_keytsc >> 8; + iv[1] = (iv[0] | 0x20) & 0x7f; + iv[2] = k->wk_keytsc; + } else { //CCMP + iv[0] = k->wk_keytsc; + iv[1] = k->wk_keytsc >> 8; + iv[2] = 0; + } + iv[3] = k->wk_keyix << 6 | IEEE80211_WEP_EXTIV; + iv[4] = k->wk_keytsc >> 16; + iv[5] = k->wk_keytsc >> 24; + iv[6] = k->wk_keytsc >> 32; + iv[7] = k->wk_keytsc >> 40; + } + mtw_write_region_1(sc, MTW_IVEIV(wcid), iv, 8); + } + + if (k->wk_flags & IEEE80211_KEY_GROUP) { + /* install group key */ + mtw_read(sc, MTW_SKEY_MODE_0_7, &attr); + attr &= ~(0xf << (k->wk_keyix * 4)); + attr |= mode << (k->wk_keyix * 4); + mtw_write(sc, MTW_SKEY_MODE_0_7, attr); + + if (cipher & (IEEE80211_CIPHER_WEP)) { + mtw_read(sc, MTW_WCID_ATTR(wcid + 1), &attr); + attr = (attr & ~0xf) | (mode << 1); + mtw_write(sc, MTW_WCID_ATTR(wcid + 1), attr); + + mtw_set_region_4(sc, MTW_IVEIV(0), 0, 4); + + mtw_read(sc, MTW_WCID_ATTR(wcid), &attr); + attr = (attr & ~0xf) | (mode << 1); + mtw_write(sc, MTW_WCID_ATTR(wcid), attr); + } + } else { + /* install pairwise key */ + mtw_read(sc, MTW_WCID_ATTR(wcid), &attr); + attr = (attr & ~0xf) | (mode << 1) | MTW_RX_PKEY_EN; + mtw_write(sc, MTW_WCID_ATTR(wcid), attr); + } + k->wk_pad = wcid; +} + +/* + * If wlan is destroyed without being brought down i.e. without + * wlan down or wpa_cli terminate, this function is called after + * vap is gone. Don't refer it. + */ +static void +mtw_key_delete_cb(void *arg) +{ + struct mtw_cmdq *cmdq = arg; + struct mtw_softc *sc = cmdq->arg1; + struct ieee80211_key *k = &cmdq->key; + uint32_t attr; + uint8_t wcid; + + MTW_LOCK_ASSERT(sc, MA_OWNED); + + if (k->wk_flags & IEEE80211_KEY_GROUP) { + /* remove group key */ + MTW_DPRINTF(sc, MTW_DEBUG_KEY, "removing group key\n"); + mtw_read(sc, MTW_SKEY_MODE_0_7, &attr); + attr &= ~(0xf << (k->wk_keyix * 4)); + mtw_write(sc, MTW_SKEY_MODE_0_7, attr); + } else { + /* remove pairwise key */ + MTW_DPRINTF(sc, MTW_DEBUG_KEY, "removing key for wcid %x\n", + k->wk_pad); + /* matching wcid was written to wk_pad in mtw_key_set() */ + wcid = k->wk_pad; + mtw_read(sc, MTW_WCID_ATTR(wcid), &attr); + attr &= ~0xf; + mtw_write(sc, MTW_WCID_ATTR(wcid), attr); + } + + k->wk_pad = 0; +} + +/* + * return 0 on error + */ +static int +mtw_key_delete(struct ieee80211vap *vap, struct ieee80211_key *k) +{ + struct ieee80211com *ic = vap->iv_ic; + struct mtw_softc *sc = ic->ic_softc; + struct ieee80211_key *k0; + uint32_t i; + + /* + * When called back, key might be gone. So, make a copy + * of some values need to delete keys before deferring. + * But, because of LOR with node lock, cannot use lock here. + * So, use atomic instead. + */ + i = MTW_CMDQ_GET(&sc->cmdq_store); + MTW_DPRINTF(sc, MTW_DEBUG_KEY, "cmdq_store=%d\n", i); + sc->cmdq[i].func = mtw_key_delete_cb; + sc->cmdq[i].arg0 = NULL; + sc->cmdq[i].arg1 = sc; + k0 = &sc->cmdq[i].key; + k0->wk_flags = k->wk_flags; + k0->wk_keyix = k->wk_keyix; + /* matching wcid was written to wk_pad in mtw_key_set() */ + k0->wk_pad = k->wk_pad; + ieee80211_runtask(ic, &sc->cmdq_task); + return (1); /* return fake success */ +} + +static void +mtw_ratectl_to(void *arg) +{ + struct mtw_softc *sc = arg; + /* do it in a process context, so it can go sleep */ + ieee80211_runtask(&sc->sc_ic, &sc->ratectl_task); + /* next timeout will be rescheduled in the callback task */ +} + +/* ARGSUSED */ +static void +mtw_ratectl_cb(void *arg, int pending) +{ + + struct mtw_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + if (vap == NULL) + return; + + ieee80211_iterate_nodes(&ic->ic_sta, mtw_iter_func, sc); + + usb_callout_reset(&sc->ratectl_ch, hz, mtw_ratectl_to, sc); + + +} + +static void +mtw_drain_fifo(void *arg) +{ + struct mtw_softc *sc = arg; + uint32_t stat; + uint16_t(*wstat)[3]; + uint8_t wcid, mcs, pid; + int8_t retry; + + MTW_LOCK_ASSERT(sc, MA_OWNED); + + for (;;) { + /* drain Tx status FIFO (maxsize = 16) */ + mtw_read(sc, MTW_TX_STAT_FIFO, &stat); + MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "tx stat 0x%08x\n", stat); + if (!(stat & MTW_TXQ_VLD)) + break; + + wcid = (stat >> MTW_TXQ_WCID_SHIFT) & 0xff; + + /* if no ACK was requested, no feedback is available */ + if (!(stat & MTW_TXQ_ACKREQ) || wcid > MTW_WCID_MAX || + wcid == 0) + continue; + + /* + * Even though each stat is Tx-complete-status like format, + * the device can poll stats. Because there is no guarantee + * that the referring node is still around when read the stats. + * So that, if we use ieee80211_ratectl_tx_update(), we will + * have hard time not to refer already freed node. + * + * To eliminate such page faults, we poll stats in softc. + * Then, update the rates later with + * ieee80211_ratectl_tx_update(). + */ + wstat = &(sc->wcid_stats[wcid]); + (*wstat)[MTW_TXCNT]++; + if (stat & MTW_TXQ_OK) + (*wstat)[MTW_SUCCESS]++; + else + counter_u64_add(sc->sc_ic.ic_oerrors, 1); + /* + * Check if there were retries, ie if the Tx success rate is + * different from the requested rate. Note that it works only + * because we do not allow rate fallback from OFDM to CCK. + */ + mcs = (stat >> MTW_TXQ_MCS_SHIFT) & 0x7f; + pid = (stat >> MTW_TXQ_PID_SHIFT) & 0xf; + if ((retry = pid - 1 - mcs) > 0) { + (*wstat)[MTW_TXCNT] += retry; + (*wstat)[MTW_RETRY] += retry; + } + } + MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "count=%d\n", sc->fifo_cnt); + + sc->fifo_cnt = 0; +} + +static void +mtw_iter_func(void *arg, struct ieee80211_node *ni) +{ + struct mtw_softc *sc = arg; + MTW_LOCK(sc); + struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs; + struct ieee80211vap *vap = ni->ni_vap; + struct mtw_node *rn = MTW_NODE(ni); + uint32_t sta[3]; + uint16_t(*wstat)[3]; + int error, ridx; + + + /* Check for special case */ + if (sc->rvp_cnt <= 1 && vap->iv_opmode == IEEE80211_M_STA && + ni != vap->iv_bss) + goto fail; + + txs->flags = IEEE80211_RATECTL_TX_STATS_NODE | + IEEE80211_RATECTL_TX_STATS_RETRIES; + txs->ni = ni; + if (sc->rvp_cnt <= 1 && + (vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_opmode == IEEE80211_M_STA)) { + /* + * read statistic counters (clear on read) and update AMRR state + */ + error = mtw_read_region_1(sc, MTW_TX_STA_CNT0, (uint8_t *)sta, + sizeof sta); + MTW_DPRINTF(sc, MTW_DEBUG_RATE, "error:%d\n", error); + if (error != 0) + goto fail; + + /* count failed TX as errors */ + if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, + le32toh(sta[0]) & 0xffff); + + txs->nretries = (le32toh(sta[1]) >> 16); + txs->nsuccess = (le32toh(sta[1]) & 0xffff); + /* nretries??? */ + txs->nframes = txs->nsuccess + (le32toh(sta[0]) & 0xffff); + + MTW_DPRINTF(sc, MTW_DEBUG_RATE, + "retrycnt=%d success=%d failcnt=%d\n", txs->nretries, + txs->nsuccess, le32toh(sta[0]) & 0xffff); + } else { + wstat = &(sc->wcid_stats[MTW_AID2WCID(ni->ni_associd)]); + + if (wstat == &(sc->wcid_stats[0]) || + wstat > &(sc->wcid_stats[MTW_WCID_MAX])) + goto fail; + + txs->nretries = (*wstat)[MTW_RETRY]; + txs->nsuccess = (*wstat)[MTW_SUCCESS]; + txs->nframes = (*wstat)[MTW_TXCNT]; + MTW_DPRINTF(sc, MTW_DEBUG_RATE, + "wstat retrycnt=%d txcnt=%d success=%d\n", txs->nretries, + txs->nframes, txs->nsuccess); + + memset(wstat, 0, sizeof(*wstat)); + } + + ieee80211_ratectl_tx_update(vap, txs); + ieee80211_ratectl_rate(ni, NULL, 0); + /* XXX TODO: methodize with MCS rates */ + for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++) { + MTW_DPRINTF(sc, MTW_DEBUG_RATE, "ni_txrate=0x%x\n", + ni->ni_txrate); + if (rt2860_rates[ridx].rate == ni->ni_txrate) { + break; + } + } + rn->amrr_ridx = ridx; +fail: + MTW_UNLOCK(sc); + + MTW_DPRINTF(sc, MTW_DEBUG_RATE, "rate=%d, ridx=%d\n", + ni->ni_txrate, rn->amrr_ridx); +} + +static void +mtw_newassoc_cb(void *arg) +{ + struct mtw_cmdq *cmdq = arg; + struct ieee80211_node *ni = cmdq->arg1; + struct mtw_softc *sc = ni->ni_vap->iv_ic->ic_softc; + + uint8_t wcid = cmdq->wcid; + + MTW_LOCK_ASSERT(sc, MA_OWNED); + + mtw_write_region_1(sc, MTW_WCID_ENTRY(wcid), ni->ni_macaddr, + IEEE80211_ADDR_LEN); + + memset(&(sc->wcid_stats[wcid]), 0, sizeof(sc->wcid_stats[wcid])); +} + +static void +mtw_newassoc(struct ieee80211_node *ni, int isnew) +{ + + struct mtw_node *mn = MTW_NODE(ni); + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = vap->iv_ic; + struct mtw_softc *sc = ic->ic_softc; + + uint8_t rate; + uint8_t ridx; + uint8_t wcid; + //int i; + // int i,j; + wcid = MTW_AID2WCID(ni->ni_associd); + + if (wcid > MTW_WCID_MAX) { + device_printf(sc->sc_dev, "wcid=%d out of range\n", wcid); + return; + } + + /* only interested in true associations */ + if (isnew && ni->ni_associd != 0) { + /* + * This function could is called though timeout function. + * Need to deferggxr. + */ + + uint32_t cnt = MTW_CMDQ_GET(&sc->cmdq_store); + MTW_DPRINTF(sc, MTW_DEBUG_STATE, "cmdq_store=%d\n", cnt); + sc->cmdq[cnt].func = mtw_newassoc_cb; + sc->cmdq[cnt].arg0 = NULL; + sc->cmdq[cnt].arg1 = ni; + sc->cmdq[cnt].wcid = wcid; + ieee80211_runtask(ic, &sc->cmdq_task); + } + + MTW_DPRINTF(sc, MTW_DEBUG_STATE, + "new assoc isnew=%d associd=%x addr=%s\n", isnew, ni->ni_associd, + ether_sprintf(ni->ni_macaddr)); + rate = vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)].mgmtrate; + /* XXX TODO: methodize with MCS rates */ + for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++) + if (rt2860_rates[ridx].rate == rate) + break; + mn->mgt_ridx = ridx; + MTW_DPRINTF(sc, MTW_DEBUG_STATE | MTW_DEBUG_RATE, + "rate=%d, ctl_ridx=%d\n", rate, ridx); + MTW_LOCK(sc); + if (sc->ratectl_run != MTW_RATECTL_OFF) { + usb_callout_reset(&sc->ratectl_ch, hz, &mtw_ratectl_to, sc); + } + MTW_UNLOCK(sc); + +} + +/* + * Return the Rx chain with the highest RSSI for a given frame. + */ +static __inline uint8_t +mtw_maxrssi_chain(struct mtw_softc *sc, const struct mtw_rxwi *rxwi) +{ + uint8_t rxchain = 0; + + if (sc->nrxchains > 1) { + if (rxwi->rssi[1] > rxwi->rssi[rxchain]) + rxchain = 1; + if (sc->nrxchains > 2) + if (rxwi->rssi[2] > rxwi->rssi[rxchain]) + rxchain = 2; + } + return (rxchain); +} +static void +mtw_get_tsf(struct mtw_softc *sc, uint64_t *buf) +{ + mtw_read_region_1(sc, MTW_TSF_TIMER_DW0, (uint8_t *)buf, sizeof(*buf)); +} + +static void +mtw_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, + const struct ieee80211_rx_stats *rxs, int rssi, int nf) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct mtw_softc *sc = vap->iv_ic->ic_softc; + struct mtw_vap *rvp = MTW_VAP(vap); + uint64_t ni_tstamp, rx_tstamp; + + rvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf); + + if (vap->iv_state == IEEE80211_S_RUN && + (subtype == IEEE80211_FC0_SUBTYPE_BEACON || + subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) { + ni_tstamp = le64toh(ni->ni_tstamp.tsf); + MTW_LOCK(sc); + mtw_get_tsf(sc, &rx_tstamp); + MTW_UNLOCK(sc); + rx_tstamp = le64toh(rx_tstamp); + + if (ni_tstamp >= rx_tstamp) { + MTW_DPRINTF(sc, MTW_DEBUG_RECV | MTW_DEBUG_BEACON, + "ibss merge, tsf %ju tstamp %ju\n", + (uintmax_t)rx_tstamp, (uintmax_t)ni_tstamp); + (void)ieee80211_ibss_merge(ni); + } + } +} +static void +mtw_rx_frame(struct mtw_softc *sc, struct mbuf *m, uint32_t dmalen) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct epoch_tracker et; + + struct mtw_rxwi *rxwi; + uint32_t flags; + uint16_t len, rxwisize; + uint8_t ant, rssi; + int8_t nf; + + rxwisize = sizeof(struct mtw_rxwi); + + if (__predict_false( + dmalen < rxwisize + sizeof(struct ieee80211_frame_ack))) { + MTW_DPRINTF(sc, MTW_DEBUG_RECV, + "payload is too short: dma length %u < %zu\n", dmalen, + rxwisize + sizeof(struct ieee80211_frame_ack)); + goto fail; + } + + rxwi = mtod(m, struct mtw_rxwi *); + len = le16toh(rxwi->len) & 0xfff; + flags = le32toh(rxwi->flags); + if (__predict_false(len > dmalen - rxwisize)) { + MTW_DPRINTF(sc, MTW_DEBUG_RECV, "bad RXWI length %u > %u\n", + len, dmalen); + goto fail; + } + + if (__predict_false(flags & (MTW_RX_CRCERR | MTW_RX_ICVERR))) { + MTW_DPRINTF(sc, MTW_DEBUG_RECV, "%s error.\n", + (flags & MTW_RX_CRCERR) ? "CRC" : "ICV"); + goto fail; + } + + if (flags & MTW_RX_L2PAD) { + MTW_DPRINTF(sc, MTW_DEBUG_RECV, + "received RT2860_RX_L2PAD frame\n"); + len += 2; + } + + m->m_data += rxwisize; + m->m_pkthdr.len = m->m_len = len; + + wh = mtod(m, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; + m->m_flags |= M_WEP; + } + + if (len >= sizeof(struct ieee80211_frame_min)) { + ni = ieee80211_find_rxnode(ic, + mtod(m, struct ieee80211_frame_min *)); + } else + ni = NULL; + + if (ni && ni->ni_flags & IEEE80211_NODE_HT) { + m->m_flags |= M_AMPDU; + } + + if (__predict_false(flags & MTW_RX_MICERR)) { + /* report MIC failures to net80211 for TKIP */ + if (ni != NULL) + ieee80211_notify_michael_failure(ni->ni_vap, wh, + rxwi->keyidx); + MTW_DPRINTF(sc, MTW_DEBUG_RECV, + "MIC error. Someone is lying.\n"); + goto fail; + } + + ant = mtw_maxrssi_chain(sc, rxwi); + rssi = rxwi->rssi[ant]; + nf = mtw_rssi2dbm(sc, rssi, ant); + + if (__predict_false(ieee80211_radiotap_active(ic))) { + struct mtw_rx_radiotap_header *tap = &sc->sc_rxtap; + uint16_t phy; + + tap->wr_flags = 0; + if (flags & MTW_RX_L2PAD) + tap->wr_flags |= IEEE80211_RADIOTAP_F_DATAPAD; + tap->wr_antsignal = rssi; + tap->wr_antenna = ant; + tap->wr_dbm_antsignal = mtw_rssi2dbm(sc, rssi, ant); + tap->wr_rate = 2; /* in case it can't be found below */ + //MTW_LOCK(sc); + + // MTW_UNLOCK(sc); + phy = le16toh(rxwi->phy); + switch (phy >> MT7601_PHY_SHIFT) { + case MTW_PHY_CCK: + switch ((phy & MTW_PHY_MCS) & ~MTW_PHY_SHPRE) { + case 0: + tap->wr_rate = 2; + break; + case 1: + tap->wr_rate = 4; + break; + case 2: + tap->wr_rate = 11; + break; + case 3: + tap->wr_rate = 22; + break; + } + if (phy & MTW_PHY_SHPRE) + tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + break; + case MTW_PHY_OFDM: + switch (phy & MTW_PHY_MCS) { + case 0: + tap->wr_rate = 12; + break; + case 1: + tap->wr_rate = 18; + break; + case 2: + tap->wr_rate = 24; + break; + case 3: + tap->wr_rate = 36; + break; + case 4: + tap->wr_rate = 48; + break; + case 5: + tap->wr_rate = 72; + break; + case 6: + tap->wr_rate = 96; + break; + case 7: + tap->wr_rate = 108; + break; + } + break; + } + } + + NET_EPOCH_ENTER(et); + if (ni != NULL) { + (void)ieee80211_input(ni, m, rssi, nf); + ieee80211_free_node(ni); + } else { + (void)ieee80211_input_all(ic, m, rssi, nf); + } + NET_EPOCH_EXIT(et); + + return; + +fail: + m_freem(m); + counter_u64_add(ic->ic_ierrors, 1); +} + +static void +mtw_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct mtw_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct mbuf *m = NULL; + struct mbuf *m0; + uint32_t dmalen, mbuf_len; + uint16_t rxwisize; + int xferlen; + + rxwisize = sizeof(struct mtw_rxwi); + + usbd_xfer_status(xfer, &xferlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + MTW_DPRINTF(sc, MTW_DEBUG_RECV, "rx done, actlen=%d\n", + xferlen); + if (xferlen < (int)(sizeof(uint32_t) + rxwisize + + sizeof(struct mtw_rxd))) { + MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB, + "xfer too short %d %d\n", xferlen, + (int)(sizeof(uint32_t) + rxwisize + + sizeof(struct mtw_rxd))); + goto tr_setup; + } + + m = sc->rx_m; + sc->rx_m = NULL; + + /* FALLTHROUGH */ + case USB_ST_SETUP: + tr_setup: + + if (sc->rx_m == NULL) { + sc->rx_m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, + MTW_MAX_RXSZ); + } + if (sc->rx_m == NULL) { + MTW_DPRINTF(sc, + MTW_DEBUG_RECV | MTW_DEBUG_RECV_DESC | + MTW_DEBUG_USB, + "could not allocate mbuf - idle with stall\n"); + counter_u64_add(ic->ic_ierrors, 1); + usbd_xfer_set_stall(xfer); + usbd_xfer_set_frames(xfer, 0); + } else { + /* + * Directly loading a mbuf cluster into DMA to + * save some data copying. This works because + * there is only one cluster. + */ + usbd_xfer_set_frame_data(xfer, 0, + mtod(sc->rx_m, caddr_t), MTW_MAX_RXSZ); + usbd_xfer_set_frames(xfer, 1); + } + usbd_transfer_submit(xfer); + break; + + default: /* Error */ + MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB, + "USB transfer error, %s\n", usbd_errstr(error)); + + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + if (error == USB_ERR_TIMEOUT) + device_printf(sc->sc_dev, "device timeout %s\n", + __func__); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + if (sc->rx_m != NULL) { + m_freem(sc->rx_m); + sc->rx_m = NULL; + } + break; + } + + if (m == NULL) + return; + + /* inputting all the frames must be last */ + + MTW_UNLOCK(sc); + + m->m_pkthdr.len = m->m_len = xferlen; + + /* HW can aggregate multiple 802.11 frames in a single USB xfer */ + for (;;) { + dmalen = le32toh(*mtod(m, uint32_t *)) & 0xffff; + + if ((dmalen >= (uint32_t)-8) || (dmalen == 0) || + ((dmalen & 3) != 0)) { + MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB, + "bad DMA length %u\n", dmalen); + break; + } + if ((dmalen + 8) > (uint32_t)xferlen) { + MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB, + "bad DMA length %u > %d\n", dmalen + 8, xferlen); + break; + } + + /* If it is the last one or a single frame, we won't copy. */ + if ((xferlen -= dmalen + 8) <= 8) { + /* trim 32-bit DMA-len header */ + m->m_data += 4; + m->m_pkthdr.len = m->m_len -= 4; + mtw_rx_frame(sc, m, dmalen); + m = NULL; /* don't free source buffer */ + break; + } + + mbuf_len = dmalen + sizeof(struct mtw_rxd); + if (__predict_false(mbuf_len > MCLBYTES)) { + MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC | MTW_DEBUG_USB, + "payload is too big: mbuf_len %u\n", mbuf_len); + counter_u64_add(ic->ic_ierrors, 1); + break; + } + + /* copy aggregated frames to another mbuf */ + m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (__predict_false(m0 == NULL)) { + MTW_DPRINTF(sc, MTW_DEBUG_RECV_DESC, + "could not allocate mbuf\n"); + counter_u64_add(ic->ic_ierrors, 1); + break; + } + m_copydata(m, 4 /* skip 32-bit DMA-len header */, mbuf_len, + mtod(m0, caddr_t)); + m0->m_pkthdr.len = m0->m_len = mbuf_len; + mtw_rx_frame(sc, m0, dmalen); + + /* update data ptr */ + m->m_data += mbuf_len + 4; + m->m_pkthdr.len = m->m_len -= mbuf_len + 4; + } + + /* make sure we free the source buffer, if any */ + m_freem(m); + +#ifdef IEEE80211_SUPPORT_SUPERG + ieee80211_ff_age_all(ic, 100); +#endif + MTW_LOCK(sc); +} + +static void +mtw_tx_free(struct mtw_endpoint_queue *pq, struct mtw_tx_data *data, int txerr) +{ + + ieee80211_tx_complete(data->ni, data->m, txerr); + data->m = NULL; + data->ni = NULL; + + STAILQ_INSERT_TAIL(&pq->tx_fh, data, next); + pq->tx_nfree++; +} +static void +mtw_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error, u_int index) +{ + struct mtw_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct mtw_tx_data *data; + struct ieee80211vap *vap = NULL; + struct usb_page_cache *pc; + struct mtw_endpoint_queue *pq = &sc->sc_epq[index]; + struct mbuf *m; + usb_frlength_t size; + int actlen; + int sumlen; + usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB, + "transfer complete: %d bytes @ index %d\n", actlen, index); + + data = usbd_xfer_get_priv(xfer); + mtw_tx_free(pq, data, 0); + usbd_xfer_set_priv(xfer, NULL); + + /* FALLTHROUGH */ + case USB_ST_SETUP: + tr_setup: + data = STAILQ_FIRST(&pq->tx_qh); + if (data == NULL) + break; + + STAILQ_REMOVE_HEAD(&pq->tx_qh, next); + + m = data->m; + + size = sizeof(data->desc); + if ((m->m_pkthdr.len + size + 3 + 8) > MTW_MAX_TXSZ) { + MTW_DPRINTF(sc, MTW_DEBUG_XMIT_DESC | MTW_DEBUG_USB, + "data overflow, %u bytes\n", m->m_pkthdr.len); + mtw_tx_free(pq, data, 1); + goto tr_setup; + } + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_in(pc, 0, &data->desc, size); + usbd_m_copy_in(pc, size, m, 0, m->m_pkthdr.len); + size += m->m_pkthdr.len; + /* + * Align end on a 4-byte boundary, pad 8 bytes (CRC + + * 4-byte padding), and be sure to zero those trailing + * bytes: + */ + usbd_frame_zero(pc, size, ((-size) & 3) + MTW_DMA_PAD); + size += ((-size) & 3) + MTW_DMA_PAD; + + vap = data->ni->ni_vap; + if (ieee80211_radiotap_active_vap(vap)) { + const struct ieee80211_frame *wh; + struct mtw_tx_radiotap_header *tap = &sc->sc_txtap; + struct mtw_txwi *txwi = + (struct mtw_txwi *)(&data->desc + + sizeof(struct mtw_txd)); + int has_l2pad; + + wh = mtod(m, struct ieee80211_frame *); + has_l2pad = IEEE80211_HAS_ADDR4(wh) != + IEEE80211_QOS_HAS_SEQ(wh); + + tap->wt_flags = 0; + tap->wt_rate = rt2860_rates[data->ridx].rate; + tap->wt_hwqueue = index; + if (le16toh(txwi->phy) & MTW_PHY_SHPRE) + tap->wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + if (has_l2pad) + tap->wt_flags |= IEEE80211_RADIOTAP_F_DATAPAD; + + ieee80211_radiotap_tx(vap, m); + } + + MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB, + "sending frame len=%u/%u @ index %d\n", m->m_pkthdr.len, + size, index); + + usbd_xfer_set_frame_len(xfer, 0, size); + usbd_xfer_set_priv(xfer, data); + usbd_transfer_submit(xfer); + mtw_start(sc); + + break; + + default: + MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB, + "USB transfer error, %s\n", usbd_errstr(error)); + + data = usbd_xfer_get_priv(xfer); + + if (data != NULL) { + if (data->ni != NULL) + vap = data->ni->ni_vap; + mtw_tx_free(pq, data, error); + usbd_xfer_set_priv(xfer, NULL); + } + + if (vap == NULL) + vap = TAILQ_FIRST(&ic->ic_vaps); + + if (error != USB_ERR_CANCELLED) { + if (error == USB_ERR_TIMEOUT) { + device_printf(sc->sc_dev, "device timeout %s\n", + __func__); + uint32_t i = MTW_CMDQ_GET(&sc->cmdq_store); + MTW_DPRINTF(sc, MTW_DEBUG_XMIT | MTW_DEBUG_USB, + "cmdq_store=%d\n", i); + sc->cmdq[i].func = mtw_usb_timeout_cb; + sc->cmdq[i].arg0 = vap; + ieee80211_runtask(ic, &sc->cmdq_task); + } + + /* + * Try to clear stall first, also if other + * errors occur, hence clearing stall + * introduces a 50 ms delay: + */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +#ifdef IEEE80211_SUPPORT_SUPERG + /* XXX TODO: make this deferred rather than unlock/relock */ + /* XXX TODO: should only do the QoS AC this belongs to */ + if (pq->tx_nfree >= RUN_TX_RING_COUNT) { + RUN_UNLOCK(sc); + ieee80211_ff_flush_all(ic); + RUN_LOCK(sc); + } +#endif +} + +static void +mtw_fw_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct mtw_softc *sc = usbd_xfer_softc(xfer); + + int actlen; + int ntries, tmp; + // struct mtw_txd *data; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + // data = usbd_xfer_get_priv(xfer); + usbd_xfer_set_priv(xfer, NULL); + switch (USB_GET_STATE(xfer)) { + + case USB_ST_TRANSFERRED: + sc->sc_sent += actlen; + memset(sc->txd_fw[sc->sc_idx], 0, actlen); + + if (actlen < 0x2000 && sc->sc_idx == 0) { + return; + } + if (sc->sc_idx == 5) { + + if ((error = mtw_write_ivb(sc, sc->sc_ivb_1, + MTW_MCU_IVB_LEN)) != 0) { + device_printf(sc->sc_dev, + "Could not write ivb error: %d\n", error); + } + + mtw_delay(sc, 10); + for (ntries = 0; ntries < 100; ntries++) { + if ((error = mtw_read_cfg(sc, MTW_MCU_DMA_ADDR, + &tmp)) != 0) { + device_printf(sc->sc_dev, + "Could not read cfg error: %d\n", error); + + } + if (tmp == MTW_MCU_READY) { + MTW_DPRINTF(sc, MTW_DEBUG_FIRMWARE, + "mcu reaady %d\n", tmp); + sc->fwloading = 1; + break; + } + + mtw_delay(sc, 10); + } + if (ntries == 100) + sc->fwloading = 0; + wakeup(&sc->fwloading); + return; + } + + if (actlen == 0x2000) { + sc->sc_idx++; + DELAY(1000); + } + + case USB_ST_SETUP: { + int dlen = 0; + dlen = sc->txd_fw[sc->sc_idx]->len; + + mtw_write_cfg(sc, MTW_MCU_DMA_ADDR, 0x40 + sc->sc_sent); + mtw_write_cfg(sc, MTW_MCU_DMA_LEN, (dlen << 16)); + + usbd_xfer_set_frame_len(xfer, 0, dlen); + usbd_xfer_set_frame_data(xfer, 0, sc->txd_fw[sc->sc_idx], dlen); + + // usbd_xfer_set_priv(xfer,sc->txd[sc->sc_idx]); + usbd_transfer_submit(xfer); + break; + + default: /* Error */ + device_printf(sc->sc_dev, "%s:%d %s\n", __FILE__, __LINE__, + usbd_errstr(error)); + sc->fwloading = 0; + wakeup(&sc->fwloading); + /* + * Print error message and clear stall + * for example. + */ + break; + } + /* + * Here it is safe to do something without the private + * USB mutex locked. + */ + } + return; +} +static void +mtw_bulk_tx_callback0(struct usb_xfer *xfer, usb_error_t error) +{ + mtw_bulk_tx_callbackN(xfer, error, 0); +} + +static void +mtw_bulk_tx_callback1(struct usb_xfer *xfer, usb_error_t error) +{ + + + mtw_bulk_tx_callbackN(xfer, error, 1); +} + +static void +mtw_bulk_tx_callback2(struct usb_xfer *xfer, usb_error_t error) +{ + mtw_bulk_tx_callbackN(xfer, error, 2); +} + +static void +mtw_bulk_tx_callback3(struct usb_xfer *xfer, usb_error_t error) +{ + mtw_bulk_tx_callbackN(xfer, error, 3); +} + +static void +mtw_bulk_tx_callback4(struct usb_xfer *xfer, usb_error_t error) +{ + mtw_bulk_tx_callbackN(xfer, error, 4); +} + +static void +mtw_bulk_tx_callback5(struct usb_xfer *xfer, usb_error_t error) +{ + mtw_bulk_tx_callbackN(xfer, error, 5); +} + +static void +mtw_set_tx_desc(struct mtw_softc *sc, struct mtw_tx_data *data) +{ + struct mbuf *m = data->m; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = data->ni->ni_vap; + struct ieee80211_frame *wh; + struct mtw_txd *txd; + struct mtw_txwi *txwi; + uint16_t xferlen, txwisize; + uint16_t mcs; + uint8_t ridx = data->ridx; + uint8_t pad; + + /* get MCS code from rate index */ + mcs = rt2860_rates[ridx].mcs; + + txwisize = sizeof(*txwi); + xferlen = txwisize + m->m_pkthdr.len; + + /* roundup to 32-bit alignment */ + xferlen = (xferlen + 3) & ~3; + + txd = (struct mtw_txd *)&data->desc; + txd->len = htole16(xferlen); + + wh = mtod(m, struct ieee80211_frame *); + + /* + * Ether both are true or both are false, the header + * are nicely aligned to 32-bit. So, no L2 padding. + */ + if (IEEE80211_HAS_ADDR4(wh) == IEEE80211_QOS_HAS_SEQ(wh)) + pad = 0; + else + pad = 2; + + /* setup TX Wireless Information */ + txwi = (struct mtw_txwi *)(txd + 1); + txwi->len = htole16(m->m_pkthdr.len - pad); + if (rt2860_rates[ridx].phy == IEEE80211_T_DS) { + mcs |= MTW_PHY_CCK; + if (ridx != MTW_RIDX_CCK1 && + (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + mcs |= MTW_PHY_SHPRE; + } else if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM) { + mcs |= MTW_PHY_OFDM; + } else if (rt2860_rates[ridx].phy == IEEE80211_T_HT) { + /* XXX TODO: [adrian] set short preamble for MCS? */ + mcs |= MTW_PHY_HT; /* Mixed, not greenfield */ + } + txwi->phy = htole16(mcs); + + /* check if RTS/CTS or CTS-to-self protection is required */ + if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && + ((m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) || + ((ic->ic_flags & IEEE80211_F_USEPROT) && + rt2860_rates[ridx].phy == IEEE80211_T_OFDM) || + ((ic->ic_htprotmode == IEEE80211_PROT_RTSCTS) && + rt2860_rates[ridx].phy == IEEE80211_T_HT))) + txwi->txop |= MTW_TX_TXOP_HT; + else + txwi->txop |= MTW_TX_TXOP_BACKOFF; + +} + +/* This function must be called locked */ +static int +mtw_tx(struct mtw_softc *sc, struct mbuf *m, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_frame *wh; + + + //const struct ieee80211_txparam *tp = ni->ni_txparms; + struct mtw_node *rn = MTW_NODE(ni); + struct mtw_tx_data *data; + struct mtw_txd *txd; + struct mtw_txwi *txwi; + uint16_t qos; + uint16_t dur; + uint16_t qid; + uint8_t type; + uint8_t tid; + uint16_t ridx; + uint8_t ctl_ridx; + uint16_t qflags; + uint8_t xflags = 0; + + int hasqos; + + MTW_LOCK_ASSERT(sc, MA_OWNED); + + wh = mtod(m, struct ieee80211_frame *); + const struct ieee80211_txparam *tp = ni->ni_txparms; + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + qflags = htole16(MTW_TXD_DATA | MTW_TXD_80211 | + MTW_TXD_WLAN | MTW_TXD_QSEL_HCCA); + + if ((hasqos = IEEE80211_QOS_HAS_SEQ(wh))) { + uint8_t *frm; + frm = ieee80211_getqos(wh); + + + //device_printf(sc->sc_dev,"JSS:frm:%d",*frm); + qos = le16toh(*(const uint16_t *)frm); + tid = ieee80211_gettid(wh); + qid = TID_TO_WME_AC(tid); + qflags |= MTW_TXD_QSEL_EDCA; + } else { + qos = 0; + tid = 0; + qid = WME_AC_BE; + } + if (type & IEEE80211_FC0_TYPE_MGT) { + qid = 0; + } + + if (type != IEEE80211_FC0_TYPE_DATA) + qflags |= htole16(MTW_TXD_WIV); + + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + type != IEEE80211_FC0_TYPE_DATA || m->m_flags & M_EAPOL) { + /* XXX TODO: methodize for 11n; use MCS0 for 11NA/11NG */ + ridx = (ic->ic_curmode == IEEE80211_MODE_11A + || ic->ic_curmode == IEEE80211_MODE_11NA) ? + MTW_RIDX_OFDM6 : MTW_RIDX_CCK1; + if (type == IEEE80211_MODE_11NG) { + ridx = 12; + } + ctl_ridx = rt2860_rates[ridx].ctl_ridx; + } else { + if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { + ridx = rn->fix_ridx; + + } else { + ridx = rn->amrr_ridx; + ctl_ridx = rt2860_rates[ridx].ctl_ridx; + } + } + + if (hasqos) + xflags = 0; + else + xflags = MTW_TX_NSEQ; + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && + (!hasqos || + (qos & IEEE80211_QOS_ACKPOLICY) != + IEEE80211_QOS_ACKPOLICY_NOACK)) { + xflags |= MTW_TX_ACK; + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + dur = rt2860_rates[ctl_ridx].sp_ack_dur; + else + dur = rt2860_rates[ctl_ridx].lp_ack_dur; + USETW(wh->i_dur, dur); + } + /* reserve slots for mgmt packets, just in case */ + if (sc->sc_epq[qid].tx_nfree < 3) { + MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "tx ring %d is full\n", qid); + return (-1); + } + + data = STAILQ_FIRST(&sc->sc_epq[qid].tx_fh); + STAILQ_REMOVE_HEAD(&sc->sc_epq[qid].tx_fh, next); + sc->sc_epq[qid].tx_nfree--; + + txd = (struct mtw_txd *)&data->desc; + txd->flags = qflags; + + txwi = (struct mtw_txwi *)(txd + 1); + txwi->xflags = xflags; + txwi->wcid = (type == IEEE80211_FC0_TYPE_DATA) ? + + MTW_AID2WCID(ni->ni_associd) : + 0xff; + + /* clear leftover garbage bits */ + txwi->flags = 0; + txwi->txop = 0; + + data->m = m; + data->ni = ni; + data->ridx = ridx; + + mtw_set_tx_desc(sc, data); + + /* + * The chip keeps track of 2 kind of Tx stats, + * * TX_STAT_FIFO, for per WCID stats, and + * * TX_STA_CNT0 for all-TX-in-one stats. + * + * To use FIFO stats, we need to store MCS into the driver-private + * PacketID field. So that, we can tell whose stats when we read them. + * We add 1 to the MCS because setting the PacketID field to 0 means + * that we don't want feedback in TX_STAT_FIFO. + * And, that's what we want for STA mode, since TX_STA_CNT0 does the + * job. + * + * FIFO stats doesn't count Tx with WCID 0xff, so we do this in + * run_tx(). + */ + + if (sc->rvp_cnt > 1 || vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_MBSS) { + + /* + * Unlike PCI based devices, we don't get any interrupt from + * USB devices, so we simulate FIFO-is-full interrupt here. + * Ralink recommends to drain FIFO stats every 100 ms, but 16 + * slots quickly get fulled. To prevent overflow, increment a + * counter on every FIFO stat request, so we know how many slots + * are left. We do this only in HOSTAP or multiple vap mode + * since FIFO stats are used only in those modes. We just drain + * stats. AMRR gets updated every 1 sec by run_ratectl_cb() via + * callout. Call it early. Otherwise overflow. + */ + if (sc->fifo_cnt++ == 10) { + /* + * With multiple vaps or if_bridge, if_start() is called + * with a non-sleepable lock, tcpinp. So, need to defer. + */ + uint32_t i = MTW_CMDQ_GET(&sc->cmdq_store); + MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "cmdq_store=%d\n", i); + sc->cmdq[i].func = mtw_drain_fifo; + sc->cmdq[i].arg0 = sc; + ieee80211_runtask(ic, &sc->cmdq_task); + } + } + + STAILQ_INSERT_TAIL(&sc->sc_epq[qid].tx_qh, data, next); + usbd_transfer_start(sc->sc_xfer[mtw_wme_ac_xfer_map[qid]]); + + MTW_DPRINTF(sc, MTW_DEBUG_XMIT, + "sending data frame len=%d rate=%d qid=%d\n", + m->m_pkthdr.len + + (int)(sizeof(struct mtw_txd) + sizeof(struct mtw_txwi)), + rt2860_rates[ridx].rate, qid); + + return (0); + } + +static int +mtw_tx_mgt(struct mtw_softc *sc, struct mbuf *m, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct mtw_node *rn = MTW_NODE(ni); + struct mtw_tx_data *data; + struct ieee80211_frame *wh; + struct mtw_txd *txd; + struct mtw_txwi *txwi; + uint8_t type; + uint16_t dur; + uint8_t ridx = rn->mgt_ridx; + uint8_t xflags = 0; + uint8_t wflags = 0; + + MTW_LOCK_ASSERT(sc, MA_OWNED); + + wh = mtod(m, struct ieee80211_frame *); + + /* tell hardware to add timestamp for probe responses */ + if ((wh->i_fc[0] & + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == + (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP)) + wflags |= MTW_TX_TS; + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + xflags |= MTW_TX_ACK; + + dur = ieee80211_ack_duration(ic->ic_rt, rt2860_rates[ridx].rate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + USETW(wh->i_dur, dur); + } + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + if (sc->sc_epq[0].tx_nfree == 0) + /* let caller free mbuf */ + return (EIO); + data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh); + STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next); + sc->sc_epq[0].tx_nfree--; + + txd = (struct mtw_txd *)&data->desc; + txd->flags = htole16( + MTW_TXD_DATA | MTW_TXD_80211 | MTW_TXD_WLAN | MTW_TXD_QSEL_EDCA); + if (type != IEEE80211_FC0_TYPE_DATA) + txd->flags |= htole16(MTW_TXD_WIV); + + txwi = (struct mtw_txwi *)(txd + 1); + txwi->wcid = 0xff; + txwi->xflags = xflags; + txwi->flags = wflags; + + txwi->txop = 0; /* clear leftover garbage bits */ + + data->m = m; + data->ni = ni; + data->ridx = ridx; + + MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "sending mgt frame len=%d rate=%d\n", + m->m_pkthdr.len + + (int)(sizeof(struct mtw_txd) + sizeof(struct mtw_txwi)), + rt2860_rates[ridx].rate); + + STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); + + usbd_transfer_start(sc->sc_xfer[MTW_BULK_TX_BE]); + + return (0); +} + +static int +mtw_sendprot(struct mtw_softc *sc, const struct mbuf *m, + struct ieee80211_node *ni, int prot, int rate) +{ + struct ieee80211com *ic = ni->ni_ic; + struct mtw_tx_data *data; + struct mtw_txd *txd; + struct mtw_txwi *txwi; + struct mbuf *mprot; + int ridx; + int protrate; + uint8_t wflags = 0; + uint8_t xflags = 0; + + MTW_LOCK_ASSERT(sc, MA_OWNED); + + /* check that there are free slots before allocating the mbuf */ + if (sc->sc_epq[0].tx_nfree == 0) + /* let caller free mbuf */ + return (ENOBUFS); + + mprot = ieee80211_alloc_prot(ni, m, rate, prot); + if (mprot == NULL) { + if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); + MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "could not allocate mbuf\n"); + return (ENOBUFS); + } + + protrate = ieee80211_ctl_rate(ic->ic_rt, rate); + wflags = MTW_TX_FRAG; + xflags = 0; + if (prot == IEEE80211_PROT_RTSCTS) + xflags |= MTW_TX_ACK; + + data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh); + STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next); + sc->sc_epq[0].tx_nfree--; + + txd = (struct mtw_txd *)&data->desc; + txd->flags = RT2860_TX_QSEL_EDCA; + txwi = (struct mtw_txwi *)(txd + 1); + txwi->wcid = 0xff; + txwi->flags = wflags; + txwi->xflags = xflags; + txwi->txop = 0; /* clear leftover garbage bits */ + + data->m = mprot; + data->ni = ieee80211_ref_node(ni); + + /* XXX TODO: methodize with MCS rates */ + for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++) + if (rt2860_rates[ridx].rate == protrate) + break; + data->ridx = ridx; + + mtw_set_tx_desc(sc, data); + MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "sending prot len=%u rate=%u\n", + m->m_pkthdr.len, rate); + + STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); + + usbd_transfer_start(sc->sc_xfer[0]); + + return (0); +} + +static int +mtw_tx_param(struct mtw_softc *sc, struct mbuf *m, struct ieee80211_node *ni, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct mtw_tx_data *data; + struct mtw_txd *txd; + struct mtw_txwi *txwi; + uint8_t ridx; + uint8_t rate; + uint8_t opflags = 0; + uint8_t xflags = 0; + int error; + + MTW_LOCK_ASSERT(sc, MA_OWNED); + + KASSERT(params != NULL, ("no raw xmit params")); + + rate = params->ibp_rate0; + if (!ieee80211_isratevalid(ic->ic_rt, rate)) { + /* let caller free mbuf */ + return (EINVAL); + } + + if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) + xflags |= MTW_TX_ACK; + if (params->ibp_flags & (IEEE80211_BPF_RTS | IEEE80211_BPF_CTS)) { + error = mtw_sendprot(sc, m, ni, + params->ibp_flags & IEEE80211_BPF_RTS ? + IEEE80211_PROT_RTSCTS : + IEEE80211_PROT_CTSONLY, + rate); + if (error) { + device_printf(sc->sc_dev, "%s:%d %d\n", __FILE__, + __LINE__, error); + return (error); + } + opflags |= MTW_TX_TXOP_SIFS; + } + + if (sc->sc_epq[0].tx_nfree == 0) { + /* let caller free mbuf */ + MTW_DPRINTF(sc, MTW_DEBUG_XMIT, + "sending raw frame, but tx ring is full\n"); + return (EIO); + } + data = STAILQ_FIRST(&sc->sc_epq[0].tx_fh); + STAILQ_REMOVE_HEAD(&sc->sc_epq[0].tx_fh, next); + sc->sc_epq[0].tx_nfree--; + + txd = (struct mtw_txd *)&data->desc; + txd->flags = htole16( + MTW_TXD_DATA | MTW_TXD_80211 | MTW_TXD_WLAN | MTW_TXD_QSEL_EDCA); + // txd->flags = htole16(MTW_TXD_QSEL_EDCA); + txwi = (struct mtw_txwi *)(txd + 1); + txwi->wcid = 0xff; + txwi->xflags = xflags; + txwi->txop = opflags; + txwi->flags = 0; /* clear leftover garbage bits */ + + data->m = m; + data->ni = ni; + /* XXX TODO: methodize with MCS rates */ + for (ridx = 0; ridx < MTW_RIDX_MAX; ridx++) + if (rt2860_rates[ridx].rate == rate) + break; + data->ridx = ridx; + + mtw_set_tx_desc(sc, data); + + MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "sending raw frame len=%u rate=%u\n", + m->m_pkthdr.len, rate); + + STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); + + usbd_transfer_start(sc->sc_xfer[MTW_BULK_RAW_TX]); + + return (0); +} + +static int +mtw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct mtw_softc *sc = ni->ni_ic->ic_softc; + int error = 0; + MTW_LOCK(sc); + /* prevent management frames from being sent if we're not ready */ + if (!(sc->sc_flags & MTW_RUNNING)) { + error = ENETDOWN; + goto done; + } + + if (params == NULL) { + /* tx mgt packet */ + if ((error = mtw_tx_mgt(sc, m, ni)) != 0) { + MTW_DPRINTF(sc, MTW_DEBUG_XMIT, "mgt tx failed\n"); + goto done; + } + } else { + /* tx raw packet with param */ + if ((error = mtw_tx_param(sc, m, ni, params)) != 0) { + MTW_DPRINTF(sc, MTW_DEBUG_XMIT, + "tx with param failed\n"); + goto done; + } + } + +done: + + MTW_UNLOCK(sc); + + if (error != 0) { + if (m != NULL) + m_freem(m); + } + + return (error); +} + +static int +mtw_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct mtw_softc *sc = ic->ic_softc; + int error; + MTW_LOCK(sc); + if ((sc->sc_flags & MTW_RUNNING) == 0) { + MTW_UNLOCK(sc); + return (ENXIO); + } + error = mbufq_enqueue(&sc->sc_snd, m); + if (error) { + MTW_UNLOCK(sc); + return (error); + } + mtw_start(sc); + MTW_UNLOCK(sc); + + return (0); +} + +static void +mtw_start(struct mtw_softc *sc) +{ + struct ieee80211_node *ni; + struct mbuf *m; + + MTW_LOCK_ASSERT(sc, MA_OWNED); + + if ((sc->sc_flags & MTW_RUNNING) == 0) { + + return; + } + while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + if (mtw_tx(sc, m, ni) != 0) { + mbufq_prepend(&sc->sc_snd, m); + break; + } + } +} + +static void +mtw_parent(struct ieee80211com *ic) +{ + + struct mtw_softc *sc = ic->ic_softc; + + MTW_LOCK(sc); + if (sc->sc_detached) { + MTW_UNLOCK(sc); + return; + } + + if (!(sc->sc_flags & MTW_RUNNING) && ic->ic_nrunning > 0) { + mtw_init_locked(sc); + MTW_UNLOCK(sc); + ieee80211_start_all(ic); + return; + } + if (!(sc->sc_flags & MTW_RUNNING) && ic->ic_nrunning > 0) { + mtw_update_promisc_locked(sc); + MTW_UNLOCK(sc); + return; + } + if ((sc->sc_flags & MTW_RUNNING) && sc->rvp_cnt <= 1 && + ic->ic_nrunning == 0) { + mtw_stop(sc); + MTW_UNLOCK(sc); + return; + } + return; +} + +static void +mt7601_set_agc(struct mtw_softc *sc, uint8_t agc) +{ + uint8_t bbp; + + mtw_bbp_write(sc, 66, agc); + mtw_bbp_write(sc, 195, 0x87); + bbp = (agc & 0xf0) | 0x08; + mtw_bbp_write(sc, 196, bbp); +} + +static int +mtw_mcu_calibrate(struct mtw_softc *sc, int func, uint32_t val) +{ + struct mtw_mcu_cmd_8 cmd; + + cmd.func = htole32(func); + cmd.val = htole32(val); + return (mtw_mcu_cmd(sc, 31, &cmd, sizeof(struct mtw_mcu_cmd_8))); +} + +static int +mtw_rf_write(struct mtw_softc *sc, uint8_t bank, uint8_t reg, uint8_t val) +{ + uint32_t tmp; + int error, ntries, shift; + + for (ntries = 0; ntries < 10; ntries++) { + if ((error = mtw_read(sc, MTW_RF_CSR, &tmp)) != 0) + return (error); + if (!(tmp & MTW_RF_CSR_KICK)) + break; + } + if (ntries == 10) + return (ETIMEDOUT); + + if (sc->asic_ver == 0x7601) + shift = MT7601_BANK_SHIFT; + else + shift = MT7610_BANK_SHIFT; + + tmp = MTW_RF_CSR_WRITE | MTW_RF_CSR_KICK | (bank & 0xf) << shift | + reg << 8 | val; + return (mtw_write(sc, MTW_RF_CSR, tmp)); +} + +void +mtw_select_chan_group(struct mtw_softc *sc, int group) +{ + uint32_t tmp; + uint8_t bbp; + + /* Tx band 20MHz 2G */ + mtw_read(sc, MTW_TX_BAND_CFG, &tmp); + tmp &= ~( + MTW_TX_BAND_SEL_2G | MTW_TX_BAND_SEL_5G | MTW_TX_BAND_UPPER_40M); + tmp |= (group == 0) ? MTW_TX_BAND_SEL_2G : MTW_TX_BAND_SEL_5G; + mtw_write(sc, MTW_TX_BAND_CFG, tmp); + + /* select 20 MHz bandwidth */ + mtw_bbp_read(sc, 4, &bbp); + bbp &= ~0x18; + bbp |= 0x40; + mtw_bbp_write(sc, 4, bbp); + + /* calibrate BBP */ + mtw_bbp_write(sc, 69, 0x12); + mtw_bbp_write(sc, 91, 0x07); + mtw_bbp_write(sc, 195, 0x23); + mtw_bbp_write(sc, 196, 0x17); + mtw_bbp_write(sc, 195, 0x24); + mtw_bbp_write(sc, 196, 0x06); + mtw_bbp_write(sc, 195, 0x81); + mtw_bbp_write(sc, 196, 0x12); + mtw_bbp_write(sc, 195, 0x83); + mtw_bbp_write(sc, 196, 0x17); + mtw_rf_write(sc, 5, 8, 0x00); + // mtw_mcu_calibrate(sc, 0x6, 0x10001); + + /* set initial AGC value */ + mt7601_set_agc(sc, 0x14); +} + +static int +mtw_rf_read(struct mtw_softc *sc, uint8_t bank, uint8_t reg, uint8_t *val) +{ + uint32_t tmp; + int error, ntries, shift; + + for (ntries = 0; ntries < 100; ntries++) { + if ((error = mtw_read(sc, MTW_RF_CSR, &tmp)) != 0) + return (error); + if (!(tmp & MTW_RF_CSR_KICK)) + break; + } + if (ntries == 100) + return (ETIMEDOUT); + + if (sc->asic_ver == 0x7601) + shift = MT7601_BANK_SHIFT; + else + shift = MT7610_BANK_SHIFT; + + tmp = MTW_RF_CSR_KICK | (bank & 0xf) << shift | reg << 8; + if ((error = mtw_write(sc, MTW_RF_CSR, tmp)) != 0) + return (error); + + for (ntries = 0; ntries < 100; ntries++) { + if ((error = mtw_read(sc, MTW_RF_CSR, &tmp)) != 0) + return (error); + if (!(tmp & MTW_RF_CSR_KICK)) + break; + } + if (ntries == 100) + return (ETIMEDOUT); + + *val = tmp & 0xff; + return (0); +} +static void +mt7601_set_chan(struct mtw_softc *sc, u_int chan) +{ + uint32_t tmp; + uint8_t bbp, rf, txpow1; + int i; + /* find the settings for this channel */ + for (i = 0; mt7601_rf_chan[i].chan != chan; i++) + ; + + mtw_rf_write(sc, 0, 17, mt7601_rf_chan[i].r17); + mtw_rf_write(sc, 0, 18, mt7601_rf_chan[i].r18); + mtw_rf_write(sc, 0, 19, mt7601_rf_chan[i].r19); + mtw_rf_write(sc, 0, 20, mt7601_rf_chan[i].r20); + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + + /* Tx automatic level control */ + mtw_read(sc, MTW_TX_ALC_CFG0, &tmp); + tmp &= ~0x3f3f; + tmp |= (txpow1 & 0x3f); + mtw_write(sc, MTW_TX_ALC_CFG0, tmp); + + /* LNA */ + mtw_bbp_write(sc, 62, 0x37 - sc->lna[0]); + mtw_bbp_write(sc, 63, 0x37 - sc->lna[0]); + mtw_bbp_write(sc, 64, 0x37 - sc->lna[0]); + + /* VCO calibration */ + mtw_rf_write(sc, 0, 4, 0x0a); + mtw_rf_write(sc, 0, 5, 0x20); + mtw_rf_read(sc, 0, 4, &rf); + mtw_rf_write(sc, 0, 4, rf | 0x80); + + /* select 20 MHz bandwidth */ + mtw_bbp_read(sc, 4, &bbp); + bbp &= ~0x18; + bbp |= 0x40; + mtw_bbp_write(sc, 4, bbp); + mtw_bbp_write(sc, 178, 0xff); +} + +static int +mtw_set_chan(struct mtw_softc *sc, struct ieee80211_channel *c) +{ + struct ieee80211com *ic = &sc->sc_ic; + u_int chan, group; + + chan = ieee80211_chan2ieee(ic, c); + if (chan == 0 || chan == IEEE80211_CHAN_ANY) + return (EINVAL); + + /* determine channel group */ + if (chan <= 14) + group = 0; + else if (chan <= 64) + group = 1; + else if (chan <= 128) + group = 2; + else + group = 3; + + if (group != sc->sc_chan_group || !sc->sc_bw_calibrated) + mtw_select_chan_group(sc, group); + + sc->sc_chan_group = group; + + /* chipset specific */ + if (sc->asic_ver == 0x7601) + mt7601_set_chan(sc, chan); + + DELAY(1000); + return (0); +} + +static void +mtw_set_channel(struct ieee80211com *ic) +{ + struct mtw_softc *sc = ic->ic_softc; + + MTW_LOCK(sc); + mtw_set_chan(sc, ic->ic_curchan); + MTW_UNLOCK(sc); + + return; +} + +static void +mtw_getradiocaps(struct ieee80211com *ic, int maxchans, int *nchans, + struct ieee80211_channel chans[]) +{ + // struct mtw_softc *sc = ic->ic_softc; + uint8_t bands[IEEE80211_MODE_BYTES]; + + memset(bands, 0, sizeof(bands)); + setbit(bands, IEEE80211_MODE_11B); + setbit(bands, IEEE80211_MODE_11G); + setbit(bands, IEEE80211_MODE_11NG); + + /* Note: for now, only support HT20 channels */ + ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, bands, 0); +} + +static void +mtw_scan_start(struct ieee80211com *ic) +{ + struct mtw_softc *sc = ic->ic_softc; + MTW_LOCK(sc); + /* abort TSF synchronization */ + mtw_abort_tsf_sync(sc); + mtw_set_bssid(sc, ieee80211broadcastaddr); + + MTW_UNLOCK(sc); + + return; +} + +static void +mtw_scan_end(struct ieee80211com *ic) +{ + struct mtw_softc *sc = ic->ic_softc; + + MTW_LOCK(sc); + + mtw_enable_tsf_sync(sc); + mtw_set_bssid(sc, sc->sc_bssid); + + MTW_UNLOCK(sc); + + return; +} + +/* + * Could be called from ieee80211_node_timeout() + * (non-sleepable thread) + */ +static void +mtw_update_beacon(struct ieee80211vap *vap, int item) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; + struct ieee80211_node *ni = vap->iv_bss; + struct mtw_softc *sc = ic->ic_softc; + struct mtw_vap *rvp = MTW_VAP(vap); + int mcast = 0; + uint32_t i; + + switch (item) { + case IEEE80211_BEACON_ERP: + mtw_updateslot(ic); + break; + case IEEE80211_BEACON_HTINFO: + mtw_updateprot(ic); + break; + case IEEE80211_BEACON_TIM: + mcast = 1; /*TODO*/ + break; + default: + break; + } + + setbit(bo->bo_flags, item); + if (rvp->beacon_mbuf == NULL) { + rvp->beacon_mbuf = ieee80211_beacon_alloc(ni); + if (rvp->beacon_mbuf == NULL) + return; + } + ieee80211_beacon_update(ni, rvp->beacon_mbuf, mcast); + + i = MTW_CMDQ_GET(&sc->cmdq_store); + MTW_DPRINTF(sc, MTW_DEBUG_BEACON, "cmdq_store=%d\n", i); + sc->cmdq[i].func = mtw_update_beacon_cb; + sc->cmdq[i].arg0 = vap; + ieee80211_runtask(ic, &sc->cmdq_task); + + return; +} + +static void +mtw_update_beacon_cb(void *arg) +{ + + struct ieee80211vap *vap = arg; + struct ieee80211_node *ni = vap->iv_bss; + struct mtw_vap *rvp = MTW_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct mtw_softc *sc = ic->ic_softc; + struct mtw_txwi txwi; + struct mbuf *m; + uint16_t txwisize; + uint8_t ridx; + if (ni->ni_chan == IEEE80211_CHAN_ANYC) + return; + if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) + return; + + /* + * No need to call ieee80211_beacon_update(), mtw_update_beacon() + * is taking care of appropriate calls. + */ + if (rvp->beacon_mbuf == NULL) { + rvp->beacon_mbuf = ieee80211_beacon_alloc(ni); + if (rvp->beacon_mbuf == NULL) + return; + } + m = rvp->beacon_mbuf; + + memset(&txwi, 0, sizeof(txwi)); + txwi.wcid = 0xff; + txwi.len = htole16(m->m_pkthdr.len); + + /* send beacons at the lowest available rate */ + ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? MTW_RIDX_OFDM6 : + MTW_RIDX_CCK1; + txwi.phy = htole16(rt2860_rates[ridx].mcs); + if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM) + txwi.phy |= htole16(MTW_PHY_OFDM); + txwi.txop = MTW_TX_TXOP_HT; + txwi.flags = MTW_TX_TS; + txwi.xflags = MTW_TX_NSEQ; + + txwisize = sizeof(txwi); + mtw_write_region_1(sc, MTW_BCN_BASE, (uint8_t *)&txwi, txwisize); + mtw_write_region_1(sc, MTW_BCN_BASE + txwisize, mtod(m, uint8_t *), + (m->m_pkthdr.len + 1) & ~1); +} + +static void +mtw_updateprot(struct ieee80211com *ic) +{ + struct mtw_softc *sc = ic->ic_softc; + uint32_t i; + + i = MTW_CMDQ_GET(&sc->cmdq_store); + MTW_DPRINTF(sc, MTW_DEBUG_BEACON, "test cmdq_store=%d\n", i); + sc->cmdq[i].func = mtw_updateprot_cb; + sc->cmdq[i].arg0 = ic; + ieee80211_runtask(ic, &sc->cmdq_task); +} + +static void +mtw_updateprot_cb(void *arg) +{ + + struct ieee80211com *ic = arg; + struct mtw_softc *sc = ic->ic_softc; + uint32_t tmp; + + tmp = RT2860_RTSTH_EN | RT2860_PROT_NAV_SHORT | RT2860_TXOP_ALLOW_ALL; + /* setup protection frame rate (MCS code) */ + tmp |= (ic->ic_curmode == IEEE80211_MODE_11A) ? + rt2860_rates[MTW_RIDX_OFDM6].mcs | MTW_PHY_OFDM : + rt2860_rates[MTW_RIDX_CCK11].mcs; + + /* CCK frames don't require protection */ + mtw_write(sc, MTW_CCK_PROT_CFG, tmp); + if (ic->ic_flags & IEEE80211_F_USEPROT) { + if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + tmp |= RT2860_PROT_CTRL_RTS_CTS; + else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) + tmp |= RT2860_PROT_CTRL_CTS; + } + mtw_write(sc, MTW_OFDM_PROT_CFG, tmp); +} + +static void +mtw_usb_timeout_cb(void *arg) +{ + struct ieee80211vap *vap = arg; + struct mtw_softc *sc = vap->iv_ic->ic_softc; + + MTW_LOCK_ASSERT(sc, MA_OWNED); + + if (vap->iv_state == IEEE80211_S_SCAN) { + MTW_DPRINTF(sc, MTW_DEBUG_USB | MTW_DEBUG_STATE, + "timeout caused by scan\n"); + /* cancel bgscan */ + ieee80211_cancel_scan(vap); + } else { + MTW_DPRINTF(sc, MTW_DEBUG_USB | MTW_DEBUG_STATE, + "timeout by unknown cause\n"); + } +} +static int mtw_reset(struct mtw_softc *sc) +{ + + usb_device_request_t req; + uint16_t tmp; + uint16_t actlen; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = MTW_RESET; + USETW(req.wValue, 1); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + return (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, + &req, &tmp, 0, &actlen, 1000)); + +} + + +static void +mtw_update_promisc_locked(struct mtw_softc *sc) +{ + + uint32_t tmp; + + mtw_read(sc, MTW_RX_FILTR_CFG, &tmp); + + tmp |= MTW_DROP_UC_NOME; + if (sc->sc_ic.ic_promisc > 0) + tmp &= ~MTW_DROP_UC_NOME; + + mtw_write(sc, MTW_RX_FILTR_CFG, tmp); + + MTW_DPRINTF(sc, MTW_DEBUG_RECV, "%s promiscuous mode\n", + (sc->sc_ic.ic_promisc > 0) ? "entering" : "leaving"); +} + +static void +mtw_update_promisc(struct ieee80211com *ic) +{ + struct mtw_softc *sc = ic->ic_softc; + + if ((sc->sc_flags & MTW_RUNNING) == 0) + return; + + MTW_LOCK(sc); + mtw_update_promisc_locked(sc); + MTW_UNLOCK(sc); +} + +static void +mtw_enable_tsf_sync(struct mtw_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t tmp; + int error; + mtw_read(sc, MTW_BCN_TIME_CFG, &tmp); + tmp &= ~0x1fffff; + tmp |= vap->iv_bss->ni_intval * 16; + tmp |= MTW_TSF_TIMER_EN | MTW_TBTT_TIMER_EN; + + /* local TSF is always updated with remote TSF on beacon reception */ + tmp |= 1 << MTW_TSF_SYNC_MODE_SHIFT; + error = mtw_write(sc, MTW_BCN_TIME_CFG, tmp); + if (error != 0) { + device_printf(sc->sc_dev, "enable_tsf_sync failed error:%d\n", + error); + } + return; +} + +static void +mtw_enable_mrr(struct mtw_softc *sc) +{ +#define CCK(mcs) (mcs) + +#define OFDM(mcs) (1 << 3 | (mcs)) + mtw_write(sc, MTW_LG_FBK_CFG0, + OFDM(6) << 28 | /* 54->48 */ + OFDM(5) << 24 | /* 48->36 */ + OFDM(4) << 20 | /* 36->24 */ + OFDM(3) << 16 | /* 24->18 */ + OFDM(2) << 12 | /* 18->12 */ + OFDM(1) << 8 | /* 12-> 9 */ + OFDM(0) << 4 | /* 9-> 6 */ + OFDM(0)); /* 6-> 6 */ + + mtw_write(sc, MTW_LG_FBK_CFG1, + CCK(2) << 12 | /* 11->5.5 */ + CCK(1) << 8 | /* 5.5-> 2 */ + CCK(0) << 4 | /* 2-> 1 */ + CCK(0)); /* 1-> 1 */ +#undef OFDM +#undef CCK +} + +static void +mtw_set_txpreamble(struct mtw_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint32_t tmp; + + mtw_read(sc, MTW_AUTO_RSP_CFG, &tmp); + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + tmp |= MTW_CCK_SHORT_EN; + else + tmp &= ~MTW_CCK_SHORT_EN; + mtw_write(sc, MTW_AUTO_RSP_CFG, tmp); +} + +static void +mtw_set_basicrates(struct mtw_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + + /* set basic rates mask */ + if (ic->ic_curmode == IEEE80211_MODE_11B) + mtw_write(sc, MTW_LEGACY_BASIC_RATE, 0x003); + else if (ic->ic_curmode == IEEE80211_MODE_11A) + mtw_write(sc, MTW_LEGACY_BASIC_RATE, 0x150); + else /* 11g */ + mtw_write(sc, MTW_LEGACY_BASIC_RATE, 0x17f); +} + +static void +mtw_set_bssid(struct mtw_softc *sc, const uint8_t *bssid) +{ + mtw_write(sc, MTW_MAC_BSSID_DW0, + bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24); + mtw_write(sc, MTW_MAC_BSSID_DW1, bssid[4] | bssid[5] << 8); +} + +static void +mtw_set_macaddr(struct mtw_softc *sc, const uint8_t *addr) +{ + mtw_write(sc, MTW_MAC_ADDR_DW0, + addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); + mtw_write(sc, MTW_MAC_ADDR_DW1, addr[4] | addr[5] << 8 | 0xff << 16); +} + +static void +mtw_updateslot(struct ieee80211com *ic) +{ + + struct mtw_softc *sc = ic->ic_softc; + uint32_t i; + + i = MTW_CMDQ_GET(&sc->cmdq_store); + MTW_DPRINTF(sc, MTW_DEBUG_BEACON, "cmdq_store=%d\n", i); + sc->cmdq[i].func = mtw_updateslot_cb; + sc->cmdq[i].arg0 = ic; + ieee80211_runtask(ic, &sc->cmdq_task); + + return; +} + +/* ARGSUSED */ +static void +mtw_updateslot_cb(void *arg) +{ + struct ieee80211com *ic = arg; + struct mtw_softc *sc = ic->ic_softc; + uint32_t tmp; + mtw_read(sc, MTW_BKOFF_SLOT_CFG, &tmp); + tmp &= ~0xff; + tmp |= IEEE80211_GET_SLOTTIME(ic); + mtw_write(sc, MTW_BKOFF_SLOT_CFG, tmp); +} + +static void +mtw_update_mcast(struct ieee80211com *ic) +{ +} + +static int8_t +mtw_rssi2dbm(struct mtw_softc *sc, uint8_t rssi, uint8_t rxchain) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_channel *c = ic->ic_curchan; + int delta; + + if (IEEE80211_IS_CHAN_5GHZ(c)) { + u_int chan = ieee80211_chan2ieee(ic, c); + delta = sc->rssi_5ghz[rxchain]; + + /* determine channel group */ + if (chan <= 64) + delta -= sc->lna[1]; + else if (chan <= 128) + delta -= sc->lna[2]; + else + delta -= sc->lna[3]; + } else + delta = sc->rssi_2ghz[rxchain] - sc->lna[0]; + + return (-12 - delta - rssi); +} +static int +mt7601_bbp_init(struct mtw_softc *sc) +{ + uint8_t bbp; + int i, error, ntries; + + /* wait for BBP to wake up */ + for (ntries = 0; ntries < 20; ntries++) { + if ((error = mtw_bbp_read(sc, 0, &bbp)) != 0) + return (error); + if (bbp != 0 && bbp != 0xff) + break; + } + + if (ntries == 20) + return (ETIMEDOUT); + + mtw_bbp_read(sc, 3, &bbp); + mtw_bbp_write(sc, 3, 0); + mtw_bbp_read(sc, 105, &bbp); + mtw_bbp_write(sc, 105, 0); + + /* initialize BBP registers to default values */ + for (i = 0; i < nitems(mt7601_def_bbp); i++) { + if ((error = mtw_bbp_write(sc, mt7601_def_bbp[i].reg, + mt7601_def_bbp[i].val)) != 0) + return (error); + } + + sc->sc_bw_calibrated = 0; + + return (0); +} + +static int +mt7601_rf_init(struct mtw_softc *sc) +{ + int i, error; + + /* RF bank 0 */ + for (i = 0; i < nitems(mt7601_rf_bank0); i++) { + error = mtw_rf_write(sc, 0, mt7601_rf_bank0[i].reg, + mt7601_rf_bank0[i].val); + if (error != 0) + return (error); + } + /* RF bank 4 */ + for (i = 0; i < nitems(mt7601_rf_bank4); i++) { + error = mtw_rf_write(sc, 4, mt7601_rf_bank4[i].reg, + mt7601_rf_bank4[i].val); + if (error != 0) + return (error); + } + /* RF bank 5 */ + for (i = 0; i < nitems(mt7601_rf_bank5); i++) { + error = mtw_rf_write(sc, 5, mt7601_rf_bank5[i].reg, + mt7601_rf_bank5[i].val); + if (error != 0) + return (error); + } + return (0); +} + +static int +mtw_txrx_enable(struct mtw_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint32_t tmp; + int error, ntries; + mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_MAC_TX_EN); + for (ntries = 0; ntries < 200; ntries++) { + if ((error = mtw_read(sc, MTW_WPDMA_GLO_CFG, &tmp)) != 0) { + return (error); + } + if ((tmp & (MTW_TX_DMA_BUSY | MTW_RX_DMA_BUSY)) == 0) + break; + mtw_delay(sc, 50); + } + if (ntries == 200) { + return (ETIMEDOUT); + } + + DELAY(50); + + tmp |= MTW_RX_DMA_EN | MTW_TX_DMA_EN | MTW_TX_WB_DDONE; + mtw_write(sc, MTW_WPDMA_GLO_CFG, tmp); + + /* enable Rx bulk aggregation (set timeout and limit) */ + tmp = MTW_USB_TX_EN | MTW_USB_RX_EN | MTW_USB_RX_AGG_EN | + MTW_USB_RX_AGG_TO(128) | MTW_USB_RX_AGG_LMT(2); + mtw_write(sc, MTW_USB_DMA_CFG, tmp); + + /* set Rx filter */ + tmp = MTW_DROP_CRC_ERR | MTW_DROP_PHY_ERR; + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + tmp |= MTW_DROP_UC_NOME | MTW_DROP_DUPL | MTW_DROP_CTS | + MTW_DROP_BA | MTW_DROP_ACK | MTW_DROP_VER_ERR | + MTW_DROP_CTRL_RSV | MTW_DROP_CFACK | MTW_DROP_CFEND; + if (ic->ic_opmode == IEEE80211_M_STA) + tmp |= MTW_DROP_RTS | MTW_DROP_PSPOLL; + } + mtw_write(sc, MTW_RX_FILTR_CFG, tmp); + + mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_MAC_RX_EN | MTW_MAC_TX_EN); + return (0); +} +static int +mt7601_rxdc_cal(struct mtw_softc *sc) +{ + uint32_t tmp; + uint8_t bbp; + int ntries; + + mtw_read(sc, MTW_MAC_SYS_CTRL, &tmp); + mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_MAC_RX_EN); + mtw_bbp_write(sc, 158, 0x8d); + mtw_bbp_write(sc, 159, 0xfc); + mtw_bbp_write(sc, 158, 0x8c); + mtw_bbp_write(sc, 159, 0x4c); + + for (ntries = 0; ntries < 20; ntries++) { + DELAY(300); + mtw_bbp_write(sc, 158, 0x8c); + mtw_bbp_read(sc, 159, &bbp); + if (bbp == 0x0c) + break; + } + + if (ntries == 20) + return (ETIMEDOUT); + + mtw_write(sc, MTW_MAC_SYS_CTRL, 0); + mtw_bbp_write(sc, 158, 0x8d); + mtw_bbp_write(sc, 159, 0xe0); + mtw_write(sc, MTW_MAC_SYS_CTRL, tmp); + return (0); +} + +static int +mt7601_r49_read(struct mtw_softc *sc, uint8_t flag, int8_t *val) +{ + uint8_t bbp; + + mtw_bbp_read(sc, 47, &bbp); + bbp = 0x90; + mtw_bbp_write(sc, 47, bbp); + bbp &= ~0x0f; + bbp |= flag; + mtw_bbp_write(sc, 47, bbp); + return (mtw_bbp_read(sc, 49, val)); +} + +static int +mt7601_rf_temperature(struct mtw_softc *sc, int8_t *val) +{ + uint32_t rfb, rfs; + uint8_t bbp; + int ntries; + + mtw_read(sc, MTW_RF_BYPASS0, &rfb); + mtw_read(sc, MTW_RF_SETTING0, &rfs); + mtw_write(sc, MTW_RF_BYPASS0, 0); + mtw_write(sc, MTW_RF_SETTING0, 0x10); + mtw_write(sc, MTW_RF_BYPASS0, 0x10); + + mtw_bbp_read(sc, 47, &bbp); + bbp &= ~0x7f; + bbp |= 0x10; + mtw_bbp_write(sc, 47, bbp); + + mtw_bbp_write(sc, 22, 0x40); + + for (ntries = 0; ntries < 10; ntries++) { + mtw_bbp_read(sc, 47, &bbp); + if ((bbp & 0x10) == 0) + break; + } + if (ntries == 10) + return (ETIMEDOUT); + + mt7601_r49_read(sc, MT7601_R47_TEMP, val); + + mtw_bbp_write(sc, 22, 0); + + mtw_bbp_read(sc, 21, &bbp); + bbp |= 0x02; + mtw_bbp_write(sc, 21, bbp); + bbp &= ~0x02; + mtw_bbp_write(sc, 21, bbp); + + mtw_write(sc, MTW_RF_BYPASS0, 0); + mtw_write(sc, MTW_RF_SETTING0, rfs); + mtw_write(sc, MTW_RF_BYPASS0, rfb); + return (0); +} + +static int +mt7601_rf_setup(struct mtw_softc *sc) +{ + uint32_t tmp; + uint8_t rf; + int error; + + if (sc->sc_rf_calibrated) + return (0); + + /* init RF registers */ + if ((error = mt7601_rf_init(sc)) != 0) + return (error); + + /* init frequency offset */ + mtw_rf_write(sc, 0, 12, sc->rf_freq_offset); + mtw_rf_read(sc, 0, 12, &rf); + + /* read temperature */ + mt7601_rf_temperature(sc, &rf); + sc->bbp_temp = rf; + device_printf(sc->sc_dev, "BBP temp 0x%x\n", rf); + + mtw_rf_read(sc, 0, 7, &rf); + if ((error = mtw_mcu_calibrate(sc, 0x1, 0)) != 0) + return (error); + mtw_delay(sc, 100); + mtw_rf_read(sc, 0, 7, &rf); + + /* Calibrate VCO RF 0/4 */ + mtw_rf_write(sc, 0, 4, 0x0a); + mtw_rf_write(sc, 0, 4, 0x20); + mtw_rf_read(sc, 0, 4, &rf); + mtw_rf_write(sc, 0, 4, rf | 0x80); + + if ((error = mtw_mcu_calibrate(sc, 0x9, 0)) != 0) + return (error); + if ((error = mt7601_rxdc_cal(sc)) != 0) + return (error); + if ((error = mtw_mcu_calibrate(sc, 0x6, 1)) != 0) + return (error); + if ((error = mtw_mcu_calibrate(sc, 0x6, 0)) != 0) + return (error); + if ((error = mtw_mcu_calibrate(sc, 0x4, 0)) != 0) + return (error); + if ((error = mtw_mcu_calibrate(sc, 0x5, 0)) != 0) + return (error); + + mtw_read(sc, MTW_LDO_CFG0, &tmp); + tmp &= ~(1 << 4); + tmp |= (1 << 2); + mtw_write(sc, MTW_LDO_CFG0, tmp); + + if ((error = mtw_mcu_calibrate(sc, 0x8, 0)) != 0) + return (error); + if ((error = mt7601_rxdc_cal(sc)) != 0) + return (error); + + sc->sc_rf_calibrated = 1; + return (0); +} + +static void +mtw_set_txrts(struct mtw_softc *sc) +{ + uint32_t tmp; + + /* set RTS threshold */ + mtw_read(sc, MTW_TX_RTS_CFG, &tmp); + tmp &= ~0xffff00; + tmp |= 0x1000 << MTW_RTS_THRES_SHIFT; + mtw_write(sc, MTW_TX_RTS_CFG, tmp); +} +static int +mtw_mcu_radio(struct mtw_softc *sc, int func, uint32_t val) +{ + struct mtw_mcu_cmd_16 cmd; + + cmd.r1 = htole32(func); + cmd.r2 = htole32(val); + cmd.r3 = 0; + cmd.r4 = 0; + return (mtw_mcu_cmd(sc, 8, &val, sizeof(struct mtw_mcu_cmd_16))); +} +static void +mtw_init_locked(struct mtw_softc *sc) +{ + + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t tmp; + int i, error, ridx, ntries; + if (ic->ic_nrunning > 1) + return; + mtw_stop(sc); + + for (i = 0; i != MTW_EP_QUEUES; i++) + mtw_setup_tx_list(sc, &sc->sc_epq[i]); + + for (ntries = 0; ntries < 100; ntries++) { + if ((error = mtw_read(sc, MTW_WPDMA_GLO_CFG, &tmp)) != 0) + goto fail; + if ((tmp & (MTW_TX_DMA_BUSY | MTW_RX_DMA_BUSY)) == 0) + break; + DELAY(1000); + } + if (ntries == 100) { + device_printf(sc->sc_dev, "timeout waiting for DMA engine\n"); + error = ETIMEDOUT; + goto fail; + } + tmp &= 0xff0; + tmp |= MTW_TX_WB_DDONE; + mtw_write(sc, MTW_WPDMA_GLO_CFG, tmp); + + mtw_set_leds(sc, MTW_LED_MODE_ON); + /* reset MAC and baseband */ + mtw_write(sc, MTW_MAC_SYS_CTRL, MTW_BBP_HRST | MTW_MAC_SRST); + mtw_write(sc, MTW_USB_DMA_CFG, 0); + mtw_write(sc, MTW_MAC_SYS_CTRL, 0); + + /* init MAC values */ + if (sc->asic_ver == 0x7601) { + for (i = 0; i < nitems(mt7601_def_mac); i++) + mtw_write(sc, mt7601_def_mac[i].reg, + mt7601_def_mac[i].val); + } + + /* wait while MAC is busy */ + for (ntries = 0; ntries < 100; ntries++) { + if ((error = mtw_read(sc, MTW_MAC_STATUS_REG, &tmp)) != 0) + goto fail; + if (!(tmp & (MTW_RX_STATUS_BUSY | MTW_TX_STATUS_BUSY))) + break; + DELAY(1000); + } + if (ntries == 100) { + error = ETIMEDOUT; + goto fail; + } + + /* set MAC address */ + + mtw_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr); + + /* clear WCID attribute table */ + mtw_set_region_4(sc, MTW_WCID_ATTR(0), 1, 8 * 32); + + mtw_write(sc, 0x1648, 0x00830083); + mtw_read(sc, MTW_FCE_L2_STUFF, &tmp); + tmp &= ~MTW_L2S_WR_MPDU_LEN_EN; + mtw_write(sc, MTW_FCE_L2_STUFF, tmp); + + /* RTS config */ + mtw_set_txrts(sc); + + /* clear Host to MCU mailbox */ + mtw_write(sc, MTW_BBP_CSR, 0); + mtw_write(sc, MTW_H2M_MAILBOX, 0); + + /* clear RX WCID search table */ + mtw_set_region_4(sc, MTW_WCID_ENTRY(0), 0xffffffff, 512); + + /* abort TSF synchronization */ + mtw_abort_tsf_sync(sc); + + mtw_read(sc, MTW_US_CYC_CNT, &tmp); + tmp = (tmp & ~0xff); + if (sc->asic_ver == 0x7601) + tmp |= 0x1e; + mtw_write(sc, MTW_US_CYC_CNT, tmp); + + /* clear shared key table */ + mtw_set_region_4(sc, MTW_SKEY(0, 0), 0, 8 * 32); + + /* clear IV/EIV table */ + mtw_set_region_4(sc, MTW_IVEIV(0), 0, 8 * 32); + + /* clear shared key mode */ + mtw_write(sc, MTW_SKEY_MODE_0_7, 0); + mtw_write(sc, MTW_SKEY_MODE_8_15, 0); + + /* txop truncation */ + mtw_write(sc, MTW_TXOP_CTRL_CFG, 0x0000583f); + + /* init Tx power for all Tx rates */ + for (ridx = 0; ridx < 5; ridx++) { + if (sc->txpow20mhz[ridx] == 0xffffffff) + continue; + mtw_write(sc, MTW_TX_PWR_CFG(ridx), sc->txpow20mhz[ridx]); + } + mtw_write(sc, MTW_TX_PWR_CFG7, 0); + mtw_write(sc, MTW_TX_PWR_CFG9, 0); + + mtw_read(sc, MTW_CMB_CTRL, &tmp); + tmp &= ~(1 << 18 | 1 << 14); + mtw_write(sc, MTW_CMB_CTRL, tmp); + + /* clear USB DMA */ + mtw_write(sc, MTW_USB_DMA_CFG, + MTW_USB_TX_EN | MTW_USB_RX_EN | MTW_USB_RX_AGG_EN | + MTW_USB_TX_CLEAR | MTW_USB_TXOP_HALT | MTW_USB_RX_WL_DROP); + mtw_delay(sc, 50); + mtw_read(sc, MTW_USB_DMA_CFG, &tmp); + tmp &= ~(MTW_USB_TX_CLEAR | MTW_USB_TXOP_HALT | MTW_USB_RX_WL_DROP); + mtw_write(sc, MTW_USB_DMA_CFG, tmp); + + /* enable radio */ + mtw_mcu_radio(sc, 0x31, 0); + + /* init RF registers */ + if (sc->asic_ver == 0x7601) + mt7601_rf_init(sc); + + /* init baseband registers */ + if (sc->asic_ver == 0x7601) + error = mt7601_bbp_init(sc); + + if (error != 0) { + device_printf(sc->sc_dev, "could not initialize BBP\n"); + goto fail; + } + + /* setup and calibrate RF */ + error = mt7601_rf_setup(sc); + + if (error != 0) { + device_printf(sc->sc_dev, "could not initialize RF\n"); + goto fail; + } + + /* select default channel */ + mtw_set_chan(sc, ic->ic_curchan); + + /* setup initial protection mode */ + mtw_updateprot_cb(ic); + + sc->sc_flags |= MTW_RUNNING; + sc->cmdq_run = MTW_CMDQ_GO; + for (i = 0; i != MTW_N_XFER; i++) + usbd_xfer_set_stall(sc->sc_xfer[i]); + + usbd_transfer_start(sc->sc_xfer[MTW_BULK_RX]); + + error = mtw_txrx_enable(sc); + if (error != 0) { + goto fail; + } + + return; + +fail: + + mtw_stop(sc); + return; +} + +static void +mtw_stop(void *arg) +{ + struct mtw_softc *sc = (struct mtw_softc *)arg; + uint32_t tmp; + int i, ntries, error; + + MTW_LOCK_ASSERT(sc, MA_OWNED); + + sc->sc_flags &= ~MTW_RUNNING; + + sc->ratectl_run = MTW_RATECTL_OFF; + sc->cmdq_run = sc->cmdq_key_set; + + MTW_UNLOCK(sc); + + for (i = 0; i < MTW_N_XFER; i++) + usbd_transfer_drain(sc->sc_xfer[i]); + + MTW_LOCK(sc); + + mtw_drain_mbufq(sc); + + if (sc->rx_m != NULL) { + m_free(sc->rx_m); + sc->rx_m = NULL; + } + + /* Disable Tx/Rx DMA. */ + mtw_read(sc, MTW_WPDMA_GLO_CFG, &tmp); + tmp &= ~(MTW_RX_DMA_EN | MTW_TX_DMA_EN); + mtw_write(sc, MTW_WPDMA_GLO_CFG, tmp); + // mtw_usb_dma_write(sc, 0); + + for (ntries = 0; ntries < 100; ntries++) { + if (mtw_read(sc, MTW_WPDMA_GLO_CFG, &tmp) != 0) + break; + if ((tmp & (MTW_TX_DMA_BUSY | MTW_RX_DMA_BUSY)) == 0) + break; + DELAY(10); + } + if (ntries == 100) { + device_printf(sc->sc_dev, "timeout waiting for DMA engine\n"); + } + + /* stop MAC Tx/Rx */ + mtw_read(sc, MTW_MAC_SYS_CTRL, &tmp); + tmp &= ~(MTW_MAC_RX_EN | MTW_MAC_TX_EN); + mtw_write(sc, MTW_MAC_SYS_CTRL, tmp); + + /* disable RTS retry */ + mtw_read(sc, MTW_TX_RTS_CFG, &tmp); + tmp &= ~0xff; + mtw_write(sc, MTW_TX_RTS_CFG, tmp); + + /* US_CYC_CFG */ + mtw_read(sc, MTW_US_CYC_CNT, &tmp); + tmp = (tmp & ~0xff); + mtw_write(sc, MTW_US_CYC_CNT, tmp); + + /* stop PBF */ + mtw_read(sc, MTW_PBF_CFG, &tmp); + tmp &= ~0x3; + mtw_write(sc, MTW_PBF_CFG, tmp); + + /* wait for pending Tx to complete */ + for (ntries = 0; ntries < 100; ntries++) { + if ((error = mtw_read(sc, MTW_TXRXQ_PCNT, &tmp)) != 0) + break; + if ((tmp & MTW_TX2Q_PCNT_MASK) == 0) + break; + } + +} + +static void +mtw_delay(struct mtw_softc *sc, u_int ms) +{ + usb_pause_mtx(mtx_owned(&sc->sc_mtx) ? &sc->sc_mtx : NULL, + USB_MS_TO_TICKS(ms)); +} + +static void +mtw_update_chw(struct ieee80211com *ic) +{ + + printf("%s: TODO\n", __func__); +} + +static int +mtw_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) +{ + + /* For now, no A-MPDU TX support in the driver */ + return (0); +} + +static device_method_t mtw_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, mtw_match), + DEVMETHOD(device_attach, mtw_attach), + DEVMETHOD(device_detach, mtw_detach), DEVMETHOD_END +}; + +static driver_t mtw_driver = { .name = "mtw", + .methods = mtw_methods, + .size = sizeof(struct mtw_softc) }; + +DRIVER_MODULE(mtw, uhub, mtw_driver, mtw_driver_loaded, NULL); +MODULE_DEPEND(mtw, wlan, 1, 1, 1); +MODULE_DEPEND(mtw, usb, 1, 1, 1); +MODULE_DEPEND(mtw, firmware, 1, 1, 1); +MODULE_VERSION(mtw, 1); +USB_PNP_HOST_INFO(mtw_devs); diff --git a/sys/dev/usb/wlan/if_mtwreg.h b/sys/dev/usb/wlan/if_mtwreg.h new file mode 100644 --- /dev/null +++ b/sys/dev/usb/wlan/if_mtwreg.h @@ -0,0 +1,1439 @@ +/* $OpenBSD: mtwreg.h,v 1.2 2022/07/27 06:41:04 hastings Exp $ */ +/* + * Copyright (c) 2007 Damien Bergamini + * 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 + * + * 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