xref: /freebsd-src/sys/dev/rtwn/if_rtwn.c (revision f45f66fadacc2efff3c2ff64e3528b8dbaad3673)
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