From 2884a00088bb01b104d5181a1002dd4e197112f4 Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Wed, 29 Jun 2022 13:17:15 -0400 Subject: [PATCH] ralinkwifi: Import USB modules. Not entirely sure if these are working as they were not really tested. But they don't seem to cause KDLs at least, so the worst that happens is that the WiFi devices fail to work. --- .../drivers/network/wlan/ralinkwifi/Jamfile | 14 +- .../wlan/ralinkwifi/dev/usb/wlan/if_rum.c | 3303 +++++++++ .../wlan/ralinkwifi/dev/usb/wlan/if_rumfw.h | 213 + .../wlan/ralinkwifi/dev/usb/wlan/if_rumreg.h | 306 + .../wlan/ralinkwifi/dev/usb/wlan/if_rumvar.h | 186 + .../wlan/ralinkwifi/dev/usb/wlan/if_run.c | 6448 +++++++++++++++++ .../wlan/ralinkwifi/dev/usb/wlan/if_runreg.h | 1660 +++++ .../wlan/ralinkwifi/dev/usb/wlan/if_runvar.h | 268 + .../wlan/ralinkwifi/dev/usb/wlan/if_ural.c | 2225 ++++++ .../wlan/ralinkwifi/dev/usb/wlan/if_uralreg.h | 210 + .../wlan/ralinkwifi/dev/usb/wlan/if_uralvar.h | 135 + .../wlan/ralinkwifi/dev/usb/wlan/opt_wlan.h | 1 + .../drivers/network/wlan/ralinkwifi/glue.c | 27 +- 13 files changed, 14992 insertions(+), 4 deletions(-) create mode 100644 src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_rum.c create mode 100644 src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_rumfw.h create mode 100644 src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_rumreg.h create mode 100644 src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_rumvar.h create mode 100644 src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_run.c create mode 100644 src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_runreg.h create mode 100644 src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_runvar.h create mode 100644 src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_ural.c create mode 100644 src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_uralreg.h create mode 100644 src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_uralvar.h create mode 100644 src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/opt_wlan.h diff --git a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/Jamfile b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/Jamfile index 00e70eb4a2..88c767d89d 100644 --- a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/Jamfile +++ b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/Jamfile @@ -1,7 +1,6 @@ SubDir HAIKU_TOP src add-ons kernel drivers network wlan ralinkwifi ; -UseHeaders [ FDirName $(HAIKU_TOP) src libs compat freebsd_network compat ] - : true ; +UseHeaders [ FDirName $(HAIKU_TOP) src libs compat freebsd_network compat ] : true ; UseHeaders [ FDirName $(HAIKU_TOP) src libs compat freebsd_wlan ] : true ; UsePrivateHeaders net system ; UsePrivateKernelHeaders ; @@ -14,14 +13,25 @@ SubDirCcFlags [ FDefines _KERNEL=1 FBSD_DRIVER=1 ] UseHeaders [ FDirName $(SUBDIR) ] : true ; SEARCH_SOURCE += [ FDirName $(SUBDIR) dev ral ] ; +SEARCH_SOURCE += [ FDirName $(SUBDIR) dev usb wlan ] ; KernelAddon ralinkwifi : if_ral_pci.c rt2560.c rt2661.c rt2860.c + + if_ural.c + if_run.c + if_rum.c + glue.c : freebsd_wlan.a libfreebsd_network.a ; + +ObjectHdrs [ FGristFiles if_ural$(SUFOBJ) if_run$(SUFOBJ) if_rum$(SUFOBJ) ] + : [ FDirName $(HAIKU_COMMON_PLATFORM_OBJECT_DIR) libs compat freebsd_network ] ; +Includes [ FGristFiles if_ural.c if_run.c if_rum.c ] + : usbdevs.h ; diff --git a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_rum.c b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_rum.c new file mode 100644 index 0000000000..cdef5ee593 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_rum.c @@ -0,0 +1,3303 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005-2007 Damien Bergamini + * Copyright (c) 2006 Niall O'Higgins + * Copyright (c) 2007-2008 Hans Petter Selasky + * Copyright (c) 2015 Andriy Voskoboinyk + * + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +/*- + * Ralink Technology RT2501USB/RT2601USB chipset driver + * http://www.ralinktech.com.tw/ + */ + +#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 + +#ifdef INET +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include "usbdevs.h" + +#define USB_DEBUG_VAR rum_debug +#include + +#include +#include +#include + +#ifdef USB_DEBUG +static int rum_debug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, rum, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, + "USB rum"); +SYSCTL_INT(_hw_usb_rum, OID_AUTO, debug, CTLFLAG_RWTUN, &rum_debug, 0, + "Debug level"); +#endif + +static const STRUCT_USB_HOST_ID rum_devs[] = { +#define RUM_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } + RUM_DEV(ABOCOM, HWU54DM), + RUM_DEV(ABOCOM, RT2573_2), + RUM_DEV(ABOCOM, RT2573_3), + RUM_DEV(ABOCOM, RT2573_4), + RUM_DEV(ABOCOM, WUG2700), + RUM_DEV(AMIT, CGWLUSB2GO), + RUM_DEV(ASUS, RT2573_1), + RUM_DEV(ASUS, RT2573_2), + RUM_DEV(BELKIN, F5D7050A), + RUM_DEV(BELKIN, F5D9050V3), + RUM_DEV(CISCOLINKSYS, WUSB54GC), + RUM_DEV(CISCOLINKSYS, WUSB54GR), + RUM_DEV(CONCEPTRONIC2, C54RU2), + RUM_DEV(COREGA, CGWLUSB2GL), + RUM_DEV(COREGA, CGWLUSB2GPX), + RUM_DEV(DICKSMITH, CWD854F), + RUM_DEV(DICKSMITH, RT2573), + RUM_DEV(EDIMAX, EW7318USG), + RUM_DEV(DLINK2, DWLG122C1), + RUM_DEV(DLINK2, WUA1340), + RUM_DEV(DLINK2, DWA111), + RUM_DEV(DLINK2, DWA110), + RUM_DEV(GIGABYTE, GNWB01GS), + RUM_DEV(GIGABYTE, GNWI05GS), + RUM_DEV(GIGASET, RT2573), + RUM_DEV(GOODWAY, RT2573), + RUM_DEV(GUILLEMOT, HWGUSB254LB), + RUM_DEV(GUILLEMOT, HWGUSB254V2AP), + RUM_DEV(HUAWEI3COM, WUB320G), + RUM_DEV(MELCO, G54HP), + RUM_DEV(MELCO, SG54HP), + RUM_DEV(MELCO, SG54HG), + RUM_DEV(MELCO, WLIUCG), + RUM_DEV(MELCO, WLRUCG), + RUM_DEV(MELCO, WLRUCGAOSS), + RUM_DEV(MSI, RT2573_1), + RUM_DEV(MSI, RT2573_2), + RUM_DEV(MSI, RT2573_3), + RUM_DEV(MSI, RT2573_4), + RUM_DEV(NOVATECH, RT2573), + RUM_DEV(PLANEX2, GWUS54HP), + RUM_DEV(PLANEX2, GWUS54MINI2), + RUM_DEV(PLANEX2, GWUSMM), + RUM_DEV(QCOM, RT2573), + RUM_DEV(QCOM, RT2573_2), + RUM_DEV(QCOM, RT2573_3), + RUM_DEV(RALINK, RT2573), + RUM_DEV(RALINK, RT2573_2), + RUM_DEV(RALINK, RT2671), + RUM_DEV(SITECOMEU, WL113R2), + RUM_DEV(SITECOMEU, WL172), + RUM_DEV(SPARKLAN, RT2573), + RUM_DEV(SURECOM, RT2573), +#undef RUM_DEV +}; + +static device_probe_t rum_match; +static device_attach_t rum_attach; +static device_detach_t rum_detach; + +static usb_callback_t rum_bulk_read_callback; +static usb_callback_t rum_bulk_write_callback; + +static usb_error_t rum_do_request(struct rum_softc *sc, + struct usb_device_request *req, void *data); +static usb_error_t rum_do_mcu_request(struct rum_softc *sc, int); +static struct ieee80211vap *rum_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 rum_vap_delete(struct ieee80211vap *); +static void rum_cmdq_cb(void *, int); +static int rum_cmd_sleepable(struct rum_softc *, const void *, + size_t, uint8_t, CMD_FUNC_PROTO); +static void rum_tx_free(struct rum_tx_data *, int); +static void rum_setup_tx_list(struct rum_softc *); +static void rum_reset_tx_list(struct rum_softc *, + struct ieee80211vap *); +static void rum_unsetup_tx_list(struct rum_softc *); +static void rum_beacon_miss(struct ieee80211vap *); +static void rum_sta_recv_mgmt(struct ieee80211_node *, + struct mbuf *, int, + const struct ieee80211_rx_stats *, int, int); +static int rum_set_power_state(struct rum_softc *, int); +static int rum_newstate(struct ieee80211vap *, + enum ieee80211_state, int); +static uint8_t rum_crypto_mode(struct rum_softc *, u_int, int); +static void rum_setup_tx_desc(struct rum_softc *, + struct rum_tx_desc *, struct ieee80211_key *, + uint32_t, uint8_t, uint8_t, int, int, int); +static uint32_t rum_tx_crypto_flags(struct rum_softc *, + struct ieee80211_node *, + const struct ieee80211_key *); +static int rum_tx_mgt(struct rum_softc *, struct mbuf *, + struct ieee80211_node *); +static int rum_tx_raw(struct rum_softc *, struct mbuf *, + struct ieee80211_node *, + const struct ieee80211_bpf_params *); +static int rum_tx_data(struct rum_softc *, struct mbuf *, + struct ieee80211_node *); +static int rum_transmit(struct ieee80211com *, struct mbuf *); +static void rum_start(struct rum_softc *); +static void rum_parent(struct ieee80211com *); +static void rum_eeprom_read(struct rum_softc *, uint16_t, void *, + int); +static uint32_t rum_read(struct rum_softc *, uint16_t); +static void rum_read_multi(struct rum_softc *, uint16_t, void *, + int); +static usb_error_t rum_write(struct rum_softc *, uint16_t, uint32_t); +static usb_error_t rum_write_multi(struct rum_softc *, uint16_t, void *, + size_t); +static usb_error_t rum_setbits(struct rum_softc *, uint16_t, uint32_t); +static usb_error_t rum_clrbits(struct rum_softc *, uint16_t, uint32_t); +static usb_error_t rum_modbits(struct rum_softc *, uint16_t, uint32_t, + uint32_t); +static int rum_bbp_busy(struct rum_softc *); +static void rum_bbp_write(struct rum_softc *, uint8_t, uint8_t); +static uint8_t rum_bbp_read(struct rum_softc *, uint8_t); +static void rum_rf_write(struct rum_softc *, uint8_t, uint32_t); +static void rum_select_antenna(struct rum_softc *); +static void rum_enable_mrr(struct rum_softc *); +static void rum_set_txpreamble(struct rum_softc *); +static void rum_set_basicrates(struct rum_softc *); +static void rum_select_band(struct rum_softc *, + struct ieee80211_channel *); +static void rum_set_chan(struct rum_softc *, + struct ieee80211_channel *); +static void rum_set_maxretry(struct rum_softc *, + struct ieee80211vap *); +static int rum_enable_tsf_sync(struct rum_softc *); +static void rum_enable_tsf(struct rum_softc *); +static void rum_abort_tsf_sync(struct rum_softc *); +static void rum_get_tsf(struct rum_softc *, uint64_t *); +static void rum_update_slot_cb(struct rum_softc *, + union sec_param *, uint8_t); +static void rum_update_slot(struct ieee80211com *); +static int rum_wme_update(struct ieee80211com *); +static void rum_set_bssid(struct rum_softc *, const uint8_t *); +static void rum_set_macaddr(struct rum_softc *, const uint8_t *); +static void rum_update_mcast(struct ieee80211com *); +static void rum_update_promisc(struct ieee80211com *); +static void rum_setpromisc(struct rum_softc *); +static const char *rum_get_rf(int); +static void rum_read_eeprom(struct rum_softc *); +static int rum_bbp_wakeup(struct rum_softc *); +static int rum_bbp_init(struct rum_softc *); +static void rum_clr_shkey_regs(struct rum_softc *); +static int rum_init(struct rum_softc *); +static void rum_stop(struct rum_softc *); +static void rum_load_microcode(struct rum_softc *, const uint8_t *, + size_t); +static int rum_set_sleep_time(struct rum_softc *, uint16_t); +static int rum_reset(struct ieee80211vap *, u_long); +static int rum_set_beacon(struct rum_softc *, + struct ieee80211vap *); +static int rum_alloc_beacon(struct rum_softc *, + struct ieee80211vap *); +static void rum_update_beacon_cb(struct rum_softc *, + union sec_param *, uint8_t); +static void rum_update_beacon(struct ieee80211vap *, int); +static int rum_common_key_set(struct rum_softc *, + struct ieee80211_key *, uint16_t); +static void rum_group_key_set_cb(struct rum_softc *, + union sec_param *, uint8_t); +static void rum_group_key_del_cb(struct rum_softc *, + union sec_param *, uint8_t); +static void rum_pair_key_set_cb(struct rum_softc *, + union sec_param *, uint8_t); +static void rum_pair_key_del_cb(struct rum_softc *, + union sec_param *, uint8_t); +static int rum_key_alloc(struct ieee80211vap *, + struct ieee80211_key *, ieee80211_keyix *, + ieee80211_keyix *); +static int rum_key_set(struct ieee80211vap *, + const struct ieee80211_key *); +static int rum_key_delete(struct ieee80211vap *, + const struct ieee80211_key *); +static int rum_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static void rum_scan_start(struct ieee80211com *); +static void rum_scan_end(struct ieee80211com *); +static void rum_set_channel(struct ieee80211com *); +static void rum_getradiocaps(struct ieee80211com *, int, int *, + struct ieee80211_channel[]); +static int rum_get_rssi(struct rum_softc *, uint8_t); +static void rum_ratectl_start(struct rum_softc *, + struct ieee80211_node *); +static void rum_ratectl_timeout(void *); +static void rum_ratectl_task(void *, int); +static int rum_pause(struct rum_softc *, int); + +static const struct { + uint32_t reg; + uint32_t val; +} rum_def_mac[] = { + { RT2573_TXRX_CSR0, 0x025fb032 }, + { RT2573_TXRX_CSR1, 0x9eaa9eaf }, + { RT2573_TXRX_CSR2, 0x8a8b8c8d }, + { RT2573_TXRX_CSR3, 0x00858687 }, + { RT2573_TXRX_CSR7, 0x2e31353b }, + { RT2573_TXRX_CSR8, 0x2a2a2a2c }, + { RT2573_TXRX_CSR15, 0x0000000f }, + { RT2573_MAC_CSR6, 0x00000fff }, + { RT2573_MAC_CSR8, 0x016c030a }, + { RT2573_MAC_CSR10, 0x00000718 }, + { RT2573_MAC_CSR12, 0x00000004 }, + { RT2573_MAC_CSR13, 0x00007f00 }, + { RT2573_SEC_CSR2, 0x00000000 }, + { RT2573_SEC_CSR3, 0x00000000 }, + { RT2573_SEC_CSR4, 0x00000000 }, + { RT2573_PHY_CSR1, 0x000023b0 }, + { RT2573_PHY_CSR5, 0x00040a06 }, + { RT2573_PHY_CSR6, 0x00080606 }, + { RT2573_PHY_CSR7, 0x00000408 }, + { RT2573_AIFSN_CSR, 0x00002273 }, + { RT2573_CWMIN_CSR, 0x00002344 }, + { RT2573_CWMAX_CSR, 0x000034aa } +}; + +static const struct { + uint8_t reg; + uint8_t val; +} rum_def_bbp[] = { + { 3, 0x80 }, + { 15, 0x30 }, + { 17, 0x20 }, + { 21, 0xc8 }, + { 22, 0x38 }, + { 23, 0x06 }, + { 24, 0xfe }, + { 25, 0x0a }, + { 26, 0x0d }, + { 32, 0x0b }, + { 34, 0x12 }, + { 37, 0x07 }, + { 39, 0xf8 }, + { 41, 0x60 }, + { 53, 0x10 }, + { 54, 0x18 }, + { 60, 0x10 }, + { 61, 0x04 }, + { 62, 0x04 }, + { 75, 0xfe }, + { 86, 0xfe }, + { 88, 0xfe }, + { 90, 0x0f }, + { 99, 0x00 }, + { 102, 0x16 }, + { 107, 0x04 } +}; + +static const uint8_t rum_chan_5ghz[] = + { 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64, + 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, + 149, 153, 157, 161, 165 }; + +static const struct rfprog { + uint8_t chan; + uint32_t r1, r2, r3, r4; +} rum_rf5226[] = { + { 1, 0x00b03, 0x001e1, 0x1a014, 0x30282 }, + { 2, 0x00b03, 0x001e1, 0x1a014, 0x30287 }, + { 3, 0x00b03, 0x001e2, 0x1a014, 0x30282 }, + { 4, 0x00b03, 0x001e2, 0x1a014, 0x30287 }, + { 5, 0x00b03, 0x001e3, 0x1a014, 0x30282 }, + { 6, 0x00b03, 0x001e3, 0x1a014, 0x30287 }, + { 7, 0x00b03, 0x001e4, 0x1a014, 0x30282 }, + { 8, 0x00b03, 0x001e4, 0x1a014, 0x30287 }, + { 9, 0x00b03, 0x001e5, 0x1a014, 0x30282 }, + { 10, 0x00b03, 0x001e5, 0x1a014, 0x30287 }, + { 11, 0x00b03, 0x001e6, 0x1a014, 0x30282 }, + { 12, 0x00b03, 0x001e6, 0x1a014, 0x30287 }, + { 13, 0x00b03, 0x001e7, 0x1a014, 0x30282 }, + { 14, 0x00b03, 0x001e8, 0x1a014, 0x30284 }, + + { 34, 0x00b03, 0x20266, 0x36014, 0x30282 }, + { 38, 0x00b03, 0x20267, 0x36014, 0x30284 }, + { 42, 0x00b03, 0x20268, 0x36014, 0x30286 }, + { 46, 0x00b03, 0x20269, 0x36014, 0x30288 }, + + { 36, 0x00b03, 0x00266, 0x26014, 0x30288 }, + { 40, 0x00b03, 0x00268, 0x26014, 0x30280 }, + { 44, 0x00b03, 0x00269, 0x26014, 0x30282 }, + { 48, 0x00b03, 0x0026a, 0x26014, 0x30284 }, + { 52, 0x00b03, 0x0026b, 0x26014, 0x30286 }, + { 56, 0x00b03, 0x0026c, 0x26014, 0x30288 }, + { 60, 0x00b03, 0x0026e, 0x26014, 0x30280 }, + { 64, 0x00b03, 0x0026f, 0x26014, 0x30282 }, + + { 100, 0x00b03, 0x0028a, 0x2e014, 0x30280 }, + { 104, 0x00b03, 0x0028b, 0x2e014, 0x30282 }, + { 108, 0x00b03, 0x0028c, 0x2e014, 0x30284 }, + { 112, 0x00b03, 0x0028d, 0x2e014, 0x30286 }, + { 116, 0x00b03, 0x0028e, 0x2e014, 0x30288 }, + { 120, 0x00b03, 0x002a0, 0x2e014, 0x30280 }, + { 124, 0x00b03, 0x002a1, 0x2e014, 0x30282 }, + { 128, 0x00b03, 0x002a2, 0x2e014, 0x30284 }, + { 132, 0x00b03, 0x002a3, 0x2e014, 0x30286 }, + { 136, 0x00b03, 0x002a4, 0x2e014, 0x30288 }, + { 140, 0x00b03, 0x002a6, 0x2e014, 0x30280 }, + + { 149, 0x00b03, 0x002a8, 0x2e014, 0x30287 }, + { 153, 0x00b03, 0x002a9, 0x2e014, 0x30289 }, + { 157, 0x00b03, 0x002ab, 0x2e014, 0x30281 }, + { 161, 0x00b03, 0x002ac, 0x2e014, 0x30283 }, + { 165, 0x00b03, 0x002ad, 0x2e014, 0x30285 } +}, rum_rf5225[] = { + { 1, 0x00b33, 0x011e1, 0x1a014, 0x30282 }, + { 2, 0x00b33, 0x011e1, 0x1a014, 0x30287 }, + { 3, 0x00b33, 0x011e2, 0x1a014, 0x30282 }, + { 4, 0x00b33, 0x011e2, 0x1a014, 0x30287 }, + { 5, 0x00b33, 0x011e3, 0x1a014, 0x30282 }, + { 6, 0x00b33, 0x011e3, 0x1a014, 0x30287 }, + { 7, 0x00b33, 0x011e4, 0x1a014, 0x30282 }, + { 8, 0x00b33, 0x011e4, 0x1a014, 0x30287 }, + { 9, 0x00b33, 0x011e5, 0x1a014, 0x30282 }, + { 10, 0x00b33, 0x011e5, 0x1a014, 0x30287 }, + { 11, 0x00b33, 0x011e6, 0x1a014, 0x30282 }, + { 12, 0x00b33, 0x011e6, 0x1a014, 0x30287 }, + { 13, 0x00b33, 0x011e7, 0x1a014, 0x30282 }, + { 14, 0x00b33, 0x011e8, 0x1a014, 0x30284 }, + + { 34, 0x00b33, 0x01266, 0x26014, 0x30282 }, + { 38, 0x00b33, 0x01267, 0x26014, 0x30284 }, + { 42, 0x00b33, 0x01268, 0x26014, 0x30286 }, + { 46, 0x00b33, 0x01269, 0x26014, 0x30288 }, + + { 36, 0x00b33, 0x01266, 0x26014, 0x30288 }, + { 40, 0x00b33, 0x01268, 0x26014, 0x30280 }, + { 44, 0x00b33, 0x01269, 0x26014, 0x30282 }, + { 48, 0x00b33, 0x0126a, 0x26014, 0x30284 }, + { 52, 0x00b33, 0x0126b, 0x26014, 0x30286 }, + { 56, 0x00b33, 0x0126c, 0x26014, 0x30288 }, + { 60, 0x00b33, 0x0126e, 0x26014, 0x30280 }, + { 64, 0x00b33, 0x0126f, 0x26014, 0x30282 }, + + { 100, 0x00b33, 0x0128a, 0x2e014, 0x30280 }, + { 104, 0x00b33, 0x0128b, 0x2e014, 0x30282 }, + { 108, 0x00b33, 0x0128c, 0x2e014, 0x30284 }, + { 112, 0x00b33, 0x0128d, 0x2e014, 0x30286 }, + { 116, 0x00b33, 0x0128e, 0x2e014, 0x30288 }, + { 120, 0x00b33, 0x012a0, 0x2e014, 0x30280 }, + { 124, 0x00b33, 0x012a1, 0x2e014, 0x30282 }, + { 128, 0x00b33, 0x012a2, 0x2e014, 0x30284 }, + { 132, 0x00b33, 0x012a3, 0x2e014, 0x30286 }, + { 136, 0x00b33, 0x012a4, 0x2e014, 0x30288 }, + { 140, 0x00b33, 0x012a6, 0x2e014, 0x30280 }, + + { 149, 0x00b33, 0x012a8, 0x2e014, 0x30287 }, + { 153, 0x00b33, 0x012a9, 0x2e014, 0x30289 }, + { 157, 0x00b33, 0x012ab, 0x2e014, 0x30281 }, + { 161, 0x00b33, 0x012ac, 0x2e014, 0x30283 }, + { 165, 0x00b33, 0x012ad, 0x2e014, 0x30285 } +}; + +static const struct usb_config rum_config[RUM_N_TRANSFER] = { + [RUM_BULK_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = (MCLBYTES + RT2573_TX_DESC_SIZE + 8), + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = rum_bulk_write_callback, + .timeout = 5000, /* ms */ + }, + [RUM_BULK_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = (MCLBYTES + RT2573_RX_DESC_SIZE), + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = rum_bulk_read_callback, + }, +}; + +static int +rum_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 != RT2573_IFACE_INDEX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(rum_devs, sizeof(rum_devs), uaa)); +} + +static int +rum_attach(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + struct rum_softc *sc = device_get_softc(self); + struct ieee80211com *ic = &sc->sc_ic; + uint32_t tmp; + uint8_t iface_index; + int error, ntries; + + device_set_usb_desc(self); + sc->sc_udev = uaa->device; + sc->sc_dev = self; + + RUM_LOCK_INIT(sc); + RUM_CMDQ_LOCK_INIT(sc); + mbufq_init(&sc->sc_snd, ifqmaxlen); + + iface_index = RT2573_IFACE_INDEX; + error = usbd_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, rum_config, RUM_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(self, "could not allocate USB transfers, " + "err=%s\n", usbd_errstr(error)); + goto detach; + } + + RUM_LOCK(sc); + /* retrieve RT2573 rev. no */ + for (ntries = 0; ntries < 100; ntries++) { + if ((tmp = rum_read(sc, RT2573_MAC_CSR0)) != 0) + break; + if (rum_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "timeout waiting for chip to settle\n"); + RUM_UNLOCK(sc); + goto detach; + } + + /* retrieve MAC address and various other things from EEPROM */ + rum_read_eeprom(sc); + + device_printf(sc->sc_dev, "MAC/BBP RT2573 (rev 0x%05x), RF %s\n", + tmp, rum_get_rf(sc->rf_rev)); + + rum_load_microcode(sc, rt2573_ucode, sizeof(rt2573_ucode)); + RUM_UNLOCK(sc); + + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(self); + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA /* station mode supported */ + | IEEE80211_C_IBSS /* IBSS mode supported */ + | IEEE80211_C_MONITOR /* monitor mode supported */ + | IEEE80211_C_HOSTAP /* HostAp mode supported */ + | IEEE80211_C_AHDEMO /* adhoc demo mode */ + | IEEE80211_C_TXPMGT /* tx power management */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_BGSCAN /* bg scanning supported */ + | IEEE80211_C_WPA /* 802.11i */ + | IEEE80211_C_WME /* 802.11e */ + | IEEE80211_C_PMGT /* Station-side power mgmt */ + | IEEE80211_C_SWSLEEP /* net80211 managed power mgmt */ + ; + + ic->ic_cryptocaps = + IEEE80211_CRYPTO_WEP | + IEEE80211_CRYPTO_AES_CCM | + IEEE80211_CRYPTO_TKIPMIC | + IEEE80211_CRYPTO_TKIP; + + rum_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, + ic->ic_channels); + + ieee80211_ifattach(ic); + ic->ic_update_promisc = rum_update_promisc; + ic->ic_raw_xmit = rum_raw_xmit; + ic->ic_scan_start = rum_scan_start; + ic->ic_scan_end = rum_scan_end; + ic->ic_set_channel = rum_set_channel; + ic->ic_getradiocaps = rum_getradiocaps; + ic->ic_transmit = rum_transmit; + ic->ic_parent = rum_parent; + ic->ic_vap_create = rum_vap_create; + ic->ic_vap_delete = rum_vap_delete; + ic->ic_updateslot = rum_update_slot; + ic->ic_wme.wme_update = rum_wme_update; + ic->ic_update_mcast = rum_update_mcast; + + ieee80211_radiotap_attach(ic, + &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), + RT2573_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + RT2573_RX_RADIOTAP_PRESENT); + + TASK_INIT(&sc->cmdq_task, 0, rum_cmdq_cb, sc); + + if (bootverbose) + ieee80211_announce(ic); + + return (0); + +detach: + rum_detach(self); + return (ENXIO); /* failure */ +} + +static int +rum_detach(device_t self) +{ + struct rum_softc *sc = device_get_softc(self); + struct ieee80211com *ic = &sc->sc_ic; + + /* Prevent further ioctls */ + RUM_LOCK(sc); + sc->sc_detached = 1; + RUM_UNLOCK(sc); + + /* stop all USB transfers */ + usbd_transfer_unsetup(sc->sc_xfer, RUM_N_TRANSFER); + + /* free TX list, if any */ + RUM_LOCK(sc); + rum_unsetup_tx_list(sc); + RUM_UNLOCK(sc); + + if (ic->ic_softc == sc) { + ieee80211_draintask(ic, &sc->cmdq_task); + ieee80211_ifdetach(ic); + } + + mbufq_drain(&sc->sc_snd); + RUM_CMDQ_LOCK_DESTROY(sc); + RUM_LOCK_DESTROY(sc); + + return (0); +} + +static usb_error_t +rum_do_request(struct rum_softc *sc, + struct usb_device_request *req, void *data) +{ + usb_error_t err; + int ntries = 10; + + while (ntries--) { + err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, + req, data, 0, NULL, 250 /* ms */); + if (err == 0) + break; + + DPRINTFN(1, "Control request failed, %s (retrying)\n", + usbd_errstr(err)); + if (rum_pause(sc, hz / 100)) + break; + } + return (err); +} + +static usb_error_t +rum_do_mcu_request(struct rum_softc *sc, int request) +{ + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RT2573_MCU_CNTL; + USETW(req.wValue, request); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + + return (rum_do_request(sc, &req, NULL)); +} + +static struct ieee80211vap * +rum_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 rum_softc *sc = ic->ic_softc; + struct rum_vap *rvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return NULL; + rvp = malloc(sizeof(struct rum_vap), M_80211_VAP, M_WAITOK | M_ZERO); + vap = &rvp->vap; + /* enable s/w bmiss handling for sta mode */ + + if (ieee80211_vap_setup(ic, vap, name, unit, opmode, + flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { + /* out of memory */ + free(rvp, M_80211_VAP); + return (NULL); + } + + /* override state transition machine */ + rvp->newstate = vap->iv_newstate; + vap->iv_newstate = rum_newstate; + vap->iv_key_alloc = rum_key_alloc; + vap->iv_key_set = rum_key_set; + vap->iv_key_delete = rum_key_delete; + vap->iv_update_beacon = rum_update_beacon; + vap->iv_reset = rum_reset; + vap->iv_max_aid = RT2573_ADDR_MAX; + + if (opmode == IEEE80211_M_STA) { + /* + * Move device to the sleep state when + * beacon is received and there is no data for us. + * + * Used only for IEEE80211_S_SLEEP state. + */ + rvp->recv_mgmt = vap->iv_recv_mgmt; + vap->iv_recv_mgmt = rum_sta_recv_mgmt; + + /* Ignored while sleeping. */ + rvp->bmiss = vap->iv_bmiss; + vap->iv_bmiss = rum_beacon_miss; + } + + usb_callout_init_mtx(&rvp->ratectl_ch, &sc->sc_mtx, 0); + TASK_INIT(&rvp->ratectl_task, 0, rum_ratectl_task, rvp); + ieee80211_ratectl_init(vap); + ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */); + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, + ieee80211_media_status, mac); + ic->ic_opmode = opmode; + return vap; +} + +static void +rum_vap_delete(struct ieee80211vap *vap) +{ + struct rum_vap *rvp = RUM_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct rum_softc *sc = ic->ic_softc; + + /* Put vap into INIT state. */ + ieee80211_new_state(vap, IEEE80211_S_INIT, -1); + ieee80211_draintask(ic, &vap->iv_nstate_task); + + RUM_LOCK(sc); + /* Cancel any unfinished Tx. */ + rum_reset_tx_list(sc, vap); + RUM_UNLOCK(sc); + + usb_callout_drain(&rvp->ratectl_ch); + ieee80211_draintask(ic, &rvp->ratectl_task); + ieee80211_ratectl_deinit(vap); + ieee80211_vap_detach(vap); + m_freem(rvp->bcn_mbuf); + free(rvp, M_80211_VAP); +} + +static void +rum_cmdq_cb(void *arg, int pending) +{ + struct rum_softc *sc = arg; + struct rum_cmdq *rc; + + RUM_CMDQ_LOCK(sc); + while (sc->cmdq[sc->cmdq_first].func != NULL) { + rc = &sc->cmdq[sc->cmdq_first]; + RUM_CMDQ_UNLOCK(sc); + + RUM_LOCK(sc); + rc->func(sc, &rc->data, rc->rvp_id); + RUM_UNLOCK(sc); + + RUM_CMDQ_LOCK(sc); + memset(rc, 0, sizeof (*rc)); + sc->cmdq_first = (sc->cmdq_first + 1) % RUM_CMDQ_SIZE; + } + RUM_CMDQ_UNLOCK(sc); +} + +static int +rum_cmd_sleepable(struct rum_softc *sc, const void *ptr, size_t len, + uint8_t rvp_id, CMD_FUNC_PROTO) +{ + struct ieee80211com *ic = &sc->sc_ic; + + KASSERT(len <= sizeof(union sec_param), ("buffer overflow")); + + RUM_CMDQ_LOCK(sc); + if (sc->cmdq[sc->cmdq_last].func != NULL) { + device_printf(sc->sc_dev, "%s: cmdq overflow\n", __func__); + RUM_CMDQ_UNLOCK(sc); + + return EAGAIN; + } + + if (ptr != NULL) + memcpy(&sc->cmdq[sc->cmdq_last].data, ptr, len); + sc->cmdq[sc->cmdq_last].rvp_id = rvp_id; + sc->cmdq[sc->cmdq_last].func = func; + sc->cmdq_last = (sc->cmdq_last + 1) % RUM_CMDQ_SIZE; + RUM_CMDQ_UNLOCK(sc); + + ieee80211_runtask(ic, &sc->cmdq_task); + + return 0; +} + +static void +rum_tx_free(struct rum_tx_data *data, int txerr) +{ + struct rum_softc *sc = data->sc; + + if (data->m != NULL) { + ieee80211_tx_complete(data->ni, data->m, txerr); + data->m = NULL; + data->ni = NULL; + } + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; +} + +static void +rum_setup_tx_list(struct rum_softc *sc) +{ + struct rum_tx_data *data; + int i; + + sc->tx_nfree = 0; + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + + for (i = 0; i < RUM_TX_LIST_COUNT; i++) { + data = &sc->tx_data[i]; + + data->sc = sc; + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; + } +} + +static void +rum_reset_tx_list(struct rum_softc *sc, struct ieee80211vap *vap) +{ + struct rum_tx_data *data, *tmp; + + KASSERT(vap != NULL, ("%s: vap is NULL\n", __func__)); + + STAILQ_FOREACH_SAFE(data, &sc->tx_q, next, tmp) { + if (data->ni != NULL && data->ni->ni_vap == vap) { + ieee80211_free_node(data->ni); + data->ni = NULL; + + KASSERT(data->m != NULL, ("%s: m is NULL\n", + __func__)); + m_freem(data->m); + data->m = NULL; + + STAILQ_REMOVE(&sc->tx_q, data, rum_tx_data, next); + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; + } + } +} + +static void +rum_unsetup_tx_list(struct rum_softc *sc) +{ + struct rum_tx_data *data; + int i; + + /* make sure any subsequent use of the queues will fail */ + sc->tx_nfree = 0; + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + + /* free up all node references and mbufs */ + for (i = 0; i < RUM_TX_LIST_COUNT; i++) { + data = &sc->tx_data[i]; + + if (data->m != NULL) { + m_freem(data->m); + data->m = NULL; + } + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; + } + } +} + +static void +rum_beacon_miss(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct rum_softc *sc = ic->ic_softc; + struct rum_vap *rvp = RUM_VAP(vap); + int sleep; + + RUM_LOCK(sc); + if (sc->sc_sleeping && sc->sc_sleep_end < ticks) { + DPRINTFN(12, "dropping 'sleeping' bit, " + "device must be awake now\n"); + + sc->sc_sleeping = 0; + } + + sleep = sc->sc_sleeping; + RUM_UNLOCK(sc); + + if (!sleep) + rvp->bmiss(vap); +#ifdef USB_DEBUG + else + DPRINTFN(13, "bmiss event is ignored whilst sleeping\n"); +#endif +} + +static void +rum_sta_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 rum_softc *sc = vap->iv_ic->ic_softc; + struct rum_vap *rvp = RUM_VAP(vap); + + if (vap->iv_state == IEEE80211_S_SLEEP && + subtype == IEEE80211_FC0_SUBTYPE_BEACON) { + RUM_LOCK(sc); + DPRINTFN(12, "beacon, mybss %d (flags %02X)\n", + !!(sc->last_rx_flags & RT2573_RX_MYBSS), + sc->last_rx_flags); + + if ((sc->last_rx_flags & (RT2573_RX_MYBSS | RT2573_RX_BC)) == + (RT2573_RX_MYBSS | RT2573_RX_BC)) { + /* + * Put it to sleep here; in case if there is a data + * for us, iv_recv_mgmt() will wakeup the device via + * SLEEP -> RUN state transition. + */ + rum_set_power_state(sc, 1); + } + RUM_UNLOCK(sc); + } + + rvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf); +} + +static int +rum_set_power_state(struct rum_softc *sc, int sleep) +{ + usb_error_t uerror; + + RUM_LOCK_ASSERT(sc); + + DPRINTFN(12, "moving to %s state (sleep time %u)\n", + sleep ? "sleep" : "awake", sc->sc_sleep_time); + + uerror = rum_do_mcu_request(sc, + sleep ? RT2573_MCU_SLEEP : RT2573_MCU_WAKEUP); + if (uerror != USB_ERR_NORMAL_COMPLETION) { + device_printf(sc->sc_dev, + "%s: could not change power state: %s\n", + __func__, usbd_errstr(uerror)); + return (EIO); + } + + sc->sc_sleeping = !!sleep; + sc->sc_sleep_end = sleep ? ticks + sc->sc_sleep_time : 0; + + return (0); +} + +static int +rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct rum_vap *rvp = RUM_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct rum_softc *sc = ic->ic_softc; + const struct ieee80211_txparam *tp; + enum ieee80211_state ostate; + struct ieee80211_node *ni; + usb_error_t uerror; + int ret = 0; + + ostate = vap->iv_state; + DPRINTF("%s -> %s\n", + ieee80211_state_name[ostate], + ieee80211_state_name[nstate]); + + IEEE80211_UNLOCK(ic); + RUM_LOCK(sc); + usb_callout_stop(&rvp->ratectl_ch); + + if (ostate == IEEE80211_S_SLEEP && vap->iv_opmode == IEEE80211_M_STA) { + rum_clrbits(sc, RT2573_TXRX_CSR4, RT2573_ACKCTS_PWRMGT); + rum_clrbits(sc, RT2573_MAC_CSR11, RT2573_AUTO_WAKEUP); + + /* + * Ignore any errors; + * any subsequent TX will wakeup it anyway + */ + (void) rum_set_power_state(sc, 0); + } + + switch (nstate) { + case IEEE80211_S_INIT: + if (ostate == IEEE80211_S_RUN) + rum_abort_tsf_sync(sc); + + break; + + case IEEE80211_S_RUN: + if (ostate == IEEE80211_S_SLEEP) + break; /* already handled */ + + ni = ieee80211_ref_node(vap->iv_bss); + + if (vap->iv_opmode != IEEE80211_M_MONITOR) { + if (ic->ic_bsschan == IEEE80211_CHAN_ANYC || + ni->ni_chan == IEEE80211_CHAN_ANYC) { + ret = EINVAL; + goto run_fail; + } + rum_update_slot_cb(sc, NULL, 0); + rum_enable_mrr(sc); + rum_set_txpreamble(sc); + rum_set_basicrates(sc); + rum_set_maxretry(sc, vap); + IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); + rum_set_bssid(sc, sc->sc_bssid); + } + + if (vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS) { + if ((ret = rum_alloc_beacon(sc, vap)) != 0) + goto run_fail; + } + + if (vap->iv_opmode != IEEE80211_M_MONITOR && + vap->iv_opmode != IEEE80211_M_AHDEMO) { + if ((ret = rum_enable_tsf_sync(sc)) != 0) + goto run_fail; + } else + rum_enable_tsf(sc); + + /* enable automatic rate adaptation */ + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) + rum_ratectl_start(sc, ni); +run_fail: + ieee80211_free_node(ni); + break; + case IEEE80211_S_SLEEP: + /* Implemented for STA mode only. */ + if (vap->iv_opmode != IEEE80211_M_STA) + break; + + uerror = rum_setbits(sc, RT2573_MAC_CSR11, RT2573_AUTO_WAKEUP); + if (uerror != USB_ERR_NORMAL_COMPLETION) { + ret = EIO; + break; + } + + uerror = rum_setbits(sc, RT2573_TXRX_CSR4, RT2573_ACKCTS_PWRMGT); + if (uerror != USB_ERR_NORMAL_COMPLETION) { + ret = EIO; + break; + } + + ret = rum_set_power_state(sc, 1); + if (ret != 0) { + device_printf(sc->sc_dev, + "%s: could not move to the SLEEP state: %s\n", + __func__, usbd_errstr(uerror)); + } + break; + default: + break; + } + RUM_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (ret == 0 ? rvp->newstate(vap, nstate, arg) : ret); +} + +static void +rum_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct rum_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211vap *vap; + struct rum_tx_data *data; + struct mbuf *m; + struct usb_page_cache *pc; + unsigned int len; + int actlen, sumlen; + + usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete, %d bytes\n", actlen); + + /* free resources */ + data = usbd_xfer_get_priv(xfer); + rum_tx_free(data, 0); + usbd_xfer_set_priv(xfer, NULL); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + data = STAILQ_FIRST(&sc->tx_q); + if (data) { + STAILQ_REMOVE_HEAD(&sc->tx_q, next); + m = data->m; + + if (m->m_pkthdr.len > (int)(MCLBYTES + RT2573_TX_DESC_SIZE)) { + DPRINTFN(0, "data overflow, %u bytes\n", + m->m_pkthdr.len); + m->m_pkthdr.len = (MCLBYTES + RT2573_TX_DESC_SIZE); + } + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_in(pc, 0, &data->desc, RT2573_TX_DESC_SIZE); + usbd_m_copy_in(pc, RT2573_TX_DESC_SIZE, m, 0, + m->m_pkthdr.len); + + vap = data->ni->ni_vap; + if (ieee80211_radiotap_active_vap(vap)) { + struct rum_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = data->rate; + tap->wt_antenna = sc->tx_ant; + + ieee80211_radiotap_tx(vap, m); + } + + /* align end on a 4-bytes boundary */ + len = (RT2573_TX_DESC_SIZE + m->m_pkthdr.len + 3) & ~3; + if ((len % 64) == 0) + len += 4; + + DPRINTFN(11, "sending frame len=%u xferlen=%u\n", + m->m_pkthdr.len, len); + + usbd_xfer_set_frame_len(xfer, 0, len); + usbd_xfer_set_priv(xfer, data); + + usbd_transfer_submit(xfer); + } + rum_start(sc); + break; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usbd_errstr(error)); + + counter_u64_add(sc->sc_ic.ic_oerrors, 1); + data = usbd_xfer_get_priv(xfer); + if (data != NULL) { + rum_tx_free(data, error); + usbd_xfer_set_priv(xfer, NULL); + } + + if (error != USB_ERR_CANCELLED) { + if (error == USB_ERR_TIMEOUT) + device_printf(sc->sc_dev, "device timeout\n"); + + /* + * 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; + } +} + +static void +rum_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct rum_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_frame_min *wh; + struct ieee80211_node *ni; + struct epoch_tracker et; + struct mbuf *m = NULL; + struct usb_page_cache *pc; + uint32_t flags; + uint8_t rssi = 0; + int len; + + usbd_xfer_status(xfer, &len, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTFN(15, "rx done, actlen=%d\n", len); + + if (len < RT2573_RX_DESC_SIZE) { + DPRINTF("%s: xfer too short %d\n", + device_get_nameunit(sc->sc_dev), len); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + + len -= RT2573_RX_DESC_SIZE; + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, 0, &sc->sc_rx_desc, RT2573_RX_DESC_SIZE); + + rssi = rum_get_rssi(sc, sc->sc_rx_desc.rssi); + flags = le32toh(sc->sc_rx_desc.flags); + sc->last_rx_flags = flags; + if (len < ((flags >> 16) & 0xfff)) { + DPRINTFN(5, "%s: frame is truncated from %d to %d " + "bytes\n", device_get_nameunit(sc->sc_dev), + (flags >> 16) & 0xfff, len); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + len = (flags >> 16) & 0xfff; + if (len < sizeof(struct ieee80211_frame_ack)) { + DPRINTFN(5, "%s: frame too short %d\n", + device_get_nameunit(sc->sc_dev), len); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + if (flags & RT2573_RX_CRC_ERROR) { + /* + * This should not happen since we did not + * request to receive those frames when we + * filled RUM_TXRX_CSR2: + */ + DPRINTFN(5, "PHY or CRC error\n"); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + if ((flags & RT2573_RX_DEC_MASK) != RT2573_RX_DEC_OK) { + switch (flags & RT2573_RX_DEC_MASK) { + case RT2573_RX_IV_ERROR: + DPRINTFN(5, "IV/EIV error\n"); + break; + case RT2573_RX_MIC_ERROR: + DPRINTFN(5, "MIC error\n"); + break; + case RT2573_RX_KEY_ERROR: + DPRINTFN(5, "Key error\n"); + break; + } + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + + m = m_get2(len, M_NOWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) { + DPRINTF("could not allocate mbuf\n"); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + usbd_copy_out(pc, RT2573_RX_DESC_SIZE, + mtod(m, uint8_t *), len); + + wh = mtod(m, struct ieee80211_frame_min *); + + if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && + (flags & RT2573_RX_CIP_MASK) != + RT2573_RX_CIP_MODE(RT2573_MODE_NOSEC)) { + wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; + m->m_flags |= M_WEP; + } + + /* finalize mbuf */ + m->m_pkthdr.len = m->m_len = len; + + if (ieee80211_radiotap_active(ic)) { + struct rum_rx_radiotap_header *tap = &sc->sc_rxtap; + + tap->wr_flags = 0; + tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate, + (flags & RT2573_RX_OFDM) ? + IEEE80211_T_OFDM : IEEE80211_T_CCK); + rum_get_tsf(sc, &tap->wr_tsf); + tap->wr_antsignal = RT2573_NOISE_FLOOR + rssi; + tap->wr_antnoise = RT2573_NOISE_FLOOR; + tap->wr_antenna = sc->rx_ant; + } + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + + /* + * At the end of a USB callback it is always safe to unlock + * the private mutex of a device! That is why we do the + * "ieee80211_input" here, and not some lines up! + */ + RUM_UNLOCK(sc); + if (m) { + if (m->m_len >= sizeof(struct ieee80211_frame_min)) + ni = ieee80211_find_rxnode(ic, wh); + else + ni = NULL; + + NET_EPOCH_ENTER(et); + if (ni != NULL) { + (void) ieee80211_input(ni, m, rssi, + RT2573_NOISE_FLOOR); + ieee80211_free_node(ni); + } else + (void) ieee80211_input_all(ic, m, rssi, + RT2573_NOISE_FLOOR); + NET_EPOCH_EXIT(et); + } + RUM_LOCK(sc); + rum_start(sc); + return; + + default: /* Error */ + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static uint8_t +rum_plcp_signal(int rate) +{ + switch (rate) { + /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ + case 12: return 0xb; + case 18: return 0xf; + case 24: return 0xa; + case 36: return 0xe; + case 48: return 0x9; + case 72: return 0xd; + case 96: return 0x8; + case 108: return 0xc; + + /* CCK rates (NB: not IEEE std, device-specific) */ + case 2: return 0x0; + case 4: return 0x1; + case 11: return 0x2; + case 22: return 0x3; + } + return 0xff; /* XXX unsupported/unknown rate */ +} + +/* + * Map net80211 cipher to RT2573 security mode. + */ +static uint8_t +rum_crypto_mode(struct rum_softc *sc, u_int cipher, int keylen) +{ + switch (cipher) { + case IEEE80211_CIPHER_WEP: + return (keylen < 8 ? RT2573_MODE_WEP40 : RT2573_MODE_WEP104); + case IEEE80211_CIPHER_TKIP: + return RT2573_MODE_TKIP; + case IEEE80211_CIPHER_AES_CCM: + return RT2573_MODE_AES_CCMP; + default: + device_printf(sc->sc_dev, "unknown cipher %d\n", cipher); + return 0; + } +} + +static void +rum_setup_tx_desc(struct rum_softc *sc, struct rum_tx_desc *desc, + struct ieee80211_key *k, uint32_t flags, uint8_t xflags, uint8_t qid, + int hdrlen, int len, int rate) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct wmeParams *wmep = &sc->wme_params[qid]; + uint16_t plcp_length; + int remainder; + + flags |= RT2573_TX_VALID; + flags |= len << 16; + + if (k != NULL && !(k->wk_flags & IEEE80211_KEY_SWCRYPT)) { + const struct ieee80211_cipher *cip = k->wk_cipher; + + len += cip->ic_header + cip->ic_trailer + cip->ic_miclen; + + desc->eiv = 0; /* for WEP */ + cip->ic_setiv(k, (uint8_t *)&desc->iv); + } + + /* setup PLCP fields */ + desc->plcp_signal = rum_plcp_signal(rate); + desc->plcp_service = 4; + + len += IEEE80211_CRC_LEN; + if (ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) { + flags |= RT2573_TX_OFDM; + + plcp_length = len & 0xfff; + desc->plcp_length_hi = plcp_length >> 6; + desc->plcp_length_lo = plcp_length & 0x3f; + } else { + if (rate == 0) + rate = 2; /* avoid division by zero */ + plcp_length = howmany(16 * len, rate); + if (rate == 22) { + remainder = (16 * len) % 22; + if (remainder != 0 && remainder < 7) + desc->plcp_service |= RT2573_PLCP_LENGEXT; + } + desc->plcp_length_hi = plcp_length >> 8; + desc->plcp_length_lo = plcp_length & 0xff; + + if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + desc->plcp_signal |= 0x08; + } + + desc->flags = htole32(flags); + desc->hdrlen = hdrlen; + desc->xflags = xflags; + + desc->wme = htole16(RT2573_QID(qid) | + RT2573_AIFSN(wmep->wmep_aifsn) | + RT2573_LOGCWMIN(wmep->wmep_logcwmin) | + RT2573_LOGCWMAX(wmep->wmep_logcwmax)); +} + +static int +rum_sendprot(struct rum_softc *sc, + const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) +{ + struct ieee80211com *ic = ni->ni_ic; + struct rum_tx_data *data; + struct mbuf *mprot; + int protrate, flags; + + RUM_LOCK_ASSERT(sc); + + mprot = ieee80211_alloc_prot(ni, m, rate, prot); + if (mprot == NULL) { + if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); + device_printf(sc->sc_dev, + "could not allocate mbuf for protection mode %d\n", prot); + return (ENOBUFS); + } + + protrate = ieee80211_ctl_rate(ic->ic_rt, rate); + flags = 0; + if (prot == IEEE80211_PROT_RTSCTS) + flags |= RT2573_TX_NEED_ACK; + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + data->m = mprot; + data->ni = ieee80211_ref_node(ni); + data->rate = protrate; + rum_setup_tx_desc(sc, &data->desc, NULL, flags, 0, 0, 0, + mprot->m_pkthdr.len, protrate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[RUM_BULK_WR]); + + return 0; +} + +static uint32_t +rum_tx_crypto_flags(struct rum_softc *sc, struct ieee80211_node *ni, + const struct ieee80211_key *k) +{ + struct ieee80211vap *vap = ni->ni_vap; + u_int cipher; + uint32_t flags = 0; + uint8_t mode, pos; + + if (!(k->wk_flags & IEEE80211_KEY_SWCRYPT)) { + cipher = k->wk_cipher->ic_cipher; + pos = k->wk_keyix; + mode = rum_crypto_mode(sc, cipher, k->wk_keylen); + if (mode == 0) + return 0; + + flags |= RT2573_TX_CIP_MODE(mode); + + /* Do not trust GROUP flag */ + if (!(k >= &vap->iv_nw_keys[0] && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) + flags |= RT2573_TX_KEY_PAIR; + else + pos += 0 * RT2573_SKEY_MAX; /* vap id */ + + flags |= RT2573_TX_KEY_ID(pos); + + if (cipher == IEEE80211_CIPHER_TKIP) + flags |= RT2573_TX_TKIPMIC; + } + + return flags; +} + +static int +rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + const struct ieee80211_txparam *tp = ni->ni_txparms; + struct ieee80211com *ic = &sc->sc_ic; + struct rum_tx_data *data; + struct ieee80211_frame *wh; + struct ieee80211_key *k = NULL; + uint32_t flags = 0; + uint16_t dur; + uint8_t ac, type, xflags = 0; + int hdrlen; + + RUM_LOCK_ASSERT(sc); + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + wh = mtod(m0, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + hdrlen = ieee80211_anyhdrsize(wh); + ac = M_WME_GETAC(m0); + + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_get_txkey(ni, m0); + if (k == NULL) + return (ENOENT); + + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !k->wk_cipher->ic_encap(k, m0)) + return (ENOBUFS); + + wh = mtod(m0, struct ieee80211_frame *); + } + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + flags |= RT2573_TX_NEED_ACK; + + dur = ieee80211_ack_duration(ic->ic_rt, tp->mgmtrate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + *(uint16_t *)wh->i_dur = htole16(dur); + + /* tell hardware to add timestamp for probe responses */ + if (type == IEEE80211_FC0_TYPE_MGT && + (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == + IEEE80211_FC0_SUBTYPE_PROBE_RESP) + flags |= RT2573_TX_TIMESTAMP; + } + + if (type != IEEE80211_FC0_TYPE_CTL && !IEEE80211_QOS_HAS_SEQ(wh)) + xflags |= RT2573_TX_HWSEQ; + + if (k != NULL) + flags |= rum_tx_crypto_flags(sc, ni, k); + + data->m = m0; + data->ni = ni; + data->rate = tp->mgmtrate; + + rum_setup_tx_desc(sc, &data->desc, k, flags, xflags, ac, hdrlen, + m0->m_pkthdr.len, tp->mgmtrate); + + DPRINTFN(10, "sending mgt frame len=%d rate=%d\n", + m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, tp->mgmtrate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[RUM_BULK_WR]); + + return (0); +} + +static int +rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_frame *wh; + struct rum_tx_data *data; + uint32_t flags; + uint8_t ac, type, xflags = 0; + int rate, error; + + RUM_LOCK_ASSERT(sc); + + wh = mtod(m0, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + ac = params->ibp_pri & 3; + + rate = params->ibp_rate0; + if (!ieee80211_isratevalid(ic->ic_rt, rate)) + return (EINVAL); + + flags = 0; + if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) + flags |= RT2573_TX_NEED_ACK; + if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { + error = rum_sendprot(sc, m0, ni, + params->ibp_flags & IEEE80211_BPF_RTS ? + IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, + rate); + if (error || sc->tx_nfree == 0) + return (ENOBUFS); + + flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; + } + + if (type != IEEE80211_FC0_TYPE_CTL && !IEEE80211_QOS_HAS_SEQ(wh)) + xflags |= RT2573_TX_HWSEQ; + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + data->m = m0; + data->ni = ni; + data->rate = rate; + + /* XXX need to setup descriptor ourself */ + rum_setup_tx_desc(sc, &data->desc, NULL, flags, xflags, ac, 0, + m0->m_pkthdr.len, rate); + + DPRINTFN(10, "sending raw frame len=%u rate=%u\n", + m0->m_pkthdr.len, rate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[RUM_BULK_WR]); + + return 0; +} + +static int +rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = &sc->sc_ic; + struct rum_tx_data *data; + struct ieee80211_frame *wh; + const struct ieee80211_txparam *tp = ni->ni_txparms; + struct ieee80211_key *k = NULL; + uint32_t flags = 0; + uint16_t dur; + uint8_t ac, type, qos, xflags = 0; + int error, hdrlen, rate; + + RUM_LOCK_ASSERT(sc); + + wh = mtod(m0, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + hdrlen = ieee80211_anyhdrsize(wh); + + if (IEEE80211_QOS_HAS_SEQ(wh)) + qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; + else + qos = 0; + ac = M_WME_GETAC(m0); + + if (m0->m_flags & M_EAPOL) + rate = tp->mgmtrate; + else if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + rate = tp->mcastrate; + else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + rate = tp->ucastrate; + else { + (void) ieee80211_ratectl_rate(ni, NULL, 0); + rate = ni->ni_txrate; + } + + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_get_txkey(ni, m0); + if (k == NULL) { + m_freem(m0); + return (ENOENT); + } + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !k->wk_cipher->ic_encap(k, m0)) { + m_freem(m0); + return (ENOBUFS); + } + + /* packet header may have moved, reset our local pointer */ + wh = mtod(m0, struct ieee80211_frame *); + } + + if (type != IEEE80211_FC0_TYPE_CTL && !IEEE80211_QOS_HAS_SEQ(wh)) + xflags |= RT2573_TX_HWSEQ; + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + int prot = IEEE80211_PROT_NONE; + if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) + prot = IEEE80211_PROT_RTSCTS; + else if ((ic->ic_flags & IEEE80211_F_USEPROT) && + ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) + prot = ic->ic_protmode; + if (prot != IEEE80211_PROT_NONE) { + error = rum_sendprot(sc, m0, ni, prot, rate); + if (error || sc->tx_nfree == 0) { + m_freem(m0); + return ENOBUFS; + } + flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS; + } + } + + if (k != NULL) + flags |= rum_tx_crypto_flags(sc, ni, k); + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + data->m = m0; + data->ni = ni; + data->rate = rate; + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + /* Unicast frame, check if an ACK is expected. */ + if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != + IEEE80211_QOS_ACKPOLICY_NOACK) + flags |= RT2573_TX_NEED_ACK; + + dur = ieee80211_ack_duration(ic->ic_rt, rate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + *(uint16_t *)wh->i_dur = htole16(dur); + } + + rum_setup_tx_desc(sc, &data->desc, k, flags, xflags, ac, hdrlen, + m0->m_pkthdr.len, rate); + + DPRINTFN(10, "sending frame len=%d rate=%d\n", + m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, rate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[RUM_BULK_WR]); + + return 0; +} + +static int +rum_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct rum_softc *sc = ic->ic_softc; + int error; + + RUM_LOCK(sc); + if (!sc->sc_running) { + RUM_UNLOCK(sc); + return (ENXIO); + } + error = mbufq_enqueue(&sc->sc_snd, m); + if (error) { + RUM_UNLOCK(sc); + return (error); + } + rum_start(sc); + RUM_UNLOCK(sc); + + return (0); +} + +static void +rum_start(struct rum_softc *sc) +{ + struct ieee80211_node *ni; + struct mbuf *m; + + RUM_LOCK_ASSERT(sc); + + if (!sc->sc_running) + return; + + while (sc->tx_nfree >= RUM_TX_MINFREE && + (m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; + if (rum_tx_data(sc, m, ni) != 0) { + if_inc_counter(ni->ni_vap->iv_ifp, + IFCOUNTER_OERRORS, 1); + ieee80211_free_node(ni); + break; + } + } +} + +static void +rum_parent(struct ieee80211com *ic) +{ + struct rum_softc *sc = ic->ic_softc; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + RUM_LOCK(sc); + if (sc->sc_detached) { + RUM_UNLOCK(sc); + return; + } + RUM_UNLOCK(sc); + + if (ic->ic_nrunning > 0) { + if (rum_init(sc) == 0) + ieee80211_start_all(ic); + else + ieee80211_stop(vap); + } else + rum_stop(sc); +} + +static void +rum_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, int len) +{ + struct usb_device_request req; + usb_error_t error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RT2573_READ_EEPROM; + USETW(req.wValue, 0); + USETW(req.wIndex, addr); + USETW(req.wLength, len); + + error = rum_do_request(sc, &req, buf); + if (error != 0) { + device_printf(sc->sc_dev, "could not read EEPROM: %s\n", + usbd_errstr(error)); + } +} + +static uint32_t +rum_read(struct rum_softc *sc, uint16_t reg) +{ + uint32_t val; + + rum_read_multi(sc, reg, &val, sizeof val); + + return le32toh(val); +} + +static void +rum_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, int len) +{ + struct usb_device_request req; + usb_error_t error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RT2573_READ_MULTI_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + error = rum_do_request(sc, &req, buf); + if (error != 0) { + device_printf(sc->sc_dev, + "could not multi read MAC register: %s\n", + usbd_errstr(error)); + } +} + +static usb_error_t +rum_write(struct rum_softc *sc, uint16_t reg, uint32_t val) +{ + uint32_t tmp = htole32(val); + + return (rum_write_multi(sc, reg, &tmp, sizeof tmp)); +} + +static usb_error_t +rum_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, size_t len) +{ + struct usb_device_request req; + usb_error_t error; + size_t offset; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RT2573_WRITE_MULTI_MAC; + USETW(req.wValue, 0); + + /* write at most 64 bytes at a time */ + for (offset = 0; offset < len; offset += 64) { + USETW(req.wIndex, reg + offset); + USETW(req.wLength, MIN(len - offset, 64)); + + error = rum_do_request(sc, &req, (char *)buf + offset); + if (error != 0) { + device_printf(sc->sc_dev, + "could not multi write MAC register: %s\n", + usbd_errstr(error)); + return (error); + } + } + + return (USB_ERR_NORMAL_COMPLETION); +} + +static usb_error_t +rum_setbits(struct rum_softc *sc, uint16_t reg, uint32_t mask) +{ + return (rum_write(sc, reg, rum_read(sc, reg) | mask)); +} + +static usb_error_t +rum_clrbits(struct rum_softc *sc, uint16_t reg, uint32_t mask) +{ + return (rum_write(sc, reg, rum_read(sc, reg) & ~mask)); +} + +static usb_error_t +rum_modbits(struct rum_softc *sc, uint16_t reg, uint32_t set, uint32_t unset) +{ + return (rum_write(sc, reg, (rum_read(sc, reg) & ~unset) | set)); +} + +static int +rum_bbp_busy(struct rum_softc *sc) +{ + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(rum_read(sc, RT2573_PHY_CSR3) & RT2573_BBP_BUSY)) + break; + if (rum_pause(sc, hz / 100)) + break; + } + if (ntries == 100) + return (ETIMEDOUT); + + return (0); +} + +static void +rum_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val) +{ + uint32_t tmp; + + DPRINTFN(2, "reg=0x%08x\n", reg); + + if (rum_bbp_busy(sc) != 0) { + device_printf(sc->sc_dev, "could not write to BBP\n"); + return; + } + + tmp = RT2573_BBP_BUSY | (reg & 0x7f) << 8 | val; + rum_write(sc, RT2573_PHY_CSR3, tmp); +} + +static uint8_t +rum_bbp_read(struct rum_softc *sc, uint8_t reg) +{ + uint32_t val; + int ntries; + + DPRINTFN(2, "reg=0x%08x\n", reg); + + if (rum_bbp_busy(sc) != 0) { + device_printf(sc->sc_dev, "could not read BBP\n"); + return 0; + } + + val = RT2573_BBP_BUSY | RT2573_BBP_READ | reg << 8; + rum_write(sc, RT2573_PHY_CSR3, val); + + for (ntries = 0; ntries < 100; ntries++) { + val = rum_read(sc, RT2573_PHY_CSR3); + if (!(val & RT2573_BBP_BUSY)) + return val & 0xff; + if (rum_pause(sc, hz / 100)) + break; + } + + device_printf(sc->sc_dev, "could not read BBP\n"); + return 0; +} + +static void +rum_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val) +{ + uint32_t tmp; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(rum_read(sc, RT2573_PHY_CSR4) & RT2573_RF_BUSY)) + break; + if (rum_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "could not write to RF\n"); + return; + } + + tmp = RT2573_RF_BUSY | RT2573_RF_20BIT | (val & 0xfffff) << 2 | + (reg & 3); + rum_write(sc, RT2573_PHY_CSR4, tmp); + + /* remember last written value in sc */ + sc->rf_regs[reg] = val; + + DPRINTFN(15, "RF R[%u] <- 0x%05x\n", reg & 3, val & 0xfffff); +} + +static void +rum_select_antenna(struct rum_softc *sc) +{ + uint8_t bbp4, bbp77; + uint32_t tmp; + + bbp4 = rum_bbp_read(sc, 4); + bbp77 = rum_bbp_read(sc, 77); + + /* TBD */ + + /* make sure Rx is disabled before switching antenna */ + tmp = rum_read(sc, RT2573_TXRX_CSR0); + rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX); + + rum_bbp_write(sc, 4, bbp4); + rum_bbp_write(sc, 77, bbp77); + + rum_write(sc, RT2573_TXRX_CSR0, tmp); +} + +/* + * Enable multi-rate retries for frames sent at OFDM rates. + * In 802.11b/g mode, allow fallback to CCK rates. + */ +static void +rum_enable_mrr(struct rum_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + + if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) { + rum_setbits(sc, RT2573_TXRX_CSR4, + RT2573_MRR_ENABLED | RT2573_MRR_CCK_FALLBACK); + } else { + rum_modbits(sc, RT2573_TXRX_CSR4, + RT2573_MRR_ENABLED, RT2573_MRR_CCK_FALLBACK); + } +} + +static void +rum_set_txpreamble(struct rum_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + rum_setbits(sc, RT2573_TXRX_CSR4, RT2573_SHORT_PREAMBLE); + else + rum_clrbits(sc, RT2573_TXRX_CSR4, RT2573_SHORT_PREAMBLE); +} + +static void +rum_set_basicrates(struct rum_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + + /* update basic rate set */ + if (ic->ic_curmode == IEEE80211_MODE_11B) { + /* 11b basic rates: 1, 2Mbps */ + rum_write(sc, RT2573_TXRX_CSR5, 0x3); + } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) { + /* 11a basic rates: 6, 12, 24Mbps */ + rum_write(sc, RT2573_TXRX_CSR5, 0x150); + } else { + /* 11b/g basic rates: 1, 2, 5.5, 11Mbps */ + rum_write(sc, RT2573_TXRX_CSR5, 0xf); + } +} + +/* + * Reprogram MAC/BBP to switch to a new band. Values taken from the reference + * driver. + */ +static void +rum_select_band(struct rum_softc *sc, struct ieee80211_channel *c) +{ + uint8_t bbp17, bbp35, bbp96, bbp97, bbp98, bbp104; + + /* update all BBP registers that depend on the band */ + bbp17 = 0x20; bbp96 = 0x48; bbp104 = 0x2c; + bbp35 = 0x50; bbp97 = 0x48; bbp98 = 0x48; + if (IEEE80211_IS_CHAN_5GHZ(c)) { + bbp17 += 0x08; bbp96 += 0x10; bbp104 += 0x0c; + bbp35 += 0x10; bbp97 += 0x10; bbp98 += 0x10; + } + if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) || + (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) { + bbp17 += 0x10; bbp96 += 0x10; bbp104 += 0x10; + } + + sc->bbp17 = bbp17; + rum_bbp_write(sc, 17, bbp17); + rum_bbp_write(sc, 96, bbp96); + rum_bbp_write(sc, 104, bbp104); + + if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) || + (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) { + rum_bbp_write(sc, 75, 0x80); + rum_bbp_write(sc, 86, 0x80); + rum_bbp_write(sc, 88, 0x80); + } + + rum_bbp_write(sc, 35, bbp35); + rum_bbp_write(sc, 97, bbp97); + rum_bbp_write(sc, 98, bbp98); + + if (IEEE80211_IS_CHAN_2GHZ(c)) { + rum_modbits(sc, RT2573_PHY_CSR0, RT2573_PA_PE_2GHZ, + RT2573_PA_PE_5GHZ); + } else { + rum_modbits(sc, RT2573_PHY_CSR0, RT2573_PA_PE_5GHZ, + RT2573_PA_PE_2GHZ); + } +} + +static void +rum_set_chan(struct rum_softc *sc, struct ieee80211_channel *c) +{ + struct ieee80211com *ic = &sc->sc_ic; + const struct rfprog *rfprog; + uint8_t bbp3, bbp94 = RT2573_BBPR94_DEFAULT; + int8_t power; + int i, chan; + + chan = ieee80211_chan2ieee(ic, c); + if (chan == 0 || chan == IEEE80211_CHAN_ANY) + return; + + /* select the appropriate RF settings based on what EEPROM says */ + rfprog = (sc->rf_rev == RT2573_RF_5225 || + sc->rf_rev == RT2573_RF_2527) ? rum_rf5225 : rum_rf5226; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rfprog[i].chan != chan; i++); + + power = sc->txpow[i]; + if (power < 0) { + bbp94 += power; + power = 0; + } else if (power > 31) { + bbp94 += power - 31; + power = 31; + } + + /* + * If we are switching from the 2GHz band to the 5GHz band or + * vice-versa, BBP registers need to be reprogrammed. + */ + if (c->ic_flags != ic->ic_curchan->ic_flags) { + rum_select_band(sc, c); + rum_select_antenna(sc); + } + ic->ic_curchan = c; + + rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); + rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); + rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7); + rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); + + rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); + rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); + rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7 | 1); + rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); + + rum_rf_write(sc, RT2573_RF1, rfprog[i].r1); + rum_rf_write(sc, RT2573_RF2, rfprog[i].r2); + rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7); + rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10); + + rum_pause(sc, hz / 100); + + /* enable smart mode for MIMO-capable RFs */ + bbp3 = rum_bbp_read(sc, 3); + + bbp3 &= ~RT2573_SMART_MODE; + if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_2527) + bbp3 |= RT2573_SMART_MODE; + + rum_bbp_write(sc, 3, bbp3); + + if (bbp94 != RT2573_BBPR94_DEFAULT) + rum_bbp_write(sc, 94, bbp94); + + /* give the chip some extra time to do the switchover */ + rum_pause(sc, hz / 100); +} + +static void +rum_set_maxretry(struct rum_softc *sc, struct ieee80211vap *vap) +{ + struct ieee80211_node *ni = vap->iv_bss; + const struct ieee80211_txparam *tp = ni->ni_txparms; + struct rum_vap *rvp = RUM_VAP(vap); + + rvp->maxretry = MIN(tp->maxretry, 0xf); + + rum_modbits(sc, RT2573_TXRX_CSR4, RT2573_SHORT_RETRY(rvp->maxretry) | + RT2573_LONG_RETRY(rvp->maxretry), + RT2573_SHORT_RETRY_MASK | RT2573_LONG_RETRY_MASK); +} + +/* + * Enable TSF synchronization and tell h/w to start sending beacons for IBSS + * and HostAP operating modes. + */ +static int +rum_enable_tsf_sync(struct rum_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t tmp; + uint16_t bintval; + + if (vap->iv_opmode != IEEE80211_M_STA) { + /* + * Change default 16ms TBTT adjustment to 8ms. + * Must be done before enabling beacon generation. + */ + if (rum_write(sc, RT2573_TXRX_CSR10, 1 << 12 | 8) != 0) + return EIO; + } + + tmp = rum_read(sc, RT2573_TXRX_CSR9) & 0xff000000; + + /* set beacon interval (in 1/16ms unit) */ + bintval = vap->iv_bss->ni_intval; + tmp |= bintval * 16; + tmp |= RT2573_TSF_TIMER_EN | RT2573_TBTT_TIMER_EN; + + switch (vap->iv_opmode) { + case IEEE80211_M_STA: + /* + * Local TSF is always updated with remote TSF on beacon + * reception. + */ + tmp |= RT2573_TSF_SYNC_MODE(RT2573_TSF_SYNC_MODE_STA); + break; + case IEEE80211_M_IBSS: + /* + * Local TSF is updated with remote TSF on beacon reception + * only if the remote TSF is greater than local TSF. + */ + tmp |= RT2573_TSF_SYNC_MODE(RT2573_TSF_SYNC_MODE_IBSS); + tmp |= RT2573_BCN_TX_EN; + break; + case IEEE80211_M_HOSTAP: + /* SYNC with nobody */ + tmp |= RT2573_TSF_SYNC_MODE(RT2573_TSF_SYNC_MODE_HOSTAP); + tmp |= RT2573_BCN_TX_EN; + break; + default: + device_printf(sc->sc_dev, + "Enabling TSF failed. undefined opmode %d\n", + vap->iv_opmode); + return EINVAL; + } + + if (rum_write(sc, RT2573_TXRX_CSR9, tmp) != 0) + return EIO; + + /* refresh current sleep time */ + return (rum_set_sleep_time(sc, bintval)); +} + +static void +rum_enable_tsf(struct rum_softc *sc) +{ + rum_modbits(sc, RT2573_TXRX_CSR9, RT2573_TSF_TIMER_EN | + RT2573_TSF_SYNC_MODE(RT2573_TSF_SYNC_MODE_DIS), 0x00ffffff); +} + +static void +rum_abort_tsf_sync(struct rum_softc *sc) +{ + rum_clrbits(sc, RT2573_TXRX_CSR9, 0x00ffffff); +} + +static void +rum_get_tsf(struct rum_softc *sc, uint64_t *buf) +{ + rum_read_multi(sc, RT2573_TXRX_CSR12, buf, sizeof (*buf)); +} + +static void +rum_update_slot_cb(struct rum_softc *sc, union sec_param *data, uint8_t rvp_id) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint8_t slottime; + + slottime = IEEE80211_GET_SLOTTIME(ic); + + rum_modbits(sc, RT2573_MAC_CSR9, slottime, 0xff); + + DPRINTF("setting slot time to %uus\n", slottime); +} + +static void +rum_update_slot(struct ieee80211com *ic) +{ + rum_cmd_sleepable(ic->ic_softc, NULL, 0, 0, rum_update_slot_cb); +} + +static int +rum_wme_update(struct ieee80211com *ic) +{ + struct chanAccParams chp; + const struct wmeParams *chanp; + struct rum_softc *sc = ic->ic_softc; + int error = 0; + + ieee80211_wme_ic_getparams(ic, &chp); + chanp = chp.cap_wmeParams; + + RUM_LOCK(sc); + error = rum_write(sc, RT2573_AIFSN_CSR, + chanp[WME_AC_VO].wmep_aifsn << 12 | + chanp[WME_AC_VI].wmep_aifsn << 8 | + chanp[WME_AC_BK].wmep_aifsn << 4 | + chanp[WME_AC_BE].wmep_aifsn); + if (error) + goto print_err; + error = rum_write(sc, RT2573_CWMIN_CSR, + chanp[WME_AC_VO].wmep_logcwmin << 12 | + chanp[WME_AC_VI].wmep_logcwmin << 8 | + chanp[WME_AC_BK].wmep_logcwmin << 4 | + chanp[WME_AC_BE].wmep_logcwmin); + if (error) + goto print_err; + error = rum_write(sc, RT2573_CWMAX_CSR, + chanp[WME_AC_VO].wmep_logcwmax << 12 | + chanp[WME_AC_VI].wmep_logcwmax << 8 | + chanp[WME_AC_BK].wmep_logcwmax << 4 | + chanp[WME_AC_BE].wmep_logcwmax); + if (error) + goto print_err; + error = rum_write(sc, RT2573_TXOP01_CSR, + chanp[WME_AC_BK].wmep_txopLimit << 16 | + chanp[WME_AC_BE].wmep_txopLimit); + if (error) + goto print_err; + error = rum_write(sc, RT2573_TXOP23_CSR, + chanp[WME_AC_VO].wmep_txopLimit << 16 | + chanp[WME_AC_VI].wmep_txopLimit); + if (error) + goto print_err; + + memcpy(sc->wme_params, chanp, sizeof(*chanp) * WME_NUM_AC); + +print_err: + RUM_UNLOCK(sc); + if (error != 0) { + device_printf(sc->sc_dev, "%s: WME update failed, error %d\n", + __func__, error); + } + + return (error); +} + +static void +rum_set_bssid(struct rum_softc *sc, const uint8_t *bssid) +{ + + rum_write(sc, RT2573_MAC_CSR4, + bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24); + rum_write(sc, RT2573_MAC_CSR5, + bssid[4] | bssid[5] << 8 | RT2573_NUM_BSSID_MSK(1)); +} + +static void +rum_set_macaddr(struct rum_softc *sc, const uint8_t *addr) +{ + + rum_write(sc, RT2573_MAC_CSR2, + addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); + rum_write(sc, RT2573_MAC_CSR3, + addr[4] | addr[5] << 8 | 0xff << 16); +} + +static void +rum_setpromisc(struct rum_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + + if (ic->ic_promisc == 0) + rum_setbits(sc, RT2573_TXRX_CSR0, RT2573_DROP_NOT_TO_ME); + else + rum_clrbits(sc, RT2573_TXRX_CSR0, RT2573_DROP_NOT_TO_ME); + + DPRINTF("%s promiscuous mode\n", ic->ic_promisc > 0 ? + "entering" : "leaving"); +} + +static void +rum_update_promisc(struct ieee80211com *ic) +{ + struct rum_softc *sc = ic->ic_softc; + + RUM_LOCK(sc); + if (sc->sc_running) + rum_setpromisc(sc); + RUM_UNLOCK(sc); +} + +static void +rum_update_mcast(struct ieee80211com *ic) +{ + /* Ignore. */ +} + +static const char * +rum_get_rf(int rev) +{ + switch (rev) { + case RT2573_RF_2527: return "RT2527 (MIMO XR)"; + case RT2573_RF_2528: return "RT2528"; + case RT2573_RF_5225: return "RT5225 (MIMO XR)"; + case RT2573_RF_5226: return "RT5226"; + default: return "unknown"; + } +} + +static void +rum_read_eeprom(struct rum_softc *sc) +{ + uint16_t val; +#ifdef RUM_DEBUG + int i; +#endif + + /* read MAC address */ + rum_eeprom_read(sc, RT2573_EEPROM_ADDRESS, sc->sc_ic.ic_macaddr, 6); + + rum_eeprom_read(sc, RT2573_EEPROM_ANTENNA, &val, 2); + val = le16toh(val); + sc->rf_rev = (val >> 11) & 0x1f; + sc->hw_radio = (val >> 10) & 0x1; + sc->rx_ant = (val >> 4) & 0x3; + sc->tx_ant = (val >> 2) & 0x3; + sc->nb_ant = val & 0x3; + + DPRINTF("RF revision=%d\n", sc->rf_rev); + + rum_eeprom_read(sc, RT2573_EEPROM_CONFIG2, &val, 2); + val = le16toh(val); + sc->ext_5ghz_lna = (val >> 6) & 0x1; + sc->ext_2ghz_lna = (val >> 4) & 0x1; + + DPRINTF("External 2GHz LNA=%d\nExternal 5GHz LNA=%d\n", + sc->ext_2ghz_lna, sc->ext_5ghz_lna); + + rum_eeprom_read(sc, RT2573_EEPROM_RSSI_2GHZ_OFFSET, &val, 2); + val = le16toh(val); + if ((val & 0xff) != 0xff) + sc->rssi_2ghz_corr = (int8_t)(val & 0xff); /* signed */ + + /* Only [-10, 10] is valid */ + if (sc->rssi_2ghz_corr < -10 || sc->rssi_2ghz_corr > 10) + sc->rssi_2ghz_corr = 0; + + rum_eeprom_read(sc, RT2573_EEPROM_RSSI_5GHZ_OFFSET, &val, 2); + val = le16toh(val); + if ((val & 0xff) != 0xff) + sc->rssi_5ghz_corr = (int8_t)(val & 0xff); /* signed */ + + /* Only [-10, 10] is valid */ + if (sc->rssi_5ghz_corr < -10 || sc->rssi_5ghz_corr > 10) + sc->rssi_5ghz_corr = 0; + + if (sc->ext_2ghz_lna) + sc->rssi_2ghz_corr -= 14; + if (sc->ext_5ghz_lna) + sc->rssi_5ghz_corr -= 14; + + DPRINTF("RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n", + sc->rssi_2ghz_corr, sc->rssi_5ghz_corr); + + rum_eeprom_read(sc, RT2573_EEPROM_FREQ_OFFSET, &val, 2); + val = le16toh(val); + if ((val & 0xff) != 0xff) + sc->rffreq = val & 0xff; + + DPRINTF("RF freq=%d\n", sc->rffreq); + + /* read Tx power for all a/b/g channels */ + rum_eeprom_read(sc, RT2573_EEPROM_TXPOWER, sc->txpow, 14); + /* XXX default Tx power for 802.11a channels */ + memset(sc->txpow + 14, 24, sizeof (sc->txpow) - 14); +#ifdef RUM_DEBUG + for (i = 0; i < 14; i++) + DPRINTF("Channel=%d Tx power=%d\n", i + 1, sc->txpow[i]); +#endif + + /* read default values for BBP registers */ + rum_eeprom_read(sc, RT2573_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16); +#ifdef RUM_DEBUG + for (i = 0; i < 14; i++) { + if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff) + continue; + DPRINTF("BBP R%d=%02x\n", sc->bbp_prom[i].reg, + sc->bbp_prom[i].val); + } +#endif +} + +static int +rum_bbp_wakeup(struct rum_softc *sc) +{ + unsigned int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (rum_read(sc, RT2573_MAC_CSR12) & 8) + break; + rum_write(sc, RT2573_MAC_CSR12, 4); /* force wakeup */ + if (rum_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, + "timeout waiting for BBP/RF to wakeup\n"); + return (ETIMEDOUT); + } + + return (0); +} + +static int +rum_bbp_init(struct rum_softc *sc) +{ + int i, ntries; + + /* wait for BBP to be ready */ + for (ntries = 0; ntries < 100; ntries++) { + const uint8_t val = rum_bbp_read(sc, 0); + if (val != 0 && val != 0xff) + break; + if (rum_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "timeout waiting for BBP\n"); + return EIO; + } + + /* initialize BBP registers to default values */ + for (i = 0; i < nitems(rum_def_bbp); i++) + rum_bbp_write(sc, rum_def_bbp[i].reg, rum_def_bbp[i].val); + + /* write vendor-specific BBP values (from EEPROM) */ + for (i = 0; i < 16; i++) { + if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff) + continue; + rum_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val); + } + + return 0; +} + +static void +rum_clr_shkey_regs(struct rum_softc *sc) +{ + rum_write(sc, RT2573_SEC_CSR0, 0); + rum_write(sc, RT2573_SEC_CSR1, 0); + rum_write(sc, RT2573_SEC_CSR5, 0); +} + +static int +rum_init(struct rum_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t tmp; + int i, ret; + + RUM_LOCK(sc); + if (sc->sc_running) { + ret = 0; + goto end; + } + + /* initialize MAC registers to default values */ + for (i = 0; i < nitems(rum_def_mac); i++) + rum_write(sc, rum_def_mac[i].reg, rum_def_mac[i].val); + + /* reset some WME parameters to default values */ + sc->wme_params[0].wmep_aifsn = 2; + sc->wme_params[0].wmep_logcwmin = 4; + sc->wme_params[0].wmep_logcwmax = 10; + + /* set host ready */ + rum_write(sc, RT2573_MAC_CSR1, RT2573_RESET_ASIC | RT2573_RESET_BBP); + rum_write(sc, RT2573_MAC_CSR1, 0); + + /* wait for BBP/RF to wakeup */ + if ((ret = rum_bbp_wakeup(sc)) != 0) + goto end; + + if ((ret = rum_bbp_init(sc)) != 0) + goto end; + + /* select default channel */ + rum_select_band(sc, ic->ic_curchan); + rum_select_antenna(sc); + rum_set_chan(sc, ic->ic_curchan); + + /* clear STA registers */ + rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta); + + /* clear security registers (if required) */ + if (sc->sc_clr_shkeys == 0) { + rum_clr_shkey_regs(sc); + sc->sc_clr_shkeys = 1; + } + + rum_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr); + + /* initialize ASIC */ + rum_write(sc, RT2573_MAC_CSR1, RT2573_HOST_READY); + + /* + * Allocate Tx and Rx xfer queues. + */ + rum_setup_tx_list(sc); + + /* update Rx filter */ + tmp = rum_read(sc, RT2573_TXRX_CSR0) & 0xffff; + + tmp |= RT2573_DROP_PHY_ERROR | RT2573_DROP_CRC_ERROR; + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + tmp |= RT2573_DROP_CTL | RT2573_DROP_VER_ERROR | + RT2573_DROP_ACKCTS; + if (ic->ic_opmode != IEEE80211_M_HOSTAP) + tmp |= RT2573_DROP_TODS; + if (ic->ic_promisc == 0) + tmp |= RT2573_DROP_NOT_TO_ME; + } + rum_write(sc, RT2573_TXRX_CSR0, tmp); + + sc->sc_running = 1; + usbd_xfer_set_stall(sc->sc_xfer[RUM_BULK_WR]); + usbd_transfer_start(sc->sc_xfer[RUM_BULK_RD]); + +end: RUM_UNLOCK(sc); + + if (ret != 0) + rum_stop(sc); + + return ret; +} + +static void +rum_stop(struct rum_softc *sc) +{ + + RUM_LOCK(sc); + if (!sc->sc_running) { + RUM_UNLOCK(sc); + return; + } + sc->sc_running = 0; + RUM_UNLOCK(sc); + + /* + * Drain the USB transfers, if not already drained: + */ + usbd_transfer_drain(sc->sc_xfer[RUM_BULK_WR]); + usbd_transfer_drain(sc->sc_xfer[RUM_BULK_RD]); + + RUM_LOCK(sc); + rum_unsetup_tx_list(sc); + + /* disable Rx */ + rum_setbits(sc, RT2573_TXRX_CSR0, RT2573_DISABLE_RX); + + /* reset ASIC */ + rum_write(sc, RT2573_MAC_CSR1, RT2573_RESET_ASIC | RT2573_RESET_BBP); + rum_write(sc, RT2573_MAC_CSR1, 0); + RUM_UNLOCK(sc); +} + +static void +rum_load_microcode(struct rum_softc *sc, const uint8_t *ucode, size_t size) +{ + uint16_t reg = RT2573_MCU_CODE_BASE; + usb_error_t err; + + /* copy firmware image into NIC */ + for (; size >= 4; reg += 4, ucode += 4, size -= 4) { + err = rum_write(sc, reg, UGETDW(ucode)); + if (err) { + /* firmware already loaded ? */ + device_printf(sc->sc_dev, "Firmware load " + "failure! (ignored)\n"); + break; + } + } + + err = rum_do_mcu_request(sc, RT2573_MCU_RUN); + if (err != USB_ERR_NORMAL_COMPLETION) { + device_printf(sc->sc_dev, "could not run firmware: %s\n", + usbd_errstr(err)); + } + + /* give the chip some time to boot */ + rum_pause(sc, hz / 8); +} + +static int +rum_set_sleep_time(struct rum_softc *sc, uint16_t bintval) +{ + struct ieee80211com *ic = &sc->sc_ic; + usb_error_t uerror; + int exp, delay; + + RUM_LOCK_ASSERT(sc); + + exp = ic->ic_lintval / bintval; + delay = ic->ic_lintval % bintval; + + if (exp > RT2573_TBCN_EXP_MAX) + exp = RT2573_TBCN_EXP_MAX; + if (delay > RT2573_TBCN_DELAY_MAX) + delay = RT2573_TBCN_DELAY_MAX; + + uerror = rum_modbits(sc, RT2573_MAC_CSR11, + RT2573_TBCN_EXP(exp) | + RT2573_TBCN_DELAY(delay), + RT2573_TBCN_EXP(RT2573_TBCN_EXP_MAX) | + RT2573_TBCN_DELAY(RT2573_TBCN_DELAY_MAX)); + + if (uerror != USB_ERR_NORMAL_COMPLETION) + return (EIO); + + sc->sc_sleep_time = IEEE80211_TU_TO_TICKS(exp * bintval + delay); + + return (0); +} + +static int +rum_reset(struct ieee80211vap *vap, u_long cmd) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni; + struct rum_softc *sc = ic->ic_softc; + int error; + + switch (cmd) { + case IEEE80211_IOC_POWERSAVE: + case IEEE80211_IOC_PROTMODE: + case IEEE80211_IOC_RTSTHRESHOLD: + error = 0; + break; + case IEEE80211_IOC_POWERSAVESLEEP: + ni = ieee80211_ref_node(vap->iv_bss); + + RUM_LOCK(sc); + error = rum_set_sleep_time(sc, ni->ni_intval); + if (vap->iv_state == IEEE80211_S_SLEEP) { + /* Use new values for wakeup timer. */ + rum_clrbits(sc, RT2573_MAC_CSR11, RT2573_AUTO_WAKEUP); + rum_setbits(sc, RT2573_MAC_CSR11, RT2573_AUTO_WAKEUP); + } + /* XXX send reassoc */ + RUM_UNLOCK(sc); + + ieee80211_free_node(ni); + break; + default: + error = ENETRESET; + break; + } + + return (error); +} + +static int +rum_set_beacon(struct rum_softc *sc, struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct rum_vap *rvp = RUM_VAP(vap); + struct mbuf *m = rvp->bcn_mbuf; + const struct ieee80211_txparam *tp; + struct rum_tx_desc desc; + + RUM_LOCK_ASSERT(sc); + + if (m == NULL) + return EINVAL; + if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) + return EINVAL; + + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; + rum_setup_tx_desc(sc, &desc, NULL, RT2573_TX_TIMESTAMP, + RT2573_TX_HWSEQ, 0, 0, m->m_pkthdr.len, tp->mgmtrate); + + /* copy the Tx descriptor into NIC memory */ + if (rum_write_multi(sc, RT2573_HW_BCN_BASE(0), (uint8_t *)&desc, + RT2573_TX_DESC_SIZE) != 0) + return EIO; + + /* copy beacon header and payload into NIC memory */ + if (rum_write_multi(sc, RT2573_HW_BCN_BASE(0) + RT2573_TX_DESC_SIZE, + mtod(m, uint8_t *), m->m_pkthdr.len) != 0) + return EIO; + + return 0; +} + +static int +rum_alloc_beacon(struct rum_softc *sc, struct ieee80211vap *vap) +{ + struct rum_vap *rvp = RUM_VAP(vap); + struct ieee80211_node *ni = vap->iv_bss; + struct mbuf *m; + + if (ni->ni_chan == IEEE80211_CHAN_ANYC) + return EINVAL; + + m = ieee80211_beacon_alloc(ni); + if (m == NULL) + return ENOMEM; + + if (rvp->bcn_mbuf != NULL) + m_freem(rvp->bcn_mbuf); + + rvp->bcn_mbuf = m; + + return (rum_set_beacon(sc, vap)); +} + +static void +rum_update_beacon_cb(struct rum_softc *sc, union sec_param *data, + uint8_t rvp_id) +{ + struct ieee80211vap *vap = data->vap; + + rum_set_beacon(sc, vap); +} + +static void +rum_update_beacon(struct ieee80211vap *vap, int item) +{ + struct ieee80211com *ic = vap->iv_ic; + struct rum_softc *sc = ic->ic_softc; + struct rum_vap *rvp = RUM_VAP(vap); + struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off; + struct ieee80211_node *ni = vap->iv_bss; + struct mbuf *m = rvp->bcn_mbuf; + int mcast = 0; + + RUM_LOCK(sc); + if (m == NULL) { + m = ieee80211_beacon_alloc(ni); + if (m == NULL) { + device_printf(sc->sc_dev, + "%s: could not allocate beacon frame\n", __func__); + RUM_UNLOCK(sc); + return; + } + rvp->bcn_mbuf = m; + } + + switch (item) { + case IEEE80211_BEACON_ERP: + rum_update_slot(ic); + break; + case IEEE80211_BEACON_TIM: + mcast = 1; /*TODO*/ + break; + default: + break; + } + RUM_UNLOCK(sc); + + setbit(bo->bo_flags, item); + ieee80211_beacon_update(ni, m, mcast); + + rum_cmd_sleepable(sc, &vap, sizeof(vap), 0, rum_update_beacon_cb); +} + +static int +rum_common_key_set(struct rum_softc *sc, struct ieee80211_key *k, + uint16_t base) +{ + + if (rum_write_multi(sc, base, k->wk_key, k->wk_keylen)) + return EIO; + + if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP) { + if (rum_write_multi(sc, base + IEEE80211_KEYBUF_SIZE, + k->wk_txmic, 8)) + return EIO; + if (rum_write_multi(sc, base + IEEE80211_KEYBUF_SIZE + 8, + k->wk_rxmic, 8)) + return EIO; + } + + return 0; +} + +static void +rum_group_key_set_cb(struct rum_softc *sc, union sec_param *data, + uint8_t rvp_id) +{ + struct ieee80211_key *k = &data->key; + uint8_t mode; + + if (sc->sc_clr_shkeys == 0) { + rum_clr_shkey_regs(sc); + sc->sc_clr_shkeys = 1; + } + + mode = rum_crypto_mode(sc, k->wk_cipher->ic_cipher, k->wk_keylen); + if (mode == 0) + goto print_err; + + DPRINTFN(1, "setting group key %d for vap %d, mode %d " + "(tx %s, rx %s)\n", k->wk_keyix, rvp_id, mode, + (k->wk_flags & IEEE80211_KEY_XMIT) ? "on" : "off", + (k->wk_flags & IEEE80211_KEY_RECV) ? "on" : "off"); + + /* Install the key. */ + if (rum_common_key_set(sc, k, RT2573_SKEY(rvp_id, k->wk_keyix)) != 0) + goto print_err; + + /* Set cipher mode. */ + if (rum_modbits(sc, rvp_id < 2 ? RT2573_SEC_CSR1 : RT2573_SEC_CSR5, + mode << (rvp_id % 2 + k->wk_keyix) * RT2573_SKEY_MAX, + RT2573_MODE_MASK << (rvp_id % 2 + k->wk_keyix) * RT2573_SKEY_MAX) + != 0) + goto print_err; + + /* Mark this key as valid. */ + if (rum_setbits(sc, RT2573_SEC_CSR0, + 1 << (rvp_id * RT2573_SKEY_MAX + k->wk_keyix)) != 0) + goto print_err; + + return; + +print_err: + device_printf(sc->sc_dev, "%s: cannot set group key %d for vap %d\n", + __func__, k->wk_keyix, rvp_id); +} + +static void +rum_group_key_del_cb(struct rum_softc *sc, union sec_param *data, + uint8_t rvp_id) +{ + struct ieee80211_key *k = &data->key; + + DPRINTF("%s: removing group key %d for vap %d\n", __func__, + k->wk_keyix, rvp_id); + rum_clrbits(sc, + rvp_id < 2 ? RT2573_SEC_CSR1 : RT2573_SEC_CSR5, + RT2573_MODE_MASK << (rvp_id % 2 + k->wk_keyix) * RT2573_SKEY_MAX); + rum_clrbits(sc, RT2573_SEC_CSR0, + rvp_id * RT2573_SKEY_MAX + k->wk_keyix); +} + +static void +rum_pair_key_set_cb(struct rum_softc *sc, union sec_param *data, + uint8_t rvp_id) +{ + struct ieee80211_key *k = &data->key; + uint8_t buf[IEEE80211_ADDR_LEN + 1]; + uint8_t mode; + + mode = rum_crypto_mode(sc, k->wk_cipher->ic_cipher, k->wk_keylen); + if (mode == 0) + goto print_err; + + DPRINTFN(1, "setting pairwise key %d for vap %d, mode %d " + "(tx %s, rx %s)\n", k->wk_keyix, rvp_id, mode, + (k->wk_flags & IEEE80211_KEY_XMIT) ? "on" : "off", + (k->wk_flags & IEEE80211_KEY_RECV) ? "on" : "off"); + + /* Install the key. */ + if (rum_common_key_set(sc, k, RT2573_PKEY(k->wk_keyix)) != 0) + goto print_err; + + IEEE80211_ADDR_COPY(buf, k->wk_macaddr); + buf[IEEE80211_ADDR_LEN] = mode; + + /* Set transmitter address and cipher mode. */ + if (rum_write_multi(sc, RT2573_ADDR_ENTRY(k->wk_keyix), + buf, sizeof buf) != 0) + goto print_err; + + /* Enable key table lookup for this vap. */ + if (sc->vap_key_count[rvp_id]++ == 0) + if (rum_setbits(sc, RT2573_SEC_CSR4, 1 << rvp_id) != 0) + goto print_err; + + /* Mark this key as valid. */ + if (rum_setbits(sc, + k->wk_keyix < 32 ? RT2573_SEC_CSR2 : RT2573_SEC_CSR3, + 1 << (k->wk_keyix % 32)) != 0) + goto print_err; + + return; + +print_err: + device_printf(sc->sc_dev, + "%s: cannot set pairwise key %d, vap %d\n", __func__, k->wk_keyix, + rvp_id); +} + +static void +rum_pair_key_del_cb(struct rum_softc *sc, union sec_param *data, + uint8_t rvp_id) +{ + struct ieee80211_key *k = &data->key; + + DPRINTF("%s: removing key %d\n", __func__, k->wk_keyix); + rum_clrbits(sc, (k->wk_keyix < 32) ? RT2573_SEC_CSR2 : RT2573_SEC_CSR3, + 1 << (k->wk_keyix % 32)); + sc->keys_bmap &= ~(1ULL << k->wk_keyix); + if (--sc->vap_key_count[rvp_id] == 0) + rum_clrbits(sc, RT2573_SEC_CSR4, 1 << rvp_id); +} + +static int +rum_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, + ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) +{ + struct rum_softc *sc = vap->iv_ic->ic_softc; + uint8_t i; + + if (!(&vap->iv_nw_keys[0] <= k && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) { + if (!(k->wk_flags & IEEE80211_KEY_SWCRYPT)) { + RUM_LOCK(sc); + for (i = 0; i < RT2573_ADDR_MAX; i++) { + if ((sc->keys_bmap & (1ULL << i)) == 0) { + sc->keys_bmap |= (1ULL << i); + *keyix = i; + break; + } + } + RUM_UNLOCK(sc); + if (i == RT2573_ADDR_MAX) { + device_printf(sc->sc_dev, + "%s: no free space in the key table\n", + __func__); + return 0; + } + } else + *keyix = 0; + } else { + *keyix = ieee80211_crypto_get_key_wepidx(vap, k); + } + *rxkeyix = *keyix; + return 1; +} + +static int +rum_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k) +{ + struct rum_softc *sc = vap->iv_ic->ic_softc; + int group; + + if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { + /* Not for us. */ + return 1; + } + + group = k >= &vap->iv_nw_keys[0] && k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]; + + return !rum_cmd_sleepable(sc, k, sizeof(*k), 0, + group ? rum_group_key_set_cb : rum_pair_key_set_cb); +} + +static int +rum_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) +{ + struct rum_softc *sc = vap->iv_ic->ic_softc; + int group; + + if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { + /* Not for us. */ + return 1; + } + + group = k >= &vap->iv_nw_keys[0] && k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]; + + return !rum_cmd_sleepable(sc, k, sizeof(*k), 0, + group ? rum_group_key_del_cb : rum_pair_key_del_cb); +} + +static int +rum_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct rum_softc *sc = ni->ni_ic->ic_softc; + int ret; + + RUM_LOCK(sc); + /* prevent management frames from being sent if we're not ready */ + if (!sc->sc_running) { + ret = ENETDOWN; + goto bad; + } + if (sc->tx_nfree < RUM_TX_MINFREE) { + ret = EIO; + goto bad; + } + + if (params == NULL) { + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + */ + if ((ret = rum_tx_mgt(sc, m, ni)) != 0) + goto bad; + } else { + /* + * Caller supplied explicit parameters to use in + * sending the frame. + */ + if ((ret = rum_tx_raw(sc, m, ni, params)) != 0) + goto bad; + } + RUM_UNLOCK(sc); + + return 0; +bad: + RUM_UNLOCK(sc); + m_freem(m); + return ret; +} + +static void +rum_ratectl_start(struct rum_softc *sc, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct rum_vap *rvp = RUM_VAP(vap); + + /* clear statistic registers (STA_CSR0 to STA_CSR5) */ + rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta); + + usb_callout_reset(&rvp->ratectl_ch, hz, rum_ratectl_timeout, rvp); +} + +static void +rum_ratectl_timeout(void *arg) +{ + struct rum_vap *rvp = arg; + struct ieee80211vap *vap = &rvp->vap; + struct ieee80211com *ic = vap->iv_ic; + + ieee80211_runtask(ic, &rvp->ratectl_task); +} + +static void +rum_ratectl_task(void *arg, int pending) +{ + struct rum_vap *rvp = arg; + struct ieee80211vap *vap = &rvp->vap; + struct rum_softc *sc = vap->iv_ic->ic_softc; + struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs; + int ok[3], fail; + + RUM_LOCK(sc); + /* read and clear statistic registers (STA_CSR0 to STA_CSR5) */ + rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof(sc->sta)); + + ok[0] = (le32toh(sc->sta[4]) & 0xffff); /* TX ok w/o retry */ + ok[1] = (le32toh(sc->sta[4]) >> 16); /* TX ok w/ one retry */ + ok[2] = (le32toh(sc->sta[5]) & 0xffff); /* TX ok w/ multiple retries */ + fail = (le32toh(sc->sta[5]) >> 16); /* TX retry-fail count */ + + txs->flags = IEEE80211_RATECTL_TX_STATS_RETRIES; + txs->nframes = ok[0] + ok[1] + ok[2] + fail; + txs->nsuccess = txs->nframes - fail; + /* XXX at least */ + txs->nretries = ok[1] + ok[2] * 2 + fail * (rvp->maxretry + 1); + + if (txs->nframes != 0) + ieee80211_ratectl_tx_update(vap, txs); + + /* count TX retry-fail as Tx errors */ + if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, fail); + + usb_callout_reset(&rvp->ratectl_ch, hz, rum_ratectl_timeout, rvp); + RUM_UNLOCK(sc); +} + +static void +rum_scan_start(struct ieee80211com *ic) +{ + struct rum_softc *sc = ic->ic_softc; + + RUM_LOCK(sc); + rum_abort_tsf_sync(sc); + rum_set_bssid(sc, ieee80211broadcastaddr); + RUM_UNLOCK(sc); + +} + +static void +rum_scan_end(struct ieee80211com *ic) +{ + struct rum_softc *sc = ic->ic_softc; + + if (ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) { + RUM_LOCK(sc); + if (ic->ic_opmode != IEEE80211_M_AHDEMO) + rum_enable_tsf_sync(sc); + else + rum_enable_tsf(sc); + rum_set_bssid(sc, sc->sc_bssid); + RUM_UNLOCK(sc); + } +} + +static void +rum_set_channel(struct ieee80211com *ic) +{ + struct rum_softc *sc = ic->ic_softc; + + RUM_LOCK(sc); + rum_set_chan(sc, ic->ic_curchan); + RUM_UNLOCK(sc); +} + +static void +rum_getradiocaps(struct ieee80211com *ic, + int maxchans, int *nchans, struct ieee80211_channel chans[]) +{ + struct rum_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); + ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, bands, 0); + + if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_5226) { + setbit(bands, IEEE80211_MODE_11A); + ieee80211_add_channel_list_5ghz(chans, maxchans, nchans, + rum_chan_5ghz, nitems(rum_chan_5ghz), bands, 0); + } +} + +static int +rum_get_rssi(struct rum_softc *sc, uint8_t raw) +{ + struct ieee80211com *ic = &sc->sc_ic; + int lna, agc, rssi; + + lna = (raw >> 5) & 0x3; + agc = raw & 0x1f; + + if (lna == 0) { + /* + * No RSSI mapping + * + * NB: Since RSSI is relative to noise floor, -1 is + * adequate for caller to know error happened. + */ + return -1; + } + + rssi = (2 * agc) - RT2573_NOISE_FLOOR; + + if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { + rssi += sc->rssi_2ghz_corr; + + if (lna == 1) + rssi -= 64; + else if (lna == 2) + rssi -= 74; + else if (lna == 3) + rssi -= 90; + } else { + rssi += sc->rssi_5ghz_corr; + + if (!sc->ext_5ghz_lna && lna != 1) + rssi += 4; + + if (lna == 1) + rssi -= 64; + else if (lna == 2) + rssi -= 86; + else if (lna == 3) + rssi -= 100; + } + return rssi; +} + +static int +rum_pause(struct rum_softc *sc, int timeout) +{ + + usb_pause_mtx(&sc->sc_mtx, timeout); + return (0); +} + +static device_method_t rum_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rum_match), + DEVMETHOD(device_attach, rum_attach), + DEVMETHOD(device_detach, rum_detach), + DEVMETHOD_END +}; + +static driver_t rum_driver = { + .name = "rum", + .methods = rum_methods, + .size = sizeof(struct rum_softc), +}; + +static devclass_t rum_devclass; + +DRIVER_MODULE(rum, uhub, rum_driver, rum_devclass, NULL, 0); +MODULE_DEPEND(rum, wlan, 1, 1, 1); +MODULE_DEPEND(rum, usb, 1, 1, 1); +MODULE_VERSION(rum, 1); +USB_PNP_HOST_INFO(rum_devs); diff --git a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_rumfw.h b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_rumfw.h new file mode 100644 index 0000000000..0f08674451 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_rumfw.h @@ -0,0 +1,213 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005-2006, Ralink Technology, Corp. + * Paul Lin + * + * 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. + */ + +/* + * This file contains the loadable 8051 microcode for the Ralink RT2573 + * chipset. + */ + +static const uint8_t rt2573_ucode[] = { + 0x02, 0x13, 0x25, 0x12, 0x10, 0xd9, 0x02, 0x12, 0x58, 0x02, 0x13, + 0x58, 0x02, 0x13, 0x5a, 0xc0, 0xd0, 0x75, 0xd0, 0x18, 0x12, 0x13, + 0x5c, 0xd0, 0xd0, 0x22, 0x02, 0x14, 0x5c, 0x02, 0x14, 0xe7, 0xed, + 0x4c, 0x70, 0x44, 0x90, 0x01, 0xa8, 0x74, 0x80, 0xf0, 0xef, 0x30, + 0xe5, 0x07, 0xe4, 0x90, 0x00, 0x0f, 0xf0, 0x80, 0x2c, 0xe5, 0x40, + 0x24, 0xc0, 0x60, 0x13, 0x24, 0xc0, 0x60, 0x16, 0x24, 0xc0, 0x60, + 0x19, 0x24, 0xc0, 0x70, 0x1a, 0xe4, 0x90, 0x00, 0x0b, 0xf0, 0x80, + 0x13, 0xe4, 0x90, 0x00, 0x13, 0xf0, 0x80, 0x0c, 0xe4, 0x90, 0x00, + 0x1b, 0xf0, 0x80, 0x05, 0xe4, 0x90, 0x00, 0x23, 0xf0, 0xe4, 0x90, + 0x01, 0xa8, 0xf0, 0xd3, 0x22, 0x90, 0x02, 0x02, 0xed, 0xf0, 0x90, + 0x02, 0x01, 0xef, 0xf0, 0xd3, 0x22, 0xef, 0x24, 0xc0, 0x60, 0x1f, + 0x24, 0xc0, 0x60, 0x2e, 0x24, 0xc0, 0x60, 0x3d, 0x24, 0xc0, 0x70, + 0x53, 0x90, 0x00, 0x0b, 0xe0, 0x30, 0xe1, 0x02, 0xc3, 0x22, 0x90, + 0x00, 0x09, 0xe0, 0xfe, 0x90, 0x00, 0x08, 0x80, 0x37, 0x90, 0x00, + 0x13, 0xe0, 0x30, 0xe1, 0x02, 0xc3, 0x22, 0x90, 0x00, 0x11, 0xe0, + 0xfe, 0x90, 0x00, 0x10, 0x80, 0x24, 0x90, 0x00, 0x1b, 0xe0, 0x30, + 0xe1, 0x02, 0xc3, 0x22, 0x90, 0x00, 0x19, 0xe0, 0xfe, 0x90, 0x00, + 0x18, 0x80, 0x11, 0x90, 0x00, 0x23, 0xe0, 0x30, 0xe1, 0x02, 0xc3, + 0x22, 0x90, 0x00, 0x21, 0xe0, 0xfe, 0x90, 0x00, 0x20, 0xe0, 0xfd, + 0xee, 0xf5, 0x37, 0xed, 0xf5, 0x38, 0xd3, 0x22, 0x30, 0x09, 0x20, + 0x20, 0x04, 0x0b, 0x90, 0x02, 0x08, 0xe0, 0x54, 0x0f, 0x70, 0x03, + 0x02, 0x12, 0x57, 0xc2, 0x09, 0x90, 0x02, 0x00, 0xe0, 0x44, 0x04, + 0xf0, 0x74, 0x04, 0x12, 0x0c, 0x3a, 0xc2, 0x04, 0xc2, 0x07, 0x90, + 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80, 0xf6, 0x90, 0x03, + 0x26, 0xe0, 0x20, 0xe2, 0x03, 0x02, 0x12, 0x57, 0x90, 0x02, 0x08, + 0xe0, 0x70, 0x1b, 0x20, 0x07, 0x03, 0x02, 0x12, 0x57, 0x90, 0x03, + 0x12, 0xe0, 0x64, 0x22, 0x60, 0x03, 0x02, 0x12, 0x57, 0xd2, 0x09, + 0xc2, 0x07, 0x74, 0x02, 0x12, 0x0c, 0x3a, 0x22, 0x90, 0x02, 0x03, + 0xe0, 0x30, 0xe4, 0x47, 0x20, 0x06, 0x44, 0xe5, 0x3c, 0x60, 0x34, + 0xe5, 0x40, 0x24, 0xc0, 0x60, 0x14, 0x24, 0xc0, 0x60, 0x18, 0x24, + 0xc0, 0x60, 0x1c, 0x24, 0xc0, 0x70, 0x22, 0x90, 0x00, 0x0b, 0xe0, + 0x30, 0xe1, 0x1b, 0x22, 0x90, 0x00, 0x13, 0xe0, 0x30, 0xe1, 0x13, + 0x22, 0x90, 0x00, 0x1b, 0xe0, 0x30, 0xe1, 0x0b, 0x22, 0x90, 0x00, + 0x23, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x12, 0x57, 0x90, 0x02, 0x03, + 0x74, 0x01, 0xf0, 0x00, 0xe0, 0x54, 0xc0, 0xf5, 0x40, 0xe5, 0x40, + 0x24, 0xc0, 0x60, 0x20, 0x24, 0xc0, 0x60, 0x30, 0x24, 0xc0, 0x60, + 0x40, 0x24, 0xc0, 0x70, 0x56, 0x90, 0x00, 0x0b, 0xe0, 0x30, 0xe1, + 0x03, 0x02, 0x12, 0x57, 0x90, 0x00, 0x09, 0xe0, 0xfe, 0x90, 0x00, + 0x08, 0x80, 0x3a, 0x90, 0x00, 0x13, 0xe0, 0x30, 0xe1, 0x03, 0x02, + 0x12, 0x57, 0x90, 0x00, 0x11, 0xe0, 0xfe, 0x90, 0x00, 0x10, 0x80, + 0x26, 0x90, 0x00, 0x1b, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x12, 0x57, + 0x90, 0x00, 0x19, 0xe0, 0xfe, 0x90, 0x00, 0x18, 0x80, 0x12, 0x90, + 0x00, 0x23, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x12, 0x57, 0x90, 0x00, + 0x21, 0xe0, 0xfe, 0x90, 0x00, 0x20, 0xe0, 0xfd, 0xee, 0xf5, 0x37, + 0xed, 0xf5, 0x38, 0x90, 0x03, 0x27, 0x74, 0x82, 0xf0, 0x90, 0x02, + 0x01, 0xe5, 0x40, 0xf0, 0x90, 0x02, 0x06, 0xe0, 0xf5, 0x3c, 0xc3, + 0xe5, 0x38, 0x95, 0x3a, 0xe5, 0x37, 0x95, 0x39, 0x50, 0x21, 0xe5, + 0x40, 0x44, 0x05, 0xff, 0xe5, 0x37, 0xa2, 0xe7, 0x13, 0xfc, 0xe5, + 0x38, 0x13, 0xfd, 0x12, 0x10, 0x20, 0xe5, 0x3c, 0x30, 0xe2, 0x04, + 0xd2, 0x06, 0x80, 0x02, 0xc2, 0x06, 0x53, 0x3c, 0x01, 0x22, 0x30, + 0x0b, 0x07, 0xe4, 0x90, 0x02, 0x02, 0xf0, 0x80, 0x06, 0x90, 0x02, + 0x02, 0x74, 0x20, 0xf0, 0xe5, 0x40, 0x44, 0x01, 0x90, 0x02, 0x01, + 0xf0, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80, 0xf6, + 0x90, 0x03, 0x27, 0x74, 0x02, 0xf0, 0xaf, 0x40, 0x12, 0x10, 0x74, + 0x40, 0xa5, 0x00, 0x80, 0xf6, 0x22, 0x90, 0x7f, 0xf8, 0xe0, 0xb4, + 0x02, 0x03, 0x12, 0x16, 0x38, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, + 0x03, 0x00, 0x80, 0xf6, 0x90, 0x03, 0x26, 0xe0, 0x20, 0xe1, 0x07, + 0xe5, 0x3b, 0x70, 0x03, 0x02, 0x13, 0x24, 0xe5, 0x3b, 0x70, 0x15, + 0x90, 0x03, 0x24, 0xe0, 0x75, 0xf0, 0x40, 0xa4, 0xf5, 0x36, 0x85, + 0xf0, 0x35, 0x75, 0x24, 0x83, 0x75, 0x3b, 0x01, 0x80, 0x03, 0x75, + 0x24, 0x03, 0xd3, 0xe5, 0x36, 0x95, 0x3a, 0xe5, 0x35, 0x95, 0x39, + 0x40, 0x36, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80, + 0xf6, 0x90, 0x03, 0x27, 0xe5, 0x24, 0xf0, 0x90, 0x00, 0x0f, 0xe0, + 0x30, 0xe1, 0x04, 0x30, 0x0e, 0xf6, 0x22, 0x30, 0x0b, 0x07, 0xe4, + 0x90, 0x02, 0x02, 0xf0, 0x80, 0x06, 0x90, 0x02, 0x02, 0x74, 0x20, + 0xf0, 0x90, 0x02, 0x01, 0x74, 0x21, 0xf0, 0x75, 0x24, 0x03, 0x80, + 0x3d, 0xe5, 0x35, 0xa2, 0xe7, 0x13, 0xfe, 0xe5, 0x36, 0x13, 0xfd, + 0xac, 0x06, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80, + 0xf6, 0x90, 0x03, 0x27, 0xe5, 0x24, 0xf0, 0x90, 0x00, 0x0f, 0xe0, + 0x30, 0xe1, 0x04, 0x30, 0x0e, 0xf6, 0x22, 0x7f, 0x25, 0x12, 0x10, + 0x20, 0xe5, 0x36, 0xb5, 0x3a, 0x08, 0xe5, 0x35, 0xb5, 0x39, 0x03, + 0x00, 0x80, 0x04, 0xe4, 0xf5, 0x3b, 0x22, 0xc3, 0xe5, 0x36, 0x95, + 0x3a, 0xf5, 0x36, 0xe5, 0x35, 0x95, 0x39, 0xf5, 0x35, 0x02, 0x12, + 0x96, 0x22, 0x75, 0xa8, 0x0f, 0x90, 0x03, 0x06, 0x74, 0x01, 0xf0, + 0x90, 0x03, 0x07, 0xf0, 0x90, 0x03, 0x08, 0x04, 0xf0, 0x90, 0x03, + 0x09, 0x74, 0x6c, 0xf0, 0x90, 0x03, 0x0a, 0x74, 0xff, 0xf0, 0x90, + 0x03, 0x02, 0x74, 0x1f, 0xf0, 0x90, 0x03, 0x00, 0x74, 0x04, 0xf0, + 0x90, 0x03, 0x25, 0x74, 0x31, 0xf0, 0xd2, 0xaf, 0x22, 0x00, 0x22, + 0x00, 0x22, 0x90, 0x03, 0x05, 0xe0, 0x30, 0xe0, 0x0b, 0xe0, 0x44, + 0x01, 0xf0, 0x30, 0x09, 0x02, 0xd2, 0x04, 0xc2, 0x07, 0x22, 0x8d, + 0x24, 0xa9, 0x07, 0x90, 0x7f, 0xfc, 0xe0, 0x75, 0x25, 0x00, 0xf5, + 0x26, 0xa3, 0xe0, 0x75, 0x27, 0x00, 0xf5, 0x28, 0xa3, 0xe0, 0xff, + 0xa3, 0xe0, 0xfd, 0xe9, 0x30, 0xe5, 0x14, 0x54, 0xc0, 0x60, 0x05, + 0x43, 0x05, 0x03, 0x80, 0x03, 0x53, 0x05, 0xfc, 0xef, 0x54, 0x3f, + 0x44, 0x40, 0xff, 0x80, 0x06, 0x53, 0x07, 0x3f, 0x53, 0x05, 0xf0, + 0xe5, 0x24, 0x30, 0xe0, 0x05, 0x43, 0x05, 0x10, 0x80, 0x03, 0x53, + 0x05, 0xef, 0x90, 0x7f, 0xfc, 0xe5, 0x26, 0xf0, 0xa3, 0xe5, 0x28, + 0xf0, 0xa3, 0xef, 0xf0, 0xa3, 0xed, 0xf0, 0x22, 0x8f, 0x24, 0xa9, + 0x05, 0x90, 0x7f, 0xfc, 0xe0, 0x75, 0x25, 0x00, 0xf5, 0x26, 0xa3, + 0xe0, 0x75, 0x27, 0x00, 0xf5, 0x28, 0xa3, 0xe0, 0xff, 0xa3, 0xe0, + 0xfd, 0xe5, 0x24, 0x30, 0xe5, 0x0b, 0x43, 0x05, 0x0f, 0xef, 0x54, + 0x3f, 0x44, 0x40, 0xff, 0x80, 0x06, 0x53, 0x05, 0xf0, 0x53, 0x07, + 0x3f, 0xe9, 0x30, 0xe0, 0x05, 0x43, 0x05, 0x10, 0x80, 0x03, 0x53, + 0x05, 0xef, 0x90, 0x7f, 0xfc, 0xe5, 0x26, 0xf0, 0xa3, 0xe5, 0x28, + 0xf0, 0xa3, 0xef, 0xf0, 0xa3, 0xed, 0xf0, 0x22, 0x90, 0x7f, 0xfc, + 0xe0, 0xf9, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0xfb, + 0xef, 0x30, 0xe5, 0x0b, 0x43, 0x03, 0x0f, 0xec, 0x54, 0x3f, 0x44, + 0x40, 0xfc, 0x80, 0x06, 0x53, 0x03, 0xf0, 0x53, 0x04, 0x3f, 0xed, + 0x30, 0xe0, 0x07, 0xef, 0x54, 0xc0, 0x60, 0x07, 0x80, 0x0a, 0xef, + 0x54, 0xc0, 0x60, 0x05, 0x43, 0x03, 0x10, 0x80, 0x03, 0x53, 0x03, + 0xef, 0x90, 0x7f, 0xfc, 0xe9, 0xf0, 0xa3, 0xee, 0xf0, 0xa3, 0xec, + 0xf0, 0xa3, 0xeb, 0xf0, 0x22, 0xe5, 0x4b, 0xfd, 0x54, 0x1f, 0x90, + 0x7f, 0xf8, 0xf0, 0xe5, 0x4a, 0xf5, 0x09, 0x90, 0x30, 0x38, 0xe0, + 0x90, 0x7f, 0xfc, 0xf0, 0x90, 0x30, 0x39, 0xe0, 0x90, 0x7f, 0xfd, + 0xf0, 0x90, 0x30, 0x3a, 0xe0, 0x90, 0x7f, 0xfe, 0xf0, 0x90, 0x30, + 0x3b, 0xe0, 0x90, 0x7f, 0xff, 0xf0, 0xed, 0x30, 0xe5, 0x0c, 0x54, + 0xc0, 0x60, 0x0d, 0x90, 0x7f, 0xf0, 0xe5, 0x47, 0xf0, 0x80, 0x05, + 0xe4, 0x90, 0x7f, 0xf0, 0xf0, 0x90, 0x7f, 0xf8, 0xe0, 0x14, 0x60, + 0x08, 0x24, 0xfe, 0x60, 0x0d, 0x24, 0x03, 0x80, 0x12, 0xaf, 0x05, + 0xad, 0x09, 0x12, 0x13, 0xc5, 0x80, 0x10, 0xaf, 0x05, 0xad, 0x09, + 0x12, 0x14, 0x12, 0x80, 0x07, 0xaf, 0x05, 0xad, 0x09, 0x12, 0x13, + 0x6f, 0x90, 0x7f, 0xfc, 0xe0, 0x90, 0x30, 0x38, 0xf0, 0x90, 0x7f, + 0xfd, 0xe0, 0x90, 0x30, 0x39, 0xf0, 0x90, 0x7f, 0xfe, 0xe0, 0x90, + 0x30, 0x3a, 0xf0, 0x90, 0x7f, 0xff, 0xe0, 0x90, 0x30, 0x3b, 0xf0, + 0x22, 0xe5, 0x4b, 0x64, 0x01, 0x60, 0x03, 0x02, 0x15, 0x71, 0xf5, + 0x4b, 0xe5, 0x44, 0x45, 0x43, 0x70, 0x03, 0x02, 0x15, 0xa0, 0x12, + 0x0c, 0x14, 0x12, 0x0b, 0x86, 0x50, 0xfb, 0x90, 0x00, 0x00, 0xe0, + 0xf5, 0x25, 0x12, 0x15, 0xb4, 0xc2, 0x92, 0xe4, 0xf5, 0x24, 0xe5, + 0x24, 0xc3, 0x95, 0x25, 0x50, 0x49, 0x7e, 0x00, 0x7f, 0x4c, 0x74, + 0x40, 0x25, 0x24, 0xf5, 0x82, 0xe4, 0x34, 0x01, 0xad, 0x82, 0xfc, + 0x75, 0x2b, 0x02, 0x7b, 0x10, 0x12, 0x07, 0x1e, 0xc2, 0x93, 0x12, + 0x15, 0xa1, 0x7d, 0xa0, 0x12, 0x15, 0xd0, 0xe5, 0x24, 0x54, 0x0f, + 0x24, 0x4c, 0xf8, 0xe6, 0xfd, 0xaf, 0x4b, 0xae, 0x4a, 0x12, 0x15, + 0xd8, 0x05, 0x4b, 0xe5, 0x4b, 0x70, 0x02, 0x05, 0x4a, 0x12, 0x0a, + 0x5f, 0x05, 0x24, 0xe5, 0x24, 0x54, 0x0f, 0x70, 0xd5, 0xd2, 0x93, + 0x80, 0xb0, 0xc3, 0xe5, 0x44, 0x95, 0x25, 0xf5, 0x44, 0xe5, 0x43, + 0x94, 0x00, 0xf5, 0x43, 0x02, 0x14, 0xf2, 0x12, 0x15, 0xb4, 0xc2, + 0x93, 0xc2, 0x92, 0x12, 0x15, 0xa1, 0x7d, 0x80, 0x12, 0x15, 0xd0, + 0x7d, 0xaa, 0x74, 0x55, 0xff, 0xfe, 0x12, 0x15, 0xd8, 0x7d, 0x55, + 0x7f, 0xaa, 0x7e, 0x2a, 0x12, 0x15, 0xd8, 0x7d, 0x30, 0xaf, 0x4b, + 0xae, 0x4a, 0x12, 0x15, 0xd8, 0x12, 0x0a, 0x5f, 0xd2, 0x93, 0x22, + 0x7d, 0xaa, 0x74, 0x55, 0xff, 0xfe, 0x12, 0x15, 0xd8, 0x7d, 0x55, + 0x7f, 0xaa, 0x7e, 0x2a, 0x12, 0x15, 0xd8, 0x22, 0xad, 0x47, 0x7f, + 0x34, 0x7e, 0x30, 0x12, 0x15, 0xd8, 0x7d, 0xff, 0x7f, 0x35, 0x7e, + 0x30, 0x12, 0x15, 0xd8, 0xe4, 0xfd, 0x7f, 0x37, 0x7e, 0x30, 0x12, + 0x15, 0xd8, 0x22, 0x74, 0x55, 0xff, 0xfe, 0x12, 0x15, 0xd8, 0x22, + 0x8f, 0x82, 0x8e, 0x83, 0xed, 0xf0, 0x22, 0xe4, 0xfc, 0x90, 0x7f, + 0xf0, 0xe0, 0xaf, 0x09, 0x14, 0x60, 0x14, 0x14, 0x60, 0x15, 0x14, + 0x60, 0x16, 0x14, 0x60, 0x17, 0x14, 0x60, 0x18, 0x24, 0x05, 0x70, + 0x16, 0xe4, 0xfc, 0x80, 0x12, 0x7c, 0x01, 0x80, 0x0e, 0x7c, 0x03, + 0x80, 0x0a, 0x7c, 0x07, 0x80, 0x06, 0x7c, 0x0f, 0x80, 0x02, 0x7c, + 0x1f, 0xec, 0x6f, 0xf4, 0x54, 0x1f, 0xfc, 0x90, 0x30, 0x34, 0xe0, + 0x54, 0xe0, 0x4c, 0xfd, 0xa3, 0xe0, 0xfc, 0x43, 0x04, 0x1f, 0x7f, + 0x34, 0x7e, 0x30, 0x12, 0x15, 0xd8, 0xad, 0x04, 0x0f, 0x12, 0x15, + 0xd8, 0xe4, 0xfd, 0x7f, 0x37, 0x02, 0x15, 0xd8, 0x02, 0x15, 0xdf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, + 0x29, 0xe9 +}; diff --git a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_rumreg.h b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_rumreg.h new file mode 100644 index 0000000000..70354c4928 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_rumreg.h @@ -0,0 +1,306 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005, 2006 Damien Bergamini + * Copyright (c) 2006 Niall O'Higgins + * + * 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 RT2573_NOISE_FLOOR -95 + +#define RT2573_TX_DESC_SIZE (sizeof (struct rum_tx_desc)) +#define RT2573_RX_DESC_SIZE (sizeof (struct rum_rx_desc)) + +#define RT2573_CONFIG_NO 1 +#define RT2573_IFACE_INDEX 0 + +#define RT2573_MCU_CNTL 0x01 +#define RT2573_WRITE_MAC 0x02 +#define RT2573_READ_MAC 0x03 +#define RT2573_WRITE_MULTI_MAC 0x06 +#define RT2573_READ_MULTI_MAC 0x07 +#define RT2573_READ_EEPROM 0x09 +#define RT2573_WRITE_LED 0x0a + +/* + * WME registers. + */ +#define RT2573_AIFSN_CSR 0x0400 +#define RT2573_CWMIN_CSR 0x0404 +#define RT2573_CWMAX_CSR 0x0408 +#define RT2573_TXOP01_CSR 0x040C +#define RT2573_TXOP23_CSR 0x0410 +#define RT2573_MCU_CODE_BASE 0x0800 + +/* + * H/w encryption/decryption support + */ +#define KEY_SIZE (IEEE80211_KEYBUF_SIZE + IEEE80211_MICBUF_SIZE) +#define RT2573_ADDR_MAX 64 +#define RT2573_SKEY_MAX 4 + +#define RT2573_SKEY(vap, kidx) (0x1000 + ((vap) * RT2573_SKEY_MAX + \ + (kidx)) * KEY_SIZE) +#define RT2573_PKEY(id) (0x1200 + (id) * KEY_SIZE) + +#define RT2573_ADDR_ENTRY(id) (0x1a00 + (id) * 8) + +/* + * Shared memory area + */ +#define RT2573_HW_BCN_BASE(id) (0x2400 + (id) * 0x100) + +/* + * Control and status registers. + */ +#define RT2573_MAC_CSR0 0x3000 +#define RT2573_MAC_CSR1 0x3004 +#define RT2573_MAC_CSR2 0x3008 +#define RT2573_MAC_CSR3 0x300c +#define RT2573_MAC_CSR4 0x3010 +#define RT2573_MAC_CSR5 0x3014 +#define RT2573_MAC_CSR6 0x3018 +#define RT2573_MAC_CSR7 0x301c +#define RT2573_MAC_CSR8 0x3020 +#define RT2573_MAC_CSR9 0x3024 +#define RT2573_MAC_CSR10 0x3028 +#define RT2573_MAC_CSR11 0x302c +#define RT2573_MAC_CSR12 0x3030 +#define RT2573_MAC_CSR13 0x3034 +#define RT2573_MAC_CSR14 0x3038 +#define RT2573_MAC_CSR15 0x303c +#define RT2573_TXRX_CSR0 0x3040 +#define RT2573_TXRX_CSR1 0x3044 +#define RT2573_TXRX_CSR2 0x3048 +#define RT2573_TXRX_CSR3 0x304c +#define RT2573_TXRX_CSR4 0x3050 +#define RT2573_TXRX_CSR5 0x3054 +#define RT2573_TXRX_CSR6 0x3058 +#define RT2573_TXRX_CSR7 0x305c +#define RT2573_TXRX_CSR8 0x3060 +#define RT2573_TXRX_CSR9 0x3064 +#define RT2573_TXRX_CSR10 0x3068 +#define RT2573_TXRX_CSR11 0x306c +#define RT2573_TXRX_CSR12 0x3070 +#define RT2573_TXRX_CSR13 0x3074 +#define RT2573_TXRX_CSR14 0x3078 +#define RT2573_TXRX_CSR15 0x307c +#define RT2573_PHY_CSR0 0x3080 +#define RT2573_PHY_CSR1 0x3084 +#define RT2573_PHY_CSR2 0x3088 +#define RT2573_PHY_CSR3 0x308c +#define RT2573_PHY_CSR4 0x3090 +#define RT2573_PHY_CSR5 0x3094 +#define RT2573_PHY_CSR6 0x3098 +#define RT2573_PHY_CSR7 0x309c +#define RT2573_SEC_CSR0 0x30a0 +#define RT2573_SEC_CSR1 0x30a4 +#define RT2573_SEC_CSR2 0x30a8 +#define RT2573_SEC_CSR3 0x30ac +#define RT2573_SEC_CSR4 0x30b0 +#define RT2573_SEC_CSR5 0x30b4 +#define RT2573_STA_CSR0 0x30c0 +#define RT2573_STA_CSR1 0x30c4 +#define RT2573_STA_CSR2 0x30c8 +#define RT2573_STA_CSR3 0x30cc +#define RT2573_STA_CSR4 0x30d0 +#define RT2573_STA_CSR5 0x30d4 + +/* possible values for register RT2573_ADDR_MODE */ +#define RT2573_MODE_MASK 0x7 +#define RT2573_MODE_NOSEC 0 +#define RT2573_MODE_WEP40 1 +#define RT2573_MODE_WEP104 2 +#define RT2573_MODE_TKIP 3 +#define RT2573_MODE_AES_CCMP 4 +#define RT2573_MODE_CKIP40 5 +#define RT2573_MODE_CKIP104 6 + +/* possible flags for register RT2573_MAC_CSR1 */ +#define RT2573_RESET_ASIC (1 << 0) +#define RT2573_RESET_BBP (1 << 1) +#define RT2573_HOST_READY (1 << 2) + +/* possible flags for register MAC_CSR5 */ +#define RT2573_NUM_BSSID_MSK(n) (((n * 3) & 3) << 16) + +/* possible flags for register MAC_CSR11 */ +#define RT2573_AUTO_WAKEUP (1 << 15) +#define RT2573_TBCN_EXP(n) ((n) << 8) +#define RT2573_TBCN_EXP_MAX 0x7f +#define RT2573_TBCN_DELAY(t) (t) +#define RT2573_TBCN_DELAY_MAX 0xff + +/* possible flags for register TXRX_CSR0 */ +/* Tx filter flags are in the low 16 bits */ +#define RT2573_AUTO_TX_SEQ (1 << 15) +/* Rx filter flags are in the high 16 bits */ +#define RT2573_DISABLE_RX (1 << 16) +#define RT2573_DROP_CRC_ERROR (1 << 17) +#define RT2573_DROP_PHY_ERROR (1 << 18) +#define RT2573_DROP_CTL (1 << 19) +#define RT2573_DROP_NOT_TO_ME (1 << 20) +#define RT2573_DROP_TODS (1 << 21) +#define RT2573_DROP_VER_ERROR (1 << 22) +#define RT2573_DROP_MULTICAST (1 << 23) +#define RT2573_DROP_BROADCAST (1 << 24) +#define RT2573_DROP_ACKCTS (1 << 25) + +/* possible flags for register TXRX_CSR4 */ +#define RT2573_ACKCTS_PWRMGT (1 << 16) +#define RT2573_SHORT_PREAMBLE (1 << 18) +#define RT2573_MRR_ENABLED (1 << 19) +#define RT2573_MRR_CCK_FALLBACK (1 << 22) +#define RT2573_LONG_RETRY(max) ((max) << 24) +#define RT2573_LONG_RETRY_MASK (0xf << 24) +#define RT2573_SHORT_RETRY(max) ((max) << 28) +#define RT2573_SHORT_RETRY_MASK (0xf << 28) + +/* possible flags for register TXRX_CSR9 */ +#define RT2573_TSF_TIMER_EN (1 << 16) +#define RT2573_TSF_SYNC_MODE(x) (((x) & 0x3) << 17) +#define RT2573_TSF_SYNC_MODE_DIS 0 +#define RT2573_TSF_SYNC_MODE_STA 1 +#define RT2573_TSF_SYNC_MODE_IBSS 2 +#define RT2573_TSF_SYNC_MODE_HOSTAP 3 +#define RT2573_TBTT_TIMER_EN (1 << 19) +#define RT2573_BCN_TX_EN (1 << 20) + +/* possible flags for register PHY_CSR0 */ +#define RT2573_PA_PE_2GHZ (1 << 16) +#define RT2573_PA_PE_5GHZ (1 << 17) + +/* possible flags for register PHY_CSR3 */ +#define RT2573_BBP_READ (1 << 15) +#define RT2573_BBP_BUSY (1 << 16) +/* possible flags for register PHY_CSR4 */ +#define RT2573_RF_20BIT (20 << 24) +#define RT2573_RF_BUSY (1U << 31) + +/* LED values */ +#define RT2573_LED_RADIO (1 << 8) +#define RT2573_LED_G (1 << 9) +#define RT2573_LED_A (1 << 10) +#define RT2573_LED_ON 0x1e1e +#define RT2573_LED_OFF 0x0 + +/* USB vendor requests */ +#define RT2573_MCU_SLEEP 7 +#define RT2573_MCU_RUN 8 +#define RT2573_MCU_WAKEUP 9 + +#define RT2573_SMART_MODE (1 << 0) + +#define RT2573_BBPR94_DEFAULT 6 + +#define RT2573_BBP_WRITE (1 << 15) + +/* dual-band RF */ +#define RT2573_RF_5226 1 +#define RT2573_RF_5225 3 +/* single-band RF */ +#define RT2573_RF_2528 2 +#define RT2573_RF_2527 4 + +#define RT2573_BBP_VERSION 0 + +struct rum_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 rum_rx_desc { + uint32_t flags; +#define RT2573_RX_BUSY (1 << 0) +#define RT2573_RX_DROP (1 << 1) +#define RT2573_RX_UC2ME (1 << 2) +#define RT2573_RX_MC (1 << 3) +#define RT2573_RX_BC (1 << 4) +#define RT2573_RX_MYBSS (1 << 5) +#define RT2573_RX_CRC_ERROR (1 << 6) +#define RT2573_RX_OFDM (1 << 7) + +#define RT2573_RX_DEC_MASK (3 << 8) +#define RT2573_RX_DEC_OK (0 << 8) + +#define RT2573_RX_IV_ERROR (1 << 8) +#define RT2573_RX_MIC_ERROR (2 << 8) +#define RT2573_RX_KEY_ERROR (3 << 8) + +#define RT2573_RX_KEY_PAIR (1 << 28) + +#define RT2573_RX_CIP_MASK (7 << 29) +#define RT2573_RX_CIP_MODE(m) ((m) << 29) + + uint8_t rate; + uint8_t rssi; + uint8_t reserved1; + uint8_t offset; + uint32_t iv; + uint32_t eiv; + uint32_t reserved2[2]; +} __packed; + +#define RT2573_RF1 0 +#define RT2573_RF2 2 +#define RT2573_RF3 1 +#define RT2573_RF4 3 + +#define RT2573_EEPROM_MACBBP 0x0000 +#define RT2573_EEPROM_ADDRESS 0x0004 +#define RT2573_EEPROM_ANTENNA 0x0020 +#define RT2573_EEPROM_CONFIG2 0x0022 +#define RT2573_EEPROM_BBP_BASE 0x0026 +#define RT2573_EEPROM_TXPOWER 0x0046 +#define RT2573_EEPROM_FREQ_OFFSET 0x005e +#define RT2573_EEPROM_RSSI_2GHZ_OFFSET 0x009a +#define RT2573_EEPROM_RSSI_5GHZ_OFFSET 0x009c diff --git a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_rumvar.h b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_rumvar.h new file mode 100644 index 0000000000..e19a7088cf --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_rumvar.h @@ -0,0 +1,186 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005, 2006 Damien Bergamini + * Copyright (c) 2006 Niall O'Higgins + * + * 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 RUM_TX_LIST_COUNT 8 +#define RUM_TX_MINFREE 2 + +struct rum_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; + int8_t wr_antsignal; + int8_t wr_antnoise; + uint8_t wr_antenna; +} __packed __aligned(8); + +#define RT2573_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_TSFT) | \ + (1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA) | \ + 0) + +struct rum_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_antenna; +} __packed; + +#define RT2573_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA)) + +struct rum_softc; + +struct rum_tx_data { + STAILQ_ENTRY(rum_tx_data) next; + struct rum_softc *sc; + struct rum_tx_desc desc; + struct mbuf *m; + struct ieee80211_node *ni; + int rate; +}; +typedef STAILQ_HEAD(, rum_tx_data) rum_txdhead; + +union sec_param { + struct ieee80211_key key; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + struct ieee80211vap *vap; +}; +#define CMD_FUNC_PROTO void (*func)(struct rum_softc *, \ + union sec_param *, uint8_t) + +struct rum_cmdq { + union sec_param data; + uint8_t rvp_id; + + CMD_FUNC_PROTO; +}; +#define RUM_CMDQ_SIZE 16 + +struct rum_vap { + struct ieee80211vap vap; + struct mbuf *bcn_mbuf; + struct usb_callout ratectl_ch; + struct task ratectl_task; + uint8_t maxretry; + + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); + void (*bmiss)(struct ieee80211vap *); + void (*recv_mgmt)(struct ieee80211_node *, + struct mbuf *, int, + const struct ieee80211_rx_stats *, + int, int); +}; +#define RUM_VAP(vap) ((struct rum_vap *)(vap)) + +enum { + RUM_BULK_WR, + RUM_BULK_RD, + RUM_N_TRANSFER = 2, +}; + +struct rum_softc { + struct ieee80211com sc_ic; + struct ieee80211_ratectl_tx_stats sc_txs; + struct mbufq sc_snd; + device_t sc_dev; + struct usb_device *sc_udev; + + struct usb_xfer *sc_xfer[RUM_N_TRANSFER]; + + uint8_t rf_rev; + uint8_t rffreq; + + struct rum_tx_data tx_data[RUM_TX_LIST_COUNT]; + rum_txdhead tx_q; + rum_txdhead tx_free; + int tx_nfree; + struct rum_rx_desc sc_rx_desc; + + struct mtx sc_mtx; + + int sc_sleep_end; + int sc_sleep_time; + uint8_t last_rx_flags; + + struct rum_cmdq cmdq[RUM_CMDQ_SIZE]; + struct mtx cmdq_mtx; + struct task cmdq_task; + uint8_t cmdq_first; + uint8_t cmdq_last; + + uint32_t sta[6]; + uint32_t rf_regs[4]; + uint8_t txpow[44]; + u_int sc_detached:1, + sc_running:1, + sc_sleeping:1, + sc_clr_shkeys:1; + + uint8_t sc_bssid[IEEE80211_ADDR_LEN]; + struct wmeParams wme_params[WME_NUM_AC]; + + uint8_t vap_key_count[1]; + uint64_t keys_bmap; + + struct { + uint8_t val; + uint8_t reg; + } __packed bbp_prom[16]; + + int hw_radio; + int rx_ant; + int tx_ant; + int nb_ant; + int ext_2ghz_lna; + int ext_5ghz_lna; + int rssi_2ghz_corr; + int rssi_5ghz_corr; + uint8_t bbp17; + + struct rum_rx_radiotap_header sc_rxtap; + struct rum_tx_radiotap_header sc_txtap; +}; + +#define RUM_LOCK_INIT(sc) \ + mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->sc_dev), \ + MTX_NETWORK_LOCK, MTX_DEF); +#define RUM_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define RUM_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define RUM_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) +#define RUM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx) + +#define RUM_CMDQ_LOCK_INIT(sc) \ + mtx_init(&(sc)->cmdq_mtx, "cmdq lock", NULL, MTX_DEF) +#define RUM_CMDQ_LOCK(sc) mtx_lock(&(sc)->cmdq_mtx) +#define RUM_CMDQ_UNLOCK(sc) mtx_unlock(&(sc)->cmdq_mtx) +#define RUM_CMDQ_LOCK_DESTROY(sc) mtx_destroy(&(sc)->cmdq_mtx) diff --git a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_run.c b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_run.c new file mode 100644 index 0000000000..c5f48afaa6 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_run.c @@ -0,0 +1,6448 @@ +/*- + * Copyright (c) 2008,2010 Damien Bergamini + * ported to FreeBSD by Akinori Furukoshi + * USB Consulting, Hans Petter Selasky + * Copyright (c) 2013-2014 Kevin Lo + * + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +/*- + * Ralink Technology RT2700U/RT2800U/RT3000U/RT3900E chipset driver. + * http://www.ralinktech.com/ + */ + +#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 +#ifdef IEEE80211_SUPPORT_SUPERG +#include +#endif + +#include +#include +#include "usbdevs.h" + +#define USB_DEBUG_VAR run_debug +#include +//#include + +#include +#include + +#ifdef USB_DEBUG +#define RUN_DEBUG +#endif + +#ifdef RUN_DEBUG +int run_debug = 0; +static SYSCTL_NODE(_hw_usb, OID_AUTO, run, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, + "USB run"); +SYSCTL_INT(_hw_usb_run, OID_AUTO, debug, CTLFLAG_RWTUN, &run_debug, 0, + "run debug level"); + +enum { + RUN_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ + RUN_DEBUG_XMIT_DESC = 0x00000002, /* xmit descriptors */ + RUN_DEBUG_RECV = 0x00000004, /* basic recv operation */ + RUN_DEBUG_RECV_DESC = 0x00000008, /* recv descriptors */ + RUN_DEBUG_STATE = 0x00000010, /* 802.11 state transitions */ + RUN_DEBUG_RATE = 0x00000020, /* rate adaptation */ + RUN_DEBUG_USB = 0x00000040, /* usb requests */ + RUN_DEBUG_FIRMWARE = 0x00000080, /* firmware(9) loading debug */ + RUN_DEBUG_BEACON = 0x00000100, /* beacon handling */ + RUN_DEBUG_INTR = 0x00000200, /* ISR */ + RUN_DEBUG_TEMP = 0x00000400, /* temperature calibration */ + RUN_DEBUG_ROM = 0x00000800, /* various ROM info */ + RUN_DEBUG_KEY = 0x00001000, /* crypto keys management */ + RUN_DEBUG_TXPWR = 0x00002000, /* dump Tx power values */ + RUN_DEBUG_RSSI = 0x00004000, /* dump RSSI lookups */ + RUN_DEBUG_RESET = 0x00008000, /* initialization progress */ + RUN_DEBUG_CALIB = 0x00010000, /* calibration progress */ + RUN_DEBUG_CMD = 0x00020000, /* command queue */ + RUN_DEBUG_ANY = 0xffffffff +}; + +#define RUN_DPRINTF(_sc, _m, ...) do { \ + if (run_debug & (_m)) \ + device_printf((_sc)->sc_dev, __VA_ARGS__); \ +} while(0) +#else +#define RUN_DPRINTF(_sc, _m, ...) do { (void) _sc; } while (0) +#endif + +#define IEEE80211_HAS_ADDR4(wh) IEEE80211_IS_DSTODS(wh) + +/* + * Because of LOR in run_key_delete(), use atomic instead. + * '& RUN_CMDQ_MASQ' is to loop cmdq[]. + */ +#define RUN_CMDQ_GET(c) (atomic_fetchadd_32((c), 1) & RUN_CMDQ_MASQ) + +static const STRUCT_USB_HOST_ID run_devs[] = { +#define RUN_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } +#define RUN_DEV_EJECT(v,p) \ + { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, RUN_EJECT) } +#define RUN_EJECT 1 + RUN_DEV(ABOCOM, RT2770), + RUN_DEV(ABOCOM, RT2870), + RUN_DEV(ABOCOM, RT3070), + RUN_DEV(ABOCOM, RT3071), + RUN_DEV(ABOCOM, RT3072), + RUN_DEV(ABOCOM2, RT2870_1), + RUN_DEV(ACCTON, RT2770), + RUN_DEV(ACCTON, RT2870_1), + RUN_DEV(ACCTON, RT2870_2), + RUN_DEV(ACCTON, RT2870_3), + RUN_DEV(ACCTON, RT2870_4), + RUN_DEV(ACCTON, RT2870_5), + RUN_DEV(ACCTON, RT3070), + RUN_DEV(ACCTON, RT3070_1), + RUN_DEV(ACCTON, RT3070_2), + RUN_DEV(ACCTON, RT3070_3), + RUN_DEV(ACCTON, RT3070_4), + RUN_DEV(ACCTON, RT3070_5), + RUN_DEV(AIRTIES, RT3070), + RUN_DEV(ALLWIN, RT2070), + RUN_DEV(ALLWIN, RT2770), + RUN_DEV(ALLWIN, RT2870), + RUN_DEV(ALLWIN, RT3070), + RUN_DEV(ALLWIN, RT3071), + RUN_DEV(ALLWIN, RT3072), + RUN_DEV(ALLWIN, RT3572), + RUN_DEV(AMIGO, RT2870_1), + RUN_DEV(AMIGO, RT2870_2), + RUN_DEV(AMIT, CGWLUSB2GNR), + RUN_DEV(AMIT, RT2870_1), + RUN_DEV(AMIT2, RT2870), + RUN_DEV(ASUS, RT2870_1), + RUN_DEV(ASUS, RT2870_2), + RUN_DEV(ASUS, RT2870_3), + RUN_DEV(ASUS, RT2870_4), + RUN_DEV(ASUS, RT2870_5), + RUN_DEV(ASUS, USBN13), + RUN_DEV(ASUS, RT3070_1), + RUN_DEV(ASUS, USBN66), + RUN_DEV(ASUS, USB_N53), + RUN_DEV(ASUS, USBN14), + RUN_DEV(ASUS2, USBN11), + RUN_DEV(AZUREWAVE, RT2870_1), + RUN_DEV(AZUREWAVE, RT2870_2), + RUN_DEV(AZUREWAVE, RT3070_1), + RUN_DEV(AZUREWAVE, RT3070_2), + RUN_DEV(AZUREWAVE, RT3070_3), + RUN_DEV(BELKIN, F9L1103), + RUN_DEV(BELKIN, F5D8053V3), + RUN_DEV(BELKIN, F5D8055), + RUN_DEV(BELKIN, F5D8055V2), + RUN_DEV(BELKIN, F6D4050V1), + RUN_DEV(BELKIN, F6D4050V2), + RUN_DEV(BELKIN, RT2870_1), + RUN_DEV(BELKIN, RT2870_2), + RUN_DEV(CISCOLINKSYS, AE1000), + RUN_DEV(CISCOLINKSYS2, RT3070), + RUN_DEV(CISCOLINKSYS3, RT3070), + RUN_DEV(CONCEPTRONIC2, RT2870_1), + RUN_DEV(CONCEPTRONIC2, RT2870_2), + RUN_DEV(CONCEPTRONIC2, RT2870_3), + RUN_DEV(CONCEPTRONIC2, RT2870_4), + RUN_DEV(CONCEPTRONIC2, RT2870_5), + RUN_DEV(CONCEPTRONIC2, RT2870_6), + RUN_DEV(CONCEPTRONIC2, RT2870_7), + RUN_DEV(CONCEPTRONIC2, RT2870_8), + RUN_DEV(CONCEPTRONIC2, RT3070_1), + RUN_DEV(CONCEPTRONIC2, RT3070_2), + RUN_DEV(CONCEPTRONIC2, VIGORN61), + RUN_DEV(COREGA, CGWLUSB300GNM), + RUN_DEV(COREGA, RT2870_1), + RUN_DEV(COREGA, RT2870_2), + RUN_DEV(COREGA, RT2870_3), + RUN_DEV(COREGA, RT3070), + RUN_DEV(CYBERTAN, RT2870), + RUN_DEV(DLINK, RT2870), + RUN_DEV(DLINK, RT3072), + RUN_DEV(DLINK, DWA125A3), + RUN_DEV(DLINK, DWA127), + RUN_DEV(DLINK, DWA140B3), + RUN_DEV(DLINK, DWA160B2), + RUN_DEV(DLINK, DWA140D1), + RUN_DEV(DLINK, DWA130F1), + RUN_DEV(DLINK, DWA162), + RUN_DEV(DLINK2, DWA130), + RUN_DEV(DLINK2, RT2870_1), + RUN_DEV(DLINK2, RT2870_2), + RUN_DEV(DLINK2, RT3070_1), + RUN_DEV(DLINK2, RT3070_2), + RUN_DEV(DLINK2, RT3070_3), + RUN_DEV(DLINK2, RT3070_4), + RUN_DEV(DLINK2, RT3070_5), + RUN_DEV(DLINK2, RT3072), + RUN_DEV(DLINK2, RT3072_1), + RUN_DEV(EDIMAX, EW7717), + RUN_DEV(EDIMAX, EW7718), + RUN_DEV(EDIMAX, EW7733UND), + RUN_DEV(EDIMAX, RT2870_1), + RUN_DEV(ENCORE, RT3070_1), + RUN_DEV(ENCORE, RT3070_2), + RUN_DEV(ENCORE, RT3070_3), + RUN_DEV(GIGABYTE, GNWB31N), + RUN_DEV(GIGABYTE, GNWB32L), + RUN_DEV(GIGABYTE, RT2870_1), + RUN_DEV(GIGASET, RT3070_1), + RUN_DEV(GIGASET, RT3070_2), + RUN_DEV(GUILLEMOT, HWNU300), + RUN_DEV(HAWKING, HWUN2), + RUN_DEV(HAWKING, RT2870_1), + RUN_DEV(HAWKING, RT2870_2), + RUN_DEV(HAWKING, RT3070), + RUN_DEV(IODATA, RT3072_1), + RUN_DEV(IODATA, RT3072_2), + RUN_DEV(IODATA, RT3072_3), + RUN_DEV(IODATA, RT3072_4), + RUN_DEV(LINKSYS4, RT3070), + RUN_DEV(LINKSYS4, WUSB100), + RUN_DEV(LINKSYS4, WUSB54GCV3), + RUN_DEV(LINKSYS4, WUSB600N), + RUN_DEV(LINKSYS4, WUSB600NV2), + RUN_DEV(LOGITEC, RT2870_1), + RUN_DEV(LOGITEC, RT2870_2), + RUN_DEV(LOGITEC, RT2870_3), + RUN_DEV(LOGITEC, LANW300NU2), + RUN_DEV(LOGITEC, LANW150NU2), + RUN_DEV(LOGITEC, LANW300NU2S), + RUN_DEV(MELCO, WLIUCG300HP), + RUN_DEV(MELCO, RT2870_2), + RUN_DEV(MELCO, WLIUCAG300N), + RUN_DEV(MELCO, WLIUCG300N), + RUN_DEV(MELCO, WLIUCG301N), + RUN_DEV(MELCO, WLIUCGN), + RUN_DEV(MELCO, WLIUCGNM), + RUN_DEV(MELCO, WLIUCG300HPV1), + RUN_DEV(MELCO, WLIUCGNM2), + RUN_DEV(MOTOROLA4, RT2770), + RUN_DEV(MOTOROLA4, RT3070), + RUN_DEV(MSI, RT3070_1), + RUN_DEV(MSI, RT3070_2), + RUN_DEV(MSI, RT3070_3), + RUN_DEV(MSI, RT3070_4), + RUN_DEV(MSI, RT3070_5), + RUN_DEV(MSI, RT3070_6), + RUN_DEV(MSI, RT3070_7), + RUN_DEV(MSI, RT3070_8), + RUN_DEV(MSI, RT3070_9), + RUN_DEV(MSI, RT3070_10), + RUN_DEV(MSI, RT3070_11), + RUN_DEV(NETGEAR, WNDA4100), + RUN_DEV(OVISLINK, RT3072), + RUN_DEV(PARA, RT3070), + RUN_DEV(PEGATRON, RT2870), + RUN_DEV(PEGATRON, RT3070), + RUN_DEV(PEGATRON, RT3070_2), + RUN_DEV(PEGATRON, RT3070_3), + RUN_DEV(PHILIPS, RT2870), + RUN_DEV(PLANEX2, GWUS300MINIS), + RUN_DEV(PLANEX2, GWUSMICRON), + RUN_DEV(PLANEX2, RT2870), + RUN_DEV(PLANEX2, RT3070), + RUN_DEV(QCOM, RT2870), + RUN_DEV(QUANTA, RT3070), + RUN_DEV(RALINK, RT2070), + RUN_DEV(RALINK, RT2770), + RUN_DEV(RALINK, RT2870), + RUN_DEV(RALINK, RT3070), + RUN_DEV(RALINK, RT3071), + RUN_DEV(RALINK, RT3072), + RUN_DEV(RALINK, RT3370), + RUN_DEV(RALINK, RT3572), + RUN_DEV(RALINK, RT3573), + RUN_DEV(RALINK, RT5370), + RUN_DEV(RALINK, RT5372), + RUN_DEV(RALINK, RT5572), + RUN_DEV(RALINK, RT8070), + RUN_DEV(SAMSUNG, WIS09ABGN), + RUN_DEV(SAMSUNG2, RT2870_1), + RUN_DEV(SENAO, RT2870_1), + RUN_DEV(SENAO, RT2870_2), + RUN_DEV(SENAO, RT2870_3), + RUN_DEV(SENAO, RT2870_4), + RUN_DEV(SENAO, RT3070), + RUN_DEV(SENAO, RT3071), + RUN_DEV(SENAO, RT3072_1), + RUN_DEV(SENAO, RT3072_2), + RUN_DEV(SENAO, RT3072_3), + RUN_DEV(SENAO, RT3072_4), + RUN_DEV(SENAO, RT3072_5), + RUN_DEV(SITECOMEU, RT2770), + RUN_DEV(SITECOMEU, RT2870_1), + RUN_DEV(SITECOMEU, RT2870_2), + RUN_DEV(SITECOMEU, RT2870_3), + RUN_DEV(SITECOMEU, RT2870_4), + RUN_DEV(SITECOMEU, RT3070), + RUN_DEV(SITECOMEU, RT3070_2), + RUN_DEV(SITECOMEU, RT3070_3), + RUN_DEV(SITECOMEU, RT3070_4), + RUN_DEV(SITECOMEU, RT3071), + RUN_DEV(SITECOMEU, RT3072_1), + RUN_DEV(SITECOMEU, RT3072_2), + RUN_DEV(SITECOMEU, RT3072_3), + RUN_DEV(SITECOMEU, RT3072_4), + RUN_DEV(SITECOMEU, RT3072_5), + RUN_DEV(SITECOMEU, RT3072_6), + RUN_DEV(SITECOMEU, WL608), + RUN_DEV(SPARKLAN, RT2870_1), + RUN_DEV(SPARKLAN, RT3070), + RUN_DEV(SWEEX2, LW153), + RUN_DEV(SWEEX2, LW303), + RUN_DEV(SWEEX2, LW313), + RUN_DEV(TOSHIBA, RT3070), + RUN_DEV(UMEDIA, RT2870_1), + RUN_DEV(ZCOM, RT2870_1), + RUN_DEV(ZCOM, RT2870_2), + RUN_DEV(ZINWELL, RT2870_1), + RUN_DEV(ZINWELL, RT2870_2), + RUN_DEV(ZINWELL, RT3070), + RUN_DEV(ZINWELL, RT3072_1), + RUN_DEV(ZINWELL, RT3072_2), + RUN_DEV(ZYXEL, RT2870_1), + RUN_DEV(ZYXEL, RT2870_2), + RUN_DEV(ZYXEL, RT3070), + RUN_DEV_EJECT(ZYXEL, NWD2705), + RUN_DEV_EJECT(RALINK, RT_STOR), +#undef RUN_DEV_EJECT +#undef RUN_DEV +}; + +static device_probe_t run_match; +static device_attach_t run_attach; +static device_detach_t run_detach; + +static usb_callback_t run_bulk_rx_callback; +static usb_callback_t run_bulk_tx_callback0; +static usb_callback_t run_bulk_tx_callback1; +static usb_callback_t run_bulk_tx_callback2; +static usb_callback_t run_bulk_tx_callback3; +static usb_callback_t run_bulk_tx_callback4; +static usb_callback_t run_bulk_tx_callback5; + +static void run_autoinst(void *, struct usb_device *, + struct usb_attach_arg *); +static int run_driver_loaded(struct module *, int, void *); +static void run_bulk_tx_callbackN(struct usb_xfer *xfer, + usb_error_t error, u_int index); +static struct ieee80211vap *run_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 run_vap_delete(struct ieee80211vap *); +static void run_cmdq_cb(void *, int); +static void run_setup_tx_list(struct run_softc *, + struct run_endpoint_queue *); +static void run_unsetup_tx_list(struct run_softc *, + struct run_endpoint_queue *); +static int run_load_microcode(struct run_softc *); +static int run_reset(struct run_softc *); +static usb_error_t run_do_request(struct run_softc *, + struct usb_device_request *, void *); +static int run_read(struct run_softc *, uint16_t, uint32_t *); +static int run_read_region_1(struct run_softc *, uint16_t, uint8_t *, int); +static int run_write_2(struct run_softc *, uint16_t, uint16_t); +static int run_write(struct run_softc *, uint16_t, uint32_t); +static int run_write_region_1(struct run_softc *, uint16_t, + const uint8_t *, int); +static int run_set_region_4(struct run_softc *, uint16_t, uint32_t, int); +static int run_efuse_read(struct run_softc *, uint16_t, uint16_t *, int); +static int run_efuse_read_2(struct run_softc *, uint16_t, uint16_t *); +static int run_eeprom_read_2(struct run_softc *, uint16_t, uint16_t *); +static int run_rt2870_rf_write(struct run_softc *, uint32_t); +static int run_rt3070_rf_read(struct run_softc *, uint8_t, uint8_t *); +static int run_rt3070_rf_write(struct run_softc *, uint8_t, uint8_t); +static int run_bbp_read(struct run_softc *, uint8_t, uint8_t *); +static int run_bbp_write(struct run_softc *, uint8_t, uint8_t); +static int run_mcu_cmd(struct run_softc *, uint8_t, uint16_t); +static const char *run_get_rf(uint16_t); +static void run_rt3593_get_txpower(struct run_softc *); +static void run_get_txpower(struct run_softc *); +static int run_read_eeprom(struct run_softc *); +static struct ieee80211_node *run_node_alloc(struct ieee80211vap *, + const uint8_t mac[IEEE80211_ADDR_LEN]); +static int run_media_change(struct ifnet *); +static int run_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static int run_wme_update(struct ieee80211com *); +static void run_key_set_cb(void *); +static int run_key_set(struct ieee80211vap *, struct ieee80211_key *); +static void run_key_delete_cb(void *); +static int run_key_delete(struct ieee80211vap *, struct ieee80211_key *); +static void run_ratectl_to(void *); +static void run_ratectl_cb(void *, int); +static void run_drain_fifo(void *); +static void run_iter_func(void *, struct ieee80211_node *); +static void run_newassoc_cb(void *); +static void run_newassoc(struct ieee80211_node *, int); +static void run_recv_mgmt(struct ieee80211_node *, struct mbuf *, int, + const struct ieee80211_rx_stats *, int, int); +static void run_rx_frame(struct run_softc *, struct mbuf *, uint32_t); +static void run_tx_free(struct run_endpoint_queue *pq, + struct run_tx_data *, int); +static void run_set_tx_desc(struct run_softc *, struct run_tx_data *); +static int run_tx(struct run_softc *, struct mbuf *, + struct ieee80211_node *); +static int run_tx_mgt(struct run_softc *, struct mbuf *, + struct ieee80211_node *); +static int run_sendprot(struct run_softc *, const struct mbuf *, + struct ieee80211_node *, int, int); +static int run_tx_param(struct run_softc *, struct mbuf *, + struct ieee80211_node *, + const struct ieee80211_bpf_params *); +static int run_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static int run_transmit(struct ieee80211com *, struct mbuf *); +static void run_start(struct run_softc *); +static void run_parent(struct ieee80211com *); +static void run_iq_calib(struct run_softc *, u_int); +static void run_set_agc(struct run_softc *, uint8_t); +static void run_select_chan_group(struct run_softc *, int); +static void run_set_rx_antenna(struct run_softc *, int); +static void run_rt2870_set_chan(struct run_softc *, u_int); +static void run_rt3070_set_chan(struct run_softc *, u_int); +static void run_rt3572_set_chan(struct run_softc *, u_int); +static void run_rt3593_set_chan(struct run_softc *, u_int); +static void run_rt5390_set_chan(struct run_softc *, u_int); +static void run_rt5592_set_chan(struct run_softc *, u_int); +static int run_set_chan(struct run_softc *, struct ieee80211_channel *); +static void run_set_channel(struct ieee80211com *); +static void run_getradiocaps(struct ieee80211com *, int, int *, + struct ieee80211_channel[]); +static void run_scan_start(struct ieee80211com *); +static void run_scan_end(struct ieee80211com *); +static void run_update_beacon(struct ieee80211vap *, int); +static void run_update_beacon_cb(void *); +static void run_updateprot(struct ieee80211com *); +static void run_updateprot_cb(void *); +static void run_usb_timeout_cb(void *); +static void run_reset_livelock(struct run_softc *); +static void run_enable_tsf_sync(struct run_softc *); +static void run_enable_tsf(struct run_softc *); +static void run_disable_tsf(struct run_softc *); +static void run_get_tsf(struct run_softc *, uint64_t *); +static void run_enable_mrr(struct run_softc *); +static void run_set_txpreamble(struct run_softc *); +static void run_set_basicrates(struct run_softc *); +static void run_set_leds(struct run_softc *, uint16_t); +static void run_set_bssid(struct run_softc *, const uint8_t *); +static void run_set_macaddr(struct run_softc *, const uint8_t *); +static void run_updateslot(struct ieee80211com *); +static void run_updateslot_cb(void *); +static void run_update_mcast(struct ieee80211com *); +static int8_t run_rssi2dbm(struct run_softc *, uint8_t, uint8_t); +static void run_update_promisc_locked(struct run_softc *); +static void run_update_promisc(struct ieee80211com *); +static void run_rt5390_bbp_init(struct run_softc *); +static int run_bbp_init(struct run_softc *); +static int run_rt3070_rf_init(struct run_softc *); +static void run_rt3593_rf_init(struct run_softc *); +static void run_rt5390_rf_init(struct run_softc *); +static int run_rt3070_filter_calib(struct run_softc *, uint8_t, uint8_t, + uint8_t *); +static void run_rt3070_rf_setup(struct run_softc *); +static void run_rt3593_rf_setup(struct run_softc *); +static void run_rt5390_rf_setup(struct run_softc *); +static int run_txrx_enable(struct run_softc *); +static void run_adjust_freq_offset(struct run_softc *); +static void run_init_locked(struct run_softc *); +static void run_stop(void *); +static void run_delay(struct run_softc *, u_int); +static void run_update_chw(struct ieee80211com *ic); +static int run_ampdu_enable(struct ieee80211_node *ni, + struct ieee80211_tx_ampdu *tap); + +static eventhandler_tag run_etag; + +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[] = { + /* CCK rates (11b) */ + { 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 }, + + /* OFDM rates (11a / 11g) */ + { 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 }, + + /* MCS - single stream */ + { 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 }, + + /* MCS - 2 streams */ + { 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 RT2860_RIDX_CCK1 0 +#define RT2860_RIDX_CCK11 3 +#define RT2860_RIDX_OFDM6 4 +#define RT2860_RIDX_MCS0 12 +#define RT2860_RIDX_MAX 36 + +static const struct { + uint16_t reg; + uint32_t val; +} rt2870_def_mac[] = { + RT2870_DEF_MAC +}; + +static const struct { + uint8_t reg; + uint8_t val; +} rt2860_def_bbp[] = { + RT2860_DEF_BBP +},rt5390_def_bbp[] = { + RT5390_DEF_BBP +},rt5592_def_bbp[] = { + RT5592_DEF_BBP +}; + +/* + * Default values for BBP register R196 for RT5592. + */ +static const uint8_t rt5592_bbp_r196[] = { + 0xe0, 0x1f, 0x38, 0x32, 0x08, 0x28, 0x19, 0x0a, 0xff, 0x00, + 0x16, 0x10, 0x10, 0x0b, 0x36, 0x2c, 0x26, 0x24, 0x42, 0x36, + 0x30, 0x2d, 0x4c, 0x46, 0x3d, 0x40, 0x3e, 0x42, 0x3d, 0x40, + 0x3c, 0x34, 0x2c, 0x2f, 0x3c, 0x35, 0x2e, 0x2a, 0x49, 0x41, + 0x36, 0x31, 0x30, 0x30, 0x0e, 0x0d, 0x28, 0x21, 0x1c, 0x16, + 0x50, 0x4a, 0x43, 0x40, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7d, 0x14, 0x32, 0x2c, 0x36, 0x4c, 0x43, 0x2c, + 0x2e, 0x36, 0x30, 0x6e +}; + +static const struct rfprog { + uint8_t chan; + uint32_t r1, r2, r3, r4; +} rt2860_rf2850[] = { + RT2860_RF2850 +}; + +struct { + uint8_t n, r, k; +} rt3070_freqs[] = { + RT3070_RF3052 +}; + +static const struct rt5592_freqs { + uint16_t n; + uint8_t k, m, r; +} rt5592_freqs_20mhz[] = { + RT5592_RF5592_20MHZ +},rt5592_freqs_40mhz[] = { + RT5592_RF5592_40MHZ +}; + +static const struct { + uint8_t reg; + uint8_t val; +} rt3070_def_rf[] = { + RT3070_DEF_RF +},rt3572_def_rf[] = { + RT3572_DEF_RF +},rt3593_def_rf[] = { + RT3593_DEF_RF +},rt5390_def_rf[] = { + RT5390_DEF_RF +},rt5392_def_rf[] = { + RT5392_DEF_RF +},rt5592_def_rf[] = { + RT5592_DEF_RF +},rt5592_2ghz_def_rf[] = { + RT5592_2GHZ_DEF_RF +},rt5592_5ghz_def_rf[] = { + RT5592_5GHZ_DEF_RF +}; + +static const struct { + u_int firstchan; + u_int lastchan; + uint8_t reg; + uint8_t val; +} rt5592_chan_5ghz[] = { + RT5592_CHAN_5GHZ +}; + +static const struct usb_config run_config[RUN_N_XFER] = { + [RUN_BULK_TX_BE] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .ep_index = 0, + .direction = UE_DIR_OUT, + .bufsize = RUN_MAX_TXSZ, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = run_bulk_tx_callback0, + .timeout = 5000, /* ms */ + }, + [RUN_BULK_TX_BK] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .ep_index = 1, + .bufsize = RUN_MAX_TXSZ, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = run_bulk_tx_callback1, + .timeout = 5000, /* ms */ + }, + [RUN_BULK_TX_VI] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .ep_index = 2, + .bufsize = RUN_MAX_TXSZ, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = run_bulk_tx_callback2, + .timeout = 5000, /* ms */ + }, + [RUN_BULK_TX_VO] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .ep_index = 3, + .bufsize = RUN_MAX_TXSZ, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = run_bulk_tx_callback3, + .timeout = 5000, /* ms */ + }, + [RUN_BULK_TX_HCCA] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .ep_index = 4, + .bufsize = RUN_MAX_TXSZ, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,}, + .callback = run_bulk_tx_callback4, + .timeout = 5000, /* ms */ + }, + [RUN_BULK_TX_PRIO] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .ep_index = 5, + .bufsize = RUN_MAX_TXSZ, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,}, + .callback = run_bulk_tx_callback5, + .timeout = 5000, /* ms */ + }, + [RUN_BULK_RX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = RUN_MAX_RXSZ, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = run_bulk_rx_callback, + } +}; + +static void +run_autoinst(void *arg, struct usb_device *udev, + struct usb_attach_arg *uaa) +{ +#ifndef __HAIKU__ + struct usb_interface *iface; + struct usb_interface_descriptor *id; + + if (uaa->dev_state != UAA_DEV_READY) + return; + + iface = usbd_get_iface(udev, 0); + if (iface == NULL) + return; + id = iface->idesc; + if (id == NULL || id->bInterfaceClass != UICLASS_MASS) + return; + if (usbd_lookup_id_by_uaa(run_devs, sizeof(run_devs), uaa)) + return; + if (usb_msc_eject(udev, 0, MSC_EJECT_STOPUNIT) == 0) + uaa->dev_state = UAA_DEV_EJECTING; +#endif +} + +static int +run_driver_loaded(struct module *mod, int what, void *arg) +{ + switch (what) { + case MOD_LOAD: + run_etag = EVENTHANDLER_REGISTER(usb_dev_configured, + run_autoinst, NULL, EVENTHANDLER_PRI_ANY); + break; + case MOD_UNLOAD: + EVENTHANDLER_DEREGISTER(usb_dev_configured, run_etag); + break; + default: + return (EOPNOTSUPP); + } + return (0); +} + +static int +run_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 != RT2860_IFACE_INDEX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(run_devs, sizeof(run_devs), uaa)); +} + +static int +run_attach(device_t self) +{ + struct run_softc *sc = device_get_softc(self); + struct usb_attach_arg *uaa = device_get_ivars(self); + struct ieee80211com *ic = &sc->sc_ic; + uint32_t ver; + uint8_t iface_index; + int ntries, error; + + device_set_usb_desc(self); + sc->sc_udev = uaa->device; + sc->sc_dev = self; + if (USB_GET_DRIVER_INFO(uaa) != RUN_EJECT) + sc->sc_flags |= RUN_FLAG_FWLOAD_NEEDED; + + mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), + MTX_NETWORK_LOCK, MTX_DEF); + mbufq_init(&sc->sc_snd, ifqmaxlen); + + iface_index = RT2860_IFACE_INDEX; + + error = usbd_transfer_setup(uaa->device, &iface_index, + sc->sc_xfer, run_config, RUN_N_XFER, sc, &sc->sc_mtx); + if (error) { + device_printf(self, "could not allocate USB transfers, " + "err=%s\n", usbd_errstr(error)); + goto detach; + } + + RUN_LOCK(sc); + + /* wait for the chip to settle */ + for (ntries = 0; ntries < 100; ntries++) { + if (run_read(sc, RT2860_ASIC_VER_ID, &ver) != 0) { + RUN_UNLOCK(sc); + goto detach; + } + if (ver != 0 && ver != 0xffffffff) + break; + run_delay(sc, 10); + } + if (ntries == 100) { + device_printf(sc->sc_dev, + "timeout waiting for NIC to initialize\n"); + RUN_UNLOCK(sc); + goto detach; + } + sc->mac_ver = ver >> 16; + sc->mac_rev = ver & 0xffff; + + /* retrieve RF rev. no and various other things from EEPROM */ + run_read_eeprom(sc); + + device_printf(sc->sc_dev, + "MAC/BBP RT%04X (rev 0x%04X), RF %s (MIMO %dT%dR), address %s\n", + sc->mac_ver, sc->mac_rev, run_get_rf(sc->rf_rev), + sc->ntxchains, sc->nrxchains, ether_sprintf(ic->ic_macaddr)); + + RUN_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 */ + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA | /* station mode supported */ + IEEE80211_C_MONITOR | /* monitor mode supported */ + IEEE80211_C_IBSS | + IEEE80211_C_HOSTAP | + IEEE80211_C_WDS | /* 4-address traffic works */ + IEEE80211_C_MBSS | + IEEE80211_C_SHPREAMBLE | /* short preamble supported */ + IEEE80211_C_SHSLOT | /* short slot time supported */ + IEEE80211_C_SWAMSDUTX | /* Do software A-MSDU TX */ + IEEE80211_C_FF | /* Atheros fast-frames */ + IEEE80211_C_WME | /* WME */ + IEEE80211_C_WPA; /* WPA1|WPA2(RSN) */ + + /* + * RF2020 is not an 11n device. + */ + if (sc->rf_rev != RT3070_RF_2020) { + 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_TKIPMIC | + IEEE80211_CRYPTO_TKIP; + + ic->ic_flags |= IEEE80211_F_DATAPAD; + ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS; + + run_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, + ic->ic_channels); + + ieee80211_ifattach(ic); + + ic->ic_scan_start = run_scan_start; + ic->ic_scan_end = run_scan_end; + ic->ic_set_channel = run_set_channel; + ic->ic_getradiocaps = run_getradiocaps; + ic->ic_node_alloc = run_node_alloc; + ic->ic_newassoc = run_newassoc; + ic->ic_updateslot = run_updateslot; + ic->ic_update_mcast = run_update_mcast; + ic->ic_wme.wme_update = run_wme_update; + ic->ic_raw_xmit = run_raw_xmit; + ic->ic_update_promisc = run_update_promisc; + ic->ic_vap_create = run_vap_create; + ic->ic_vap_delete = run_vap_delete; + ic->ic_transmit = run_transmit; + ic->ic_parent = run_parent; + ic->ic_update_chw = run_update_chw; + ic->ic_ampdu_enable = run_ampdu_enable; + + ieee80211_radiotap_attach(ic, + &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), + RUN_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + RUN_RX_RADIOTAP_PRESENT); + + TASK_INIT(&sc->cmdq_task, 0, run_cmdq_cb, sc); + TASK_INIT(&sc->ratectl_task, 0, run_ratectl_cb, sc); + usb_callout_init_mtx(&sc->ratectl_ch, &sc->sc_mtx, 0); + + if (bootverbose) + ieee80211_announce(ic); + + return (0); + +detach: + run_detach(self); + return (ENXIO); +} + +static void +run_drain_mbufq(struct run_softc *sc) +{ + struct mbuf *m; + struct ieee80211_node *ni; + + RUN_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 +run_detach(device_t self) +{ + struct run_softc *sc = device_get_softc(self); + struct ieee80211com *ic = &sc->sc_ic; + int i; + + RUN_LOCK(sc); + sc->sc_detached = 1; + RUN_UNLOCK(sc); + + /* stop all USB transfers */ + usbd_transfer_unsetup(sc->sc_xfer, RUN_N_XFER); + + RUN_LOCK(sc); + sc->ratectl_run = RUN_RATECTL_OFF; + sc->cmdq_run = sc->cmdq_key_set = RUN_CMDQ_ABORT; + + /* free TX list, if any */ + for (i = 0; i != RUN_EP_QUEUES; i++) + run_unsetup_tx_list(sc, &sc->sc_epq[i]); + + /* Free TX queue */ + run_drain_mbufq(sc); + RUN_UNLOCK(sc); + + if (sc->sc_ic.ic_softc == sc) { + /* drain tasks */ + usb_callout_drain(&sc->ratectl_ch); + ieee80211_draintask(ic, &sc->cmdq_task); + ieee80211_draintask(ic, &sc->ratectl_task); + ieee80211_ifdetach(ic); + } + + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static struct ieee80211vap * +run_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 run_softc *sc = ic->ic_softc; + struct run_vap *rvp; + struct ieee80211vap *vap; + int i; + + if (sc->rvp_cnt >= RUN_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 run_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 = run_update_beacon; + vap->iv_max_aid = RT2870_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_32K; + 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 *)run_key_delete; + vap->iv_key_set = (void *)run_key_set; + + /* override state transition machine */ + rvp->newstate = vap->iv_newstate; + vap->iv_newstate = run_newstate; + if (opmode == IEEE80211_M_IBSS) { + rvp->recv_mgmt = vap->iv_recv_mgmt; + vap->iv_recv_mgmt = run_recv_mgmt; + } + + ieee80211_ratectl_init(vap); + ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */); + + /* complete setup */ + ieee80211_vap_attach(vap, run_media_change, ieee80211_media_status, + mac); + + /* make sure id is always unique */ + for (i = 0; i < RUN_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 = RUN_CMDQ_GO; + + RUN_DPRINTF(sc, RUN_DEBUG_STATE, "rvp_id=%d bmap=%x rvp_cnt=%d\n", + rvp->rvp_id, sc->rvp_bmap, sc->rvp_cnt); + + return (vap); +} + +static void +run_vap_delete(struct ieee80211vap *vap) +{ + struct run_vap *rvp = RUN_VAP(vap); + struct ieee80211com *ic; + struct run_softc *sc; + uint8_t rvp_id; + + if (vap == NULL) + return; + + ic = vap->iv_ic; + sc = ic->ic_softc; + + RUN_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); + run_set_region_4(sc, RT2860_SKEY(rvp_id, 0), 0, 128); + run_set_region_4(sc, RT2860_BCN_BASE(rvp_id), 0, 512); + --sc->rvp_cnt; + + RUN_DPRINTF(sc, RUN_DEBUG_STATE, + "vap=%p rvp_id=%d bmap=%x rvp_cnt=%d\n", + vap, rvp_id, sc->rvp_bmap, sc->rvp_cnt); + + RUN_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. + * '& RUN_CMDQ_MASQ' is to loop cmdq[]. + */ +static void +run_cmdq_cb(void *arg, int pending) +{ + struct run_softc *sc = arg; + uint8_t i; + + /* call cmdq[].func locked */ + RUN_LOCK(sc); + for (i = sc->cmdq_exec; sc->cmdq[i].func && pending; + i = sc->cmdq_exec, pending--) { + RUN_DPRINTF(sc, RUN_DEBUG_CMD, "cmdq_exec=%d pending=%d\n", + i, pending); + if (sc->cmdq_run == RUN_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 &= RUN_CMDQ_MASQ; + } + RUN_UNLOCK(sc); +} + +static void +run_setup_tx_list(struct run_softc *sc, struct run_endpoint_queue *pq) +{ + struct run_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[RUN_TX_RING_COUNT]; data++) { + data->sc = sc; + STAILQ_INSERT_TAIL(&pq->tx_fh, data, next); + } + pq->tx_nfree = RUN_TX_RING_COUNT; +} + +static void +run_unsetup_tx_list(struct run_softc *sc, struct run_endpoint_queue *pq) +{ + struct run_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[RUN_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 +run_load_microcode(struct run_softc *sc) +{ + usb_device_request_t req; + const struct firmware *fw; + const u_char *base; + uint32_t tmp; + int ntries, error; + const uint64_t *temp; + uint64_t bytes; + + RUN_UNLOCK(sc); + fw = firmware_get("runfw"); + RUN_LOCK(sc); + if (fw == NULL) { + device_printf(sc->sc_dev, + "failed loadfirmware of file %s\n", "runfw"); + return ENOENT; + } + + if (fw->datasize != 8192) { + device_printf(sc->sc_dev, + "invalid firmware size (should be 8KB)\n"); + error = EINVAL; + goto fail; + } + + /* + * RT3071/RT3072 use a different firmware + * run-rt2870 (8KB) contains both, + * first half (4KB) is for rt2870, + * last half is for rt3071. + */ + base = fw->data; + if ((sc->mac_ver) != 0x2860 && + (sc->mac_ver) != 0x2872 && + (sc->mac_ver) != 0x3070) { + base += 4096; + } + + /* cheap sanity check */ + temp = fw->data; + bytes = *temp; + if (bytes != be64toh(0xffffff0210280210ULL)) { + device_printf(sc->sc_dev, "firmware checksum failed\n"); + error = EINVAL; + goto fail; + } + + /* write microcode image */ + if (sc->sc_flags & RUN_FLAG_FWLOAD_NEEDED) { + run_write_region_1(sc, RT2870_FW_BASE, base, 4096); + run_write(sc, RT2860_H2M_MAILBOX_CID, 0xffffffff); + run_write(sc, RT2860_H2M_MAILBOX_STATUS, 0xffffffff); + } + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RT2870_RESET; + USETW(req.wValue, 8); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + if ((error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL)) + != 0) { + device_printf(sc->sc_dev, "firmware reset failed\n"); + goto fail; + } + + run_delay(sc, 10); + + run_write(sc, RT2860_H2M_BBPAGENT, 0); + run_write(sc, RT2860_H2M_MAILBOX, 0); + run_write(sc, RT2860_H2M_INTSRC, 0); + if ((error = run_mcu_cmd(sc, RT2860_MCU_CMD_RFRESET, 0)) != 0) + goto fail; + + /* wait until microcontroller is ready */ + for (ntries = 0; ntries < 1000; ntries++) { + if ((error = run_read(sc, RT2860_SYS_CTRL, &tmp)) != 0) + goto fail; + if (tmp & RT2860_MCU_READY) + break; + run_delay(sc, 10); + } + if (ntries == 1000) { + device_printf(sc->sc_dev, + "timeout waiting for MCU to initialize\n"); + error = ETIMEDOUT; + goto fail; + } + device_printf(sc->sc_dev, "firmware %s ver. %u.%u loaded\n", + (base == fw->data) ? "RT2870" : "RT3071", + *(base + 4092), *(base + 4093)); + +fail: + firmware_put(fw, FIRMWARE_UNLOAD); + return (error); +} + +static int +run_reset(struct run_softc *sc) +{ + usb_device_request_t req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RT2870_RESET; + USETW(req.wValue, 1); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL)); +} + +static usb_error_t +run_do_request(struct run_softc *sc, + struct usb_device_request *req, void *data) +{ + usb_error_t err; + int ntries = 10; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + while (ntries--) { + err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, + req, data, 0, NULL, 250 /* ms */); + if (err == 0) + break; + RUN_DPRINTF(sc, RUN_DEBUG_USB, + "Control request failed, %s (retrying)\n", + usbd_errstr(err)); + run_delay(sc, 10); + } + return (err); +} + +static int +run_read(struct run_softc *sc, uint16_t reg, uint32_t *val) +{ + uint32_t tmp; + int error; + + error = run_read_region_1(sc, reg, (uint8_t *)&tmp, sizeof tmp); + if (error == 0) + *val = le32toh(tmp); + else + *val = 0xffffffff; + return (error); +} + +static int +run_read_region_1(struct run_softc *sc, uint16_t reg, uint8_t *buf, int len) +{ + usb_device_request_t req; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RT2870_READ_REGION_1; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + return (run_do_request(sc, &req, buf)); +} + +static int +run_write_2(struct run_softc *sc, uint16_t reg, uint16_t val) +{ + usb_device_request_t req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RT2870_WRITE_2; + USETW(req.wValue, val); + USETW(req.wIndex, reg); + USETW(req.wLength, 0); + + return (run_do_request(sc, &req, NULL)); +} + +static int +run_write(struct run_softc *sc, uint16_t reg, uint32_t val) +{ + int error; + + if ((error = run_write_2(sc, reg, val & 0xffff)) == 0) + error = run_write_2(sc, reg + 2, val >> 16); + return (error); +} + +static int +run_write_region_1(struct run_softc *sc, uint16_t reg, const uint8_t *buf, + int len) +{ +#if 1 + int i, error = 0; + /* + * NB: the WRITE_REGION_1 command is not stable on RT2860. + * We thus issue multiple WRITE_2 commands instead. + */ + KASSERT((len & 1) == 0, ("run_write_region_1: Data too long.\n")); + for (i = 0; i < len && error == 0; i += 2) + error = run_write_2(sc, reg + i, buf[i] | buf[i + 1] << 8); + return (error); +#else + usb_device_request_t req; + int error = 0; + + /* + * NOTE: It appears the WRITE_REGION_1 command cannot be + * passed a huge amount of data, which will crash the + * firmware. Limit amount of data passed to 64-bytes at a + * time. + */ + while (len > 0) { + int delta = 64; + if (delta > len) + delta = len; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RT2870_WRITE_REGION_1; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, delta); + error = run_do_request(sc, &req, __DECONST(uint8_t *, buf)); + if (error != 0) + break; + reg += delta; + buf += delta; + len -= delta; + } + return (error); +#endif +} + +static int +run_set_region_4(struct run_softc *sc, uint16_t reg, uint32_t val, int len) +{ + int i, error = 0; + + KASSERT((len & 3) == 0, ("run_set_region_4: Invalid data length.\n")); + for (i = 0; i < len && error == 0; i += 4) + error = run_write(sc, reg + i, val); + return (error); +} + +static int +run_efuse_read(struct run_softc *sc, uint16_t addr, uint16_t *val, int count) +{ + uint32_t tmp; + uint16_t reg; + int error, ntries; + + if ((error = run_read(sc, RT3070_EFUSE_CTRL, &tmp)) != 0) + return (error); + + if (count == 2) + addr *= 2; + /*- + * Read one 16-byte block into registers EFUSE_DATA[0-3]: + * DATA0: F E D C + * DATA1: B A 9 8 + * DATA2: 7 6 5 4 + * DATA3: 3 2 1 0 + */ + tmp &= ~(RT3070_EFSROM_MODE_MASK | RT3070_EFSROM_AIN_MASK); + tmp |= (addr & ~0xf) << RT3070_EFSROM_AIN_SHIFT | RT3070_EFSROM_KICK; + run_write(sc, RT3070_EFUSE_CTRL, tmp); + for (ntries = 0; ntries < 100; ntries++) { + if ((error = run_read(sc, RT3070_EFUSE_CTRL, &tmp)) != 0) + return (error); + if (!(tmp & RT3070_EFSROM_KICK)) + break; + run_delay(sc, 2); + } + if (ntries == 100) + return (ETIMEDOUT); + + if ((tmp & RT3070_EFUSE_AOUT_MASK) == RT3070_EFUSE_AOUT_MASK) { + *val = 0xffff; /* address not found */ + return (0); + } + /* determine to which 32-bit register our 16-bit word belongs */ + reg = RT3070_EFUSE_DATA3 - (addr & 0xc); + if ((error = run_read(sc, reg, &tmp)) != 0) + return (error); + + tmp >>= (8 * (addr & 0x3)); + *val = (addr & 1) ? tmp >> 16 : tmp & 0xffff; + + return (0); +} + +/* Read 16-bit from eFUSE ROM for RT3xxx. */ +static int +run_efuse_read_2(struct run_softc *sc, uint16_t addr, uint16_t *val) +{ + return (run_efuse_read(sc, addr, val, 2)); +} + +static int +run_eeprom_read_2(struct run_softc *sc, uint16_t addr, uint16_t *val) +{ + usb_device_request_t req; + uint16_t tmp; + int error; + + addr *= 2; + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RT2870_EEPROM_READ; + USETW(req.wValue, 0); + USETW(req.wIndex, addr); + USETW(req.wLength, sizeof(tmp)); + + error = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, &tmp); + if (error == 0) + *val = le16toh(tmp); + else + *val = 0xffff; + return (error); +} + +static __inline int +run_srom_read(struct run_softc *sc, uint16_t addr, uint16_t *val) +{ + /* either eFUSE ROM or EEPROM */ + return sc->sc_srom_read(sc, addr, val); +} + +static int +run_rt2870_rf_write(struct run_softc *sc, uint32_t val) +{ + uint32_t tmp; + int error, ntries; + + for (ntries = 0; ntries < 10; ntries++) { + if ((error = run_read(sc, RT2860_RF_CSR_CFG0, &tmp)) != 0) + return (error); + if (!(tmp & RT2860_RF_REG_CTRL)) + break; + } + if (ntries == 10) + return (ETIMEDOUT); + + return (run_write(sc, RT2860_RF_CSR_CFG0, val)); +} + +static int +run_rt3070_rf_read(struct run_softc *sc, uint8_t reg, uint8_t *val) +{ + uint32_t tmp; + int error, ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0) + return (error); + if (!(tmp & RT3070_RF_KICK)) + break; + } + if (ntries == 100) + return (ETIMEDOUT); + + tmp = RT3070_RF_KICK | reg << 8; + if ((error = run_write(sc, RT3070_RF_CSR_CFG, tmp)) != 0) + return (error); + + for (ntries = 0; ntries < 100; ntries++) { + if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0) + return (error); + if (!(tmp & RT3070_RF_KICK)) + break; + } + if (ntries == 100) + return (ETIMEDOUT); + + *val = tmp & 0xff; + return (0); +} + +static int +run_rt3070_rf_write(struct run_softc *sc, uint8_t reg, uint8_t val) +{ + uint32_t tmp; + int error, ntries; + + for (ntries = 0; ntries < 10; ntries++) { + if ((error = run_read(sc, RT3070_RF_CSR_CFG, &tmp)) != 0) + return (error); + if (!(tmp & RT3070_RF_KICK)) + break; + } + if (ntries == 10) + return (ETIMEDOUT); + + tmp = RT3070_RF_WRITE | RT3070_RF_KICK | reg << 8 | val; + return (run_write(sc, RT3070_RF_CSR_CFG, tmp)); +} + +static int +run_bbp_read(struct run_softc *sc, uint8_t reg, uint8_t *val) +{ + uint32_t tmp; + int ntries, error; + + for (ntries = 0; ntries < 10; ntries++) { + if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0) + return (error); + if (!(tmp & RT2860_BBP_CSR_KICK)) + break; + } + if (ntries == 10) + return (ETIMEDOUT); + + tmp = RT2860_BBP_CSR_READ | RT2860_BBP_CSR_KICK | reg << 8; + if ((error = run_write(sc, RT2860_BBP_CSR_CFG, tmp)) != 0) + return (error); + + for (ntries = 0; ntries < 10; ntries++) { + if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0) + return (error); + if (!(tmp & RT2860_BBP_CSR_KICK)) + break; + } + if (ntries == 10) + return (ETIMEDOUT); + + *val = tmp & 0xff; + return (0); +} + +static int +run_bbp_write(struct run_softc *sc, uint8_t reg, uint8_t val) +{ + uint32_t tmp; + int ntries, error; + + for (ntries = 0; ntries < 10; ntries++) { + if ((error = run_read(sc, RT2860_BBP_CSR_CFG, &tmp)) != 0) + return (error); + if (!(tmp & RT2860_BBP_CSR_KICK)) + break; + } + if (ntries == 10) + return (ETIMEDOUT); + + tmp = RT2860_BBP_CSR_KICK | reg << 8 | val; + return (run_write(sc, RT2860_BBP_CSR_CFG, tmp)); +} + +/* + * Send a command to the 8051 microcontroller unit. + */ +static int +run_mcu_cmd(struct run_softc *sc, uint8_t cmd, uint16_t arg) +{ + uint32_t tmp; + int error, ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if ((error = run_read(sc, RT2860_H2M_MAILBOX, &tmp)) != 0) + return error; + if (!(tmp & RT2860_H2M_BUSY)) + break; + } + if (ntries == 100) + return ETIMEDOUT; + + tmp = RT2860_H2M_BUSY | RT2860_TOKEN_NO_INTR << 16 | arg; + if ((error = run_write(sc, RT2860_H2M_MAILBOX, tmp)) == 0) + error = run_write(sc, RT2860_HOST_CMD, cmd); + return (error); +} + +/* + * 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 const char * +run_get_rf(uint16_t rev) +{ + switch (rev) { + case RT2860_RF_2820: return "RT2820"; + case RT2860_RF_2850: return "RT2850"; + case RT2860_RF_2720: return "RT2720"; + case RT2860_RF_2750: return "RT2750"; + case RT3070_RF_3020: return "RT3020"; + case RT3070_RF_2020: return "RT2020"; + case RT3070_RF_3021: return "RT3021"; + case RT3070_RF_3022: return "RT3022"; + case RT3070_RF_3052: return "RT3052"; + case RT3593_RF_3053: return "RT3053"; + case RT5592_RF_5592: return "RT5592"; + case RT5390_RF_5370: return "RT5370"; + case RT5390_RF_5372: return "RT5372"; + } + return ("unknown"); +} + +static void +run_rt3593_get_txpower(struct run_softc *sc) +{ + uint16_t addr, val; + int i; + + /* Read power settings for 2GHz channels. */ + for (i = 0; i < 14; i += 2) { + addr = (sc->ntxchains == 3) ? RT3593_EEPROM_PWR2GHZ_BASE1 : + RT2860_EEPROM_PWR2GHZ_BASE1; + run_srom_read(sc, addr + i / 2, &val); + sc->txpow1[i + 0] = (int8_t)(val & 0xff); + sc->txpow1[i + 1] = (int8_t)(val >> 8); + + addr = (sc->ntxchains == 3) ? RT3593_EEPROM_PWR2GHZ_BASE2 : + RT2860_EEPROM_PWR2GHZ_BASE2; + run_srom_read(sc, addr + i / 2, &val); + sc->txpow2[i + 0] = (int8_t)(val & 0xff); + sc->txpow2[i + 1] = (int8_t)(val >> 8); + + if (sc->ntxchains == 3) { + run_srom_read(sc, RT3593_EEPROM_PWR2GHZ_BASE3 + i / 2, + &val); + sc->txpow3[i + 0] = (int8_t)(val & 0xff); + sc->txpow3[i + 1] = (int8_t)(val >> 8); + } + } + /* Fix broken Tx power entries. */ + for (i = 0; i < 14; i++) { + if (sc->txpow1[i] > 31) + sc->txpow1[i] = 5; + if (sc->txpow2[i] > 31) + sc->txpow2[i] = 5; + if (sc->ntxchains == 3) { + if (sc->txpow3[i] > 31) + sc->txpow3[i] = 5; + } + } + /* Read power settings for 5GHz channels. */ + for (i = 0; i < 40; i += 2) { + run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE1 + i / 2, &val); + sc->txpow1[i + 14] = (int8_t)(val & 0xff); + sc->txpow1[i + 15] = (int8_t)(val >> 8); + + run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE2 + i / 2, &val); + sc->txpow2[i + 14] = (int8_t)(val & 0xff); + sc->txpow2[i + 15] = (int8_t)(val >> 8); + + if (sc->ntxchains == 3) { + run_srom_read(sc, RT3593_EEPROM_PWR5GHZ_BASE3 + i / 2, + &val); + sc->txpow3[i + 14] = (int8_t)(val & 0xff); + sc->txpow3[i + 15] = (int8_t)(val >> 8); + } + } +} + +static void +run_get_txpower(struct run_softc *sc) +{ + uint16_t val; + int i; + + /* Read power settings for 2GHz channels. */ + for (i = 0; i < 14; i += 2) { + run_srom_read(sc, RT2860_EEPROM_PWR2GHZ_BASE1 + i / 2, &val); + sc->txpow1[i + 0] = (int8_t)(val & 0xff); + sc->txpow1[i + 1] = (int8_t)(val >> 8); + + if (sc->mac_ver != 0x5390) { + run_srom_read(sc, + RT2860_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->mac_ver >= 0x5390) { + if (sc->txpow1[i] < 0 || sc->txpow1[i] > 39) + sc->txpow1[i] = 5; + } else { + if (sc->txpow1[i] < 0 || sc->txpow1[i] > 31) + sc->txpow1[i] = 5; + } + if (sc->mac_ver > 0x5390) { + if (sc->txpow2[i] < 0 || sc->txpow2[i] > 39) + sc->txpow2[i] = 5; + } else if (sc->mac_ver < 0x5390) { + if (sc->txpow2[i] < 0 || sc->txpow2[i] > 31) + sc->txpow2[i] = 5; + } + RUN_DPRINTF(sc, RUN_DEBUG_TXPWR, + "chan %d: power1=%d, power2=%d\n", + rt2860_rf2850[i].chan, sc->txpow1[i], sc->txpow2[i]); + } + /* Read power settings for 5GHz channels. */ + for (i = 0; i < 40; i += 2) { + run_srom_read(sc, RT2860_EEPROM_PWR5GHZ_BASE1 + i / 2, &val); + sc->txpow1[i + 14] = (int8_t)(val & 0xff); + sc->txpow1[i + 15] = (int8_t)(val >> 8); + + run_srom_read(sc, RT2860_EEPROM_PWR5GHZ_BASE2 + i / 2, &val); + sc->txpow2[i + 14] = (int8_t)(val & 0xff); + sc->txpow2[i + 15] = (int8_t)(val >> 8); + } + /* Fix broken Tx power entries. */ + for (i = 0; i < 40; i++ ) { + if (sc->mac_ver != 0x5592) { + if (sc->txpow1[14 + i] < -7 || sc->txpow1[14 + i] > 15) + sc->txpow1[14 + i] = 5; + if (sc->txpow2[14 + i] < -7 || sc->txpow2[14 + i] > 15) + sc->txpow2[14 + i] = 5; + } + RUN_DPRINTF(sc, RUN_DEBUG_TXPWR, + "chan %d: power1=%d, power2=%d\n", + rt2860_rf2850[14 + i].chan, sc->txpow1[14 + i], + sc->txpow2[14 + i]); + } +} + +static int +run_read_eeprom(struct run_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + int8_t delta_2ghz, delta_5ghz; + uint32_t tmp; + uint16_t val; + int ridx, ant, i; + + /* check whether the ROM is eFUSE ROM or EEPROM */ + sc->sc_srom_read = run_eeprom_read_2; + if (sc->mac_ver >= 0x3070) { + run_read(sc, RT3070_EFUSE_CTRL, &tmp); + RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EFUSE_CTRL=0x%08x\n", tmp); + if ((tmp & RT3070_SEL_EFUSE) || sc->mac_ver == 0x3593) + sc->sc_srom_read = run_efuse_read_2; + } + + /* read ROM version */ + run_srom_read(sc, RT2860_EEPROM_VERSION, &val); + RUN_DPRINTF(sc, RUN_DEBUG_ROM, + "EEPROM rev=%d, FAE=%d\n", val >> 8, val & 0xff); + + /* read MAC address */ + run_srom_read(sc, RT2860_EEPROM_MAC01, &val); + ic->ic_macaddr[0] = val & 0xff; + ic->ic_macaddr[1] = val >> 8; + run_srom_read(sc, RT2860_EEPROM_MAC23, &val); + ic->ic_macaddr[2] = val & 0xff; + ic->ic_macaddr[3] = val >> 8; + run_srom_read(sc, RT2860_EEPROM_MAC45, &val); + ic->ic_macaddr[4] = val & 0xff; + ic->ic_macaddr[5] = val >> 8; + + if (sc->mac_ver < 0x3593) { + /* read vender BBP settings */ + for (i = 0; i < 10; i++) { + run_srom_read(sc, RT2860_EEPROM_BBP_BASE + i, &val); + sc->bbp[i].val = val & 0xff; + sc->bbp[i].reg = val >> 8; + RUN_DPRINTF(sc, RUN_DEBUG_ROM, + "BBP%d=0x%02x\n", sc->bbp[i].reg, sc->bbp[i].val); + } + if (sc->mac_ver >= 0x3071) { + /* read vendor RF settings */ + for (i = 0; i < 10; i++) { + run_srom_read(sc, RT3071_EEPROM_RF_BASE + i, + &val); + sc->rf[i].val = val & 0xff; + sc->rf[i].reg = val >> 8; + RUN_DPRINTF(sc, RUN_DEBUG_ROM, "RF%d=0x%02x\n", + sc->rf[i].reg, sc->rf[i].val); + } + } + } + + /* read RF frequency offset from EEPROM */ + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_FREQ_LEDS : + RT3593_EEPROM_FREQ, &val); + sc->freq = ((val & 0xff) != 0xff) ? val & 0xff : 0; + RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM freq offset %d\n", + sc->freq & 0xff); + + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_FREQ_LEDS : + RT3593_EEPROM_FREQ_LEDS, &val); + if (val >> 8 != 0xff) { + /* read LEDs operating mode */ + sc->leds = val >> 8; + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED1 : + RT3593_EEPROM_LED1, &sc->led[0]); + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED2 : + RT3593_EEPROM_LED2, &sc->led[1]); + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LED3 : + RT3593_EEPROM_LED3, &sc->led[2]); + } else { + /* broken EEPROM, use default settings */ + sc->leds = 0x01; + sc->led[0] = 0x5555; + sc->led[1] = 0x2221; + sc->led[2] = 0x5627; /* differs from RT2860 */ + } + RUN_DPRINTF(sc, RUN_DEBUG_ROM, + "EEPROM LED mode=0x%02x, LEDs=0x%04x/0x%04x/0x%04x\n", + sc->leds, sc->led[0], sc->led[1], sc->led[2]); + + /* read RF information */ + if (sc->mac_ver == 0x5390 || sc->mac_ver ==0x5392) + run_srom_read(sc, 0x00, &val); + else + run_srom_read(sc, RT2860_EEPROM_ANTENNA, &val); + + if (val == 0xffff) { + device_printf(sc->sc_dev, + "invalid EEPROM antenna info, using default\n"); + if (sc->mac_ver == 0x3572) { + /* default to RF3052 2T2R */ + sc->rf_rev = RT3070_RF_3052; + sc->ntxchains = 2; + sc->nrxchains = 2; + } else if (sc->mac_ver >= 0x3070) { + /* default to RF3020 1T1R */ + sc->rf_rev = RT3070_RF_3020; + sc->ntxchains = 1; + sc->nrxchains = 1; + } else { + /* default to RF2820 1T2R */ + sc->rf_rev = RT2860_RF_2820; + sc->ntxchains = 1; + sc->nrxchains = 2; + } + } else { + if (sc->mac_ver == 0x5390 || sc->mac_ver ==0x5392) { + sc->rf_rev = val; + run_srom_read(sc, RT2860_EEPROM_ANTENNA, &val); + } else + sc->rf_rev = (val >> 8) & 0xf; + sc->ntxchains = (val >> 4) & 0xf; + sc->nrxchains = val & 0xf; + } + RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM RF rev=0x%04x chains=%dT%dR\n", + sc->rf_rev, sc->ntxchains, sc->nrxchains); + + /* check if RF supports automatic Tx access gain control */ + run_srom_read(sc, RT2860_EEPROM_CONFIG, &val); + RUN_DPRINTF(sc, RUN_DEBUG_ROM, "EEPROM CFG 0x%04x\n", val); + /* check if driver should patch the DAC issue */ + if ((val >> 8) != 0xff) + sc->patch_dac = (val >> 15) & 1; + 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 Tx power settings. */ + if (sc->mac_ver == 0x3593) + run_rt3593_get_txpower(sc); + else + run_get_txpower(sc); + + /* read Tx power compensation for each Tx rate */ + run_srom_read(sc, RT2860_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; + } + RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_TXPWR, + "power compensation=%d (2GHz), %d (5GHz)\n", delta_2ghz, delta_5ghz); + + for (ridx = 0; ridx < 5; ridx++) { + uint32_t reg; + + run_srom_read(sc, RT2860_EEPROM_RPWR + ridx * 2, &val); + reg = val; + run_srom_read(sc, RT2860_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); + + RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_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. */ + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI1_2GHZ : + RT3593_EEPROM_RSSI1_2GHZ, &val); + sc->rssi_2ghz[0] = val & 0xff; /* Ant A */ + sc->rssi_2ghz[1] = val >> 8; /* Ant B */ + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI2_2GHZ : + RT3593_EEPROM_RSSI2_2GHZ, &val); + if (sc->mac_ver >= 0x3070) { + if (sc->mac_ver == 0x3593) { + sc->txmixgain_2ghz = 0; + sc->rssi_2ghz[2] = val & 0xff; /* Ant C */ + } else { + /* + * 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; + } + RUN_DPRINTF(sc, RUN_DEBUG_ROM, "tx mixer gain=%u (2GHz)\n", + sc->txmixgain_2ghz); + } else + sc->rssi_2ghz[2] = val & 0xff; /* Ant C */ + if (sc->mac_ver == 0x3593) + run_srom_read(sc, RT3593_EEPROM_LNA_5GHZ, &val); + sc->lna[2] = val >> 8; /* channel group 2 */ + + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI1_5GHZ : + RT3593_EEPROM_RSSI1_5GHZ, &val); + sc->rssi_5ghz[0] = val & 0xff; /* Ant A */ + sc->rssi_5ghz[1] = val >> 8; /* Ant B */ + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_RSSI2_5GHZ : + RT3593_EEPROM_RSSI2_5GHZ, &val); + if (sc->mac_ver == 0x3572) { + /* + * On RT3572 chips (limited to 2 Rx chains), this ROM + * field contains the Tx mixer gain for the 5GHz band. + */ + if ((val & 0xff) != 0xff) + sc->txmixgain_5ghz = val & 0x7; + RUN_DPRINTF(sc, RUN_DEBUG_ROM, "tx mixer gain=%u (5GHz)\n", + sc->txmixgain_5ghz); + } else + sc->rssi_5ghz[2] = val & 0xff; /* Ant C */ + if (sc->mac_ver == 0x3593) { + sc->txmixgain_5ghz = 0; + run_srom_read(sc, RT3593_EEPROM_LNA_5GHZ, &val); + } + sc->lna[3] = val >> 8; /* channel group 3 */ + + run_srom_read(sc, (sc->mac_ver != 0x3593) ? RT2860_EEPROM_LNA : + RT3593_EEPROM_LNA, &val); + sc->lna[0] = val & 0xff; /* channel group 0 */ + sc->lna[1] = val >> 8; /* channel group 1 */ + + /* fix broken 5GHz LNA entries */ + if (sc->lna[2] == 0 || sc->lna[2] == 0xff) { + RUN_DPRINTF(sc, RUN_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) { + RUN_DPRINTF(sc, RUN_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) { + RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_RSSI, + "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) { + RUN_DPRINTF(sc, RUN_DEBUG_ROM | RUN_DEBUG_RSSI, + "invalid RSSI%d offset: %d (5GHz)\n", + ant + 1, sc->rssi_5ghz[ant]); + sc->rssi_5ghz[ant] = 0; + } + } + return (0); +} + +static struct ieee80211_node * +run_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + return malloc(sizeof (struct run_node), M_80211_NODE, + M_NOWAIT | M_ZERO); +} + +static int +run_media_change(struct ifnet *ifp) +{ + struct ieee80211vap *vap = ifp->if_softc; + struct ieee80211com *ic = vap->iv_ic; + const struct ieee80211_txparam *tp; + struct run_softc *sc = ic->ic_softc; + uint8_t rate, ridx; + int error; + + RUN_LOCK(sc); + + error = ieee80211_media_change(ifp); + if (error != 0) { + RUN_UNLOCK(sc); + return (error); + } + + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { + struct ieee80211_node *ni; + struct run_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 < RT2860_RIDX_MAX; ridx++) + if (rt2860_rates[ridx].rate == rate) + break; + + ni = ieee80211_ref_node(vap->iv_bss); + rn = RUN_NODE(ni); + rn->fix_ridx = ridx; + RUN_DPRINTF(sc, RUN_DEBUG_RATE, "rate=%d, fix_ridx=%d\n", + rate, rn->fix_ridx); + ieee80211_free_node(ni); + } + +#if 0 + if ((ifp->if_flags & IFF_UP) && + (ifp->if_drv_flags & RUN_RUNNING)){ + run_init_locked(sc); + } +#endif + + RUN_UNLOCK(sc); + + return (0); +} + +static int +run_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + const struct ieee80211_txparam *tp; + struct ieee80211com *ic = vap->iv_ic; + struct run_softc *sc = ic->ic_softc; + struct run_vap *rvp = RUN_VAP(vap); + enum ieee80211_state ostate; + uint32_t sta[3]; + uint8_t ratectl; + uint8_t restart_ratectl = 0; + uint8_t bid = 1 << rvp->rvp_id; + + ostate = vap->iv_state; + RUN_DPRINTF(sc, RUN_DEBUG_STATE, "%s -> %s\n", + ieee80211_state_name[ostate], + ieee80211_state_name[nstate]); + + IEEE80211_UNLOCK(ic); + RUN_LOCK(sc); + + ratectl = sc->ratectl_run; /* remember current state */ + sc->ratectl_run = RUN_RATECTL_OFF; + usb_callout_stop(&sc->ratectl_ch); + + if (ostate == IEEE80211_S_RUN) { + /* turn link LED off */ + run_set_leds(sc, RT2860_LED_RADIO); + } + + 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) + run_disable_tsf(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; + run_update_beacon_cb(vap); + break; + case IEEE80211_M_IBSS: + sc->adhoc_running |= bid; + if (!sc->ap_running) + ic->ic_opmode = vap->iv_opmode; + run_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) */ + run_read_region_1(sc, RT2860_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) { + RUN_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (-1); + } + run_updateslot(ic); + run_enable_mrr(sc); + run_set_txpreamble(sc); + run_set_basicrates(sc); + ni = ieee80211_ref_node(vap->iv_bss); + IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); + run_set_bssid(sc, sc->sc_bssid); + ieee80211_free_node(ni); + run_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 + run_enable_tsf(sc); + + /* turn link LED on */ + run_set_leds(sc, RT2860_LED_RADIO | + (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan) ? + RT2860_LED_LINK_2GHZ : RT2860_LED_LINK_5GHZ)); + + break; + default: + RUN_DPRINTF(sc, RUN_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, run_ratectl_to, sc); + + RUN_UNLOCK(sc); + IEEE80211_LOCK(ic); + + return(rvp->newstate(vap, nstate, arg)); +} + +static int +run_wme_update(struct ieee80211com *ic) +{ + struct chanAccParams chp; + struct run_softc *sc = ic->ic_softc; + const struct wmeParams *ac; + int aci, error = 0; + + ieee80211_wme_ic_getparams(ic, &chp); + ac = chp.cap_wmeParams; + + /* update MAC TX configuration registers */ + RUN_LOCK(sc); + for (aci = 0; aci < WME_NUM_AC; aci++) { + error = run_write(sc, RT2860_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 = run_write(sc, RT2860_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 = run_write(sc, RT2860_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 = run_write(sc, RT2860_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 = run_write(sc, RT2860_WMM_TXOP0_CFG, + ac[WME_AC_BK].wmep_txopLimit << 16 | + ac[WME_AC_BE].wmep_txopLimit); + if (error) goto err; + error = run_write(sc, RT2860_WMM_TXOP1_CFG, + ac[WME_AC_VO].wmep_txopLimit << 16 | + ac[WME_AC_VI].wmep_txopLimit); + +err: + RUN_UNLOCK(sc); + if (error) + RUN_DPRINTF(sc, RUN_DEBUG_USB, "WME update failed\n"); + + return (error); +} + +static void +run_key_set_cb(void *arg) +{ + struct run_cmdq *cmdq = arg; + struct ieee80211vap *vap = cmdq->arg1; + struct ieee80211_key *k = cmdq->k; + struct ieee80211com *ic = vap->iv_ic; + struct run_softc *sc = ic->ic_softc; + struct ieee80211_node *ni; + u_int cipher = k->wk_cipher->ic_cipher; + uint32_t attr; + uint16_t base, associd; + uint8_t mode, wcid, iv[8]; + + RUN_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; + associd = (ni != NULL) ? ni->ni_associd : 0; + + /* map net80211 cipher to RT2860 security mode */ + switch (cipher) { + case IEEE80211_CIPHER_WEP: + if(k->wk_keylen < 8) + mode = RT2860_MODE_WEP40; + else + mode = RT2860_MODE_WEP104; + break; + case IEEE80211_CIPHER_TKIP: + mode = RT2860_MODE_TKIP; + break; + case IEEE80211_CIPHER_AES_CCM: + mode = RT2860_MODE_AES_CCMP; + break; + default: + RUN_DPRINTF(sc, RUN_DEBUG_KEY, "undefined case\n"); + return; + } + + RUN_DPRINTF(sc, RUN_DEBUG_KEY, + "associd=%x, keyix=%d, mode=%x, type=%s, tx=%s, rx=%s\n", + associd, k->wk_keyix, mode, + (k->wk_flags & IEEE80211_KEY_GROUP) ? "group" : "pairwise", + (k->wk_flags & IEEE80211_KEY_XMIT) ? "on" : "off", + (k->wk_flags & IEEE80211_KEY_RECV) ? "on" : "off"); + + if (k->wk_flags & IEEE80211_KEY_GROUP) { + wcid = 0; /* NB: update WCID0 for group keys */ + base = RT2860_SKEY(RUN_VAP(vap)->rvp_id, k->wk_keyix); + } else { + wcid = (vap->iv_opmode == IEEE80211_M_STA) ? + 1 : RUN_AID2WCID(associd); + base = RT2860_PKEY(wcid); + } + + if (cipher == IEEE80211_CIPHER_TKIP) { + if(run_write_region_1(sc, base, k->wk_key, 16)) + return; + if(run_write_region_1(sc, base + 16, &k->wk_key[16], 8)) /* wk_txmic */ + return; + if(run_write_region_1(sc, base + 24, &k->wk_key[24], 8)) /* wk_rxmic */ + return; + } else { + /* roundup len to 16-bit: XXX fix write_region_1() instead */ + if(run_write_region_1(sc, base, k->wk_key, (k->wk_keylen + 1) & ~1)) + return; + } + + 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; + } + if (run_write_region_1(sc, RT2860_IVEIV(wcid), iv, 8)) + return; + } + + if (k->wk_flags & IEEE80211_KEY_GROUP) { + /* install group key */ + if (run_read(sc, RT2860_SKEY_MODE_0_7, &attr)) + return; + attr &= ~(0xf << (k->wk_keyix * 4)); + attr |= mode << (k->wk_keyix * 4); + if (run_write(sc, RT2860_SKEY_MODE_0_7, attr)) + return; + } else { + /* install pairwise key */ + if (run_read(sc, RT2860_WCID_ATTR(wcid), &attr)) + return; + attr = (attr & ~0xf) | (mode << 1) | RT2860_RX_PKEY_EN; + if (run_write(sc, RT2860_WCID_ATTR(wcid), attr)) + return; + } + + /* TODO create a pass-thru key entry? */ + + /* need wcid to delete the right key later */ + k->wk_pad = wcid; +} + +/* + * Don't have to be deferred, but in order to keep order of + * execution, i.e. with run_key_delete(), defer this and let + * run_cmdq_cb() maintain the order. + * + * return 0 on error + */ +static int +run_key_set(struct ieee80211vap *vap, struct ieee80211_key *k) +{ + struct ieee80211com *ic = vap->iv_ic; + struct run_softc *sc = ic->ic_softc; + uint32_t i; + + i = RUN_CMDQ_GET(&sc->cmdq_store); + RUN_DPRINTF(sc, RUN_DEBUG_KEY, "cmdq_store=%d\n", i); + sc->cmdq[i].func = run_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) { + RUN_LOCK(sc); + sc->cmdq_key_set = RUN_CMDQ_GO; + RUN_UNLOCK(sc); + } + + return (1); +} + +/* + * 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 +run_key_delete_cb(void *arg) +{ + struct run_cmdq *cmdq = arg; + struct run_softc *sc = cmdq->arg1; + struct ieee80211_key *k = &cmdq->key; + uint32_t attr; + uint8_t wcid; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + if (k->wk_flags & IEEE80211_KEY_GROUP) { + /* remove group key */ + RUN_DPRINTF(sc, RUN_DEBUG_KEY, "removing group key\n"); + run_read(sc, RT2860_SKEY_MODE_0_7, &attr); + attr &= ~(0xf << (k->wk_keyix * 4)); + run_write(sc, RT2860_SKEY_MODE_0_7, attr); + } else { + /* remove pairwise key */ + RUN_DPRINTF(sc, RUN_DEBUG_KEY, + "removing key for wcid %x\n", k->wk_pad); + /* matching wcid was written to wk_pad in run_key_set() */ + wcid = k->wk_pad; + run_read(sc, RT2860_WCID_ATTR(wcid), &attr); + attr &= ~0xf; + run_write(sc, RT2860_WCID_ATTR(wcid), attr); + run_set_region_4(sc, RT2860_WCID_ENTRY(wcid), 0, 8); + } + + k->wk_pad = 0; +} + +/* + * return 0 on error + */ +static int +run_key_delete(struct ieee80211vap *vap, struct ieee80211_key *k) +{ + struct ieee80211com *ic = vap->iv_ic; + struct run_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 = RUN_CMDQ_GET(&sc->cmdq_store); + RUN_DPRINTF(sc, RUN_DEBUG_KEY, "cmdq_store=%d\n", i); + sc->cmdq[i].func = run_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 run_key_set() */ + k0->wk_pad = k->wk_pad; + ieee80211_runtask(ic, &sc->cmdq_task); + return (1); /* return fake success */ + +} + +static void +run_ratectl_to(void *arg) +{ + struct run_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 +run_ratectl_cb(void *arg, int pending) +{ + struct run_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + if (vap == NULL) + return; + + if (sc->rvp_cnt > 1 || vap->iv_opmode != IEEE80211_M_STA) { + /* + * run_reset_livelock() doesn't do anything with AMRR, + * but Ralink wants us to call it every 1 sec. So, we + * piggyback here rather than creating another callout. + * Livelock may occur only in HOSTAP or IBSS mode + * (when h/w is sending beacons). + */ + RUN_LOCK(sc); + run_reset_livelock(sc); + /* just in case, there are some stats to drain */ + run_drain_fifo(sc); + RUN_UNLOCK(sc); + } + + ieee80211_iterate_nodes(&ic->ic_sta, run_iter_func, sc); + + RUN_LOCK(sc); + if(sc->ratectl_run != RUN_RATECTL_OFF) + usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc); + RUN_UNLOCK(sc); +} + +static void +run_drain_fifo(void *arg) +{ + struct run_softc *sc = arg; + uint32_t stat; + uint16_t (*wstat)[3]; + uint8_t wcid, mcs, pid; + int8_t retry; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + for (;;) { + /* drain Tx status FIFO (maxsize = 16) */ + run_read(sc, RT2860_TX_STAT_FIFO, &stat); + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "tx stat 0x%08x\n", stat); + if (!(stat & RT2860_TXQ_VLD)) + break; + + wcid = (stat >> RT2860_TXQ_WCID_SHIFT) & 0xff; + + /* if no ACK was requested, no feedback is available */ + if (!(stat & RT2860_TXQ_ACKREQ) || wcid > RT2870_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)[RUN_TXCNT]++; + if (stat & RT2860_TXQ_OK) + (*wstat)[RUN_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 >> RT2860_TXQ_MCS_SHIFT) & 0x7f; + pid = (stat >> RT2860_TXQ_PID_SHIFT) & 0xf; + if ((retry = pid -1 - mcs) > 0) { + (*wstat)[RUN_TXCNT] += retry; + (*wstat)[RUN_RETRY] += retry; + } + } + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "count=%d\n", sc->fifo_cnt); + + sc->fifo_cnt = 0; +} + +static void +run_iter_func(void *arg, struct ieee80211_node *ni) +{ + struct run_softc *sc = arg; + struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs; + struct ieee80211vap *vap = ni->ni_vap; + struct run_node *rn = RUN_NODE(ni); + union run_stats sta[2]; + uint16_t (*wstat)[3]; + int error, ridx; + + RUN_LOCK(sc); + + /* 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 = run_read_region_1(sc, RT2860_TX_STA_CNT0, (uint8_t *)sta, + sizeof sta); + if (error != 0) + goto fail; + + /* count failed TX as errors */ + if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, + le16toh(sta[0].error.fail)); + + txs->nretries = le16toh(sta[1].tx.retry); + txs->nsuccess = le16toh(sta[1].tx.success); + /* nretries??? */ + txs->nframes = txs->nretries + txs->nsuccess + + le16toh(sta[0].error.fail); + + RUN_DPRINTF(sc, RUN_DEBUG_RATE, + "retrycnt=%d success=%d failcnt=%d\n", + txs->nretries, txs->nsuccess, le16toh(sta[0].error.fail)); + } else { + wstat = &(sc->wcid_stats[RUN_AID2WCID(ni->ni_associd)]); + + if (wstat == &(sc->wcid_stats[0]) || + wstat > &(sc->wcid_stats[RT2870_WCID_MAX])) + goto fail; + + txs->nretries = (*wstat)[RUN_RETRY]; + txs->nsuccess = (*wstat)[RUN_SUCCESS]; + txs->nframes = (*wstat)[RUN_TXCNT]; + RUN_DPRINTF(sc, RUN_DEBUG_RATE, + "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 < RT2860_RIDX_MAX; ridx++) + if (rt2860_rates[ridx].rate == ni->ni_txrate) + break; + rn->amrr_ridx = ridx; + +fail: + RUN_UNLOCK(sc); + + RUN_DPRINTF(sc, RUN_DEBUG_RATE, "rate=%d, ridx=%d\n", ni->ni_txrate, rn->amrr_ridx); +} + +static void +run_newassoc_cb(void *arg) +{ + struct run_cmdq *cmdq = arg; + struct ieee80211_node *ni = cmdq->arg1; + struct run_softc *sc = ni->ni_vap->iv_ic->ic_softc; + uint8_t wcid = cmdq->wcid; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + run_write_region_1(sc, RT2860_WCID_ENTRY(wcid), + ni->ni_macaddr, IEEE80211_ADDR_LEN); + + memset(&(sc->wcid_stats[wcid]), 0, sizeof(sc->wcid_stats[wcid])); +} + +static void +run_newassoc(struct ieee80211_node *ni, int isnew) +{ + struct run_node *rn = RUN_NODE(ni); + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = vap->iv_ic; + struct run_softc *sc = ic->ic_softc; + uint8_t rate; + uint8_t ridx; + uint8_t wcid; + + wcid = (vap->iv_opmode == IEEE80211_M_STA) ? + 1 : RUN_AID2WCID(ni->ni_associd); + + if (wcid > RT2870_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 defer. + */ + uint32_t cnt = RUN_CMDQ_GET(&sc->cmdq_store); + RUN_DPRINTF(sc, RUN_DEBUG_STATE, "cmdq_store=%d\n", cnt); + sc->cmdq[cnt].func = run_newassoc_cb; + sc->cmdq[cnt].arg0 = NULL; + sc->cmdq[cnt].arg1 = ni; + sc->cmdq[cnt].wcid = wcid; + ieee80211_runtask(ic, &sc->cmdq_task); + } + + RUN_DPRINTF(sc, RUN_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 < RT2860_RIDX_MAX; ridx++) + if (rt2860_rates[ridx].rate == rate) + break; + rn->mgt_ridx = ridx; + RUN_DPRINTF(sc, RUN_DEBUG_STATE | RUN_DEBUG_RATE, + "rate=%d, mgmt_ridx=%d\n", rate, rn->mgt_ridx); + + RUN_LOCK(sc); + if(sc->ratectl_run != RUN_RATECTL_OFF) + usb_callout_reset(&sc->ratectl_ch, hz, run_ratectl_to, sc); + RUN_UNLOCK(sc); +} + +/* + * Return the Rx chain with the highest RSSI for a given frame. + */ +static __inline uint8_t +run_maxrssi_chain(struct run_softc *sc, const struct rt2860_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 +run_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 run_softc *sc = vap->iv_ic->ic_softc; + struct run_vap *rvp = RUN_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); + RUN_LOCK(sc); + run_get_tsf(sc, &rx_tstamp); + RUN_UNLOCK(sc); + rx_tstamp = le64toh(rx_tstamp); + + if (ni_tstamp >= rx_tstamp) { + RUN_DPRINTF(sc, RUN_DEBUG_RECV | RUN_DEBUG_BEACON, + "ibss merge, tsf %ju tstamp %ju\n", + (uintmax_t)rx_tstamp, (uintmax_t)ni_tstamp); + (void) ieee80211_ibss_merge(ni); + } + } +} + +static void +run_rx_frame(struct run_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 rt2870_rxd *rxd; + struct rt2860_rxwi *rxwi; + uint32_t flags; + uint16_t len, rxwisize; + uint8_t ant, rssi; + int8_t nf; + + rxwisize = sizeof(struct rt2860_rxwi); + if (sc->mac_ver == 0x5592) + rxwisize += sizeof(uint64_t); + else if (sc->mac_ver == 0x3593) + rxwisize += sizeof(uint32_t); + + if (__predict_false(dmalen < + rxwisize + sizeof(struct ieee80211_frame_ack))) { + RUN_DPRINTF(sc, RUN_DEBUG_RECV, + "payload is too short: dma length %u < %zu\n", + dmalen, rxwisize + sizeof(struct ieee80211_frame_ack)); + goto fail; + } + + rxwi = mtod(m, struct rt2860_rxwi *); + len = le16toh(rxwi->len) & 0xfff; + + if (__predict_false(len > dmalen - rxwisize)) { + RUN_DPRINTF(sc, RUN_DEBUG_RECV, + "bad RXWI length %u > %u\n", len, dmalen); + goto fail; + } + + /* Rx descriptor is located at the end */ + rxd = (struct rt2870_rxd *)(mtod(m, caddr_t) + dmalen); + flags = le32toh(rxd->flags); + + if (__predict_false(flags & (RT2860_RX_CRCERR | RT2860_RX_ICVERR))) { + RUN_DPRINTF(sc, RUN_DEBUG_RECV, "%s error.\n", + (flags & RT2860_RX_CRCERR)?"CRC":"ICV"); + goto fail; + } + + if (flags & RT2860_RX_L2PAD) { + RUN_DPRINTF(sc, RUN_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) != 0 && + (flags & RT2860_RX_DEC) != 0) { + 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 & RT2860_RX_MICERR)) { + /* report MIC failures to net80211 for TKIP */ + if (ni != NULL) + ieee80211_notify_michael_failure(ni->ni_vap, wh, + rxwi->keyidx); + RUN_DPRINTF(sc, RUN_DEBUG_RECV, + "MIC error. Someone is lying.\n"); + goto fail; + } + + ant = run_maxrssi_chain(sc, rxwi); + rssi = rxwi->rssi[ant]; + nf = run_rssi2dbm(sc, rssi, ant); + + if (__predict_false(ieee80211_radiotap_active(ic))) { + struct run_rx_radiotap_header *tap = &sc->sc_rxtap; + uint16_t phy; + + tap->wr_flags = 0; + if (flags & RT2860_RX_L2PAD) + tap->wr_flags |= IEEE80211_RADIOTAP_F_DATAPAD; + tap->wr_antsignal = rssi; + tap->wr_antenna = ant; + tap->wr_dbm_antsignal = run_rssi2dbm(sc, rssi, ant); + tap->wr_rate = 2; /* in case it can't be found below */ + RUN_LOCK(sc); + run_get_tsf(sc, &tap->wr_tsf); + RUN_UNLOCK(sc); + phy = le16toh(rxwi->phy); + switch (phy & RT2860_PHY_MODE) { + case RT2860_PHY_CCK: + switch ((phy & RT2860_PHY_MCS) & ~RT2860_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 & RT2860_PHY_SHPRE) + tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + break; + case RT2860_PHY_OFDM: + switch (phy & RT2860_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 +run_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct run_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 rt2860_rxwi); + if (sc->mac_ver == 0x5592) + rxwisize += sizeof(uint64_t); + else if (sc->mac_ver == 0x3593) + rxwisize += sizeof(uint32_t); + + usbd_xfer_status(xfer, &xferlen, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + RUN_DPRINTF(sc, RUN_DEBUG_RECV, + "rx done, actlen=%d\n", xferlen); + + if (xferlen < (int)(sizeof(uint32_t) + rxwisize + + sizeof(struct rt2870_rxd))) { + RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB, + "xfer too short %d\n", xferlen); + 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, + RUN_MAX_RXSZ); + } + if (sc->rx_m == NULL) { + RUN_DPRINTF(sc, RUN_DEBUG_RECV | + RUN_DEBUG_RECV_DESC | RUN_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), RUN_MAX_RXSZ); + usbd_xfer_set_frames(xfer, 1); + } + usbd_transfer_submit(xfer); + break; + + default: /* 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\n"); + 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 */ + + RUN_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)) { + RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB, + "bad DMA length %u\n", dmalen); + break; + } + if ((dmalen + 8) > (uint32_t)xferlen) { + RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_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; + run_rx_frame(sc, m, dmalen); + m = NULL; /* don't free source buffer */ + break; + } + + mbuf_len = dmalen + sizeof(struct rt2870_rxd); + if (__predict_false(mbuf_len > MCLBYTES)) { + RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_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)) { + RUN_DPRINTF(sc, RUN_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; + run_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 + RUN_LOCK(sc); +} + +static void +run_tx_free(struct run_endpoint_queue *pq, + struct run_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 +run_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error, u_int index) +{ + struct run_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct run_tx_data *data; + struct ieee80211vap *vap = NULL; + struct usb_page_cache *pc; + struct run_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: + RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB, + "transfer complete: %d bytes @ index %d\n", actlen, index); + + data = usbd_xfer_get_priv(xfer); + run_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 = (sc->mac_ver == 0x5592) ? + sizeof(data->desc) + sizeof(uint32_t) : sizeof(data->desc); + if ((m->m_pkthdr.len + + size + 3 + 8) > RUN_MAX_TXSZ) { + RUN_DPRINTF(sc, RUN_DEBUG_XMIT_DESC | RUN_DEBUG_USB, + "data overflow, %u bytes\n", m->m_pkthdr.len); + run_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) + 8); + size += ((-size) & 3) + 8; + + vap = data->ni->ni_vap; + if (ieee80211_radiotap_active_vap(vap)) { + const struct ieee80211_frame *wh; + struct run_tx_radiotap_header *tap = &sc->sc_txtap; + struct rt2860_txwi *txwi = + (struct rt2860_txwi *)(&data->desc + sizeof(struct rt2870_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) & RT2860_PHY_SHPRE) + tap->wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + if (has_l2pad) + tap->wt_flags |= IEEE80211_RADIOTAP_F_DATAPAD; + + ieee80211_radiotap_tx(vap, m); + } + + RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_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); + run_start(sc); + + break; + + default: + RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_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; + run_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\n"); + uint32_t i = RUN_CMDQ_GET(&sc->cmdq_store); + RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_USB, + "cmdq_store=%d\n", i); + sc->cmdq[i].func = run_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 +run_bulk_tx_callback0(struct usb_xfer *xfer, usb_error_t error) +{ + run_bulk_tx_callbackN(xfer, error, 0); +} + +static void +run_bulk_tx_callback1(struct usb_xfer *xfer, usb_error_t error) +{ + run_bulk_tx_callbackN(xfer, error, 1); +} + +static void +run_bulk_tx_callback2(struct usb_xfer *xfer, usb_error_t error) +{ + run_bulk_tx_callbackN(xfer, error, 2); +} + +static void +run_bulk_tx_callback3(struct usb_xfer *xfer, usb_error_t error) +{ + run_bulk_tx_callbackN(xfer, error, 3); +} + +static void +run_bulk_tx_callback4(struct usb_xfer *xfer, usb_error_t error) +{ + run_bulk_tx_callbackN(xfer, error, 4); +} + +static void +run_bulk_tx_callback5(struct usb_xfer *xfer, usb_error_t error) +{ + run_bulk_tx_callbackN(xfer, error, 5); +} + +static void +run_set_tx_desc(struct run_softc *sc, struct run_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 rt2870_txd *txd; + struct rt2860_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 = (sc->mac_ver == 0x5592) ? + sizeof(*txwi) + sizeof(uint32_t) : sizeof(*txwi); + xferlen = txwisize + m->m_pkthdr.len; + + /* roundup to 32-bit alignment */ + xferlen = (xferlen + 3) & ~3; + + txd = (struct rt2870_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 rt2860_txwi *)(txd + 1); + txwi->len = htole16(m->m_pkthdr.len - pad); + if (rt2860_rates[ridx].phy == IEEE80211_T_DS) { + mcs |= RT2860_PHY_CCK; + if (ridx != RT2860_RIDX_CCK1 && + (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + mcs |= RT2860_PHY_SHPRE; + } else if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM) { + mcs |= RT2860_PHY_OFDM; + } else if (rt2860_rates[ridx].phy == IEEE80211_T_HT) { + /* XXX TODO: [adrian] set short preamble for MCS? */ + mcs |= RT2860_PHY_HT_MIX; /* 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 |= RT2860_TX_TXOP_HT; + else + txwi->txop |= RT2860_TX_TXOP_BACKOFF; + + if (vap->iv_opmode != IEEE80211_M_STA && !IEEE80211_QOS_HAS_SEQ(wh)) + txwi->xflags |= RT2860_TX_NSEQ; +} + +/* This function must be called locked */ +static int +run_tx(struct run_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 run_node *rn = RUN_NODE(ni); + struct run_tx_data *data; + struct rt2870_txd *txd; + struct rt2860_txwi *txwi; + uint16_t qos; + uint16_t dur; + uint16_t qid; + uint8_t type; + uint8_t tid; + uint8_t ridx; + uint8_t ctl_ridx; + uint8_t qflags; + uint8_t xflags = 0; + int hasqos; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + wh = mtod(m, struct ieee80211_frame *); + + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + /* + * There are 7 bulk endpoints: 1 for RX + * and 6 for TX (4 EDCAs + HCCA + Prio). + * Update 03-14-2009: some devices like the Planex GW-US300MiniS + * seem to have only 4 TX bulk endpoints (Fukaumi Naoki). + */ + if ((hasqos = IEEE80211_QOS_HAS_SEQ(wh))) { + uint8_t *frm; + + frm = ieee80211_getqos(wh); + qos = le16toh(*(const uint16_t *)frm); + tid = qos & IEEE80211_QOS_TID; + qid = TID_TO_WME_AC(tid); + } else { + qos = 0; + tid = 0; + qid = WME_AC_BE; + } + qflags = (qid < 4) ? RT2860_TX_QSEL_EDCA : RT2860_TX_QSEL_HCCA; + + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "qos %d\tqid %d\ttid %d\tqflags %x\n", + qos, qid, tid, qflags); + + /* pickup a rate index */ + 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) ? + RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1; + 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 (!IEEE80211_IS_MULTICAST(wh->i_addr1) && + (!hasqos || (qos & IEEE80211_QOS_ACKPOLICY) != + IEEE80211_QOS_ACKPOLICY_NOACK)) { + xflags |= RT2860_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; + *(uint16_t *)wh->i_dur = htole16(dur); + } + + /* reserve slots for mgmt packets, just in case */ + if (sc->sc_epq[qid].tx_nfree < 3) { + RUN_DPRINTF(sc, RUN_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 rt2870_txd *)&data->desc; + txd->flags = qflags; + txwi = (struct rt2860_txwi *)(txd + 1); + txwi->xflags = xflags; + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + txwi->wcid = 0; + else + txwi->wcid = (vap->iv_opmode == IEEE80211_M_STA) ? + 1 : RUN_AID2WCID(ni->ni_associd); + + /* clear leftover garbage bits */ + txwi->flags = 0; + txwi->txop = 0; + + data->m = m; + data->ni = ni; + data->ridx = ridx; + + run_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) { + uint16_t pid = (rt2860_rates[ridx].mcs + 1) & 0xf; + txwi->len |= htole16(pid << RT2860_TX_PID_SHIFT); + + /* + * 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 = RUN_CMDQ_GET(&sc->cmdq_store); + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "cmdq_store=%d\n", i); + sc->cmdq[i].func = run_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[qid]); + + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, + "sending data frame len=%d rate=%d qid=%d\n", + m->m_pkthdr.len + (int)(sizeof(struct rt2870_txd) + + sizeof(struct rt2860_txwi)), rt2860_rates[ridx].rate, qid); + + return (0); +} + +static int +run_tx_mgt(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct run_node *rn = RUN_NODE(ni); + struct run_tx_data *data; + struct ieee80211_frame *wh; + struct rt2870_txd *txd; + struct rt2860_txwi *txwi; + uint16_t dur; + uint8_t ridx = rn->mgt_ridx; + uint8_t xflags = 0; + uint8_t wflags = 0; + + RUN_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 |= RT2860_TX_TS; + else if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + xflags |= RT2860_TX_ACK; + + dur = ieee80211_ack_duration(ic->ic_rt, rt2860_rates[ridx].rate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + *(uint16_t *)wh->i_dur = htole16(dur); + } + + 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 rt2870_txd *)&data->desc; + txd->flags = RT2860_TX_QSEL_EDCA; + txwi = (struct rt2860_txwi *)(txd + 1); + txwi->wcid = 0xff; + txwi->flags = wflags; + txwi->xflags = xflags; + txwi->txop = 0; /* clear leftover garbage bits */ + + data->m = m; + data->ni = ni; + data->ridx = ridx; + + run_set_tx_desc(sc, data); + + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "sending mgt frame len=%d rate=%d\n", + m->m_pkthdr.len + (int)(sizeof(struct rt2870_txd) + + sizeof(struct rt2860_txwi)), rt2860_rates[ridx].rate); + + STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); + + usbd_transfer_start(sc->sc_xfer[0]); + + return (0); +} + +static int +run_sendprot(struct run_softc *sc, + const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) +{ + struct ieee80211com *ic = ni->ni_ic; + struct run_tx_data *data; + struct rt2870_txd *txd; + struct rt2860_txwi *txwi; + struct mbuf *mprot; + int ridx; + int protrate; + uint8_t wflags = 0; + uint8_t xflags = 0; + + RUN_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); + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "could not allocate mbuf\n"); + return (ENOBUFS); + } + + protrate = ieee80211_ctl_rate(ic->ic_rt, rate); + wflags = RT2860_TX_FRAG; + xflags = 0; + if (prot == IEEE80211_PROT_RTSCTS) + xflags |= RT2860_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 rt2870_txd *)&data->desc; + txd->flags = RT2860_TX_QSEL_EDCA; + txwi = (struct rt2860_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 < RT2860_RIDX_MAX; ridx++) + if (rt2860_rates[ridx].rate == protrate) + break; + data->ridx = ridx; + + run_set_tx_desc(sc, data); + + RUN_DPRINTF(sc, RUN_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 +run_tx_param(struct run_softc *sc, struct mbuf *m, struct ieee80211_node *ni, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct run_tx_data *data; + struct rt2870_txd *txd; + struct rt2860_txwi *txwi; + uint8_t ridx; + uint8_t rate; + uint8_t opflags = 0; + uint8_t xflags = 0; + int error; + + RUN_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 |= RT2860_TX_ACK; + if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { + error = run_sendprot(sc, m, ni, + params->ibp_flags & IEEE80211_BPF_RTS ? + IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, + rate); + if (error) { + /* let caller free mbuf */ + return error; + } + opflags |= /*XXX RT2573_TX_LONG_RETRY |*/ RT2860_TX_TXOP_SIFS; + } + + if (sc->sc_epq[0].tx_nfree == 0) { + /* let caller free mbuf */ + RUN_DPRINTF(sc, RUN_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 rt2870_txd *)&data->desc; + txd->flags = RT2860_TX_QSEL_EDCA; + txwi = (struct rt2860_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 < RT2860_RIDX_MAX; ridx++) + if (rt2860_rates[ridx].rate == rate) + break; + data->ridx = ridx; + + run_set_tx_desc(sc, data); + + RUN_DPRINTF(sc, RUN_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[0]); + + return (0); +} + +static int +run_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct run_softc *sc = ni->ni_ic->ic_softc; + int error = 0; + + RUN_LOCK(sc); + + /* prevent management frames from being sent if we're not ready */ + if (!(sc->sc_flags & RUN_RUNNING)) { + error = ENETDOWN; + goto done; + } + + if (params == NULL) { + /* tx mgt packet */ + if ((error = run_tx_mgt(sc, m, ni)) != 0) { + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "mgt tx failed\n"); + goto done; + } + } else { + /* tx raw packet with param */ + if ((error = run_tx_param(sc, m, ni, params)) != 0) { + RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "tx with param failed\n"); + goto done; + } + } + +done: + RUN_UNLOCK(sc); + + if (error != 0) { + if(m != NULL) + m_freem(m); + } + + return (error); +} + +static int +run_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct run_softc *sc = ic->ic_softc; + int error; + + RUN_LOCK(sc); + if ((sc->sc_flags & RUN_RUNNING) == 0) { + RUN_UNLOCK(sc); + return (ENXIO); + } + error = mbufq_enqueue(&sc->sc_snd, m); + if (error) { + RUN_UNLOCK(sc); + return (error); + } + run_start(sc); + RUN_UNLOCK(sc); + + return (0); +} + +static void +run_start(struct run_softc *sc) +{ + struct ieee80211_node *ni; + struct mbuf *m; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + if ((sc->sc_flags & RUN_RUNNING) == 0) + return; + + while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + if (run_tx(sc, m, ni) != 0) { + mbufq_prepend(&sc->sc_snd, m); + break; + } + } +} + +static void +run_parent(struct ieee80211com *ic) +{ + struct run_softc *sc = ic->ic_softc; + int startall = 0; + + RUN_LOCK(sc); + if (sc->sc_detached) { + RUN_UNLOCK(sc); + return; + } + + if (ic->ic_nrunning > 0) { + if (!(sc->sc_flags & RUN_RUNNING)) { + startall = 1; + run_init_locked(sc); + } else + run_update_promisc_locked(sc); + } else if ((sc->sc_flags & RUN_RUNNING) && sc->rvp_cnt <= 1) + run_stop(sc); + RUN_UNLOCK(sc); + if (startall) + ieee80211_start_all(ic); +} + +static void +run_iq_calib(struct run_softc *sc, u_int chan) +{ + uint16_t val; + + /* Tx0 IQ gain. */ + run_bbp_write(sc, 158, 0x2c); + if (chan <= 14) + run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX0_2GHZ, &val, 1); + else if (chan <= 64) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5GHZ, + &val, 1); + } else if (chan <= 138) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5GHZ, + &val, 1); + } else if (chan <= 165) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5GHZ, + &val, 1); + } else + val = 0; + run_bbp_write(sc, 159, val); + + /* Tx0 IQ phase. */ + run_bbp_write(sc, 158, 0x2d); + if (chan <= 14) { + run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX0_2GHZ, + &val, 1); + } else if (chan <= 64) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5GHZ, + &val, 1); + } else if (chan <= 138) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5GHZ, + &val, 1); + } else if (chan <= 165) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5GHZ, + &val, 1); + } else + val = 0; + run_bbp_write(sc, 159, val); + + /* Tx1 IQ gain. */ + run_bbp_write(sc, 158, 0x4a); + if (chan <= 14) { + run_efuse_read(sc, RT5390_EEPROM_IQ_GAIN_CAL_TX1_2GHZ, + &val, 1); + } else if (chan <= 64) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5GHZ, + &val, 1); + } else if (chan <= 138) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5GHZ, + &val, 1); + } else if (chan <= 165) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5GHZ, + &val, 1); + } else + val = 0; + run_bbp_write(sc, 159, val); + + /* Tx1 IQ phase. */ + run_bbp_write(sc, 158, 0x4b); + if (chan <= 14) { + run_efuse_read(sc, RT5390_EEPROM_IQ_PHASE_CAL_TX1_2GHZ, + &val, 1); + } else if (chan <= 64) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5GHZ, + &val, 1); + } else if (chan <= 138) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5GHZ, + &val, 1); + } else if (chan <= 165) { + run_efuse_read(sc, + RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5GHZ, + &val, 1); + } else + val = 0; + run_bbp_write(sc, 159, val); + + /* RF IQ compensation control. */ + run_bbp_write(sc, 158, 0x04); + run_efuse_read(sc, RT5390_EEPROM_RF_IQ_COMPENSATION_CTL, + &val, 1); + run_bbp_write(sc, 159, val); + + /* RF IQ imbalance compensation control. */ + run_bbp_write(sc, 158, 0x03); + run_efuse_read(sc, + RT5390_EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CTL, &val, 1); + run_bbp_write(sc, 159, val); +} + +static void +run_set_agc(struct run_softc *sc, uint8_t agc) +{ + uint8_t bbp; + + if (sc->mac_ver == 0x3572) { + run_bbp_read(sc, 27, &bbp); + bbp &= ~(0x3 << 5); + run_bbp_write(sc, 27, bbp | 0 << 5); /* select Rx0 */ + run_bbp_write(sc, 66, agc); + run_bbp_write(sc, 27, bbp | 1 << 5); /* select Rx1 */ + run_bbp_write(sc, 66, agc); + } else + run_bbp_write(sc, 66, agc); +} + +static void +run_select_chan_group(struct run_softc *sc, int group) +{ + uint32_t tmp; + uint8_t agc; + + run_bbp_write(sc, 62, 0x37 - sc->lna[group]); + run_bbp_write(sc, 63, 0x37 - sc->lna[group]); + run_bbp_write(sc, 64, 0x37 - sc->lna[group]); + if (sc->mac_ver < 0x3572) + run_bbp_write(sc, 86, 0x00); + + if (sc->mac_ver == 0x3593) { + run_bbp_write(sc, 77, 0x98); + run_bbp_write(sc, 83, (group == 0) ? 0x8a : 0x9a); + } + + if (group == 0) { + if (sc->ext_2ghz_lna) { + if (sc->mac_ver >= 0x5390) + run_bbp_write(sc, 75, 0x52); + else { + run_bbp_write(sc, 82, 0x62); + run_bbp_write(sc, 75, 0x46); + } + } else { + if (sc->mac_ver == 0x5592) { + run_bbp_write(sc, 79, 0x1c); + run_bbp_write(sc, 80, 0x0e); + run_bbp_write(sc, 81, 0x3a); + run_bbp_write(sc, 82, 0x62); + + run_bbp_write(sc, 195, 0x80); + run_bbp_write(sc, 196, 0xe0); + run_bbp_write(sc, 195, 0x81); + run_bbp_write(sc, 196, 0x1f); + run_bbp_write(sc, 195, 0x82); + run_bbp_write(sc, 196, 0x38); + run_bbp_write(sc, 195, 0x83); + run_bbp_write(sc, 196, 0x32); + run_bbp_write(sc, 195, 0x85); + run_bbp_write(sc, 196, 0x28); + run_bbp_write(sc, 195, 0x86); + run_bbp_write(sc, 196, 0x19); + } else if (sc->mac_ver >= 0x5390) + run_bbp_write(sc, 75, 0x50); + else { + run_bbp_write(sc, 82, + (sc->mac_ver == 0x3593) ? 0x62 : 0x84); + run_bbp_write(sc, 75, 0x50); + } + } + } else { + if (sc->mac_ver == 0x5592) { + run_bbp_write(sc, 79, 0x18); + run_bbp_write(sc, 80, 0x08); + run_bbp_write(sc, 81, 0x38); + run_bbp_write(sc, 82, 0x92); + + run_bbp_write(sc, 195, 0x80); + run_bbp_write(sc, 196, 0xf0); + run_bbp_write(sc, 195, 0x81); + run_bbp_write(sc, 196, 0x1e); + run_bbp_write(sc, 195, 0x82); + run_bbp_write(sc, 196, 0x28); + run_bbp_write(sc, 195, 0x83); + run_bbp_write(sc, 196, 0x20); + run_bbp_write(sc, 195, 0x85); + run_bbp_write(sc, 196, 0x7f); + run_bbp_write(sc, 195, 0x86); + run_bbp_write(sc, 196, 0x7f); + } else if (sc->mac_ver == 0x3572) + run_bbp_write(sc, 82, 0x94); + else + run_bbp_write(sc, 82, + (sc->mac_ver == 0x3593) ? 0x82 : 0xf2); + if (sc->ext_5ghz_lna) + run_bbp_write(sc, 75, 0x46); + else + run_bbp_write(sc, 75, 0x50); + } + + run_read(sc, RT2860_TX_BAND_CFG, &tmp); + tmp &= ~(RT2860_5G_BAND_SEL_N | RT2860_5G_BAND_SEL_P); + tmp |= (group == 0) ? RT2860_5G_BAND_SEL_N : RT2860_5G_BAND_SEL_P; + run_write(sc, RT2860_TX_BAND_CFG, tmp); + + /* enable appropriate Power Amplifiers and Low Noise Amplifiers */ + tmp = RT2860_RFTR_EN | RT2860_TRSW_EN | RT2860_LNA_PE0_EN; + if (sc->mac_ver == 0x3593) + tmp |= 1 << 29 | 1 << 28; + if (sc->nrxchains > 1) + tmp |= RT2860_LNA_PE1_EN; + if (group == 0) { /* 2GHz */ + tmp |= RT2860_PA_PE_G0_EN; + if (sc->ntxchains > 1) + tmp |= RT2860_PA_PE_G1_EN; + if (sc->mac_ver == 0x3593) { + if (sc->ntxchains > 2) + tmp |= 1 << 25; + } + } else { /* 5GHz */ + tmp |= RT2860_PA_PE_A0_EN; + if (sc->ntxchains > 1) + tmp |= RT2860_PA_PE_A1_EN; + } + if (sc->mac_ver == 0x3572) { + run_rt3070_rf_write(sc, 8, 0x00); + run_write(sc, RT2860_TX_PIN_CFG, tmp); + run_rt3070_rf_write(sc, 8, 0x80); + } else + run_write(sc, RT2860_TX_PIN_CFG, tmp); + + if (sc->mac_ver == 0x5592) { + run_bbp_write(sc, 195, 0x8d); + run_bbp_write(sc, 196, 0x1a); + } + + if (sc->mac_ver == 0x3593) { + run_read(sc, RT2860_GPIO_CTRL, &tmp); + tmp &= ~0x01010000; + if (group == 0) + tmp |= 0x00010000; + tmp = (tmp & ~0x00009090) | 0x00000090; + run_write(sc, RT2860_GPIO_CTRL, tmp); + } + + /* set initial AGC value */ + if (group == 0) { /* 2GHz band */ + if (sc->mac_ver >= 0x3070) + agc = 0x1c + sc->lna[0] * 2; + else + agc = 0x2e + sc->lna[0]; + } else { /* 5GHz band */ + if (sc->mac_ver == 0x5592) + agc = 0x24 + sc->lna[group] * 2; + else if (sc->mac_ver == 0x3572 || sc->mac_ver == 0x3593) + agc = 0x22 + (sc->lna[group] * 5) / 3; + else + agc = 0x32 + (sc->lna[group] * 5) / 3; + } + run_set_agc(sc, agc); +} + +static void +run_rt2870_set_chan(struct run_softc *sc, u_int chan) +{ + const struct rfprog *rfprog = rt2860_rf2850; + uint32_t r2, r3, r4; + int8_t txpow1, txpow2; + int i; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rfprog[i].chan != chan; i++); + + r2 = rfprog[i].r2; + if (sc->ntxchains == 1) + r2 |= 1 << 14; /* 1T: disable Tx chain 2 */ + if (sc->nrxchains == 1) + r2 |= 1 << 17 | 1 << 6; /* 1R: disable Rx chains 2 & 3 */ + else if (sc->nrxchains == 2) + r2 |= 1 << 6; /* 2R: disable Rx chain 3 */ + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + + /* Initialize RF R3 and R4. */ + r3 = rfprog[i].r3 & 0xffffc1ff; + r4 = (rfprog[i].r4 & ~(0x001f87c0)) | (sc->freq << 15); + if (chan > 14) { + if (txpow1 >= 0) { + txpow1 = (txpow1 > 0xf) ? (0xf) : (txpow1); + r3 |= (txpow1 << 10) | (1 << 9); + } else { + txpow1 += 7; + + /* txpow1 is not possible larger than 15. */ + r3 |= (txpow1 << 10); + } + if (txpow2 >= 0) { + txpow2 = (txpow2 > 0xf) ? (0xf) : (txpow2); + r4 |= (txpow2 << 7) | (1 << 6); + } else { + txpow2 += 7; + r4 |= (txpow2 << 7); + } + } else { + /* Set Tx0 power. */ + r3 |= (txpow1 << 9); + + /* Set frequency offset and Tx1 power. */ + r4 |= (txpow2 << 6); + } + + run_rt2870_rf_write(sc, rfprog[i].r1); + run_rt2870_rf_write(sc, r2); + run_rt2870_rf_write(sc, r3 & ~(1 << 2)); + run_rt2870_rf_write(sc, r4); + + run_delay(sc, 10); + + run_rt2870_rf_write(sc, rfprog[i].r1); + run_rt2870_rf_write(sc, r2); + run_rt2870_rf_write(sc, r3 | (1 << 2)); + run_rt2870_rf_write(sc, r4); + + run_delay(sc, 10); + + run_rt2870_rf_write(sc, rfprog[i].r1); + run_rt2870_rf_write(sc, r2); + run_rt2870_rf_write(sc, r3 & ~(1 << 2)); + run_rt2870_rf_write(sc, r4); +} + +static void +run_rt3070_set_chan(struct run_softc *sc, u_int chan) +{ + int8_t txpow1, txpow2; + uint8_t rf; + int i; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rt2860_rf2850[i].chan != chan; i++); + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + + run_rt3070_rf_write(sc, 2, rt3070_freqs[i].n); + + /* RT3370/RT3390: RF R3 [7:4] is not reserved bits. */ + run_rt3070_rf_read(sc, 3, &rf); + rf = (rf & ~0x0f) | rt3070_freqs[i].k; + run_rt3070_rf_write(sc, 3, rf); + + run_rt3070_rf_read(sc, 6, &rf); + rf = (rf & ~0x03) | rt3070_freqs[i].r; + run_rt3070_rf_write(sc, 6, rf); + + /* set Tx0 power */ + run_rt3070_rf_read(sc, 12, &rf); + rf = (rf & ~0x1f) | txpow1; + run_rt3070_rf_write(sc, 12, rf); + + /* set Tx1 power */ + run_rt3070_rf_read(sc, 13, &rf); + rf = (rf & ~0x1f) | txpow2; + run_rt3070_rf_write(sc, 13, rf); + + run_rt3070_rf_read(sc, 1, &rf); + rf &= ~0xfc; + if (sc->ntxchains == 1) + rf |= 1 << 7 | 1 << 5; /* 1T: disable Tx chains 2 & 3 */ + else if (sc->ntxchains == 2) + rf |= 1 << 7; /* 2T: disable Tx chain 3 */ + if (sc->nrxchains == 1) + rf |= 1 << 6 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */ + else if (sc->nrxchains == 2) + rf |= 1 << 6; /* 2R: disable Rx chain 3 */ + run_rt3070_rf_write(sc, 1, rf); + + /* set RF offset */ + run_rt3070_rf_read(sc, 23, &rf); + rf = (rf & ~0x7f) | sc->freq; + run_rt3070_rf_write(sc, 23, rf); + + /* program RF filter */ + run_rt3070_rf_read(sc, 24, &rf); /* Tx */ + rf = (rf & ~0x3f) | sc->rf24_20mhz; + run_rt3070_rf_write(sc, 24, rf); + run_rt3070_rf_read(sc, 31, &rf); /* Rx */ + rf = (rf & ~0x3f) | sc->rf24_20mhz; + run_rt3070_rf_write(sc, 31, rf); + + /* enable RF tuning */ + run_rt3070_rf_read(sc, 7, &rf); + run_rt3070_rf_write(sc, 7, rf | 0x01); +} + +static void +run_rt3572_set_chan(struct run_softc *sc, u_int chan) +{ + int8_t txpow1, txpow2; + uint32_t tmp; + uint8_t rf; + int i; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rt2860_rf2850[i].chan != chan; i++); + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + + if (chan <= 14) { + run_bbp_write(sc, 25, sc->bbp25); + run_bbp_write(sc, 26, sc->bbp26); + } else { + /* enable IQ phase correction */ + run_bbp_write(sc, 25, 0x09); + run_bbp_write(sc, 26, 0xff); + } + + run_rt3070_rf_write(sc, 2, rt3070_freqs[i].n); + run_rt3070_rf_write(sc, 3, rt3070_freqs[i].k); + run_rt3070_rf_read(sc, 6, &rf); + rf = (rf & ~0x0f) | rt3070_freqs[i].r; + rf |= (chan <= 14) ? 0x08 : 0x04; + run_rt3070_rf_write(sc, 6, rf); + + /* set PLL mode */ + run_rt3070_rf_read(sc, 5, &rf); + rf &= ~(0x08 | 0x04); + rf |= (chan <= 14) ? 0x04 : 0x08; + run_rt3070_rf_write(sc, 5, rf); + + /* set Tx power for chain 0 */ + if (chan <= 14) + rf = 0x60 | txpow1; + else + rf = 0xe0 | (txpow1 & 0xc) << 1 | (txpow1 & 0x3); + run_rt3070_rf_write(sc, 12, rf); + + /* set Tx power for chain 1 */ + if (chan <= 14) + rf = 0x60 | txpow2; + else + rf = 0xe0 | (txpow2 & 0xc) << 1 | (txpow2 & 0x3); + run_rt3070_rf_write(sc, 13, rf); + + /* set Tx/Rx streams */ + run_rt3070_rf_read(sc, 1, &rf); + rf &= ~0xfc; + if (sc->ntxchains == 1) + rf |= 1 << 7 | 1 << 5; /* 1T: disable Tx chains 2 & 3 */ + else if (sc->ntxchains == 2) + rf |= 1 << 7; /* 2T: disable Tx chain 3 */ + if (sc->nrxchains == 1) + rf |= 1 << 6 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */ + else if (sc->nrxchains == 2) + rf |= 1 << 6; /* 2R: disable Rx chain 3 */ + run_rt3070_rf_write(sc, 1, rf); + + /* set RF offset */ + run_rt3070_rf_read(sc, 23, &rf); + rf = (rf & ~0x7f) | sc->freq; + run_rt3070_rf_write(sc, 23, rf); + + /* program RF filter */ + rf = sc->rf24_20mhz; + run_rt3070_rf_write(sc, 24, rf); /* Tx */ + run_rt3070_rf_write(sc, 31, rf); /* Rx */ + + /* enable RF tuning */ + run_rt3070_rf_read(sc, 7, &rf); + rf = (chan <= 14) ? 0xd8 : ((rf & ~0xc8) | 0x14); + run_rt3070_rf_write(sc, 7, rf); + + /* TSSI */ + rf = (chan <= 14) ? 0xc3 : 0xc0; + run_rt3070_rf_write(sc, 9, rf); + + /* set loop filter 1 */ + run_rt3070_rf_write(sc, 10, 0xf1); + /* set loop filter 2 */ + run_rt3070_rf_write(sc, 11, (chan <= 14) ? 0xb9 : 0x00); + + /* set tx_mx2_ic */ + run_rt3070_rf_write(sc, 15, (chan <= 14) ? 0x53 : 0x43); + /* set tx_mx1_ic */ + if (chan <= 14) + rf = 0x48 | sc->txmixgain_2ghz; + else + rf = 0x78 | sc->txmixgain_5ghz; + run_rt3070_rf_write(sc, 16, rf); + + /* set tx_lo1 */ + run_rt3070_rf_write(sc, 17, 0x23); + /* set tx_lo2 */ + if (chan <= 14) + rf = 0x93; + else if (chan <= 64) + rf = 0xb7; + else if (chan <= 128) + rf = 0x74; + else + rf = 0x72; + run_rt3070_rf_write(sc, 19, rf); + + /* set rx_lo1 */ + if (chan <= 14) + rf = 0xb3; + else if (chan <= 64) + rf = 0xf6; + else if (chan <= 128) + rf = 0xf4; + else + rf = 0xf3; + run_rt3070_rf_write(sc, 20, rf); + + /* set pfd_delay */ + if (chan <= 14) + rf = 0x15; + else if (chan <= 64) + rf = 0x3d; + else + rf = 0x01; + run_rt3070_rf_write(sc, 25, rf); + + /* set rx_lo2 */ + run_rt3070_rf_write(sc, 26, (chan <= 14) ? 0x85 : 0x87); + /* set ldo_rf_vc */ + run_rt3070_rf_write(sc, 27, (chan <= 14) ? 0x00 : 0x01); + /* set drv_cc */ + run_rt3070_rf_write(sc, 29, (chan <= 14) ? 0x9b : 0x9f); + + run_read(sc, RT2860_GPIO_CTRL, &tmp); + tmp &= ~0x8080; + if (chan <= 14) + tmp |= 0x80; + run_write(sc, RT2860_GPIO_CTRL, tmp); + + /* enable RF tuning */ + run_rt3070_rf_read(sc, 7, &rf); + run_rt3070_rf_write(sc, 7, rf | 0x01); + + run_delay(sc, 2); +} + +static void +run_rt3593_set_chan(struct run_softc *sc, u_int chan) +{ + int8_t txpow1, txpow2, txpow3; + uint8_t h20mhz, rf; + int i; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rt2860_rf2850[i].chan != chan; i++); + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + txpow3 = (sc->ntxchains == 3) ? sc->txpow3[i] : 0; + + if (chan <= 14) { + run_bbp_write(sc, 25, sc->bbp25); + run_bbp_write(sc, 26, sc->bbp26); + } else { + /* Enable IQ phase correction. */ + run_bbp_write(sc, 25, 0x09); + run_bbp_write(sc, 26, 0xff); + } + + run_rt3070_rf_write(sc, 8, rt3070_freqs[i].n); + run_rt3070_rf_write(sc, 9, rt3070_freqs[i].k & 0x0f); + run_rt3070_rf_read(sc, 11, &rf); + rf = (rf & ~0x03) | (rt3070_freqs[i].r & 0x03); + run_rt3070_rf_write(sc, 11, rf); + + /* Set pll_idoh. */ + run_rt3070_rf_read(sc, 11, &rf); + rf &= ~0x4c; + rf |= (chan <= 14) ? 0x44 : 0x48; + run_rt3070_rf_write(sc, 11, rf); + + if (chan <= 14) + rf = txpow1 & 0x1f; + else + rf = 0x40 | ((txpow1 & 0x18) << 1) | (txpow1 & 0x07); + run_rt3070_rf_write(sc, 53, rf); + + if (chan <= 14) + rf = txpow2 & 0x1f; + else + rf = 0x40 | ((txpow2 & 0x18) << 1) | (txpow2 & 0x07); + run_rt3070_rf_write(sc, 55, rf); + + if (chan <= 14) + rf = txpow3 & 0x1f; + else + rf = 0x40 | ((txpow3 & 0x18) << 1) | (txpow3 & 0x07); + run_rt3070_rf_write(sc, 54, rf); + + rf = RT3070_RF_BLOCK | RT3070_PLL_PD; + if (sc->ntxchains == 3) + rf |= RT3070_TX0_PD | RT3070_TX1_PD | RT3070_TX2_PD; + else + rf |= RT3070_TX0_PD | RT3070_TX1_PD; + rf |= RT3070_RX0_PD | RT3070_RX1_PD | RT3070_RX2_PD; + run_rt3070_rf_write(sc, 1, rf); + + run_adjust_freq_offset(sc); + + run_rt3070_rf_write(sc, 31, (chan <= 14) ? 0xa0 : 0x80); + + h20mhz = (sc->rf24_20mhz & 0x20) >> 5; + run_rt3070_rf_read(sc, 30, &rf); + rf = (rf & ~0x06) | (h20mhz << 1) | (h20mhz << 2); + run_rt3070_rf_write(sc, 30, rf); + + run_rt3070_rf_read(sc, 36, &rf); + if (chan <= 14) + rf |= 0x80; + else + rf &= ~0x80; + run_rt3070_rf_write(sc, 36, rf); + + /* Set vcolo_bs. */ + run_rt3070_rf_write(sc, 34, (chan <= 14) ? 0x3c : 0x20); + /* Set pfd_delay. */ + run_rt3070_rf_write(sc, 12, (chan <= 14) ? 0x1a : 0x12); + + /* Set vco bias current control. */ + run_rt3070_rf_read(sc, 6, &rf); + rf &= ~0xc0; + if (chan <= 14) + rf |= 0x40; + else if (chan <= 128) + rf |= 0x80; + else + rf |= 0x40; + run_rt3070_rf_write(sc, 6, rf); + + run_rt3070_rf_read(sc, 30, &rf); + rf = (rf & ~0x18) | 0x10; + run_rt3070_rf_write(sc, 30, rf); + + run_rt3070_rf_write(sc, 10, (chan <= 14) ? 0xd3 : 0xd8); + run_rt3070_rf_write(sc, 13, (chan <= 14) ? 0x12 : 0x23); + + run_rt3070_rf_read(sc, 51, &rf); + rf = (rf & ~0x03) | 0x01; + run_rt3070_rf_write(sc, 51, rf); + /* Set tx_mx1_cc. */ + run_rt3070_rf_read(sc, 51, &rf); + rf &= ~0x1c; + rf |= (chan <= 14) ? 0x14 : 0x10; + run_rt3070_rf_write(sc, 51, rf); + /* Set tx_mx1_ic. */ + run_rt3070_rf_read(sc, 51, &rf); + rf &= ~0xe0; + rf |= (chan <= 14) ? 0x60 : 0x40; + run_rt3070_rf_write(sc, 51, rf); + /* Set tx_lo1_ic. */ + run_rt3070_rf_read(sc, 49, &rf); + rf &= ~0x1c; + rf |= (chan <= 14) ? 0x0c : 0x08; + run_rt3070_rf_write(sc, 49, rf); + /* Set tx_lo1_en. */ + run_rt3070_rf_read(sc, 50, &rf); + run_rt3070_rf_write(sc, 50, rf & ~0x20); + /* Set drv_cc. */ + run_rt3070_rf_read(sc, 57, &rf); + rf &= ~0xfc; + rf |= (chan <= 14) ? 0x6c : 0x3c; + run_rt3070_rf_write(sc, 57, rf); + /* Set rx_mix1_ic, rxa_lnactr, lna_vc, lna_inbias_en and lna_en. */ + run_rt3070_rf_write(sc, 44, (chan <= 14) ? 0x93 : 0x9b); + /* Set drv_gnd_a, tx_vga_cc_a and tx_mx2_gain. */ + run_rt3070_rf_write(sc, 52, (chan <= 14) ? 0x45 : 0x05); + /* Enable VCO calibration. */ + run_rt3070_rf_read(sc, 3, &rf); + rf &= ~RT5390_VCOCAL; + rf |= (chan <= 14) ? RT5390_VCOCAL : 0xbe; + run_rt3070_rf_write(sc, 3, rf); + + if (chan <= 14) + rf = 0x23; + else if (chan <= 64) + rf = 0x36; + else if (chan <= 128) + rf = 0x32; + else + rf = 0x30; + run_rt3070_rf_write(sc, 39, rf); + if (chan <= 14) + rf = 0xbb; + else if (chan <= 64) + rf = 0xeb; + else if (chan <= 128) + rf = 0xb3; + else + rf = 0x9b; + run_rt3070_rf_write(sc, 45, rf); + + /* Set FEQ/AEQ control. */ + run_bbp_write(sc, 105, 0x34); +} + +static void +run_rt5390_set_chan(struct run_softc *sc, u_int chan) +{ + int8_t txpow1, txpow2; + uint8_t rf; + int i; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rt2860_rf2850[i].chan != chan; i++); + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + + run_rt3070_rf_write(sc, 8, rt3070_freqs[i].n); + run_rt3070_rf_write(sc, 9, rt3070_freqs[i].k & 0x0f); + run_rt3070_rf_read(sc, 11, &rf); + rf = (rf & ~0x03) | (rt3070_freqs[i].r & 0x03); + run_rt3070_rf_write(sc, 11, rf); + + run_rt3070_rf_read(sc, 49, &rf); + rf = (rf & ~0x3f) | (txpow1 & 0x3f); + /* The valid range of the RF R49 is 0x00 to 0x27. */ + if ((rf & 0x3f) > 0x27) + rf = (rf & ~0x3f) | 0x27; + run_rt3070_rf_write(sc, 49, rf); + + if (sc->mac_ver == 0x5392) { + run_rt3070_rf_read(sc, 50, &rf); + rf = (rf & ~0x3f) | (txpow2 & 0x3f); + /* The valid range of the RF R50 is 0x00 to 0x27. */ + if ((rf & 0x3f) > 0x27) + rf = (rf & ~0x3f) | 0x27; + run_rt3070_rf_write(sc, 50, rf); + } + + run_rt3070_rf_read(sc, 1, &rf); + rf |= RT3070_RF_BLOCK | RT3070_PLL_PD | RT3070_RX0_PD | RT3070_TX0_PD; + if (sc->mac_ver == 0x5392) + rf |= RT3070_RX1_PD | RT3070_TX1_PD; + run_rt3070_rf_write(sc, 1, rf); + + if (sc->mac_ver != 0x5392) { + run_rt3070_rf_read(sc, 2, &rf); + rf |= 0x80; + run_rt3070_rf_write(sc, 2, rf); + run_delay(sc, 10); + rf &= 0x7f; + run_rt3070_rf_write(sc, 2, rf); + } + + run_adjust_freq_offset(sc); + + if (sc->mac_ver == 0x5392) { + /* Fix for RT5392C. */ + if (sc->mac_rev >= 0x0223) { + if (chan <= 4) + rf = 0x0f; + else if (chan >= 5 && chan <= 7) + rf = 0x0e; + else + rf = 0x0d; + run_rt3070_rf_write(sc, 23, rf); + + if (chan <= 4) + rf = 0x0c; + else if (chan == 5) + rf = 0x0b; + else if (chan >= 6 && chan <= 7) + rf = 0x0a; + else if (chan >= 8 && chan <= 10) + rf = 0x09; + else + rf = 0x08; + run_rt3070_rf_write(sc, 59, rf); + } else { + if (chan <= 11) + rf = 0x0f; + else + rf = 0x0b; + run_rt3070_rf_write(sc, 59, rf); + } + } else { + /* Fix for RT5390F. */ + if (sc->mac_rev >= 0x0502) { + if (chan <= 11) + rf = 0x43; + else + rf = 0x23; + run_rt3070_rf_write(sc, 55, rf); + + if (chan <= 11) + rf = 0x0f; + else if (chan == 12) + rf = 0x0d; + else + rf = 0x0b; + run_rt3070_rf_write(sc, 59, rf); + } else { + run_rt3070_rf_write(sc, 55, 0x44); + run_rt3070_rf_write(sc, 59, 0x8f); + } + } + + /* Enable VCO calibration. */ + run_rt3070_rf_read(sc, 3, &rf); + rf |= RT5390_VCOCAL; + run_rt3070_rf_write(sc, 3, rf); +} + +static void +run_rt5592_set_chan(struct run_softc *sc, u_int chan) +{ + const struct rt5592_freqs *freqs; + uint32_t tmp; + uint8_t reg, rf, txpow_bound; + int8_t txpow1, txpow2; + int i; + + run_read(sc, RT5592_DEBUG_INDEX, &tmp); + freqs = (tmp & RT5592_SEL_XTAL) ? + rt5592_freqs_40mhz : rt5592_freqs_20mhz; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rt2860_rf2850[i].chan != chan; i++, freqs++); + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + + run_read(sc, RT3070_LDO_CFG0, &tmp); + tmp &= ~0x1c000000; + if (chan > 14) + tmp |= 0x14000000; + run_write(sc, RT3070_LDO_CFG0, tmp); + + /* N setting. */ + run_rt3070_rf_write(sc, 8, freqs->n & 0xff); + run_rt3070_rf_read(sc, 9, &rf); + rf &= ~(1 << 4); + rf |= ((freqs->n & 0x0100) >> 8) << 4; + run_rt3070_rf_write(sc, 9, rf); + + /* K setting. */ + run_rt3070_rf_read(sc, 9, &rf); + rf &= ~0x0f; + rf |= (freqs->k & 0x0f); + run_rt3070_rf_write(sc, 9, rf); + + /* Mode setting. */ + run_rt3070_rf_read(sc, 11, &rf); + rf &= ~0x0c; + rf |= ((freqs->m - 0x8) & 0x3) << 2; + run_rt3070_rf_write(sc, 11, rf); + run_rt3070_rf_read(sc, 9, &rf); + rf &= ~(1 << 7); + rf |= (((freqs->m - 0x8) & 0x4) >> 2) << 7; + run_rt3070_rf_write(sc, 9, rf); + + /* R setting. */ + run_rt3070_rf_read(sc, 11, &rf); + rf &= ~0x03; + rf |= (freqs->r - 0x1); + run_rt3070_rf_write(sc, 11, rf); + + if (chan <= 14) { + /* Initialize RF registers for 2GHZ. */ + for (i = 0; i < nitems(rt5592_2ghz_def_rf); i++) { + run_rt3070_rf_write(sc, rt5592_2ghz_def_rf[i].reg, + rt5592_2ghz_def_rf[i].val); + } + + rf = (chan <= 10) ? 0x07 : 0x06; + run_rt3070_rf_write(sc, 23, rf); + run_rt3070_rf_write(sc, 59, rf); + + run_rt3070_rf_write(sc, 55, 0x43); + + /* + * RF R49/R50 Tx power ALC code. + * G-band bit<7:6>=1:0, bit<5:0> range from 0x0 ~ 0x27. + */ + reg = 2; + txpow_bound = 0x27; + } else { + /* Initialize RF registers for 5GHZ. */ + for (i = 0; i < nitems(rt5592_5ghz_def_rf); i++) { + run_rt3070_rf_write(sc, rt5592_5ghz_def_rf[i].reg, + rt5592_5ghz_def_rf[i].val); + } + for (i = 0; i < nitems(rt5592_chan_5ghz); i++) { + if (chan >= rt5592_chan_5ghz[i].firstchan && + chan <= rt5592_chan_5ghz[i].lastchan) { + run_rt3070_rf_write(sc, rt5592_chan_5ghz[i].reg, + rt5592_chan_5ghz[i].val); + } + } + + /* + * RF R49/R50 Tx power ALC code. + * A-band bit<7:6>=1:1, bit<5:0> range from 0x0 ~ 0x2b. + */ + reg = 3; + txpow_bound = 0x2b; + } + + /* RF R49 ch0 Tx power ALC code. */ + run_rt3070_rf_read(sc, 49, &rf); + rf &= ~0xc0; + rf |= (reg << 6); + rf = (rf & ~0x3f) | (txpow1 & 0x3f); + if ((rf & 0x3f) > txpow_bound) + rf = (rf & ~0x3f) | txpow_bound; + run_rt3070_rf_write(sc, 49, rf); + + /* RF R50 ch1 Tx power ALC code. */ + run_rt3070_rf_read(sc, 50, &rf); + rf &= ~(1 << 7 | 1 << 6); + rf |= (reg << 6); + rf = (rf & ~0x3f) | (txpow2 & 0x3f); + if ((rf & 0x3f) > txpow_bound) + rf = (rf & ~0x3f) | txpow_bound; + run_rt3070_rf_write(sc, 50, rf); + + /* Enable RF_BLOCK, PLL_PD, RX0_PD, and TX0_PD. */ + run_rt3070_rf_read(sc, 1, &rf); + rf |= (RT3070_RF_BLOCK | RT3070_PLL_PD | RT3070_RX0_PD | RT3070_TX0_PD); + if (sc->ntxchains > 1) + rf |= RT3070_TX1_PD; + if (sc->nrxchains > 1) + rf |= RT3070_RX1_PD; + run_rt3070_rf_write(sc, 1, rf); + + run_rt3070_rf_write(sc, 6, 0xe4); + + run_rt3070_rf_write(sc, 30, 0x10); + run_rt3070_rf_write(sc, 31, 0x80); + run_rt3070_rf_write(sc, 32, 0x80); + + run_adjust_freq_offset(sc); + + /* Enable VCO calibration. */ + run_rt3070_rf_read(sc, 3, &rf); + rf |= RT5390_VCOCAL; + run_rt3070_rf_write(sc, 3, rf); +} + +static void +run_set_rx_antenna(struct run_softc *sc, int aux) +{ + uint32_t tmp; + uint8_t bbp152; + + if (aux) { + if (sc->rf_rev == RT5390_RF_5370) { + run_bbp_read(sc, 152, &bbp152); + run_bbp_write(sc, 152, bbp152 & ~0x80); + } else { + run_mcu_cmd(sc, RT2860_MCU_CMD_ANTSEL, 0); + run_read(sc, RT2860_GPIO_CTRL, &tmp); + run_write(sc, RT2860_GPIO_CTRL, (tmp & ~0x0808) | 0x08); + } + } else { + if (sc->rf_rev == RT5390_RF_5370) { + run_bbp_read(sc, 152, &bbp152); + run_bbp_write(sc, 152, bbp152 | 0x80); + } else { + run_mcu_cmd(sc, RT2860_MCU_CMD_ANTSEL, 1); + run_read(sc, RT2860_GPIO_CTRL, &tmp); + run_write(sc, RT2860_GPIO_CTRL, tmp & ~0x0808); + } + } +} + +static int +run_set_chan(struct run_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); + + if (sc->mac_ver == 0x5592) + run_rt5592_set_chan(sc, chan); + else if (sc->mac_ver >= 0x5390) + run_rt5390_set_chan(sc, chan); + else if (sc->mac_ver == 0x3593) + run_rt3593_set_chan(sc, chan); + else if (sc->mac_ver == 0x3572) + run_rt3572_set_chan(sc, chan); + else if (sc->mac_ver >= 0x3070) + run_rt3070_set_chan(sc, chan); + else + run_rt2870_set_chan(sc, chan); + + /* determine channel group */ + if (chan <= 14) + group = 0; + else if (chan <= 64) + group = 1; + else if (chan <= 128) + group = 2; + else + group = 3; + + /* XXX necessary only when group has changed! */ + run_select_chan_group(sc, group); + + run_delay(sc, 10); + + /* Perform IQ calibration. */ + if (sc->mac_ver >= 0x5392) + run_iq_calib(sc, chan); + + return (0); +} + +static void +run_set_channel(struct ieee80211com *ic) +{ + struct run_softc *sc = ic->ic_softc; + + RUN_LOCK(sc); + run_set_chan(sc, ic->ic_curchan); + RUN_UNLOCK(sc); + + return; +} + +static void +run_getradiocaps(struct ieee80211com *ic, + int maxchans, int *nchans, struct ieee80211_channel chans[]) +{ + struct run_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); + if (sc->rf_rev != RT3070_RF_2020) + setbit(bands, IEEE80211_MODE_11NG); + + /* Note: for now, only support HT20 channels */ + ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, bands, 0); + + if (sc->rf_rev == RT2860_RF_2750 || sc->rf_rev == RT2860_RF_2850 || + sc->rf_rev == RT3070_RF_3052 || sc->rf_rev == RT3593_RF_3053 || + sc->rf_rev == RT5592_RF_5592) { + setbit(bands, IEEE80211_MODE_11A); + if (sc->rf_rev != RT3070_RF_2020) + setbit(bands, IEEE80211_MODE_11NA); + /* Note: for now, only support HT20 channels */ + ieee80211_add_channel_list_5ghz(chans, maxchans, nchans, + run_chan_5ghz, nitems(run_chan_5ghz), bands, 0); + } +} + +static void +run_scan_start(struct ieee80211com *ic) +{ + struct run_softc *sc = ic->ic_softc; + + RUN_LOCK(sc); + + /* abort TSF synchronization */ + run_disable_tsf(sc); + run_set_bssid(sc, ieee80211broadcastaddr); + + RUN_UNLOCK(sc); + + return; +} + +static void +run_scan_end(struct ieee80211com *ic) +{ + struct run_softc *sc = ic->ic_softc; + + RUN_LOCK(sc); + + run_enable_tsf_sync(sc); + run_set_bssid(sc, sc->sc_bssid); + + RUN_UNLOCK(sc); + + return; +} + +/* + * Could be called from ieee80211_node_timeout() + * (non-sleepable thread) + */ +static void +run_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 run_softc *sc = ic->ic_softc; + struct run_vap *rvp = RUN_VAP(vap); + int mcast = 0; + uint32_t i; + + switch (item) { + case IEEE80211_BEACON_ERP: + run_updateslot(ic); + break; + case IEEE80211_BEACON_HTINFO: + run_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 = RUN_CMDQ_GET(&sc->cmdq_store); + RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "cmdq_store=%d\n", i); + sc->cmdq[i].func = run_update_beacon_cb; + sc->cmdq[i].arg0 = vap; + ieee80211_runtask(ic, &sc->cmdq_task); + + return; +} + +static void +run_update_beacon_cb(void *arg) +{ + struct ieee80211vap *vap = arg; + struct ieee80211_node *ni = vap->iv_bss; + struct run_vap *rvp = RUN_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct run_softc *sc = ic->ic_softc; + struct rt2860_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(), run_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) ? + RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1; + txwi.phy = htole16(rt2860_rates[ridx].mcs); + if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM) + txwi.phy |= htole16(RT2860_PHY_OFDM); + txwi.txop = RT2860_TX_TXOP_HT; + txwi.flags = RT2860_TX_TS; + txwi.xflags = RT2860_TX_NSEQ; + + txwisize = (sc->mac_ver == 0x5592) ? + sizeof(txwi) + sizeof(uint32_t) : sizeof(txwi); + run_write_region_1(sc, RT2860_BCN_BASE(rvp->rvp_id), (uint8_t *)&txwi, + txwisize); + run_write_region_1(sc, RT2860_BCN_BASE(rvp->rvp_id) + txwisize, + mtod(m, uint8_t *), (m->m_pkthdr.len + 1) & ~1); +} + +static void +run_updateprot(struct ieee80211com *ic) +{ + struct run_softc *sc = ic->ic_softc; + uint32_t i; + + i = RUN_CMDQ_GET(&sc->cmdq_store); + RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "cmdq_store=%d\n", i); + sc->cmdq[i].func = run_updateprot_cb; + sc->cmdq[i].arg0 = ic; + ieee80211_runtask(ic, &sc->cmdq_task); +} + +static void +run_updateprot_cb(void *arg) +{ + struct ieee80211com *ic = arg; + struct run_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[RT2860_RIDX_OFDM6].mcs | RT2860_PHY_OFDM : + rt2860_rates[RT2860_RIDX_CCK11].mcs; + + /* CCK frames don't require protection */ + run_write(sc, RT2860_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; + } + run_write(sc, RT2860_OFDM_PROT_CFG, tmp); +} + +static void +run_usb_timeout_cb(void *arg) +{ + struct ieee80211vap *vap = arg; + struct run_softc *sc = vap->iv_ic->ic_softc; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + if(vap->iv_state == IEEE80211_S_RUN && + vap->iv_opmode != IEEE80211_M_STA) + run_reset_livelock(sc); + else if (vap->iv_state == IEEE80211_S_SCAN) { + RUN_DPRINTF(sc, RUN_DEBUG_USB | RUN_DEBUG_STATE, + "timeout caused by scan\n"); + /* cancel bgscan */ + ieee80211_cancel_scan(vap); + } else + RUN_DPRINTF(sc, RUN_DEBUG_USB | RUN_DEBUG_STATE, + "timeout by unknown cause\n"); +} + +static void +run_reset_livelock(struct run_softc *sc) +{ + uint32_t tmp; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + /* + * In IBSS or HostAP modes (when the hardware sends beacons), the MAC + * can run into a livelock and start sending CTS-to-self frames like + * crazy if protection is enabled. Reset MAC/BBP for a while + */ + run_read(sc, RT2860_DEBUG, &tmp); + RUN_DPRINTF(sc, RUN_DEBUG_RESET, "debug reg %08x\n", tmp); + if ((tmp & (1 << 29)) && (tmp & (1 << 7 | 1 << 5))) { + RUN_DPRINTF(sc, RUN_DEBUG_RESET, + "CTS-to-self livelock detected\n"); + run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_SRST); + run_delay(sc, 1); + run_write(sc, RT2860_MAC_SYS_CTRL, + RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); + } +} + +static void +run_update_promisc_locked(struct run_softc *sc) +{ + uint32_t tmp; + + run_read(sc, RT2860_RX_FILTR_CFG, &tmp); + + tmp |= RT2860_DROP_UC_NOME; + if (sc->sc_ic.ic_promisc > 0) + tmp &= ~RT2860_DROP_UC_NOME; + + run_write(sc, RT2860_RX_FILTR_CFG, tmp); + + RUN_DPRINTF(sc, RUN_DEBUG_RECV, "%s promiscuous mode\n", + (sc->sc_ic.ic_promisc > 0) ? "entering" : "leaving"); +} + +static void +run_update_promisc(struct ieee80211com *ic) +{ + struct run_softc *sc = ic->ic_softc; + + if ((sc->sc_flags & RUN_RUNNING) == 0) + return; + + RUN_LOCK(sc); + run_update_promisc_locked(sc); + RUN_UNLOCK(sc); +} + +static void +run_enable_tsf_sync(struct run_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t tmp; + + RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "rvp_id=%d ic_opmode=%d\n", + RUN_VAP(vap)->rvp_id, ic->ic_opmode); + + run_read(sc, RT2860_BCN_TIME_CFG, &tmp); + tmp &= ~0x1fffff; + tmp |= vap->iv_bss->ni_intval * 16; + tmp |= RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN; + + if (ic->ic_opmode == IEEE80211_M_STA) { + /* + * Local TSF is always updated with remote TSF on beacon + * reception. + */ + tmp |= 1 << RT2860_TSF_SYNC_MODE_SHIFT; + } else if (ic->ic_opmode == IEEE80211_M_IBSS) { + tmp |= RT2860_BCN_TX_EN; + /* + * Local TSF is updated with remote TSF on beacon reception + * only if the remote TSF is greater than local TSF. + */ + tmp |= 2 << RT2860_TSF_SYNC_MODE_SHIFT; + } else if (ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_MBSS) { + tmp |= RT2860_BCN_TX_EN; + /* SYNC with nobody */ + tmp |= 3 << RT2860_TSF_SYNC_MODE_SHIFT; + } else { + RUN_DPRINTF(sc, RUN_DEBUG_BEACON, + "Enabling TSF failed. undefined opmode\n"); + return; + } + + run_write(sc, RT2860_BCN_TIME_CFG, tmp); +} + +static void +run_enable_tsf(struct run_softc *sc) +{ + uint32_t tmp; + + if (run_read(sc, RT2860_BCN_TIME_CFG, &tmp) == 0) { + tmp &= ~(RT2860_BCN_TX_EN | RT2860_TBTT_TIMER_EN); + tmp |= RT2860_TSF_TIMER_EN; + run_write(sc, RT2860_BCN_TIME_CFG, tmp); + } +} + +static void +run_disable_tsf(struct run_softc *sc) +{ + uint32_t tmp; + + if (run_read(sc, RT2860_BCN_TIME_CFG, &tmp) == 0) { + tmp &= ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN | + RT2860_TBTT_TIMER_EN); + run_write(sc, RT2860_BCN_TIME_CFG, tmp); + } +} + +static void +run_get_tsf(struct run_softc *sc, uint64_t *buf) +{ + run_read_region_1(sc, RT2860_TSF_TIMER_DW0, (uint8_t *)buf, + sizeof(*buf)); +} + +static void +run_enable_mrr(struct run_softc *sc) +{ +#define CCK(mcs) (mcs) +#define OFDM(mcs) (1 << 3 | (mcs)) + run_write(sc, RT2860_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 */ + + run_write(sc, RT2860_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 +run_set_txpreamble(struct run_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint32_t tmp; + + run_read(sc, RT2860_AUTO_RSP_CFG, &tmp); + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + tmp |= RT2860_CCK_SHORT_EN; + else + tmp &= ~RT2860_CCK_SHORT_EN; + run_write(sc, RT2860_AUTO_RSP_CFG, tmp); +} + +static void +run_set_basicrates(struct run_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + + /* set basic rates mask */ + if (ic->ic_curmode == IEEE80211_MODE_11B) + run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x003); + else if (ic->ic_curmode == IEEE80211_MODE_11A) + run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x150); + else /* 11g */ + run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x15f); +} + +static void +run_set_leds(struct run_softc *sc, uint16_t which) +{ + (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LEDS, + which | (sc->leds & 0x7f)); +} + +static void +run_set_bssid(struct run_softc *sc, const uint8_t *bssid) +{ + run_write(sc, RT2860_MAC_BSSID_DW0, + bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24); + run_write(sc, RT2860_MAC_BSSID_DW1, + bssid[4] | bssid[5] << 8); +} + +static void +run_set_macaddr(struct run_softc *sc, const uint8_t *addr) +{ + run_write(sc, RT2860_MAC_ADDR_DW0, + addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); + run_write(sc, RT2860_MAC_ADDR_DW1, + addr[4] | addr[5] << 8 | 0xff << 16); +} + +static void +run_updateslot(struct ieee80211com *ic) +{ + struct run_softc *sc = ic->ic_softc; + uint32_t i; + + i = RUN_CMDQ_GET(&sc->cmdq_store); + RUN_DPRINTF(sc, RUN_DEBUG_BEACON, "cmdq_store=%d\n", i); + sc->cmdq[i].func = run_updateslot_cb; + sc->cmdq[i].arg0 = ic; + ieee80211_runtask(ic, &sc->cmdq_task); + + return; +} + +/* ARGSUSED */ +static void +run_updateslot_cb(void *arg) +{ + struct ieee80211com *ic = arg; + struct run_softc *sc = ic->ic_softc; + uint32_t tmp; + + run_read(sc, RT2860_BKOFF_SLOT_CFG, &tmp); + tmp &= ~0xff; + tmp |= IEEE80211_GET_SLOTTIME(ic); + run_write(sc, RT2860_BKOFF_SLOT_CFG, tmp); +} + +static void +run_update_mcast(struct ieee80211com *ic) +{ +} + +static int8_t +run_rssi2dbm(struct run_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 void +run_rt5390_bbp_init(struct run_softc *sc) +{ + u_int i; + uint8_t bbp; + + /* Apply maximum likelihood detection for 2 stream case. */ + run_bbp_read(sc, 105, &bbp); + if (sc->nrxchains > 1) + run_bbp_write(sc, 105, bbp | RT5390_MLD); + + /* Avoid data lost and CRC error. */ + run_bbp_read(sc, 4, &bbp); + run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL); + + if (sc->mac_ver == 0x5592) { + for (i = 0; i < nitems(rt5592_def_bbp); i++) { + run_bbp_write(sc, rt5592_def_bbp[i].reg, + rt5592_def_bbp[i].val); + } + for (i = 0; i < nitems(rt5592_bbp_r196); i++) { + run_bbp_write(sc, 195, i + 0x80); + run_bbp_write(sc, 196, rt5592_bbp_r196[i]); + } + } else { + for (i = 0; i < nitems(rt5390_def_bbp); i++) { + run_bbp_write(sc, rt5390_def_bbp[i].reg, + rt5390_def_bbp[i].val); + } + } + if (sc->mac_ver == 0x5392) { + run_bbp_write(sc, 88, 0x90); + run_bbp_write(sc, 95, 0x9a); + run_bbp_write(sc, 98, 0x12); + run_bbp_write(sc, 106, 0x12); + run_bbp_write(sc, 134, 0xd0); + run_bbp_write(sc, 135, 0xf6); + run_bbp_write(sc, 148, 0x84); + } + + run_bbp_read(sc, 152, &bbp); + run_bbp_write(sc, 152, bbp | 0x80); + + /* Fix BBP254 for RT5592C. */ + if (sc->mac_ver == 0x5592 && sc->mac_rev >= 0x0221) { + run_bbp_read(sc, 254, &bbp); + run_bbp_write(sc, 254, bbp | 0x80); + } + + /* Disable hardware antenna diversity. */ + if (sc->mac_ver == 0x5390) + run_bbp_write(sc, 154, 0); + + /* Initialize Rx CCK/OFDM frequency offset report. */ + run_bbp_write(sc, 142, 1); + run_bbp_write(sc, 143, 57); +} + +static int +run_bbp_init(struct run_softc *sc) +{ + int i, error, ntries; + uint8_t bbp0; + + /* wait for BBP to wake up */ + for (ntries = 0; ntries < 20; ntries++) { + if ((error = run_bbp_read(sc, 0, &bbp0)) != 0) + return error; + if (bbp0 != 0 && bbp0 != 0xff) + break; + } + if (ntries == 20) + return (ETIMEDOUT); + + /* initialize BBP registers to default values */ + if (sc->mac_ver >= 0x5390) + run_rt5390_bbp_init(sc); + else { + for (i = 0; i < nitems(rt2860_def_bbp); i++) { + run_bbp_write(sc, rt2860_def_bbp[i].reg, + rt2860_def_bbp[i].val); + } + } + + if (sc->mac_ver == 0x3593) { + run_bbp_write(sc, 79, 0x13); + run_bbp_write(sc, 80, 0x05); + run_bbp_write(sc, 81, 0x33); + run_bbp_write(sc, 86, 0x46); + run_bbp_write(sc, 137, 0x0f); + } + + /* fix BBP84 for RT2860E */ + if (sc->mac_ver == 0x2860 && sc->mac_rev != 0x0101) + run_bbp_write(sc, 84, 0x19); + + if (sc->mac_ver >= 0x3070 && (sc->mac_ver != 0x3593 && + sc->mac_ver != 0x5592)) { + run_bbp_write(sc, 79, 0x13); + run_bbp_write(sc, 80, 0x05); + run_bbp_write(sc, 81, 0x33); + } else if (sc->mac_ver == 0x2860 && sc->mac_rev == 0x0100) { + run_bbp_write(sc, 69, 0x16); + run_bbp_write(sc, 73, 0x12); + } + return (0); +} + +static int +run_rt3070_rf_init(struct run_softc *sc) +{ + uint32_t tmp; + uint8_t bbp4, mingain, rf, target; + u_int i; + + run_rt3070_rf_read(sc, 30, &rf); + /* toggle RF R30 bit 7 */ + run_rt3070_rf_write(sc, 30, rf | 0x80); + run_delay(sc, 10); + run_rt3070_rf_write(sc, 30, rf & ~0x80); + + /* initialize RF registers to default value */ + if (sc->mac_ver == 0x3572) { + for (i = 0; i < nitems(rt3572_def_rf); i++) { + run_rt3070_rf_write(sc, rt3572_def_rf[i].reg, + rt3572_def_rf[i].val); + } + } else { + for (i = 0; i < nitems(rt3070_def_rf); i++) { + run_rt3070_rf_write(sc, rt3070_def_rf[i].reg, + rt3070_def_rf[i].val); + } + } + + if (sc->mac_ver == 0x3070 && sc->mac_rev < 0x0201) { + /* + * Change voltage from 1.2V to 1.35V for RT3070. + * The DAC issue (RT3070_LDO_CFG0) has been fixed + * in RT3070(F). + */ + run_read(sc, RT3070_LDO_CFG0, &tmp); + tmp = (tmp & ~0x0f000000) | 0x0d000000; + run_write(sc, RT3070_LDO_CFG0, tmp); + + } else if (sc->mac_ver == 0x3071) { + run_rt3070_rf_read(sc, 6, &rf); + run_rt3070_rf_write(sc, 6, rf | 0x40); + run_rt3070_rf_write(sc, 31, 0x14); + + run_read(sc, RT3070_LDO_CFG0, &tmp); + tmp &= ~0x1f000000; + if (sc->mac_rev < 0x0211) + tmp |= 0x0d000000; /* 1.3V */ + else + tmp |= 0x01000000; /* 1.2V */ + run_write(sc, RT3070_LDO_CFG0, tmp); + + /* patch LNA_PE_G1 */ + run_read(sc, RT3070_GPIO_SWITCH, &tmp); + run_write(sc, RT3070_GPIO_SWITCH, tmp & ~0x20); + + } else if (sc->mac_ver == 0x3572) { + run_rt3070_rf_read(sc, 6, &rf); + run_rt3070_rf_write(sc, 6, rf | 0x40); + + /* increase voltage from 1.2V to 1.35V */ + run_read(sc, RT3070_LDO_CFG0, &tmp); + tmp = (tmp & ~0x1f000000) | 0x0d000000; + run_write(sc, RT3070_LDO_CFG0, tmp); + + if (sc->mac_rev < 0x0211 || !sc->patch_dac) { + run_delay(sc, 1); /* wait for 1msec */ + /* decrease voltage back to 1.2V */ + tmp = (tmp & ~0x1f000000) | 0x01000000; + run_write(sc, RT3070_LDO_CFG0, tmp); + } + } + + /* select 20MHz bandwidth */ + run_rt3070_rf_read(sc, 31, &rf); + run_rt3070_rf_write(sc, 31, rf & ~0x20); + + /* calibrate filter for 20MHz bandwidth */ + sc->rf24_20mhz = 0x1f; /* default value */ + target = (sc->mac_ver < 0x3071) ? 0x16 : 0x13; + run_rt3070_filter_calib(sc, 0x07, target, &sc->rf24_20mhz); + + /* select 40MHz bandwidth */ + run_bbp_read(sc, 4, &bbp4); + run_bbp_write(sc, 4, (bbp4 & ~0x18) | 0x10); + run_rt3070_rf_read(sc, 31, &rf); + run_rt3070_rf_write(sc, 31, rf | 0x20); + + /* calibrate filter for 40MHz bandwidth */ + sc->rf24_40mhz = 0x2f; /* default value */ + target = (sc->mac_ver < 0x3071) ? 0x19 : 0x15; + run_rt3070_filter_calib(sc, 0x27, target, &sc->rf24_40mhz); + + /* go back to 20MHz bandwidth */ + run_bbp_read(sc, 4, &bbp4); + run_bbp_write(sc, 4, bbp4 & ~0x18); + + if (sc->mac_ver == 0x3572) { + /* save default BBP registers 25 and 26 values */ + run_bbp_read(sc, 25, &sc->bbp25); + run_bbp_read(sc, 26, &sc->bbp26); + } else if (sc->mac_rev < 0x0201 || sc->mac_rev < 0x0211) + run_rt3070_rf_write(sc, 27, 0x03); + + run_read(sc, RT3070_OPT_14, &tmp); + run_write(sc, RT3070_OPT_14, tmp | 1); + + if (sc->mac_ver == 0x3070 || sc->mac_ver == 0x3071) { + run_rt3070_rf_read(sc, 17, &rf); + rf &= ~RT3070_TX_LO1; + if ((sc->mac_ver == 0x3070 || + (sc->mac_ver == 0x3071 && sc->mac_rev >= 0x0211)) && + !sc->ext_2ghz_lna) + rf |= 0x20; /* fix for long range Rx issue */ + mingain = (sc->mac_ver == 0x3070) ? 1 : 2; + if (sc->txmixgain_2ghz >= mingain) + rf = (rf & ~0x7) | sc->txmixgain_2ghz; + run_rt3070_rf_write(sc, 17, rf); + } + + if (sc->mac_ver == 0x3071) { + run_rt3070_rf_read(sc, 1, &rf); + rf &= ~(RT3070_RX0_PD | RT3070_TX0_PD); + rf |= RT3070_RF_BLOCK | RT3070_RX1_PD | RT3070_TX1_PD; + run_rt3070_rf_write(sc, 1, rf); + + run_rt3070_rf_read(sc, 15, &rf); + run_rt3070_rf_write(sc, 15, rf & ~RT3070_TX_LO2); + + run_rt3070_rf_read(sc, 20, &rf); + run_rt3070_rf_write(sc, 20, rf & ~RT3070_RX_LO1); + + run_rt3070_rf_read(sc, 21, &rf); + run_rt3070_rf_write(sc, 21, rf & ~RT3070_RX_LO2); + } + + if (sc->mac_ver == 0x3070 || sc->mac_ver == 0x3071) { + /* fix Tx to Rx IQ glitch by raising RF voltage */ + run_rt3070_rf_read(sc, 27, &rf); + rf &= ~0x77; + if (sc->mac_rev < 0x0211) + rf |= 0x03; + run_rt3070_rf_write(sc, 27, rf); + } + return (0); +} + +static void +run_rt3593_rf_init(struct run_softc *sc) +{ + uint32_t tmp; + uint8_t rf; + u_int i; + + /* Disable the GPIO bits 4 and 7 for LNA PE control. */ + run_read(sc, RT3070_GPIO_SWITCH, &tmp); + tmp &= ~(1 << 4 | 1 << 7); + run_write(sc, RT3070_GPIO_SWITCH, tmp); + + /* Initialize RF registers to default value. */ + for (i = 0; i < nitems(rt3593_def_rf); i++) { + run_rt3070_rf_write(sc, rt3593_def_rf[i].reg, + rt3593_def_rf[i].val); + } + + /* Toggle RF R2 to initiate calibration. */ + run_rt3070_rf_write(sc, 2, RT5390_RESCAL); + + /* Initialize RF frequency offset. */ + run_adjust_freq_offset(sc); + + run_rt3070_rf_read(sc, 18, &rf); + run_rt3070_rf_write(sc, 18, rf | RT3593_AUTOTUNE_BYPASS); + + /* + * Increase voltage from 1.2V to 1.35V, wait for 1 msec to + * decrease voltage back to 1.2V. + */ + run_read(sc, RT3070_LDO_CFG0, &tmp); + tmp = (tmp & ~0x1f000000) | 0x0d000000; + run_write(sc, RT3070_LDO_CFG0, tmp); + run_delay(sc, 1); + tmp = (tmp & ~0x1f000000) | 0x01000000; + run_write(sc, RT3070_LDO_CFG0, tmp); + + sc->rf24_20mhz = 0x1f; + sc->rf24_40mhz = 0x2f; + + /* Save default BBP registers 25 and 26 values. */ + run_bbp_read(sc, 25, &sc->bbp25); + run_bbp_read(sc, 26, &sc->bbp26); + + run_read(sc, RT3070_OPT_14, &tmp); + run_write(sc, RT3070_OPT_14, tmp | 1); +} + +static void +run_rt5390_rf_init(struct run_softc *sc) +{ + uint32_t tmp; + uint8_t rf; + u_int i; + + /* Toggle RF R2 to initiate calibration. */ + if (sc->mac_ver == 0x5390) { + run_rt3070_rf_read(sc, 2, &rf); + run_rt3070_rf_write(sc, 2, rf | RT5390_RESCAL); + run_delay(sc, 10); + run_rt3070_rf_write(sc, 2, rf & ~RT5390_RESCAL); + } else { + run_rt3070_rf_write(sc, 2, RT5390_RESCAL); + run_delay(sc, 10); + } + + /* Initialize RF registers to default value. */ + if (sc->mac_ver == 0x5592) { + for (i = 0; i < nitems(rt5592_def_rf); i++) { + run_rt3070_rf_write(sc, rt5592_def_rf[i].reg, + rt5592_def_rf[i].val); + } + /* Initialize RF frequency offset. */ + run_adjust_freq_offset(sc); + } else if (sc->mac_ver == 0x5392) { + for (i = 0; i < nitems(rt5392_def_rf); i++) { + run_rt3070_rf_write(sc, rt5392_def_rf[i].reg, + rt5392_def_rf[i].val); + } + if (sc->mac_rev >= 0x0223) { + run_rt3070_rf_write(sc, 23, 0x0f); + run_rt3070_rf_write(sc, 24, 0x3e); + run_rt3070_rf_write(sc, 51, 0x32); + run_rt3070_rf_write(sc, 53, 0x22); + run_rt3070_rf_write(sc, 56, 0xc1); + run_rt3070_rf_write(sc, 59, 0x0f); + } + } else { + for (i = 0; i < nitems(rt5390_def_rf); i++) { + run_rt3070_rf_write(sc, rt5390_def_rf[i].reg, + rt5390_def_rf[i].val); + } + if (sc->mac_rev >= 0x0502) { + run_rt3070_rf_write(sc, 6, 0xe0); + run_rt3070_rf_write(sc, 25, 0x80); + run_rt3070_rf_write(sc, 46, 0x73); + run_rt3070_rf_write(sc, 53, 0x00); + run_rt3070_rf_write(sc, 56, 0x42); + run_rt3070_rf_write(sc, 61, 0xd1); + } + } + + sc->rf24_20mhz = 0x1f; /* default value */ + sc->rf24_40mhz = (sc->mac_ver == 0x5592) ? 0 : 0x2f; + + if (sc->mac_rev < 0x0211) + run_rt3070_rf_write(sc, 27, 0x3); + + run_read(sc, RT3070_OPT_14, &tmp); + run_write(sc, RT3070_OPT_14, tmp | 1); +} + +static int +run_rt3070_filter_calib(struct run_softc *sc, uint8_t init, uint8_t target, + uint8_t *val) +{ + uint8_t rf22, rf24; + uint8_t bbp55_pb, bbp55_sb, delta; + int ntries; + + /* program filter */ + run_rt3070_rf_read(sc, 24, &rf24); + rf24 = (rf24 & 0xc0) | init; /* initial filter value */ + run_rt3070_rf_write(sc, 24, rf24); + + /* enable baseband loopback mode */ + run_rt3070_rf_read(sc, 22, &rf22); + run_rt3070_rf_write(sc, 22, rf22 | 0x01); + + /* set power and frequency of passband test tone */ + run_bbp_write(sc, 24, 0x00); + for (ntries = 0; ntries < 100; ntries++) { + /* transmit test tone */ + run_bbp_write(sc, 25, 0x90); + run_delay(sc, 10); + /* read received power */ + run_bbp_read(sc, 55, &bbp55_pb); + if (bbp55_pb != 0) + break; + } + if (ntries == 100) + return (ETIMEDOUT); + + /* set power and frequency of stopband test tone */ + run_bbp_write(sc, 24, 0x06); + for (ntries = 0; ntries < 100; ntries++) { + /* transmit test tone */ + run_bbp_write(sc, 25, 0x90); + run_delay(sc, 10); + /* read received power */ + run_bbp_read(sc, 55, &bbp55_sb); + + delta = bbp55_pb - bbp55_sb; + if (delta > target) + break; + + /* reprogram filter */ + rf24++; + run_rt3070_rf_write(sc, 24, rf24); + } + if (ntries < 100) { + if (rf24 != init) + rf24--; /* backtrack */ + *val = rf24; + run_rt3070_rf_write(sc, 24, rf24); + } + + /* restore initial state */ + run_bbp_write(sc, 24, 0x00); + + /* disable baseband loopback mode */ + run_rt3070_rf_read(sc, 22, &rf22); + run_rt3070_rf_write(sc, 22, rf22 & ~0x01); + + return (0); +} + +static void +run_rt3070_rf_setup(struct run_softc *sc) +{ + uint8_t bbp, rf; + int i; + + if (sc->mac_ver == 0x3572) { + /* enable DC filter */ + if (sc->mac_rev >= 0x0201) + run_bbp_write(sc, 103, 0xc0); + + run_bbp_read(sc, 138, &bbp); + if (sc->ntxchains == 1) + bbp |= 0x20; /* turn off DAC1 */ + if (sc->nrxchains == 1) + bbp &= ~0x02; /* turn off ADC1 */ + run_bbp_write(sc, 138, bbp); + + if (sc->mac_rev >= 0x0211) { + /* improve power consumption */ + run_bbp_read(sc, 31, &bbp); + run_bbp_write(sc, 31, bbp & ~0x03); + } + + run_rt3070_rf_read(sc, 16, &rf); + rf = (rf & ~0x07) | sc->txmixgain_2ghz; + run_rt3070_rf_write(sc, 16, rf); + + } else if (sc->mac_ver == 0x3071) { + if (sc->mac_rev >= 0x0211) { + /* enable DC filter */ + run_bbp_write(sc, 103, 0xc0); + + /* improve power consumption */ + run_bbp_read(sc, 31, &bbp); + run_bbp_write(sc, 31, bbp & ~0x03); + } + + run_bbp_read(sc, 138, &bbp); + if (sc->ntxchains == 1) + bbp |= 0x20; /* turn off DAC1 */ + if (sc->nrxchains == 1) + bbp &= ~0x02; /* turn off ADC1 */ + run_bbp_write(sc, 138, bbp); + + run_write(sc, RT2860_TX_SW_CFG1, 0); + if (sc->mac_rev < 0x0211) { + run_write(sc, RT2860_TX_SW_CFG2, + sc->patch_dac ? 0x2c : 0x0f); + } else + run_write(sc, RT2860_TX_SW_CFG2, 0); + + } else if (sc->mac_ver == 0x3070) { + if (sc->mac_rev >= 0x0201) { + /* enable DC filter */ + run_bbp_write(sc, 103, 0xc0); + + /* improve power consumption */ + run_bbp_read(sc, 31, &bbp); + run_bbp_write(sc, 31, bbp & ~0x03); + } + + if (sc->mac_rev < 0x0201) { + run_write(sc, RT2860_TX_SW_CFG1, 0); + run_write(sc, RT2860_TX_SW_CFG2, 0x2c); + } else + run_write(sc, RT2860_TX_SW_CFG2, 0); + } + + /* initialize RF registers from ROM for >=RT3071*/ + if (sc->mac_ver >= 0x3071) { + for (i = 0; i < 10; i++) { + if (sc->rf[i].reg == 0 || sc->rf[i].reg == 0xff) + continue; + run_rt3070_rf_write(sc, sc->rf[i].reg, sc->rf[i].val); + } + } +} + +static void +run_rt3593_rf_setup(struct run_softc *sc) +{ + uint8_t bbp, rf; + + if (sc->mac_rev >= 0x0211) { + /* Enable DC filter. */ + run_bbp_write(sc, 103, 0xc0); + } + run_write(sc, RT2860_TX_SW_CFG1, 0); + if (sc->mac_rev < 0x0211) { + run_write(sc, RT2860_TX_SW_CFG2, + sc->patch_dac ? 0x2c : 0x0f); + } else + run_write(sc, RT2860_TX_SW_CFG2, 0); + + run_rt3070_rf_read(sc, 50, &rf); + run_rt3070_rf_write(sc, 50, rf & ~RT3593_TX_LO2); + + run_rt3070_rf_read(sc, 51, &rf); + rf = (rf & ~(RT3593_TX_LO1 | 0x0c)) | + ((sc->txmixgain_2ghz & 0x07) << 2); + run_rt3070_rf_write(sc, 51, rf); + + run_rt3070_rf_read(sc, 38, &rf); + run_rt3070_rf_write(sc, 38, rf & ~RT5390_RX_LO1); + + run_rt3070_rf_read(sc, 39, &rf); + run_rt3070_rf_write(sc, 39, rf & ~RT5390_RX_LO2); + + run_rt3070_rf_read(sc, 1, &rf); + run_rt3070_rf_write(sc, 1, rf & ~(RT3070_RF_BLOCK | RT3070_PLL_PD)); + + run_rt3070_rf_read(sc, 30, &rf); + rf = (rf & ~0x18) | 0x10; + run_rt3070_rf_write(sc, 30, rf); + + /* Apply maximum likelihood detection for 2 stream case. */ + run_bbp_read(sc, 105, &bbp); + if (sc->nrxchains > 1) + run_bbp_write(sc, 105, bbp | RT5390_MLD); + + /* Avoid data lost and CRC error. */ + run_bbp_read(sc, 4, &bbp); + run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL); + + run_bbp_write(sc, 92, 0x02); + run_bbp_write(sc, 82, 0x82); + run_bbp_write(sc, 106, 0x05); + run_bbp_write(sc, 104, 0x92); + run_bbp_write(sc, 88, 0x90); + run_bbp_write(sc, 148, 0xc8); + run_bbp_write(sc, 47, 0x48); + run_bbp_write(sc, 120, 0x50); + + run_bbp_write(sc, 163, 0x9d); + + /* SNR mapping. */ + run_bbp_write(sc, 142, 0x06); + run_bbp_write(sc, 143, 0xa0); + run_bbp_write(sc, 142, 0x07); + run_bbp_write(sc, 143, 0xa1); + run_bbp_write(sc, 142, 0x08); + run_bbp_write(sc, 143, 0xa2); + + run_bbp_write(sc, 31, 0x08); + run_bbp_write(sc, 68, 0x0b); + run_bbp_write(sc, 105, 0x04); +} + +static void +run_rt5390_rf_setup(struct run_softc *sc) +{ + uint8_t bbp, rf; + + if (sc->mac_rev >= 0x0211) { + /* Enable DC filter. */ + run_bbp_write(sc, 103, 0xc0); + + if (sc->mac_ver != 0x5592) { + /* Improve power consumption. */ + run_bbp_read(sc, 31, &bbp); + run_bbp_write(sc, 31, bbp & ~0x03); + } + } + + run_bbp_read(sc, 138, &bbp); + if (sc->ntxchains == 1) + bbp |= 0x20; /* turn off DAC1 */ + if (sc->nrxchains == 1) + bbp &= ~0x02; /* turn off ADC1 */ + run_bbp_write(sc, 138, bbp); + + run_rt3070_rf_read(sc, 38, &rf); + run_rt3070_rf_write(sc, 38, rf & ~RT5390_RX_LO1); + + run_rt3070_rf_read(sc, 39, &rf); + run_rt3070_rf_write(sc, 39, rf & ~RT5390_RX_LO2); + + /* Avoid data lost and CRC error. */ + run_bbp_read(sc, 4, &bbp); + run_bbp_write(sc, 4, bbp | RT5390_MAC_IF_CTRL); + + run_rt3070_rf_read(sc, 30, &rf); + rf = (rf & ~0x18) | 0x10; + run_rt3070_rf_write(sc, 30, rf); + + if (sc->mac_ver != 0x5592) { + run_write(sc, RT2860_TX_SW_CFG1, 0); + if (sc->mac_rev < 0x0211) { + run_write(sc, RT2860_TX_SW_CFG2, + sc->patch_dac ? 0x2c : 0x0f); + } else + run_write(sc, RT2860_TX_SW_CFG2, 0); + } +} + +static int +run_txrx_enable(struct run_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint32_t tmp; + int error, ntries; + + run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_TX_EN); + for (ntries = 0; ntries < 200; ntries++) { + if ((error = run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp)) != 0) + return (error); + if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) + break; + run_delay(sc, 50); + } + if (ntries == 200) + return (ETIMEDOUT); + + run_delay(sc, 50); + + tmp |= RT2860_RX_DMA_EN | RT2860_TX_DMA_EN | RT2860_TX_WB_DDONE; + run_write(sc, RT2860_WPDMA_GLO_CFG, tmp); + + /* enable Rx bulk aggregation (set timeout and limit) */ + tmp = RT2860_USB_TX_EN | RT2860_USB_RX_EN | RT2860_USB_RX_AGG_EN | + RT2860_USB_RX_AGG_TO(128) | RT2860_USB_RX_AGG_LMT(2); + run_write(sc, RT2860_USB_DMA_CFG, tmp); + + /* set Rx filter */ + tmp = RT2860_DROP_CRC_ERR | RT2860_DROP_PHY_ERR; + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + tmp |= RT2860_DROP_UC_NOME | RT2860_DROP_DUPL | + RT2860_DROP_CTS | RT2860_DROP_BA | RT2860_DROP_ACK | + RT2860_DROP_VER_ERR | RT2860_DROP_CTRL_RSV | + RT2860_DROP_CFACK | RT2860_DROP_CFEND; + if (ic->ic_opmode == IEEE80211_M_STA) + tmp |= RT2860_DROP_RTS | RT2860_DROP_PSPOLL; + } + run_write(sc, RT2860_RX_FILTR_CFG, tmp); + + run_write(sc, RT2860_MAC_SYS_CTRL, + RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); + + return (0); +} + +static void +run_adjust_freq_offset(struct run_softc *sc) +{ + uint8_t rf, tmp; + + run_rt3070_rf_read(sc, 17, &rf); + tmp = rf; + rf = (rf & ~0x7f) | (sc->freq & 0x7f); + rf = MIN(rf, 0x5f); + + if (tmp != rf) + run_mcu_cmd(sc, 0x74, (tmp << 8 ) | rf); +} + +static void +run_init_locked(struct run_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t tmp; + uint8_t bbp1, bbp3; + int i; + int ridx; + int ntries; + + if (ic->ic_nrunning > 1) + return; + + run_stop(sc); + + if (run_load_microcode(sc) != 0) { + device_printf(sc->sc_dev, "could not load 8051 microcode\n"); + goto fail; + } + + for (ntries = 0; ntries < 100; ntries++) { + if (run_read(sc, RT2860_ASIC_VER_ID, &tmp) != 0) + goto fail; + if (tmp != 0 && tmp != 0xffffffff) + break; + run_delay(sc, 10); + } + if (ntries == 100) + goto fail; + + for (i = 0; i != RUN_EP_QUEUES; i++) + run_setup_tx_list(sc, &sc->sc_epq[i]); + + run_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr); + + for (ntries = 0; ntries < 100; ntries++) { + if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0) + goto fail; + if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) + break; + run_delay(sc, 10); + } + if (ntries == 100) { + device_printf(sc->sc_dev, "timeout waiting for DMA engine\n"); + goto fail; + } + tmp &= 0xff0; + tmp |= RT2860_TX_WB_DDONE; + run_write(sc, RT2860_WPDMA_GLO_CFG, tmp); + + /* turn off PME_OEN to solve high-current issue */ + run_read(sc, RT2860_SYS_CTRL, &tmp); + run_write(sc, RT2860_SYS_CTRL, tmp & ~RT2860_PME_OEN); + + run_write(sc, RT2860_MAC_SYS_CTRL, + RT2860_BBP_HRST | RT2860_MAC_SRST); + run_write(sc, RT2860_USB_DMA_CFG, 0); + + if (run_reset(sc) != 0) { + device_printf(sc->sc_dev, "could not reset chipset\n"); + goto fail; + } + + run_write(sc, RT2860_MAC_SYS_CTRL, 0); + + /* init Tx power for all Tx rates (from EEPROM) */ + for (ridx = 0; ridx < 5; ridx++) { + if (sc->txpow20mhz[ridx] == 0xffffffff) + continue; + run_write(sc, RT2860_TX_PWR_CFG(ridx), sc->txpow20mhz[ridx]); + } + + for (i = 0; i < nitems(rt2870_def_mac); i++) + run_write(sc, rt2870_def_mac[i].reg, rt2870_def_mac[i].val); + run_write(sc, RT2860_WMM_AIFSN_CFG, 0x00002273); + run_write(sc, RT2860_WMM_CWMIN_CFG, 0x00002344); + run_write(sc, RT2860_WMM_CWMAX_CFG, 0x000034aa); + + if (sc->mac_ver >= 0x5390) { + run_write(sc, RT2860_TX_SW_CFG0, + 4 << RT2860_DLY_PAPE_EN_SHIFT | 4); + if (sc->mac_ver >= 0x5392) { + run_write(sc, RT2860_MAX_LEN_CFG, 0x00002fff); + if (sc->mac_ver == 0x5592) { + run_write(sc, RT2860_HT_FBK_CFG1, 0xedcba980); + run_write(sc, RT2860_TXOP_HLDR_ET, 0x00000082); + } else { + run_write(sc, RT2860_HT_FBK_CFG1, 0xedcb4980); + run_write(sc, RT2860_LG_FBK_CFG0, 0xedcba322); + } + } + } else if (sc->mac_ver == 0x3593) { + run_write(sc, RT2860_TX_SW_CFG0, + 4 << RT2860_DLY_PAPE_EN_SHIFT | 2); + } else if (sc->mac_ver >= 0x3070) { + /* set delay of PA_PE assertion to 1us (unit of 0.25us) */ + run_write(sc, RT2860_TX_SW_CFG0, + 4 << RT2860_DLY_PAPE_EN_SHIFT); + } + + /* wait while MAC is busy */ + for (ntries = 0; ntries < 100; ntries++) { + if (run_read(sc, RT2860_MAC_STATUS_REG, &tmp) != 0) + goto fail; + if (!(tmp & (RT2860_RX_STATUS_BUSY | RT2860_TX_STATUS_BUSY))) + break; + run_delay(sc, 10); + } + if (ntries == 100) + goto fail; + + /* clear Host to MCU mailbox */ + run_write(sc, RT2860_H2M_BBPAGENT, 0); + run_write(sc, RT2860_H2M_MAILBOX, 0); + run_delay(sc, 10); + + if (run_bbp_init(sc) != 0) { + device_printf(sc->sc_dev, "could not initialize BBP\n"); + goto fail; + } + + /* abort TSF synchronization */ + run_disable_tsf(sc); + + /* clear RX WCID search table */ + run_set_region_4(sc, RT2860_WCID_ENTRY(0), 0, 512); + /* clear WCID attribute table */ + run_set_region_4(sc, RT2860_WCID_ATTR(0), 0, 8 * 32); + + /* hostapd sets a key before init. So, don't clear it. */ + if (sc->cmdq_key_set != RUN_CMDQ_GO) { + /* clear shared key table */ + run_set_region_4(sc, RT2860_SKEY(0, 0), 0, 8 * 32); + /* clear shared key mode */ + run_set_region_4(sc, RT2860_SKEY_MODE_0_7, 0, 4); + } + + run_read(sc, RT2860_US_CYC_CNT, &tmp); + tmp = (tmp & ~0xff) | 0x1e; + run_write(sc, RT2860_US_CYC_CNT, tmp); + + if (sc->mac_rev != 0x0101) + run_write(sc, RT2860_TXOP_CTRL_CFG, 0x0000583f); + + run_write(sc, RT2860_WMM_TXOP0_CFG, 0); + run_write(sc, RT2860_WMM_TXOP1_CFG, 48 << 16 | 96); + + /* write vendor-specific BBP values (from EEPROM) */ + if (sc->mac_ver < 0x3593) { + for (i = 0; i < 10; i++) { + if (sc->bbp[i].reg == 0 || sc->bbp[i].reg == 0xff) + continue; + run_bbp_write(sc, sc->bbp[i].reg, sc->bbp[i].val); + } + } + + /* select Main antenna for 1T1R devices */ + if (sc->rf_rev == RT3070_RF_3020 || sc->rf_rev == RT5390_RF_5370) + run_set_rx_antenna(sc, 0); + + /* send LEDs operating mode to microcontroller */ + (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED1, sc->led[0]); + (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED2, sc->led[1]); + (void)run_mcu_cmd(sc, RT2860_MCU_CMD_LED3, sc->led[2]); + + if (sc->mac_ver >= 0x5390) + run_rt5390_rf_init(sc); + else if (sc->mac_ver == 0x3593) + run_rt3593_rf_init(sc); + else if (sc->mac_ver >= 0x3070) + run_rt3070_rf_init(sc); + + /* disable non-existing Rx chains */ + run_bbp_read(sc, 3, &bbp3); + bbp3 &= ~(1 << 3 | 1 << 4); + if (sc->nrxchains == 2) + bbp3 |= 1 << 3; + else if (sc->nrxchains == 3) + bbp3 |= 1 << 4; + run_bbp_write(sc, 3, bbp3); + + /* disable non-existing Tx chains */ + run_bbp_read(sc, 1, &bbp1); + if (sc->ntxchains == 1) + bbp1 &= ~(1 << 3 | 1 << 4); + run_bbp_write(sc, 1, bbp1); + + if (sc->mac_ver >= 0x5390) + run_rt5390_rf_setup(sc); + else if (sc->mac_ver == 0x3593) + run_rt3593_rf_setup(sc); + else if (sc->mac_ver >= 0x3070) + run_rt3070_rf_setup(sc); + + /* select default channel */ + run_set_chan(sc, ic->ic_curchan); + + /* setup initial protection mode */ + run_updateprot_cb(ic); + + /* turn radio LED on */ + run_set_leds(sc, RT2860_LED_RADIO); + + /* Set up AUTO_RSP_CFG register for auto response */ + run_write(sc, RT2860_AUTO_RSP_CFG, RT2860_AUTO_RSP_EN | + RT2860_BAC_ACKPOLICY_EN | RT2860_CTS_40M_MODE_EN); + + sc->sc_flags |= RUN_RUNNING; + sc->cmdq_run = RUN_CMDQ_GO; + + for (i = 0; i != RUN_N_XFER; i++) + usbd_xfer_set_stall(sc->sc_xfer[i]); + + usbd_transfer_start(sc->sc_xfer[RUN_BULK_RX]); + + if (run_txrx_enable(sc) != 0) + goto fail; + + return; + +fail: + run_stop(sc); +} + +static void +run_stop(void *arg) +{ + struct run_softc *sc = (struct run_softc *)arg; + uint32_t tmp; + int i; + int ntries; + + RUN_LOCK_ASSERT(sc, MA_OWNED); + + if (sc->sc_flags & RUN_RUNNING) + run_set_leds(sc, 0); /* turn all LEDs off */ + + sc->sc_flags &= ~RUN_RUNNING; + + sc->ratectl_run = RUN_RATECTL_OFF; + sc->cmdq_run = sc->cmdq_key_set; + + RUN_UNLOCK(sc); + + for(i = 0; i < RUN_N_XFER; i++) + usbd_transfer_drain(sc->sc_xfer[i]); + + RUN_LOCK(sc); + + run_drain_mbufq(sc); + + if (sc->rx_m != NULL) { + m_free(sc->rx_m); + sc->rx_m = NULL; + } + + /* Disable Tx/Rx DMA. */ + if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0) + return; + tmp &= ~(RT2860_RX_DMA_EN | RT2860_TX_DMA_EN); + run_write(sc, RT2860_WPDMA_GLO_CFG, tmp); + + for (ntries = 0; ntries < 100; ntries++) { + if (run_read(sc, RT2860_WPDMA_GLO_CFG, &tmp) != 0) + return; + if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) + break; + run_delay(sc, 10); + } + if (ntries == 100) { + device_printf(sc->sc_dev, "timeout waiting for DMA engine\n"); + return; + } + + /* disable Tx/Rx */ + run_read(sc, RT2860_MAC_SYS_CTRL, &tmp); + tmp &= ~(RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); + run_write(sc, RT2860_MAC_SYS_CTRL, tmp); + + /* wait for pending Tx to complete */ + for (ntries = 0; ntries < 100; ntries++) { + if (run_read(sc, RT2860_TXRXQ_PCNT, &tmp) != 0) { + RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_RESET, + "Cannot read Tx queue count\n"); + break; + } + if ((tmp & RT2860_TX2Q_PCNT_MASK) == 0) { + RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_RESET, + "All Tx cleared\n"); + break; + } + run_delay(sc, 10); + } + if (ntries >= 100) + RUN_DPRINTF(sc, RUN_DEBUG_XMIT | RUN_DEBUG_RESET, + "There are still pending Tx\n"); + run_delay(sc, 10); + run_write(sc, RT2860_USB_DMA_CFG, 0); + + run_write(sc, RT2860_MAC_SYS_CTRL, RT2860_BBP_HRST | RT2860_MAC_SRST); + run_write(sc, RT2860_MAC_SYS_CTRL, 0); + + for (i = 0; i != RUN_EP_QUEUES; i++) + run_unsetup_tx_list(sc, &sc->sc_epq[i]); +} + +static void +run_delay(struct run_softc *sc, u_int ms) +{ + usb_pause_mtx(mtx_owned(&sc->sc_mtx) ? + &sc->sc_mtx : NULL, USB_MS_TO_TICKS(ms)); +} + +static void +run_update_chw(struct ieee80211com *ic) +{ + + printf("%s: TODO\n", __func__); +} + +static int +run_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 run_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, run_match), + DEVMETHOD(device_attach, run_attach), + DEVMETHOD(device_detach, run_detach), + DEVMETHOD_END +}; + +static driver_t run_driver = { + .name = "run", + .methods = run_methods, + .size = sizeof(struct run_softc) +}; + +static devclass_t run_devclass; + +DRIVER_MODULE(run, uhub, run_driver, run_devclass, run_driver_loaded, NULL); +MODULE_DEPEND(run, wlan, 1, 1, 1); +MODULE_DEPEND(run, usb, 1, 1, 1); +MODULE_DEPEND(run, firmware, 1, 1, 1); +MODULE_VERSION(run, 1); +USB_PNP_HOST_INFO(run_devs); diff --git a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_runreg.h b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_runreg.h new file mode 100644 index 0000000000..8e0c8f1f2b --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_runreg.h @@ -0,0 +1,1660 @@ +/* $OpenBSD: rt2860reg.h,v 1.19 2009/05/18 19:25:07 damien Exp $ */ + +/*- + * Copyright (c) 2007 + * 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. + * + * $FreeBSD$ + */ + +#ifndef _IF_RUNREG_H_ +#define _IF_RUNREG_H_ + +#define RT2860_CONFIG_NO 1 +#define RT2860_IFACE_INDEX 0 + +#define RT3070_OPT_14 0x0114 + +/* SCH/DMA registers */ +#define RT2860_INT_STATUS 0x0200 +#define RT2860_INT_MASK 0x0204 +#define RT2860_WPDMA_GLO_CFG 0x0208 +#define RT2860_WPDMA_RST_IDX 0x020c +#define RT2860_DELAY_INT_CFG 0x0210 +#define RT2860_WMM_AIFSN_CFG 0x0214 +#define RT2860_WMM_CWMIN_CFG 0x0218 +#define RT2860_WMM_CWMAX_CFG 0x021c +#define RT2860_WMM_TXOP0_CFG 0x0220 +#define RT2860_WMM_TXOP1_CFG 0x0224 +#define RT2860_GPIO_CTRL 0x0228 +#define RT2860_MCU_CMD_REG 0x022c +#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 RT2860_RX_BASE_PTR 0x0290 +#define RT2860_RX_MAX_CNT 0x0294 +#define RT2860_RX_CALC_IDX 0x0298 +#define RT2860_FS_DRX_IDX 0x029c +#define RT2860_USB_DMA_CFG 0x02a0 /* RT2870 only */ +#define RT2860_US_CYC_CNT 0x02a4 + +/* PBF registers */ +#define RT2860_SYS_CTRL 0x0400 +#define RT2860_HOST_CMD 0x0404 +#define RT2860_PBF_CFG 0x0408 +#define RT2860_MAX_PCNT 0x040c +#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 RT2860_RX0Q_IO 0x0424 +#define RT2860_BCN_OFFSET0 0x042c +#define RT2860_BCN_OFFSET1 0x0430 +#define RT2860_TXRXQ_STA 0x0434 +#define RT2860_TXRXQ_PCNT 0x0438 +#define RT2860_PBF_DBG 0x043c +#define RT2860_CAP_CTRL 0x0440 + +/* RT3070 registers */ +#define RT3070_RF_CSR_CFG 0x0500 +#define RT3070_EFUSE_CTRL 0x0580 +#define RT3070_EFUSE_DATA0 0x0590 +#define RT3070_EFUSE_DATA1 0x0594 +#define RT3070_EFUSE_DATA2 0x0598 +#define RT3070_EFUSE_DATA3 0x059c +#define RT3070_LDO_CFG0 0x05d4 +#define RT3070_GPIO_SWITCH 0x05dc + +/* RT5592 registers */ +#define RT5592_DEBUG_INDEX 0x05e8 + +/* MAC registers */ +#define RT2860_ASIC_VER_ID 0x1000 +#define RT2860_MAC_SYS_CTRL 0x1004 +#define RT2860_MAC_ADDR_DW0 0x1008 +#define RT2860_MAC_ADDR_DW1 0x100c +#define RT2860_MAC_BSSID_DW0 0x1010 +#define RT2860_MAC_BSSID_DW1 0x1014 +#define RT2860_MAX_LEN_CFG 0x1018 +#define RT2860_BBP_CSR_CFG 0x101c +#define RT2860_RF_CSR_CFG0 0x1020 +#define RT2860_RF_CSR_CFG1 0x1024 +#define RT2860_RF_CSR_CFG2 0x1028 +#define RT2860_LED_CFG 0x102c + +/* undocumented registers */ +#define RT2860_DEBUG 0x10f4 + +/* MAC Timing control registers */ +#define RT2860_XIFS_TIME_CFG 0x1100 +#define RT2860_BKOFF_SLOT_CFG 0x1104 +#define RT2860_NAV_TIME_CFG 0x1108 +#define RT2860_CH_TIME_CFG 0x110c +#define RT2860_PBF_LIFE_TIMER 0x1110 +#define RT2860_BCN_TIME_CFG 0x1114 +#define RT2860_TBTT_SYNC_CFG 0x1118 +#define RT2860_TSF_TIMER_DW0 0x111c +#define RT2860_TSF_TIMER_DW1 0x1120 +#define RT2860_TBTT_TIMER 0x1124 +#define RT2860_INT_TIMER_CFG 0x1128 +#define RT2860_INT_TIMER_EN 0x112c +#define RT2860_CH_IDLE_TIME 0x1130 + +/* MAC Power Save configuration registers */ +#define RT2860_MAC_STATUS_REG 0x1200 +#define RT2860_PWR_PIN_CFG 0x1204 +#define RT2860_AUTO_WAKEUP_CFG 0x1208 + +/* MAC TX configuration registers */ +#define RT2860_EDCA_AC_CFG(aci) (0x1300 + (aci) * 4) +#define RT2860_EDCA_TID_AC_MAP 0x1310 +#define RT2860_TX_PWR_CFG(ridx) (0x1314 + (ridx) * 4) +#define RT2860_TX_PIN_CFG 0x1328 +#define RT2860_TX_BAND_CFG 0x132c +#define RT2860_TX_SW_CFG0 0x1330 +#define RT2860_TX_SW_CFG1 0x1334 +#define RT2860_TX_SW_CFG2 0x1338 +#define RT2860_TXOP_THRES_CFG 0x133c +#define RT2860_TXOP_CTRL_CFG 0x1340 +#define RT2860_TX_RTS_CFG 0x1344 +#define RT2860_TX_TIMEOUT_CFG 0x1348 +#define RT2860_TX_RTY_CFG 0x134c +#define RT2860_TX_LINK_CFG 0x1350 +#define RT2860_HT_FBK_CFG0 0x1354 +#define RT2860_HT_FBK_CFG1 0x1358 +#define RT2860_LG_FBK_CFG0 0x135c +#define RT2860_LG_FBK_CFG1 0x1360 +#define RT2860_CCK_PROT_CFG 0x1364 +#define RT2860_OFDM_PROT_CFG 0x1368 +#define RT2860_MM20_PROT_CFG 0x136c +#define RT2860_MM40_PROT_CFG 0x1370 +#define RT2860_GF20_PROT_CFG 0x1374 +#define RT2860_GF40_PROT_CFG 0x1378 +#define RT2860_EXP_CTS_TIME 0x137c +#define RT2860_EXP_ACK_TIME 0x1380 + +/* MAC RX configuration registers */ +#define RT2860_RX_FILTR_CFG 0x1400 +#define RT2860_AUTO_RSP_CFG 0x1404 +#define RT2860_LEGACY_BASIC_RATE 0x1408 +#define RT2860_HT_BASIC_RATE 0x140c +#define RT2860_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 + +/* MAC HCCA/PSMP configuration registers */ +#define RT2860_TXOP_HLDR_ADDR0 0x1600 +#define RT2860_TXOP_HLDR_ADDR1 0x1604 +#define RT2860_TXOP_HLDR_ET 0x1608 +#define RT2860_QOS_CFPOLL_RA_DW0 0x160c +#define RT2860_QOS_CFPOLL_A1_DW1 0x1610 +#define RT2860_QOS_CFPOLL_QC 0x1614 + +/* MAC Statistics Counters */ +#define RT2860_RX_STA_CNT0 0x1700 +#define RT2860_RX_STA_CNT1 0x1704 +#define RT2860_RX_STA_CNT2 0x1708 +#define RT2860_TX_STA_CNT0 0x170c +#define RT2860_TX_STA_CNT1 0x1710 +#define RT2860_TX_STA_CNT2 0x1714 +#define RT2860_TX_STAT_FIFO 0x1718 + +/* RX WCID search table */ +#define RT2860_WCID_ENTRY(wcid) (0x1800 + (wcid) * 8) + +#define RT2860_FW_BASE 0x2000 +#define RT2870_FW_BASE 0x3000 + +/* Pair-wise key table */ +#define RT2860_PKEY(wcid) (0x4000 + (wcid) * 32) + +/* IV/EIV table */ +#define RT2860_IVEIV(wcid) (0x6000 + (wcid) * 8) + +/* WCID attribute table */ +#define RT2860_WCID_ATTR(wcid) (0x6800 + (wcid) * 4) + +/* Shared Key Table */ +#define RT2860_SKEY(vap, kidx) (0x6c00 + (vap) * 128 + (kidx) * 32) + +/* Shared Key Mode */ +#define RT2860_SKEY_MODE_0_7 0x7000 +#define RT2860_SKEY_MODE_8_15 0x7004 +#define RT2860_SKEY_MODE_16_23 0x7008 +#define RT2860_SKEY_MODE_24_31 0x700c + +/* Shared Memory between MCU and host */ +#define RT2860_H2M_MAILBOX 0x7010 +#define RT2860_H2M_MAILBOX_CID 0x7014 +#define RT2860_H2M_MAILBOX_STATUS 0x701c +#define RT2860_H2M_INTSRC 0x7024 +#define RT2860_H2M_BBPAGENT 0x7028 +#define RT2860_BCN_BASE(vap) (0x7800 + (vap) * 512) + +/* possible flags for register RT2860_PCI_EECTRL */ +#define RT2860_C (1 << 0) +#define RT2860_S (1 << 1) +#define RT2860_D (1 << 2) +#define RT2860_SHIFT_D 2 +#define RT2860_Q (1 << 3) +#define RT2860_SHIFT_Q 3 + +/* possible flags for registers INT_STATUS/INT_MASK */ +#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 */ +#define RT2860_HDR_SEG_LEN_SHIFT 8 +#define RT2860_BIG_ENDIAN (1 << 7) +#define RT2860_TX_WB_DDONE (1 << 6) +#define RT2860_WPDMA_BT_SIZE_SHIFT 4 +#define RT2860_WPDMA_BT_SIZE16 0 +#define RT2860_WPDMA_BT_SIZE32 1 +#define RT2860_WPDMA_BT_SIZE64 2 +#define RT2860_WPDMA_BT_SIZE128 3 +#define RT2860_RX_DMA_BUSY (1 << 3) +#define RT2860_RX_DMA_EN (1 << 2) +#define RT2860_TX_DMA_BUSY (1 << 1) +#define RT2860_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 (1 << 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 USB_DMA_CFG */ +#define RT2860_USB_TX_BUSY (1U << 31) +#define RT2860_USB_RX_BUSY (1 << 30) +#define RT2860_USB_EPOUT_VLD_SHIFT 24 +#define RT2860_USB_TX_EN (1 << 23) +#define RT2860_USB_RX_EN (1 << 22) +#define RT2860_USB_RX_AGG_EN (1 << 21) +#define RT2860_USB_TXOP_HALT (1 << 20) +#define RT2860_USB_TX_CLEAR (1 << 19) +#define RT2860_USB_PHY_WD_EN (1 << 16) +#define RT2860_USB_RX_AGG_LMT(x) ((x) << 8) /* in unit of 1KB */ +#define RT2860_USB_RX_AGG_TO(x) ((x) & 0xff) /* in unit of 33ns */ + +/* possible flags for register US_CYC_CNT */ +#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 SYS_CTRL */ +#define RT2860_HST_PM_SEL (1 << 16) +#define RT2860_CAP_MODE (1 << 14) +#define RT2860_PME_OEN (1 << 13) +#define RT2860_CLKSELECT (1 << 12) +#define RT2860_PBF_CLK_EN (1 << 11) +#define RT2860_MAC_CLK_EN (1 << 10) +#define RT2860_DMA_CLK_EN (1 << 9) +#define RT2860_MCU_READY (1 << 7) +#define RT2860_ASY_RESET (1 << 4) +#define RT2860_PBF_RESET (1 << 3) +#define RT2860_MAC_RESET (1 << 2) +#define RT2860_DMA_RESET (1 << 1) +#define RT2860_MCU_RESET (1 << 0) + +/* possible values for register HOST_CMD */ +#define RT2860_MCU_CMD_SLEEP 0x30 +#define RT2860_MCU_CMD_WAKEUP 0x31 +#define RT2860_MCU_CMD_LEDS 0x50 +#define RT2860_MCU_CMD_LED_RSSI 0x51 +#define RT2860_MCU_CMD_LED1 0x52 +#define RT2860_MCU_CMD_LED2 0x53 +#define RT2860_MCU_CMD_LED3 0x54 +#define RT2860_MCU_CMD_RFRESET 0x72 +#define RT2860_MCU_CMD_ANTSEL 0x73 +#define RT2860_MCU_CMD_BBP 0x80 +#define RT2860_MCU_CMD_PSLEVEL 0x83 + +/* possible flags for register PBF_CFG */ +#define RT2860_TX1Q_NUM_SHIFT 21 +#define RT2860_TX2Q_NUM_SHIFT 16 +#define RT2860_NULL0_MODE (1 << 15) +#define RT2860_NULL1_MODE (1 << 14) +#define RT2860_RX_DROP_MODE (1 << 13) +#define RT2860_TX0Q_MANUAL (1 << 12) +#define RT2860_TX1Q_MANUAL (1 << 11) +#define RT2860_TX2Q_MANUAL (1 << 10) +#define RT2860_RX0Q_MANUAL (1 << 9) +#define RT2860_HCCA_EN (1 << 8) +#define RT2860_TX0Q_EN (1 << 4) +#define RT2860_TX1Q_EN (1 << 3) +#define RT2860_TX2Q_EN (1 << 2) +#define RT2860_RX0Q_EN (1 << 1) + +/* possible flags for register BUF_CTRL */ +#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 */ +#define RT2860_RX0Q_PCNT_MASK 0xff000000 +#define RT2860_TX2Q_PCNT_MASK 0x00ff0000 +#define RT2860_TX1Q_PCNT_MASK 0x0000ff00 +#define RT2860_TX0Q_PCNT_MASK 0x000000ff + +/* possible flags for register CAP_CTRL */ +#define RT2860_CAP_ADC_FEQ (1U << 31) +#define RT2860_CAP_START (1 << 30) +#define RT2860_MAN_TRIG (1 << 29) +#define RT2860_TRIG_OFFSET_SHIFT 16 +#define RT2860_START_ADDR_SHIFT 0 + +/* possible flags for register RF_CSR_CFG */ +#define RT3070_RF_KICK (1 << 17) +#define RT3070_RF_WRITE (1 << 16) + +/* possible flags for register EFUSE_CTRL */ +#define RT3070_SEL_EFUSE (1U << 31) +#define RT3070_EFSROM_KICK (1 << 30) +#define RT3070_EFSROM_AIN_MASK 0x03ff0000 +#define RT3070_EFSROM_AIN_SHIFT 16 +#define RT3070_EFSROM_MODE_MASK 0x000000c0 +#define RT3070_EFUSE_AOUT_MASK 0x0000003f + +/* possible flag for register DEBUG_INDEX */ +#define RT5592_SEL_XTAL (1U << 31) + +/* possible flags for register MAC_SYS_CTRL */ +#define RT2860_RX_TS_EN (1 << 7) +#define RT2860_WLAN_HALT_EN (1 << 6) +#define RT2860_PBF_LOOP_EN (1 << 5) +#define RT2860_CONT_TX_TEST (1 << 4) +#define RT2860_MAC_RX_EN (1 << 3) +#define RT2860_MAC_TX_EN (1 << 2) +#define RT2860_BBP_HRST (1 << 1) +#define RT2860_MAC_SRST (1 << 0) + +/* possible flags for register MAC_BSSID_DW1 */ +#define RT2860_MULTI_BCN_NUM_SHIFT 18 +#define RT2860_MULTI_BSSID_MODE_SHIFT 16 + +/* possible flags for register MAX_LEN_CFG */ +#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/H2M_BBPAGENT */ +#define RT2860_BBP_RW_PARALLEL (1 << 19) +#define RT2860_BBP_PAR_DUR_112_5 (1 << 18) +#define RT2860_BBP_CSR_KICK (1 << 17) +#define RT2860_BBP_CSR_READ (1 << 16) +#define RT2860_BBP_ADDR_SHIFT 8 +#define RT2860_BBP_DATA_SHIFT 0 + +/* possible flags for register RF_CSR_CFG0 */ +#define RT2860_RF_REG_CTRL (1U << 31) +#define RT2860_RF_LE_SEL1 (1 << 30) +#define RT2860_RF_LE_STBY (1 << 29) +#define RT2860_RF_REG_WIDTH_SHIFT 24 +#define RT2860_RF_REG_0_SHIFT 0 + +/* possible flags for register RF_CSR_CFG1 */ +#define RT2860_RF_DUR_5 (1 << 24) +#define RT2860_RF_REG_1_SHIFT 0 + +/* possible flags for register LED_CFG */ +#define RT2860_LED_POL (1 << 30) +#define RT2860_Y_LED_MODE_SHIFT 28 +#define RT2860_G_LED_MODE_SHIFT 26 +#define RT2860_R_LED_MODE_SHIFT 24 +#define RT2860_LED_MODE_OFF 0 +#define RT2860_LED_MODE_BLINK_TX 1 +#define RT2860_LED_MODE_SLOW_BLINK 2 +#define RT2860_LED_MODE_ON 3 +#define RT2860_SLOW_BLK_TIME_SHIFT 16 +#define RT2860_LED_OFF_TIME_SHIFT 8 +#define RT2860_LED_ON_TIME_SHIFT 0 + +/* possible flags for register XIFS_TIME_CFG */ +#define RT2860_BB_RXEND_EN (1 << 29) +#define RT2860_EIFS_TIME_SHIFT 20 +#define RT2860_OFDM_XIFS_TIME_SHIFT 16 +#define RT2860_OFDM_SIFS_TIME_SHIFT 8 +#define RT2860_CCK_SIFS_TIME_SHIFT 0 + +/* possible flags for register BKOFF_SLOT_CFG */ +#define RT2860_CC_DELAY_TIME_SHIFT 8 +#define RT2860_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 (1 << 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 */ +#define RT2860_TSF_INS_COMP_SHIFT 24 +#define RT2860_BCN_TX_EN (1 << 20) +#define RT2860_TBTT_TIMER_EN (1 << 19) +#define RT2860_TSF_SYNC_MODE_SHIFT 17 +#define RT2860_TSF_SYNC_MODE_DIS 0 +#define RT2860_TSF_SYNC_MODE_STA 1 +#define RT2860_TSF_SYNC_MODE_IBSS 2 +#define RT2860_TSF_SYNC_MODE_HOSTAP 3 +#define RT2860_TSF_TIMER_EN (1 << 16) +#define RT2860_BCN_INTVAL_SHIFT 0 + +/* possible flags for register TBTT_SYNC_CFG */ +#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 */ +#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 */ +#define RT2860_RX_STATUS_BUSY (1 << 1) +#define RT2860_TX_STATUS_BUSY (1 << 0) + +/* possible flags for register PWR_PIN_CFG */ +#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 */ +#define RT2860_AUTO_WAKEUP_EN (1 << 15) +#define RT2860_SLEEP_TBTT_NUM_SHIFT 8 +#define RT2860_WAKEUP_LEAD_TIME_SHIFT 0 + +/* possible flags for register TX_PIN_CFG */ +#define RT2860_TRSW_POL (1 << 19) +#define RT2860_TRSW_EN (1 << 18) +#define RT2860_RFTR_POL (1 << 17) +#define RT2860_RFTR_EN (1 << 16) +#define RT2860_LNA_PE_G1_POL (1 << 15) +#define RT2860_LNA_PE_A1_POL (1 << 14) +#define RT2860_LNA_PE_G0_POL (1 << 13) +#define RT2860_LNA_PE_A0_POL (1 << 12) +#define RT2860_LNA_PE_G1_EN (1 << 11) +#define RT2860_LNA_PE_A1_EN (1 << 10) +#define RT2860_LNA_PE1_EN (RT2860_LNA_PE_A1_EN | RT2860_LNA_PE_G1_EN) +#define RT2860_LNA_PE_G0_EN (1 << 9) +#define RT2860_LNA_PE_A0_EN (1 << 8) +#define RT2860_LNA_PE0_EN (RT2860_LNA_PE_A0_EN | RT2860_LNA_PE_G0_EN) +#define RT2860_PA_PE_G1_POL (1 << 7) +#define RT2860_PA_PE_A1_POL (1 << 6) +#define RT2860_PA_PE_G0_POL (1 << 5) +#define RT2860_PA_PE_A0_POL (1 << 4) +#define RT2860_PA_PE_G1_EN (1 << 3) +#define RT2860_PA_PE_A1_EN (1 << 2) +#define RT2860_PA_PE_G0_EN (1 << 1) +#define RT2860_PA_PE_A0_EN (1 << 0) + +/* possible flags for register TX_BAND_CFG */ +#define RT2860_5G_BAND_SEL_N (1 << 2) +#define RT2860_5G_BAND_SEL_P (1 << 1) +#define RT2860_TX_BAND_SEL (1 << 0) + +/* possible flags for register TX_SW_CFG0 */ +#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 */ +#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 */ +#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 */ +#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 */ +#define RT2860_EXT_CW_MIN_SHIFT 16 +#define RT2860_EXT_CCA_DLY_SHIFT 8 +#define RT2860_EXT_CCA_EN (1 << 7) +#define RT2860_LSIG_TXOP_EN (1 << 6) +#define RT2860_TXOP_TRUN_EN_MIMOPS (1 << 4) +#define RT2860_TXOP_TRUN_EN_TXOP (1 << 3) +#define RT2860_TXOP_TRUN_EN_RATE (1 << 2) +#define RT2860_TXOP_TRUN_EN_AC (1 << 1) +#define RT2860_TXOP_TRUN_EN_TIMEOUT (1 << 0) + +/* possible flags for register TX_RTS_CFG */ +#define RT2860_RTS_FBK_EN (1 << 24) +#define RT2860_RTS_THRES_SHIFT 8 +#define RT2860_RTS_RTY_LIMIT_SHIFT 0 + +/* possible flags for register TX_TIMEOUT_CFG */ +#define RT2860_TXOP_TIMEOUT_SHIFT 16 +#define RT2860_RX_ACK_TIMEOUT_SHIFT 8 +#define RT2860_MPDU_LIFE_TIME_SHIFT 4 + +/* possible flags for register TX_RTY_CFG */ +#define RT2860_TX_AUTOFB_EN (1 << 30) +#define RT2860_AGG_RTY_MODE_TIMER (1 << 29) +#define RT2860_NAG_RTY_MODE_TIMER (1 << 28) +#define RT2860_LONG_RTY_THRES_SHIFT 16 +#define RT2860_LONG_RTY_LIMIT_SHIFT 8 +#define RT2860_SHORT_RTY_LIMIT_SHIFT 0 + +/* possible flags for register TX_LINK_CFG */ +#define RT2860_REMOTE_MFS_SHIFT 24 +#define RT2860_REMOTE_MFB_SHIFT 16 +#define RT2860_TX_CFACK_EN (1 << 12) +#define RT2860_TX_RDG_EN (1 << 11) +#define RT2860_TX_MRQ_EN (1 << 10) +#define RT2860_REMOTE_UMFS_EN (1 << 9) +#define RT2860_TX_MFB_EN (1 << 8) +#define RT2860_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 */ +#define RT2860_DROP_CTRL_RSV (1 << 16) +#define RT2860_DROP_BAR (1 << 15) +#define RT2860_DROP_BA (1 << 14) +#define RT2860_DROP_PSPOLL (1 << 13) +#define RT2860_DROP_RTS (1 << 12) +#define RT2860_DROP_CTS (1 << 11) +#define RT2860_DROP_ACK (1 << 10) +#define RT2860_DROP_CFEND (1 << 9) +#define RT2860_DROP_CFACK (1 << 8) +#define RT2860_DROP_DUPL (1 << 7) +#define RT2860_DROP_BC (1 << 6) +#define RT2860_DROP_MC (1 << 5) +#define RT2860_DROP_VER_ERR (1 << 4) +#define RT2860_DROP_NOT_MYBSS (1 << 3) +#define RT2860_DROP_UC_NOME (1 << 2) +#define RT2860_DROP_PHY_ERR (1 << 1) +#define RT2860_DROP_CRC_ERR (1 << 0) + +/* possible flags for register AUTO_RSP_CFG */ +#define RT2860_CTRL_PWR_BIT (1 << 7) +#define RT2860_BAC_ACK_POLICY (1 << 6) +#define RT2860_CCK_SHORT_EN (1 << 4) +#define RT2860_CTS_40M_REF_EN (1 << 3) +#define RT2860_CTS_40M_MODE_EN (1 << 2) +#define RT2860_BAC_ACKPOLICY_EN (1 << 1) +#define RT2860_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 */ +#define RT2860_TXOP_ETM1_EN (1 << 25) +#define RT2860_TXOP_ETM0_EN (1 << 24) +#define RT2860_TXOP_ETM_THRES_SHIFT 16 +#define RT2860_TXOP_ETO_EN (1 << 8) +#define RT2860_TXOP_ETO_THRES_SHIFT 1 +#define RT2860_PER_RX_RST_EN (1 << 0) + +/* possible flags for register TX_STAT_FIFO */ +#define RT2860_TXQ_MCS_SHIFT 16 +#define RT2860_TXQ_WCID_SHIFT 8 +#define RT2860_TXQ_ACKREQ (1 << 7) +#define RT2860_TXQ_AGG (1 << 6) +#define RT2860_TXQ_OK (1 << 5) +#define RT2860_TXQ_PID_SHIFT 1 +#define RT2860_TXQ_VLD (1 << 0) + +/* possible flags for register WCID_ATTR */ +#define RT2860_MODE_NOSEC 0 +#define RT2860_MODE_WEP40 1 +#define RT2860_MODE_WEP104 2 +#define RT2860_MODE_TKIP 3 +#define RT2860_MODE_AES_CCMP 4 +#define RT2860_MODE_CKIP40 5 +#define RT2860_MODE_CKIP104 6 +#define RT2860_MODE_CKIP128 7 +#define RT2860_RX_PKEY_EN (1 << 0) + +/* possible flags for register H2M_MAILBOX */ +#define RT2860_H2M_BUSY (1 << 24) +#define RT2860_TOKEN_NO_INTR 0xff + +/* possible flags for MCU command RT2860_MCU_CMD_LEDS */ +#define RT2860_LED_RADIO (1 << 13) +#define RT2860_LED_LINK_2GHZ (1 << 14) +#define RT2860_LED_LINK_5GHZ (1 << 15) + +/* possible flags for RT3020 RF register 1 */ +#define RT3070_RF_BLOCK (1 << 0) +#define RT3070_PLL_PD (1 << 1) +#define RT3070_RX0_PD (1 << 2) +#define RT3070_TX0_PD (1 << 3) +#define RT3070_RX1_PD (1 << 4) +#define RT3070_TX1_PD (1 << 5) +#define RT3070_RX2_PD (1 << 6) +#define RT3070_TX2_PD (1 << 7) + +/* possible flags for RT3020 RF register 15 */ +#define RT3070_TX_LO2 (1 << 3) + +/* possible flags for RT3020 RF register 17 */ +#define RT3070_TX_LO1 (1 << 3) + +/* possible flags for RT3020 RF register 20 */ +#define RT3070_RX_LO1 (1 << 3) + +/* possible flags for RT3020 RF register 21 */ +#define RT3070_RX_LO2 (1 << 3) + +/* possible flags for RT3053 RF register 18 */ +#define RT3593_AUTOTUNE_BYPASS (1 << 6) + +/* possible flags for RT3053 RF register 50 */ +#define RT3593_TX_LO2 (1 << 4) + +/* possible flags for RT3053 RF register 51 */ +#define RT3593_TX_LO1 (1 << 4) + +/* Possible flags for RT5390 RF register 2. */ +#define RT5390_RESCAL (1 << 7) + +/* Possible flags for RT5390 RF register 3. */ +#define RT5390_VCOCAL (1 << 7) + +/* Possible flags for RT5390 RF register 38. */ +#define RT5390_RX_LO1 (1 << 5) + +/* Possible flags for RT5390 RF register 39. */ +#define RT5390_RX_LO2 (1 << 7) + +/* Possible flags for RT5390 BBP register 4. */ +#define RT5390_MAC_IF_CTRL (1 << 6) + +/* Possible flags for RT5390 BBP register 105. */ +#define RT5390_MLD (1 << 2) +#define RT5390_EN_SIG_MODULATION (1 << 3) + +/* 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; + +/* RT2870 TX descriptor */ +struct rt2870_txd { + uint16_t len; + uint8_t pad; + uint8_t flags; +} __packed; + +/* TX Wireless Information */ +struct rt2860_txwi { + uint8_t flags; +#define RT2860_TX_MPDU_DSITY_SHIFT 5 +#define RT2860_TX_AMPDU (1 << 4) +#define RT2860_TX_TS (1 << 3) +#define RT2860_TX_CFACK (1 << 2) +#define RT2860_TX_MMPS (1 << 1) +#define RT2860_TX_FRAG (1 << 0) + + uint8_t txop; +#define RT2860_TX_TXOP_HT 0 +#define RT2860_TX_TXOP_PIFS 1 +#define RT2860_TX_TXOP_SIFS 2 +#define RT2860_TX_TXOP_BACKOFF 3 + + uint16_t phy; +#define RT2860_PHY_MODE 0xc000 +#define RT2860_PHY_CCK (0 << 14) +#define RT2860_PHY_OFDM (1 << 14) +#define RT2860_PHY_HT_MIX (2 << 14) +#define RT2860_PHY_HT_GF (3 << 14) +#define RT2860_PHY_SGI (1 << 8) +#define RT2860_PHY_BW40 (1 << 7) +#define RT2860_PHY_MCS 0x7f +#define RT2860_PHY_SHPRE (1 << 3) + + uint8_t xflags; +#define RT2860_TX_BAWINSIZE_SHIFT 2 +#define RT2860_TX_NSEQ (1 << 1) +#define RT2860_TX_ACK (1 << 0) + + uint8_t wcid; /* Wireless Client ID */ + uint16_t len; +#define RT2860_TX_PID_SHIFT 12 + + uint32_t iv; + uint32_t eiv; +} __packed; + +/* RT2860 RX descriptor */ +struct rt2860_rxd { + uint32_t sdp0; + uint16_t sdl1; /* unused */ + uint16_t sdl0; +#define RT2860_RX_DDONE (1 << 15) +#define RT2860_RX_LS0 (1 << 14) + + uint32_t sdp1; /* unused */ + uint32_t flags; +#define RT2860_RX_DEC (1 << 16) +#define RT2860_RX_AMPDU (1 << 15) +#define RT2860_RX_L2PAD (1 << 14) +#define RT2860_RX_RSSI (1 << 13) +#define RT2860_RX_HTC (1 << 12) +#define RT2860_RX_AMSDU (1 << 11) +#define RT2860_RX_MICERR (1 << 10) +#define RT2860_RX_ICVERR (1 << 9) +#define RT2860_RX_CRCERR (1 << 8) +#define RT2860_RX_MYBSS (1 << 7) +#define RT2860_RX_BC (1 << 6) +#define RT2860_RX_MC (1 << 5) +#define RT2860_RX_UC2ME (1 << 4) +#define RT2860_RX_FRAG (1 << 3) +#define RT2860_RX_NULL (1 << 2) +#define RT2860_RX_DATA (1 << 1) +#define RT2860_RX_BA (1 << 0) +} __packed; + +/* RT2870 RX descriptor */ +struct rt2870_rxd { + /* single 32-bit field */ + uint32_t flags; +} __packed; + +/* RX Wireless Information */ +struct rt2860_rxwi { + uint8_t wcid; + uint8_t keyidx; +#define RT2860_RX_UDF_SHIFT 5 +#define RT2860_RX_BSS_IDX_SHIFT 2 + + uint16_t len; +#define RT2860_RX_TID_SHIFT 12 + + uint16_t seq; + uint16_t phy; + uint8_t rssi[3]; + uint8_t reserved1; + uint8_t snr[2]; + uint16_t reserved2; +} __packed; + +#define RT2860_RF_2820 0x0001 /* 2T3R */ +#define RT2860_RF_2850 0x0002 /* dual-band 2T3R */ +#define RT2860_RF_2720 0x0003 /* 1T2R */ +#define RT2860_RF_2750 0x0004 /* dual-band 1T2R */ +#define RT3070_RF_3020 0x0005 /* 1T1R */ +#define RT3070_RF_2020 0x0006 /* b/g */ +#define RT3070_RF_3021 0x0007 /* 1T2R */ +#define RT3070_RF_3022 0x0008 /* 2T2R */ +#define RT3070_RF_3052 0x0009 /* dual-band 2T2R */ +#define RT3593_RF_3053 0x000d /* dual-band 3T3R */ +#define RT5592_RF_5592 0x000f /* dual-band 2T2R */ +#define RT5390_RF_5370 0x5370 /* 1T1R */ +#define RT5390_RF_5372 0x5372 /* 2T2R */ + +/* USB commands for RT2870 only */ +#define RT2870_RESET 1 +#define RT2870_WRITE_2 2 +#define RT2870_WRITE_REGION_1 6 +#define RT2870_READ_REGION_1 7 +#define RT2870_EEPROM_READ 9 + +#define RT2860_EEPROM_DELAY 1 /* minimum hold time (microsecond) */ + +#define RT2860_EEPROM_VERSION 0x01 +#define RT2860_EEPROM_MAC01 0x02 +#define RT2860_EEPROM_MAC23 0x03 +#define RT2860_EEPROM_MAC45 0x04 +#define RT2860_EEPROM_PCIE_PSLEVEL 0x11 +#define RT2860_EEPROM_REV 0x12 +#define RT2860_EEPROM_ANTENNA 0x1a +#define RT2860_EEPROM_CONFIG 0x1b +#define RT2860_EEPROM_COUNTRY 0x1c +#define RT2860_EEPROM_FREQ_LEDS 0x1d +#define RT2860_EEPROM_LED1 0x1e +#define RT2860_EEPROM_LED2 0x1f +#define RT2860_EEPROM_LED3 0x20 +#define RT2860_EEPROM_LNA 0x22 +#define RT2860_EEPROM_RSSI1_2GHZ 0x23 +#define RT2860_EEPROM_RSSI2_2GHZ 0x24 +#define RT2860_EEPROM_RSSI1_5GHZ 0x25 +#define RT2860_EEPROM_RSSI2_5GHZ 0x26 +#define RT2860_EEPROM_DELTAPWR 0x28 +#define RT2860_EEPROM_PWR2GHZ_BASE1 0x29 +#define RT2860_EEPROM_PWR2GHZ_BASE2 0x30 +#define RT2860_EEPROM_TSSI1_2GHZ 0x37 +#define RT2860_EEPROM_TSSI2_2GHZ 0x38 +#define RT2860_EEPROM_TSSI3_2GHZ 0x39 +#define RT2860_EEPROM_TSSI4_2GHZ 0x3a +#define RT2860_EEPROM_TSSI5_2GHZ 0x3b +#define RT2860_EEPROM_PWR5GHZ_BASE1 0x3c +#define RT2860_EEPROM_PWR5GHZ_BASE2 0x53 +#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 RT2860_EEPROM_RPWR 0x6f +#define RT2860_EEPROM_BBP_BASE 0x78 +#define RT3071_EEPROM_RF_BASE 0x82 + +/* EEPROM registers for RT3593. */ +#define RT3593_EEPROM_FREQ_LEDS 0x21 +#define RT3593_EEPROM_FREQ 0x22 +#define RT3593_EEPROM_LED1 0x22 +#define RT3593_EEPROM_LED2 0x23 +#define RT3593_EEPROM_LED3 0x24 +#define RT3593_EEPROM_LNA 0x26 +#define RT3593_EEPROM_LNA_5GHZ 0x27 +#define RT3593_EEPROM_RSSI1_2GHZ 0x28 +#define RT3593_EEPROM_RSSI2_2GHZ 0x29 +#define RT3593_EEPROM_RSSI1_5GHZ 0x2a +#define RT3593_EEPROM_RSSI2_5GHZ 0x2b +#define RT3593_EEPROM_PWR2GHZ_BASE1 0x30 +#define RT3593_EEPROM_PWR2GHZ_BASE2 0x37 +#define RT3593_EEPROM_PWR2GHZ_BASE3 0x3e +#define RT3593_EEPROM_PWR5GHZ_BASE1 0x4b +#define RT3593_EEPROM_PWR5GHZ_BASE2 0x65 +#define RT3593_EEPROM_PWR5GHZ_BASE3 0x7f + +/* + * EEPROM IQ calibration. + */ +#define RT5390_EEPROM_IQ_GAIN_CAL_TX0_2GHZ 0x130 +#define RT5390_EEPROM_IQ_PHASE_CAL_TX0_2GHZ 0x131 +#define RT5390_EEPROM_IQ_GAIN_CAL_TX1_2GHZ 0x133 +#define RT5390_EEPROM_IQ_PHASE_CAL_TX1_2GHZ 0x134 +#define RT5390_EEPROM_RF_IQ_COMPENSATION_CTL 0x13c +#define RT5390_EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CTL 0x13d +#define RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5GHZ 0x144 +#define RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5GHZ 0x145 +#define RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5GHZ 0x146 +#define RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5GHZ 0x147 +#define RT5390_EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5GHZ 0x148 +#define RT5390_EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5GHZ 0x149 +#define RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5GHZ 0x14a +#define RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5GHZ 0x14b +#define RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5GHZ 0x14c +#define RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5GHZ 0x14d +#define RT5390_EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5GHZ 0x14e +#define RT5390_EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5GHZ 0x14f + +/* + * EEPROM access macro. + */ +#define RT2860_EEPROM_CTL(sc, val) do { \ + RAL_WRITE((sc), RT2860_PCI_EECTRL, (val)); \ + RAL_BARRIER_READ_WRITE((sc)); \ + DELAY(RT2860_EEPROM_DELAY); \ +} while (/* CONSTCOND */0) + +/* + * Default values for MAC registers; values taken from the reference driver. + */ +#define RT2870_DEF_MAC \ + { RT2860_BCN_OFFSET0, 0xf8f0e8e0 }, \ + { RT2860_BCN_OFFSET1, 0x6f77d0c8 }, \ + { RT2860_LEGACY_BASIC_RATE, 0x0000013f }, \ + { RT2860_HT_BASIC_RATE, 0x00008003 }, \ + { RT2860_MAC_SYS_CTRL, 0x00000000 }, \ + { RT2860_BKOFF_SLOT_CFG, 0x00000209 }, \ + { RT2860_TX_SW_CFG0, 0x00000000 }, \ + { RT2860_TX_SW_CFG1, 0x00080606 }, \ + { RT2860_TX_LINK_CFG, 0x00001020 }, \ + { RT2860_TX_TIMEOUT_CFG, 0x000a2090 }, \ + { RT2860_MAX_LEN_CFG, 0x00001f00 }, \ + { RT2860_LED_CFG, 0x7f031e46 }, \ + { RT2860_WMM_AIFSN_CFG, 0x00002273 }, \ + { RT2860_WMM_CWMIN_CFG, 0x00002344 }, \ + { RT2860_WMM_CWMAX_CFG, 0x000034aa }, \ + { RT2860_MAX_PCNT, 0x1f3fbf9f }, \ + { RT2860_TX_RTY_CFG, 0x47d01f0f }, \ + { RT2860_AUTO_RSP_CFG, 0x00000013 }, \ + { RT2860_CCK_PROT_CFG, 0x05740003 }, \ + { RT2860_OFDM_PROT_CFG, 0x05740003 }, \ + { RT2860_PBF_CFG, 0x00f40006 }, \ + { RT2860_WPDMA_GLO_CFG, 0x00000030 }, \ + { RT2860_GF20_PROT_CFG, 0x01744004 }, \ + { RT2860_GF40_PROT_CFG, 0x03f44084 }, \ + { RT2860_MM20_PROT_CFG, 0x01744004 }, \ + { RT2860_MM40_PROT_CFG, 0x03f44084 }, \ + { RT2860_TXOP_CTRL_CFG, 0x0000583f }, \ + { RT2860_TXOP_HLDR_ET, 0x00000002 }, \ + { RT2860_TX_RTS_CFG, 0x00092b20 }, \ + { RT2860_EXP_ACK_TIME, 0x002400ca }, \ + { RT2860_XIFS_TIME_CFG, 0x33a41010 }, \ + { RT2860_PWR_PIN_CFG, 0x00000003 } + +/* + * Default values for BBP registers; values taken from the reference driver. + */ +#define RT2860_DEF_BBP \ + { 65, 0x2c }, \ + { 66, 0x38 }, \ + { 68, 0x0b }, \ + { 69, 0x12 }, \ + { 70, 0x0a }, \ + { 73, 0x10 }, \ + { 81, 0x37 }, \ + { 82, 0x62 }, \ + { 83, 0x6a }, \ + { 84, 0x99 }, \ + { 86, 0x00 }, \ + { 91, 0x04 }, \ + { 92, 0x00 }, \ + { 103, 0x00 }, \ + { 105, 0x05 }, \ + { 106, 0x35 } + +#define RT5390_DEF_BBP \ + { 31, 0x08 }, \ + { 65, 0x2c }, \ + { 66, 0x38 }, \ + { 68, 0x0b }, \ + { 69, 0x0d }, \ + { 70, 0x06 }, \ + { 73, 0x13 }, \ + { 75, 0x46 }, \ + { 76, 0x28 }, \ + { 77, 0x59 }, \ + { 81, 0x37 }, \ + { 82, 0x62 }, \ + { 83, 0x7a }, \ + { 84, 0x9a }, \ + { 86, 0x38 }, \ + { 91, 0x04 }, \ + { 92, 0x02 }, \ + { 103, 0xc0 }, \ + { 104, 0x92 }, \ + { 105, 0x3c }, \ + { 106, 0x03 }, \ + { 128, 0x12 } + +#define RT5592_DEF_BBP \ + { 20, 0x06 }, \ + { 31, 0x08 }, \ + { 65, 0x2c }, \ + { 66, 0x38 }, \ + { 68, 0xdd }, \ + { 69, 0x1a }, \ + { 70, 0x05 }, \ + { 73, 0x13 }, \ + { 74, 0x0f }, \ + { 75, 0x4f }, \ + { 76, 0x28 }, \ + { 77, 0x59 }, \ + { 81, 0x37 }, \ + { 82, 0x62 }, \ + { 83, 0x6a }, \ + { 84, 0x9a }, \ + { 86, 0x38 }, \ + { 88, 0x90 }, \ + { 91, 0x04 }, \ + { 92, 0x02 }, \ + { 95, 0x9a }, \ + { 98, 0x12 }, \ + { 103, 0xc0 }, \ + { 104, 0x92 }, \ + { 105, 0x3c }, \ + { 106, 0x35 }, \ + { 128, 0x12 }, \ + { 134, 0xd0 }, \ + { 135, 0xf6 }, \ + { 137, 0x0f } + +/* + * Channel map for run(4) driver; taken from the table below. + */ +static const uint8_t run_chan_5ghz[] = + { 36, 38, 40, 44, 46, 48, 52, 54, 56, 60, 62, 64, 100, 102, 104, + 108, 110, 112, 116, 118, 120, 124, 126, 128, 132, 134, 136, 140, + 149, 151, 153, 157, 159, 161, 165, 167, 169, 171, 173, + 184, 188, 192, 196, 208, 212, 216 }; + +/* + * Default settings for RF registers; values derived from the reference driver. + */ +#define RT2860_RF2850 \ + { 1, 0x98402ecc, 0x984c0786, 0x9816b455, 0x9800510b }, \ + { 2, 0x98402ecc, 0x984c0786, 0x98168a55, 0x9800519f }, \ + { 3, 0x98402ecc, 0x984c078a, 0x98168a55, 0x9800518b }, \ + { 4, 0x98402ecc, 0x984c078a, 0x98168a55, 0x9800519f }, \ + { 5, 0x98402ecc, 0x984c078e, 0x98168a55, 0x9800518b }, \ + { 6, 0x98402ecc, 0x984c078e, 0x98168a55, 0x9800519f }, \ + { 7, 0x98402ecc, 0x984c0792, 0x98168a55, 0x9800518b }, \ + { 8, 0x98402ecc, 0x984c0792, 0x98168a55, 0x9800519f }, \ + { 9, 0x98402ecc, 0x984c0796, 0x98168a55, 0x9800518b }, \ + { 10, 0x98402ecc, 0x984c0796, 0x98168a55, 0x9800519f }, \ + { 11, 0x98402ecc, 0x984c079a, 0x98168a55, 0x9800518b }, \ + { 12, 0x98402ecc, 0x984c079a, 0x98168a55, 0x9800519f }, \ + { 13, 0x98402ecc, 0x984c079e, 0x98168a55, 0x9800518b }, \ + { 14, 0x98402ecc, 0x984c07a2, 0x98168a55, 0x98005193 }, \ + { 36, 0x98402ecc, 0x984c099a, 0x98158a55, 0x980ed1a3 }, \ + { 38, 0x98402ecc, 0x984c099e, 0x98158a55, 0x980ed193 }, \ + { 40, 0x98402ec8, 0x984c0682, 0x98158a55, 0x980ed183 }, \ + { 44, 0x98402ec8, 0x984c0682, 0x98158a55, 0x980ed1a3 }, \ + { 46, 0x98402ec8, 0x984c0686, 0x98158a55, 0x980ed18b }, \ + { 48, 0x98402ec8, 0x984c0686, 0x98158a55, 0x980ed19b }, \ + { 52, 0x98402ec8, 0x984c068a, 0x98158a55, 0x980ed193 }, \ + { 54, 0x98402ec8, 0x984c068a, 0x98158a55, 0x980ed1a3 }, \ + { 56, 0x98402ec8, 0x984c068e, 0x98158a55, 0x980ed18b }, \ + { 60, 0x98402ec8, 0x984c0692, 0x98158a55, 0x980ed183 }, \ + { 62, 0x98402ec8, 0x984c0692, 0x98158a55, 0x980ed193 }, \ + { 64, 0x98402ec8, 0x984c0692, 0x98158a55, 0x980ed1a3 }, \ + { 100, 0x98402ec8, 0x984c06b2, 0x98178a55, 0x980ed783 }, \ + { 102, 0x98402ec8, 0x985c06b2, 0x98578a55, 0x980ed793 }, \ + { 104, 0x98402ec8, 0x985c06b2, 0x98578a55, 0x980ed1a3 }, \ + { 108, 0x98402ecc, 0x985c0a32, 0x98578a55, 0x980ed193 }, \ + { 110, 0x98402ecc, 0x984c0a36, 0x98178a55, 0x980ed183 }, \ + { 112, 0x98402ecc, 0x984c0a36, 0x98178a55, 0x980ed19b }, \ + { 116, 0x98402ecc, 0x984c0a3a, 0x98178a55, 0x980ed1a3 }, \ + { 118, 0x98402ecc, 0x984c0a3e, 0x98178a55, 0x980ed193 }, \ + { 120, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed183 }, \ + { 124, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed193 }, \ + { 126, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed15b }, \ + { 128, 0x98402ec4, 0x984c0382, 0x98178a55, 0x980ed1a3 }, \ + { 132, 0x98402ec4, 0x984c0386, 0x98178a55, 0x980ed18b }, \ + { 134, 0x98402ec4, 0x984c0386, 0x98178a55, 0x980ed193 }, \ + { 136, 0x98402ec4, 0x984c0386, 0x98178a55, 0x980ed19b }, \ + { 140, 0x98402ec4, 0x984c038a, 0x98178a55, 0x980ed183 }, \ + { 149, 0x98402ec4, 0x984c038a, 0x98178a55, 0x980ed1a7 }, \ + { 151, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed187 }, \ + { 153, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed18f }, \ + { 157, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed19f }, \ + { 159, 0x98402ec4, 0x984c038e, 0x98178a55, 0x980ed1a7 }, \ + { 161, 0x98402ec4, 0x984c0392, 0x98178a55, 0x980ed187 }, \ + { 165, 0x98402ec4, 0x984c0392, 0x98178a55, 0x980ed197 }, \ + { 167, 0x98402ec4, 0x984c03d2, 0x98179855, 0x9815531f }, \ + { 169, 0x98402ec4, 0x984c03d2, 0x98179855, 0x98155327 }, \ + { 171, 0x98402ec4, 0x984c03d6, 0x98179855, 0x98155307 }, \ + { 173, 0x98402ec4, 0x984c03d6, 0x98179855, 0x9815530f }, \ + { 184, 0x95002ccc, 0x9500491e, 0x9509be55, 0x950c0a0b }, \ + { 188, 0x95002ccc, 0x95004922, 0x9509be55, 0x950c0a13 }, \ + { 192, 0x95002ccc, 0x95004926, 0x9509be55, 0x950c0a1b }, \ + { 196, 0x95002ccc, 0x9500492a, 0x9509be55, 0x950c0a23 }, \ + { 208, 0x95002ccc, 0x9500493a, 0x9509be55, 0x950c0a13 }, \ + { 212, 0x95002ccc, 0x9500493e, 0x9509be55, 0x950c0a1b }, \ + { 216, 0x95002ccc, 0x95004982, 0x9509be55, 0x950c0a23 } + +#define RT3070_RF3052 \ + { 0xf1, 2, 2 }, \ + { 0xf1, 2, 7 }, \ + { 0xf2, 2, 2 }, \ + { 0xf2, 2, 7 }, \ + { 0xf3, 2, 2 }, \ + { 0xf3, 2, 7 }, \ + { 0xf4, 2, 2 }, \ + { 0xf4, 2, 7 }, \ + { 0xf5, 2, 2 }, \ + { 0xf5, 2, 7 }, \ + { 0xf6, 2, 2 }, \ + { 0xf6, 2, 7 }, \ + { 0xf7, 2, 2 }, \ + { 0xf8, 2, 4 }, \ + { 0x56, 0, 4 }, \ + { 0x56, 0, 6 }, \ + { 0x56, 0, 8 }, \ + { 0x57, 0, 0 }, \ + { 0x57, 0, 2 }, \ + { 0x57, 0, 4 }, \ + { 0x57, 0, 8 }, \ + { 0x57, 0, 10 }, \ + { 0x58, 0, 0 }, \ + { 0x58, 0, 4 }, \ + { 0x58, 0, 6 }, \ + { 0x58, 0, 8 }, \ + { 0x5b, 0, 8 }, \ + { 0x5b, 0, 10 }, \ + { 0x5c, 0, 0 }, \ + { 0x5c, 0, 4 }, \ + { 0x5c, 0, 6 }, \ + { 0x5c, 0, 8 }, \ + { 0x5d, 0, 0 }, \ + { 0x5d, 0, 2 }, \ + { 0x5d, 0, 4 }, \ + { 0x5d, 0, 8 }, \ + { 0x5d, 0, 10 }, \ + { 0x5e, 0, 0 }, \ + { 0x5e, 0, 4 }, \ + { 0x5e, 0, 6 }, \ + { 0x5e, 0, 8 }, \ + { 0x5f, 0, 0 }, \ + { 0x5f, 0, 9 }, \ + { 0x5f, 0, 11 }, \ + { 0x60, 0, 1 }, \ + { 0x60, 0, 5 }, \ + { 0x60, 0, 7 }, \ + { 0x60, 0, 9 }, \ + { 0x61, 0, 1 }, \ + { 0x61, 0, 3 }, \ + { 0x61, 0, 5 }, \ + { 0x61, 0, 7 }, \ + { 0x61, 0, 9 } + +#define RT5592_RF5592_20MHZ \ + { 0x1e2, 4, 10, 3 }, \ + { 0x1e3, 4, 10, 3 }, \ + { 0x1e4, 4, 10, 3 }, \ + { 0x1e5, 4, 10, 3 }, \ + { 0x1e6, 4, 10, 3 }, \ + { 0x1e7, 4, 10, 3 }, \ + { 0x1e8, 4, 10, 3 }, \ + { 0x1e9, 4, 10, 3 }, \ + { 0x1ea, 4, 10, 3 }, \ + { 0x1eb, 4, 10, 3 }, \ + { 0x1ec, 4, 10, 3 }, \ + { 0x1ed, 4, 10, 3 }, \ + { 0x1ee, 4, 10, 3 }, \ + { 0x1f0, 8, 10, 3 }, \ + { 0xac, 8, 12, 1 }, \ + { 0xad, 0, 12, 1 }, \ + { 0xad, 4, 12, 1 }, \ + { 0xae, 0, 12, 1 }, \ + { 0xae, 4, 12, 1 }, \ + { 0xae, 8, 12, 1 }, \ + { 0xaf, 4, 12, 1 }, \ + { 0xaf, 8, 12, 1 }, \ + { 0xb0, 0, 12, 1 }, \ + { 0xb0, 8, 12, 1 }, \ + { 0xb1, 0, 12, 1 }, \ + { 0xb1, 4, 12, 1 }, \ + { 0xb7, 4, 12, 1 }, \ + { 0xb7, 8, 12, 1 }, \ + { 0xb8, 0, 12, 1 }, \ + { 0xb8, 8, 12, 1 }, \ + { 0xb9, 0, 12, 1 }, \ + { 0xb9, 4, 12, 1 }, \ + { 0xba, 0, 12, 1 }, \ + { 0xba, 4, 12, 1 }, \ + { 0xba, 8, 12, 1 }, \ + { 0xbb, 4, 12, 1 }, \ + { 0xbb, 8, 12, 1 }, \ + { 0xbc, 0, 12, 1 }, \ + { 0xbc, 8, 12, 1 }, \ + { 0xbd, 0, 12, 1 }, \ + { 0xbd, 4, 12, 1 }, \ + { 0xbe, 0, 12, 1 }, \ + { 0xbf, 6, 12, 1 }, \ + { 0xbf, 10, 12, 1 }, \ + { 0xc0, 2, 12, 1 }, \ + { 0xc0, 10, 12, 1 }, \ + { 0xc1, 2, 12, 1 }, \ + { 0xc1, 6, 12, 1 }, \ + { 0xc2, 2, 12, 1 }, \ + { 0xa4, 0, 12, 1 }, \ + { 0xa4, 4, 12, 1 }, \ + { 0xa5, 8, 12, 1 }, \ + { 0xa6, 0, 12, 1 } + +#define RT5592_RF5592_40MHZ \ + { 0xf1, 2, 10, 3 }, \ + { 0xf1, 7, 10, 3 }, \ + { 0xf2, 2, 10, 3 }, \ + { 0xf2, 7, 10, 3 }, \ + { 0xf3, 2, 10, 3 }, \ + { 0xf3, 7, 10, 3 }, \ + { 0xf4, 2, 10, 3 }, \ + { 0xf4, 7, 10, 3 }, \ + { 0xf5, 2, 10, 3 }, \ + { 0xf5, 7, 10, 3 }, \ + { 0xf6, 2, 10, 3 }, \ + { 0xf6, 7, 10, 3 }, \ + { 0xf7, 2, 10, 3 }, \ + { 0xf8, 4, 10, 3 }, \ + { 0x56, 4, 12, 1 }, \ + { 0x56, 6, 12, 1 }, \ + { 0x56, 8, 12, 1 }, \ + { 0x57, 0, 12, 1 }, \ + { 0x57, 2, 12, 1 }, \ + { 0x57, 4, 12, 1 }, \ + { 0x57, 8, 12, 1 }, \ + { 0x57, 10, 12, 1 }, \ + { 0x58, 0, 12, 1 }, \ + { 0x58, 4, 12, 1 }, \ + { 0x58, 6, 12, 1 }, \ + { 0x58, 8, 12, 1 }, \ + { 0x5b, 8, 12, 1 }, \ + { 0x5b, 10, 12, 1 }, \ + { 0x5c, 0, 12, 1 }, \ + { 0x5c, 4, 12, 1 }, \ + { 0x5c, 6, 12, 1 }, \ + { 0x5c, 8, 12, 1 }, \ + { 0x5d, 0, 12, 1 }, \ + { 0x5d, 2, 12, 1 }, \ + { 0x5d, 4, 12, 1 }, \ + { 0x5d, 8, 12, 1 }, \ + { 0x5d, 10, 12, 1 }, \ + { 0x5e, 0, 12, 1 }, \ + { 0x5e, 4, 12, 1 }, \ + { 0x5e, 6, 12, 1 }, \ + { 0x5e, 8, 12, 1 }, \ + { 0x5f, 0, 12, 1 }, \ + { 0x5f, 9, 12, 1 }, \ + { 0x5f, 11, 12, 1 }, \ + { 0x60, 1, 12, 1 }, \ + { 0x60, 5, 12, 1 }, \ + { 0x60, 7, 12, 1 }, \ + { 0x60, 9, 12, 1 }, \ + { 0x61, 1, 12, 1 }, \ + { 0x52, 0, 12, 1 }, \ + { 0x52, 4, 12, 1 }, \ + { 0x52, 8, 12, 1 }, \ + { 0x53, 0, 12, 1 } + +#define RT3070_DEF_RF \ + { 4, 0x40 }, \ + { 5, 0x03 }, \ + { 6, 0x02 }, \ + { 7, 0x60 }, \ + { 9, 0x0f }, \ + { 10, 0x41 }, \ + { 11, 0x21 }, \ + { 12, 0x7b }, \ + { 14, 0x90 }, \ + { 15, 0x58 }, \ + { 16, 0xb3 }, \ + { 17, 0x92 }, \ + { 18, 0x2c }, \ + { 19, 0x02 }, \ + { 20, 0xba }, \ + { 21, 0xdb }, \ + { 24, 0x16 }, \ + { 25, 0x03 }, \ + { 29, 0x1f } + +#define RT3572_DEF_RF \ + { 0, 0x70 }, \ + { 1, 0x81 }, \ + { 2, 0xf1 }, \ + { 3, 0x02 }, \ + { 4, 0x4c }, \ + { 5, 0x05 }, \ + { 6, 0x4a }, \ + { 7, 0xd8 }, \ + { 9, 0xc3 }, \ + { 10, 0xf1 }, \ + { 11, 0xb9 }, \ + { 12, 0x70 }, \ + { 13, 0x65 }, \ + { 14, 0xa0 }, \ + { 15, 0x53 }, \ + { 16, 0x4c }, \ + { 17, 0x23 }, \ + { 18, 0xac }, \ + { 19, 0x93 }, \ + { 20, 0xb3 }, \ + { 21, 0xd0 }, \ + { 22, 0x00 }, \ + { 23, 0x3c }, \ + { 24, 0x16 }, \ + { 25, 0x15 }, \ + { 26, 0x85 }, \ + { 27, 0x00 }, \ + { 28, 0x00 }, \ + { 29, 0x9b }, \ + { 30, 0x09 }, \ + { 31, 0x10 } + +#define RT3593_DEF_RF \ + { 1, 0x03 }, \ + { 3, 0x80 }, \ + { 5, 0x00 }, \ + { 6, 0x40 }, \ + { 8, 0xf1 }, \ + { 9, 0x02 }, \ + { 10, 0xd3 }, \ + { 11, 0x40 }, \ + { 12, 0x4e }, \ + { 13, 0x12 }, \ + { 18, 0x40 }, \ + { 22, 0x20 }, \ + { 30, 0x10 }, \ + { 31, 0x80 }, \ + { 32, 0x78 }, \ + { 33, 0x3b }, \ + { 34, 0x3c }, \ + { 35, 0xe0 }, \ + { 38, 0x86 }, \ + { 39, 0x23 }, \ + { 44, 0xd3 }, \ + { 45, 0xbb }, \ + { 46, 0x60 }, \ + { 49, 0x81 }, \ + { 50, 0x86 }, \ + { 51, 0x75 }, \ + { 52, 0x45 }, \ + { 53, 0x18 }, \ + { 54, 0x18 }, \ + { 55, 0x18 }, \ + { 56, 0xdb }, \ + { 57, 0x6e } + +#define RT5390_DEF_RF \ + { 1, 0x0f }, \ + { 2, 0x80 }, \ + { 3, 0x88 }, \ + { 5, 0x10 }, \ + { 6, 0xa0 }, \ + { 7, 0x00 }, \ + { 10, 0x53 }, \ + { 11, 0x4a }, \ + { 12, 0x46 }, \ + { 13, 0x9f }, \ + { 14, 0x00 }, \ + { 15, 0x00 }, \ + { 16, 0x00 }, \ + { 18, 0x03 }, \ + { 19, 0x00 }, \ + { 20, 0x00 }, \ + { 21, 0x00 }, \ + { 22, 0x20 }, \ + { 23, 0x00 }, \ + { 24, 0x00 }, \ + { 25, 0xc0 }, \ + { 26, 0x00 }, \ + { 27, 0x09 }, \ + { 28, 0x00 }, \ + { 29, 0x10 }, \ + { 30, 0x10 }, \ + { 31, 0x80 }, \ + { 32, 0x80 }, \ + { 33, 0x00 }, \ + { 34, 0x07 }, \ + { 35, 0x12 }, \ + { 36, 0x00 }, \ + { 37, 0x08 }, \ + { 38, 0x85 }, \ + { 39, 0x1b }, \ + { 40, 0x0b }, \ + { 41, 0xbb }, \ + { 42, 0xd2 }, \ + { 43, 0x9a }, \ + { 44, 0x0e }, \ + { 45, 0xa2 }, \ + { 46, 0x7b }, \ + { 47, 0x00 }, \ + { 48, 0x10 }, \ + { 49, 0x94 }, \ + { 52, 0x38 }, \ + { 53, 0x84 }, \ + { 54, 0x78 }, \ + { 55, 0x44 }, \ + { 56, 0x22 }, \ + { 57, 0x80 }, \ + { 58, 0x7f }, \ + { 59, 0x8f }, \ + { 60, 0x45 }, \ + { 61, 0xdd }, \ + { 62, 0x00 }, \ + { 63, 0x00 } + +#define RT5392_DEF_RF \ + { 1, 0x17 }, \ + { 3, 0x88 }, \ + { 5, 0x10 }, \ + { 6, 0xe0 }, \ + { 7, 0x00 }, \ + { 10, 0x53 }, \ + { 11, 0x4a }, \ + { 12, 0x46 }, \ + { 13, 0x9f }, \ + { 14, 0x00 }, \ + { 15, 0x00 }, \ + { 16, 0x00 }, \ + { 18, 0x03 }, \ + { 19, 0x4d }, \ + { 20, 0x00 }, \ + { 21, 0x8d }, \ + { 22, 0x20 }, \ + { 23, 0x0b }, \ + { 24, 0x44 }, \ + { 25, 0x80 }, \ + { 26, 0x82 }, \ + { 27, 0x09 }, \ + { 28, 0x00 }, \ + { 29, 0x10 }, \ + { 30, 0x10 }, \ + { 31, 0x80 }, \ + { 32, 0x20 }, \ + { 33, 0xc0 }, \ + { 34, 0x07 }, \ + { 35, 0x12 }, \ + { 36, 0x00 }, \ + { 37, 0x08 }, \ + { 38, 0x89 }, \ + { 39, 0x1b }, \ + { 40, 0x0f }, \ + { 41, 0xbb }, \ + { 42, 0xd5 }, \ + { 43, 0x9b }, \ + { 44, 0x0e }, \ + { 45, 0xa2 }, \ + { 46, 0x73 }, \ + { 47, 0x0c }, \ + { 48, 0x10 }, \ + { 49, 0x94 }, \ + { 50, 0x94 }, \ + { 51, 0x3a }, \ + { 52, 0x48 }, \ + { 53, 0x44 }, \ + { 54, 0x38 }, \ + { 55, 0x43 }, \ + { 56, 0xa1 }, \ + { 57, 0x00 }, \ + { 58, 0x39 }, \ + { 59, 0x07 }, \ + { 60, 0x45 }, \ + { 61, 0x91 }, \ + { 62, 0x39 }, \ + { 63, 0x07 } + +#define RT5592_DEF_RF \ + { 1, 0x3f }, \ + { 3, 0x08 }, \ + { 5, 0x10 }, \ + { 6, 0xe4 }, \ + { 7, 0x00 }, \ + { 14, 0x00 }, \ + { 15, 0x00 }, \ + { 16, 0x00 }, \ + { 18, 0x03 }, \ + { 19, 0x4d }, \ + { 20, 0x10 }, \ + { 21, 0x8d }, \ + { 26, 0x82 }, \ + { 28, 0x00 }, \ + { 29, 0x10 }, \ + { 33, 0xc0 }, \ + { 34, 0x07 }, \ + { 35, 0x12 }, \ + { 47, 0x0c }, \ + { 53, 0x22 }, \ + { 63, 0x07 } + +#define RT5592_2GHZ_DEF_RF \ + { 10, 0x90 }, \ + { 11, 0x4a }, \ + { 12, 0x52 }, \ + { 13, 0x42 }, \ + { 22, 0x40 }, \ + { 24, 0x4a }, \ + { 25, 0x80 }, \ + { 27, 0x42 }, \ + { 36, 0x80 }, \ + { 37, 0x08 }, \ + { 38, 0x89 }, \ + { 39, 0x1b }, \ + { 40, 0x0d }, \ + { 41, 0x9b }, \ + { 42, 0xd5 }, \ + { 43, 0x72 }, \ + { 44, 0x0e }, \ + { 45, 0xa2 }, \ + { 46, 0x6b }, \ + { 48, 0x10 }, \ + { 51, 0x3e }, \ + { 52, 0x48 }, \ + { 54, 0x38 }, \ + { 56, 0xa1 }, \ + { 57, 0x00 }, \ + { 58, 0x39 }, \ + { 60, 0x45 }, \ + { 61, 0x91 }, \ + { 62, 0x39 } + +#define RT5592_5GHZ_DEF_RF \ + { 10, 0x97 }, \ + { 11, 0x40 }, \ + { 25, 0xbf }, \ + { 27, 0x42 }, \ + { 36, 0x00 }, \ + { 37, 0x04 }, \ + { 38, 0x85 }, \ + { 40, 0x42 }, \ + { 41, 0xbb }, \ + { 42, 0xd7 }, \ + { 45, 0x41 }, \ + { 48, 0x00 }, \ + { 57, 0x77 }, \ + { 60, 0x05 }, \ + { 61, 0x01 } + +#define RT5592_CHAN_5GHZ \ + { 36, 64, 12, 0x2e }, \ + { 100, 165, 12, 0x0e }, \ + { 36, 64, 13, 0x22 }, \ + { 100, 165, 13, 0x42 }, \ + { 36, 64, 22, 0x60 }, \ + { 100, 165, 22, 0x40 }, \ + { 36, 64, 23, 0x7f }, \ + { 100, 153, 23, 0x3c }, \ + { 155, 165, 23, 0x38 }, \ + { 36, 50, 24, 0x09 }, \ + { 52, 64, 24, 0x07 }, \ + { 100, 153, 24, 0x06 }, \ + { 155, 165, 24, 0x05 }, \ + { 36, 64, 39, 0x1c }, \ + { 100, 138, 39, 0x1a }, \ + { 140, 165, 39, 0x18 }, \ + { 36, 64, 43, 0x5b }, \ + { 100, 138, 43, 0x3b }, \ + { 140, 165, 43, 0x1b }, \ + { 36, 64, 44, 0x40 }, \ + { 100, 138, 44, 0x20 }, \ + { 140, 165, 44, 0x10 }, \ + { 36, 64, 46, 0x00 }, \ + { 100, 138, 46, 0x18 }, \ + { 140, 165, 46, 0x08 }, \ + { 36, 64, 51, 0xfe }, \ + { 100, 124, 51, 0xfc }, \ + { 126, 165, 51, 0xec }, \ + { 36, 64, 52, 0x0c }, \ + { 100, 138, 52, 0x06 }, \ + { 140, 165, 52, 0x06 }, \ + { 36, 64, 54, 0xf8 }, \ + { 100, 165, 54, 0xeb }, \ + { 36, 50, 55, 0x06 }, \ + { 52, 64, 55, 0x04 }, \ + { 100, 138, 55, 0x01 }, \ + { 140, 165, 55, 0x00 }, \ + { 36, 50, 56, 0xd3 }, \ + { 52, 128, 56, 0xbb }, \ + { 130, 165, 56, 0xab }, \ + { 36, 64, 58, 0x15 }, \ + { 100, 116, 58, 0x1d }, \ + { 118, 165, 58, 0x15 }, \ + { 36, 64, 59, 0x7f }, \ + { 100, 138, 59, 0x3f }, \ + { 140, 165, 59, 0x7c }, \ + { 36, 64, 62, 0x15 }, \ + { 100, 116, 62, 0x1d }, \ + { 118, 165, 62, 0x15 } + +union run_stats { + uint32_t raw; + struct { + uint16_t fail; + uint16_t pad; + } error; + struct { + uint16_t success; + uint16_t retry; + } tx; +} __aligned(4); + +#endif /* _IF_RUNREG_H_ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_runvar.h b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_runvar.h new file mode 100644 index 0000000000..bb6231c3f9 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_runvar.h @@ -0,0 +1,268 @@ +/* $OpenBSD: if_runvar.h,v 1.3 2009/03/26 20:17:27 damien Exp $ */ + +/*- + * Copyright (c) 2008,2009 Damien Bergamini + * ported to FreeBSD by Akinori Furukoshi + * USB Consulting, Hans Petter Selasky + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef _IF_RUNVAR_H_ +#define _IF_RUNVAR_H_ + +/* Support up to 4KB frames - useful for A-MSDU/FF. */ +#define RUN_MAX_RXSZ \ + MIN(4096, MJUMPAGESIZE) + +/* Support up to 8KB frames - useful for A-MSDU/FF. */ +#define RUN_MAX_TXSZ \ + (sizeof (struct rt2870_txd) + \ + sizeof (struct rt2860_txwi) + \ + 8192 + 11) + +#define RUN_TX_TIMEOUT 5000 /* ms */ + +/* Tx ring count was 8/endpoint, now 32 for all 4 (or 6) endpoints. */ +#define RUN_TX_RING_COUNT 32 +#define RUN_RX_RING_COUNT 1 + +#define RT2870_WCID_MAX 64 +#define RUN_AID2WCID(aid) ((aid) & 0xff) + +#define RUN_VAP_MAX 8 + +struct run_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; + int8_t wr_dbm_antsignal; + uint8_t wr_antenna; + uint8_t wr_antsignal; +} __packed __aligned(8); + +#define RUN_RX_RADIOTAP_PRESENT \ + (1 << IEEE80211_RADIOTAP_TSFT | \ + 1 << IEEE80211_RADIOTAP_FLAGS | \ + 1 << IEEE80211_RADIOTAP_RATE | \ + 1 << IEEE80211_RADIOTAP_CHANNEL | \ + 1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL | \ + 1 << IEEE80211_RADIOTAP_ANTENNA | \ + 1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) + +struct run_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 IEEE80211_RADIOTAP_HWQUEUE 15 + +#define RUN_TX_RADIOTAP_PRESENT \ + (1 << IEEE80211_RADIOTAP_FLAGS | \ + 1 << IEEE80211_RADIOTAP_RATE | \ + 1 << IEEE80211_RADIOTAP_CHANNEL | \ + 1 << IEEE80211_RADIOTAP_HWQUEUE) + +struct run_softc; + +struct run_tx_data { + STAILQ_ENTRY(run_tx_data) next; + struct run_softc *sc; + struct mbuf *m; + struct ieee80211_node *ni; + uint32_t align[0]; /* dummy field */ + uint8_t desc[sizeof(struct rt2870_txd) + + sizeof(struct rt2860_txwi)]; + uint8_t ridx; +}; +STAILQ_HEAD(run_tx_data_head, run_tx_data); + +struct run_node { + struct ieee80211_node ni; + uint8_t amrr_ridx; + uint8_t mgt_ridx; + uint8_t fix_ridx; +}; +#define RUN_NODE(ni) ((struct run_node *)(ni)) + +struct run_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; +}; + +struct run_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 RUN_VAP(vap) ((struct run_vap *)(vap)) + +/* + * There are 7 bulk endpoints: 1 for RX + * and 6 for TX (4 EDCAs + HCCA + Prio). + * Update 03-14-2009: some devices like the Planex GW-US300MiniS + * seem to have only 4 TX bulk endpoints (Fukaumi Naoki). + */ +enum { + RUN_BULK_TX_BE, /* = WME_AC_BE */ + RUN_BULK_TX_BK, /* = WME_AC_BK */ + RUN_BULK_TX_VI, /* = WME_AC_VI */ + RUN_BULK_TX_VO, /* = WME_AC_VO */ + RUN_BULK_TX_HCCA, + RUN_BULK_TX_PRIO, + RUN_BULK_RX, + RUN_N_XFER, +}; + +#define RUN_EP_QUEUES RUN_BULK_RX + +struct run_endpoint_queue { + struct run_tx_data tx_data[RUN_TX_RING_COUNT]; + struct run_tx_data_head tx_qh; + struct run_tx_data_head tx_fh; + uint32_t tx_nfree; +}; + +struct run_softc { + struct mtx sc_mtx; + struct ieee80211com sc_ic; + struct ieee80211_ratectl_tx_stats sc_txs; + struct mbufq sc_snd; + device_t sc_dev; + struct usb_device *sc_udev; + int sc_need_fwload; + + int sc_flags; +#define RUN_FLAG_FWLOAD_NEEDED 0x01 +#define RUN_RUNNING 0x02 + + uint16_t wcid_stats[RT2870_WCID_MAX + 1][3]; +#define RUN_TXCNT 0 +#define RUN_SUCCESS 1 +#define RUN_RETRY 2 + + int (*sc_srom_read)(struct run_softc *, + uint16_t, uint16_t *); + + uint16_t mac_ver; + uint16_t mac_rev; + uint16_t rf_rev; + uint8_t freq; + uint8_t ntxchains; + uint8_t nrxchains; + + uint8_t bbp25; + uint8_t bbp26; + uint8_t rf24_20mhz; + uint8_t rf24_40mhz; + uint8_t patch_dac; + 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]; + + struct { + uint8_t reg; + uint8_t val; + } bbp[10], rf[10]; + uint8_t leds; + uint16_t led[3]; + uint32_t txpow20mhz[5]; + uint32_t txpow40mhz_2ghz[5]; + uint32_t txpow40mhz_5ghz[5]; + + struct run_endpoint_queue sc_epq[RUN_EP_QUEUES]; + + struct task ratectl_task; + struct usb_callout ratectl_ch; + uint8_t ratectl_run; +#define RUN_RATECTL_OFF 0 + +/* need to be power of 2, otherwise RUN_CMDQ_GET fails */ +#define RUN_CMDQ_MAX 16 +#define RUN_CMDQ_MASQ (RUN_CMDQ_MAX - 1) + struct run_cmdq cmdq[RUN_CMDQ_MAX]; + struct task cmdq_task; + uint32_t cmdq_store; + uint8_t cmdq_exec; + uint8_t cmdq_run; + uint8_t cmdq_key_set; +#define RUN_CMDQ_ABORT 0 +#define RUN_CMDQ_GO 1 + + struct usb_xfer *sc_xfer[RUN_N_XFER]; + + struct mbuf *rx_m; + + uint8_t fifo_cnt; + + uint8_t running; + uint8_t runbmap; + uint8_t ap_running; + uint8_t adhoc_running; + uint8_t sta_running; + uint8_t rvp_cnt; + uint8_t rvp_bmap; + uint8_t sc_detached; + + uint8_t sc_bssid[IEEE80211_ADDR_LEN]; + + union { + struct run_rx_radiotap_header th; + uint8_t pad[64]; + } sc_rxtapu; +#define sc_rxtap sc_rxtapu.th + + union { + struct run_tx_radiotap_header th; + uint8_t pad[64]; + } sc_txtapu; +#define sc_txtap sc_txtapu.th +}; + +#define RUN_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define RUN_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define RUN_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t) + +#endif /* _IF_RUNVAR_H_ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_ural.c b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_ural.c new file mode 100644 index 0000000000..7e6b2a039d --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_ural.c @@ -0,0 +1,2225 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005, 2006 + * Damien Bergamini + * + * Copyright (c) 2006, 2008 + * Hans Petter Selasky + * + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +/*- + * Ralink Technology RT2500USB chipset driver + * http://www.ralinktech.com/ + */ + +#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 + +#ifdef INET +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include "usbdevs.h" + +#define USB_DEBUG_VAR ural_debug +#include + +#include +#include + +#ifdef USB_DEBUG +static int ural_debug = 0; + +static SYSCTL_NODE(_hw_usb, OID_AUTO, ural, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, + "USB ural"); +SYSCTL_INT(_hw_usb_ural, OID_AUTO, debug, CTLFLAG_RWTUN, &ural_debug, 0, + "Debug level"); +#endif + +#define URAL_RSSI(rssi) \ + ((rssi) > (RAL_NOISE_FLOOR + RAL_RSSI_CORR) ? \ + ((rssi) - (RAL_NOISE_FLOOR + RAL_RSSI_CORR)) : 0) + +/* various supported device vendors/products */ +static const STRUCT_USB_HOST_ID ural_devs[] = { +#define URAL_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } + URAL_DEV(ASUS, WL167G), + URAL_DEV(ASUS, RT2570), + URAL_DEV(BELKIN, F5D7050), + URAL_DEV(BELKIN, F5D7051), + URAL_DEV(CISCOLINKSYS, HU200TS), + URAL_DEV(CISCOLINKSYS, WUSB54G), + URAL_DEV(CISCOLINKSYS, WUSB54GP), + URAL_DEV(CONCEPTRONIC2, C54RU), + URAL_DEV(DLINK, DWLG122), + URAL_DEV(GIGABYTE, GN54G), + URAL_DEV(GIGABYTE, GNWBKG), + URAL_DEV(GUILLEMOT, HWGUSB254), + URAL_DEV(MELCO, KG54), + URAL_DEV(MELCO, KG54AI), + URAL_DEV(MELCO, KG54YB), + URAL_DEV(MELCO, NINWIFI), + URAL_DEV(MSI, RT2570), + URAL_DEV(MSI, RT2570_2), + URAL_DEV(MSI, RT2570_3), + URAL_DEV(NOVATECH, NV902), + URAL_DEV(RALINK, RT2570), + URAL_DEV(RALINK, RT2570_2), + URAL_DEV(RALINK, RT2570_3), + URAL_DEV(SIEMENS2, WL54G), + URAL_DEV(SMC, 2862WG), + URAL_DEV(SPHAIRON, UB801R), + URAL_DEV(SURECOM, RT2570), + URAL_DEV(VTECH, RT2570), + URAL_DEV(ZINWELL, RT2570), +#undef URAL_DEV +}; + +static usb_callback_t ural_bulk_read_callback; +static usb_callback_t ural_bulk_write_callback; + +static usb_error_t ural_do_request(struct ural_softc *sc, + struct usb_device_request *req, void *data); +static struct ieee80211vap *ural_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 ural_vap_delete(struct ieee80211vap *); +static void ural_tx_free(struct ural_tx_data *, int); +static void ural_setup_tx_list(struct ural_softc *); +static void ural_unsetup_tx_list(struct ural_softc *); +static int ural_newstate(struct ieee80211vap *, + enum ieee80211_state, int); +static void ural_setup_tx_desc(struct ural_softc *, + struct ural_tx_desc *, uint32_t, int, int); +static int ural_tx_bcn(struct ural_softc *, struct mbuf *, + struct ieee80211_node *); +static int ural_tx_mgt(struct ural_softc *, struct mbuf *, + struct ieee80211_node *); +static int ural_tx_data(struct ural_softc *, struct mbuf *, + struct ieee80211_node *); +static int ural_transmit(struct ieee80211com *, struct mbuf *); +static void ural_start(struct ural_softc *); +static void ural_parent(struct ieee80211com *); +static void ural_set_testmode(struct ural_softc *); +static void ural_eeprom_read(struct ural_softc *, uint16_t, void *, + int); +static uint16_t ural_read(struct ural_softc *, uint16_t); +static void ural_read_multi(struct ural_softc *, uint16_t, void *, + int); +static void ural_write(struct ural_softc *, uint16_t, uint16_t); +static void ural_write_multi(struct ural_softc *, uint16_t, void *, + int) __unused; +static void ural_bbp_write(struct ural_softc *, uint8_t, uint8_t); +static uint8_t ural_bbp_read(struct ural_softc *, uint8_t); +static void ural_rf_write(struct ural_softc *, uint8_t, uint32_t); +static void ural_scan_start(struct ieee80211com *); +static void ural_scan_end(struct ieee80211com *); +static void ural_getradiocaps(struct ieee80211com *, int, int *, + struct ieee80211_channel[]); +static void ural_set_channel(struct ieee80211com *); +static void ural_set_chan(struct ural_softc *, + struct ieee80211_channel *); +static void ural_disable_rf_tune(struct ural_softc *); +static void ural_enable_tsf_sync(struct ural_softc *); +static void ural_enable_tsf(struct ural_softc *); +static void ural_update_slot(struct ural_softc *); +static void ural_set_txpreamble(struct ural_softc *); +static void ural_set_basicrates(struct ural_softc *, + const struct ieee80211_channel *); +static void ural_set_bssid(struct ural_softc *, const uint8_t *); +static void ural_set_macaddr(struct ural_softc *, const uint8_t *); +static void ural_update_promisc(struct ieee80211com *); +static void ural_setpromisc(struct ural_softc *); +static const char *ural_get_rf(int); +static void ural_read_eeprom(struct ural_softc *); +static int ural_bbp_init(struct ural_softc *); +static void ural_set_txantenna(struct ural_softc *, int); +static void ural_set_rxantenna(struct ural_softc *, int); +static void ural_init(struct ural_softc *); +static void ural_stop(struct ural_softc *); +static int ural_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static void ural_ratectl_start(struct ural_softc *, + struct ieee80211_node *); +static void ural_ratectl_timeout(void *); +static void ural_ratectl_task(void *, int); +static int ural_pause(struct ural_softc *sc, int timeout); + +/* + * Default values for MAC registers; values taken from the reference driver. + */ +static const struct { + uint16_t reg; + uint16_t val; +} ural_def_mac[] = { + { RAL_TXRX_CSR5, 0x8c8d }, + { RAL_TXRX_CSR6, 0x8b8a }, + { RAL_TXRX_CSR7, 0x8687 }, + { RAL_TXRX_CSR8, 0x0085 }, + { RAL_MAC_CSR13, 0x1111 }, + { RAL_MAC_CSR14, 0x1e11 }, + { RAL_TXRX_CSR21, 0xe78f }, + { RAL_MAC_CSR9, 0xff1d }, + { RAL_MAC_CSR11, 0x0002 }, + { RAL_MAC_CSR22, 0x0053 }, + { RAL_MAC_CSR15, 0x0000 }, + { RAL_MAC_CSR8, RAL_FRAME_SIZE }, + { RAL_TXRX_CSR19, 0x0000 }, + { RAL_TXRX_CSR18, 0x005a }, + { RAL_PHY_CSR2, 0x0000 }, + { RAL_TXRX_CSR0, 0x1ec0 }, + { RAL_PHY_CSR4, 0x000f } +}; + +/* + * Default values for BBP registers; values taken from the reference driver. + */ +static const struct { + uint8_t reg; + uint8_t val; +} ural_def_bbp[] = { + { 3, 0x02 }, + { 4, 0x19 }, + { 14, 0x1c }, + { 15, 0x30 }, + { 16, 0xac }, + { 17, 0x48 }, + { 18, 0x18 }, + { 19, 0xff }, + { 20, 0x1e }, + { 21, 0x08 }, + { 22, 0x08 }, + { 23, 0x08 }, + { 24, 0x80 }, + { 25, 0x50 }, + { 26, 0x08 }, + { 27, 0x23 }, + { 30, 0x10 }, + { 31, 0x2b }, + { 32, 0xb9 }, + { 34, 0x12 }, + { 35, 0x50 }, + { 39, 0xc4 }, + { 40, 0x02 }, + { 41, 0x60 }, + { 53, 0x10 }, + { 54, 0x18 }, + { 56, 0x08 }, + { 57, 0x10 }, + { 58, 0x08 }, + { 61, 0x60 }, + { 62, 0x10 }, + { 75, 0xff } +}; + +/* + * Default values for RF register R2 indexed by channel numbers. + */ +static const uint32_t ural_rf2522_r2[] = { + 0x307f6, 0x307fb, 0x30800, 0x30805, 0x3080a, 0x3080f, 0x30814, + 0x30819, 0x3081e, 0x30823, 0x30828, 0x3082d, 0x30832, 0x3083e +}; + +static const uint32_t ural_rf2523_r2[] = { + 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, + 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 +}; + +static const uint32_t ural_rf2524_r2[] = { + 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, + 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346 +}; + +static const uint32_t ural_rf2525_r2[] = { + 0x20327, 0x20328, 0x20329, 0x2032a, 0x2032b, 0x2032c, 0x2032d, + 0x2032e, 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20346 +}; + +static const uint32_t ural_rf2525_hi_r2[] = { + 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20344, 0x20345, + 0x20346, 0x20347, 0x20348, 0x20349, 0x2034a, 0x2034b, 0x2034e +}; + +static const uint32_t ural_rf2525e_r2[] = { + 0x2044d, 0x2044e, 0x2044f, 0x20460, 0x20461, 0x20462, 0x20463, + 0x20464, 0x20465, 0x20466, 0x20467, 0x20468, 0x20469, 0x2046b +}; + +static const uint32_t ural_rf2526_hi_r2[] = { + 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d, 0x0022d, + 0x0022e, 0x0022e, 0x0022f, 0x0022d, 0x00240, 0x00240, 0x00241 +}; + +static const uint32_t ural_rf2526_r2[] = { + 0x00226, 0x00227, 0x00227, 0x00228, 0x00228, 0x00229, 0x00229, + 0x0022a, 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d +}; + +/* + * For dual-band RF, RF registers R1 and R4 also depend on channel number; + * values taken from the reference driver. + */ +static const struct { + uint8_t chan; + uint32_t r1; + uint32_t r2; + uint32_t r4; +} ural_rf5222[] = { + { 1, 0x08808, 0x0044d, 0x00282 }, + { 2, 0x08808, 0x0044e, 0x00282 }, + { 3, 0x08808, 0x0044f, 0x00282 }, + { 4, 0x08808, 0x00460, 0x00282 }, + { 5, 0x08808, 0x00461, 0x00282 }, + { 6, 0x08808, 0x00462, 0x00282 }, + { 7, 0x08808, 0x00463, 0x00282 }, + { 8, 0x08808, 0x00464, 0x00282 }, + { 9, 0x08808, 0x00465, 0x00282 }, + { 10, 0x08808, 0x00466, 0x00282 }, + { 11, 0x08808, 0x00467, 0x00282 }, + { 12, 0x08808, 0x00468, 0x00282 }, + { 13, 0x08808, 0x00469, 0x00282 }, + { 14, 0x08808, 0x0046b, 0x00286 }, + + { 36, 0x08804, 0x06225, 0x00287 }, + { 40, 0x08804, 0x06226, 0x00287 }, + { 44, 0x08804, 0x06227, 0x00287 }, + { 48, 0x08804, 0x06228, 0x00287 }, + { 52, 0x08804, 0x06229, 0x00287 }, + { 56, 0x08804, 0x0622a, 0x00287 }, + { 60, 0x08804, 0x0622b, 0x00287 }, + { 64, 0x08804, 0x0622c, 0x00287 }, + + { 100, 0x08804, 0x02200, 0x00283 }, + { 104, 0x08804, 0x02201, 0x00283 }, + { 108, 0x08804, 0x02202, 0x00283 }, + { 112, 0x08804, 0x02203, 0x00283 }, + { 116, 0x08804, 0x02204, 0x00283 }, + { 120, 0x08804, 0x02205, 0x00283 }, + { 124, 0x08804, 0x02206, 0x00283 }, + { 128, 0x08804, 0x02207, 0x00283 }, + { 132, 0x08804, 0x02208, 0x00283 }, + { 136, 0x08804, 0x02209, 0x00283 }, + { 140, 0x08804, 0x0220a, 0x00283 }, + + { 149, 0x08808, 0x02429, 0x00281 }, + { 153, 0x08808, 0x0242b, 0x00281 }, + { 157, 0x08808, 0x0242d, 0x00281 }, + { 161, 0x08808, 0x0242f, 0x00281 } +}; + +static const uint8_t ural_chan_5ghz[] = + { 36, 40, 44, 48, 52, 56, 60, 64, + 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, + 149, 153, 157, 161 }; + +static const struct usb_config ural_config[URAL_N_TRANSFER] = { + [URAL_BULK_WR] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_OUT, + .bufsize = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE + 4), + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = ural_bulk_write_callback, + .timeout = 5000, /* ms */ + }, + [URAL_BULK_RD] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_IN, + .bufsize = (RAL_FRAME_SIZE + RAL_RX_DESC_SIZE), + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = ural_bulk_read_callback, + }, +}; + +static device_probe_t ural_match; +static device_attach_t ural_attach; +static device_detach_t ural_detach; + +static device_method_t ural_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ural_match), + DEVMETHOD(device_attach, ural_attach), + DEVMETHOD(device_detach, ural_detach), + DEVMETHOD_END +}; + +static driver_t ural_driver = { + .name = "ural", + .methods = ural_methods, + .size = sizeof(struct ural_softc), +}; + +static devclass_t ural_devclass; + +DRIVER_MODULE(ural, uhub, ural_driver, ural_devclass, NULL, 0); +MODULE_DEPEND(ural, usb, 1, 1, 1); +MODULE_DEPEND(ural, wlan, 1, 1, 1); +MODULE_VERSION(ural, 1); +USB_PNP_HOST_INFO(ural_devs); + +static int +ural_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 != RAL_IFACE_INDEX) + return (ENXIO); + + return (usbd_lookup_id_by_uaa(ural_devs, sizeof(ural_devs), uaa)); +} + +static int +ural_attach(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + struct ural_softc *sc = device_get_softc(self); + struct ieee80211com *ic = &sc->sc_ic; + uint8_t iface_index; + int error; + + device_set_usb_desc(self); + sc->sc_udev = uaa->device; + sc->sc_dev = self; + + mtx_init(&sc->sc_mtx, device_get_nameunit(self), + MTX_NETWORK_LOCK, MTX_DEF); + mbufq_init(&sc->sc_snd, ifqmaxlen); + + iface_index = RAL_IFACE_INDEX; + error = usbd_transfer_setup(uaa->device, + &iface_index, sc->sc_xfer, ural_config, + URAL_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(self, "could not allocate USB transfers, " + "err=%s\n", usbd_errstr(error)); + goto detach; + } + + RAL_LOCK(sc); + /* retrieve RT2570 rev. no */ + sc->asic_rev = ural_read(sc, RAL_MAC_CSR0); + + /* retrieve MAC address and various other things from EEPROM */ + ural_read_eeprom(sc); + RAL_UNLOCK(sc); + + device_printf(self, "MAC/BBP RT2570 (rev 0x%02x), RF %s\n", + sc->asic_rev, ural_get_rf(sc->rf_rev)); + + ic->ic_softc = sc; + ic->ic_name = device_get_nameunit(self); + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA /* station mode supported */ + | IEEE80211_C_IBSS /* IBSS mode supported */ + | IEEE80211_C_MONITOR /* monitor mode supported */ + | IEEE80211_C_HOSTAP /* HostAp mode supported */ + | IEEE80211_C_TXPMGT /* tx power management */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_BGSCAN /* bg scanning supported */ + | IEEE80211_C_WPA /* 802.11i */ + ; + + ural_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, + ic->ic_channels); + + ieee80211_ifattach(ic); + ic->ic_update_promisc = ural_update_promisc; + ic->ic_raw_xmit = ural_raw_xmit; + ic->ic_scan_start = ural_scan_start; + ic->ic_scan_end = ural_scan_end; + ic->ic_getradiocaps = ural_getradiocaps; + ic->ic_set_channel = ural_set_channel; + ic->ic_parent = ural_parent; + ic->ic_transmit = ural_transmit; + ic->ic_vap_create = ural_vap_create; + ic->ic_vap_delete = ural_vap_delete; + + ieee80211_radiotap_attach(ic, + &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), + RAL_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + RAL_RX_RADIOTAP_PRESENT); + + if (bootverbose) + ieee80211_announce(ic); + + return (0); + +detach: + ural_detach(self); + return (ENXIO); /* failure */ +} + +static int +ural_detach(device_t self) +{ + struct ural_softc *sc = device_get_softc(self); + struct ieee80211com *ic = &sc->sc_ic; + + /* prevent further ioctls */ + RAL_LOCK(sc); + sc->sc_detached = 1; + RAL_UNLOCK(sc); + + /* stop all USB transfers */ + usbd_transfer_unsetup(sc->sc_xfer, URAL_N_TRANSFER); + + /* free TX list, if any */ + RAL_LOCK(sc); + ural_unsetup_tx_list(sc); + RAL_UNLOCK(sc); + + if (ic->ic_softc == sc) + ieee80211_ifdetach(ic); + mbufq_drain(&sc->sc_snd); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static usb_error_t +ural_do_request(struct ural_softc *sc, + struct usb_device_request *req, void *data) +{ + usb_error_t err; + int ntries = 10; + + while (ntries--) { + err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, + req, data, 0, NULL, 250 /* ms */); + if (err == 0) + break; + + DPRINTFN(1, "Control request failed, %s (retrying)\n", + usbd_errstr(err)); + if (ural_pause(sc, hz / 100)) + break; + } + return (err); +} + +static struct ieee80211vap * +ural_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 ural_softc *sc = ic->ic_softc; + struct ural_vap *uvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return NULL; + uvp = malloc(sizeof(struct ural_vap), M_80211_VAP, M_WAITOK | M_ZERO); + vap = &uvp->vap; + /* enable s/w bmiss handling for sta mode */ + + if (ieee80211_vap_setup(ic, vap, name, unit, opmode, + flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { + /* out of memory */ + free(uvp, M_80211_VAP); + return (NULL); + } + + /* override state transition machine */ + uvp->newstate = vap->iv_newstate; + vap->iv_newstate = ural_newstate; + + usb_callout_init_mtx(&uvp->ratectl_ch, &sc->sc_mtx, 0); + TASK_INIT(&uvp->ratectl_task, 0, ural_ratectl_task, uvp); + ieee80211_ratectl_init(vap); + ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */); + + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, + ieee80211_media_status, mac); + ic->ic_opmode = opmode; + return vap; +} + +static void +ural_vap_delete(struct ieee80211vap *vap) +{ + struct ural_vap *uvp = URAL_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + + usb_callout_drain(&uvp->ratectl_ch); + ieee80211_draintask(ic, &uvp->ratectl_task); + ieee80211_ratectl_deinit(vap); + ieee80211_vap_detach(vap); + free(uvp, M_80211_VAP); +} + +static void +ural_tx_free(struct ural_tx_data *data, int txerr) +{ + struct ural_softc *sc = data->sc; + + if (data->m != NULL) { + ieee80211_tx_complete(data->ni, data->m, txerr); + data->m = NULL; + data->ni = NULL; + } + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; +} + +static void +ural_setup_tx_list(struct ural_softc *sc) +{ + struct ural_tx_data *data; + int i; + + sc->tx_nfree = 0; + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + + for (i = 0; i < RAL_TX_LIST_COUNT; i++) { + data = &sc->tx_data[i]; + + data->sc = sc; + STAILQ_INSERT_TAIL(&sc->tx_free, data, next); + sc->tx_nfree++; + } +} + +static void +ural_unsetup_tx_list(struct ural_softc *sc) +{ + struct ural_tx_data *data; + int i; + + /* make sure any subsequent use of the queues will fail */ + sc->tx_nfree = 0; + STAILQ_INIT(&sc->tx_q); + STAILQ_INIT(&sc->tx_free); + + /* free up all node references and mbufs */ + for (i = 0; i < RAL_TX_LIST_COUNT; i++) { + data = &sc->tx_data[i]; + + 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 +ural_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct ural_vap *uvp = URAL_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct ural_softc *sc = ic->ic_softc; + const struct ieee80211_txparam *tp; + struct ieee80211_node *ni; + struct mbuf *m; + + DPRINTF("%s -> %s\n", + ieee80211_state_name[vap->iv_state], + ieee80211_state_name[nstate]); + + IEEE80211_UNLOCK(ic); + RAL_LOCK(sc); + usb_callout_stop(&uvp->ratectl_ch); + + switch (nstate) { + case IEEE80211_S_INIT: + if (vap->iv_state == IEEE80211_S_RUN) { + /* abort TSF synchronization */ + ural_write(sc, RAL_TXRX_CSR19, 0); + + /* force tx led to stop blinking */ + ural_write(sc, RAL_MAC_CSR20, 0); + } + break; + + case IEEE80211_S_RUN: + ni = ieee80211_ref_node(vap->iv_bss); + + if (vap->iv_opmode != IEEE80211_M_MONITOR) { + if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) + goto fail; + + ural_update_slot(sc); + ural_set_txpreamble(sc); + ural_set_basicrates(sc, ic->ic_bsschan); + IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid); + ural_set_bssid(sc, sc->sc_bssid); + } + + if (vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS) { + m = ieee80211_beacon_alloc(ni); + if (m == NULL) { + device_printf(sc->sc_dev, + "could not allocate beacon\n"); + goto fail; + } + ieee80211_ref_node(ni); + if (ural_tx_bcn(sc, m, ni) != 0) { + device_printf(sc->sc_dev, + "could not send beacon\n"); + goto fail; + } + } + + /* make tx led blink on tx (controlled by ASIC) */ + ural_write(sc, RAL_MAC_CSR20, 1); + + if (vap->iv_opmode != IEEE80211_M_MONITOR) + ural_enable_tsf_sync(sc); + else + ural_enable_tsf(sc); + + /* enable automatic rate adaptation */ + /* XXX should use ic_bsschan but not valid until after newstate call below */ + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; + if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) + ural_ratectl_start(sc, ni); + ieee80211_free_node(ni); + break; + + default: + break; + } + RAL_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (uvp->newstate(vap, nstate, arg)); + +fail: + RAL_UNLOCK(sc); + IEEE80211_LOCK(ic); + ieee80211_free_node(ni); + return (-1); +} + +static void +ural_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct ural_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211vap *vap; + struct ural_tx_data *data; + struct mbuf *m; + struct usb_page_cache *pc; + int len; + + usbd_xfer_status(xfer, &len, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + DPRINTFN(11, "transfer complete, %d bytes\n", len); + + /* free resources */ + data = usbd_xfer_get_priv(xfer); + ural_tx_free(data, 0); + usbd_xfer_set_priv(xfer, NULL); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + data = STAILQ_FIRST(&sc->tx_q); + if (data) { + STAILQ_REMOVE_HEAD(&sc->tx_q, next); + m = data->m; + + if (m->m_pkthdr.len > (int)(RAL_FRAME_SIZE + RAL_TX_DESC_SIZE)) { + DPRINTFN(0, "data overflow, %u bytes\n", + m->m_pkthdr.len); + m->m_pkthdr.len = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE); + } + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_in(pc, 0, &data->desc, RAL_TX_DESC_SIZE); + usbd_m_copy_in(pc, RAL_TX_DESC_SIZE, m, 0, + m->m_pkthdr.len); + + vap = data->ni->ni_vap; + if (ieee80211_radiotap_active_vap(vap)) { + struct ural_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = data->rate; + tap->wt_antenna = sc->tx_ant; + + ieee80211_radiotap_tx(vap, m); + } + + /* xfer length needs to be a multiple of two! */ + len = (RAL_TX_DESC_SIZE + m->m_pkthdr.len + 1) & ~1; + if ((len % 64) == 0) + len += 2; + + DPRINTFN(11, "sending frame len=%u xferlen=%u\n", + m->m_pkthdr.len, len); + + usbd_xfer_set_frame_len(xfer, 0, len); + usbd_xfer_set_priv(xfer, data); + + usbd_transfer_submit(xfer); + } + ural_start(sc); + break; + + default: /* Error */ + DPRINTFN(11, "transfer error, %s\n", + usbd_errstr(error)); + + data = usbd_xfer_get_priv(xfer); + if (data != NULL) { + ural_tx_free(data, error); + usbd_xfer_set_priv(xfer, NULL); + } + + if (error == USB_ERR_STALLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + if (error == USB_ERR_TIMEOUT) + device_printf(sc->sc_dev, "device timeout\n"); + break; + } +} + +static void +ural_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct ural_softc *sc = usbd_xfer_softc(xfer); + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + struct epoch_tracker et; + struct mbuf *m = NULL; + struct usb_page_cache *pc; + uint32_t flags; + int8_t rssi = 0, nf = 0; + int len; + + usbd_xfer_status(xfer, &len, NULL, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + + DPRINTFN(15, "rx done, actlen=%d\n", len); + + if (len < (int)(RAL_RX_DESC_SIZE + IEEE80211_MIN_LEN)) { + DPRINTF("%s: xfer too short %d\n", + device_get_nameunit(sc->sc_dev), len); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + + len -= RAL_RX_DESC_SIZE; + /* rx descriptor is located at the end */ + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_out(pc, len, &sc->sc_rx_desc, RAL_RX_DESC_SIZE); + + rssi = URAL_RSSI(sc->sc_rx_desc.rssi); + nf = RAL_NOISE_FLOOR; + flags = le32toh(sc->sc_rx_desc.flags); + if (flags & (RAL_RX_PHY_ERROR | RAL_RX_CRC_ERROR)) { + /* + * This should not happen since we did not + * request to receive those frames when we + * filled RAL_TXRX_CSR2: + */ + DPRINTFN(5, "PHY or CRC error\n"); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) { + DPRINTF("could not allocate mbuf\n"); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + usbd_copy_out(pc, 0, mtod(m, uint8_t *), len); + + /* finalize mbuf */ + m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff; + + if (ieee80211_radiotap_active(ic)) { + struct ural_rx_radiotap_header *tap = &sc->sc_rxtap; + + /* XXX set once */ + tap->wr_flags = 0; + tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate, + (flags & RAL_RX_OFDM) ? + IEEE80211_T_OFDM : IEEE80211_T_CCK); + tap->wr_antenna = sc->rx_ant; + tap->wr_antsignal = nf + rssi; + tap->wr_antnoise = nf; + } + /* Strip trailing 802.11 MAC FCS. */ + m_adj(m, -IEEE80211_CRC_LEN); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + + /* + * At the end of a USB callback it is always safe to unlock + * the private mutex of a device! That is why we do the + * "ieee80211_input" here, and not some lines up! + */ + RAL_UNLOCK(sc); + if (m) { + ni = ieee80211_find_rxnode(ic, + mtod(m, struct ieee80211_frame_min *)); + 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); + } + RAL_LOCK(sc); + ural_start(sc); + return; + + default: /* Error */ + if (error != USB_ERR_CANCELLED) { + /* try to clear stall first */ + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + return; + } +} + +static uint8_t +ural_plcp_signal(int rate) +{ + switch (rate) { + /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ + case 12: return 0xb; + case 18: return 0xf; + case 24: return 0xa; + case 36: return 0xe; + case 48: return 0x9; + case 72: return 0xd; + case 96: return 0x8; + case 108: return 0xc; + + /* CCK rates (NB: not IEEE std, device-specific) */ + case 2: return 0x0; + case 4: return 0x1; + case 11: return 0x2; + case 22: return 0x3; + } + return 0xff; /* XXX unsupported/unknown rate */ +} + +static void +ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc, + uint32_t flags, int len, int rate) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint16_t plcp_length; + int remainder; + + desc->flags = htole32(flags); + desc->flags |= htole32(RAL_TX_NEWSEQ); + desc->flags |= htole32(len << 16); + + desc->wme = htole16(RAL_AIFSN(2) | RAL_LOGCWMIN(3) | RAL_LOGCWMAX(5)); + desc->wme |= htole16(RAL_IVOFFSET(sizeof (struct ieee80211_frame))); + + /* setup PLCP fields */ + desc->plcp_signal = ural_plcp_signal(rate); + desc->plcp_service = 4; + + len += IEEE80211_CRC_LEN; + if (ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) { + desc->flags |= htole32(RAL_TX_OFDM); + + plcp_length = len & 0xfff; + desc->plcp_length_hi = plcp_length >> 6; + desc->plcp_length_lo = plcp_length & 0x3f; + } else { + if (rate == 0) + rate = 2; /* avoid division by zero */ + plcp_length = howmany(16 * len, rate); + if (rate == 22) { + remainder = (16 * len) % 22; + if (remainder != 0 && remainder < 7) + desc->plcp_service |= RAL_PLCP_LENGEXT; + } + desc->plcp_length_hi = plcp_length >> 8; + desc->plcp_length_lo = plcp_length & 0xff; + + if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + desc->plcp_signal |= 0x08; + } + + desc->iv = 0; + desc->eiv = 0; +} + +#define RAL_TX_TIMEOUT 5000 + +static int +ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + const struct ieee80211_txparam *tp; + struct ural_tx_data *data; + + if (sc->tx_nfree == 0) { + m_freem(m0); + ieee80211_free_node(ni); + return (EIO); + } + if (ic->ic_bsschan == IEEE80211_CHAN_ANYC) { + m_freem(m0); + ieee80211_free_node(ni); + return (ENXIO); + } + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)]; + + data->m = m0; + data->ni = ni; + data->rate = tp->mgmtrate; + + ural_setup_tx_desc(sc, &data->desc, + RAL_TX_IFS_NEWBACKOFF | RAL_TX_TIMESTAMP, m0->m_pkthdr.len, + tp->mgmtrate); + + DPRINTFN(10, "sending beacon frame len=%u rate=%u\n", + m0->m_pkthdr.len, tp->mgmtrate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]); + + return (0); +} + +static int +ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + const struct ieee80211_txparam *tp = ni->ni_txparms; + struct ieee80211com *ic = ni->ni_ic; + struct ural_tx_data *data; + struct ieee80211_frame *wh; + struct ieee80211_key *k; + uint32_t flags; + uint16_t dur; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + wh = mtod(m0, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_encap(ni, m0); + if (k == NULL) { + m_freem(m0); + return ENOBUFS; + } + wh = mtod(m0, struct ieee80211_frame *); + } + + data->m = m0; + data->ni = ni; + data->rate = tp->mgmtrate; + + flags = 0; + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + flags |= RAL_TX_ACK; + + dur = ieee80211_ack_duration(ic->ic_rt, tp->mgmtrate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + *(uint16_t *)wh->i_dur = htole16(dur); + + /* tell hardware to add timestamp for probe responses */ + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == + IEEE80211_FC0_TYPE_MGT && + (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == + IEEE80211_FC0_SUBTYPE_PROBE_RESP) + flags |= RAL_TX_TIMESTAMP; + } + + ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, tp->mgmtrate); + + DPRINTFN(10, "sending mgt frame len=%u rate=%u\n", + m0->m_pkthdr.len, tp->mgmtrate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]); + + return 0; +} + +static int +ural_sendprot(struct ural_softc *sc, + const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ural_tx_data *data; + struct mbuf *mprot; + int protrate, flags; + + mprot = ieee80211_alloc_prot(ni, m, rate, prot); + if (mprot == NULL) { + if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); + device_printf(sc->sc_dev, + "could not allocate mbuf for protection mode %d\n", prot); + return ENOBUFS; + } + + protrate = ieee80211_ctl_rate(ic->ic_rt, rate); + flags = RAL_TX_RETRY(7); + if (prot == IEEE80211_PROT_RTSCTS) + flags |= RAL_TX_ACK; + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + data->m = mprot; + data->ni = ieee80211_ref_node(ni); + data->rate = protrate; + ural_setup_tx_desc(sc, &data->desc, flags, mprot->m_pkthdr.len, protrate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]); + + return 0; +} + +static int +ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ural_tx_data *data; + uint32_t flags; + int error; + int rate; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + KASSERT(params != NULL, ("no raw xmit params")); + + rate = params->ibp_rate0; + if (!ieee80211_isratevalid(ic->ic_rt, rate)) { + m_freem(m0); + return EINVAL; + } + flags = 0; + if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) + flags |= RAL_TX_ACK; + if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) { + error = ural_sendprot(sc, m0, ni, + params->ibp_flags & IEEE80211_BPF_RTS ? + IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY, + rate); + if (error || sc->tx_nfree == 0) { + m_freem(m0); + return ENOBUFS; + } + flags |= RAL_TX_IFS_SIFS; + } + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + data->m = m0; + data->ni = ni; + data->rate = rate; + + /* XXX need to setup descriptor ourself */ + ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, rate); + + DPRINTFN(10, "sending raw frame len=%u rate=%u\n", + m0->m_pkthdr.len, rate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]); + + return 0; +} + +static int +ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ural_tx_data *data; + struct ieee80211_frame *wh; + const struct ieee80211_txparam *tp = ni->ni_txparms; + struct ieee80211_key *k; + uint32_t flags = 0; + uint16_t dur; + int error, rate; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + + wh = mtod(m0, struct ieee80211_frame *); + + if (m0->m_flags & M_EAPOL) + rate = tp->mgmtrate; + else if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + rate = tp->mcastrate; + else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + rate = tp->ucastrate; + else { + (void) ieee80211_ratectl_rate(ni, NULL, 0); + rate = ni->ni_txrate; + } + + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_crypto_encap(ni, m0); + if (k == NULL) { + m_freem(m0); + return ENOBUFS; + } + /* packet header may have moved, reset our local pointer */ + wh = mtod(m0, struct ieee80211_frame *); + } + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + int prot = IEEE80211_PROT_NONE; + if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) + prot = IEEE80211_PROT_RTSCTS; + else if ((ic->ic_flags & IEEE80211_F_USEPROT) && + ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_OFDM) + prot = ic->ic_protmode; + if (prot != IEEE80211_PROT_NONE) { + error = ural_sendprot(sc, m0, ni, prot, rate); + if (error || sc->tx_nfree == 0) { + m_freem(m0); + return ENOBUFS; + } + flags |= RAL_TX_IFS_SIFS; + } + } + + data = STAILQ_FIRST(&sc->tx_free); + STAILQ_REMOVE_HEAD(&sc->tx_free, next); + sc->tx_nfree--; + + data->m = m0; + data->ni = ni; + data->rate = rate; + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + flags |= RAL_TX_ACK; + flags |= RAL_TX_RETRY(7); + + dur = ieee80211_ack_duration(ic->ic_rt, rate, + ic->ic_flags & IEEE80211_F_SHPREAMBLE); + *(uint16_t *)wh->i_dur = htole16(dur); + } + + ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, rate); + + DPRINTFN(10, "sending data frame len=%u rate=%u\n", + m0->m_pkthdr.len, rate); + + STAILQ_INSERT_TAIL(&sc->tx_q, data, next); + usbd_transfer_start(sc->sc_xfer[URAL_BULK_WR]); + + return 0; +} + +static int +ural_transmit(struct ieee80211com *ic, struct mbuf *m) +{ + struct ural_softc *sc = ic->ic_softc; + int error; + + RAL_LOCK(sc); + if (!sc->sc_running) { + RAL_UNLOCK(sc); + return (ENXIO); + } + error = mbufq_enqueue(&sc->sc_snd, m); + if (error) { + RAL_UNLOCK(sc); + return (error); + } + ural_start(sc); + RAL_UNLOCK(sc); + + return (0); +} + +static void +ural_start(struct ural_softc *sc) +{ + struct ieee80211_node *ni; + struct mbuf *m; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + + if (sc->sc_running == 0) + return; + + while (sc->tx_nfree >= RAL_TX_MINFREE && + (m = mbufq_dequeue(&sc->sc_snd)) != NULL) { + ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; + if (ural_tx_data(sc, m, ni) != 0) { + if_inc_counter(ni->ni_vap->iv_ifp, + IFCOUNTER_OERRORS, 1); + ieee80211_free_node(ni); + break; + } + } +} + +static void +ural_parent(struct ieee80211com *ic) +{ + struct ural_softc *sc = ic->ic_softc; + int startall = 0; + + RAL_LOCK(sc); + if (sc->sc_detached) { + RAL_UNLOCK(sc); + return; + } + if (ic->ic_nrunning > 0) { + if (sc->sc_running == 0) { + ural_init(sc); + startall = 1; + } else + ural_setpromisc(sc); + } else if (sc->sc_running) + ural_stop(sc); + RAL_UNLOCK(sc); + if (startall) + ieee80211_start_all(ic); +} + +static void +ural_set_testmode(struct ural_softc *sc) +{ + struct usb_device_request req; + usb_error_t error; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RAL_VENDOR_REQUEST; + USETW(req.wValue, 4); + USETW(req.wIndex, 1); + USETW(req.wLength, 0); + + error = ural_do_request(sc, &req, NULL); + if (error != 0) { + device_printf(sc->sc_dev, "could not set test mode: %s\n", + usbd_errstr(error)); + } +} + +static void +ural_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, int len) +{ + struct usb_device_request req; + usb_error_t error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RAL_READ_EEPROM; + USETW(req.wValue, 0); + USETW(req.wIndex, addr); + USETW(req.wLength, len); + + error = ural_do_request(sc, &req, buf); + if (error != 0) { + device_printf(sc->sc_dev, "could not read EEPROM: %s\n", + usbd_errstr(error)); + } +} + +static uint16_t +ural_read(struct ural_softc *sc, uint16_t reg) +{ + struct usb_device_request req; + usb_error_t error; + uint16_t val; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RAL_READ_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, sizeof (uint16_t)); + + error = ural_do_request(sc, &req, &val); + if (error != 0) { + device_printf(sc->sc_dev, "could not read MAC register: %s\n", + usbd_errstr(error)); + return 0; + } + + return le16toh(val); +} + +static void +ural_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len) +{ + struct usb_device_request req; + usb_error_t error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = RAL_READ_MULTI_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + error = ural_do_request(sc, &req, buf); + if (error != 0) { + device_printf(sc->sc_dev, "could not read MAC register: %s\n", + usbd_errstr(error)); + } +} + +static void +ural_write(struct ural_softc *sc, uint16_t reg, uint16_t val) +{ + struct usb_device_request req; + usb_error_t error; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RAL_WRITE_MAC; + USETW(req.wValue, val); + USETW(req.wIndex, reg); + USETW(req.wLength, 0); + + error = ural_do_request(sc, &req, NULL); + if (error != 0) { + device_printf(sc->sc_dev, "could not write MAC register: %s\n", + usbd_errstr(error)); + } +} + +static void +ural_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len) +{ + struct usb_device_request req; + usb_error_t error; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = RAL_WRITE_MULTI_MAC; + USETW(req.wValue, 0); + USETW(req.wIndex, reg); + USETW(req.wLength, len); + + error = ural_do_request(sc, &req, buf); + if (error != 0) { + device_printf(sc->sc_dev, "could not write MAC register: %s\n", + usbd_errstr(error)); + } +} + +static void +ural_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val) +{ + uint16_t tmp; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY)) + break; + if (ural_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "could not write to BBP\n"); + return; + } + + tmp = reg << 8 | val; + ural_write(sc, RAL_PHY_CSR7, tmp); +} + +static uint8_t +ural_bbp_read(struct ural_softc *sc, uint8_t reg) +{ + uint16_t val; + int ntries; + + val = RAL_BBP_WRITE | reg << 8; + ural_write(sc, RAL_PHY_CSR7, val); + + for (ntries = 0; ntries < 100; ntries++) { + if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY)) + break; + if (ural_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "could not read BBP\n"); + return 0; + } + + return ural_read(sc, RAL_PHY_CSR7) & 0xff; +} + +static void +ural_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val) +{ + uint32_t tmp; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(ural_read(sc, RAL_PHY_CSR10) & RAL_RF_LOBUSY)) + break; + if (ural_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "could not write to RF\n"); + return; + } + + tmp = RAL_RF_BUSY | RAL_RF_20BIT | (val & 0xfffff) << 2 | (reg & 0x3); + ural_write(sc, RAL_PHY_CSR9, tmp & 0xffff); + ural_write(sc, RAL_PHY_CSR10, tmp >> 16); + + /* remember last written value in sc */ + sc->rf_regs[reg] = val; + + DPRINTFN(15, "RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff); +} + +static void +ural_scan_start(struct ieee80211com *ic) +{ + struct ural_softc *sc = ic->ic_softc; + + RAL_LOCK(sc); + ural_write(sc, RAL_TXRX_CSR19, 0); + ural_set_bssid(sc, ieee80211broadcastaddr); + RAL_UNLOCK(sc); +} + +static void +ural_scan_end(struct ieee80211com *ic) +{ + struct ural_softc *sc = ic->ic_softc; + + RAL_LOCK(sc); + ural_enable_tsf_sync(sc); + ural_set_bssid(sc, sc->sc_bssid); + RAL_UNLOCK(sc); + +} + +static void +ural_getradiocaps(struct ieee80211com *ic, + int maxchans, int *nchans, struct ieee80211_channel chans[]) +{ + struct ural_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); + ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, bands, 0); + + if (sc->rf_rev == RAL_RF_5222) { + setbit(bands, IEEE80211_MODE_11A); + ieee80211_add_channel_list_5ghz(chans, maxchans, nchans, + ural_chan_5ghz, nitems(ural_chan_5ghz), bands, 0); + } +} + +static void +ural_set_channel(struct ieee80211com *ic) +{ + struct ural_softc *sc = ic->ic_softc; + + RAL_LOCK(sc); + ural_set_chan(sc, ic->ic_curchan); + RAL_UNLOCK(sc); +} + +static void +ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint8_t power, tmp; + int i, chan; + + chan = ieee80211_chan2ieee(ic, c); + if (chan == 0 || chan == IEEE80211_CHAN_ANY) + return; + + if (IEEE80211_IS_CHAN_2GHZ(c)) + power = min(sc->txpow[chan - 1], 31); + else + power = 31; + + /* adjust txpower using ifconfig settings */ + power -= (100 - ic->ic_txpowlimit) / 8; + + DPRINTFN(2, "setting channel to %u, txpower to %u\n", chan, power); + + switch (sc->rf_rev) { + case RAL_RF_2522: + ural_rf_write(sc, RAL_RF1, 0x00814); + ural_rf_write(sc, RAL_RF2, ural_rf2522_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); + break; + + case RAL_RF_2523: + ural_rf_write(sc, RAL_RF1, 0x08804); + ural_rf_write(sc, RAL_RF2, ural_rf2523_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x38044); + ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); + break; + + case RAL_RF_2524: + ural_rf_write(sc, RAL_RF1, 0x0c808); + ural_rf_write(sc, RAL_RF2, ural_rf2524_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); + ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); + break; + + case RAL_RF_2525: + ural_rf_write(sc, RAL_RF1, 0x08808); + ural_rf_write(sc, RAL_RF2, ural_rf2525_hi_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); + ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); + + ural_rf_write(sc, RAL_RF1, 0x08808); + ural_rf_write(sc, RAL_RF2, ural_rf2525_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); + ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286); + break; + + case RAL_RF_2525E: + ural_rf_write(sc, RAL_RF1, 0x08808); + ural_rf_write(sc, RAL_RF2, ural_rf2525e_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); + ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282); + break; + + case RAL_RF_2526: + ural_rf_write(sc, RAL_RF2, ural_rf2526_hi_r2[chan - 1]); + ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); + ural_rf_write(sc, RAL_RF1, 0x08804); + + ural_rf_write(sc, RAL_RF2, ural_rf2526_r2[chan - 1]); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044); + ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381); + break; + + /* dual-band RF */ + case RAL_RF_5222: + for (i = 0; ural_rf5222[i].chan != chan; i++); + + ural_rf_write(sc, RAL_RF1, ural_rf5222[i].r1); + ural_rf_write(sc, RAL_RF2, ural_rf5222[i].r2); + ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040); + ural_rf_write(sc, RAL_RF4, ural_rf5222[i].r4); + break; + } + + if (ic->ic_opmode != IEEE80211_M_MONITOR && + (ic->ic_flags & IEEE80211_F_SCAN) == 0) { + /* set Japan filter bit for channel 14 */ + tmp = ural_bbp_read(sc, 70); + + tmp &= ~RAL_JAPAN_FILTER; + if (chan == 14) + tmp |= RAL_JAPAN_FILTER; + + ural_bbp_write(sc, 70, tmp); + + /* clear CRC errors */ + ural_read(sc, RAL_STA_CSR0); + + ural_pause(sc, hz / 100); + ural_disable_rf_tune(sc); + } + + /* XXX doesn't belong here */ + /* update basic rate set */ + ural_set_basicrates(sc, c); + + /* give the hardware some time to do the switchover */ + ural_pause(sc, hz / 100); +} + +/* + * Disable RF auto-tuning. + */ +static void +ural_disable_rf_tune(struct ural_softc *sc) +{ + uint32_t tmp; + + if (sc->rf_rev != RAL_RF_2523) { + tmp = sc->rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE; + ural_rf_write(sc, RAL_RF1, tmp); + } + + tmp = sc->rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE; + ural_rf_write(sc, RAL_RF3, tmp); + + DPRINTFN(2, "disabling RF autotune\n"); +} + +/* + * Refer to IEEE Std 802.11-1999 pp. 123 for more information on TSF + * synchronization. + */ +static void +ural_enable_tsf_sync(struct ural_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint16_t logcwmin, preload, tmp; + + /* first, disable TSF synchronization */ + ural_write(sc, RAL_TXRX_CSR19, 0); + + tmp = (16 * vap->iv_bss->ni_intval) << 4; + ural_write(sc, RAL_TXRX_CSR18, tmp); + + logcwmin = (ic->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0; + preload = (ic->ic_opmode == IEEE80211_M_IBSS) ? 320 : 6; + tmp = logcwmin << 12 | preload; + ural_write(sc, RAL_TXRX_CSR20, tmp); + + /* finally, enable TSF synchronization */ + tmp = RAL_ENABLE_TSF | RAL_ENABLE_TBCN; + if (ic->ic_opmode == IEEE80211_M_STA) + tmp |= RAL_ENABLE_TSF_SYNC(1); + else + tmp |= RAL_ENABLE_TSF_SYNC(2) | RAL_ENABLE_BEACON_GENERATOR; + ural_write(sc, RAL_TXRX_CSR19, tmp); + + DPRINTF("enabling TSF synchronization\n"); +} + +static void +ural_enable_tsf(struct ural_softc *sc) +{ + /* first, disable TSF synchronization */ + ural_write(sc, RAL_TXRX_CSR19, 0); + ural_write(sc, RAL_TXRX_CSR19, RAL_ENABLE_TSF | RAL_ENABLE_TSF_SYNC(2)); +} + +#define RAL_RXTX_TURNAROUND 5 /* us */ +static void +ural_update_slot(struct ural_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint16_t slottime, sifs, eifs; + + slottime = IEEE80211_GET_SLOTTIME(ic); + + /* + * These settings may sound a bit inconsistent but this is what the + * reference driver does. + */ + if (ic->ic_curmode == IEEE80211_MODE_11B) { + sifs = 16 - RAL_RXTX_TURNAROUND; + eifs = 364; + } else { + sifs = 10 - RAL_RXTX_TURNAROUND; + eifs = 64; + } + + ural_write(sc, RAL_MAC_CSR10, slottime); + ural_write(sc, RAL_MAC_CSR11, sifs); + ural_write(sc, RAL_MAC_CSR12, eifs); +} + +static void +ural_set_txpreamble(struct ural_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint16_t tmp; + + tmp = ural_read(sc, RAL_TXRX_CSR10); + + tmp &= ~RAL_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + tmp |= RAL_SHORT_PREAMBLE; + + ural_write(sc, RAL_TXRX_CSR10, tmp); +} + +static void +ural_set_basicrates(struct ural_softc *sc, const struct ieee80211_channel *c) +{ + /* XXX wrong, take from rate set */ + /* update basic rate set */ + if (IEEE80211_IS_CHAN_5GHZ(c)) { + /* 11a basic rates: 6, 12, 24Mbps */ + ural_write(sc, RAL_TXRX_CSR11, 0x150); + } else if (IEEE80211_IS_CHAN_ANYG(c)) { + /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */ + ural_write(sc, RAL_TXRX_CSR11, 0x15f); + } else { + /* 11b basic rates: 1, 2Mbps */ + ural_write(sc, RAL_TXRX_CSR11, 0x3); + } +} + +static void +ural_set_bssid(struct ural_softc *sc, const uint8_t *bssid) +{ + uint16_t tmp; + + tmp = bssid[0] | bssid[1] << 8; + ural_write(sc, RAL_MAC_CSR5, tmp); + + tmp = bssid[2] | bssid[3] << 8; + ural_write(sc, RAL_MAC_CSR6, tmp); + + tmp = bssid[4] | bssid[5] << 8; + ural_write(sc, RAL_MAC_CSR7, tmp); + + DPRINTF("setting BSSID to %6D\n", bssid, ":"); +} + +static void +ural_set_macaddr(struct ural_softc *sc, const uint8_t *addr) +{ + uint16_t tmp; + + tmp = addr[0] | addr[1] << 8; + ural_write(sc, RAL_MAC_CSR2, tmp); + + tmp = addr[2] | addr[3] << 8; + ural_write(sc, RAL_MAC_CSR3, tmp); + + tmp = addr[4] | addr[5] << 8; + ural_write(sc, RAL_MAC_CSR4, tmp); + + DPRINTF("setting MAC address to %6D\n", addr, ":"); +} + +static void +ural_setpromisc(struct ural_softc *sc) +{ + uint32_t tmp; + + tmp = ural_read(sc, RAL_TXRX_CSR2); + + tmp &= ~RAL_DROP_NOT_TO_ME; + if (sc->sc_ic.ic_promisc == 0) + tmp |= RAL_DROP_NOT_TO_ME; + + ural_write(sc, RAL_TXRX_CSR2, tmp); + + DPRINTF("%s promiscuous mode\n", sc->sc_ic.ic_promisc ? + "entering" : "leaving"); +} + +static void +ural_update_promisc(struct ieee80211com *ic) +{ + struct ural_softc *sc = ic->ic_softc; + + RAL_LOCK(sc); + if (sc->sc_running) + ural_setpromisc(sc); + RAL_UNLOCK(sc); +} + +static const char * +ural_get_rf(int rev) +{ + switch (rev) { + case RAL_RF_2522: return "RT2522"; + case RAL_RF_2523: return "RT2523"; + case RAL_RF_2524: return "RT2524"; + case RAL_RF_2525: return "RT2525"; + case RAL_RF_2525E: return "RT2525e"; + case RAL_RF_2526: return "RT2526"; + case RAL_RF_5222: return "RT5222"; + default: return "unknown"; + } +} + +static void +ural_read_eeprom(struct ural_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint16_t val; + + ural_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2); + val = le16toh(val); + sc->rf_rev = (val >> 11) & 0x7; + sc->hw_radio = (val >> 10) & 0x1; + sc->led_mode = (val >> 6) & 0x7; + sc->rx_ant = (val >> 4) & 0x3; + sc->tx_ant = (val >> 2) & 0x3; + sc->nb_ant = val & 0x3; + + /* read MAC address */ + ural_eeprom_read(sc, RAL_EEPROM_ADDRESS, ic->ic_macaddr, 6); + + /* read default values for BBP registers */ + ural_eeprom_read(sc, RAL_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16); + + /* read Tx power for all b/g channels */ + ural_eeprom_read(sc, RAL_EEPROM_TXPOWER, sc->txpow, 14); +} + +static int +ural_bbp_init(struct ural_softc *sc) +{ + int i, ntries; + + /* wait for BBP to be ready */ + for (ntries = 0; ntries < 100; ntries++) { + if (ural_bbp_read(sc, RAL_BBP_VERSION) != 0) + break; + if (ural_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, "timeout waiting for BBP\n"); + return EIO; + } + + /* initialize BBP registers to default values */ + for (i = 0; i < nitems(ural_def_bbp); i++) + ural_bbp_write(sc, ural_def_bbp[i].reg, ural_def_bbp[i].val); + +#if 0 + /* initialize BBP registers to values stored in EEPROM */ + for (i = 0; i < 16; i++) { + if (sc->bbp_prom[i].reg == 0xff) + continue; + ural_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val); + } +#endif + + return 0; +} + +static void +ural_set_txantenna(struct ural_softc *sc, int antenna) +{ + uint16_t tmp; + uint8_t tx; + + tx = ural_bbp_read(sc, RAL_BBP_TX) & ~RAL_BBP_ANTMASK; + if (antenna == 1) + tx |= RAL_BBP_ANTA; + else if (antenna == 2) + tx |= RAL_BBP_ANTB; + else + tx |= RAL_BBP_DIVERSITY; + + /* need to force I/Q flip for RF 2525e, 2526 and 5222 */ + if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526 || + sc->rf_rev == RAL_RF_5222) + tx |= RAL_BBP_FLIPIQ; + + ural_bbp_write(sc, RAL_BBP_TX, tx); + + /* update values in PHY_CSR5 and PHY_CSR6 */ + tmp = ural_read(sc, RAL_PHY_CSR5) & ~0x7; + ural_write(sc, RAL_PHY_CSR5, tmp | (tx & 0x7)); + + tmp = ural_read(sc, RAL_PHY_CSR6) & ~0x7; + ural_write(sc, RAL_PHY_CSR6, tmp | (tx & 0x7)); +} + +static void +ural_set_rxantenna(struct ural_softc *sc, int antenna) +{ + uint8_t rx; + + rx = ural_bbp_read(sc, RAL_BBP_RX) & ~RAL_BBP_ANTMASK; + if (antenna == 1) + rx |= RAL_BBP_ANTA; + else if (antenna == 2) + rx |= RAL_BBP_ANTB; + else + rx |= RAL_BBP_DIVERSITY; + + /* need to force no I/Q flip for RF 2525e and 2526 */ + if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526) + rx &= ~RAL_BBP_FLIPIQ; + + ural_bbp_write(sc, RAL_BBP_RX, rx); +} + +static void +ural_init(struct ural_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint16_t tmp; + int i, ntries; + + RAL_LOCK_ASSERT(sc, MA_OWNED); + + ural_set_testmode(sc); + ural_write(sc, 0x308, 0x00f0); /* XXX magic */ + + ural_stop(sc); + + /* initialize MAC registers to default values */ + for (i = 0; i < nitems(ural_def_mac); i++) + ural_write(sc, ural_def_mac[i].reg, ural_def_mac[i].val); + + /* wait for BBP and RF to wake up (this can take a long time!) */ + for (ntries = 0; ntries < 100; ntries++) { + tmp = ural_read(sc, RAL_MAC_CSR17); + if ((tmp & (RAL_BBP_AWAKE | RAL_RF_AWAKE)) == + (RAL_BBP_AWAKE | RAL_RF_AWAKE)) + break; + if (ural_pause(sc, hz / 100)) + break; + } + if (ntries == 100) { + device_printf(sc->sc_dev, + "timeout waiting for BBP/RF to wakeup\n"); + goto fail; + } + + /* we're ready! */ + ural_write(sc, RAL_MAC_CSR1, RAL_HOST_READY); + + /* set basic rate set (will be updated later) */ + ural_write(sc, RAL_TXRX_CSR11, 0x15f); + + if (ural_bbp_init(sc) != 0) + goto fail; + + ural_set_chan(sc, ic->ic_curchan); + + /* clear statistic registers (STA_CSR0 to STA_CSR10) */ + ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta); + + ural_set_txantenna(sc, sc->tx_ant); + ural_set_rxantenna(sc, sc->rx_ant); + + ural_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr); + + /* + * Allocate Tx and Rx xfer queues. + */ + ural_setup_tx_list(sc); + + /* kick Rx */ + tmp = RAL_DROP_PHY | RAL_DROP_CRC; + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + tmp |= RAL_DROP_CTL | RAL_DROP_BAD_VERSION; + if (ic->ic_opmode != IEEE80211_M_HOSTAP) + tmp |= RAL_DROP_TODS; + if (ic->ic_promisc == 0) + tmp |= RAL_DROP_NOT_TO_ME; + } + ural_write(sc, RAL_TXRX_CSR2, tmp); + + sc->sc_running = 1; + usbd_xfer_set_stall(sc->sc_xfer[URAL_BULK_WR]); + usbd_transfer_start(sc->sc_xfer[URAL_BULK_RD]); + return; + +fail: ural_stop(sc); +} + +static void +ural_stop(struct ural_softc *sc) +{ + + RAL_LOCK_ASSERT(sc, MA_OWNED); + + sc->sc_running = 0; + + /* + * Drain all the transfers, if not already drained: + */ + RAL_UNLOCK(sc); + usbd_transfer_drain(sc->sc_xfer[URAL_BULK_WR]); + usbd_transfer_drain(sc->sc_xfer[URAL_BULK_RD]); + RAL_LOCK(sc); + + ural_unsetup_tx_list(sc); + + /* disable Rx */ + ural_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX); + /* reset ASIC and BBP (but won't reset MAC registers!) */ + ural_write(sc, RAL_MAC_CSR1, RAL_RESET_ASIC | RAL_RESET_BBP); + /* wait a little */ + ural_pause(sc, hz / 10); + ural_write(sc, RAL_MAC_CSR1, 0); + /* wait a little */ + ural_pause(sc, hz / 10); +} + +static int +ural_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ural_softc *sc = ic->ic_softc; + + RAL_LOCK(sc); + /* prevent management frames from being sent if we're not ready */ + if (!sc->sc_running) { + RAL_UNLOCK(sc); + m_freem(m); + return ENETDOWN; + } + if (sc->tx_nfree < RAL_TX_MINFREE) { + RAL_UNLOCK(sc); + m_freem(m); + return EIO; + } + + if (params == NULL) { + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + */ + if (ural_tx_mgt(sc, m, ni) != 0) + goto bad; + } else { + /* + * Caller supplied explicit parameters to use in + * sending the frame. + */ + if (ural_tx_raw(sc, m, ni, params) != 0) + goto bad; + } + RAL_UNLOCK(sc); + return 0; +bad: + RAL_UNLOCK(sc); + return EIO; /* XXX */ +} + +static void +ural_ratectl_start(struct ural_softc *sc, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ural_vap *uvp = URAL_VAP(vap); + + /* clear statistic registers (STA_CSR0 to STA_CSR10) */ + ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta); + + usb_callout_reset(&uvp->ratectl_ch, hz, ural_ratectl_timeout, uvp); +} + +static void +ural_ratectl_timeout(void *arg) +{ + struct ural_vap *uvp = arg; + struct ieee80211vap *vap = &uvp->vap; + struct ieee80211com *ic = vap->iv_ic; + + ieee80211_runtask(ic, &uvp->ratectl_task); +} + +static void +ural_ratectl_task(void *arg, int pending) +{ + struct ural_vap *uvp = arg; + struct ieee80211vap *vap = &uvp->vap; + struct ural_softc *sc = vap->iv_ic->ic_softc; + struct ieee80211_ratectl_tx_stats *txs = &sc->sc_txs; + int fail; + + RAL_LOCK(sc); + /* read and clear statistic registers (STA_CSR0 to STA_CSR10) */ + ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof(sc->sta)); + + txs->flags = IEEE80211_RATECTL_TX_STATS_RETRIES; + txs->nsuccess = sc->sta[7] + /* TX ok w/o retry */ + sc->sta[8]; /* TX ok w/ retry */ + fail = sc->sta[9]; /* TX retry-fail count */ + txs->nframes = txs->nsuccess + fail; + /* XXX fail * maxretry */ + txs->nretries = sc->sta[8] + fail; + + ieee80211_ratectl_tx_update(vap, txs); + + /* count TX retry-fail as Tx errors */ + if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, fail); + + usb_callout_reset(&uvp->ratectl_ch, hz, ural_ratectl_timeout, uvp); + RAL_UNLOCK(sc); +} + +static int +ural_pause(struct ural_softc *sc, int timeout) +{ + + usb_pause_mtx(&sc->sc_mtx, timeout); + return (0); +} diff --git a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_uralreg.h b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_uralreg.h new file mode 100644 index 0000000000..22d763d0dc --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_uralreg.h @@ -0,0 +1,210 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005, 2006 + * 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 RAL_NOISE_FLOOR -95 +#define RAL_RSSI_CORR 120 + +#define RAL_RX_DESC_SIZE (sizeof (struct ural_rx_desc)) +#define RAL_TX_DESC_SIZE (sizeof (struct ural_tx_desc)) +#define RAL_FRAME_SIZE 0x780 /* NOTE: using 0x980 does not work */ + +#define RAL_CONFIG_NO 1 +#define RAL_IFACE_INDEX 0 + +#define RAL_VENDOR_REQUEST 0x01 +#define RAL_WRITE_MAC 0x02 +#define RAL_READ_MAC 0x03 +#define RAL_WRITE_MULTI_MAC 0x06 +#define RAL_READ_MULTI_MAC 0x07 +#define RAL_READ_EEPROM 0x09 + +/* + * MAC registers. + */ +#define RAL_MAC_CSR0 0x0400 /* ASIC Version */ +#define RAL_MAC_CSR1 0x0402 /* System control */ +#define RAL_MAC_CSR2 0x0404 /* MAC addr0 */ +#define RAL_MAC_CSR3 0x0406 /* MAC addr1 */ +#define RAL_MAC_CSR4 0x0408 /* MAC addr2 */ +#define RAL_MAC_CSR5 0x040a /* BSSID0 */ +#define RAL_MAC_CSR6 0x040c /* BSSID1 */ +#define RAL_MAC_CSR7 0x040e /* BSSID2 */ +#define RAL_MAC_CSR8 0x0410 /* Max frame length */ +#define RAL_MAC_CSR9 0x0412 /* Timer control */ +#define RAL_MAC_CSR10 0x0414 /* Slot time */ +#define RAL_MAC_CSR11 0x0416 /* IFS */ +#define RAL_MAC_CSR12 0x0418 /* EIFS */ +#define RAL_MAC_CSR13 0x041a /* Power mode0 */ +#define RAL_MAC_CSR14 0x041c /* Power mode1 */ +#define RAL_MAC_CSR15 0x041e /* Power saving transition0 */ +#define RAL_MAC_CSR16 0x0420 /* Power saving transition1 */ +#define RAL_MAC_CSR17 0x0422 /* Power state control */ +#define RAL_MAC_CSR18 0x0424 /* Auto wake-up control */ +#define RAL_MAC_CSR19 0x0426 /* GPIO control */ +#define RAL_MAC_CSR20 0x0428 /* LED control0 */ +#define RAL_MAC_CSR22 0x042c /* XXX not documented */ + +/* + * Tx/Rx Registers. + */ +#define RAL_TXRX_CSR0 0x0440 /* Security control */ +#define RAL_TXRX_CSR2 0x0444 /* Rx control */ +#define RAL_TXRX_CSR5 0x044a /* CCK Tx BBP ID0 */ +#define RAL_TXRX_CSR6 0x044c /* CCK Tx BBP ID1 */ +#define RAL_TXRX_CSR7 0x044e /* OFDM Tx BBP ID0 */ +#define RAL_TXRX_CSR8 0x0450 /* OFDM Tx BBP ID1 */ +#define RAL_TXRX_CSR10 0x0454 /* Auto responder control */ +#define RAL_TXRX_CSR11 0x0456 /* Auto responder basic rate */ +#define RAL_TXRX_CSR18 0x0464 /* Beacon interval */ +#define RAL_TXRX_CSR19 0x0466 /* Beacon/sync control */ +#define RAL_TXRX_CSR20 0x0468 /* Beacon alignment */ +#define RAL_TXRX_CSR21 0x046a /* XXX not documented */ + +/* + * Security registers. + */ +#define RAL_SEC_CSR0 0x0480 /* Shared key 0, word 0 */ + +/* + * PHY registers. + */ +#define RAL_PHY_CSR2 0x04c4 /* Tx MAC configuration */ +#define RAL_PHY_CSR4 0x04c8 /* Interface configuration */ +#define RAL_PHY_CSR5 0x04ca /* BBP Pre-Tx CCK */ +#define RAL_PHY_CSR6 0x04cc /* BBP Pre-Tx OFDM */ +#define RAL_PHY_CSR7 0x04ce /* BBP serial control */ +#define RAL_PHY_CSR8 0x04d0 /* BBP serial status */ +#define RAL_PHY_CSR9 0x04d2 /* RF serial control0 */ +#define RAL_PHY_CSR10 0x04d4 /* RF serial control1 */ + +/* + * Statistics registers. + */ +#define RAL_STA_CSR0 0x04e0 /* FCS error */ + +#define RAL_DISABLE_RX (1 << 0) +#define RAL_DROP_CRC (1 << 1) +#define RAL_DROP_PHY (1 << 2) +#define RAL_DROP_CTL (1 << 3) +#define RAL_DROP_NOT_TO_ME (1 << 4) +#define RAL_DROP_TODS (1 << 5) +#define RAL_DROP_BAD_VERSION (1 << 6) +#define RAL_DROP_MULTICAST (1 << 9) +#define RAL_DROP_BROADCAST (1 << 10) + +#define RAL_SHORT_PREAMBLE (1 << 2) + +#define RAL_RESET_ASIC (1 << 0) +#define RAL_RESET_BBP (1 << 1) +#define RAL_HOST_READY (1 << 2) + +#define RAL_ENABLE_TSF (1 << 0) +#define RAL_ENABLE_TSF_SYNC(x) (((x) & 0x3) << 1) +#define RAL_ENABLE_TBCN (1 << 3) +#define RAL_ENABLE_BEACON_GENERATOR (1 << 4) + +#define RAL_RF_AWAKE (3 << 7) +#define RAL_BBP_AWAKE (3 << 5) + +#define RAL_BBP_WRITE (1 << 15) +#define RAL_BBP_BUSY (1 << 0) + +#define RAL_RF1_AUTOTUNE 0x08000 +#define RAL_RF3_AUTOTUNE 0x00040 + +#define RAL_RF_2522 0x00 +#define RAL_RF_2523 0x01 +#define RAL_RF_2524 0x02 +#define RAL_RF_2525 0x03 +#define RAL_RF_2525E 0x04 +#define RAL_RF_2526 0x05 +/* dual-band RF */ +#define RAL_RF_5222 0x10 + +#define RAL_BBP_VERSION 0 +#define RAL_BBP_TX 2 +#define RAL_BBP_RX 14 + +#define RAL_BBP_ANTA 0x00 +#define RAL_BBP_DIVERSITY 0x01 +#define RAL_BBP_ANTB 0x02 +#define RAL_BBP_ANTMASK 0x03 +#define RAL_BBP_FLIPIQ 0x04 + +#define RAL_JAPAN_FILTER 0x08 + +struct ural_tx_desc { + uint32_t flags; +#define RAL_TX_RETRY(x) ((x) << 4) +#define RAL_TX_MORE_FRAG (1 << 8) +#define RAL_TX_ACK (1 << 9) +#define RAL_TX_TIMESTAMP (1 << 10) +#define RAL_TX_OFDM (1 << 11) +#define RAL_TX_NEWSEQ (1 << 12) + +#define RAL_TX_IFS_MASK 0x00006000 +#define RAL_TX_IFS_BACKOFF (0 << 13) +#define RAL_TX_IFS_SIFS (1 << 13) +#define RAL_TX_IFS_NEWBACKOFF (2 << 13) +#define RAL_TX_IFS_NONE (3 << 13) + + uint16_t wme; +#define RAL_LOGCWMAX(x) (((x) & 0xf) << 12) +#define RAL_LOGCWMIN(x) (((x) & 0xf) << 8) +#define RAL_AIFSN(x) (((x) & 0x3) << 6) +#define RAL_IVOFFSET(x) (((x) & 0x3f)) + + uint16_t reserved1; + uint8_t plcp_signal; + uint8_t plcp_service; +#define RAL_PLCP_LENGEXT 0x80 + + uint8_t plcp_length_lo; + uint8_t plcp_length_hi; + uint32_t iv; + uint32_t eiv; +} __packed; + +struct ural_rx_desc { + uint32_t flags; +#define RAL_RX_CRC_ERROR (1 << 5) +#define RAL_RX_OFDM (1 << 6) +#define RAL_RX_PHY_ERROR (1 << 7) + + uint8_t rssi; + uint8_t rate; + uint16_t reserved; + + uint32_t iv; + uint32_t eiv; +} __packed; + +#define RAL_RF_LOBUSY (1 << 15) +#define RAL_RF_BUSY (1U << 31) +#define RAL_RF_20BIT (20 << 24) + +#define RAL_RF1 0 +#define RAL_RF2 2 +#define RAL_RF3 1 +#define RAL_RF4 3 + +#define RAL_EEPROM_ADDRESS 0x0004 +#define RAL_EEPROM_TXPOWER 0x003c +#define RAL_EEPROM_CONFIG0 0x0016 +#define RAL_EEPROM_BBP_BASE 0x001c diff --git a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_uralvar.h b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_uralvar.h new file mode 100644 index 0000000000..6f6d01ed75 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/if_uralvar.h @@ -0,0 +1,135 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2005 + * 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 RAL_TX_LIST_COUNT 8 +#define RAL_TX_MINFREE 2 + +#define URAL_SCAN_START 1 +#define URAL_SCAN_END 2 +#define URAL_SET_CHANNEL 3 + +struct ural_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + int8_t wr_antsignal; + int8_t wr_antnoise; + uint8_t wr_antenna; +} __packed __aligned(8); + +#define RAL_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE)) + +struct ural_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_antenna; +} __packed; + +#define RAL_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA)) + +struct ural_softc; + +struct ural_tx_data { + STAILQ_ENTRY(ural_tx_data) next; + struct ural_softc *sc; + struct ural_tx_desc desc; + struct mbuf *m; + struct ieee80211_node *ni; + int rate; +}; +typedef STAILQ_HEAD(, ural_tx_data) ural_txdhead; + +struct ural_vap { + struct ieee80211vap vap; + + struct usb_callout ratectl_ch; + struct task ratectl_task; + + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); +}; +#define URAL_VAP(vap) ((struct ural_vap *)(vap)) + +enum { + URAL_BULK_WR, + URAL_BULK_RD, + URAL_N_TRANSFER = 2, +}; + +struct ural_softc { + struct ieee80211com sc_ic; + struct ieee80211_ratectl_tx_stats sc_txs; + struct mbufq sc_snd; + device_t sc_dev; + struct usb_device *sc_udev; + + uint32_t asic_rev; + uint8_t rf_rev; + + struct usb_xfer *sc_xfer[URAL_N_TRANSFER]; + + struct ural_tx_data tx_data[RAL_TX_LIST_COUNT]; + ural_txdhead tx_q; + ural_txdhead tx_free; + int tx_nfree; + struct ural_rx_desc sc_rx_desc; + + struct mtx sc_mtx; + + uint16_t sta[11]; + uint32_t rf_regs[4]; + uint8_t txpow[14]; + u_int sc_detached:1, + sc_running:1; + + uint8_t sc_bssid[IEEE80211_ADDR_LEN]; + + struct { + uint8_t val; + uint8_t reg; + } __packed bbp_prom[16]; + + int led_mode; + int hw_radio; + int rx_ant; + int tx_ant; + int nb_ant; + + struct ural_rx_radiotap_header sc_rxtap; + struct ural_tx_radiotap_header sc_txtap; +}; + +#define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define RAL_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t) diff --git a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/opt_wlan.h b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/opt_wlan.h new file mode 100644 index 0000000000..5b88e8fea7 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/dev/usb/wlan/opt_wlan.h @@ -0,0 +1 @@ +#include diff --git a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/glue.c b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/glue.c index 925c0dd850..981bca680a 100644 --- a/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/glue.c +++ b/src/add-ons/kernel/drivers/network/wlan/ralinkwifi/glue.c @@ -25,19 +25,42 @@ #include -HAIKU_FBSD_WLAN_DRIVER_GLUE(ralinkwifi, ral, pci) +HAIKU_FBSD_WLAN_DRIVERS_GLUE(ralinkwifi) HAIKU_DRIVER_REQUIREMENTS(FBSD_WLAN); HAIKU_FIRMWARE_VERSION(0); HAIKU_FIRMWARE_NAME_MAP({ {"rt2561fw", "rt2561.ucode"}, {"rt2561sfw", "rt2561s.ucode"}, {"rt2661fw", "rt2661.ucode"}, - {"rt2860fw", "rt2860.ucode"} + {"rt2860fw", "rt2860.ucode"}, + {"runfw", "rt2870.ucode"}, }); NO_HAIKU_FBSD_MII_DRIVER(); NO_HAIKU_REENABLE_INTERRUPTS(); +extern driver_t* DRIVER_MODULE_NAME(ral, pci); +extern driver_t* DRIVER_MODULE_NAME(ural, uhub); +extern driver_t* DRIVER_MODULE_NAME(run, uhub); +extern driver_t* DRIVER_MODULE_NAME(rum, uhub); + + +status_t +__haiku_handle_fbsd_drivers_list(status_t (*handler)(driver_t *[], driver_t *[])) +{ + driver_t *pci_drivers[] = { + DRIVER_MODULE_NAME(ral, pci), + NULL + }; + driver_t *usb_drivers[] = { + DRIVER_MODULE_NAME(ural, uhub), + DRIVER_MODULE_NAME(run, uhub), + DRIVER_MODULE_NAME(rum, uhub), + NULL + }; + return (*handler)(pci_drivers, usb_drivers); +} + #define RT2661_INT_MASK_CSR 0x346c #define RT2661_MCU_INT_MASK_CSR 0x0018