17453645fSAndriy Voskoboinyk /* $OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $ */ 2bcaed14bSAdrian Chadd 3bcaed14bSAdrian Chadd /*- 4bcaed14bSAdrian Chadd * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> 57453645fSAndriy Voskoboinyk * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org> 67453645fSAndriy Voskoboinyk * Copyright (c) 2015-2016 Andriy Voskoboinyk <avos@FreeBSD.org> 7bcaed14bSAdrian Chadd * 8bcaed14bSAdrian Chadd * Permission to use, copy, modify, and distribute this software for any 9bcaed14bSAdrian Chadd * purpose with or without fee is hereby granted, provided that the above 10bcaed14bSAdrian Chadd * copyright notice and this permission notice appear in all copies. 11bcaed14bSAdrian Chadd * 12bcaed14bSAdrian Chadd * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13bcaed14bSAdrian Chadd * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14bcaed14bSAdrian Chadd * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15bcaed14bSAdrian Chadd * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16bcaed14bSAdrian Chadd * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17bcaed14bSAdrian Chadd * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18bcaed14bSAdrian Chadd * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19bcaed14bSAdrian Chadd */ 20bcaed14bSAdrian Chadd 21bcaed14bSAdrian Chadd #include <sys/cdefs.h> 22bcaed14bSAdrian Chadd /* 237453645fSAndriy Voskoboinyk * Driver for Realtek RTL8188CE-VAU/RTL8188CUS/RTL8188EU/RTL8188RU/RTL8192CU/RTL8812AU/RTL8821AU. 24bcaed14bSAdrian Chadd */ 257453645fSAndriy Voskoboinyk #include "opt_wlan.h" 26bcaed14bSAdrian Chadd 27bcaed14bSAdrian Chadd #include <sys/param.h> 28bcaed14bSAdrian Chadd #include <sys/sockio.h> 297453645fSAndriy Voskoboinyk #include <sys/sysctl.h> 307453645fSAndriy Voskoboinyk #include <sys/lock.h> 317453645fSAndriy Voskoboinyk #include <sys/mutex.h> 32bcaed14bSAdrian Chadd #include <sys/mbuf.h> 33bcaed14bSAdrian Chadd #include <sys/kernel.h> 34bcaed14bSAdrian Chadd #include <sys/socket.h> 35bcaed14bSAdrian Chadd #include <sys/systm.h> 36bcaed14bSAdrian Chadd #include <sys/malloc.h> 37bcaed14bSAdrian Chadd #include <sys/module.h> 38bcaed14bSAdrian Chadd #include <sys/bus.h> 39bcaed14bSAdrian Chadd #include <sys/endian.h> 407453645fSAndriy Voskoboinyk #include <sys/linker.h> 41bcaed14bSAdrian Chadd #include <sys/firmware.h> 427453645fSAndriy Voskoboinyk #include <sys/kdb.h> 43bcaed14bSAdrian Chadd 44bcaed14bSAdrian Chadd #include <net/bpf.h> 45bcaed14bSAdrian Chadd #include <net/if.h> 46bcaed14bSAdrian Chadd #include <net/if_var.h> 47bcaed14bSAdrian Chadd #include <net/if_arp.h> 48bcaed14bSAdrian Chadd #include <net/ethernet.h> 49bcaed14bSAdrian Chadd #include <net/if_dl.h> 50bcaed14bSAdrian Chadd #include <net/if_media.h> 51bcaed14bSAdrian Chadd #include <net/if_types.h> 52bcaed14bSAdrian Chadd 53bcaed14bSAdrian Chadd #include <netinet/in.h> 54bcaed14bSAdrian Chadd #include <netinet/in_systm.h> 55bcaed14bSAdrian Chadd #include <netinet/in_var.h> 56bcaed14bSAdrian Chadd #include <netinet/if_ether.h> 577453645fSAndriy Voskoboinyk #include <netinet/ip.h> 587453645fSAndriy Voskoboinyk 597453645fSAndriy Voskoboinyk #include <net80211/ieee80211_var.h> 607453645fSAndriy Voskoboinyk #include <net80211/ieee80211_regdomain.h> 617453645fSAndriy Voskoboinyk #include <net80211/ieee80211_radiotap.h> 627453645fSAndriy Voskoboinyk #include <net80211/ieee80211_ratectl.h> 63bcaed14bSAdrian Chadd 64bcaed14bSAdrian Chadd #include <dev/rtwn/if_rtwnreg.h> 657453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h> 66bcaed14bSAdrian Chadd 677453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_beacon.h> 687453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_calib.h> 697453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_cam.h> 707453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_debug.h> 717453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_efuse.h> 727453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_fw.h> 737453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_ridx.h> 747453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_rx.h> 757453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_task.h> 767453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_tx.h> 77bcaed14bSAdrian Chadd 787453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/r92c_reg.h> 79bcaed14bSAdrian Chadd 807453645fSAndriy Voskoboinyk static void rtwn_radiotap_attach(struct rtwn_softc *); 817453645fSAndriy Voskoboinyk static void rtwn_vap_decrement_counters(struct rtwn_softc *, 827453645fSAndriy Voskoboinyk enum ieee80211_opmode, int); 837453645fSAndriy Voskoboinyk static void rtwn_set_ic_opmode(struct rtwn_softc *); 84bcaed14bSAdrian Chadd static struct ieee80211vap *rtwn_vap_create(struct ieee80211com *, 857453645fSAndriy Voskoboinyk const char [IFNAMSIZ], int, enum ieee80211_opmode, 867453645fSAndriy Voskoboinyk int, const uint8_t [IEEE80211_ADDR_LEN], 87bcaed14bSAdrian Chadd const uint8_t [IEEE80211_ADDR_LEN]); 88bcaed14bSAdrian Chadd static void rtwn_vap_delete(struct ieee80211vap *); 89bcaed14bSAdrian Chadd static int rtwn_read_chipid(struct rtwn_softc *); 907453645fSAndriy Voskoboinyk static int rtwn_ioctl_reset(struct ieee80211vap *, u_long); 917453645fSAndriy Voskoboinyk static void rtwn_set_media_status(struct rtwn_softc *, 927453645fSAndriy Voskoboinyk union sec_param *); 9359ed13aaSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE 947453645fSAndriy Voskoboinyk static int rtwn_tx_fwpkt_check(struct rtwn_softc *, 957453645fSAndriy Voskoboinyk struct ieee80211vap *); 967453645fSAndriy Voskoboinyk static int rtwn_construct_nulldata(struct rtwn_softc *, 977453645fSAndriy Voskoboinyk struct ieee80211vap *, uint8_t *, int); 987453645fSAndriy Voskoboinyk static int rtwn_push_nulldata(struct rtwn_softc *, 997453645fSAndriy Voskoboinyk struct ieee80211vap *); 1007453645fSAndriy Voskoboinyk static void rtwn_pwrmode_init(void *); 1017453645fSAndriy Voskoboinyk static void rtwn_set_pwrmode_cb(struct rtwn_softc *, 1027453645fSAndriy Voskoboinyk union sec_param *); 1037453645fSAndriy Voskoboinyk #endif 1047453645fSAndriy Voskoboinyk static void rtwn_tsf_sync_adhoc(void *); 1057453645fSAndriy Voskoboinyk static void rtwn_tsf_sync_adhoc_task(void *, int); 1067453645fSAndriy Voskoboinyk static void rtwn_tsf_sync_enable(struct rtwn_softc *, 1077453645fSAndriy Voskoboinyk struct ieee80211vap *); 1087453645fSAndriy Voskoboinyk static void rtwn_set_ack_preamble(struct rtwn_softc *); 1097453645fSAndriy Voskoboinyk static void rtwn_set_mode(struct rtwn_softc *, uint8_t, int); 1107453645fSAndriy Voskoboinyk static int rtwn_monitor_newstate(struct ieee80211vap *, 1117453645fSAndriy Voskoboinyk enum ieee80211_state, int); 1127453645fSAndriy Voskoboinyk static int rtwn_newstate(struct ieee80211vap *, 1137453645fSAndriy Voskoboinyk enum ieee80211_state, int); 1147453645fSAndriy Voskoboinyk static void rtwn_calc_basicrates(struct rtwn_softc *); 1157453645fSAndriy Voskoboinyk static int rtwn_run(struct rtwn_softc *, 1167453645fSAndriy Voskoboinyk struct ieee80211vap *); 1177453645fSAndriy Voskoboinyk #ifndef D4054 118bcaed14bSAdrian Chadd static void rtwn_watchdog(void *); 1197453645fSAndriy Voskoboinyk #endif 1207453645fSAndriy Voskoboinyk static void rtwn_parent(struct ieee80211com *); 121bcaed14bSAdrian Chadd static int rtwn_dma_init(struct rtwn_softc *); 1227453645fSAndriy Voskoboinyk static int rtwn_mac_init(struct rtwn_softc *); 1237453645fSAndriy Voskoboinyk static void rtwn_mrr_init(struct rtwn_softc *); 124bcaed14bSAdrian Chadd static void rtwn_scan_start(struct ieee80211com *); 1257453645fSAndriy Voskoboinyk static void rtwn_scan_curchan(struct ieee80211_scan_state *, 1267453645fSAndriy Voskoboinyk unsigned long); 127bcaed14bSAdrian Chadd static void rtwn_scan_end(struct ieee80211com *); 128a7c31fe1SAndriy Voskoboinyk static void rtwn_getradiocaps(struct ieee80211com *, int, int *, 129a7c31fe1SAndriy Voskoboinyk struct ieee80211_channel[]); 1307453645fSAndriy Voskoboinyk static void rtwn_update_chw(struct ieee80211com *); 131bcaed14bSAdrian Chadd static void rtwn_set_channel(struct ieee80211com *); 1327453645fSAndriy Voskoboinyk static int rtwn_wme_update(struct ieee80211com *); 1337453645fSAndriy Voskoboinyk static void rtwn_update_slot(struct ieee80211com *); 1347453645fSAndriy Voskoboinyk static void rtwn_update_slot_cb(struct rtwn_softc *, 1357453645fSAndriy Voskoboinyk union sec_param *); 1367453645fSAndriy Voskoboinyk static void rtwn_update_aifs(struct rtwn_softc *, uint8_t); 1377453645fSAndriy Voskoboinyk static void rtwn_update_promisc(struct ieee80211com *); 138bcaed14bSAdrian Chadd static void rtwn_update_mcast(struct ieee80211com *); 1397453645fSAndriy Voskoboinyk static int rtwn_set_bssid(struct rtwn_softc *, 1407453645fSAndriy Voskoboinyk const uint8_t *, int); 1417453645fSAndriy Voskoboinyk static int rtwn_set_macaddr(struct rtwn_softc *, 1427453645fSAndriy Voskoboinyk const uint8_t *, int); 1437453645fSAndriy Voskoboinyk static struct ieee80211_node *rtwn_node_alloc(struct ieee80211vap *, 1447453645fSAndriy Voskoboinyk const uint8_t mac[IEEE80211_ADDR_LEN]); 1457453645fSAndriy Voskoboinyk static void rtwn_newassoc(struct ieee80211_node *, int); 1467453645fSAndriy Voskoboinyk static void rtwn_node_free(struct ieee80211_node *); 1477453645fSAndriy Voskoboinyk static void rtwn_init_beacon_reg(struct rtwn_softc *); 148798e1ce3SAndriy Voskoboinyk static int rtwn_init(struct rtwn_softc *); 149bcaed14bSAdrian Chadd static void rtwn_stop(struct rtwn_softc *); 150bcaed14bSAdrian Chadd 1517453645fSAndriy Voskoboinyk MALLOC_DEFINE(M_RTWN_PRIV, "rtwn_priv", "rtwn driver private state"); 152bcaed14bSAdrian Chadd 1537453645fSAndriy Voskoboinyk static const uint16_t wme2reg[] = 1547453645fSAndriy Voskoboinyk { R92C_EDCA_BE_PARAM, R92C_EDCA_BK_PARAM, 1557453645fSAndriy Voskoboinyk R92C_EDCA_VI_PARAM, R92C_EDCA_VO_PARAM }; 156bcaed14bSAdrian Chadd 1577453645fSAndriy Voskoboinyk int 1587453645fSAndriy Voskoboinyk rtwn_attach(struct rtwn_softc *sc) 159bcaed14bSAdrian Chadd { 160bcaed14bSAdrian Chadd struct ieee80211com *ic = &sc->sc_ic; 1617453645fSAndriy Voskoboinyk int error; 162bcaed14bSAdrian Chadd 1637453645fSAndriy Voskoboinyk sc->cur_bcnq_id = RTWN_VAP_ID_INVALID; 164bcaed14bSAdrian Chadd 1657453645fSAndriy Voskoboinyk RTWN_NT_LOCK_INIT(sc); 1667453645fSAndriy Voskoboinyk rtwn_cmdq_init(sc); 1677453645fSAndriy Voskoboinyk #ifndef D4054 1687453645fSAndriy Voskoboinyk callout_init_mtx(&sc->sc_watchdog_to, &sc->sc_mtx, 0); 1697453645fSAndriy Voskoboinyk #endif 1707453645fSAndriy Voskoboinyk callout_init(&sc->sc_calib_to, 0); 1717453645fSAndriy Voskoboinyk callout_init(&sc->sc_pwrmode_init, 0); 172bcaed14bSAdrian Chadd mbufq_init(&sc->sc_snd, ifqmaxlen); 173bcaed14bSAdrian Chadd 1747453645fSAndriy Voskoboinyk RTWN_LOCK(sc); 175bcaed14bSAdrian Chadd error = rtwn_read_chipid(sc); 1767453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 177bcaed14bSAdrian Chadd if (error != 0) { 1787453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "unsupported test chip\n"); 1797453645fSAndriy Voskoboinyk goto detach; 180bcaed14bSAdrian Chadd } 181bcaed14bSAdrian Chadd 1827453645fSAndriy Voskoboinyk error = rtwn_read_rom(sc); 183bcaed14bSAdrian Chadd if (error != 0) { 1847453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "%s: cannot read rom, error %d\n", 1857453645fSAndriy Voskoboinyk __func__, error); 1867453645fSAndriy Voskoboinyk goto detach; 187bcaed14bSAdrian Chadd } 188bcaed14bSAdrian Chadd 1897453645fSAndriy Voskoboinyk if (sc->macid_limit > RTWN_MACID_LIMIT) { 1907453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, 1917453645fSAndriy Voskoboinyk "macid limit will be reduced from %d to %d\n", 1927453645fSAndriy Voskoboinyk sc->macid_limit, RTWN_MACID_LIMIT); 1937453645fSAndriy Voskoboinyk sc->macid_limit = RTWN_MACID_LIMIT; 194bcaed14bSAdrian Chadd } 1957453645fSAndriy Voskoboinyk if (sc->cam_entry_limit > RTWN_CAM_ENTRY_LIMIT) { 1967453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, 1977453645fSAndriy Voskoboinyk "cam entry limit will be reduced from %d to %d\n", 1987453645fSAndriy Voskoboinyk sc->cam_entry_limit, RTWN_CAM_ENTRY_LIMIT); 1997453645fSAndriy Voskoboinyk sc->cam_entry_limit = RTWN_CAM_ENTRY_LIMIT; 2007453645fSAndriy Voskoboinyk } 2017453645fSAndriy Voskoboinyk if (sc->txdesc_len > RTWN_TX_DESC_SIZE) { 2027453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, 2037453645fSAndriy Voskoboinyk "adjust size for Tx descriptor (current %d, needed %d)\n", 2047453645fSAndriy Voskoboinyk RTWN_TX_DESC_SIZE, sc->txdesc_len); 2057453645fSAndriy Voskoboinyk goto detach; 2067453645fSAndriy Voskoboinyk } 207bcaed14bSAdrian Chadd 2087453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "MAC/BB %s, RF 6052 %dT%dR\n", 2097453645fSAndriy Voskoboinyk sc->name, sc->ntxchains, sc->nrxchains); 210bcaed14bSAdrian Chadd 211bcaed14bSAdrian Chadd ic->ic_softc = sc; 212bcaed14bSAdrian Chadd ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ 2137453645fSAndriy Voskoboinyk ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ 214bcaed14bSAdrian Chadd 215bcaed14bSAdrian Chadd /* set device capabilities */ 216bcaed14bSAdrian Chadd ic->ic_caps = 217bcaed14bSAdrian Chadd IEEE80211_C_STA /* station mode */ 218bcaed14bSAdrian Chadd | IEEE80211_C_MONITOR /* monitor mode */ 2197453645fSAndriy Voskoboinyk | IEEE80211_C_IBSS /* adhoc mode */ 2207453645fSAndriy Voskoboinyk | IEEE80211_C_HOSTAP /* hostap mode */ 2217453645fSAndriy Voskoboinyk #if 0 /* TODO: HRPWM register setup */ 2227453645fSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE 2237453645fSAndriy Voskoboinyk | IEEE80211_C_PMGT /* Station-side power mgmt */ 2247453645fSAndriy Voskoboinyk #endif 2257453645fSAndriy Voskoboinyk #endif 226bcaed14bSAdrian Chadd | IEEE80211_C_SHPREAMBLE /* short preamble supported */ 227bcaed14bSAdrian Chadd | IEEE80211_C_SHSLOT /* short slot time supported */ 2287453645fSAndriy Voskoboinyk #if 0 229bcaed14bSAdrian Chadd | IEEE80211_C_BGSCAN /* capable of bg scanning */ 2307453645fSAndriy Voskoboinyk #endif 2317453645fSAndriy Voskoboinyk | IEEE80211_C_WPA /* 802.11i */ 232bcaed14bSAdrian Chadd | IEEE80211_C_WME /* 802.11e */ 2337453645fSAndriy Voskoboinyk | IEEE80211_C_SWAMSDUTX /* Do software A-MSDU TX */ 2347453645fSAndriy Voskoboinyk | IEEE80211_C_FF /* Atheros fast-frames */ 235b71805e9SAdrian Chadd | IEEE80211_C_TXPMGT /* TX power control */ 236bcaed14bSAdrian Chadd ; 237bcaed14bSAdrian Chadd 2387453645fSAndriy Voskoboinyk if (sc->sc_hwcrypto != RTWN_CRYPTO_SW) { 2397453645fSAndriy Voskoboinyk ic->ic_cryptocaps = 2407453645fSAndriy Voskoboinyk IEEE80211_CRYPTO_WEP | 2417453645fSAndriy Voskoboinyk IEEE80211_CRYPTO_TKIP | 2427453645fSAndriy Voskoboinyk IEEE80211_CRYPTO_AES_CCM; 2437453645fSAndriy Voskoboinyk } 2447453645fSAndriy Voskoboinyk 2457453645fSAndriy Voskoboinyk ic->ic_htcaps = 2467453645fSAndriy Voskoboinyk IEEE80211_HTCAP_SHORTGI20 /* short GI in 20MHz */ 2477453645fSAndriy Voskoboinyk | IEEE80211_HTCAP_MAXAMSDU_3839 /* max A-MSDU length */ 2487453645fSAndriy Voskoboinyk | IEEE80211_HTCAP_SMPS_OFF /* SM PS mode disabled */ 2497453645fSAndriy Voskoboinyk /* s/w capabilities */ 2507453645fSAndriy Voskoboinyk | IEEE80211_HTC_HT /* HT operation */ 25105c3851bSAdrian Chadd | IEEE80211_HTC_RX_AMSDU_AMPDU /* A-MSDU in A-MPDU */ 2527453645fSAndriy Voskoboinyk | IEEE80211_HTC_AMPDU /* A-MPDU tx */ 2537453645fSAndriy Voskoboinyk | IEEE80211_HTC_AMSDU /* A-MSDU tx */ 2547453645fSAndriy Voskoboinyk ; 2557453645fSAndriy Voskoboinyk 2567453645fSAndriy Voskoboinyk if (sc->sc_ht40) { 2577453645fSAndriy Voskoboinyk ic->ic_htcaps |= 2587453645fSAndriy Voskoboinyk IEEE80211_HTCAP_CHWIDTH40 /* 40 MHz channel width */ 2597453645fSAndriy Voskoboinyk | IEEE80211_HTCAP_SHORTGI40 /* short GI in 40MHz */ 2607453645fSAndriy Voskoboinyk ; 2617453645fSAndriy Voskoboinyk } 2627453645fSAndriy Voskoboinyk 2637453645fSAndriy Voskoboinyk ic->ic_txstream = sc->ntxchains; 2647453645fSAndriy Voskoboinyk ic->ic_rxstream = sc->nrxchains; 2657453645fSAndriy Voskoboinyk 2667453645fSAndriy Voskoboinyk /* Enable TX watchdog */ 2677453645fSAndriy Voskoboinyk #ifdef D4054 2687453645fSAndriy Voskoboinyk ic->ic_flags_ext |= IEEE80211_FEXT_WATCHDOG; 2697453645fSAndriy Voskoboinyk #endif 2707453645fSAndriy Voskoboinyk 2717453645fSAndriy Voskoboinyk /* Adjust capabilities. */ 2727453645fSAndriy Voskoboinyk rtwn_adj_devcaps(sc); 273a7c31fe1SAndriy Voskoboinyk 274a7c31fe1SAndriy Voskoboinyk rtwn_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, 275a7c31fe1SAndriy Voskoboinyk ic->ic_channels); 276bcaed14bSAdrian Chadd 2777453645fSAndriy Voskoboinyk /* XXX TODO: setup regdomain if R92C_CHANNEL_PLAN_BY_HW bit is set. */ 278bcaed14bSAdrian Chadd 2797453645fSAndriy Voskoboinyk ieee80211_ifattach(ic); 2807453645fSAndriy Voskoboinyk ic->ic_raw_xmit = rtwn_raw_xmit; 281bcaed14bSAdrian Chadd ic->ic_scan_start = rtwn_scan_start; 2827453645fSAndriy Voskoboinyk sc->sc_scan_curchan = ic->ic_scan_curchan; 2837453645fSAndriy Voskoboinyk ic->ic_scan_curchan = rtwn_scan_curchan; 284bcaed14bSAdrian Chadd ic->ic_scan_end = rtwn_scan_end; 285a7c31fe1SAndriy Voskoboinyk ic->ic_getradiocaps = rtwn_getradiocaps; 2867453645fSAndriy Voskoboinyk ic->ic_update_chw = rtwn_update_chw; 287bcaed14bSAdrian Chadd ic->ic_set_channel = rtwn_set_channel; 288bcaed14bSAdrian Chadd ic->ic_transmit = rtwn_transmit; 289bcaed14bSAdrian Chadd ic->ic_parent = rtwn_parent; 290bcaed14bSAdrian Chadd ic->ic_vap_create = rtwn_vap_create; 291bcaed14bSAdrian Chadd ic->ic_vap_delete = rtwn_vap_delete; 2927453645fSAndriy Voskoboinyk ic->ic_wme.wme_update = rtwn_wme_update; 2937453645fSAndriy Voskoboinyk ic->ic_updateslot = rtwn_update_slot; 2947453645fSAndriy Voskoboinyk ic->ic_update_promisc = rtwn_update_promisc; 2957453645fSAndriy Voskoboinyk ic->ic_update_mcast = rtwn_update_mcast; 2967453645fSAndriy Voskoboinyk ic->ic_node_alloc = rtwn_node_alloc; 2977453645fSAndriy Voskoboinyk ic->ic_newassoc = rtwn_newassoc; 2987453645fSAndriy Voskoboinyk sc->sc_node_free = ic->ic_node_free; 2997453645fSAndriy Voskoboinyk ic->ic_node_free = rtwn_node_free; 300bcaed14bSAdrian Chadd 3017453645fSAndriy Voskoboinyk rtwn_postattach(sc); 3027453645fSAndriy Voskoboinyk rtwn_radiotap_attach(sc); 303bcaed14bSAdrian Chadd 304bcaed14bSAdrian Chadd if (bootverbose) 305bcaed14bSAdrian Chadd ieee80211_announce(ic); 306bcaed14bSAdrian Chadd 307bcaed14bSAdrian Chadd return (0); 308bcaed14bSAdrian Chadd 3097453645fSAndriy Voskoboinyk detach: 3107453645fSAndriy Voskoboinyk return (ENXIO); /* failure */ 311bcaed14bSAdrian Chadd } 312bcaed14bSAdrian Chadd 313bcaed14bSAdrian Chadd static void 3147453645fSAndriy Voskoboinyk rtwn_radiotap_attach(struct rtwn_softc *sc) 315bcaed14bSAdrian Chadd { 3167453645fSAndriy Voskoboinyk struct rtwn_rx_radiotap_header *rxtap = &sc->sc_rxtap; 3177453645fSAndriy Voskoboinyk struct rtwn_tx_radiotap_header *txtap = &sc->sc_txtap; 318bcaed14bSAdrian Chadd 3197453645fSAndriy Voskoboinyk ieee80211_radiotap_attach(&sc->sc_ic, 3207453645fSAndriy Voskoboinyk &txtap->wt_ihdr, sizeof(*txtap), RTWN_TX_RADIOTAP_PRESENT, 3217453645fSAndriy Voskoboinyk &rxtap->wr_ihdr, sizeof(*rxtap), RTWN_RX_RADIOTAP_PRESENT); 3227453645fSAndriy Voskoboinyk } 3237453645fSAndriy Voskoboinyk 3247ddf1949SAdrian Chadd #ifdef RTWN_DEBUG 3257ddf1949SAdrian Chadd static int 3267ddf1949SAdrian Chadd rtwn_sysctl_reg_readwrite(SYSCTL_HANDLER_ARGS) 3277ddf1949SAdrian Chadd { 3287ddf1949SAdrian Chadd struct rtwn_softc *sc = arg1; 3297ddf1949SAdrian Chadd int error; 3307ddf1949SAdrian Chadd uint32_t val; 3317ddf1949SAdrian Chadd 3327ddf1949SAdrian Chadd if (sc->sc_reg_addr > 0xffff) 3337ddf1949SAdrian Chadd return (EINVAL); 3347ddf1949SAdrian Chadd 3357ddf1949SAdrian Chadd RTWN_LOCK(sc); 3367ddf1949SAdrian Chadd val = rtwn_read_4(sc, sc->sc_reg_addr); 3377ddf1949SAdrian Chadd RTWN_UNLOCK(sc); 3387ddf1949SAdrian Chadd error = sysctl_handle_int(oidp, &val, 0, req); 3397ddf1949SAdrian Chadd if (error || !req->newptr) 3407ddf1949SAdrian Chadd return (error); 3417ddf1949SAdrian Chadd RTWN_LOCK(sc); 3427ddf1949SAdrian Chadd rtwn_write_4(sc, sc->sc_reg_addr, val); 3437ddf1949SAdrian Chadd RTWN_UNLOCK(sc); 3447ddf1949SAdrian Chadd return (0); 3457ddf1949SAdrian Chadd } 3467ddf1949SAdrian Chadd #endif /* RTWN_DEBUG */ 3477ddf1949SAdrian Chadd 3487453645fSAndriy Voskoboinyk void 3497453645fSAndriy Voskoboinyk rtwn_sysctlattach(struct rtwn_softc *sc) 3507453645fSAndriy Voskoboinyk { 3517453645fSAndriy Voskoboinyk struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); 3527453645fSAndriy Voskoboinyk struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); 3537453645fSAndriy Voskoboinyk 3547ddf1949SAdrian Chadd sc->sc_reg_addr = 0; 3557ddf1949SAdrian Chadd #ifdef RTWN_DEBUG 3567ddf1949SAdrian Chadd SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3577ddf1949SAdrian Chadd "reg_addr", CTLFLAG_RW, &sc->sc_reg_addr, 3587ddf1949SAdrian Chadd sc->sc_reg_addr, "debug register address"); 3597ddf1949SAdrian Chadd SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3607ddf1949SAdrian Chadd "reg_val", CTLTYPE_INT | CTLFLAG_RW, sc, 0, 3617ddf1949SAdrian Chadd rtwn_sysctl_reg_readwrite, "I", "debug register read/write"); 3627ddf1949SAdrian Chadd #endif /* RTWN_DEBUG */ 3637ddf1949SAdrian Chadd 3647453645fSAndriy Voskoboinyk sc->sc_ht40 = 0; 3657453645fSAndriy Voskoboinyk SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3667453645fSAndriy Voskoboinyk "ht40", CTLFLAG_RDTUN, &sc->sc_ht40, 3677453645fSAndriy Voskoboinyk sc->sc_ht40, "Enable 40 MHz mode support"); 3687453645fSAndriy Voskoboinyk 369fcb5e8d0SAdrian Chadd sc->sc_ena_tsf64 = 0; 370fcb5e8d0SAdrian Chadd SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 371fcb5e8d0SAdrian Chadd "ena_tsf64", CTLFLAG_RWTUN, &sc->sc_ena_tsf64, 372fcb5e8d0SAdrian Chadd sc->sc_ena_tsf64, "Enable/disable per-packet TSF64 reporting"); 373fcb5e8d0SAdrian Chadd 3747453645fSAndriy Voskoboinyk #ifdef RTWN_DEBUG 3757453645fSAndriy Voskoboinyk SYSCTL_ADD_U32(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3767453645fSAndriy Voskoboinyk "debug", CTLFLAG_RWTUN, &sc->sc_debug, sc->sc_debug, 3777453645fSAndriy Voskoboinyk "Control debugging printfs"); 3787453645fSAndriy Voskoboinyk #endif 3797453645fSAndriy Voskoboinyk 3807453645fSAndriy Voskoboinyk sc->sc_hwcrypto = RTWN_CRYPTO_PAIR; 3817453645fSAndriy Voskoboinyk SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3827453645fSAndriy Voskoboinyk "hwcrypto", CTLFLAG_RDTUN, &sc->sc_hwcrypto, 3837453645fSAndriy Voskoboinyk sc->sc_hwcrypto, "Enable h/w crypto: " 3847453645fSAndriy Voskoboinyk "0 - disable, 1 - pairwise keys, 2 - all keys"); 3857453645fSAndriy Voskoboinyk if (sc->sc_hwcrypto >= RTWN_CRYPTO_MAX) 3867453645fSAndriy Voskoboinyk sc->sc_hwcrypto = RTWN_CRYPTO_FULL; 3877453645fSAndriy Voskoboinyk 3887453645fSAndriy Voskoboinyk sc->sc_ratectl_sysctl = RTWN_RATECTL_NET80211; 3897453645fSAndriy Voskoboinyk SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3907453645fSAndriy Voskoboinyk "ratectl", CTLFLAG_RDTUN, &sc->sc_ratectl_sysctl, 3917453645fSAndriy Voskoboinyk sc->sc_ratectl_sysctl, "Select rate control mechanism: " 3927453645fSAndriy Voskoboinyk "0 - disabled, 1 - via net80211, 2 - via firmware"); 3937453645fSAndriy Voskoboinyk if (sc->sc_ratectl_sysctl >= RTWN_RATECTL_MAX) 3947453645fSAndriy Voskoboinyk sc->sc_ratectl_sysctl = RTWN_RATECTL_FW; 3957453645fSAndriy Voskoboinyk 3967453645fSAndriy Voskoboinyk sc->sc_ratectl = sc->sc_ratectl_sysctl; 3977453645fSAndriy Voskoboinyk SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 3987453645fSAndriy Voskoboinyk "ratectl_selected", CTLFLAG_RD, &sc->sc_ratectl, 3997453645fSAndriy Voskoboinyk sc->sc_ratectl, 4007453645fSAndriy Voskoboinyk "Currently selected rate control mechanism (by the driver)"); 4017453645fSAndriy Voskoboinyk } 4027453645fSAndriy Voskoboinyk 4037453645fSAndriy Voskoboinyk void 4047453645fSAndriy Voskoboinyk rtwn_detach(struct rtwn_softc *sc) 4057453645fSAndriy Voskoboinyk { 4067453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 4077453645fSAndriy Voskoboinyk 4087453645fSAndriy Voskoboinyk if (ic->ic_softc == sc) { 4097453645fSAndriy Voskoboinyk /* Stop command queue. */ 4107453645fSAndriy Voskoboinyk RTWN_CMDQ_LOCK(sc); 4117453645fSAndriy Voskoboinyk sc->sc_detached = 1; 4127453645fSAndriy Voskoboinyk RTWN_CMDQ_UNLOCK(sc); 4137453645fSAndriy Voskoboinyk 4147453645fSAndriy Voskoboinyk ieee80211_draintask(ic, &sc->cmdq_task); 4157453645fSAndriy Voskoboinyk ieee80211_ifdetach(ic); 4167453645fSAndriy Voskoboinyk } 4177453645fSAndriy Voskoboinyk 4187453645fSAndriy Voskoboinyk rtwn_cmdq_destroy(sc); 4197453645fSAndriy Voskoboinyk if (RTWN_NT_LOCK_INITIALIZED(sc)) 4207453645fSAndriy Voskoboinyk RTWN_NT_LOCK_DESTROY(sc); 4217453645fSAndriy Voskoboinyk } 4227453645fSAndriy Voskoboinyk 4237453645fSAndriy Voskoboinyk void 4247453645fSAndriy Voskoboinyk rtwn_suspend(struct rtwn_softc *sc) 4257453645fSAndriy Voskoboinyk { 4267453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 4277453645fSAndriy Voskoboinyk 4287453645fSAndriy Voskoboinyk ieee80211_suspend_all(ic); 4297453645fSAndriy Voskoboinyk } 4307453645fSAndriy Voskoboinyk 4317453645fSAndriy Voskoboinyk void 4327453645fSAndriy Voskoboinyk rtwn_resume(struct rtwn_softc *sc) 4337453645fSAndriy Voskoboinyk { 4347453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 4357453645fSAndriy Voskoboinyk 4367453645fSAndriy Voskoboinyk ieee80211_resume_all(ic); 437bcaed14bSAdrian Chadd } 438bcaed14bSAdrian Chadd 439ec07af2aSAdrian Chadd void 440ec07af2aSAdrian Chadd rtwn_attach_vht_cap_info_mcs(struct rtwn_softc *sc) 441ec07af2aSAdrian Chadd { 442ec07af2aSAdrian Chadd struct ieee80211com *ic = &sc->sc_ic; 443ec07af2aSAdrian Chadd uint32_t rx_mcs = 0, tx_mcs = 0; 444ec07af2aSAdrian Chadd 445ec07af2aSAdrian Chadd for (int i = 0 ; i < 8; i++) { 446ec07af2aSAdrian Chadd if (i < sc->ntxchains) 447ec07af2aSAdrian Chadd tx_mcs |= (IEEE80211_VHT_MCS_SUPPORT_0_9 << (i*2)); 448ec07af2aSAdrian Chadd else 449ec07af2aSAdrian Chadd tx_mcs |= (IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2)); 450ec07af2aSAdrian Chadd 451ec07af2aSAdrian Chadd if (i < sc->nrxchains) 452ec07af2aSAdrian Chadd rx_mcs |= (IEEE80211_VHT_MCS_SUPPORT_0_9 << (i*2)); 453ec07af2aSAdrian Chadd else 454ec07af2aSAdrian Chadd rx_mcs |= (IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2)); 455ec07af2aSAdrian Chadd } 456ec07af2aSAdrian Chadd ic->ic_vht_cap.supp_mcs.rx_mcs_map = rx_mcs; 457ec07af2aSAdrian Chadd ic->ic_vht_cap.supp_mcs.rx_highest = 0; 458ec07af2aSAdrian Chadd ic->ic_vht_cap.supp_mcs.tx_mcs_map = tx_mcs; 459ec07af2aSAdrian Chadd ic->ic_vht_cap.supp_mcs.tx_highest = 0; 460ec07af2aSAdrian Chadd } 461ec07af2aSAdrian Chadd 462bcaed14bSAdrian Chadd static void 4637453645fSAndriy Voskoboinyk rtwn_vap_decrement_counters(struct rtwn_softc *sc, 4647453645fSAndriy Voskoboinyk enum ieee80211_opmode opmode, int id) 465bcaed14bSAdrian Chadd { 466bcaed14bSAdrian Chadd 4677453645fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc); 4687453645fSAndriy Voskoboinyk 4697453645fSAndriy Voskoboinyk if (id != RTWN_VAP_ID_INVALID) { 4707453645fSAndriy Voskoboinyk KASSERT(id == 0 || id == 1, ("wrong vap id %d!\n", id)); 4717453645fSAndriy Voskoboinyk KASSERT(sc->vaps[id] != NULL, ("vap pointer is NULL\n")); 4727453645fSAndriy Voskoboinyk sc->vaps[id] = NULL; 473bcaed14bSAdrian Chadd } 474bcaed14bSAdrian Chadd 4757453645fSAndriy Voskoboinyk switch (opmode) { 4767453645fSAndriy Voskoboinyk case IEEE80211_M_HOSTAP: 4777453645fSAndriy Voskoboinyk sc->ap_vaps--; 4787453645fSAndriy Voskoboinyk /* FALLTHROUGH */ 4797453645fSAndriy Voskoboinyk case IEEE80211_M_IBSS: 4807453645fSAndriy Voskoboinyk sc->bcn_vaps--; 4817453645fSAndriy Voskoboinyk /* FALLTHROUGH */ 4827453645fSAndriy Voskoboinyk case IEEE80211_M_STA: 4837453645fSAndriy Voskoboinyk sc->nvaps--; 4847453645fSAndriy Voskoboinyk break; 4857453645fSAndriy Voskoboinyk case IEEE80211_M_MONITOR: 4867453645fSAndriy Voskoboinyk sc->mon_vaps--; 4877453645fSAndriy Voskoboinyk break; 4887453645fSAndriy Voskoboinyk default: 4897453645fSAndriy Voskoboinyk KASSERT(0, ("wrong opmode %d\n", opmode)); 4907453645fSAndriy Voskoboinyk break; 491bcaed14bSAdrian Chadd } 492bcaed14bSAdrian Chadd 4937453645fSAndriy Voskoboinyk KASSERT(sc->vaps_running >= 0 && sc->monvaps_running >= 0, 4947453645fSAndriy Voskoboinyk ("number of running vaps is negative (vaps %d, monvaps %d)\n", 4957453645fSAndriy Voskoboinyk sc->vaps_running, sc->monvaps_running)); 4967453645fSAndriy Voskoboinyk KASSERT(sc->vaps_running - sc->monvaps_running <= RTWN_PORT_COUNT, 4977453645fSAndriy Voskoboinyk ("number of running vaps is too big (vaps %d, monvaps %d)\n", 4987453645fSAndriy Voskoboinyk sc->vaps_running, sc->monvaps_running)); 499bcaed14bSAdrian Chadd 5007453645fSAndriy Voskoboinyk KASSERT(sc->nvaps >= 0 && sc->nvaps <= RTWN_PORT_COUNT, 5017453645fSAndriy Voskoboinyk ("wrong value %d for nvaps\n", sc->nvaps)); 5027453645fSAndriy Voskoboinyk KASSERT(sc->mon_vaps >= 0, ("mon_vaps is negative (%d)\n", 5037453645fSAndriy Voskoboinyk sc->mon_vaps)); 5047453645fSAndriy Voskoboinyk KASSERT(sc->bcn_vaps >= 0 && ((RTWN_CHIP_HAS_BCNQ1(sc) && 5057453645fSAndriy Voskoboinyk sc->bcn_vaps <= RTWN_PORT_COUNT) || sc->bcn_vaps <= 1), 5067453645fSAndriy Voskoboinyk ("bcn_vaps value %d is wrong\n", sc->bcn_vaps)); 5077453645fSAndriy Voskoboinyk KASSERT(sc->ap_vaps >= 0 && ((RTWN_CHIP_HAS_BCNQ1(sc) && 5087453645fSAndriy Voskoboinyk sc->ap_vaps <= RTWN_PORT_COUNT) || sc->ap_vaps <= 1), 5097453645fSAndriy Voskoboinyk ("ap_vaps value %d is wrong\n", sc->ap_vaps)); 510bcaed14bSAdrian Chadd } 511bcaed14bSAdrian Chadd 512bcaed14bSAdrian Chadd static void 5137453645fSAndriy Voskoboinyk rtwn_set_ic_opmode(struct rtwn_softc *sc) 514bcaed14bSAdrian Chadd { 5157453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 516bcaed14bSAdrian Chadd 5177453645fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc); 518bcaed14bSAdrian Chadd 5197453645fSAndriy Voskoboinyk /* for ieee80211_reset_erp() */ 5207453645fSAndriy Voskoboinyk if (sc->bcn_vaps - sc->ap_vaps > 0) 5217453645fSAndriy Voskoboinyk ic->ic_opmode = IEEE80211_M_IBSS; 5227453645fSAndriy Voskoboinyk else if (sc->ap_vaps > 0) 5237453645fSAndriy Voskoboinyk ic->ic_opmode = IEEE80211_M_HOSTAP; 5247453645fSAndriy Voskoboinyk else if (sc->nvaps > 0) 5257453645fSAndriy Voskoboinyk ic->ic_opmode = IEEE80211_M_STA; 5267453645fSAndriy Voskoboinyk else 5277453645fSAndriy Voskoboinyk ic->ic_opmode = IEEE80211_M_MONITOR; 528bcaed14bSAdrian Chadd } 529bcaed14bSAdrian Chadd 530bcaed14bSAdrian Chadd static struct ieee80211vap * 531bcaed14bSAdrian Chadd rtwn_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, 532bcaed14bSAdrian Chadd enum ieee80211_opmode opmode, int flags, 533bcaed14bSAdrian Chadd const uint8_t bssid[IEEE80211_ADDR_LEN], 534bcaed14bSAdrian Chadd const uint8_t mac[IEEE80211_ADDR_LEN]) 535bcaed14bSAdrian Chadd { 5367453645fSAndriy Voskoboinyk struct rtwn_softc *sc = ic->ic_softc; 5377453645fSAndriy Voskoboinyk struct rtwn_vap *uvp; 538bcaed14bSAdrian Chadd struct ieee80211vap *vap; 5397453645fSAndriy Voskoboinyk int id = RTWN_VAP_ID_INVALID; 540bcaed14bSAdrian Chadd 5417453645fSAndriy Voskoboinyk RTWN_LOCK(sc); 5427453645fSAndriy Voskoboinyk KASSERT(sc->nvaps <= RTWN_PORT_COUNT, 5437453645fSAndriy Voskoboinyk ("nvaps overflow (%d > %d)\n", sc->nvaps, RTWN_PORT_COUNT)); 5447453645fSAndriy Voskoboinyk KASSERT(sc->ap_vaps <= RTWN_PORT_COUNT, 5457453645fSAndriy Voskoboinyk ("ap_vaps overflow (%d > %d)\n", sc->ap_vaps, RTWN_PORT_COUNT)); 5467453645fSAndriy Voskoboinyk KASSERT(sc->bcn_vaps <= RTWN_PORT_COUNT, 5477453645fSAndriy Voskoboinyk ("bcn_vaps overflow (%d > %d)\n", sc->bcn_vaps, RTWN_PORT_COUNT)); 548bcaed14bSAdrian Chadd 5497453645fSAndriy Voskoboinyk if (opmode != IEEE80211_M_MONITOR) { 5507453645fSAndriy Voskoboinyk switch (sc->nvaps) { 5517453645fSAndriy Voskoboinyk case 0: 5527453645fSAndriy Voskoboinyk id = 0; 5537453645fSAndriy Voskoboinyk break; 5547453645fSAndriy Voskoboinyk case 1: 5557453645fSAndriy Voskoboinyk if (sc->vaps[1] == NULL) 5567453645fSAndriy Voskoboinyk id = 1; 5577453645fSAndriy Voskoboinyk else if (sc->vaps[0] == NULL) 5587453645fSAndriy Voskoboinyk id = 0; 5597453645fSAndriy Voskoboinyk KASSERT(id != RTWN_VAP_ID_INVALID, 5607453645fSAndriy Voskoboinyk ("no free ports left\n")); 5617453645fSAndriy Voskoboinyk break; 5627453645fSAndriy Voskoboinyk case 2: 5637453645fSAndriy Voskoboinyk default: 5647453645fSAndriy Voskoboinyk goto fail; 5657453645fSAndriy Voskoboinyk } 5667453645fSAndriy Voskoboinyk 5677453645fSAndriy Voskoboinyk if (opmode == IEEE80211_M_IBSS || 5687453645fSAndriy Voskoboinyk opmode == IEEE80211_M_HOSTAP) { 5697453645fSAndriy Voskoboinyk if ((sc->bcn_vaps == 1 && !RTWN_CHIP_HAS_BCNQ1(sc)) || 5707453645fSAndriy Voskoboinyk sc->bcn_vaps == RTWN_PORT_COUNT) 5717453645fSAndriy Voskoboinyk goto fail; 5727453645fSAndriy Voskoboinyk } 5737453645fSAndriy Voskoboinyk } 5747453645fSAndriy Voskoboinyk 5757453645fSAndriy Voskoboinyk switch (opmode) { 5767453645fSAndriy Voskoboinyk case IEEE80211_M_HOSTAP: 5777453645fSAndriy Voskoboinyk sc->ap_vaps++; 5787453645fSAndriy Voskoboinyk /* FALLTHROUGH */ 5797453645fSAndriy Voskoboinyk case IEEE80211_M_IBSS: 5807453645fSAndriy Voskoboinyk sc->bcn_vaps++; 5817453645fSAndriy Voskoboinyk /* FALLTHROUGH */ 5827453645fSAndriy Voskoboinyk case IEEE80211_M_STA: 5837453645fSAndriy Voskoboinyk sc->nvaps++; 5847453645fSAndriy Voskoboinyk break; 5857453645fSAndriy Voskoboinyk case IEEE80211_M_MONITOR: 5867453645fSAndriy Voskoboinyk sc->mon_vaps++; 5877453645fSAndriy Voskoboinyk break; 5887453645fSAndriy Voskoboinyk default: 5897453645fSAndriy Voskoboinyk KASSERT(0, ("unknown opmode %d\n", opmode)); 5907453645fSAndriy Voskoboinyk goto fail; 5917453645fSAndriy Voskoboinyk } 5927453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 5937453645fSAndriy Voskoboinyk 5947453645fSAndriy Voskoboinyk uvp = malloc(sizeof(struct rtwn_vap), M_80211_VAP, M_WAITOK | M_ZERO); 5957453645fSAndriy Voskoboinyk uvp->id = id; 5967453645fSAndriy Voskoboinyk if (id != RTWN_VAP_ID_INVALID) { 5977453645fSAndriy Voskoboinyk RTWN_LOCK(sc); 5987453645fSAndriy Voskoboinyk sc->vaps[id] = uvp; 5997453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 6007453645fSAndriy Voskoboinyk } 6017453645fSAndriy Voskoboinyk vap = &uvp->vap; 6027453645fSAndriy Voskoboinyk /* enable s/w bmiss handling for sta mode */ 6037453645fSAndriy Voskoboinyk 604bcaed14bSAdrian Chadd if (ieee80211_vap_setup(ic, vap, name, unit, opmode, 605bcaed14bSAdrian Chadd flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) { 606bcaed14bSAdrian Chadd /* out of memory */ 6077453645fSAndriy Voskoboinyk free(uvp, M_80211_VAP); 6087453645fSAndriy Voskoboinyk 6097453645fSAndriy Voskoboinyk RTWN_LOCK(sc); 6107453645fSAndriy Voskoboinyk rtwn_vap_decrement_counters(sc, opmode, id); 6117453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 6127453645fSAndriy Voskoboinyk 613bcaed14bSAdrian Chadd return (NULL); 614bcaed14bSAdrian Chadd } 615bcaed14bSAdrian Chadd 6167453645fSAndriy Voskoboinyk rtwn_beacon_init(sc, &uvp->bcn_desc.txd[0], uvp->id); 6177453645fSAndriy Voskoboinyk rtwn_vap_preattach(sc, vap); 618bcaed14bSAdrian Chadd 6197453645fSAndriy Voskoboinyk /* override state transition machine */ 6207453645fSAndriy Voskoboinyk uvp->newstate = vap->iv_newstate; 6217453645fSAndriy Voskoboinyk if (opmode == IEEE80211_M_MONITOR) 6227453645fSAndriy Voskoboinyk vap->iv_newstate = rtwn_monitor_newstate; 6237453645fSAndriy Voskoboinyk else 6247453645fSAndriy Voskoboinyk vap->iv_newstate = rtwn_newstate; 6257453645fSAndriy Voskoboinyk vap->iv_update_beacon = rtwn_update_beacon; 6267453645fSAndriy Voskoboinyk vap->iv_reset = rtwn_ioctl_reset; 6277453645fSAndriy Voskoboinyk vap->iv_key_alloc = rtwn_key_alloc; 6287453645fSAndriy Voskoboinyk vap->iv_key_set = rtwn_key_set; 6297453645fSAndriy Voskoboinyk vap->iv_key_delete = rtwn_key_delete; 6307453645fSAndriy Voskoboinyk vap->iv_max_aid = sc->macid_limit; 6317453645fSAndriy Voskoboinyk 6327453645fSAndriy Voskoboinyk /* 802.11n parameters */ 6337453645fSAndriy Voskoboinyk vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_16; 6347453645fSAndriy Voskoboinyk vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K; 635c3c2f0ddSAdrian Chadd vap->iv_ampdu_limit = IEEE80211_HTCAP_MAXRXAMPDU_64K; 6367453645fSAndriy Voskoboinyk 637b8ad00b0SAndriy Voskoboinyk TIMEOUT_TASK_INIT(taskqueue_thread, &uvp->tx_beacon_csa, 0, 638b8ad00b0SAndriy Voskoboinyk rtwn_tx_beacon_csa, vap); 6397453645fSAndriy Voskoboinyk if (opmode == IEEE80211_M_IBSS) { 6407453645fSAndriy Voskoboinyk uvp->recv_mgmt = vap->iv_recv_mgmt; 6417453645fSAndriy Voskoboinyk vap->iv_recv_mgmt = rtwn_adhoc_recv_mgmt; 6427453645fSAndriy Voskoboinyk TASK_INIT(&uvp->tsf_sync_adhoc_task, 0, 6437453645fSAndriy Voskoboinyk rtwn_tsf_sync_adhoc_task, vap); 6447453645fSAndriy Voskoboinyk callout_init(&uvp->tsf_sync_adhoc, 0); 6457453645fSAndriy Voskoboinyk } 6467453645fSAndriy Voskoboinyk 6477453645fSAndriy Voskoboinyk /* 6487453645fSAndriy Voskoboinyk * NB: driver can select net80211 RA even when user requests 6497453645fSAndriy Voskoboinyk * another mechanism. 6507453645fSAndriy Voskoboinyk */ 6517453645fSAndriy Voskoboinyk ieee80211_ratectl_init(vap); 6527453645fSAndriy Voskoboinyk 6537453645fSAndriy Voskoboinyk /* complete setup */ 654bcaed14bSAdrian Chadd ieee80211_vap_attach(vap, ieee80211_media_change, 655bcaed14bSAdrian Chadd ieee80211_media_status, mac); 6567453645fSAndriy Voskoboinyk 6577453645fSAndriy Voskoboinyk RTWN_LOCK(sc); 6587453645fSAndriy Voskoboinyk rtwn_set_ic_opmode(sc); 6597453645fSAndriy Voskoboinyk if (sc->sc_flags & RTWN_RUNNING) { 6607453645fSAndriy Voskoboinyk if (uvp->id != RTWN_VAP_ID_INVALID) 6617453645fSAndriy Voskoboinyk rtwn_set_macaddr(sc, vap->iv_myaddr, uvp->id); 6627453645fSAndriy Voskoboinyk 6637453645fSAndriy Voskoboinyk rtwn_rxfilter_update(sc); 6647453645fSAndriy Voskoboinyk } 6657453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 6667453645fSAndriy Voskoboinyk 667bcaed14bSAdrian Chadd return (vap); 6687453645fSAndriy Voskoboinyk 6697453645fSAndriy Voskoboinyk fail: 6707453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 6717453645fSAndriy Voskoboinyk return (NULL); 672bcaed14bSAdrian Chadd } 673bcaed14bSAdrian Chadd 674bcaed14bSAdrian Chadd static void 675bcaed14bSAdrian Chadd rtwn_vap_delete(struct ieee80211vap *vap) 676bcaed14bSAdrian Chadd { 6777453645fSAndriy Voskoboinyk struct ieee80211com *ic = vap->iv_ic; 6787453645fSAndriy Voskoboinyk struct rtwn_softc *sc = ic->ic_softc; 6797453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 680713db49dSBjoern A. Zeeb int i; 681bcaed14bSAdrian Chadd 6827453645fSAndriy Voskoboinyk /* Put vap into INIT state + stop device if needed. */ 6837453645fSAndriy Voskoboinyk ieee80211_stop(vap); 684713db49dSBjoern A. Zeeb for (i = 0; i < NET80211_IV_NSTATE_NUM; i++) 685713db49dSBjoern A. Zeeb ieee80211_draintask(ic, &vap->iv_nstate_task[i]); 6867453645fSAndriy Voskoboinyk ieee80211_draintask(ic, &ic->ic_parent_task); 6877453645fSAndriy Voskoboinyk 6887453645fSAndriy Voskoboinyk RTWN_LOCK(sc); 6897453645fSAndriy Voskoboinyk /* Cancel any unfinished Tx. */ 6907453645fSAndriy Voskoboinyk rtwn_reset_lists(sc, vap); 691d067ef0fSAndriy Voskoboinyk if (uvp->bcn_mbuf != NULL) 692d067ef0fSAndriy Voskoboinyk m_freem(uvp->bcn_mbuf); 6937453645fSAndriy Voskoboinyk rtwn_vap_decrement_counters(sc, vap->iv_opmode, uvp->id); 6947453645fSAndriy Voskoboinyk rtwn_set_ic_opmode(sc); 6957453645fSAndriy Voskoboinyk if (sc->sc_flags & RTWN_RUNNING) 6967453645fSAndriy Voskoboinyk rtwn_rxfilter_update(sc); 6977453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 6987453645fSAndriy Voskoboinyk 6997453645fSAndriy Voskoboinyk if (vap->iv_opmode == IEEE80211_M_IBSS) { 7007453645fSAndriy Voskoboinyk ieee80211_draintask(ic, &uvp->tsf_sync_adhoc_task); 7017453645fSAndriy Voskoboinyk callout_drain(&uvp->tsf_sync_adhoc); 7027453645fSAndriy Voskoboinyk } 7037453645fSAndriy Voskoboinyk 7047453645fSAndriy Voskoboinyk ieee80211_ratectl_deinit(vap); 705bcaed14bSAdrian Chadd ieee80211_vap_detach(vap); 7067453645fSAndriy Voskoboinyk free(uvp, M_80211_VAP); 707bcaed14bSAdrian Chadd } 708bcaed14bSAdrian Chadd 709bcaed14bSAdrian Chadd static int 710bcaed14bSAdrian Chadd rtwn_read_chipid(struct rtwn_softc *sc) 711bcaed14bSAdrian Chadd { 712bcaed14bSAdrian Chadd uint32_t reg; 713bcaed14bSAdrian Chadd 714bcaed14bSAdrian Chadd reg = rtwn_read_4(sc, R92C_SYS_CFG); 7157453645fSAndriy Voskoboinyk if (reg & R92C_SYS_CFG_TRP_VAUX_EN) /* test chip */ 7167453645fSAndriy Voskoboinyk return (EOPNOTSUPP); 717bcaed14bSAdrian Chadd 7187453645fSAndriy Voskoboinyk rtwn_read_chipid_vendor(sc, reg); 7197453645fSAndriy Voskoboinyk 720bcaed14bSAdrian Chadd return (0); 721bcaed14bSAdrian Chadd } 722bcaed14bSAdrian Chadd 7237453645fSAndriy Voskoboinyk static int 7247453645fSAndriy Voskoboinyk rtwn_ioctl_reset(struct ieee80211vap *vap, u_long cmd) 725bcaed14bSAdrian Chadd { 7267453645fSAndriy Voskoboinyk int error; 727bcaed14bSAdrian Chadd 7287453645fSAndriy Voskoboinyk switch (cmd) { 7297453645fSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE 7307453645fSAndriy Voskoboinyk case IEEE80211_IOC_POWERSAVE: 7317453645fSAndriy Voskoboinyk case IEEE80211_IOC_POWERSAVESLEEP: 7327453645fSAndriy Voskoboinyk { 7337453645fSAndriy Voskoboinyk struct rtwn_softc *sc = vap->iv_ic->ic_softc; 7347453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 735bcaed14bSAdrian Chadd 7367453645fSAndriy Voskoboinyk if (vap->iv_opmode == IEEE80211_M_STA && uvp->id == 0) { 7377453645fSAndriy Voskoboinyk RTWN_LOCK(sc); 7387453645fSAndriy Voskoboinyk if (sc->sc_flags & RTWN_RUNNING) 7397453645fSAndriy Voskoboinyk error = rtwn_set_pwrmode(sc, vap, 1); 7407453645fSAndriy Voskoboinyk else 7417453645fSAndriy Voskoboinyk error = 0; 7427453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 7437453645fSAndriy Voskoboinyk if (error != 0) 7447453645fSAndriy Voskoboinyk error = ENETRESET; 7457453645fSAndriy Voskoboinyk } else 7467453645fSAndriy Voskoboinyk error = EOPNOTSUPP; 7477453645fSAndriy Voskoboinyk break; 7487453645fSAndriy Voskoboinyk } 7497453645fSAndriy Voskoboinyk #endif 7507453645fSAndriy Voskoboinyk case IEEE80211_IOC_SHORTGI: 7517453645fSAndriy Voskoboinyk case IEEE80211_IOC_RTSTHRESHOLD: 7527453645fSAndriy Voskoboinyk case IEEE80211_IOC_PROTMODE: 7537453645fSAndriy Voskoboinyk case IEEE80211_IOC_HTPROTMODE: 7543111723cSAndriy Voskoboinyk case IEEE80211_IOC_LDPC: 7557453645fSAndriy Voskoboinyk error = 0; 7567453645fSAndriy Voskoboinyk break; 757b71805e9SAdrian Chadd case IEEE80211_IOC_TXPOWER: 758b71805e9SAdrian Chadd { 759b71805e9SAdrian Chadd struct rtwn_softc *sc = vap->iv_ic->ic_softc; 760b71805e9SAdrian Chadd RTWN_LOCK(sc); 761b71805e9SAdrian Chadd error = rtwn_set_tx_power(sc, vap); 762b71805e9SAdrian Chadd RTWN_UNLOCK(sc); 763b71805e9SAdrian Chadd } 764b71805e9SAdrian Chadd break; 7657453645fSAndriy Voskoboinyk default: 7667453645fSAndriy Voskoboinyk error = ENETRESET; 7677453645fSAndriy Voskoboinyk break; 768bcaed14bSAdrian Chadd } 769bcaed14bSAdrian Chadd 7707453645fSAndriy Voskoboinyk return (error); 7717453645fSAndriy Voskoboinyk } 7727453645fSAndriy Voskoboinyk 7737453645fSAndriy Voskoboinyk static void 7747453645fSAndriy Voskoboinyk rtwn_set_media_status(struct rtwn_softc *sc, union sec_param *data) 775354df9d4SAndriy Voskoboinyk { 7767453645fSAndriy Voskoboinyk sc->sc_set_media_status(sc, data->macid); 7777453645fSAndriy Voskoboinyk } 7787453645fSAndriy Voskoboinyk 77959ed13aaSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE 7807453645fSAndriy Voskoboinyk static int 7817453645fSAndriy Voskoboinyk rtwn_tx_fwpkt_check(struct rtwn_softc *sc, struct ieee80211vap *vap) 7827453645fSAndriy Voskoboinyk { 7837453645fSAndriy Voskoboinyk int ntries, error; 7847453645fSAndriy Voskoboinyk 7857453645fSAndriy Voskoboinyk for (ntries = 0; ntries < 5; ntries++) { 7867453645fSAndriy Voskoboinyk error = rtwn_push_nulldata(sc, vap); 7877453645fSAndriy Voskoboinyk if (error == 0) 7887453645fSAndriy Voskoboinyk break; 7897453645fSAndriy Voskoboinyk } 7907453645fSAndriy Voskoboinyk if (ntries == 5) { 7917453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, 7927453645fSAndriy Voskoboinyk "%s: cannot push f/w frames into chip, error %d!\n", 7937453645fSAndriy Voskoboinyk __func__, error); 7947453645fSAndriy Voskoboinyk return (error); 7957453645fSAndriy Voskoboinyk } 7967453645fSAndriy Voskoboinyk 7977453645fSAndriy Voskoboinyk return (0); 7987453645fSAndriy Voskoboinyk } 7997453645fSAndriy Voskoboinyk 8007453645fSAndriy Voskoboinyk static int 8017453645fSAndriy Voskoboinyk rtwn_construct_nulldata(struct rtwn_softc *sc, struct ieee80211vap *vap, 8027453645fSAndriy Voskoboinyk uint8_t *ptr, int qos) 8037453645fSAndriy Voskoboinyk { 8047453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 8057453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 8067453645fSAndriy Voskoboinyk struct rtwn_tx_desc_common *txd; 8077453645fSAndriy Voskoboinyk struct ieee80211_frame *wh; 8087453645fSAndriy Voskoboinyk int pktlen; 8097453645fSAndriy Voskoboinyk 8107453645fSAndriy Voskoboinyk /* XXX obtain from net80211 */ 8117453645fSAndriy Voskoboinyk wh = (struct ieee80211_frame *)(ptr + sc->txdesc_len); 8127453645fSAndriy Voskoboinyk wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; 8137453645fSAndriy Voskoboinyk wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; 8147453645fSAndriy Voskoboinyk IEEE80211_ADDR_COPY(wh->i_addr1, vap->iv_bss->ni_bssid); 8157453645fSAndriy Voskoboinyk IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); 8167453645fSAndriy Voskoboinyk IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_bss->ni_macaddr); 8177453645fSAndriy Voskoboinyk 8187453645fSAndriy Voskoboinyk txd = (struct rtwn_tx_desc_common *)ptr; 8197453645fSAndriy Voskoboinyk txd->offset = sc->txdesc_len; 8207453645fSAndriy Voskoboinyk pktlen = sc->txdesc_len; 8217453645fSAndriy Voskoboinyk if (qos) { 8227453645fSAndriy Voskoboinyk struct ieee80211_qosframe *qwh; 8237453645fSAndriy Voskoboinyk const int tid = WME_AC_TO_TID(WME_AC_BE); 8247453645fSAndriy Voskoboinyk 8257453645fSAndriy Voskoboinyk qwh = (struct ieee80211_qosframe *)wh; 8267453645fSAndriy Voskoboinyk qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS_NULL; 8277453645fSAndriy Voskoboinyk qwh->i_qos[0] = tid & IEEE80211_QOS_TID; 8287453645fSAndriy Voskoboinyk 8297453645fSAndriy Voskoboinyk txd->pktlen = htole16(sizeof(struct ieee80211_qosframe)); 8307453645fSAndriy Voskoboinyk pktlen += sizeof(struct ieee80211_qosframe); 8317453645fSAndriy Voskoboinyk } else { 8327453645fSAndriy Voskoboinyk wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_NODATA; 8337453645fSAndriy Voskoboinyk 8347453645fSAndriy Voskoboinyk txd->pktlen = htole16(sizeof(struct ieee80211_frame)); 8357453645fSAndriy Voskoboinyk pktlen += sizeof(struct ieee80211_frame); 8367453645fSAndriy Voskoboinyk } 8377453645fSAndriy Voskoboinyk 8387453645fSAndriy Voskoboinyk rtwn_fill_tx_desc_null(sc, ptr, 8397453645fSAndriy Voskoboinyk ic->ic_curmode == IEEE80211_MODE_11B, qos, uvp->id); 8407453645fSAndriy Voskoboinyk 8417453645fSAndriy Voskoboinyk return (pktlen); 8427453645fSAndriy Voskoboinyk } 8437453645fSAndriy Voskoboinyk 8447453645fSAndriy Voskoboinyk static int 8457453645fSAndriy Voskoboinyk rtwn_push_nulldata(struct rtwn_softc *sc, struct ieee80211vap *vap) 8467453645fSAndriy Voskoboinyk { 8477453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 8487453645fSAndriy Voskoboinyk struct ieee80211com *ic = vap->iv_ic; 8497453645fSAndriy Voskoboinyk struct ieee80211_channel *c = ic->ic_curchan; 8507453645fSAndriy Voskoboinyk struct mbuf *m; 8517453645fSAndriy Voskoboinyk uint8_t *ptr; 8527453645fSAndriy Voskoboinyk int required_size, bcn_size, null_size, null_data, error; 8537453645fSAndriy Voskoboinyk 8547453645fSAndriy Voskoboinyk if (!(sc->sc_flags & RTWN_FW_LOADED)) 8557453645fSAndriy Voskoboinyk return (0); /* requires firmware */ 8567453645fSAndriy Voskoboinyk 8577453645fSAndriy Voskoboinyk KASSERT(sc->page_size > 0, ("page size was not set!\n")); 8587453645fSAndriy Voskoboinyk 8597453645fSAndriy Voskoboinyk /* Leave some space for beacon (multi-vap) */ 8607453645fSAndriy Voskoboinyk bcn_size = roundup(RTWN_BCN_MAX_SIZE, sc->page_size); 8617453645fSAndriy Voskoboinyk /* 1 page for Null Data + 1 page for Qos Null Data frames. */ 8627453645fSAndriy Voskoboinyk required_size = bcn_size + sc->page_size * 2; 8637453645fSAndriy Voskoboinyk 8647453645fSAndriy Voskoboinyk m = m_get2(required_size, M_NOWAIT, MT_DATA, M_PKTHDR); 8657453645fSAndriy Voskoboinyk if (m == NULL) 8667453645fSAndriy Voskoboinyk return (ENOMEM); 8677453645fSAndriy Voskoboinyk 8687453645fSAndriy Voskoboinyk /* Setup beacon descriptor. */ 8697453645fSAndriy Voskoboinyk rtwn_beacon_set_rate(sc, &uvp->bcn_desc.txd[0], 8707453645fSAndriy Voskoboinyk IEEE80211_IS_CHAN_5GHZ(c)); 8717453645fSAndriy Voskoboinyk 8727453645fSAndriy Voskoboinyk ptr = mtod(m, uint8_t *); 8737453645fSAndriy Voskoboinyk memset(ptr, 0, required_size - sc->txdesc_len); 8747453645fSAndriy Voskoboinyk 8757453645fSAndriy Voskoboinyk /* Construct Null Data frame. */ 8767453645fSAndriy Voskoboinyk ptr += bcn_size - sc->txdesc_len; 8777453645fSAndriy Voskoboinyk null_size = rtwn_construct_nulldata(sc, vap, ptr, 0); 8787453645fSAndriy Voskoboinyk KASSERT(null_size < sc->page_size, 8797453645fSAndriy Voskoboinyk ("recalculate size for Null Data frame\n")); 8807453645fSAndriy Voskoboinyk 8817453645fSAndriy Voskoboinyk /* Construct Qos Null Data frame. */ 8827453645fSAndriy Voskoboinyk ptr += roundup(null_size, sc->page_size); 8837453645fSAndriy Voskoboinyk null_size = rtwn_construct_nulldata(sc, vap, ptr, 1); 8847453645fSAndriy Voskoboinyk KASSERT(null_size < sc->page_size, 8857453645fSAndriy Voskoboinyk ("recalculate size for Qos Null Data frame\n")); 8867453645fSAndriy Voskoboinyk 8877453645fSAndriy Voskoboinyk /* Do not try to detect a beacon here. */ 8887453645fSAndriy Voskoboinyk rtwn_setbits_1_shift(sc, R92C_CR, 0, R92C_CR_ENSWBCN, 1); 8897453645fSAndriy Voskoboinyk rtwn_setbits_1_shift(sc, R92C_FWHW_TXQ_CTRL, 8907453645fSAndriy Voskoboinyk R92C_FWHW_TXQ_CTRL_REAL_BEACON, 0, 2); 8917453645fSAndriy Voskoboinyk 892d067ef0fSAndriy Voskoboinyk if (uvp->bcn_mbuf != NULL) { 893d067ef0fSAndriy Voskoboinyk rtwn_beacon_unload(sc, uvp->id); 8947453645fSAndriy Voskoboinyk m_freem(uvp->bcn_mbuf); 895d067ef0fSAndriy Voskoboinyk } 8967453645fSAndriy Voskoboinyk 8977453645fSAndriy Voskoboinyk m->m_pkthdr.len = m->m_len = required_size - sc->txdesc_len; 8987453645fSAndriy Voskoboinyk uvp->bcn_mbuf = m; 8997453645fSAndriy Voskoboinyk 9007453645fSAndriy Voskoboinyk error = rtwn_tx_beacon_check(sc, uvp); 9017453645fSAndriy Voskoboinyk if (error != 0) { 9027453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_BEACON, 9037453645fSAndriy Voskoboinyk "%s: frame was not recognized!\n", __func__); 9047453645fSAndriy Voskoboinyk goto fail; 9057453645fSAndriy Voskoboinyk } 9067453645fSAndriy Voskoboinyk 9077453645fSAndriy Voskoboinyk /* Setup addresses in firmware. */ 9087453645fSAndriy Voskoboinyk null_data = howmany(bcn_size, sc->page_size); 9097453645fSAndriy Voskoboinyk error = rtwn_set_rsvd_page(sc, 0, null_data, null_data + 1); 9107453645fSAndriy Voskoboinyk if (error != 0) { 9117453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, 9127453645fSAndriy Voskoboinyk "%s: CMD_RSVD_PAGE was not sent, error %d\n", 9137453645fSAndriy Voskoboinyk __func__, error); 9147453645fSAndriy Voskoboinyk goto fail; 9157453645fSAndriy Voskoboinyk } 9167453645fSAndriy Voskoboinyk 9177453645fSAndriy Voskoboinyk fail: 9187453645fSAndriy Voskoboinyk /* Re-enable beacon detection. */ 9197453645fSAndriy Voskoboinyk rtwn_setbits_1_shift(sc, R92C_FWHW_TXQ_CTRL, 9207453645fSAndriy Voskoboinyk 0, R92C_FWHW_TXQ_CTRL_REAL_BEACON, 2); 9217453645fSAndriy Voskoboinyk rtwn_setbits_1_shift(sc, R92C_CR, R92C_CR_ENSWBCN, 0, 1); 9227453645fSAndriy Voskoboinyk 9237453645fSAndriy Voskoboinyk /* Restore beacon (if present). */ 9247453645fSAndriy Voskoboinyk if (sc->bcn_vaps > 0 && sc->vaps[!uvp->id] != NULL) { 9257453645fSAndriy Voskoboinyk struct rtwn_vap *uvp2 = sc->vaps[!uvp->id]; 9267453645fSAndriy Voskoboinyk 9277453645fSAndriy Voskoboinyk if (uvp2->curr_mode != R92C_MSR_NOLINK) 9287453645fSAndriy Voskoboinyk error = rtwn_tx_beacon_check(sc, uvp2); 9297453645fSAndriy Voskoboinyk } 9307453645fSAndriy Voskoboinyk 9317453645fSAndriy Voskoboinyk return (error); 9327453645fSAndriy Voskoboinyk } 9337453645fSAndriy Voskoboinyk 9347453645fSAndriy Voskoboinyk static void 9357453645fSAndriy Voskoboinyk rtwn_pwrmode_init(void *arg) 9367453645fSAndriy Voskoboinyk { 9377453645fSAndriy Voskoboinyk struct rtwn_softc *sc = arg; 9387453645fSAndriy Voskoboinyk 9397453645fSAndriy Voskoboinyk rtwn_cmd_sleepable(sc, NULL, 0, rtwn_set_pwrmode_cb); 9407453645fSAndriy Voskoboinyk } 9417453645fSAndriy Voskoboinyk 9427453645fSAndriy Voskoboinyk static void 9437453645fSAndriy Voskoboinyk rtwn_set_pwrmode_cb(struct rtwn_softc *sc, union sec_param *data) 9447453645fSAndriy Voskoboinyk { 9457453645fSAndriy Voskoboinyk struct ieee80211vap *vap = &sc->vaps[0]->vap; 9467453645fSAndriy Voskoboinyk 9477453645fSAndriy Voskoboinyk if (vap != NULL) 9487453645fSAndriy Voskoboinyk rtwn_set_pwrmode(sc, vap, 1); 9497453645fSAndriy Voskoboinyk } 9507453645fSAndriy Voskoboinyk #endif 9517453645fSAndriy Voskoboinyk 9527453645fSAndriy Voskoboinyk static void 9537453645fSAndriy Voskoboinyk rtwn_tsf_sync_adhoc(void *arg) 9547453645fSAndriy Voskoboinyk { 9557453645fSAndriy Voskoboinyk struct ieee80211vap *vap = arg; 9567453645fSAndriy Voskoboinyk struct ieee80211com *ic = vap->iv_ic; 9577453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 9587453645fSAndriy Voskoboinyk 9597453645fSAndriy Voskoboinyk if (uvp->curr_mode != R92C_MSR_NOLINK) { 9607453645fSAndriy Voskoboinyk /* Do it in process context. */ 9617453645fSAndriy Voskoboinyk ieee80211_runtask(ic, &uvp->tsf_sync_adhoc_task); 962354df9d4SAndriy Voskoboinyk } 963354df9d4SAndriy Voskoboinyk } 964354df9d4SAndriy Voskoboinyk 965bcaed14bSAdrian Chadd /* 9667453645fSAndriy Voskoboinyk * Workaround for TSF synchronization: 9677453645fSAndriy Voskoboinyk * when BSSID filter in IBSS mode is not set 9687453645fSAndriy Voskoboinyk * (and TSF synchronization is enabled), then any beacon may update it. 9697453645fSAndriy Voskoboinyk * This routine synchronizes it when BSSID matching is enabled (IBSS merge 9707453645fSAndriy Voskoboinyk * is not possible during this period). 9717453645fSAndriy Voskoboinyk * 9727453645fSAndriy Voskoboinyk * NOTE: there is no race with rtwn_newstate(), since it uses the same 9737453645fSAndriy Voskoboinyk * taskqueue. 974bcaed14bSAdrian Chadd */ 9757453645fSAndriy Voskoboinyk static void 9767453645fSAndriy Voskoboinyk rtwn_tsf_sync_adhoc_task(void *arg, int pending) 977bcaed14bSAdrian Chadd { 9787453645fSAndriy Voskoboinyk struct ieee80211vap *vap = arg; 9797453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 9807453645fSAndriy Voskoboinyk struct rtwn_softc *sc = vap->iv_ic->ic_softc; 9817453645fSAndriy Voskoboinyk struct ieee80211_node *ni; 982bcaed14bSAdrian Chadd 9837453645fSAndriy Voskoboinyk RTWN_LOCK(sc); 9847453645fSAndriy Voskoboinyk ni = ieee80211_ref_node(vap->iv_bss); 985bcaed14bSAdrian Chadd 9867453645fSAndriy Voskoboinyk /* Accept beacons with the same BSSID. */ 9877453645fSAndriy Voskoboinyk rtwn_set_rx_bssid_all(sc, 0); 988bcaed14bSAdrian Chadd 989c15d8692SAndriy Voskoboinyk /* Deny RCR updates. */ 990c15d8692SAndriy Voskoboinyk sc->sc_flags |= RTWN_RCR_LOCKED; 991c15d8692SAndriy Voskoboinyk 9927453645fSAndriy Voskoboinyk /* Enable synchronization. */ 9937453645fSAndriy Voskoboinyk rtwn_setbits_1(sc, R92C_BCN_CTRL(uvp->id), 9947453645fSAndriy Voskoboinyk R92C_BCN_CTRL_DIS_TSF_UDT0, 0); 995bcaed14bSAdrian Chadd 9967453645fSAndriy Voskoboinyk /* Synchronize. */ 9977453645fSAndriy Voskoboinyk rtwn_delay(sc, ni->ni_intval * 5 * 1000); 998bcaed14bSAdrian Chadd 9997453645fSAndriy Voskoboinyk /* Disable synchronization. */ 10007453645fSAndriy Voskoboinyk rtwn_setbits_1(sc, R92C_BCN_CTRL(uvp->id), 10017453645fSAndriy Voskoboinyk 0, R92C_BCN_CTRL_DIS_TSF_UDT0); 10027453645fSAndriy Voskoboinyk 10037453645fSAndriy Voskoboinyk /* Accept all beacons. */ 1004c15d8692SAndriy Voskoboinyk sc->sc_flags &= ~RTWN_RCR_LOCKED; 10057453645fSAndriy Voskoboinyk rtwn_set_rx_bssid_all(sc, 1); 10067453645fSAndriy Voskoboinyk 10077453645fSAndriy Voskoboinyk /* Schedule next TSF synchronization. */ 10087453645fSAndriy Voskoboinyk callout_reset(&uvp->tsf_sync_adhoc, 60*hz, rtwn_tsf_sync_adhoc, vap); 10097453645fSAndriy Voskoboinyk 10107453645fSAndriy Voskoboinyk ieee80211_free_node(ni); 10117453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 1012bcaed14bSAdrian Chadd } 1013bcaed14bSAdrian Chadd 1014bcaed14bSAdrian Chadd static void 10157453645fSAndriy Voskoboinyk rtwn_tsf_sync_enable(struct rtwn_softc *sc, struct ieee80211vap *vap) 1016bcaed14bSAdrian Chadd { 1017bcaed14bSAdrian Chadd struct ieee80211com *ic = &sc->sc_ic; 10187453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 1019bcaed14bSAdrian Chadd 10207453645fSAndriy Voskoboinyk /* Reset TSF. */ 10217453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_DUAL_TSF_RST, R92C_DUAL_TSF_RESET(uvp->id)); 10227453645fSAndriy Voskoboinyk 10237453645fSAndriy Voskoboinyk switch (vap->iv_opmode) { 10247453645fSAndriy Voskoboinyk case IEEE80211_M_STA: 1025bcaed14bSAdrian Chadd /* Enable TSF synchronization. */ 10267453645fSAndriy Voskoboinyk rtwn_setbits_1(sc, R92C_BCN_CTRL(uvp->id), 10277453645fSAndriy Voskoboinyk R92C_BCN_CTRL_DIS_TSF_UDT0, 0); 10289efd2154SAdrian Chadd /* Enable TSF beacon handling, needed for RA */ 10299efd2154SAdrian Chadd rtwn_sta_beacon_enable(sc, uvp->id, true); 10307453645fSAndriy Voskoboinyk break; 10317453645fSAndriy Voskoboinyk case IEEE80211_M_IBSS: 10327453645fSAndriy Voskoboinyk ieee80211_runtask(ic, &uvp->tsf_sync_adhoc_task); 10337453645fSAndriy Voskoboinyk /* FALLTHROUGH */ 10347453645fSAndriy Voskoboinyk case IEEE80211_M_HOSTAP: 10357453645fSAndriy Voskoboinyk /* Enable beaconing. */ 10367453645fSAndriy Voskoboinyk rtwn_beacon_enable(sc, uvp->id, 1); 10377453645fSAndriy Voskoboinyk break; 10387453645fSAndriy Voskoboinyk default: 10397453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "undefined opmode %d\n", 10407453645fSAndriy Voskoboinyk vap->iv_opmode); 10417453645fSAndriy Voskoboinyk return; 10427453645fSAndriy Voskoboinyk } 1043bcaed14bSAdrian Chadd } 1044bcaed14bSAdrian Chadd 1045bcaed14bSAdrian Chadd static void 10467453645fSAndriy Voskoboinyk rtwn_set_ack_preamble(struct rtwn_softc *sc) 1047bcaed14bSAdrian Chadd { 10487453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 10497453645fSAndriy Voskoboinyk uint32_t reg; 1050bcaed14bSAdrian Chadd 10517453645fSAndriy Voskoboinyk reg = rtwn_read_4(sc, R92C_WMAC_TRXPTCL_CTL); 10527453645fSAndriy Voskoboinyk if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) 10537453645fSAndriy Voskoboinyk reg |= R92C_WMAC_TRXPTCL_SHPRE; 1054bcaed14bSAdrian Chadd else 10557453645fSAndriy Voskoboinyk reg &= ~R92C_WMAC_TRXPTCL_SHPRE; 10567453645fSAndriy Voskoboinyk rtwn_write_4(sc, R92C_WMAC_TRXPTCL_CTL, reg); 1057bcaed14bSAdrian Chadd } 1058bcaed14bSAdrian Chadd 1059bcaed14bSAdrian Chadd static void 10607453645fSAndriy Voskoboinyk rtwn_set_mode(struct rtwn_softc *sc, uint8_t mode, int id) 1061bcaed14bSAdrian Chadd { 1062bcaed14bSAdrian Chadd 10637453645fSAndriy Voskoboinyk rtwn_setbits_1(sc, R92C_MSR, R92C_MSR_MASK << id * 2, mode << id * 2); 10647453645fSAndriy Voskoboinyk if (sc->vaps[id] != NULL) 10657453645fSAndriy Voskoboinyk sc->vaps[id]->curr_mode = mode; 1066bcaed14bSAdrian Chadd } 1067bcaed14bSAdrian Chadd 10687453645fSAndriy Voskoboinyk static int 10697453645fSAndriy Voskoboinyk rtwn_monitor_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, 10707453645fSAndriy Voskoboinyk int arg) 10717453645fSAndriy Voskoboinyk { 10727453645fSAndriy Voskoboinyk struct ieee80211com *ic = vap->iv_ic; 10737453645fSAndriy Voskoboinyk struct rtwn_softc *sc = ic->ic_softc; 10747453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 1075bcaed14bSAdrian Chadd 10767453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_STATE, "%s -> %s\n", 10777453645fSAndriy Voskoboinyk ieee80211_state_name[vap->iv_state], 10787453645fSAndriy Voskoboinyk ieee80211_state_name[nstate]); 10797453645fSAndriy Voskoboinyk 10807453645fSAndriy Voskoboinyk if (vap->iv_state != nstate) { 10817453645fSAndriy Voskoboinyk IEEE80211_UNLOCK(ic); 10827453645fSAndriy Voskoboinyk RTWN_LOCK(sc); 10837453645fSAndriy Voskoboinyk 10847453645fSAndriy Voskoboinyk switch (nstate) { 10857453645fSAndriy Voskoboinyk case IEEE80211_S_INIT: 10867453645fSAndriy Voskoboinyk sc->vaps_running--; 10877453645fSAndriy Voskoboinyk sc->monvaps_running--; 10887453645fSAndriy Voskoboinyk 10897453645fSAndriy Voskoboinyk if (sc->vaps_running == 0) { 10907453645fSAndriy Voskoboinyk /* Turn link LED off. */ 10917453645fSAndriy Voskoboinyk rtwn_set_led(sc, RTWN_LED_LINK, 0); 10927453645fSAndriy Voskoboinyk } 10937453645fSAndriy Voskoboinyk break; 10947453645fSAndriy Voskoboinyk case IEEE80211_S_RUN: 10957453645fSAndriy Voskoboinyk sc->vaps_running++; 10967453645fSAndriy Voskoboinyk sc->monvaps_running++; 10977453645fSAndriy Voskoboinyk 10987453645fSAndriy Voskoboinyk if (sc->vaps_running == 1) { 10997453645fSAndriy Voskoboinyk /* Turn link LED on. */ 11007453645fSAndriy Voskoboinyk rtwn_set_led(sc, RTWN_LED_LINK, 1); 11017453645fSAndriy Voskoboinyk } 11027453645fSAndriy Voskoboinyk break; 11037453645fSAndriy Voskoboinyk default: 11047453645fSAndriy Voskoboinyk /* NOTREACHED */ 11057453645fSAndriy Voskoboinyk break; 11067453645fSAndriy Voskoboinyk } 11077453645fSAndriy Voskoboinyk 11087453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 11097453645fSAndriy Voskoboinyk IEEE80211_LOCK(ic); 11107453645fSAndriy Voskoboinyk } 11117453645fSAndriy Voskoboinyk 11127453645fSAndriy Voskoboinyk return (uvp->newstate(vap, nstate, arg)); 1113bcaed14bSAdrian Chadd } 1114bcaed14bSAdrian Chadd 1115bcaed14bSAdrian Chadd static int 1116bcaed14bSAdrian Chadd rtwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 1117bcaed14bSAdrian Chadd { 11187453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 1119bcaed14bSAdrian Chadd struct ieee80211com *ic = vap->iv_ic; 1120bcaed14bSAdrian Chadd struct rtwn_softc *sc = ic->ic_softc; 11217453645fSAndriy Voskoboinyk enum ieee80211_state ostate; 11227453645fSAndriy Voskoboinyk int error, early_newstate; 11237453645fSAndriy Voskoboinyk 11247453645fSAndriy Voskoboinyk ostate = vap->iv_state; 11257453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_STATE, "%s -> %s\n", 11267453645fSAndriy Voskoboinyk ieee80211_state_name[ostate], ieee80211_state_name[nstate]); 11277453645fSAndriy Voskoboinyk 11287453645fSAndriy Voskoboinyk if (vap->iv_bss->ni_chan == IEEE80211_CHAN_ANYC && 11297453645fSAndriy Voskoboinyk ostate == IEEE80211_S_INIT && nstate == IEEE80211_S_RUN) { 11307453645fSAndriy Voskoboinyk /* need to call iv_newstate() firstly */ 11317453645fSAndriy Voskoboinyk error = uvp->newstate(vap, nstate, arg); 11327453645fSAndriy Voskoboinyk if (error != 0) 11337453645fSAndriy Voskoboinyk return (error); 11347453645fSAndriy Voskoboinyk 11357453645fSAndriy Voskoboinyk early_newstate = 1; 11367453645fSAndriy Voskoboinyk } else 11377453645fSAndriy Voskoboinyk early_newstate = 0; 1138bcaed14bSAdrian Chadd 1139b8ad00b0SAndriy Voskoboinyk if (ostate == IEEE80211_S_CSA) { 1140b8ad00b0SAndriy Voskoboinyk taskqueue_cancel_timeout(taskqueue_thread, 1141b8ad00b0SAndriy Voskoboinyk &uvp->tx_beacon_csa, NULL); 1142b8ad00b0SAndriy Voskoboinyk 1143b8ad00b0SAndriy Voskoboinyk /* 1144b8ad00b0SAndriy Voskoboinyk * In multi-vap case second counter may not be cleared 1145b8ad00b0SAndriy Voskoboinyk * properly. 1146b8ad00b0SAndriy Voskoboinyk */ 1147b8ad00b0SAndriy Voskoboinyk vap->iv_csa_count = 0; 1148b8ad00b0SAndriy Voskoboinyk } 1149bcaed14bSAdrian Chadd IEEE80211_UNLOCK(ic); 1150bcaed14bSAdrian Chadd RTWN_LOCK(sc); 1151b8ad00b0SAndriy Voskoboinyk 1152b8ad00b0SAndriy Voskoboinyk if (ostate == IEEE80211_S_CSA) { 1153b8ad00b0SAndriy Voskoboinyk /* Unblock all queues (multi-vap case). */ 1154b8ad00b0SAndriy Voskoboinyk rtwn_write_1(sc, R92C_TXPAUSE, 0); 1155b8ad00b0SAndriy Voskoboinyk } 1156b8ad00b0SAndriy Voskoboinyk 1157b8ad00b0SAndriy Voskoboinyk if ((ostate == IEEE80211_S_RUN && nstate != IEEE80211_S_CSA) || 1158b8ad00b0SAndriy Voskoboinyk ostate == IEEE80211_S_CSA) { 11597453645fSAndriy Voskoboinyk sc->vaps_running--; 1160bcaed14bSAdrian Chadd 1161bcaed14bSAdrian Chadd /* Set media status to 'No Link'. */ 11627453645fSAndriy Voskoboinyk rtwn_set_mode(sc, R92C_MSR_NOLINK, uvp->id); 11637453645fSAndriy Voskoboinyk 11647453645fSAndriy Voskoboinyk if (vap->iv_opmode == IEEE80211_M_IBSS) { 11657453645fSAndriy Voskoboinyk /* Stop periodical TSF synchronization. */ 11667453645fSAndriy Voskoboinyk callout_stop(&uvp->tsf_sync_adhoc); 11677453645fSAndriy Voskoboinyk } 11687453645fSAndriy Voskoboinyk 11697453645fSAndriy Voskoboinyk /* Disable TSF synchronization / beaconing. */ 11707453645fSAndriy Voskoboinyk rtwn_beacon_enable(sc, uvp->id, 0); 11719efd2154SAdrian Chadd rtwn_sta_beacon_enable(sc, uvp->id, false); 11727453645fSAndriy Voskoboinyk rtwn_setbits_1(sc, R92C_BCN_CTRL(uvp->id), 11737453645fSAndriy Voskoboinyk 0, R92C_BCN_CTRL_DIS_TSF_UDT0); 11747453645fSAndriy Voskoboinyk 11757453645fSAndriy Voskoboinyk /* NB: monitor mode vaps are using port 0. */ 11767453645fSAndriy Voskoboinyk if (uvp->id != 0 || sc->monvaps_running == 0) { 11777453645fSAndriy Voskoboinyk /* Reset TSF. */ 11787453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_DUAL_TSF_RST, 11797453645fSAndriy Voskoboinyk R92C_DUAL_TSF_RESET(uvp->id)); 11807453645fSAndriy Voskoboinyk } 11817453645fSAndriy Voskoboinyk 11827453645fSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE 11837453645fSAndriy Voskoboinyk if ((ic->ic_caps & IEEE80211_C_PMGT) != 0 && uvp->id == 0) { 11847453645fSAndriy Voskoboinyk /* Disable power management. */ 11857453645fSAndriy Voskoboinyk callout_stop(&sc->sc_pwrmode_init); 11867453645fSAndriy Voskoboinyk rtwn_set_pwrmode(sc, vap, 0); 11877453645fSAndriy Voskoboinyk } 11887453645fSAndriy Voskoboinyk #endif 11897453645fSAndriy Voskoboinyk if (sc->vaps_running - sc->monvaps_running > 0) { 11907453645fSAndriy Voskoboinyk /* Recalculate basic rates bitmap. */ 11917453645fSAndriy Voskoboinyk rtwn_calc_basicrates(sc); 11927453645fSAndriy Voskoboinyk } 11937453645fSAndriy Voskoboinyk 11947453645fSAndriy Voskoboinyk if (sc->vaps_running == sc->monvaps_running) { 11957453645fSAndriy Voskoboinyk /* Stop calibration. */ 11967453645fSAndriy Voskoboinyk callout_stop(&sc->sc_calib_to); 1197bcaed14bSAdrian Chadd 1198bcaed14bSAdrian Chadd /* Stop Rx of data frames. */ 1199bcaed14bSAdrian Chadd rtwn_write_2(sc, R92C_RXFLTMAP2, 0); 1200bcaed14bSAdrian Chadd 1201bcaed14bSAdrian Chadd /* Reset EDCA parameters. */ 1202bcaed14bSAdrian Chadd rtwn_write_4(sc, R92C_EDCA_VO_PARAM, 0x002f3217); 1203bcaed14bSAdrian Chadd rtwn_write_4(sc, R92C_EDCA_VI_PARAM, 0x005e4317); 1204bcaed14bSAdrian Chadd rtwn_write_4(sc, R92C_EDCA_BE_PARAM, 0x00105320); 1205bcaed14bSAdrian Chadd rtwn_write_4(sc, R92C_EDCA_BK_PARAM, 0x0000a444); 12067453645fSAndriy Voskoboinyk 12077453645fSAndriy Voskoboinyk if (sc->vaps_running == 0) { 1208bcaed14bSAdrian Chadd /* Turn link LED off. */ 1209bcaed14bSAdrian Chadd rtwn_set_led(sc, RTWN_LED_LINK, 0); 12107453645fSAndriy Voskoboinyk } 12117453645fSAndriy Voskoboinyk } 12127453645fSAndriy Voskoboinyk } 1213bcaed14bSAdrian Chadd 12147453645fSAndriy Voskoboinyk error = 0; 12157453645fSAndriy Voskoboinyk switch (nstate) { 12167453645fSAndriy Voskoboinyk case IEEE80211_S_SCAN: 1217bcaed14bSAdrian Chadd /* Pause AC Tx queues. */ 12187453645fSAndriy Voskoboinyk if (sc->vaps_running == 0) 12197453645fSAndriy Voskoboinyk rtwn_setbits_1(sc, R92C_TXPAUSE, 0, R92C_TX_QUEUE_AC); 1220bcaed14bSAdrian Chadd break; 1221bcaed14bSAdrian Chadd case IEEE80211_S_RUN: 12227453645fSAndriy Voskoboinyk error = rtwn_run(sc, vap); 12237453645fSAndriy Voskoboinyk if (error != 0) { 12247453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, 12257453645fSAndriy Voskoboinyk "%s: could not move to RUN state\n", __func__); 1226bcaed14bSAdrian Chadd break; 1227bcaed14bSAdrian Chadd } 1228bcaed14bSAdrian Chadd 12297453645fSAndriy Voskoboinyk sc->vaps_running++; 12307453645fSAndriy Voskoboinyk break; 1231b8ad00b0SAndriy Voskoboinyk case IEEE80211_S_CSA: 1232b8ad00b0SAndriy Voskoboinyk /* Block all Tx queues (except beacon queue). */ 1233b8ad00b0SAndriy Voskoboinyk rtwn_setbits_1(sc, R92C_TXPAUSE, 0, 1234b8ad00b0SAndriy Voskoboinyk R92C_TX_QUEUE_AC | R92C_TX_QUEUE_MGT | R92C_TX_QUEUE_HIGH); 1235b8ad00b0SAndriy Voskoboinyk break; 12367453645fSAndriy Voskoboinyk default: 12377453645fSAndriy Voskoboinyk break; 12387453645fSAndriy Voskoboinyk } 12397453645fSAndriy Voskoboinyk 12407453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 12417453645fSAndriy Voskoboinyk IEEE80211_LOCK(ic); 12427453645fSAndriy Voskoboinyk if (error != 0) 12437453645fSAndriy Voskoboinyk return (error); 12447453645fSAndriy Voskoboinyk 12457453645fSAndriy Voskoboinyk return (early_newstate ? 0 : uvp->newstate(vap, nstate, arg)); 12467453645fSAndriy Voskoboinyk } 12477453645fSAndriy Voskoboinyk 12487453645fSAndriy Voskoboinyk static void 12497453645fSAndriy Voskoboinyk rtwn_calc_basicrates(struct rtwn_softc *sc) 12507453645fSAndriy Voskoboinyk { 12517453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 12527453645fSAndriy Voskoboinyk uint32_t basicrates; 12537453645fSAndriy Voskoboinyk int i; 12547453645fSAndriy Voskoboinyk 12557453645fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc); 12567453645fSAndriy Voskoboinyk 12577453645fSAndriy Voskoboinyk if (ic->ic_flags & IEEE80211_F_SCAN) 12587453645fSAndriy Voskoboinyk return; /* will be done by rtwn_scan_end(). */ 12597453645fSAndriy Voskoboinyk 12607453645fSAndriy Voskoboinyk basicrates = 0; 12617453645fSAndriy Voskoboinyk for (i = 0; i < nitems(sc->vaps); i++) { 12627453645fSAndriy Voskoboinyk struct rtwn_vap *rvp; 12637453645fSAndriy Voskoboinyk struct ieee80211vap *vap; 12647453645fSAndriy Voskoboinyk struct ieee80211_node *ni; 1265aaaca5f2SAdrian Chadd struct ieee80211_htrateset *rs_ht; 1266aaaca5f2SAdrian Chadd uint32_t rates = 0, htrates = 0; 12677453645fSAndriy Voskoboinyk 12687453645fSAndriy Voskoboinyk rvp = sc->vaps[i]; 12697453645fSAndriy Voskoboinyk if (rvp == NULL || rvp->curr_mode == R92C_MSR_NOLINK) 12707453645fSAndriy Voskoboinyk continue; 12717453645fSAndriy Voskoboinyk 12727453645fSAndriy Voskoboinyk vap = &rvp->vap; 12737453645fSAndriy Voskoboinyk if (vap->iv_bss == NULL) 12747453645fSAndriy Voskoboinyk continue; 12757453645fSAndriy Voskoboinyk 12767453645fSAndriy Voskoboinyk ni = ieee80211_ref_node(vap->iv_bss); 1277aaaca5f2SAdrian Chadd if (ni->ni_flags & IEEE80211_NODE_HT) 1278aaaca5f2SAdrian Chadd rs_ht = &ni->ni_htrates; 1279aaaca5f2SAdrian Chadd else 1280aaaca5f2SAdrian Chadd rs_ht = NULL; 1281aaaca5f2SAdrian Chadd /* 1282aaaca5f2SAdrian Chadd * Only fetches basic rates; fetch 802.11abg and 11n basic 1283aaaca5f2SAdrian Chadd * rates 1284aaaca5f2SAdrian Chadd */ 1285aaaca5f2SAdrian Chadd rtwn_get_rates(sc, &ni->ni_rates, rs_ht, &rates, &htrates, 1286aaaca5f2SAdrian Chadd NULL, 1); 1287aaaca5f2SAdrian Chadd 1288aaaca5f2SAdrian Chadd /* 1289aaaca5f2SAdrian Chadd * We need at least /an/ OFDM and/or MCS rate for HT 1290aaaca5f2SAdrian Chadd * operation, or the MAC will generate MCS7 ACK/Block-ACK 1291aaaca5f2SAdrian Chadd * frames and thus performance will suffer. 1292aaaca5f2SAdrian Chadd */ 1293aaaca5f2SAdrian Chadd if (ni->ni_flags & IEEE80211_NODE_HT) { 1294aaaca5f2SAdrian Chadd htrates |= 0x01; /* MCS0 */ 1295aaaca5f2SAdrian Chadd rates |= (1 << RTWN_RIDX_OFDM6); 1296aaaca5f2SAdrian Chadd } 1297aaaca5f2SAdrian Chadd 12987453645fSAndriy Voskoboinyk basicrates |= rates; 1299aaaca5f2SAdrian Chadd basicrates |= (htrates << RTWN_RIDX_HT_MCS_SHIFT); 1300aaaca5f2SAdrian Chadd 1301aaaca5f2SAdrian Chadd /* Filter out undesired high rates */ 1302aaaca5f2SAdrian Chadd if (ni->ni_chan != IEEE80211_CHAN_ANYC && 1303aaaca5f2SAdrian Chadd IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) 1304aaaca5f2SAdrian Chadd basicrates &= R92C_RRSR_RATE_MASK_5GHZ; 1305aaaca5f2SAdrian Chadd else 1306aaaca5f2SAdrian Chadd basicrates &= R92C_RRSR_RATE_MASK_2GHZ; 1307aaaca5f2SAdrian Chadd 13087453645fSAndriy Voskoboinyk ieee80211_free_node(ni); 13097453645fSAndriy Voskoboinyk } 13107453645fSAndriy Voskoboinyk 1311*f45f66faSAdrian Chadd if (basicrates == 0) { 1312*f45f66faSAdrian Chadd device_printf(sc->sc_dev, 1313*f45f66faSAdrian Chadd "WARNING: no configured basic rates!\n"); 13147453645fSAndriy Voskoboinyk return; 1315*f45f66faSAdrian Chadd } 13167453645fSAndriy Voskoboinyk 13177453645fSAndriy Voskoboinyk rtwn_set_basicrates(sc, basicrates); 1318*f45f66faSAdrian Chadd rtwn_set_rts_rate(sc, basicrates); 13197453645fSAndriy Voskoboinyk } 13207453645fSAndriy Voskoboinyk 13217453645fSAndriy Voskoboinyk static int 13227453645fSAndriy Voskoboinyk rtwn_run(struct rtwn_softc *sc, struct ieee80211vap *vap) 13237453645fSAndriy Voskoboinyk { 13247453645fSAndriy Voskoboinyk struct ieee80211com *ic = vap->iv_ic; 13257453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = RTWN_VAP(vap); 13267453645fSAndriy Voskoboinyk struct ieee80211_node *ni; 13277453645fSAndriy Voskoboinyk uint8_t mode; 13287453645fSAndriy Voskoboinyk int error; 13297453645fSAndriy Voskoboinyk 13307453645fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc); 13317453645fSAndriy Voskoboinyk 13327453645fSAndriy Voskoboinyk error = 0; 13337453645fSAndriy Voskoboinyk ni = ieee80211_ref_node(vap->iv_bss); 13347453645fSAndriy Voskoboinyk 13357453645fSAndriy Voskoboinyk if (ic->ic_bsschan == IEEE80211_CHAN_ANYC || 13367453645fSAndriy Voskoboinyk ni->ni_chan == IEEE80211_CHAN_ANYC) { 13377453645fSAndriy Voskoboinyk error = EINVAL; 13387453645fSAndriy Voskoboinyk goto fail; 13397453645fSAndriy Voskoboinyk } 13407453645fSAndriy Voskoboinyk 13417453645fSAndriy Voskoboinyk switch (vap->iv_opmode) { 13427453645fSAndriy Voskoboinyk case IEEE80211_M_STA: 13437453645fSAndriy Voskoboinyk mode = R92C_MSR_INFRA; 13447453645fSAndriy Voskoboinyk break; 13457453645fSAndriy Voskoboinyk case IEEE80211_M_IBSS: 13467453645fSAndriy Voskoboinyk mode = R92C_MSR_ADHOC; 13477453645fSAndriy Voskoboinyk break; 13487453645fSAndriy Voskoboinyk case IEEE80211_M_HOSTAP: 13497453645fSAndriy Voskoboinyk mode = R92C_MSR_AP; 13507453645fSAndriy Voskoboinyk break; 13517453645fSAndriy Voskoboinyk default: 13527453645fSAndriy Voskoboinyk KASSERT(0, ("undefined opmode %d\n", vap->iv_opmode)); 13537453645fSAndriy Voskoboinyk error = EINVAL; 13547453645fSAndriy Voskoboinyk goto fail; 13557453645fSAndriy Voskoboinyk } 13567453645fSAndriy Voskoboinyk 1357bcaed14bSAdrian Chadd /* Set media status to 'Associated'. */ 13587453645fSAndriy Voskoboinyk rtwn_set_mode(sc, mode, uvp->id); 13597453645fSAndriy Voskoboinyk 13607453645fSAndriy Voskoboinyk /* Set AssocID. */ 13617453645fSAndriy Voskoboinyk /* XXX multi-vap? */ 13627453645fSAndriy Voskoboinyk rtwn_write_2(sc, R92C_BCN_PSR_RPT, 13637453645fSAndriy Voskoboinyk 0xc000 | IEEE80211_NODE_AID(ni)); 1364bcaed14bSAdrian Chadd 1365bcaed14bSAdrian Chadd /* Set BSSID. */ 13667453645fSAndriy Voskoboinyk rtwn_set_bssid(sc, ni->ni_bssid, uvp->id); 1367bcaed14bSAdrian Chadd 13687453645fSAndriy Voskoboinyk /* Set beacon interval. */ 13697453645fSAndriy Voskoboinyk rtwn_write_2(sc, R92C_BCN_INTERVAL(uvp->id), ni->ni_intval); 1370bcaed14bSAdrian Chadd 13717453645fSAndriy Voskoboinyk if (sc->vaps_running == sc->monvaps_running) { 1372bcaed14bSAdrian Chadd /* Enable Rx of data frames. */ 1373bcaed14bSAdrian Chadd rtwn_write_2(sc, R92C_RXFLTMAP2, 0xffff); 1374bcaed14bSAdrian Chadd 1375bcaed14bSAdrian Chadd /* Flush all AC queues. */ 1376bcaed14bSAdrian Chadd rtwn_write_1(sc, R92C_TXPAUSE, 0); 13777453645fSAndriy Voskoboinyk } 1378bcaed14bSAdrian Chadd 13797453645fSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE 13807453645fSAndriy Voskoboinyk /* Upload (QoS) Null Data frame to firmware. */ 13817453645fSAndriy Voskoboinyk /* Note: do this for port 0 only. */ 13827453645fSAndriy Voskoboinyk if ((ic->ic_caps & IEEE80211_C_PMGT) != 0 && 13837453645fSAndriy Voskoboinyk vap->iv_opmode == IEEE80211_M_STA && uvp->id == 0) { 13847453645fSAndriy Voskoboinyk error = rtwn_tx_fwpkt_check(sc, vap); 13857453645fSAndriy Voskoboinyk if (error != 0) 13867453645fSAndriy Voskoboinyk goto fail; 13877453645fSAndriy Voskoboinyk 13887453645fSAndriy Voskoboinyk /* Setup power management. */ 13897453645fSAndriy Voskoboinyk /* 13907453645fSAndriy Voskoboinyk * NB: it will be enabled immediately - delay it, 13917453645fSAndriy Voskoboinyk * so 4-Way handshake will not be interrupted. 13927453645fSAndriy Voskoboinyk */ 13937453645fSAndriy Voskoboinyk callout_reset(&sc->sc_pwrmode_init, 5*hz, 13947453645fSAndriy Voskoboinyk rtwn_pwrmode_init, sc); 13957453645fSAndriy Voskoboinyk } 13967453645fSAndriy Voskoboinyk #endif 13977453645fSAndriy Voskoboinyk 1398d067ef0fSAndriy Voskoboinyk /* Enable TSF synchronization. */ 1399d067ef0fSAndriy Voskoboinyk rtwn_tsf_sync_enable(sc, vap); 1400d067ef0fSAndriy Voskoboinyk 14017453645fSAndriy Voskoboinyk if (vap->iv_opmode == IEEE80211_M_HOSTAP || 14027453645fSAndriy Voskoboinyk vap->iv_opmode == IEEE80211_M_IBSS) { 14037453645fSAndriy Voskoboinyk error = rtwn_setup_beacon(sc, ni); 14047453645fSAndriy Voskoboinyk if (error != 0) { 14057453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, 14067453645fSAndriy Voskoboinyk "unable to push beacon into the chip, " 14077453645fSAndriy Voskoboinyk "error %d\n", error); 14087453645fSAndriy Voskoboinyk goto fail; 14097453645fSAndriy Voskoboinyk } 14107453645fSAndriy Voskoboinyk } 14117453645fSAndriy Voskoboinyk 14127453645fSAndriy Voskoboinyk /* Set ACK preamble type. */ 14137453645fSAndriy Voskoboinyk rtwn_set_ack_preamble(sc); 1414bcaed14bSAdrian Chadd 14157453645fSAndriy Voskoboinyk /* Set basic rates mask. */ 14167453645fSAndriy Voskoboinyk rtwn_calc_basicrates(sc); 14177453645fSAndriy Voskoboinyk 14187453645fSAndriy Voskoboinyk #ifdef RTWN_TODO 1419bcaed14bSAdrian Chadd rtwn_write_1(sc, R92C_SIFS_CCK + 1, 10); 1420bcaed14bSAdrian Chadd rtwn_write_1(sc, R92C_SIFS_OFDM + 1, 10); 1421bcaed14bSAdrian Chadd rtwn_write_1(sc, R92C_SPEC_SIFS + 1, 10); 1422bcaed14bSAdrian Chadd rtwn_write_1(sc, R92C_MAC_SPEC_SIFS + 1, 10); 1423bcaed14bSAdrian Chadd rtwn_write_1(sc, R92C_R2T_SIFS + 1, 10); 1424bcaed14bSAdrian Chadd rtwn_write_1(sc, R92C_T2T_SIFS + 1, 10); 14257453645fSAndriy Voskoboinyk #endif 1426bcaed14bSAdrian Chadd 14277453645fSAndriy Voskoboinyk if (sc->vaps_running == sc->monvaps_running) { 14287453645fSAndriy Voskoboinyk /* Reset temperature calibration state machine. */ 14297453645fSAndriy Voskoboinyk sc->sc_flags &= ~RTWN_TEMP_MEASURED; 14307453645fSAndriy Voskoboinyk sc->thcal_temp = sc->thermal_meter; 14317453645fSAndriy Voskoboinyk 14327453645fSAndriy Voskoboinyk /* Start periodic calibration. */ 14337453645fSAndriy Voskoboinyk callout_reset(&sc->sc_calib_to, 2*hz, rtwn_calib_to, 14347453645fSAndriy Voskoboinyk sc); 14357453645fSAndriy Voskoboinyk 14367453645fSAndriy Voskoboinyk if (sc->vaps_running == 0) { 1437bcaed14bSAdrian Chadd /* Turn link LED on. */ 1438bcaed14bSAdrian Chadd rtwn_set_led(sc, RTWN_LED_LINK, 1); 1439bcaed14bSAdrian Chadd } 1440bcaed14bSAdrian Chadd } 1441bcaed14bSAdrian Chadd 14427453645fSAndriy Voskoboinyk fail: 1443bcaed14bSAdrian Chadd ieee80211_free_node(ni); 1444bcaed14bSAdrian Chadd 1445bcaed14bSAdrian Chadd return (error); 1446bcaed14bSAdrian Chadd } 1447bcaed14bSAdrian Chadd 14487453645fSAndriy Voskoboinyk #ifndef D4054 1449bcaed14bSAdrian Chadd static void 1450bcaed14bSAdrian Chadd rtwn_watchdog(void *arg) 1451bcaed14bSAdrian Chadd { 1452bcaed14bSAdrian Chadd struct rtwn_softc *sc = arg; 1453bcaed14bSAdrian Chadd struct ieee80211com *ic = &sc->sc_ic; 1454bcaed14bSAdrian Chadd 14557453645fSAndriy Voskoboinyk RTWN_ASSERT_LOCKED(sc); 1456bcaed14bSAdrian Chadd 1457bcaed14bSAdrian Chadd KASSERT(sc->sc_flags & RTWN_RUNNING, ("not running")); 1458bcaed14bSAdrian Chadd 1459bcaed14bSAdrian Chadd if (sc->sc_tx_timer != 0 && --sc->sc_tx_timer == 0) { 1460bcaed14bSAdrian Chadd ic_printf(ic, "device timeout\n"); 14615274f944SAndriy Voskoboinyk ieee80211_restart_all(ic); 1462bcaed14bSAdrian Chadd return; 1463bcaed14bSAdrian Chadd } 14647453645fSAndriy Voskoboinyk callout_reset(&sc->sc_watchdog_to, hz, rtwn_watchdog, sc); 1465bcaed14bSAdrian Chadd } 14667453645fSAndriy Voskoboinyk #endif 14677453645fSAndriy Voskoboinyk 14687453645fSAndriy Voskoboinyk static void 14697453645fSAndriy Voskoboinyk rtwn_parent(struct ieee80211com *ic) 14707453645fSAndriy Voskoboinyk { 14717453645fSAndriy Voskoboinyk struct rtwn_softc *sc = ic->ic_softc; 14727453645fSAndriy Voskoboinyk struct ieee80211vap *vap; 14737453645fSAndriy Voskoboinyk 14747453645fSAndriy Voskoboinyk if (ic->ic_nrunning > 0) { 14757453645fSAndriy Voskoboinyk if (rtwn_init(sc) != 0) { 14767453645fSAndriy Voskoboinyk IEEE80211_LOCK(ic); 14777453645fSAndriy Voskoboinyk TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 14787453645fSAndriy Voskoboinyk ieee80211_stop_locked(vap); 14797453645fSAndriy Voskoboinyk IEEE80211_UNLOCK(ic); 14807453645fSAndriy Voskoboinyk } else 14817453645fSAndriy Voskoboinyk ieee80211_start_all(ic); 14827453645fSAndriy Voskoboinyk } else 14837453645fSAndriy Voskoboinyk rtwn_stop(sc); 14847453645fSAndriy Voskoboinyk } 14857453645fSAndriy Voskoboinyk 1486bcaed14bSAdrian Chadd static int 1487bcaed14bSAdrian Chadd rtwn_dma_init(struct rtwn_softc *sc) 1488bcaed14bSAdrian Chadd { 14897453645fSAndriy Voskoboinyk #define RTWN_CHK(res) do { \ 14907453645fSAndriy Voskoboinyk if (res != 0) \ 14917453645fSAndriy Voskoboinyk return (EIO); \ 14927453645fSAndriy Voskoboinyk } while(0) 14937453645fSAndriy Voskoboinyk uint16_t reg; 14947453645fSAndriy Voskoboinyk uint8_t tx_boundary; 1495bcaed14bSAdrian Chadd int error; 1496bcaed14bSAdrian Chadd 1497bcaed14bSAdrian Chadd /* Initialize LLT table. */ 1498bcaed14bSAdrian Chadd error = rtwn_llt_init(sc); 1499bcaed14bSAdrian Chadd if (error != 0) 15007453645fSAndriy Voskoboinyk return (error); 1501bcaed14bSAdrian Chadd 15027453645fSAndriy Voskoboinyk /* Set the number of pages for each queue. */ 15037453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_RESET, 15047453645fSAndriy Voskoboinyk "%s: pages per queue: high %d, normal %d, low %d, public %d\n", 15057453645fSAndriy Voskoboinyk __func__, sc->nhqpages, sc->nnqpages, sc->nlqpages, 15067453645fSAndriy Voskoboinyk sc->npubqpages); 15077453645fSAndriy Voskoboinyk 15087453645fSAndriy Voskoboinyk RTWN_CHK(rtwn_write_1(sc, R92C_RQPN_NPQ, sc->nnqpages)); 15097453645fSAndriy Voskoboinyk RTWN_CHK(rtwn_write_4(sc, R92C_RQPN, 1510bcaed14bSAdrian Chadd /* Set number of pages for public queue. */ 15117453645fSAndriy Voskoboinyk SM(R92C_RQPN_PUBQ, sc->npubqpages) | 1512bcaed14bSAdrian Chadd /* Set number of pages for high priority queue. */ 15137453645fSAndriy Voskoboinyk SM(R92C_RQPN_HPQ, sc->nhqpages) | 1514bcaed14bSAdrian Chadd /* Set number of pages for low priority queue. */ 15157453645fSAndriy Voskoboinyk SM(R92C_RQPN_LPQ, sc->nlqpages) | 1516bcaed14bSAdrian Chadd /* Load values. */ 15177453645fSAndriy Voskoboinyk R92C_RQPN_LD)); 1518bcaed14bSAdrian Chadd 15197453645fSAndriy Voskoboinyk /* Initialize TX buffer boundary. */ 15207453645fSAndriy Voskoboinyk KASSERT(sc->page_count < 255 && sc->page_count > 0, 15217453645fSAndriy Voskoboinyk ("page_count is %d\n", sc->page_count)); 15227453645fSAndriy Voskoboinyk tx_boundary = sc->page_count + 1; 15237453645fSAndriy Voskoboinyk RTWN_CHK(rtwn_write_1(sc, R92C_TXPKTBUF_BCNQ_BDNY, tx_boundary)); 15247453645fSAndriy Voskoboinyk RTWN_CHK(rtwn_write_1(sc, R92C_TXPKTBUF_MGQ_BDNY, tx_boundary)); 15257453645fSAndriy Voskoboinyk RTWN_CHK(rtwn_write_1(sc, R92C_TXPKTBUF_WMAC_LBK_BF_HD, tx_boundary)); 15267453645fSAndriy Voskoboinyk RTWN_CHK(rtwn_write_1(sc, R92C_TRXFF_BNDY, tx_boundary)); 15277453645fSAndriy Voskoboinyk RTWN_CHK(rtwn_write_1(sc, R92C_TDECTRL + 1, tx_boundary)); 1528bcaed14bSAdrian Chadd 15297453645fSAndriy Voskoboinyk error = rtwn_init_bcnq1_boundary(sc); 15307453645fSAndriy Voskoboinyk if (error != 0) 15317453645fSAndriy Voskoboinyk return (error); 1532bcaed14bSAdrian Chadd 15337453645fSAndriy Voskoboinyk /* Set queue to USB pipe mapping. */ 15347453645fSAndriy Voskoboinyk /* Note: PCIe devices are using some magic number here. */ 15357453645fSAndriy Voskoboinyk reg = rtwn_get_qmap(sc); 15367453645fSAndriy Voskoboinyk RTWN_CHK(rtwn_setbits_2(sc, R92C_TRXDMA_CTRL, 15377453645fSAndriy Voskoboinyk R92C_TRXDMA_CTRL_QMAP_M, reg)); 1538bcaed14bSAdrian Chadd 15397453645fSAndriy Voskoboinyk /* Configure Tx/Rx DMA (PCIe). */ 15407453645fSAndriy Voskoboinyk rtwn_set_desc_addr(sc); 1541bcaed14bSAdrian Chadd 1542bcaed14bSAdrian Chadd /* Set Tx/Rx transfer page boundary. */ 15437453645fSAndriy Voskoboinyk RTWN_CHK(rtwn_write_2(sc, R92C_TRXFF_BNDY + 2, 15447453645fSAndriy Voskoboinyk sc->rx_dma_size - 1)); 1545bcaed14bSAdrian Chadd 1546bcaed14bSAdrian Chadd /* Set Tx/Rx transfer page size. */ 15477453645fSAndriy Voskoboinyk rtwn_set_page_size(sc); 15487453645fSAndriy Voskoboinyk 15497453645fSAndriy Voskoboinyk return (0); 15507453645fSAndriy Voskoboinyk } 15517453645fSAndriy Voskoboinyk 15527453645fSAndriy Voskoboinyk static int 15537453645fSAndriy Voskoboinyk rtwn_mac_init(struct rtwn_softc *sc) 15547453645fSAndriy Voskoboinyk { 15557453645fSAndriy Voskoboinyk int i, error; 15567453645fSAndriy Voskoboinyk 15577453645fSAndriy Voskoboinyk /* Write MAC initialization values. */ 15587453645fSAndriy Voskoboinyk for (i = 0; i < sc->mac_size; i++) { 15597453645fSAndriy Voskoboinyk error = rtwn_write_1(sc, sc->mac_prog[i].reg, 15607453645fSAndriy Voskoboinyk sc->mac_prog[i].val); 15617453645fSAndriy Voskoboinyk if (error != 0) 15627453645fSAndriy Voskoboinyk return (error); 15637453645fSAndriy Voskoboinyk } 15647453645fSAndriy Voskoboinyk 1565bcaed14bSAdrian Chadd return (0); 1566bcaed14bSAdrian Chadd } 1567bcaed14bSAdrian Chadd 1568bcaed14bSAdrian Chadd static void 15697453645fSAndriy Voskoboinyk rtwn_mrr_init(struct rtwn_softc *sc) 1570bcaed14bSAdrian Chadd { 1571bcaed14bSAdrian Chadd int i; 1572bcaed14bSAdrian Chadd 15737453645fSAndriy Voskoboinyk /* Drop rate index by 1 per retry. */ 15747453645fSAndriy Voskoboinyk for (i = 0; i < R92C_DARFRC_SIZE; i++) { 15757453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_DARFRC + i, i + 1); 15767453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_RARFRC + i, i + 1); 1577bcaed14bSAdrian Chadd } 15785036353aSAndriy Voskoboinyk } 15795036353aSAndriy Voskoboinyk 15805036353aSAndriy Voskoboinyk static void 1581bcaed14bSAdrian Chadd rtwn_scan_start(struct ieee80211com *ic) 1582bcaed14bSAdrian Chadd { 15835036353aSAndriy Voskoboinyk struct rtwn_softc *sc = ic->ic_softc; 1584bcaed14bSAdrian Chadd 15855036353aSAndriy Voskoboinyk RTWN_LOCK(sc); 15868361f9cdSAndriy Voskoboinyk /* Pause beaconing. */ 15878361f9cdSAndriy Voskoboinyk rtwn_setbits_1(sc, R92C_TXPAUSE, 0, R92C_TX_QUEUE_BCN); 15885036353aSAndriy Voskoboinyk /* Receive beacons / probe responses from any BSSID. */ 15897453645fSAndriy Voskoboinyk if (sc->bcn_vaps == 0) 15905036353aSAndriy Voskoboinyk rtwn_set_rx_bssid_all(sc, 1); 15915036353aSAndriy Voskoboinyk RTWN_UNLOCK(sc); 1592bcaed14bSAdrian Chadd } 1593bcaed14bSAdrian Chadd 1594bcaed14bSAdrian Chadd static void 15957453645fSAndriy Voskoboinyk rtwn_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) 15967453645fSAndriy Voskoboinyk { 15977453645fSAndriy Voskoboinyk struct rtwn_softc *sc = ss->ss_ic->ic_softc; 15987453645fSAndriy Voskoboinyk 15997453645fSAndriy Voskoboinyk /* Make link LED blink during scan. */ 16007453645fSAndriy Voskoboinyk RTWN_LOCK(sc); 16017453645fSAndriy Voskoboinyk rtwn_set_led(sc, RTWN_LED_LINK, !sc->ledlink); 16027453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 16037453645fSAndriy Voskoboinyk 16047453645fSAndriy Voskoboinyk sc->sc_scan_curchan(ss, maxdwell); 16057453645fSAndriy Voskoboinyk } 16067453645fSAndriy Voskoboinyk 16077453645fSAndriy Voskoboinyk static void 1608bcaed14bSAdrian Chadd rtwn_scan_end(struct ieee80211com *ic) 1609bcaed14bSAdrian Chadd { 16105036353aSAndriy Voskoboinyk struct rtwn_softc *sc = ic->ic_softc; 1611bcaed14bSAdrian Chadd 16125036353aSAndriy Voskoboinyk RTWN_LOCK(sc); 16135036353aSAndriy Voskoboinyk /* Restore limitations. */ 16147453645fSAndriy Voskoboinyk if (ic->ic_promisc == 0 && sc->bcn_vaps == 0) 16155036353aSAndriy Voskoboinyk rtwn_set_rx_bssid_all(sc, 0); 16167453645fSAndriy Voskoboinyk 16177453645fSAndriy Voskoboinyk /* Restore LED state. */ 16187453645fSAndriy Voskoboinyk rtwn_set_led(sc, RTWN_LED_LINK, (sc->vaps_running != 0)); 16197453645fSAndriy Voskoboinyk 16207453645fSAndriy Voskoboinyk /* Restore basic rates mask. */ 16217453645fSAndriy Voskoboinyk rtwn_calc_basicrates(sc); 16228361f9cdSAndriy Voskoboinyk 16238361f9cdSAndriy Voskoboinyk /* Resume beaconing. */ 16248361f9cdSAndriy Voskoboinyk rtwn_setbits_1(sc, R92C_TXPAUSE, R92C_TX_QUEUE_BCN, 0); 16255036353aSAndriy Voskoboinyk RTWN_UNLOCK(sc); 1626bcaed14bSAdrian Chadd } 1627bcaed14bSAdrian Chadd 1628bcaed14bSAdrian Chadd static void 1629a7c31fe1SAndriy Voskoboinyk rtwn_getradiocaps(struct ieee80211com *ic, 1630a7c31fe1SAndriy Voskoboinyk int maxchans, int *nchans, struct ieee80211_channel chans[]) 1631a7c31fe1SAndriy Voskoboinyk { 16327453645fSAndriy Voskoboinyk struct rtwn_softc *sc = ic->ic_softc; 1633a7c31fe1SAndriy Voskoboinyk uint8_t bands[IEEE80211_MODE_BYTES]; 16342b9f12f6SBjoern A. Zeeb int cbw_flags, i; 16352b9f12f6SBjoern A. Zeeb 16362b9f12f6SBjoern A. Zeeb cbw_flags = (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) ? 16372b9f12f6SBjoern A. Zeeb NET80211_CBW_FLAG_HT40 : 0; 1638a7c31fe1SAndriy Voskoboinyk 1639a7c31fe1SAndriy Voskoboinyk memset(bands, 0, sizeof(bands)); 1640a7c31fe1SAndriy Voskoboinyk setbit(bands, IEEE80211_MODE_11B); 1641a7c31fe1SAndriy Voskoboinyk setbit(bands, IEEE80211_MODE_11G); 16427453645fSAndriy Voskoboinyk setbit(bands, IEEE80211_MODE_11NG); 1643b84b3638SAndriy Voskoboinyk ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, 16442b9f12f6SBjoern A. Zeeb bands, cbw_flags); 16457453645fSAndriy Voskoboinyk 16467453645fSAndriy Voskoboinyk /* XXX workaround add_channel_list() limitations */ 16477453645fSAndriy Voskoboinyk setbit(bands, IEEE80211_MODE_11A); 16487453645fSAndriy Voskoboinyk setbit(bands, IEEE80211_MODE_11NA); 16492be951a5SAdrian Chadd 16502be951a5SAdrian Chadd if (IEEE80211_CONF_VHT(ic)) { 16512be951a5SAdrian Chadd setbit(bands, IEEE80211_MODE_VHT_5GHZ); 16522be951a5SAdrian Chadd /* Only enable VHT80 if HT40/VHT40 is available */ 16532be951a5SAdrian Chadd if (sc->sc_ht40) 16542be951a5SAdrian Chadd cbw_flags |= NET80211_CBW_FLAG_VHT80; 16552be951a5SAdrian Chadd } 16562be951a5SAdrian Chadd 16577453645fSAndriy Voskoboinyk for (i = 0; i < nitems(sc->chan_num_5ghz); i++) { 16587453645fSAndriy Voskoboinyk if (sc->chan_num_5ghz[i] == 0) 16597453645fSAndriy Voskoboinyk continue; 16607453645fSAndriy Voskoboinyk 16617453645fSAndriy Voskoboinyk ieee80211_add_channel_list_5ghz(chans, maxchans, nchans, 16627453645fSAndriy Voskoboinyk sc->chan_list_5ghz[i], sc->chan_num_5ghz[i], bands, 16632b9f12f6SBjoern A. Zeeb cbw_flags); 16647453645fSAndriy Voskoboinyk } 16657453645fSAndriy Voskoboinyk } 16667453645fSAndriy Voskoboinyk 16677453645fSAndriy Voskoboinyk static void 16687453645fSAndriy Voskoboinyk rtwn_update_chw(struct ieee80211com *ic) 16697453645fSAndriy Voskoboinyk { 1670a7c31fe1SAndriy Voskoboinyk } 1671a7c31fe1SAndriy Voskoboinyk 1672a7c31fe1SAndriy Voskoboinyk static void 1673bcaed14bSAdrian Chadd rtwn_set_channel(struct ieee80211com *ic) 1674bcaed14bSAdrian Chadd { 1675bcaed14bSAdrian Chadd struct rtwn_softc *sc = ic->ic_softc; 16767453645fSAndriy Voskoboinyk struct ieee80211_channel *c = ic->ic_curchan; 1677bcaed14bSAdrian Chadd 1678bcaed14bSAdrian Chadd RTWN_LOCK(sc); 16797453645fSAndriy Voskoboinyk rtwn_set_chan(sc, c); 16807453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 1681bcaed14bSAdrian Chadd } 16827453645fSAndriy Voskoboinyk 16837453645fSAndriy Voskoboinyk static int 16847453645fSAndriy Voskoboinyk rtwn_wme_update(struct ieee80211com *ic) 16857453645fSAndriy Voskoboinyk { 16869fbe631aSAdrian Chadd struct chanAccParams chp; 16877453645fSAndriy Voskoboinyk struct ieee80211_channel *c = ic->ic_curchan; 16887453645fSAndriy Voskoboinyk struct rtwn_softc *sc = ic->ic_softc; 16897453645fSAndriy Voskoboinyk struct wmeParams *wmep = sc->cap_wmeParams; 16907453645fSAndriy Voskoboinyk uint8_t aifs, acm, slottime; 16917453645fSAndriy Voskoboinyk int ac; 16927453645fSAndriy Voskoboinyk 16939fbe631aSAdrian Chadd ieee80211_wme_ic_getparams(ic, &chp); 16949fbe631aSAdrian Chadd 16957453645fSAndriy Voskoboinyk /* Prevent possible races. */ 16967453645fSAndriy Voskoboinyk IEEE80211_LOCK(ic); /* XXX */ 16977453645fSAndriy Voskoboinyk RTWN_LOCK(sc); 16989fbe631aSAdrian Chadd memcpy(wmep, chp.cap_wmeParams, sizeof(sc->cap_wmeParams)); 16997453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 17007453645fSAndriy Voskoboinyk IEEE80211_UNLOCK(ic); 17017453645fSAndriy Voskoboinyk 17027453645fSAndriy Voskoboinyk acm = 0; 17037453645fSAndriy Voskoboinyk slottime = IEEE80211_GET_SLOTTIME(ic); 17047453645fSAndriy Voskoboinyk 17057453645fSAndriy Voskoboinyk RTWN_LOCK(sc); 17067453645fSAndriy Voskoboinyk for (ac = WME_AC_BE; ac < WME_NUM_AC; ac++) { 17077453645fSAndriy Voskoboinyk /* AIFS[AC] = AIFSN[AC] * aSlotTime + aSIFSTime. */ 17087453645fSAndriy Voskoboinyk aifs = wmep[ac].wmep_aifsn * slottime + 17097453645fSAndriy Voskoboinyk (IEEE80211_IS_CHAN_5GHZ(c) ? 17107453645fSAndriy Voskoboinyk IEEE80211_DUR_OFDM_SIFS : IEEE80211_DUR_SIFS); 17117453645fSAndriy Voskoboinyk rtwn_write_4(sc, wme2reg[ac], 17127453645fSAndriy Voskoboinyk SM(R92C_EDCA_PARAM_TXOP, wmep[ac].wmep_txopLimit) | 17137453645fSAndriy Voskoboinyk SM(R92C_EDCA_PARAM_ECWMIN, wmep[ac].wmep_logcwmin) | 17147453645fSAndriy Voskoboinyk SM(R92C_EDCA_PARAM_ECWMAX, wmep[ac].wmep_logcwmax) | 17157453645fSAndriy Voskoboinyk SM(R92C_EDCA_PARAM_AIFS, aifs)); 17167453645fSAndriy Voskoboinyk if (ac != WME_AC_BE) 17177453645fSAndriy Voskoboinyk acm |= wmep[ac].wmep_acm << ac; 17187453645fSAndriy Voskoboinyk } 17197453645fSAndriy Voskoboinyk 17207453645fSAndriy Voskoboinyk if (acm != 0) 17217453645fSAndriy Voskoboinyk acm |= R92C_ACMHWCTRL_EN; 17227453645fSAndriy Voskoboinyk rtwn_setbits_1(sc, R92C_ACMHWCTRL, R92C_ACMHWCTRL_ACM_MASK, acm); 17237453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 17247453645fSAndriy Voskoboinyk 17257453645fSAndriy Voskoboinyk return 0; 17267453645fSAndriy Voskoboinyk } 17277453645fSAndriy Voskoboinyk 17287453645fSAndriy Voskoboinyk static void 17297453645fSAndriy Voskoboinyk rtwn_update_slot(struct ieee80211com *ic) 17307453645fSAndriy Voskoboinyk { 17317453645fSAndriy Voskoboinyk rtwn_cmd_sleepable(ic->ic_softc, NULL, 0, rtwn_update_slot_cb); 17327453645fSAndriy Voskoboinyk } 17337453645fSAndriy Voskoboinyk 17347453645fSAndriy Voskoboinyk static void 17357453645fSAndriy Voskoboinyk rtwn_update_slot_cb(struct rtwn_softc *sc, union sec_param *data) 17367453645fSAndriy Voskoboinyk { 17377453645fSAndriy Voskoboinyk struct ieee80211com *ic = &sc->sc_ic; 17387453645fSAndriy Voskoboinyk uint8_t slottime; 17397453645fSAndriy Voskoboinyk 17407453645fSAndriy Voskoboinyk slottime = IEEE80211_GET_SLOTTIME(ic); 17417453645fSAndriy Voskoboinyk 17427453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_STATE, "%s: setting slot time to %uus\n", 17437453645fSAndriy Voskoboinyk __func__, slottime); 17447453645fSAndriy Voskoboinyk 17457453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_SLOT, slottime); 17467453645fSAndriy Voskoboinyk rtwn_update_aifs(sc, slottime); 17477453645fSAndriy Voskoboinyk } 17487453645fSAndriy Voskoboinyk 17497453645fSAndriy Voskoboinyk static void 17507453645fSAndriy Voskoboinyk rtwn_update_aifs(struct rtwn_softc *sc, uint8_t slottime) 17517453645fSAndriy Voskoboinyk { 17527453645fSAndriy Voskoboinyk struct ieee80211_channel *c = sc->sc_ic.ic_curchan; 17537453645fSAndriy Voskoboinyk const struct wmeParams *wmep = sc->cap_wmeParams; 17547453645fSAndriy Voskoboinyk uint8_t aifs, ac; 17557453645fSAndriy Voskoboinyk 17567453645fSAndriy Voskoboinyk for (ac = WME_AC_BE; ac < WME_NUM_AC; ac++) { 17577453645fSAndriy Voskoboinyk /* AIFS[AC] = AIFSN[AC] * aSlotTime + aSIFSTime. */ 17587453645fSAndriy Voskoboinyk aifs = wmep[ac].wmep_aifsn * slottime + 17597453645fSAndriy Voskoboinyk (IEEE80211_IS_CHAN_5GHZ(c) ? 17607453645fSAndriy Voskoboinyk IEEE80211_DUR_OFDM_SIFS : IEEE80211_DUR_SIFS); 17617453645fSAndriy Voskoboinyk rtwn_write_1(sc, wme2reg[ac], aifs); 17627453645fSAndriy Voskoboinyk } 17637453645fSAndriy Voskoboinyk } 17647453645fSAndriy Voskoboinyk 17657453645fSAndriy Voskoboinyk static void 17667453645fSAndriy Voskoboinyk rtwn_update_promisc(struct ieee80211com *ic) 17677453645fSAndriy Voskoboinyk { 17687453645fSAndriy Voskoboinyk struct rtwn_softc *sc = ic->ic_softc; 17697453645fSAndriy Voskoboinyk 17707453645fSAndriy Voskoboinyk RTWN_LOCK(sc); 17717453645fSAndriy Voskoboinyk if (sc->sc_flags & RTWN_RUNNING) 17727453645fSAndriy Voskoboinyk rtwn_set_promisc(sc); 1773bcaed14bSAdrian Chadd RTWN_UNLOCK(sc); 1774bcaed14bSAdrian Chadd } 1775bcaed14bSAdrian Chadd 1776bcaed14bSAdrian Chadd static void 1777bcaed14bSAdrian Chadd rtwn_update_mcast(struct ieee80211com *ic) 1778bcaed14bSAdrian Chadd { 17797453645fSAndriy Voskoboinyk struct rtwn_softc *sc = ic->ic_softc; 1780bcaed14bSAdrian Chadd 17817453645fSAndriy Voskoboinyk RTWN_LOCK(sc); 17827453645fSAndriy Voskoboinyk if (sc->sc_flags & RTWN_RUNNING) 17837453645fSAndriy Voskoboinyk rtwn_set_multi(sc); 17847453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 1785bcaed14bSAdrian Chadd } 1786bcaed14bSAdrian Chadd 1787bcaed14bSAdrian Chadd static int 17887453645fSAndriy Voskoboinyk rtwn_set_bssid(struct rtwn_softc *sc, const uint8_t *bssid, int id) 1789bcaed14bSAdrian Chadd { 17907453645fSAndriy Voskoboinyk int error; 1791bcaed14bSAdrian Chadd 17927453645fSAndriy Voskoboinyk error = rtwn_write_4(sc, R92C_BSSID(id), le32dec(&bssid[0])); 17937453645fSAndriy Voskoboinyk if (error != 0) 17947453645fSAndriy Voskoboinyk return (error); 17957453645fSAndriy Voskoboinyk error = rtwn_write_2(sc, R92C_BSSID(id) + 4, le16dec(&bssid[4])); 1796bcaed14bSAdrian Chadd 17977453645fSAndriy Voskoboinyk return (error); 1798bcaed14bSAdrian Chadd } 1799bcaed14bSAdrian Chadd 1800bcaed14bSAdrian Chadd static int 18017453645fSAndriy Voskoboinyk rtwn_set_macaddr(struct rtwn_softc *sc, const uint8_t *addr, int id) 1802bcaed14bSAdrian Chadd { 18037453645fSAndriy Voskoboinyk int error; 1804bcaed14bSAdrian Chadd 18057453645fSAndriy Voskoboinyk error = rtwn_write_4(sc, R92C_MACID(id), le32dec(&addr[0])); 18067453645fSAndriy Voskoboinyk if (error != 0) 18077453645fSAndriy Voskoboinyk return (error); 18087453645fSAndriy Voskoboinyk error = rtwn_write_2(sc, R92C_MACID(id) + 4, le16dec(&addr[4])); 1809bcaed14bSAdrian Chadd 18107453645fSAndriy Voskoboinyk return (error); 1811bcaed14bSAdrian Chadd } 1812bcaed14bSAdrian Chadd 18137453645fSAndriy Voskoboinyk static struct ieee80211_node * 18147453645fSAndriy Voskoboinyk rtwn_node_alloc(struct ieee80211vap *vap, 18157453645fSAndriy Voskoboinyk const uint8_t mac[IEEE80211_ADDR_LEN]) 18167453645fSAndriy Voskoboinyk { 18177453645fSAndriy Voskoboinyk struct rtwn_node *un; 18187453645fSAndriy Voskoboinyk 18197453645fSAndriy Voskoboinyk un = malloc(sizeof (struct rtwn_node), M_80211_NODE, 18207453645fSAndriy Voskoboinyk M_NOWAIT | M_ZERO); 18217453645fSAndriy Voskoboinyk 18227453645fSAndriy Voskoboinyk if (un == NULL) 18237453645fSAndriy Voskoboinyk return NULL; 18247453645fSAndriy Voskoboinyk 18257453645fSAndriy Voskoboinyk un->id = RTWN_MACID_UNDEFINED; 18267453645fSAndriy Voskoboinyk un->avg_pwdb = -1; 18277453645fSAndriy Voskoboinyk 18287453645fSAndriy Voskoboinyk return &un->ni; 1829bcaed14bSAdrian Chadd } 1830bcaed14bSAdrian Chadd 1831bcaed14bSAdrian Chadd static void 183290589b90SAndriy Voskoboinyk rtwn_newassoc(struct ieee80211_node *ni, int isnew __unused) 1833bcaed14bSAdrian Chadd { 18347453645fSAndriy Voskoboinyk struct rtwn_softc *sc = ni->ni_ic->ic_softc; 18357453645fSAndriy Voskoboinyk struct rtwn_node *un = RTWN_NODE(ni); 18367453645fSAndriy Voskoboinyk int id; 1837bcaed14bSAdrian Chadd 183890589b90SAndriy Voskoboinyk if (un->id != RTWN_MACID_UNDEFINED) 1839bcaed14bSAdrian Chadd return; 1840bcaed14bSAdrian Chadd 18417453645fSAndriy Voskoboinyk RTWN_NT_LOCK(sc); 18427453645fSAndriy Voskoboinyk for (id = 0; id <= sc->macid_limit; id++) { 18437453645fSAndriy Voskoboinyk if (id != RTWN_MACID_BC && sc->node_list[id] == NULL) { 18447453645fSAndriy Voskoboinyk un->id = id; 18457453645fSAndriy Voskoboinyk sc->node_list[id] = ni; 1846bcaed14bSAdrian Chadd break; 1847bcaed14bSAdrian Chadd } 1848bcaed14bSAdrian Chadd } 18497453645fSAndriy Voskoboinyk RTWN_NT_UNLOCK(sc); 18507453645fSAndriy Voskoboinyk 18517453645fSAndriy Voskoboinyk if (id > sc->macid_limit) { 18527453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, "%s: node table is full\n", 18537453645fSAndriy Voskoboinyk __func__); 18547453645fSAndriy Voskoboinyk return; 1855bcaed14bSAdrian Chadd } 1856bcaed14bSAdrian Chadd 18577453645fSAndriy Voskoboinyk /* Notify firmware. */ 18587453645fSAndriy Voskoboinyk id |= RTWN_MACID_VALID; 18597453645fSAndriy Voskoboinyk rtwn_cmd_sleepable(sc, &id, sizeof(id), rtwn_set_media_status); 1860bcaed14bSAdrian Chadd } 1861bcaed14bSAdrian Chadd 1862bcaed14bSAdrian Chadd static void 18637453645fSAndriy Voskoboinyk rtwn_node_free(struct ieee80211_node *ni) 1864bcaed14bSAdrian Chadd { 18657453645fSAndriy Voskoboinyk struct rtwn_softc *sc = ni->ni_ic->ic_softc; 18667453645fSAndriy Voskoboinyk struct rtwn_node *un = RTWN_NODE(ni); 1867bcaed14bSAdrian Chadd 18687453645fSAndriy Voskoboinyk RTWN_NT_LOCK(sc); 18697453645fSAndriy Voskoboinyk if (un->id != RTWN_MACID_UNDEFINED) { 18707453645fSAndriy Voskoboinyk sc->node_list[un->id] = NULL; 18717453645fSAndriy Voskoboinyk rtwn_cmd_sleepable(sc, &un->id, sizeof(un->id), 18727453645fSAndriy Voskoboinyk rtwn_set_media_status); 1873bcaed14bSAdrian Chadd } 18747453645fSAndriy Voskoboinyk RTWN_NT_UNLOCK(sc); 1875bcaed14bSAdrian Chadd 18767453645fSAndriy Voskoboinyk sc->sc_node_free(ni); 1877bcaed14bSAdrian Chadd } 18787453645fSAndriy Voskoboinyk 18797453645fSAndriy Voskoboinyk static void 18807453645fSAndriy Voskoboinyk rtwn_init_beacon_reg(struct rtwn_softc *sc) 18817453645fSAndriy Voskoboinyk { 18827453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_BCN_CTRL(0), R92C_BCN_CTRL_DIS_TSF_UDT0); 18837453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_BCN_CTRL(1), R92C_BCN_CTRL_DIS_TSF_UDT0); 18847453645fSAndriy Voskoboinyk rtwn_write_2(sc, R92C_TBTT_PROHIBIT, 0x6404); 18857453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_DRVERLYINT, 0x05); 18867453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_BCNDMATIM, 0x02); 18877453645fSAndriy Voskoboinyk rtwn_write_2(sc, R92C_BCNTCFG, 0x660f); 1888bcaed14bSAdrian Chadd } 1889bcaed14bSAdrian Chadd 1890798e1ce3SAndriy Voskoboinyk static int 1891798e1ce3SAndriy Voskoboinyk rtwn_init(struct rtwn_softc *sc) 1892bcaed14bSAdrian Chadd { 1893bcaed14bSAdrian Chadd struct ieee80211com *ic = &sc->sc_ic; 1894bcaed14bSAdrian Chadd int i, error; 1895bcaed14bSAdrian Chadd 1896798e1ce3SAndriy Voskoboinyk RTWN_LOCK(sc); 1897798e1ce3SAndriy Voskoboinyk if (sc->sc_flags & RTWN_RUNNING) { 1898798e1ce3SAndriy Voskoboinyk RTWN_UNLOCK(sc); 18997453645fSAndriy Voskoboinyk return (0); 1900798e1ce3SAndriy Voskoboinyk } 19017453645fSAndriy Voskoboinyk sc->sc_flags |= RTWN_STARTED; 1902bcaed14bSAdrian Chadd 1903bcaed14bSAdrian Chadd /* Power on adapter. */ 1904bcaed14bSAdrian Chadd error = rtwn_power_on(sc); 19057453645fSAndriy Voskoboinyk if (error != 0) 19067453645fSAndriy Voskoboinyk goto fail; 19077453645fSAndriy Voskoboinyk 19087453645fSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE 19097453645fSAndriy Voskoboinyk /* Load 8051 microcode. */ 19107453645fSAndriy Voskoboinyk error = rtwn_load_firmware(sc); 19117453645fSAndriy Voskoboinyk if (error == 0) 19127453645fSAndriy Voskoboinyk sc->sc_flags |= RTWN_FW_LOADED; 19137453645fSAndriy Voskoboinyk 19147453645fSAndriy Voskoboinyk /* Init firmware commands ring. */ 19157453645fSAndriy Voskoboinyk sc->fwcur = 0; 19167453645fSAndriy Voskoboinyk #endif 19177453645fSAndriy Voskoboinyk 19187453645fSAndriy Voskoboinyk /* Initialize MAC block. */ 19197453645fSAndriy Voskoboinyk error = rtwn_mac_init(sc); 1920bcaed14bSAdrian Chadd if (error != 0) { 19217453645fSAndriy Voskoboinyk device_printf(sc->sc_dev, 19227453645fSAndriy Voskoboinyk "%s: error while initializing MAC block\n", __func__); 1923bcaed14bSAdrian Chadd goto fail; 1924bcaed14bSAdrian Chadd } 1925bcaed14bSAdrian Chadd 1926bcaed14bSAdrian Chadd /* Initialize DMA. */ 1927bcaed14bSAdrian Chadd error = rtwn_dma_init(sc); 19287453645fSAndriy Voskoboinyk if (error != 0) 19297453645fSAndriy Voskoboinyk goto fail; 19307453645fSAndriy Voskoboinyk 19317453645fSAndriy Voskoboinyk /* Drop incorrect TX (USB). */ 19327453645fSAndriy Voskoboinyk rtwn_drop_incorrect_tx(sc); 19337453645fSAndriy Voskoboinyk 19347453645fSAndriy Voskoboinyk /* Set info size in Rx descriptors (in 64-bit words). */ 19357453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_RX_DRVINFO_SZ, R92C_RX_DRVINFO_SZ_DEF); 19367453645fSAndriy Voskoboinyk 19377453645fSAndriy Voskoboinyk /* Init interrupts. */ 19387453645fSAndriy Voskoboinyk rtwn_init_intr(sc); 19397453645fSAndriy Voskoboinyk 19407453645fSAndriy Voskoboinyk for (i = 0; i < nitems(sc->vaps); i++) { 19417453645fSAndriy Voskoboinyk struct rtwn_vap *uvp = sc->vaps[i]; 19427453645fSAndriy Voskoboinyk 19437453645fSAndriy Voskoboinyk /* Set initial network type. */ 19447453645fSAndriy Voskoboinyk rtwn_set_mode(sc, R92C_MSR_NOLINK, i); 19457453645fSAndriy Voskoboinyk 19467453645fSAndriy Voskoboinyk if (uvp == NULL) 19477453645fSAndriy Voskoboinyk continue; 19487453645fSAndriy Voskoboinyk 19497453645fSAndriy Voskoboinyk /* Set MAC address. */ 19507453645fSAndriy Voskoboinyk error = rtwn_set_macaddr(sc, uvp->vap.iv_myaddr, uvp->id); 19517453645fSAndriy Voskoboinyk if (error != 0) 1952bcaed14bSAdrian Chadd goto fail; 1953bcaed14bSAdrian Chadd } 1954bcaed14bSAdrian Chadd 19557453645fSAndriy Voskoboinyk /* Initialize Rx filter. */ 1956bcaed14bSAdrian Chadd rtwn_rxfilter_init(sc); 1957bcaed14bSAdrian Chadd 1958bcaed14bSAdrian Chadd /* Set short/long retry limits. */ 1959bcaed14bSAdrian Chadd rtwn_write_2(sc, R92C_RL, 19607453645fSAndriy Voskoboinyk SM(R92C_RL_SRL, 0x30) | SM(R92C_RL_LRL, 0x30)); 1961bcaed14bSAdrian Chadd 1962bcaed14bSAdrian Chadd /* Initialize EDCA parameters. */ 19637453645fSAndriy Voskoboinyk rtwn_init_edca(sc); 1964bcaed14bSAdrian Chadd 19657453645fSAndriy Voskoboinyk rtwn_setbits_1(sc, R92C_FWHW_TXQ_CTRL, 0, 19667453645fSAndriy Voskoboinyk R92C_FWHW_TXQ_CTRL_AMPDU_RTY_NEW); 1967bcaed14bSAdrian Chadd /* Set ACK timeout. */ 19687453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_ACKTO, sc->ackto); 19697453645fSAndriy Voskoboinyk 19707453645fSAndriy Voskoboinyk /* Setup aggregation. */ 19717453645fSAndriy Voskoboinyk /* Tx aggregation. */ 19727453645fSAndriy Voskoboinyk rtwn_init_tx_agg(sc); 19737453645fSAndriy Voskoboinyk rtwn_init_rx_agg(sc); 1974bcaed14bSAdrian Chadd 1975bcaed14bSAdrian Chadd /* Initialize beacon parameters. */ 19767453645fSAndriy Voskoboinyk rtwn_init_beacon_reg(sc); 1977bcaed14bSAdrian Chadd 19787453645fSAndriy Voskoboinyk /* Init A-MPDU parameters. */ 19797453645fSAndriy Voskoboinyk rtwn_init_ampdu(sc); 1980bcaed14bSAdrian Chadd 19817453645fSAndriy Voskoboinyk /* Init MACTXEN / MACRXEN after setting RxFF boundary. */ 19827453645fSAndriy Voskoboinyk rtwn_setbits_1(sc, R92C_CR, 0, R92C_CR_MACTXEN | R92C_CR_MACRXEN); 1983bcaed14bSAdrian Chadd 19847453645fSAndriy Voskoboinyk /* Initialize BB/RF blocks. */ 19857453645fSAndriy Voskoboinyk rtwn_init_bb(sc); 19867453645fSAndriy Voskoboinyk rtwn_init_rf(sc); 1987bcaed14bSAdrian Chadd 19887453645fSAndriy Voskoboinyk /* Initialize wireless band. */ 19897453645fSAndriy Voskoboinyk rtwn_set_chan(sc, ic->ic_curchan); 1990bcaed14bSAdrian Chadd 1991bcaed14bSAdrian Chadd /* Clear per-station keys table. */ 19927453645fSAndriy Voskoboinyk rtwn_init_cam(sc); 1993bcaed14bSAdrian Chadd 19947453645fSAndriy Voskoboinyk /* Enable decryption / encryption. */ 19957453645fSAndriy Voskoboinyk rtwn_init_seccfg(sc); 1996bcaed14bSAdrian Chadd 19977453645fSAndriy Voskoboinyk /* Install static keys (if any). */ 19987453645fSAndriy Voskoboinyk for (i = 0; i < nitems(sc->vaps); i++) { 19997453645fSAndriy Voskoboinyk if (sc->vaps[i] != NULL) { 20007453645fSAndriy Voskoboinyk error = rtwn_init_static_keys(sc, sc->vaps[i]); 2001798e1ce3SAndriy Voskoboinyk if (error != 0) 20027453645fSAndriy Voskoboinyk goto fail; 20037453645fSAndriy Voskoboinyk } 2004bcaed14bSAdrian Chadd } 2005bcaed14bSAdrian Chadd 20067453645fSAndriy Voskoboinyk /* Initialize antenna selection. */ 20077453645fSAndriy Voskoboinyk rtwn_init_antsel(sc); 2008bcaed14bSAdrian Chadd 20097453645fSAndriy Voskoboinyk /* Enable hardware sequence numbering. */ 20107453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_HWSEQ_CTRL, R92C_TX_QUEUE_ALL); 2011bcaed14bSAdrian Chadd 20127453645fSAndriy Voskoboinyk /* Disable BAR. */ 20137453645fSAndriy Voskoboinyk rtwn_write_4(sc, R92C_BAR_MODE_CTRL, 0x0201ffff); 2014798e1ce3SAndriy Voskoboinyk 20157453645fSAndriy Voskoboinyk /* NAV limit. */ 20167453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_NAV_UPPER, 0); 2017bcaed14bSAdrian Chadd 20187453645fSAndriy Voskoboinyk /* Initialize GPIO setting. */ 20197453645fSAndriy Voskoboinyk rtwn_setbits_1(sc, R92C_GPIO_MUXCFG, R92C_GPIO_MUXCFG_ENBT, 0); 2020bcaed14bSAdrian Chadd 20217453645fSAndriy Voskoboinyk /* Initialize MRR. */ 20227453645fSAndriy Voskoboinyk rtwn_mrr_init(sc); 2023bcaed14bSAdrian Chadd 20247453645fSAndriy Voskoboinyk /* Device-specific post initialization. */ 20257453645fSAndriy Voskoboinyk rtwn_post_init(sc); 20267453645fSAndriy Voskoboinyk 20277453645fSAndriy Voskoboinyk rtwn_start_xfers(sc); 20287453645fSAndriy Voskoboinyk 20297453645fSAndriy Voskoboinyk #ifndef D4054 20307453645fSAndriy Voskoboinyk callout_reset(&sc->sc_watchdog_to, hz, rtwn_watchdog, sc); 20317453645fSAndriy Voskoboinyk #endif 20327453645fSAndriy Voskoboinyk 20337453645fSAndriy Voskoboinyk sc->sc_flags |= RTWN_RUNNING; 20347453645fSAndriy Voskoboinyk fail: 20357453645fSAndriy Voskoboinyk RTWN_UNLOCK(sc); 20367453645fSAndriy Voskoboinyk 20377453645fSAndriy Voskoboinyk return (error); 2038bcaed14bSAdrian Chadd } 2039bcaed14bSAdrian Chadd 2040bcaed14bSAdrian Chadd static void 2041bcaed14bSAdrian Chadd rtwn_stop(struct rtwn_softc *sc) 2042bcaed14bSAdrian Chadd { 2043bcaed14bSAdrian Chadd 2044bcaed14bSAdrian Chadd RTWN_LOCK(sc); 20457453645fSAndriy Voskoboinyk if (!(sc->sc_flags & RTWN_STARTED)) { 2046bcaed14bSAdrian Chadd RTWN_UNLOCK(sc); 2047bcaed14bSAdrian Chadd return; 2048bcaed14bSAdrian Chadd } 2049bcaed14bSAdrian Chadd 20507453645fSAndriy Voskoboinyk #ifndef D4054 20517453645fSAndriy Voskoboinyk callout_stop(&sc->sc_watchdog_to); 20521318032eSAndriy Voskoboinyk sc->sc_tx_timer = 0; 20537453645fSAndriy Voskoboinyk #endif 20547453645fSAndriy Voskoboinyk sc->sc_flags &= ~(RTWN_STARTED | RTWN_RUNNING | RTWN_FW_LOADED); 20557453645fSAndriy Voskoboinyk sc->sc_flags &= ~RTWN_TEMP_MEASURED; 20567453645fSAndriy Voskoboinyk sc->fwver = 0; 20577453645fSAndriy Voskoboinyk sc->thcal_temp = 0; 20587453645fSAndriy Voskoboinyk sc->cur_bcnq_id = RTWN_VAP_ID_INVALID; 205909606165SAndriy Voskoboinyk bzero(&sc->last_physt, sizeof(sc->last_physt)); 2060bcaed14bSAdrian Chadd 20617453645fSAndriy Voskoboinyk #ifdef D4054 20627453645fSAndriy Voskoboinyk ieee80211_tx_watchdog_stop(&sc->sc_ic); 20637453645fSAndriy Voskoboinyk #endif 2064bcaed14bSAdrian Chadd 20657453645fSAndriy Voskoboinyk rtwn_abort_xfers(sc); 20667453645fSAndriy Voskoboinyk rtwn_drain_mbufq(sc); 20677453645fSAndriy Voskoboinyk rtwn_power_off(sc); 20687453645fSAndriy Voskoboinyk rtwn_reset_lists(sc, NULL); 2069bcaed14bSAdrian Chadd RTWN_UNLOCK(sc); 2070bcaed14bSAdrian Chadd } 20717453645fSAndriy Voskoboinyk 20727453645fSAndriy Voskoboinyk MODULE_VERSION(rtwn, 2); 20737453645fSAndriy Voskoboinyk MODULE_DEPEND(rtwn, wlan, 1, 1, 1); 20747453645fSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE 20757453645fSAndriy Voskoboinyk MODULE_DEPEND(rtwn, firmware, 1, 1, 1); 20767453645fSAndriy Voskoboinyk #endif 2077