xref: /freebsd/sys/dev/rtwn/if_rtwn.c (revision 713db49d)
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
rtwn_attach(struct rtwn_softc * sc)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 */
235bcaed14bSAdrian Chadd 		;
236bcaed14bSAdrian Chadd 
2377453645fSAndriy Voskoboinyk 	if (sc->sc_hwcrypto != RTWN_CRYPTO_SW) {
2387453645fSAndriy Voskoboinyk 		ic->ic_cryptocaps =
2397453645fSAndriy Voskoboinyk 		    IEEE80211_CRYPTO_WEP |
2407453645fSAndriy Voskoboinyk 		    IEEE80211_CRYPTO_TKIP |
2417453645fSAndriy Voskoboinyk 		    IEEE80211_CRYPTO_AES_CCM;
2427453645fSAndriy Voskoboinyk 	}
2437453645fSAndriy Voskoboinyk 
2447453645fSAndriy Voskoboinyk 	ic->ic_htcaps =
2457453645fSAndriy Voskoboinyk 	      IEEE80211_HTCAP_SHORTGI20		/* short GI in 20MHz */
2467453645fSAndriy Voskoboinyk 	    | IEEE80211_HTCAP_MAXAMSDU_3839	/* max A-MSDU length */
2477453645fSAndriy Voskoboinyk 	    | IEEE80211_HTCAP_SMPS_OFF		/* SM PS mode disabled */
2487453645fSAndriy Voskoboinyk 	    /* s/w capabilities */
2497453645fSAndriy Voskoboinyk 	    | IEEE80211_HTC_HT			/* HT operation */
2507453645fSAndriy Voskoboinyk 	    | IEEE80211_HTC_AMPDU		/* A-MPDU tx */
2517453645fSAndriy Voskoboinyk 	    | IEEE80211_HTC_AMSDU		/* A-MSDU tx */
2527453645fSAndriy Voskoboinyk 	    ;
2537453645fSAndriy Voskoboinyk 
2547453645fSAndriy Voskoboinyk 	if (sc->sc_ht40) {
2557453645fSAndriy Voskoboinyk 		ic->ic_htcaps |=
2567453645fSAndriy Voskoboinyk 		      IEEE80211_HTCAP_CHWIDTH40	/* 40 MHz channel width */
2577453645fSAndriy Voskoboinyk 		    | IEEE80211_HTCAP_SHORTGI40	/* short GI in 40MHz */
2587453645fSAndriy Voskoboinyk 		    ;
2597453645fSAndriy Voskoboinyk 	}
2607453645fSAndriy Voskoboinyk 
2617453645fSAndriy Voskoboinyk 	ic->ic_txstream = sc->ntxchains;
2627453645fSAndriy Voskoboinyk 	ic->ic_rxstream = sc->nrxchains;
2637453645fSAndriy Voskoboinyk 
2647453645fSAndriy Voskoboinyk 	/* Enable TX watchdog */
2657453645fSAndriy Voskoboinyk #ifdef D4054
2667453645fSAndriy Voskoboinyk 	ic->ic_flags_ext |= IEEE80211_FEXT_WATCHDOG;
2677453645fSAndriy Voskoboinyk #endif
2687453645fSAndriy Voskoboinyk 
2697453645fSAndriy Voskoboinyk 	/* Adjust capabilities. */
2707453645fSAndriy Voskoboinyk 	rtwn_adj_devcaps(sc);
271a7c31fe1SAndriy Voskoboinyk 
272a7c31fe1SAndriy Voskoboinyk 	rtwn_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
273a7c31fe1SAndriy Voskoboinyk 	    ic->ic_channels);
274bcaed14bSAdrian Chadd 
2757453645fSAndriy Voskoboinyk 	/* XXX TODO: setup regdomain if R92C_CHANNEL_PLAN_BY_HW bit is set. */
276bcaed14bSAdrian Chadd 
2777453645fSAndriy Voskoboinyk 	ieee80211_ifattach(ic);
2787453645fSAndriy Voskoboinyk 	ic->ic_raw_xmit = rtwn_raw_xmit;
279bcaed14bSAdrian Chadd 	ic->ic_scan_start = rtwn_scan_start;
2807453645fSAndriy Voskoboinyk 	sc->sc_scan_curchan = ic->ic_scan_curchan;
2817453645fSAndriy Voskoboinyk 	ic->ic_scan_curchan = rtwn_scan_curchan;
282bcaed14bSAdrian Chadd 	ic->ic_scan_end = rtwn_scan_end;
283a7c31fe1SAndriy Voskoboinyk 	ic->ic_getradiocaps = rtwn_getradiocaps;
2847453645fSAndriy Voskoboinyk 	ic->ic_update_chw = rtwn_update_chw;
285bcaed14bSAdrian Chadd 	ic->ic_set_channel = rtwn_set_channel;
286bcaed14bSAdrian Chadd 	ic->ic_transmit = rtwn_transmit;
287bcaed14bSAdrian Chadd 	ic->ic_parent = rtwn_parent;
288bcaed14bSAdrian Chadd 	ic->ic_vap_create = rtwn_vap_create;
289bcaed14bSAdrian Chadd 	ic->ic_vap_delete = rtwn_vap_delete;
2907453645fSAndriy Voskoboinyk 	ic->ic_wme.wme_update = rtwn_wme_update;
2917453645fSAndriy Voskoboinyk 	ic->ic_updateslot = rtwn_update_slot;
2927453645fSAndriy Voskoboinyk 	ic->ic_update_promisc = rtwn_update_promisc;
2937453645fSAndriy Voskoboinyk 	ic->ic_update_mcast = rtwn_update_mcast;
2947453645fSAndriy Voskoboinyk 	ic->ic_node_alloc = rtwn_node_alloc;
2957453645fSAndriy Voskoboinyk 	ic->ic_newassoc = rtwn_newassoc;
2967453645fSAndriy Voskoboinyk 	sc->sc_node_free = ic->ic_node_free;
2977453645fSAndriy Voskoboinyk 	ic->ic_node_free = rtwn_node_free;
298bcaed14bSAdrian Chadd 
2997453645fSAndriy Voskoboinyk 	rtwn_postattach(sc);
3007453645fSAndriy Voskoboinyk 	rtwn_radiotap_attach(sc);
301bcaed14bSAdrian Chadd 
302bcaed14bSAdrian Chadd 	if (bootverbose)
303bcaed14bSAdrian Chadd 		ieee80211_announce(ic);
304bcaed14bSAdrian Chadd 
305bcaed14bSAdrian Chadd 	return (0);
306bcaed14bSAdrian Chadd 
3077453645fSAndriy Voskoboinyk detach:
3087453645fSAndriy Voskoboinyk 	return (ENXIO);			/* failure */
309bcaed14bSAdrian Chadd }
310bcaed14bSAdrian Chadd 
311bcaed14bSAdrian Chadd static void
rtwn_radiotap_attach(struct rtwn_softc * sc)3127453645fSAndriy Voskoboinyk rtwn_radiotap_attach(struct rtwn_softc *sc)
313bcaed14bSAdrian Chadd {
3147453645fSAndriy Voskoboinyk 	struct rtwn_rx_radiotap_header *rxtap = &sc->sc_rxtap;
3157453645fSAndriy Voskoboinyk 	struct rtwn_tx_radiotap_header *txtap = &sc->sc_txtap;
316bcaed14bSAdrian Chadd 
3177453645fSAndriy Voskoboinyk 	ieee80211_radiotap_attach(&sc->sc_ic,
3187453645fSAndriy Voskoboinyk 	    &txtap->wt_ihdr, sizeof(*txtap), RTWN_TX_RADIOTAP_PRESENT,
3197453645fSAndriy Voskoboinyk 	    &rxtap->wr_ihdr, sizeof(*rxtap), RTWN_RX_RADIOTAP_PRESENT);
3207453645fSAndriy Voskoboinyk }
3217453645fSAndriy Voskoboinyk 
3227453645fSAndriy Voskoboinyk void
rtwn_sysctlattach(struct rtwn_softc * sc)3237453645fSAndriy Voskoboinyk rtwn_sysctlattach(struct rtwn_softc *sc)
3247453645fSAndriy Voskoboinyk {
3257453645fSAndriy Voskoboinyk 	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
3267453645fSAndriy Voskoboinyk 	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
3277453645fSAndriy Voskoboinyk 
3287453645fSAndriy Voskoboinyk #if 1
3297453645fSAndriy Voskoboinyk 	sc->sc_ht40 = 0;
3307453645fSAndriy Voskoboinyk 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
3317453645fSAndriy Voskoboinyk 	    "ht40", CTLFLAG_RDTUN, &sc->sc_ht40,
3327453645fSAndriy Voskoboinyk 	    sc->sc_ht40, "Enable 40 MHz mode support");
3337453645fSAndriy Voskoboinyk #endif
3347453645fSAndriy Voskoboinyk 
3357453645fSAndriy Voskoboinyk #ifdef RTWN_DEBUG
3367453645fSAndriy Voskoboinyk 	SYSCTL_ADD_U32(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
3377453645fSAndriy Voskoboinyk 	    "debug", CTLFLAG_RWTUN, &sc->sc_debug, sc->sc_debug,
3387453645fSAndriy Voskoboinyk 	    "Control debugging printfs");
3397453645fSAndriy Voskoboinyk #endif
3407453645fSAndriy Voskoboinyk 
3417453645fSAndriy Voskoboinyk 	sc->sc_hwcrypto = RTWN_CRYPTO_PAIR;
3427453645fSAndriy Voskoboinyk 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
3437453645fSAndriy Voskoboinyk 	    "hwcrypto", CTLFLAG_RDTUN, &sc->sc_hwcrypto,
3447453645fSAndriy Voskoboinyk 	    sc->sc_hwcrypto, "Enable h/w crypto: "
3457453645fSAndriy Voskoboinyk 	    "0 - disable, 1 - pairwise keys, 2 - all keys");
3467453645fSAndriy Voskoboinyk 	if (sc->sc_hwcrypto >= RTWN_CRYPTO_MAX)
3477453645fSAndriy Voskoboinyk 		sc->sc_hwcrypto = RTWN_CRYPTO_FULL;
3487453645fSAndriy Voskoboinyk 
3497453645fSAndriy Voskoboinyk 	sc->sc_ratectl_sysctl = RTWN_RATECTL_NET80211;
3507453645fSAndriy Voskoboinyk 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
3517453645fSAndriy Voskoboinyk 	    "ratectl", CTLFLAG_RDTUN, &sc->sc_ratectl_sysctl,
3527453645fSAndriy Voskoboinyk 	    sc->sc_ratectl_sysctl, "Select rate control mechanism: "
3537453645fSAndriy Voskoboinyk 	    "0 - disabled, 1 - via net80211, 2 - via firmware");
3547453645fSAndriy Voskoboinyk 	if (sc->sc_ratectl_sysctl >= RTWN_RATECTL_MAX)
3557453645fSAndriy Voskoboinyk 		sc->sc_ratectl_sysctl = RTWN_RATECTL_FW;
3567453645fSAndriy Voskoboinyk 
3577453645fSAndriy Voskoboinyk 	sc->sc_ratectl = sc->sc_ratectl_sysctl;
3587453645fSAndriy Voskoboinyk 	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
3597453645fSAndriy Voskoboinyk 	    "ratectl_selected", CTLFLAG_RD, &sc->sc_ratectl,
3607453645fSAndriy Voskoboinyk 	    sc->sc_ratectl,
3617453645fSAndriy Voskoboinyk 	    "Currently selected rate control mechanism (by the driver)");
3627453645fSAndriy Voskoboinyk }
3637453645fSAndriy Voskoboinyk 
3647453645fSAndriy Voskoboinyk void
rtwn_detach(struct rtwn_softc * sc)3657453645fSAndriy Voskoboinyk rtwn_detach(struct rtwn_softc *sc)
3667453645fSAndriy Voskoboinyk {
3677453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
3687453645fSAndriy Voskoboinyk 
3697453645fSAndriy Voskoboinyk 	if (ic->ic_softc == sc) {
3707453645fSAndriy Voskoboinyk 		/* Stop command queue. */
3717453645fSAndriy Voskoboinyk 		RTWN_CMDQ_LOCK(sc);
3727453645fSAndriy Voskoboinyk 		sc->sc_detached = 1;
3737453645fSAndriy Voskoboinyk 		RTWN_CMDQ_UNLOCK(sc);
3747453645fSAndriy Voskoboinyk 
3757453645fSAndriy Voskoboinyk 		ieee80211_draintask(ic, &sc->cmdq_task);
3767453645fSAndriy Voskoboinyk 		ieee80211_ifdetach(ic);
3777453645fSAndriy Voskoboinyk 	}
3787453645fSAndriy Voskoboinyk 
3797453645fSAndriy Voskoboinyk 	rtwn_cmdq_destroy(sc);
3807453645fSAndriy Voskoboinyk 	if (RTWN_NT_LOCK_INITIALIZED(sc))
3817453645fSAndriy Voskoboinyk 		RTWN_NT_LOCK_DESTROY(sc);
3827453645fSAndriy Voskoboinyk }
3837453645fSAndriy Voskoboinyk 
3847453645fSAndriy Voskoboinyk void
rtwn_suspend(struct rtwn_softc * sc)3857453645fSAndriy Voskoboinyk rtwn_suspend(struct rtwn_softc *sc)
3867453645fSAndriy Voskoboinyk {
3877453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
3887453645fSAndriy Voskoboinyk 
3897453645fSAndriy Voskoboinyk 	ieee80211_suspend_all(ic);
3907453645fSAndriy Voskoboinyk }
3917453645fSAndriy Voskoboinyk 
3927453645fSAndriy Voskoboinyk void
rtwn_resume(struct rtwn_softc * sc)3937453645fSAndriy Voskoboinyk rtwn_resume(struct rtwn_softc *sc)
3947453645fSAndriy Voskoboinyk {
3957453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
3967453645fSAndriy Voskoboinyk 
3977453645fSAndriy Voskoboinyk 	ieee80211_resume_all(ic);
398bcaed14bSAdrian Chadd }
399bcaed14bSAdrian Chadd 
400bcaed14bSAdrian Chadd static void
rtwn_vap_decrement_counters(struct rtwn_softc * sc,enum ieee80211_opmode opmode,int id)4017453645fSAndriy Voskoboinyk rtwn_vap_decrement_counters(struct rtwn_softc *sc,
4027453645fSAndriy Voskoboinyk     enum ieee80211_opmode opmode, int id)
403bcaed14bSAdrian Chadd {
404bcaed14bSAdrian Chadd 
4057453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
4067453645fSAndriy Voskoboinyk 
4077453645fSAndriy Voskoboinyk 	if (id != RTWN_VAP_ID_INVALID) {
4087453645fSAndriy Voskoboinyk 		KASSERT(id == 0 || id == 1, ("wrong vap id %d!\n", id));
4097453645fSAndriy Voskoboinyk 		KASSERT(sc->vaps[id] != NULL, ("vap pointer is NULL\n"));
4107453645fSAndriy Voskoboinyk 		sc->vaps[id] = NULL;
411bcaed14bSAdrian Chadd 	}
412bcaed14bSAdrian Chadd 
4137453645fSAndriy Voskoboinyk 	switch (opmode) {
4147453645fSAndriy Voskoboinyk 	case IEEE80211_M_HOSTAP:
4157453645fSAndriy Voskoboinyk 		sc->ap_vaps--;
4167453645fSAndriy Voskoboinyk 		/* FALLTHROUGH */
4177453645fSAndriy Voskoboinyk 	case IEEE80211_M_IBSS:
4187453645fSAndriy Voskoboinyk 		sc->bcn_vaps--;
4197453645fSAndriy Voskoboinyk 		/* FALLTHROUGH */
4207453645fSAndriy Voskoboinyk 	case IEEE80211_M_STA:
4217453645fSAndriy Voskoboinyk 		sc->nvaps--;
4227453645fSAndriy Voskoboinyk 		break;
4237453645fSAndriy Voskoboinyk 	case IEEE80211_M_MONITOR:
4247453645fSAndriy Voskoboinyk 		sc->mon_vaps--;
4257453645fSAndriy Voskoboinyk 		break;
4267453645fSAndriy Voskoboinyk 	default:
4277453645fSAndriy Voskoboinyk 		KASSERT(0, ("wrong opmode %d\n", opmode));
4287453645fSAndriy Voskoboinyk 		break;
429bcaed14bSAdrian Chadd 	}
430bcaed14bSAdrian Chadd 
4317453645fSAndriy Voskoboinyk 	KASSERT(sc->vaps_running >= 0 && sc->monvaps_running >= 0,
4327453645fSAndriy Voskoboinyk 	    ("number of running vaps is negative (vaps %d, monvaps %d)\n",
4337453645fSAndriy Voskoboinyk 	    sc->vaps_running, sc->monvaps_running));
4347453645fSAndriy Voskoboinyk 	KASSERT(sc->vaps_running - sc->monvaps_running <= RTWN_PORT_COUNT,
4357453645fSAndriy Voskoboinyk 	    ("number of running vaps is too big (vaps %d, monvaps %d)\n",
4367453645fSAndriy Voskoboinyk 	    sc->vaps_running, sc->monvaps_running));
437bcaed14bSAdrian Chadd 
4387453645fSAndriy Voskoboinyk 	KASSERT(sc->nvaps >= 0 && sc->nvaps <= RTWN_PORT_COUNT,
4397453645fSAndriy Voskoboinyk 	    ("wrong value %d for nvaps\n", sc->nvaps));
4407453645fSAndriy Voskoboinyk 	KASSERT(sc->mon_vaps >= 0, ("mon_vaps is negative (%d)\n",
4417453645fSAndriy Voskoboinyk 	    sc->mon_vaps));
4427453645fSAndriy Voskoboinyk 	KASSERT(sc->bcn_vaps >= 0 && ((RTWN_CHIP_HAS_BCNQ1(sc) &&
4437453645fSAndriy Voskoboinyk 	    sc->bcn_vaps <= RTWN_PORT_COUNT) || sc->bcn_vaps <= 1),
4447453645fSAndriy Voskoboinyk 	    ("bcn_vaps value %d is wrong\n", sc->bcn_vaps));
4457453645fSAndriy Voskoboinyk 	KASSERT(sc->ap_vaps >= 0 && ((RTWN_CHIP_HAS_BCNQ1(sc) &&
4467453645fSAndriy Voskoboinyk 	    sc->ap_vaps <= RTWN_PORT_COUNT) || sc->ap_vaps <= 1),
4477453645fSAndriy Voskoboinyk 	    ("ap_vaps value %d is wrong\n", sc->ap_vaps));
448bcaed14bSAdrian Chadd }
449bcaed14bSAdrian Chadd 
450bcaed14bSAdrian Chadd static void
rtwn_set_ic_opmode(struct rtwn_softc * sc)4517453645fSAndriy Voskoboinyk rtwn_set_ic_opmode(struct rtwn_softc *sc)
452bcaed14bSAdrian Chadd {
4537453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
454bcaed14bSAdrian Chadd 
4557453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
456bcaed14bSAdrian Chadd 
4577453645fSAndriy Voskoboinyk 	/* for ieee80211_reset_erp() */
4587453645fSAndriy Voskoboinyk 	if (sc->bcn_vaps - sc->ap_vaps > 0)
4597453645fSAndriy Voskoboinyk 		ic->ic_opmode = IEEE80211_M_IBSS;
4607453645fSAndriy Voskoboinyk 	else if (sc->ap_vaps > 0)
4617453645fSAndriy Voskoboinyk 		ic->ic_opmode = IEEE80211_M_HOSTAP;
4627453645fSAndriy Voskoboinyk 	else if (sc->nvaps > 0)
4637453645fSAndriy Voskoboinyk 		ic->ic_opmode = IEEE80211_M_STA;
4647453645fSAndriy Voskoboinyk 	else
4657453645fSAndriy Voskoboinyk 		ic->ic_opmode = IEEE80211_M_MONITOR;
466bcaed14bSAdrian Chadd }
467bcaed14bSAdrian Chadd 
468bcaed14bSAdrian Chadd static struct ieee80211vap *
rtwn_vap_create(struct ieee80211com * ic,const char name[IFNAMSIZ],int unit,enum ieee80211_opmode opmode,int flags,const uint8_t bssid[IEEE80211_ADDR_LEN],const uint8_t mac[IEEE80211_ADDR_LEN])469bcaed14bSAdrian Chadd rtwn_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit,
470bcaed14bSAdrian Chadd     enum ieee80211_opmode opmode, int flags,
471bcaed14bSAdrian Chadd     const uint8_t bssid[IEEE80211_ADDR_LEN],
472bcaed14bSAdrian Chadd     const uint8_t mac[IEEE80211_ADDR_LEN])
473bcaed14bSAdrian Chadd {
4747453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = ic->ic_softc;
4757453645fSAndriy Voskoboinyk 	struct rtwn_vap *uvp;
476bcaed14bSAdrian Chadd 	struct ieee80211vap *vap;
4777453645fSAndriy Voskoboinyk 	int id = RTWN_VAP_ID_INVALID;
478bcaed14bSAdrian Chadd 
4797453645fSAndriy Voskoboinyk 	RTWN_LOCK(sc);
4807453645fSAndriy Voskoboinyk 	KASSERT(sc->nvaps <= RTWN_PORT_COUNT,
4817453645fSAndriy Voskoboinyk 	    ("nvaps overflow (%d > %d)\n", sc->nvaps, RTWN_PORT_COUNT));
4827453645fSAndriy Voskoboinyk 	KASSERT(sc->ap_vaps <= RTWN_PORT_COUNT,
4837453645fSAndriy Voskoboinyk 	    ("ap_vaps overflow (%d > %d)\n", sc->ap_vaps, RTWN_PORT_COUNT));
4847453645fSAndriy Voskoboinyk 	KASSERT(sc->bcn_vaps <= RTWN_PORT_COUNT,
4857453645fSAndriy Voskoboinyk 	    ("bcn_vaps overflow (%d > %d)\n", sc->bcn_vaps, RTWN_PORT_COUNT));
486bcaed14bSAdrian Chadd 
4877453645fSAndriy Voskoboinyk 	if (opmode != IEEE80211_M_MONITOR) {
4887453645fSAndriy Voskoboinyk 		switch (sc->nvaps) {
4897453645fSAndriy Voskoboinyk 		case 0:
4907453645fSAndriy Voskoboinyk 			id = 0;
4917453645fSAndriy Voskoboinyk 			break;
4927453645fSAndriy Voskoboinyk 		case 1:
4937453645fSAndriy Voskoboinyk 			if (sc->vaps[1] == NULL)
4947453645fSAndriy Voskoboinyk 				id = 1;
4957453645fSAndriy Voskoboinyk 			else if (sc->vaps[0] == NULL)
4967453645fSAndriy Voskoboinyk 				id = 0;
4977453645fSAndriy Voskoboinyk 			KASSERT(id != RTWN_VAP_ID_INVALID,
4987453645fSAndriy Voskoboinyk 			    ("no free ports left\n"));
4997453645fSAndriy Voskoboinyk 			break;
5007453645fSAndriy Voskoboinyk 		case 2:
5017453645fSAndriy Voskoboinyk 		default:
5027453645fSAndriy Voskoboinyk 			goto fail;
5037453645fSAndriy Voskoboinyk 		}
5047453645fSAndriy Voskoboinyk 
5057453645fSAndriy Voskoboinyk 		if (opmode == IEEE80211_M_IBSS ||
5067453645fSAndriy Voskoboinyk 		    opmode == IEEE80211_M_HOSTAP) {
5077453645fSAndriy Voskoboinyk 			if ((sc->bcn_vaps == 1 && !RTWN_CHIP_HAS_BCNQ1(sc)) ||
5087453645fSAndriy Voskoboinyk 			    sc->bcn_vaps == RTWN_PORT_COUNT)
5097453645fSAndriy Voskoboinyk 				goto fail;
5107453645fSAndriy Voskoboinyk 		}
5117453645fSAndriy Voskoboinyk 	}
5127453645fSAndriy Voskoboinyk 
5137453645fSAndriy Voskoboinyk 	switch (opmode) {
5147453645fSAndriy Voskoboinyk 	case IEEE80211_M_HOSTAP:
5157453645fSAndriy Voskoboinyk 		sc->ap_vaps++;
5167453645fSAndriy Voskoboinyk 		/* FALLTHROUGH */
5177453645fSAndriy Voskoboinyk 	case IEEE80211_M_IBSS:
5187453645fSAndriy Voskoboinyk 		sc->bcn_vaps++;
5197453645fSAndriy Voskoboinyk 		/* FALLTHROUGH */
5207453645fSAndriy Voskoboinyk 	case IEEE80211_M_STA:
5217453645fSAndriy Voskoboinyk 		sc->nvaps++;
5227453645fSAndriy Voskoboinyk 		break;
5237453645fSAndriy Voskoboinyk 	case IEEE80211_M_MONITOR:
5247453645fSAndriy Voskoboinyk 		sc->mon_vaps++;
5257453645fSAndriy Voskoboinyk 		break;
5267453645fSAndriy Voskoboinyk 	default:
5277453645fSAndriy Voskoboinyk 		KASSERT(0, ("unknown opmode %d\n", opmode));
5287453645fSAndriy Voskoboinyk 		goto fail;
5297453645fSAndriy Voskoboinyk 	}
5307453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
5317453645fSAndriy Voskoboinyk 
5327453645fSAndriy Voskoboinyk 	uvp = malloc(sizeof(struct rtwn_vap), M_80211_VAP, M_WAITOK | M_ZERO);
5337453645fSAndriy Voskoboinyk 	uvp->id = id;
5347453645fSAndriy Voskoboinyk 	if (id != RTWN_VAP_ID_INVALID) {
5357453645fSAndriy Voskoboinyk 		RTWN_LOCK(sc);
5367453645fSAndriy Voskoboinyk 		sc->vaps[id] = uvp;
5377453645fSAndriy Voskoboinyk 		RTWN_UNLOCK(sc);
5387453645fSAndriy Voskoboinyk 	}
5397453645fSAndriy Voskoboinyk 	vap = &uvp->vap;
5407453645fSAndriy Voskoboinyk 	/* enable s/w bmiss handling for sta mode */
5417453645fSAndriy Voskoboinyk 
542bcaed14bSAdrian Chadd 	if (ieee80211_vap_setup(ic, vap, name, unit, opmode,
543bcaed14bSAdrian Chadd 	    flags | IEEE80211_CLONE_NOBEACONS, bssid) != 0) {
544bcaed14bSAdrian Chadd 		/* out of memory */
5457453645fSAndriy Voskoboinyk 		free(uvp, M_80211_VAP);
5467453645fSAndriy Voskoboinyk 
5477453645fSAndriy Voskoboinyk 		RTWN_LOCK(sc);
5487453645fSAndriy Voskoboinyk 		rtwn_vap_decrement_counters(sc, opmode, id);
5497453645fSAndriy Voskoboinyk 		RTWN_UNLOCK(sc);
5507453645fSAndriy Voskoboinyk 
551bcaed14bSAdrian Chadd 		return (NULL);
552bcaed14bSAdrian Chadd 	}
553bcaed14bSAdrian Chadd 
5547453645fSAndriy Voskoboinyk 	rtwn_beacon_init(sc, &uvp->bcn_desc.txd[0], uvp->id);
5557453645fSAndriy Voskoboinyk 	rtwn_vap_preattach(sc, vap);
556bcaed14bSAdrian Chadd 
5577453645fSAndriy Voskoboinyk 	/* override state transition machine */
5587453645fSAndriy Voskoboinyk 	uvp->newstate = vap->iv_newstate;
5597453645fSAndriy Voskoboinyk 	if (opmode == IEEE80211_M_MONITOR)
5607453645fSAndriy Voskoboinyk 		vap->iv_newstate = rtwn_monitor_newstate;
5617453645fSAndriy Voskoboinyk 	else
5627453645fSAndriy Voskoboinyk 		vap->iv_newstate = rtwn_newstate;
5637453645fSAndriy Voskoboinyk 	vap->iv_update_beacon = rtwn_update_beacon;
5647453645fSAndriy Voskoboinyk 	vap->iv_reset = rtwn_ioctl_reset;
5657453645fSAndriy Voskoboinyk 	vap->iv_key_alloc = rtwn_key_alloc;
5667453645fSAndriy Voskoboinyk 	vap->iv_key_set = rtwn_key_set;
5677453645fSAndriy Voskoboinyk 	vap->iv_key_delete = rtwn_key_delete;
5687453645fSAndriy Voskoboinyk 	vap->iv_max_aid = sc->macid_limit;
5697453645fSAndriy Voskoboinyk 
5707453645fSAndriy Voskoboinyk 	/* 802.11n parameters */
5717453645fSAndriy Voskoboinyk 	vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_16;
5727453645fSAndriy Voskoboinyk 	vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K;
5737453645fSAndriy Voskoboinyk 
574b8ad00b0SAndriy Voskoboinyk 	TIMEOUT_TASK_INIT(taskqueue_thread, &uvp->tx_beacon_csa, 0,
575b8ad00b0SAndriy Voskoboinyk 	    rtwn_tx_beacon_csa, vap);
5767453645fSAndriy Voskoboinyk 	if (opmode == IEEE80211_M_IBSS) {
5777453645fSAndriy Voskoboinyk 		uvp->recv_mgmt = vap->iv_recv_mgmt;
5787453645fSAndriy Voskoboinyk 		vap->iv_recv_mgmt = rtwn_adhoc_recv_mgmt;
5797453645fSAndriy Voskoboinyk 		TASK_INIT(&uvp->tsf_sync_adhoc_task, 0,
5807453645fSAndriy Voskoboinyk 		    rtwn_tsf_sync_adhoc_task, vap);
5817453645fSAndriy Voskoboinyk 		callout_init(&uvp->tsf_sync_adhoc, 0);
5827453645fSAndriy Voskoboinyk 	}
5837453645fSAndriy Voskoboinyk 
5847453645fSAndriy Voskoboinyk 	/*
5857453645fSAndriy Voskoboinyk 	 * NB: driver can select net80211 RA even when user requests
5867453645fSAndriy Voskoboinyk 	 * another mechanism.
5877453645fSAndriy Voskoboinyk 	 */
5887453645fSAndriy Voskoboinyk 	ieee80211_ratectl_init(vap);
5897453645fSAndriy Voskoboinyk 
5907453645fSAndriy Voskoboinyk 	/* complete setup */
591bcaed14bSAdrian Chadd 	ieee80211_vap_attach(vap, ieee80211_media_change,
592bcaed14bSAdrian Chadd 	    ieee80211_media_status, mac);
5937453645fSAndriy Voskoboinyk 
5947453645fSAndriy Voskoboinyk 	RTWN_LOCK(sc);
5957453645fSAndriy Voskoboinyk 	rtwn_set_ic_opmode(sc);
5967453645fSAndriy Voskoboinyk 	if (sc->sc_flags & RTWN_RUNNING) {
5977453645fSAndriy Voskoboinyk 		if (uvp->id != RTWN_VAP_ID_INVALID)
5987453645fSAndriy Voskoboinyk 			rtwn_set_macaddr(sc, vap->iv_myaddr, uvp->id);
5997453645fSAndriy Voskoboinyk 
6007453645fSAndriy Voskoboinyk 		rtwn_rxfilter_update(sc);
6017453645fSAndriy Voskoboinyk 	}
6027453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
6037453645fSAndriy Voskoboinyk 
604bcaed14bSAdrian Chadd 	return (vap);
6057453645fSAndriy Voskoboinyk 
6067453645fSAndriy Voskoboinyk fail:
6077453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
6087453645fSAndriy Voskoboinyk 	return (NULL);
609bcaed14bSAdrian Chadd }
610bcaed14bSAdrian Chadd 
611bcaed14bSAdrian Chadd static void
rtwn_vap_delete(struct ieee80211vap * vap)612bcaed14bSAdrian Chadd rtwn_vap_delete(struct ieee80211vap *vap)
613bcaed14bSAdrian Chadd {
6147453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = vap->iv_ic;
6157453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = ic->ic_softc;
6167453645fSAndriy Voskoboinyk 	struct rtwn_vap *uvp = RTWN_VAP(vap);
617713db49dSBjoern A. Zeeb 	int i;
618bcaed14bSAdrian Chadd 
6197453645fSAndriy Voskoboinyk 	/* Put vap into INIT state + stop device if needed. */
6207453645fSAndriy Voskoboinyk 	ieee80211_stop(vap);
621713db49dSBjoern A. Zeeb 	for (i = 0; i < NET80211_IV_NSTATE_NUM; i++)
622713db49dSBjoern A. Zeeb 		ieee80211_draintask(ic, &vap->iv_nstate_task[i]);
6237453645fSAndriy Voskoboinyk 	ieee80211_draintask(ic, &ic->ic_parent_task);
6247453645fSAndriy Voskoboinyk 
6257453645fSAndriy Voskoboinyk 	RTWN_LOCK(sc);
6267453645fSAndriy Voskoboinyk 	/* Cancel any unfinished Tx. */
6277453645fSAndriy Voskoboinyk 	rtwn_reset_lists(sc, vap);
628d067ef0fSAndriy Voskoboinyk 	if (uvp->bcn_mbuf != NULL)
629d067ef0fSAndriy Voskoboinyk 		m_freem(uvp->bcn_mbuf);
6307453645fSAndriy Voskoboinyk 	rtwn_vap_decrement_counters(sc, vap->iv_opmode, uvp->id);
6317453645fSAndriy Voskoboinyk 	rtwn_set_ic_opmode(sc);
6327453645fSAndriy Voskoboinyk 	if (sc->sc_flags & RTWN_RUNNING)
6337453645fSAndriy Voskoboinyk 		rtwn_rxfilter_update(sc);
6347453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
6357453645fSAndriy Voskoboinyk 
6367453645fSAndriy Voskoboinyk 	if (vap->iv_opmode == IEEE80211_M_IBSS) {
6377453645fSAndriy Voskoboinyk 		ieee80211_draintask(ic, &uvp->tsf_sync_adhoc_task);
6387453645fSAndriy Voskoboinyk 		callout_drain(&uvp->tsf_sync_adhoc);
6397453645fSAndriy Voskoboinyk 	}
6407453645fSAndriy Voskoboinyk 
6417453645fSAndriy Voskoboinyk 	ieee80211_ratectl_deinit(vap);
642bcaed14bSAdrian Chadd 	ieee80211_vap_detach(vap);
6437453645fSAndriy Voskoboinyk 	free(uvp, M_80211_VAP);
644bcaed14bSAdrian Chadd }
645bcaed14bSAdrian Chadd 
646bcaed14bSAdrian Chadd static int
rtwn_read_chipid(struct rtwn_softc * sc)647bcaed14bSAdrian Chadd rtwn_read_chipid(struct rtwn_softc *sc)
648bcaed14bSAdrian Chadd {
649bcaed14bSAdrian Chadd 	uint32_t reg;
650bcaed14bSAdrian Chadd 
651bcaed14bSAdrian Chadd 	reg = rtwn_read_4(sc, R92C_SYS_CFG);
6527453645fSAndriy Voskoboinyk 	if (reg & R92C_SYS_CFG_TRP_VAUX_EN)	/* test chip */
6537453645fSAndriy Voskoboinyk 		return (EOPNOTSUPP);
654bcaed14bSAdrian Chadd 
6557453645fSAndriy Voskoboinyk 	rtwn_read_chipid_vendor(sc, reg);
6567453645fSAndriy Voskoboinyk 
657bcaed14bSAdrian Chadd 	return (0);
658bcaed14bSAdrian Chadd }
659bcaed14bSAdrian Chadd 
6607453645fSAndriy Voskoboinyk static int
rtwn_ioctl_reset(struct ieee80211vap * vap,u_long cmd)6617453645fSAndriy Voskoboinyk rtwn_ioctl_reset(struct ieee80211vap *vap, u_long cmd)
662bcaed14bSAdrian Chadd {
6637453645fSAndriy Voskoboinyk 	int error;
664bcaed14bSAdrian Chadd 
6657453645fSAndriy Voskoboinyk 	switch (cmd) {
6667453645fSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE
6677453645fSAndriy Voskoboinyk 	case IEEE80211_IOC_POWERSAVE:
6687453645fSAndriy Voskoboinyk 	case IEEE80211_IOC_POWERSAVESLEEP:
6697453645fSAndriy Voskoboinyk 	{
6707453645fSAndriy Voskoboinyk 		struct rtwn_softc *sc = vap->iv_ic->ic_softc;
6717453645fSAndriy Voskoboinyk 		struct rtwn_vap *uvp = RTWN_VAP(vap);
672bcaed14bSAdrian Chadd 
6737453645fSAndriy Voskoboinyk 		if (vap->iv_opmode == IEEE80211_M_STA && uvp->id == 0) {
6747453645fSAndriy Voskoboinyk 			RTWN_LOCK(sc);
6757453645fSAndriy Voskoboinyk 			if (sc->sc_flags & RTWN_RUNNING)
6767453645fSAndriy Voskoboinyk 				error = rtwn_set_pwrmode(sc, vap, 1);
6777453645fSAndriy Voskoboinyk 			else
6787453645fSAndriy Voskoboinyk 				error = 0;
6797453645fSAndriy Voskoboinyk 			RTWN_UNLOCK(sc);
6807453645fSAndriy Voskoboinyk 			if (error != 0)
6817453645fSAndriy Voskoboinyk 				error = ENETRESET;
6827453645fSAndriy Voskoboinyk 		} else
6837453645fSAndriy Voskoboinyk 			error = EOPNOTSUPP;
6847453645fSAndriy Voskoboinyk 		break;
6857453645fSAndriy Voskoboinyk 	}
6867453645fSAndriy Voskoboinyk #endif
6877453645fSAndriy Voskoboinyk 	case IEEE80211_IOC_SHORTGI:
6887453645fSAndriy Voskoboinyk 	case IEEE80211_IOC_RTSTHRESHOLD:
6897453645fSAndriy Voskoboinyk 	case IEEE80211_IOC_PROTMODE:
6907453645fSAndriy Voskoboinyk 	case IEEE80211_IOC_HTPROTMODE:
6913111723cSAndriy Voskoboinyk 	case IEEE80211_IOC_LDPC:
6927453645fSAndriy Voskoboinyk 		error = 0;
6937453645fSAndriy Voskoboinyk 		break;
6947453645fSAndriy Voskoboinyk 	default:
6957453645fSAndriy Voskoboinyk 		error = ENETRESET;
6967453645fSAndriy Voskoboinyk 		break;
697bcaed14bSAdrian Chadd 	}
698bcaed14bSAdrian Chadd 
6997453645fSAndriy Voskoboinyk 	return (error);
7007453645fSAndriy Voskoboinyk }
7017453645fSAndriy Voskoboinyk 
7027453645fSAndriy Voskoboinyk static void
rtwn_set_media_status(struct rtwn_softc * sc,union sec_param * data)7037453645fSAndriy Voskoboinyk rtwn_set_media_status(struct rtwn_softc *sc, union sec_param *data)
704354df9d4SAndriy Voskoboinyk {
7057453645fSAndriy Voskoboinyk 	sc->sc_set_media_status(sc, data->macid);
7067453645fSAndriy Voskoboinyk }
7077453645fSAndriy Voskoboinyk 
70859ed13aaSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE
7097453645fSAndriy Voskoboinyk static int
rtwn_tx_fwpkt_check(struct rtwn_softc * sc,struct ieee80211vap * vap)7107453645fSAndriy Voskoboinyk rtwn_tx_fwpkt_check(struct rtwn_softc *sc, struct ieee80211vap *vap)
7117453645fSAndriy Voskoboinyk {
7127453645fSAndriy Voskoboinyk 	int ntries, error;
7137453645fSAndriy Voskoboinyk 
7147453645fSAndriy Voskoboinyk 	for (ntries = 0; ntries < 5; ntries++) {
7157453645fSAndriy Voskoboinyk 		error = rtwn_push_nulldata(sc, vap);
7167453645fSAndriy Voskoboinyk 		if (error == 0)
7177453645fSAndriy Voskoboinyk 			break;
7187453645fSAndriy Voskoboinyk 	}
7197453645fSAndriy Voskoboinyk 	if (ntries == 5) {
7207453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev,
7217453645fSAndriy Voskoboinyk 		    "%s: cannot push f/w frames into chip, error %d!\n",
7227453645fSAndriy Voskoboinyk 		    __func__, error);
7237453645fSAndriy Voskoboinyk 		return (error);
7247453645fSAndriy Voskoboinyk 	}
7257453645fSAndriy Voskoboinyk 
7267453645fSAndriy Voskoboinyk 	return (0);
7277453645fSAndriy Voskoboinyk }
7287453645fSAndriy Voskoboinyk 
7297453645fSAndriy Voskoboinyk static int
rtwn_construct_nulldata(struct rtwn_softc * sc,struct ieee80211vap * vap,uint8_t * ptr,int qos)7307453645fSAndriy Voskoboinyk rtwn_construct_nulldata(struct rtwn_softc *sc, struct ieee80211vap *vap,
7317453645fSAndriy Voskoboinyk     uint8_t *ptr, int qos)
7327453645fSAndriy Voskoboinyk {
7337453645fSAndriy Voskoboinyk 	struct rtwn_vap *uvp = RTWN_VAP(vap);
7347453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
7357453645fSAndriy Voskoboinyk 	struct rtwn_tx_desc_common *txd;
7367453645fSAndriy Voskoboinyk 	struct ieee80211_frame *wh;
7377453645fSAndriy Voskoboinyk 	int pktlen;
7387453645fSAndriy Voskoboinyk 
7397453645fSAndriy Voskoboinyk 	/* XXX obtain from net80211 */
7407453645fSAndriy Voskoboinyk 	wh = (struct ieee80211_frame *)(ptr + sc->txdesc_len);
7417453645fSAndriy Voskoboinyk 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
7427453645fSAndriy Voskoboinyk 	wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
7437453645fSAndriy Voskoboinyk 	IEEE80211_ADDR_COPY(wh->i_addr1, vap->iv_bss->ni_bssid);
7447453645fSAndriy Voskoboinyk 	IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
7457453645fSAndriy Voskoboinyk 	IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_bss->ni_macaddr);
7467453645fSAndriy Voskoboinyk 
7477453645fSAndriy Voskoboinyk 	txd = (struct rtwn_tx_desc_common *)ptr;
7487453645fSAndriy Voskoboinyk 	txd->offset = sc->txdesc_len;
7497453645fSAndriy Voskoboinyk 	pktlen = sc->txdesc_len;
7507453645fSAndriy Voskoboinyk 	if (qos) {
7517453645fSAndriy Voskoboinyk 		struct ieee80211_qosframe *qwh;
7527453645fSAndriy Voskoboinyk 		const int tid = WME_AC_TO_TID(WME_AC_BE);
7537453645fSAndriy Voskoboinyk 
7547453645fSAndriy Voskoboinyk 		qwh = (struct ieee80211_qosframe *)wh;
7557453645fSAndriy Voskoboinyk 		qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS_NULL;
7567453645fSAndriy Voskoboinyk 		qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
7577453645fSAndriy Voskoboinyk 
7587453645fSAndriy Voskoboinyk 		txd->pktlen = htole16(sizeof(struct ieee80211_qosframe));
7597453645fSAndriy Voskoboinyk 		pktlen += sizeof(struct ieee80211_qosframe);
7607453645fSAndriy Voskoboinyk 	} else {
7617453645fSAndriy Voskoboinyk 		wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_NODATA;
7627453645fSAndriy Voskoboinyk 
7637453645fSAndriy Voskoboinyk 		txd->pktlen = htole16(sizeof(struct ieee80211_frame));
7647453645fSAndriy Voskoboinyk 		pktlen += sizeof(struct ieee80211_frame);
7657453645fSAndriy Voskoboinyk 	}
7667453645fSAndriy Voskoboinyk 
7677453645fSAndriy Voskoboinyk 	rtwn_fill_tx_desc_null(sc, ptr,
7687453645fSAndriy Voskoboinyk 	    ic->ic_curmode == IEEE80211_MODE_11B, qos, uvp->id);
7697453645fSAndriy Voskoboinyk 
7707453645fSAndriy Voskoboinyk 	return (pktlen);
7717453645fSAndriy Voskoboinyk }
7727453645fSAndriy Voskoboinyk 
7737453645fSAndriy Voskoboinyk static int
rtwn_push_nulldata(struct rtwn_softc * sc,struct ieee80211vap * vap)7747453645fSAndriy Voskoboinyk rtwn_push_nulldata(struct rtwn_softc *sc, struct ieee80211vap *vap)
7757453645fSAndriy Voskoboinyk {
7767453645fSAndriy Voskoboinyk 	struct rtwn_vap *uvp = RTWN_VAP(vap);
7777453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = vap->iv_ic;
7787453645fSAndriy Voskoboinyk 	struct ieee80211_channel *c = ic->ic_curchan;
7797453645fSAndriy Voskoboinyk 	struct mbuf *m;
7807453645fSAndriy Voskoboinyk 	uint8_t *ptr;
7817453645fSAndriy Voskoboinyk 	int required_size, bcn_size, null_size, null_data, error;
7827453645fSAndriy Voskoboinyk 
7837453645fSAndriy Voskoboinyk 	if (!(sc->sc_flags & RTWN_FW_LOADED))
7847453645fSAndriy Voskoboinyk 		return (0);	/* requires firmware */
7857453645fSAndriy Voskoboinyk 
7867453645fSAndriy Voskoboinyk 	KASSERT(sc->page_size > 0, ("page size was not set!\n"));
7877453645fSAndriy Voskoboinyk 
7887453645fSAndriy Voskoboinyk 	/* Leave some space for beacon (multi-vap) */
7897453645fSAndriy Voskoboinyk 	bcn_size = roundup(RTWN_BCN_MAX_SIZE, sc->page_size);
7907453645fSAndriy Voskoboinyk 	/* 1 page for Null Data + 1 page for Qos Null Data frames. */
7917453645fSAndriy Voskoboinyk 	required_size = bcn_size + sc->page_size * 2;
7927453645fSAndriy Voskoboinyk 
7937453645fSAndriy Voskoboinyk 	m = m_get2(required_size, M_NOWAIT, MT_DATA, M_PKTHDR);
7947453645fSAndriy Voskoboinyk 	if (m == NULL)
7957453645fSAndriy Voskoboinyk 		return (ENOMEM);
7967453645fSAndriy Voskoboinyk 
7977453645fSAndriy Voskoboinyk 	/* Setup beacon descriptor. */
7987453645fSAndriy Voskoboinyk 	rtwn_beacon_set_rate(sc, &uvp->bcn_desc.txd[0],
7997453645fSAndriy Voskoboinyk 	    IEEE80211_IS_CHAN_5GHZ(c));
8007453645fSAndriy Voskoboinyk 
8017453645fSAndriy Voskoboinyk 	ptr = mtod(m, uint8_t *);
8027453645fSAndriy Voskoboinyk 	memset(ptr, 0, required_size - sc->txdesc_len);
8037453645fSAndriy Voskoboinyk 
8047453645fSAndriy Voskoboinyk 	/* Construct Null Data frame. */
8057453645fSAndriy Voskoboinyk 	ptr += bcn_size - sc->txdesc_len;
8067453645fSAndriy Voskoboinyk 	null_size = rtwn_construct_nulldata(sc, vap, ptr, 0);
8077453645fSAndriy Voskoboinyk 	KASSERT(null_size < sc->page_size,
8087453645fSAndriy Voskoboinyk 	    ("recalculate size for Null Data frame\n"));
8097453645fSAndriy Voskoboinyk 
8107453645fSAndriy Voskoboinyk 	/* Construct Qos Null Data frame. */
8117453645fSAndriy Voskoboinyk 	ptr += roundup(null_size, sc->page_size);
8127453645fSAndriy Voskoboinyk 	null_size = rtwn_construct_nulldata(sc, vap, ptr, 1);
8137453645fSAndriy Voskoboinyk 	KASSERT(null_size < sc->page_size,
8147453645fSAndriy Voskoboinyk 	    ("recalculate size for Qos Null Data frame\n"));
8157453645fSAndriy Voskoboinyk 
8167453645fSAndriy Voskoboinyk 	/* Do not try to detect a beacon here. */
8177453645fSAndriy Voskoboinyk 	rtwn_setbits_1_shift(sc, R92C_CR, 0, R92C_CR_ENSWBCN, 1);
8187453645fSAndriy Voskoboinyk 	rtwn_setbits_1_shift(sc, R92C_FWHW_TXQ_CTRL,
8197453645fSAndriy Voskoboinyk 	    R92C_FWHW_TXQ_CTRL_REAL_BEACON, 0, 2);
8207453645fSAndriy Voskoboinyk 
821d067ef0fSAndriy Voskoboinyk 	if (uvp->bcn_mbuf != NULL) {
822d067ef0fSAndriy Voskoboinyk 		rtwn_beacon_unload(sc, uvp->id);
8237453645fSAndriy Voskoboinyk 		m_freem(uvp->bcn_mbuf);
824d067ef0fSAndriy Voskoboinyk 	}
8257453645fSAndriy Voskoboinyk 
8267453645fSAndriy Voskoboinyk 	m->m_pkthdr.len = m->m_len = required_size - sc->txdesc_len;
8277453645fSAndriy Voskoboinyk 	uvp->bcn_mbuf = m;
8287453645fSAndriy Voskoboinyk 
8297453645fSAndriy Voskoboinyk 	error = rtwn_tx_beacon_check(sc, uvp);
8307453645fSAndriy Voskoboinyk 	if (error != 0) {
8317453645fSAndriy Voskoboinyk 		RTWN_DPRINTF(sc, RTWN_DEBUG_BEACON,
8327453645fSAndriy Voskoboinyk 		    "%s: frame was not recognized!\n", __func__);
8337453645fSAndriy Voskoboinyk 		goto fail;
8347453645fSAndriy Voskoboinyk 	}
8357453645fSAndriy Voskoboinyk 
8367453645fSAndriy Voskoboinyk 	/* Setup addresses in firmware. */
8377453645fSAndriy Voskoboinyk 	null_data = howmany(bcn_size, sc->page_size);
8387453645fSAndriy Voskoboinyk 	error = rtwn_set_rsvd_page(sc, 0, null_data, null_data + 1);
8397453645fSAndriy Voskoboinyk 	if (error != 0) {
8407453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev,
8417453645fSAndriy Voskoboinyk 		    "%s: CMD_RSVD_PAGE was not sent, error %d\n",
8427453645fSAndriy Voskoboinyk 		    __func__, error);
8437453645fSAndriy Voskoboinyk 		goto fail;
8447453645fSAndriy Voskoboinyk 	}
8457453645fSAndriy Voskoboinyk 
8467453645fSAndriy Voskoboinyk fail:
8477453645fSAndriy Voskoboinyk 	/* Re-enable beacon detection. */
8487453645fSAndriy Voskoboinyk 	rtwn_setbits_1_shift(sc, R92C_FWHW_TXQ_CTRL,
8497453645fSAndriy Voskoboinyk 	    0, R92C_FWHW_TXQ_CTRL_REAL_BEACON, 2);
8507453645fSAndriy Voskoboinyk 	rtwn_setbits_1_shift(sc, R92C_CR, R92C_CR_ENSWBCN, 0, 1);
8517453645fSAndriy Voskoboinyk 
8527453645fSAndriy Voskoboinyk 	/* Restore beacon (if present). */
8537453645fSAndriy Voskoboinyk 	if (sc->bcn_vaps > 0 && sc->vaps[!uvp->id] != NULL) {
8547453645fSAndriy Voskoboinyk 		struct rtwn_vap *uvp2 = sc->vaps[!uvp->id];
8557453645fSAndriy Voskoboinyk 
8567453645fSAndriy Voskoboinyk 		if (uvp2->curr_mode != R92C_MSR_NOLINK)
8577453645fSAndriy Voskoboinyk 			error = rtwn_tx_beacon_check(sc, uvp2);
8587453645fSAndriy Voskoboinyk 	}
8597453645fSAndriy Voskoboinyk 
8607453645fSAndriy Voskoboinyk 	return (error);
8617453645fSAndriy Voskoboinyk }
8627453645fSAndriy Voskoboinyk 
8637453645fSAndriy Voskoboinyk static void
rtwn_pwrmode_init(void * arg)8647453645fSAndriy Voskoboinyk rtwn_pwrmode_init(void *arg)
8657453645fSAndriy Voskoboinyk {
8667453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = arg;
8677453645fSAndriy Voskoboinyk 
8687453645fSAndriy Voskoboinyk 	rtwn_cmd_sleepable(sc, NULL, 0, rtwn_set_pwrmode_cb);
8697453645fSAndriy Voskoboinyk }
8707453645fSAndriy Voskoboinyk 
8717453645fSAndriy Voskoboinyk static void
rtwn_set_pwrmode_cb(struct rtwn_softc * sc,union sec_param * data)8727453645fSAndriy Voskoboinyk rtwn_set_pwrmode_cb(struct rtwn_softc *sc, union sec_param *data)
8737453645fSAndriy Voskoboinyk {
8747453645fSAndriy Voskoboinyk 	struct ieee80211vap *vap = &sc->vaps[0]->vap;
8757453645fSAndriy Voskoboinyk 
8767453645fSAndriy Voskoboinyk 	if (vap != NULL)
8777453645fSAndriy Voskoboinyk 		rtwn_set_pwrmode(sc, vap, 1);
8787453645fSAndriy Voskoboinyk }
8797453645fSAndriy Voskoboinyk #endif
8807453645fSAndriy Voskoboinyk 
8817453645fSAndriy Voskoboinyk static void
rtwn_tsf_sync_adhoc(void * arg)8827453645fSAndriy Voskoboinyk rtwn_tsf_sync_adhoc(void *arg)
8837453645fSAndriy Voskoboinyk {
8847453645fSAndriy Voskoboinyk 	struct ieee80211vap *vap = arg;
8857453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = vap->iv_ic;
8867453645fSAndriy Voskoboinyk 	struct rtwn_vap *uvp = RTWN_VAP(vap);
8877453645fSAndriy Voskoboinyk 
8887453645fSAndriy Voskoboinyk 	if (uvp->curr_mode != R92C_MSR_NOLINK) {
8897453645fSAndriy Voskoboinyk 		/* Do it in process context. */
8907453645fSAndriy Voskoboinyk 		ieee80211_runtask(ic, &uvp->tsf_sync_adhoc_task);
891354df9d4SAndriy Voskoboinyk 	}
892354df9d4SAndriy Voskoboinyk }
893354df9d4SAndriy Voskoboinyk 
894bcaed14bSAdrian Chadd /*
8957453645fSAndriy Voskoboinyk  * Workaround for TSF synchronization:
8967453645fSAndriy Voskoboinyk  * when BSSID filter in IBSS mode is not set
8977453645fSAndriy Voskoboinyk  * (and TSF synchronization is enabled), then any beacon may update it.
8987453645fSAndriy Voskoboinyk  * This routine synchronizes it when BSSID matching is enabled (IBSS merge
8997453645fSAndriy Voskoboinyk  * is not possible during this period).
9007453645fSAndriy Voskoboinyk  *
9017453645fSAndriy Voskoboinyk  * NOTE: there is no race with rtwn_newstate(), since it uses the same
9027453645fSAndriy Voskoboinyk  * taskqueue.
903bcaed14bSAdrian Chadd  */
9047453645fSAndriy Voskoboinyk static void
rtwn_tsf_sync_adhoc_task(void * arg,int pending)9057453645fSAndriy Voskoboinyk rtwn_tsf_sync_adhoc_task(void *arg, int pending)
906bcaed14bSAdrian Chadd {
9077453645fSAndriy Voskoboinyk 	struct ieee80211vap *vap = arg;
9087453645fSAndriy Voskoboinyk 	struct rtwn_vap *uvp = RTWN_VAP(vap);
9097453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = vap->iv_ic->ic_softc;
9107453645fSAndriy Voskoboinyk 	struct ieee80211_node *ni;
911bcaed14bSAdrian Chadd 
9127453645fSAndriy Voskoboinyk 	RTWN_LOCK(sc);
9137453645fSAndriy Voskoboinyk 	ni = ieee80211_ref_node(vap->iv_bss);
914bcaed14bSAdrian Chadd 
9157453645fSAndriy Voskoboinyk 	/* Accept beacons with the same BSSID. */
9167453645fSAndriy Voskoboinyk 	rtwn_set_rx_bssid_all(sc, 0);
917bcaed14bSAdrian Chadd 
918c15d8692SAndriy Voskoboinyk 	/* Deny RCR updates. */
919c15d8692SAndriy Voskoboinyk 	sc->sc_flags |= RTWN_RCR_LOCKED;
920c15d8692SAndriy Voskoboinyk 
9217453645fSAndriy Voskoboinyk 	/* Enable synchronization. */
9227453645fSAndriy Voskoboinyk 	rtwn_setbits_1(sc, R92C_BCN_CTRL(uvp->id),
9237453645fSAndriy Voskoboinyk 	    R92C_BCN_CTRL_DIS_TSF_UDT0, 0);
924bcaed14bSAdrian Chadd 
9257453645fSAndriy Voskoboinyk 	/* Synchronize. */
9267453645fSAndriy Voskoboinyk 	rtwn_delay(sc, ni->ni_intval * 5 * 1000);
927bcaed14bSAdrian Chadd 
9287453645fSAndriy Voskoboinyk 	/* Disable synchronization. */
9297453645fSAndriy Voskoboinyk 	rtwn_setbits_1(sc, R92C_BCN_CTRL(uvp->id),
9307453645fSAndriy Voskoboinyk 	    0, R92C_BCN_CTRL_DIS_TSF_UDT0);
9317453645fSAndriy Voskoboinyk 
9327453645fSAndriy Voskoboinyk 	/* Accept all beacons. */
933c15d8692SAndriy Voskoboinyk 	sc->sc_flags &= ~RTWN_RCR_LOCKED;
9347453645fSAndriy Voskoboinyk 	rtwn_set_rx_bssid_all(sc, 1);
9357453645fSAndriy Voskoboinyk 
9367453645fSAndriy Voskoboinyk 	/* Schedule next TSF synchronization. */
9377453645fSAndriy Voskoboinyk 	callout_reset(&uvp->tsf_sync_adhoc, 60*hz, rtwn_tsf_sync_adhoc, vap);
9387453645fSAndriy Voskoboinyk 
9397453645fSAndriy Voskoboinyk 	ieee80211_free_node(ni);
9407453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
941bcaed14bSAdrian Chadd }
942bcaed14bSAdrian Chadd 
943bcaed14bSAdrian Chadd static void
rtwn_tsf_sync_enable(struct rtwn_softc * sc,struct ieee80211vap * vap)9447453645fSAndriy Voskoboinyk rtwn_tsf_sync_enable(struct rtwn_softc *sc, struct ieee80211vap *vap)
945bcaed14bSAdrian Chadd {
946bcaed14bSAdrian Chadd 	struct ieee80211com *ic = &sc->sc_ic;
9477453645fSAndriy Voskoboinyk 	struct rtwn_vap *uvp = RTWN_VAP(vap);
948bcaed14bSAdrian Chadd 
9497453645fSAndriy Voskoboinyk 	/* Reset TSF. */
9507453645fSAndriy Voskoboinyk 	rtwn_write_1(sc, R92C_DUAL_TSF_RST, R92C_DUAL_TSF_RESET(uvp->id));
9517453645fSAndriy Voskoboinyk 
9527453645fSAndriy Voskoboinyk 	switch (vap->iv_opmode) {
9537453645fSAndriy Voskoboinyk 	case IEEE80211_M_STA:
954bcaed14bSAdrian Chadd 		/* Enable TSF synchronization. */
9557453645fSAndriy Voskoboinyk 		rtwn_setbits_1(sc, R92C_BCN_CTRL(uvp->id),
9567453645fSAndriy Voskoboinyk 		    R92C_BCN_CTRL_DIS_TSF_UDT0, 0);
9577453645fSAndriy Voskoboinyk 		break;
9587453645fSAndriy Voskoboinyk 	case IEEE80211_M_IBSS:
9597453645fSAndriy Voskoboinyk 		ieee80211_runtask(ic, &uvp->tsf_sync_adhoc_task);
9607453645fSAndriy Voskoboinyk 		/* FALLTHROUGH */
9617453645fSAndriy Voskoboinyk 	case IEEE80211_M_HOSTAP:
9627453645fSAndriy Voskoboinyk 		/* Enable beaconing. */
9637453645fSAndriy Voskoboinyk 		rtwn_beacon_enable(sc, uvp->id, 1);
9647453645fSAndriy Voskoboinyk 		break;
9657453645fSAndriy Voskoboinyk 	default:
9667453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev, "undefined opmode %d\n",
9677453645fSAndriy Voskoboinyk 		    vap->iv_opmode);
9687453645fSAndriy Voskoboinyk 		return;
9697453645fSAndriy Voskoboinyk 	}
970bcaed14bSAdrian Chadd }
971bcaed14bSAdrian Chadd 
972bcaed14bSAdrian Chadd static void
rtwn_set_ack_preamble(struct rtwn_softc * sc)9737453645fSAndriy Voskoboinyk rtwn_set_ack_preamble(struct rtwn_softc *sc)
974bcaed14bSAdrian Chadd {
9757453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
9767453645fSAndriy Voskoboinyk 	uint32_t reg;
977bcaed14bSAdrian Chadd 
9787453645fSAndriy Voskoboinyk 	reg = rtwn_read_4(sc, R92C_WMAC_TRXPTCL_CTL);
9797453645fSAndriy Voskoboinyk 	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
9807453645fSAndriy Voskoboinyk 		reg |= R92C_WMAC_TRXPTCL_SHPRE;
981bcaed14bSAdrian Chadd 	else
9827453645fSAndriy Voskoboinyk 		reg &= ~R92C_WMAC_TRXPTCL_SHPRE;
9837453645fSAndriy Voskoboinyk 	rtwn_write_4(sc, R92C_WMAC_TRXPTCL_CTL, reg);
984bcaed14bSAdrian Chadd }
985bcaed14bSAdrian Chadd 
986bcaed14bSAdrian Chadd static void
rtwn_set_mode(struct rtwn_softc * sc,uint8_t mode,int id)9877453645fSAndriy Voskoboinyk rtwn_set_mode(struct rtwn_softc *sc, uint8_t mode, int id)
988bcaed14bSAdrian Chadd {
989bcaed14bSAdrian Chadd 
9907453645fSAndriy Voskoboinyk 	rtwn_setbits_1(sc, R92C_MSR, R92C_MSR_MASK << id * 2, mode << id * 2);
9917453645fSAndriy Voskoboinyk 	if (sc->vaps[id] != NULL)
9927453645fSAndriy Voskoboinyk 		sc->vaps[id]->curr_mode = mode;
993bcaed14bSAdrian Chadd }
994bcaed14bSAdrian Chadd 
9957453645fSAndriy Voskoboinyk static int
rtwn_monitor_newstate(struct ieee80211vap * vap,enum ieee80211_state nstate,int arg)9967453645fSAndriy Voskoboinyk rtwn_monitor_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate,
9977453645fSAndriy Voskoboinyk     int arg)
9987453645fSAndriy Voskoboinyk {
9997453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = vap->iv_ic;
10007453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = ic->ic_softc;
10017453645fSAndriy Voskoboinyk 	struct rtwn_vap *uvp = RTWN_VAP(vap);
1002bcaed14bSAdrian Chadd 
10037453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_STATE, "%s -> %s\n",
10047453645fSAndriy Voskoboinyk 	    ieee80211_state_name[vap->iv_state],
10057453645fSAndriy Voskoboinyk 	    ieee80211_state_name[nstate]);
10067453645fSAndriy Voskoboinyk 
10077453645fSAndriy Voskoboinyk 	if (vap->iv_state != nstate) {
10087453645fSAndriy Voskoboinyk 		IEEE80211_UNLOCK(ic);
10097453645fSAndriy Voskoboinyk 		RTWN_LOCK(sc);
10107453645fSAndriy Voskoboinyk 
10117453645fSAndriy Voskoboinyk 		switch (nstate) {
10127453645fSAndriy Voskoboinyk 		case IEEE80211_S_INIT:
10137453645fSAndriy Voskoboinyk 			sc->vaps_running--;
10147453645fSAndriy Voskoboinyk 			sc->monvaps_running--;
10157453645fSAndriy Voskoboinyk 
10167453645fSAndriy Voskoboinyk 			if (sc->vaps_running == 0) {
10177453645fSAndriy Voskoboinyk 				/* Turn link LED off. */
10187453645fSAndriy Voskoboinyk 				rtwn_set_led(sc, RTWN_LED_LINK, 0);
10197453645fSAndriy Voskoboinyk 			}
10207453645fSAndriy Voskoboinyk 			break;
10217453645fSAndriy Voskoboinyk 		case IEEE80211_S_RUN:
10227453645fSAndriy Voskoboinyk 			sc->vaps_running++;
10237453645fSAndriy Voskoboinyk 			sc->monvaps_running++;
10247453645fSAndriy Voskoboinyk 
10257453645fSAndriy Voskoboinyk 			if (sc->vaps_running == 1) {
10267453645fSAndriy Voskoboinyk 				/* Turn link LED on. */
10277453645fSAndriy Voskoboinyk 				rtwn_set_led(sc, RTWN_LED_LINK, 1);
10287453645fSAndriy Voskoboinyk 			}
10297453645fSAndriy Voskoboinyk 			break;
10307453645fSAndriy Voskoboinyk 		default:
10317453645fSAndriy Voskoboinyk 			/* NOTREACHED */
10327453645fSAndriy Voskoboinyk 			break;
10337453645fSAndriy Voskoboinyk 		}
10347453645fSAndriy Voskoboinyk 
10357453645fSAndriy Voskoboinyk 		RTWN_UNLOCK(sc);
10367453645fSAndriy Voskoboinyk 		IEEE80211_LOCK(ic);
10377453645fSAndriy Voskoboinyk 	}
10387453645fSAndriy Voskoboinyk 
10397453645fSAndriy Voskoboinyk 	return (uvp->newstate(vap, nstate, arg));
1040bcaed14bSAdrian Chadd }
1041bcaed14bSAdrian Chadd 
1042bcaed14bSAdrian Chadd static int
rtwn_newstate(struct ieee80211vap * vap,enum ieee80211_state nstate,int arg)1043bcaed14bSAdrian Chadd rtwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
1044bcaed14bSAdrian Chadd {
10457453645fSAndriy Voskoboinyk 	struct rtwn_vap *uvp = RTWN_VAP(vap);
1046bcaed14bSAdrian Chadd 	struct ieee80211com *ic = vap->iv_ic;
1047bcaed14bSAdrian Chadd 	struct rtwn_softc *sc = ic->ic_softc;
10487453645fSAndriy Voskoboinyk 	enum ieee80211_state ostate;
10497453645fSAndriy Voskoboinyk 	int error, early_newstate;
10507453645fSAndriy Voskoboinyk 
10517453645fSAndriy Voskoboinyk 	ostate = vap->iv_state;
10527453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_STATE, "%s -> %s\n",
10537453645fSAndriy Voskoboinyk 	    ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
10547453645fSAndriy Voskoboinyk 
10557453645fSAndriy Voskoboinyk 	if (vap->iv_bss->ni_chan == IEEE80211_CHAN_ANYC &&
10567453645fSAndriy Voskoboinyk 	    ostate == IEEE80211_S_INIT && nstate == IEEE80211_S_RUN) {
10577453645fSAndriy Voskoboinyk 		/* need to call iv_newstate() firstly */
10587453645fSAndriy Voskoboinyk 		error = uvp->newstate(vap, nstate, arg);
10597453645fSAndriy Voskoboinyk 		if (error != 0)
10607453645fSAndriy Voskoboinyk 			return (error);
10617453645fSAndriy Voskoboinyk 
10627453645fSAndriy Voskoboinyk 		early_newstate = 1;
10637453645fSAndriy Voskoboinyk 	} else
10647453645fSAndriy Voskoboinyk 		early_newstate = 0;
1065bcaed14bSAdrian Chadd 
1066b8ad00b0SAndriy Voskoboinyk 	if (ostate == IEEE80211_S_CSA) {
1067b8ad00b0SAndriy Voskoboinyk 		taskqueue_cancel_timeout(taskqueue_thread,
1068b8ad00b0SAndriy Voskoboinyk 		    &uvp->tx_beacon_csa, NULL);
1069b8ad00b0SAndriy Voskoboinyk 
1070b8ad00b0SAndriy Voskoboinyk 		/*
1071b8ad00b0SAndriy Voskoboinyk 		 * In multi-vap case second counter may not be cleared
1072b8ad00b0SAndriy Voskoboinyk 		 * properly.
1073b8ad00b0SAndriy Voskoboinyk 		 */
1074b8ad00b0SAndriy Voskoboinyk 		vap->iv_csa_count = 0;
1075b8ad00b0SAndriy Voskoboinyk 	}
1076bcaed14bSAdrian Chadd 	IEEE80211_UNLOCK(ic);
1077bcaed14bSAdrian Chadd 	RTWN_LOCK(sc);
1078b8ad00b0SAndriy Voskoboinyk 
1079b8ad00b0SAndriy Voskoboinyk 	if (ostate == IEEE80211_S_CSA) {
1080b8ad00b0SAndriy Voskoboinyk 		/* Unblock all queues (multi-vap case). */
1081b8ad00b0SAndriy Voskoboinyk 		rtwn_write_1(sc, R92C_TXPAUSE, 0);
1082b8ad00b0SAndriy Voskoboinyk 	}
1083b8ad00b0SAndriy Voskoboinyk 
1084b8ad00b0SAndriy Voskoboinyk 	if ((ostate == IEEE80211_S_RUN && nstate != IEEE80211_S_CSA) ||
1085b8ad00b0SAndriy Voskoboinyk 	    ostate == IEEE80211_S_CSA) {
10867453645fSAndriy Voskoboinyk 		sc->vaps_running--;
1087bcaed14bSAdrian Chadd 
1088bcaed14bSAdrian Chadd 		/* Set media status to 'No Link'. */
10897453645fSAndriy Voskoboinyk 		rtwn_set_mode(sc, R92C_MSR_NOLINK, uvp->id);
10907453645fSAndriy Voskoboinyk 
10917453645fSAndriy Voskoboinyk 		if (vap->iv_opmode == IEEE80211_M_IBSS) {
10927453645fSAndriy Voskoboinyk 			/* Stop periodical TSF synchronization. */
10937453645fSAndriy Voskoboinyk 			callout_stop(&uvp->tsf_sync_adhoc);
10947453645fSAndriy Voskoboinyk 		}
10957453645fSAndriy Voskoboinyk 
10967453645fSAndriy Voskoboinyk 		/* Disable TSF synchronization / beaconing. */
10977453645fSAndriy Voskoboinyk 		rtwn_beacon_enable(sc, uvp->id, 0);
10987453645fSAndriy Voskoboinyk 		rtwn_setbits_1(sc, R92C_BCN_CTRL(uvp->id),
10997453645fSAndriy Voskoboinyk 		    0, R92C_BCN_CTRL_DIS_TSF_UDT0);
11007453645fSAndriy Voskoboinyk 
11017453645fSAndriy Voskoboinyk 		/* NB: monitor mode vaps are using port 0. */
11027453645fSAndriy Voskoboinyk 		if (uvp->id != 0 || sc->monvaps_running == 0) {
11037453645fSAndriy Voskoboinyk 			/* Reset TSF. */
11047453645fSAndriy Voskoboinyk 			rtwn_write_1(sc, R92C_DUAL_TSF_RST,
11057453645fSAndriy Voskoboinyk 			    R92C_DUAL_TSF_RESET(uvp->id));
11067453645fSAndriy Voskoboinyk 		}
11077453645fSAndriy Voskoboinyk 
11087453645fSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE
11097453645fSAndriy Voskoboinyk 		if ((ic->ic_caps & IEEE80211_C_PMGT) != 0 && uvp->id == 0) {
11107453645fSAndriy Voskoboinyk 			/* Disable power management. */
11117453645fSAndriy Voskoboinyk 			callout_stop(&sc->sc_pwrmode_init);
11127453645fSAndriy Voskoboinyk 			rtwn_set_pwrmode(sc, vap, 0);
11137453645fSAndriy Voskoboinyk 		}
11147453645fSAndriy Voskoboinyk #endif
11157453645fSAndriy Voskoboinyk 		if (sc->vaps_running - sc->monvaps_running > 0) {
11167453645fSAndriy Voskoboinyk 			/* Recalculate basic rates bitmap. */
11177453645fSAndriy Voskoboinyk 			rtwn_calc_basicrates(sc);
11187453645fSAndriy Voskoboinyk 		}
11197453645fSAndriy Voskoboinyk 
11207453645fSAndriy Voskoboinyk 		if (sc->vaps_running == sc->monvaps_running) {
11217453645fSAndriy Voskoboinyk 			/* Stop calibration. */
11227453645fSAndriy Voskoboinyk 			callout_stop(&sc->sc_calib_to);
1123bcaed14bSAdrian Chadd 
1124bcaed14bSAdrian Chadd 			/* Stop Rx of data frames. */
1125bcaed14bSAdrian Chadd 			rtwn_write_2(sc, R92C_RXFLTMAP2, 0);
1126bcaed14bSAdrian Chadd 
1127bcaed14bSAdrian Chadd 			/* Reset EDCA parameters. */
1128bcaed14bSAdrian Chadd 			rtwn_write_4(sc, R92C_EDCA_VO_PARAM, 0x002f3217);
1129bcaed14bSAdrian Chadd 			rtwn_write_4(sc, R92C_EDCA_VI_PARAM, 0x005e4317);
1130bcaed14bSAdrian Chadd 			rtwn_write_4(sc, R92C_EDCA_BE_PARAM, 0x00105320);
1131bcaed14bSAdrian Chadd 			rtwn_write_4(sc, R92C_EDCA_BK_PARAM, 0x0000a444);
11327453645fSAndriy Voskoboinyk 
11337453645fSAndriy Voskoboinyk 			if (sc->vaps_running == 0) {
1134bcaed14bSAdrian Chadd 				/* Turn link LED off. */
1135bcaed14bSAdrian Chadd 				rtwn_set_led(sc, RTWN_LED_LINK, 0);
11367453645fSAndriy Voskoboinyk 			}
11377453645fSAndriy Voskoboinyk 		}
11387453645fSAndriy Voskoboinyk 	}
1139bcaed14bSAdrian Chadd 
11407453645fSAndriy Voskoboinyk 	error = 0;
11417453645fSAndriy Voskoboinyk 	switch (nstate) {
11427453645fSAndriy Voskoboinyk 	case IEEE80211_S_SCAN:
1143bcaed14bSAdrian Chadd 		/* Pause AC Tx queues. */
11447453645fSAndriy Voskoboinyk 		if (sc->vaps_running == 0)
11457453645fSAndriy Voskoboinyk 			rtwn_setbits_1(sc, R92C_TXPAUSE, 0, R92C_TX_QUEUE_AC);
1146bcaed14bSAdrian Chadd 		break;
1147bcaed14bSAdrian Chadd 	case IEEE80211_S_RUN:
11487453645fSAndriy Voskoboinyk 		error = rtwn_run(sc, vap);
11497453645fSAndriy Voskoboinyk 		if (error != 0) {
11507453645fSAndriy Voskoboinyk 			device_printf(sc->sc_dev,
11517453645fSAndriy Voskoboinyk 			    "%s: could not move to RUN state\n", __func__);
1152bcaed14bSAdrian Chadd 			break;
1153bcaed14bSAdrian Chadd 		}
1154bcaed14bSAdrian Chadd 
11557453645fSAndriy Voskoboinyk 		sc->vaps_running++;
11567453645fSAndriy Voskoboinyk 		break;
1157b8ad00b0SAndriy Voskoboinyk 	case IEEE80211_S_CSA:
1158b8ad00b0SAndriy Voskoboinyk 		/* Block all Tx queues (except beacon queue). */
1159b8ad00b0SAndriy Voskoboinyk 		rtwn_setbits_1(sc, R92C_TXPAUSE, 0,
1160b8ad00b0SAndriy Voskoboinyk 		    R92C_TX_QUEUE_AC | R92C_TX_QUEUE_MGT | R92C_TX_QUEUE_HIGH);
1161b8ad00b0SAndriy Voskoboinyk 		break;
11627453645fSAndriy Voskoboinyk 	default:
11637453645fSAndriy Voskoboinyk 		break;
11647453645fSAndriy Voskoboinyk 	}
11657453645fSAndriy Voskoboinyk 
11667453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
11677453645fSAndriy Voskoboinyk 	IEEE80211_LOCK(ic);
11687453645fSAndriy Voskoboinyk 	if (error != 0)
11697453645fSAndriy Voskoboinyk 		return (error);
11707453645fSAndriy Voskoboinyk 
11717453645fSAndriy Voskoboinyk 	return (early_newstate ? 0 : uvp->newstate(vap, nstate, arg));
11727453645fSAndriy Voskoboinyk }
11737453645fSAndriy Voskoboinyk 
11747453645fSAndriy Voskoboinyk static void
rtwn_calc_basicrates(struct rtwn_softc * sc)11757453645fSAndriy Voskoboinyk rtwn_calc_basicrates(struct rtwn_softc *sc)
11767453645fSAndriy Voskoboinyk {
11777453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
11787453645fSAndriy Voskoboinyk 	uint32_t basicrates;
11797453645fSAndriy Voskoboinyk 	int i;
11807453645fSAndriy Voskoboinyk 
11817453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
11827453645fSAndriy Voskoboinyk 
11837453645fSAndriy Voskoboinyk 	if (ic->ic_flags & IEEE80211_F_SCAN)
11847453645fSAndriy Voskoboinyk 		return;		/* will be done by rtwn_scan_end(). */
11857453645fSAndriy Voskoboinyk 
11867453645fSAndriy Voskoboinyk 	basicrates = 0;
11877453645fSAndriy Voskoboinyk 	for (i = 0; i < nitems(sc->vaps); i++) {
11887453645fSAndriy Voskoboinyk 		struct rtwn_vap *rvp;
11897453645fSAndriy Voskoboinyk 		struct ieee80211vap *vap;
11907453645fSAndriy Voskoboinyk 		struct ieee80211_node *ni;
11917453645fSAndriy Voskoboinyk 		uint32_t rates;
11927453645fSAndriy Voskoboinyk 
11937453645fSAndriy Voskoboinyk 		rvp = sc->vaps[i];
11947453645fSAndriy Voskoboinyk 		if (rvp == NULL || rvp->curr_mode == R92C_MSR_NOLINK)
11957453645fSAndriy Voskoboinyk 			continue;
11967453645fSAndriy Voskoboinyk 
11977453645fSAndriy Voskoboinyk 		vap = &rvp->vap;
11987453645fSAndriy Voskoboinyk 		if (vap->iv_bss == NULL)
11997453645fSAndriy Voskoboinyk 			continue;
12007453645fSAndriy Voskoboinyk 
12017453645fSAndriy Voskoboinyk 		ni = ieee80211_ref_node(vap->iv_bss);
12027453645fSAndriy Voskoboinyk 		rtwn_get_rates(sc, &ni->ni_rates, NULL, &rates, NULL, 1);
12037453645fSAndriy Voskoboinyk 		basicrates |= rates;
12047453645fSAndriy Voskoboinyk 		ieee80211_free_node(ni);
12057453645fSAndriy Voskoboinyk 	}
12067453645fSAndriy Voskoboinyk 
12077453645fSAndriy Voskoboinyk 	if (basicrates == 0)
12087453645fSAndriy Voskoboinyk 		return;
12097453645fSAndriy Voskoboinyk 
12107453645fSAndriy Voskoboinyk 	/* XXX initial RTS rate? */
12117453645fSAndriy Voskoboinyk 	rtwn_set_basicrates(sc, basicrates);
12127453645fSAndriy Voskoboinyk }
12137453645fSAndriy Voskoboinyk 
12147453645fSAndriy Voskoboinyk static int
rtwn_run(struct rtwn_softc * sc,struct ieee80211vap * vap)12157453645fSAndriy Voskoboinyk rtwn_run(struct rtwn_softc *sc, struct ieee80211vap *vap)
12167453645fSAndriy Voskoboinyk {
12177453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = vap->iv_ic;
12187453645fSAndriy Voskoboinyk 	struct rtwn_vap *uvp = RTWN_VAP(vap);
12197453645fSAndriy Voskoboinyk 	struct ieee80211_node *ni;
12207453645fSAndriy Voskoboinyk 	uint8_t mode;
12217453645fSAndriy Voskoboinyk 	int error;
12227453645fSAndriy Voskoboinyk 
12237453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
12247453645fSAndriy Voskoboinyk 
12257453645fSAndriy Voskoboinyk 	error = 0;
12267453645fSAndriy Voskoboinyk 	ni = ieee80211_ref_node(vap->iv_bss);
12277453645fSAndriy Voskoboinyk 
12287453645fSAndriy Voskoboinyk 	if (ic->ic_bsschan == IEEE80211_CHAN_ANYC ||
12297453645fSAndriy Voskoboinyk 	    ni->ni_chan == IEEE80211_CHAN_ANYC) {
12307453645fSAndriy Voskoboinyk 		error = EINVAL;
12317453645fSAndriy Voskoboinyk 		goto fail;
12327453645fSAndriy Voskoboinyk 	}
12337453645fSAndriy Voskoboinyk 
12347453645fSAndriy Voskoboinyk 	switch (vap->iv_opmode) {
12357453645fSAndriy Voskoboinyk 	case IEEE80211_M_STA:
12367453645fSAndriy Voskoboinyk 		mode = R92C_MSR_INFRA;
12377453645fSAndriy Voskoboinyk 		break;
12387453645fSAndriy Voskoboinyk 	case IEEE80211_M_IBSS:
12397453645fSAndriy Voskoboinyk 		mode = R92C_MSR_ADHOC;
12407453645fSAndriy Voskoboinyk 		break;
12417453645fSAndriy Voskoboinyk 	case IEEE80211_M_HOSTAP:
12427453645fSAndriy Voskoboinyk 		mode = R92C_MSR_AP;
12437453645fSAndriy Voskoboinyk 		break;
12447453645fSAndriy Voskoboinyk 	default:
12457453645fSAndriy Voskoboinyk 		KASSERT(0, ("undefined opmode %d\n", vap->iv_opmode));
12467453645fSAndriy Voskoboinyk 		error = EINVAL;
12477453645fSAndriy Voskoboinyk 		goto fail;
12487453645fSAndriy Voskoboinyk 	}
12497453645fSAndriy Voskoboinyk 
1250bcaed14bSAdrian Chadd 	/* Set media status to 'Associated'. */
12517453645fSAndriy Voskoboinyk 	rtwn_set_mode(sc, mode, uvp->id);
12527453645fSAndriy Voskoboinyk 
12537453645fSAndriy Voskoboinyk 	/* Set AssocID. */
12547453645fSAndriy Voskoboinyk 	/* XXX multi-vap? */
12557453645fSAndriy Voskoboinyk 	rtwn_write_2(sc, R92C_BCN_PSR_RPT,
12567453645fSAndriy Voskoboinyk 	    0xc000 | IEEE80211_NODE_AID(ni));
1257bcaed14bSAdrian Chadd 
1258bcaed14bSAdrian Chadd 	/* Set BSSID. */
12597453645fSAndriy Voskoboinyk 	rtwn_set_bssid(sc, ni->ni_bssid, uvp->id);
1260bcaed14bSAdrian Chadd 
12617453645fSAndriy Voskoboinyk 	/* Set beacon interval. */
12627453645fSAndriy Voskoboinyk 	rtwn_write_2(sc, R92C_BCN_INTERVAL(uvp->id), ni->ni_intval);
1263bcaed14bSAdrian Chadd 
12647453645fSAndriy Voskoboinyk 	if (sc->vaps_running == sc->monvaps_running) {
1265bcaed14bSAdrian Chadd 		/* Enable Rx of data frames. */
1266bcaed14bSAdrian Chadd 		rtwn_write_2(sc, R92C_RXFLTMAP2, 0xffff);
1267bcaed14bSAdrian Chadd 
1268bcaed14bSAdrian Chadd 		/* Flush all AC queues. */
1269bcaed14bSAdrian Chadd 		rtwn_write_1(sc, R92C_TXPAUSE, 0);
12707453645fSAndriy Voskoboinyk 	}
1271bcaed14bSAdrian Chadd 
12727453645fSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE
12737453645fSAndriy Voskoboinyk 	/* Upload (QoS) Null Data frame to firmware. */
12747453645fSAndriy Voskoboinyk 	/* Note: do this for port 0 only. */
12757453645fSAndriy Voskoboinyk 	if ((ic->ic_caps & IEEE80211_C_PMGT) != 0 &&
12767453645fSAndriy Voskoboinyk 	    vap->iv_opmode == IEEE80211_M_STA && uvp->id == 0) {
12777453645fSAndriy Voskoboinyk 		error = rtwn_tx_fwpkt_check(sc, vap);
12787453645fSAndriy Voskoboinyk 		if (error != 0)
12797453645fSAndriy Voskoboinyk 			goto fail;
12807453645fSAndriy Voskoboinyk 
12817453645fSAndriy Voskoboinyk 		/* Setup power management. */
12827453645fSAndriy Voskoboinyk 		/*
12837453645fSAndriy Voskoboinyk 		 * NB: it will be enabled immediately - delay it,
12847453645fSAndriy Voskoboinyk 		 * so 4-Way handshake will not be interrupted.
12857453645fSAndriy Voskoboinyk 		 */
12867453645fSAndriy Voskoboinyk 		callout_reset(&sc->sc_pwrmode_init, 5*hz,
12877453645fSAndriy Voskoboinyk 		    rtwn_pwrmode_init, sc);
12887453645fSAndriy Voskoboinyk 	}
12897453645fSAndriy Voskoboinyk #endif
12907453645fSAndriy Voskoboinyk 
1291d067ef0fSAndriy Voskoboinyk 	/* Enable TSF synchronization. */
1292d067ef0fSAndriy Voskoboinyk 	rtwn_tsf_sync_enable(sc, vap);
1293d067ef0fSAndriy Voskoboinyk 
12947453645fSAndriy Voskoboinyk 	if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
12957453645fSAndriy Voskoboinyk 	    vap->iv_opmode == IEEE80211_M_IBSS) {
12967453645fSAndriy Voskoboinyk 		error = rtwn_setup_beacon(sc, ni);
12977453645fSAndriy Voskoboinyk 		if (error != 0) {
12987453645fSAndriy Voskoboinyk 			device_printf(sc->sc_dev,
12997453645fSAndriy Voskoboinyk 			    "unable to push beacon into the chip, "
13007453645fSAndriy Voskoboinyk 			    "error %d\n", error);
13017453645fSAndriy Voskoboinyk 			goto fail;
13027453645fSAndriy Voskoboinyk 		}
13037453645fSAndriy Voskoboinyk 	}
13047453645fSAndriy Voskoboinyk 
13057453645fSAndriy Voskoboinyk 	/* Set ACK preamble type. */
13067453645fSAndriy Voskoboinyk 	rtwn_set_ack_preamble(sc);
1307bcaed14bSAdrian Chadd 
13087453645fSAndriy Voskoboinyk 	/* Set basic rates mask. */
13097453645fSAndriy Voskoboinyk 	rtwn_calc_basicrates(sc);
13107453645fSAndriy Voskoboinyk 
13117453645fSAndriy Voskoboinyk #ifdef RTWN_TODO
1312bcaed14bSAdrian Chadd 	rtwn_write_1(sc, R92C_SIFS_CCK + 1, 10);
1313bcaed14bSAdrian Chadd 	rtwn_write_1(sc, R92C_SIFS_OFDM + 1, 10);
1314bcaed14bSAdrian Chadd 	rtwn_write_1(sc, R92C_SPEC_SIFS + 1, 10);
1315bcaed14bSAdrian Chadd 	rtwn_write_1(sc, R92C_MAC_SPEC_SIFS + 1, 10);
1316bcaed14bSAdrian Chadd 	rtwn_write_1(sc, R92C_R2T_SIFS + 1, 10);
1317bcaed14bSAdrian Chadd 	rtwn_write_1(sc, R92C_T2T_SIFS + 1, 10);
13187453645fSAndriy Voskoboinyk #endif
1319bcaed14bSAdrian Chadd 
13207453645fSAndriy Voskoboinyk 	if (sc->vaps_running == sc->monvaps_running) {
13217453645fSAndriy Voskoboinyk 		/* Reset temperature calibration state machine. */
13227453645fSAndriy Voskoboinyk 		sc->sc_flags &= ~RTWN_TEMP_MEASURED;
13237453645fSAndriy Voskoboinyk 		sc->thcal_temp = sc->thermal_meter;
13247453645fSAndriy Voskoboinyk 
13257453645fSAndriy Voskoboinyk 		/* Start periodic calibration. */
13267453645fSAndriy Voskoboinyk 		callout_reset(&sc->sc_calib_to, 2*hz, rtwn_calib_to,
13277453645fSAndriy Voskoboinyk 		    sc);
13287453645fSAndriy Voskoboinyk 
13297453645fSAndriy Voskoboinyk 		if (sc->vaps_running == 0) {
1330bcaed14bSAdrian Chadd 			/* Turn link LED on. */
1331bcaed14bSAdrian Chadd 			rtwn_set_led(sc, RTWN_LED_LINK, 1);
1332bcaed14bSAdrian Chadd 		}
1333bcaed14bSAdrian Chadd 	}
1334bcaed14bSAdrian Chadd 
13357453645fSAndriy Voskoboinyk fail:
1336bcaed14bSAdrian Chadd 	ieee80211_free_node(ni);
1337bcaed14bSAdrian Chadd 
1338bcaed14bSAdrian Chadd 	return (error);
1339bcaed14bSAdrian Chadd }
1340bcaed14bSAdrian Chadd 
13417453645fSAndriy Voskoboinyk #ifndef D4054
1342bcaed14bSAdrian Chadd static void
rtwn_watchdog(void * arg)1343bcaed14bSAdrian Chadd rtwn_watchdog(void *arg)
1344bcaed14bSAdrian Chadd {
1345bcaed14bSAdrian Chadd 	struct rtwn_softc *sc = arg;
1346bcaed14bSAdrian Chadd 	struct ieee80211com *ic = &sc->sc_ic;
1347bcaed14bSAdrian Chadd 
13487453645fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
1349bcaed14bSAdrian Chadd 
1350bcaed14bSAdrian Chadd 	KASSERT(sc->sc_flags & RTWN_RUNNING, ("not running"));
1351bcaed14bSAdrian Chadd 
1352bcaed14bSAdrian Chadd 	if (sc->sc_tx_timer != 0 && --sc->sc_tx_timer == 0) {
1353bcaed14bSAdrian Chadd 		ic_printf(ic, "device timeout\n");
13545274f944SAndriy Voskoboinyk 		ieee80211_restart_all(ic);
1355bcaed14bSAdrian Chadd 		return;
1356bcaed14bSAdrian Chadd 	}
13577453645fSAndriy Voskoboinyk 	callout_reset(&sc->sc_watchdog_to, hz, rtwn_watchdog, sc);
1358bcaed14bSAdrian Chadd }
13597453645fSAndriy Voskoboinyk #endif
13607453645fSAndriy Voskoboinyk 
13617453645fSAndriy Voskoboinyk static void
rtwn_parent(struct ieee80211com * ic)13627453645fSAndriy Voskoboinyk rtwn_parent(struct ieee80211com *ic)
13637453645fSAndriy Voskoboinyk {
13647453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = ic->ic_softc;
13657453645fSAndriy Voskoboinyk 	struct ieee80211vap *vap;
13667453645fSAndriy Voskoboinyk 
13677453645fSAndriy Voskoboinyk 	if (ic->ic_nrunning > 0) {
13687453645fSAndriy Voskoboinyk 		if (rtwn_init(sc) != 0) {
13697453645fSAndriy Voskoboinyk 			IEEE80211_LOCK(ic);
13707453645fSAndriy Voskoboinyk 			TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
13717453645fSAndriy Voskoboinyk 				ieee80211_stop_locked(vap);
13727453645fSAndriy Voskoboinyk 			IEEE80211_UNLOCK(ic);
13737453645fSAndriy Voskoboinyk 		} else
13747453645fSAndriy Voskoboinyk 			ieee80211_start_all(ic);
13757453645fSAndriy Voskoboinyk 	} else
13767453645fSAndriy Voskoboinyk 		rtwn_stop(sc);
13777453645fSAndriy Voskoboinyk }
13787453645fSAndriy Voskoboinyk 
1379bcaed14bSAdrian Chadd static int
rtwn_dma_init(struct rtwn_softc * sc)1380bcaed14bSAdrian Chadd rtwn_dma_init(struct rtwn_softc *sc)
1381bcaed14bSAdrian Chadd {
13827453645fSAndriy Voskoboinyk #define RTWN_CHK(res) do {	\
13837453645fSAndriy Voskoboinyk 	if (res != 0)		\
13847453645fSAndriy Voskoboinyk 		return (EIO);	\
13857453645fSAndriy Voskoboinyk } while(0)
13867453645fSAndriy Voskoboinyk 	uint16_t reg;
13877453645fSAndriy Voskoboinyk 	uint8_t tx_boundary;
1388bcaed14bSAdrian Chadd 	int error;
1389bcaed14bSAdrian Chadd 
1390bcaed14bSAdrian Chadd 	/* Initialize LLT table. */
1391bcaed14bSAdrian Chadd 	error = rtwn_llt_init(sc);
1392bcaed14bSAdrian Chadd 	if (error != 0)
13937453645fSAndriy Voskoboinyk 		return (error);
1394bcaed14bSAdrian Chadd 
13957453645fSAndriy Voskoboinyk 	/* Set the number of pages for each queue. */
13967453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_RESET,
13977453645fSAndriy Voskoboinyk 	    "%s: pages per queue: high %d, normal %d, low %d, public %d\n",
13987453645fSAndriy Voskoboinyk 	    __func__, sc->nhqpages, sc->nnqpages, sc->nlqpages,
13997453645fSAndriy Voskoboinyk 	    sc->npubqpages);
14007453645fSAndriy Voskoboinyk 
14017453645fSAndriy Voskoboinyk 	RTWN_CHK(rtwn_write_1(sc, R92C_RQPN_NPQ, sc->nnqpages));
14027453645fSAndriy Voskoboinyk 	RTWN_CHK(rtwn_write_4(sc, R92C_RQPN,
1403bcaed14bSAdrian Chadd 	    /* Set number of pages for public queue. */
14047453645fSAndriy Voskoboinyk 	    SM(R92C_RQPN_PUBQ, sc->npubqpages) |
1405bcaed14bSAdrian Chadd 	    /* Set number of pages for high priority queue. */
14067453645fSAndriy Voskoboinyk 	    SM(R92C_RQPN_HPQ, sc->nhqpages) |
1407bcaed14bSAdrian Chadd 	    /* Set number of pages for low priority queue. */
14087453645fSAndriy Voskoboinyk 	    SM(R92C_RQPN_LPQ, sc->nlqpages) |
1409bcaed14bSAdrian Chadd 	    /* Load values. */
14107453645fSAndriy Voskoboinyk 	    R92C_RQPN_LD));
1411bcaed14bSAdrian Chadd 
14127453645fSAndriy Voskoboinyk 	/* Initialize TX buffer boundary. */
14137453645fSAndriy Voskoboinyk 	KASSERT(sc->page_count < 255 && sc->page_count > 0,
14147453645fSAndriy Voskoboinyk 	    ("page_count is %d\n", sc->page_count));
14157453645fSAndriy Voskoboinyk 	tx_boundary = sc->page_count + 1;
14167453645fSAndriy Voskoboinyk 	RTWN_CHK(rtwn_write_1(sc, R92C_TXPKTBUF_BCNQ_BDNY, tx_boundary));
14177453645fSAndriy Voskoboinyk 	RTWN_CHK(rtwn_write_1(sc, R92C_TXPKTBUF_MGQ_BDNY, tx_boundary));
14187453645fSAndriy Voskoboinyk 	RTWN_CHK(rtwn_write_1(sc, R92C_TXPKTBUF_WMAC_LBK_BF_HD, tx_boundary));
14197453645fSAndriy Voskoboinyk 	RTWN_CHK(rtwn_write_1(sc, R92C_TRXFF_BNDY, tx_boundary));
14207453645fSAndriy Voskoboinyk 	RTWN_CHK(rtwn_write_1(sc, R92C_TDECTRL + 1, tx_boundary));
1421bcaed14bSAdrian Chadd 
14227453645fSAndriy Voskoboinyk 	error = rtwn_init_bcnq1_boundary(sc);
14237453645fSAndriy Voskoboinyk 	if (error != 0)
14247453645fSAndriy Voskoboinyk 		return (error);
1425bcaed14bSAdrian Chadd 
14267453645fSAndriy Voskoboinyk 	/* Set queue to USB pipe mapping. */
14277453645fSAndriy Voskoboinyk 	/* Note: PCIe devices are using some magic number here. */
14287453645fSAndriy Voskoboinyk 	reg = rtwn_get_qmap(sc);
14297453645fSAndriy Voskoboinyk 	RTWN_CHK(rtwn_setbits_2(sc, R92C_TRXDMA_CTRL,
14307453645fSAndriy Voskoboinyk 	    R92C_TRXDMA_CTRL_QMAP_M, reg));
1431bcaed14bSAdrian Chadd 
14327453645fSAndriy Voskoboinyk 	/* Configure Tx/Rx DMA (PCIe). */
14337453645fSAndriy Voskoboinyk 	rtwn_set_desc_addr(sc);
1434bcaed14bSAdrian Chadd 
1435bcaed14bSAdrian Chadd 	/* Set Tx/Rx transfer page boundary. */
14367453645fSAndriy Voskoboinyk 	RTWN_CHK(rtwn_write_2(sc, R92C_TRXFF_BNDY + 2,
14377453645fSAndriy Voskoboinyk 	    sc->rx_dma_size - 1));
1438bcaed14bSAdrian Chadd 
1439bcaed14bSAdrian Chadd 	/* Set Tx/Rx transfer page size. */
14407453645fSAndriy Voskoboinyk 	rtwn_set_page_size(sc);
14417453645fSAndriy Voskoboinyk 
14427453645fSAndriy Voskoboinyk 	return (0);
14437453645fSAndriy Voskoboinyk }
14447453645fSAndriy Voskoboinyk 
14457453645fSAndriy Voskoboinyk static int
rtwn_mac_init(struct rtwn_softc * sc)14467453645fSAndriy Voskoboinyk rtwn_mac_init(struct rtwn_softc *sc)
14477453645fSAndriy Voskoboinyk {
14487453645fSAndriy Voskoboinyk 	int i, error;
14497453645fSAndriy Voskoboinyk 
14507453645fSAndriy Voskoboinyk 	/* Write MAC initialization values. */
14517453645fSAndriy Voskoboinyk 	for (i = 0; i < sc->mac_size; i++) {
14527453645fSAndriy Voskoboinyk 		error = rtwn_write_1(sc, sc->mac_prog[i].reg,
14537453645fSAndriy Voskoboinyk 		    sc->mac_prog[i].val);
14547453645fSAndriy Voskoboinyk 		if (error != 0)
14557453645fSAndriy Voskoboinyk 			return (error);
14567453645fSAndriy Voskoboinyk 	}
14577453645fSAndriy Voskoboinyk 
1458bcaed14bSAdrian Chadd 	return (0);
1459bcaed14bSAdrian Chadd }
1460bcaed14bSAdrian Chadd 
1461bcaed14bSAdrian Chadd static void
rtwn_mrr_init(struct rtwn_softc * sc)14627453645fSAndriy Voskoboinyk rtwn_mrr_init(struct rtwn_softc *sc)
1463bcaed14bSAdrian Chadd {
1464bcaed14bSAdrian Chadd 	int i;
1465bcaed14bSAdrian Chadd 
14667453645fSAndriy Voskoboinyk 	/* Drop rate index by 1 per retry. */
14677453645fSAndriy Voskoboinyk 	for (i = 0; i < R92C_DARFRC_SIZE; i++) {
14687453645fSAndriy Voskoboinyk 		rtwn_write_1(sc, R92C_DARFRC + i, i + 1);
14697453645fSAndriy Voskoboinyk 		rtwn_write_1(sc, R92C_RARFRC + i, i + 1);
1470bcaed14bSAdrian Chadd 	}
14715036353aSAndriy Voskoboinyk }
14725036353aSAndriy Voskoboinyk 
14735036353aSAndriy Voskoboinyk static void
rtwn_scan_start(struct ieee80211com * ic)1474bcaed14bSAdrian Chadd rtwn_scan_start(struct ieee80211com *ic)
1475bcaed14bSAdrian Chadd {
14765036353aSAndriy Voskoboinyk 	struct rtwn_softc *sc = ic->ic_softc;
1477bcaed14bSAdrian Chadd 
14785036353aSAndriy Voskoboinyk 	RTWN_LOCK(sc);
14798361f9cdSAndriy Voskoboinyk 	/* Pause beaconing. */
14808361f9cdSAndriy Voskoboinyk 	rtwn_setbits_1(sc, R92C_TXPAUSE, 0, R92C_TX_QUEUE_BCN);
14815036353aSAndriy Voskoboinyk 	/* Receive beacons / probe responses from any BSSID. */
14827453645fSAndriy Voskoboinyk 	if (sc->bcn_vaps == 0)
14835036353aSAndriy Voskoboinyk 		rtwn_set_rx_bssid_all(sc, 1);
14845036353aSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
1485bcaed14bSAdrian Chadd }
1486bcaed14bSAdrian Chadd 
1487bcaed14bSAdrian Chadd static void
rtwn_scan_curchan(struct ieee80211_scan_state * ss,unsigned long maxdwell)14887453645fSAndriy Voskoboinyk rtwn_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
14897453645fSAndriy Voskoboinyk {
14907453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = ss->ss_ic->ic_softc;
14917453645fSAndriy Voskoboinyk 
14927453645fSAndriy Voskoboinyk 	/* Make link LED blink during scan. */
14937453645fSAndriy Voskoboinyk 	RTWN_LOCK(sc);
14947453645fSAndriy Voskoboinyk 	rtwn_set_led(sc, RTWN_LED_LINK, !sc->ledlink);
14957453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
14967453645fSAndriy Voskoboinyk 
14977453645fSAndriy Voskoboinyk 	sc->sc_scan_curchan(ss, maxdwell);
14987453645fSAndriy Voskoboinyk }
14997453645fSAndriy Voskoboinyk 
15007453645fSAndriy Voskoboinyk static void
rtwn_scan_end(struct ieee80211com * ic)1501bcaed14bSAdrian Chadd rtwn_scan_end(struct ieee80211com *ic)
1502bcaed14bSAdrian Chadd {
15035036353aSAndriy Voskoboinyk 	struct rtwn_softc *sc = ic->ic_softc;
1504bcaed14bSAdrian Chadd 
15055036353aSAndriy Voskoboinyk 	RTWN_LOCK(sc);
15065036353aSAndriy Voskoboinyk 	/* Restore limitations. */
15077453645fSAndriy Voskoboinyk 	if (ic->ic_promisc == 0 && sc->bcn_vaps == 0)
15085036353aSAndriy Voskoboinyk 		rtwn_set_rx_bssid_all(sc, 0);
15097453645fSAndriy Voskoboinyk 
15107453645fSAndriy Voskoboinyk 	/* Restore LED state. */
15117453645fSAndriy Voskoboinyk 	rtwn_set_led(sc, RTWN_LED_LINK, (sc->vaps_running != 0));
15127453645fSAndriy Voskoboinyk 
15137453645fSAndriy Voskoboinyk 	/* Restore basic rates mask. */
15147453645fSAndriy Voskoboinyk 	rtwn_calc_basicrates(sc);
15158361f9cdSAndriy Voskoboinyk 
15168361f9cdSAndriy Voskoboinyk 	/* Resume beaconing. */
15178361f9cdSAndriy Voskoboinyk 	rtwn_setbits_1(sc, R92C_TXPAUSE, R92C_TX_QUEUE_BCN, 0);
15185036353aSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
1519bcaed14bSAdrian Chadd }
1520bcaed14bSAdrian Chadd 
1521bcaed14bSAdrian Chadd static void
rtwn_getradiocaps(struct ieee80211com * ic,int maxchans,int * nchans,struct ieee80211_channel chans[])1522a7c31fe1SAndriy Voskoboinyk rtwn_getradiocaps(struct ieee80211com *ic,
1523a7c31fe1SAndriy Voskoboinyk     int maxchans, int *nchans, struct ieee80211_channel chans[])
1524a7c31fe1SAndriy Voskoboinyk {
15257453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = ic->ic_softc;
1526a7c31fe1SAndriy Voskoboinyk 	uint8_t bands[IEEE80211_MODE_BYTES];
15272b9f12f6SBjoern A. Zeeb 	int cbw_flags, i;
15282b9f12f6SBjoern A. Zeeb 
15292b9f12f6SBjoern A. Zeeb 	cbw_flags = (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) ?
15302b9f12f6SBjoern A. Zeeb 	    NET80211_CBW_FLAG_HT40 : 0;
1531a7c31fe1SAndriy Voskoboinyk 
1532a7c31fe1SAndriy Voskoboinyk 	memset(bands, 0, sizeof(bands));
1533a7c31fe1SAndriy Voskoboinyk 	setbit(bands, IEEE80211_MODE_11B);
1534a7c31fe1SAndriy Voskoboinyk 	setbit(bands, IEEE80211_MODE_11G);
15357453645fSAndriy Voskoboinyk 	setbit(bands, IEEE80211_MODE_11NG);
1536b84b3638SAndriy Voskoboinyk 	ieee80211_add_channels_default_2ghz(chans, maxchans, nchans,
15372b9f12f6SBjoern A. Zeeb 	    bands, cbw_flags);
15387453645fSAndriy Voskoboinyk 
15397453645fSAndriy Voskoboinyk 	/* XXX workaround add_channel_list() limitations */
15407453645fSAndriy Voskoboinyk 	setbit(bands, IEEE80211_MODE_11A);
15417453645fSAndriy Voskoboinyk 	setbit(bands, IEEE80211_MODE_11NA);
15427453645fSAndriy Voskoboinyk 	for (i = 0; i < nitems(sc->chan_num_5ghz); i++) {
15437453645fSAndriy Voskoboinyk 		if (sc->chan_num_5ghz[i] == 0)
15447453645fSAndriy Voskoboinyk 			continue;
15457453645fSAndriy Voskoboinyk 
15467453645fSAndriy Voskoboinyk 		ieee80211_add_channel_list_5ghz(chans, maxchans, nchans,
15477453645fSAndriy Voskoboinyk 		    sc->chan_list_5ghz[i], sc->chan_num_5ghz[i], bands,
15482b9f12f6SBjoern A. Zeeb 		    cbw_flags);
15497453645fSAndriy Voskoboinyk 	}
15507453645fSAndriy Voskoboinyk }
15517453645fSAndriy Voskoboinyk 
15527453645fSAndriy Voskoboinyk static void
rtwn_update_chw(struct ieee80211com * ic)15537453645fSAndriy Voskoboinyk rtwn_update_chw(struct ieee80211com *ic)
15547453645fSAndriy Voskoboinyk {
1555a7c31fe1SAndriy Voskoboinyk }
1556a7c31fe1SAndriy Voskoboinyk 
1557a7c31fe1SAndriy Voskoboinyk static void
rtwn_set_channel(struct ieee80211com * ic)1558bcaed14bSAdrian Chadd rtwn_set_channel(struct ieee80211com *ic)
1559bcaed14bSAdrian Chadd {
1560bcaed14bSAdrian Chadd 	struct rtwn_softc *sc = ic->ic_softc;
15617453645fSAndriy Voskoboinyk 	struct ieee80211_channel *c = ic->ic_curchan;
1562bcaed14bSAdrian Chadd 
1563bcaed14bSAdrian Chadd 	RTWN_LOCK(sc);
15647453645fSAndriy Voskoboinyk 	rtwn_set_chan(sc, c);
15657453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
1566bcaed14bSAdrian Chadd }
15677453645fSAndriy Voskoboinyk 
15687453645fSAndriy Voskoboinyk static int
rtwn_wme_update(struct ieee80211com * ic)15697453645fSAndriy Voskoboinyk rtwn_wme_update(struct ieee80211com *ic)
15707453645fSAndriy Voskoboinyk {
15719fbe631aSAdrian Chadd 	struct chanAccParams chp;
15727453645fSAndriy Voskoboinyk 	struct ieee80211_channel *c = ic->ic_curchan;
15737453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = ic->ic_softc;
15747453645fSAndriy Voskoboinyk 	struct wmeParams *wmep = sc->cap_wmeParams;
15757453645fSAndriy Voskoboinyk 	uint8_t aifs, acm, slottime;
15767453645fSAndriy Voskoboinyk 	int ac;
15777453645fSAndriy Voskoboinyk 
15789fbe631aSAdrian Chadd 	ieee80211_wme_ic_getparams(ic, &chp);
15799fbe631aSAdrian Chadd 
15807453645fSAndriy Voskoboinyk 	/* Prevent possible races. */
15817453645fSAndriy Voskoboinyk 	IEEE80211_LOCK(ic);	/* XXX */
15827453645fSAndriy Voskoboinyk 	RTWN_LOCK(sc);
15839fbe631aSAdrian Chadd 	memcpy(wmep, chp.cap_wmeParams, sizeof(sc->cap_wmeParams));
15847453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
15857453645fSAndriy Voskoboinyk 	IEEE80211_UNLOCK(ic);
15867453645fSAndriy Voskoboinyk 
15877453645fSAndriy Voskoboinyk 	acm = 0;
15887453645fSAndriy Voskoboinyk 	slottime = IEEE80211_GET_SLOTTIME(ic);
15897453645fSAndriy Voskoboinyk 
15907453645fSAndriy Voskoboinyk 	RTWN_LOCK(sc);
15917453645fSAndriy Voskoboinyk 	for (ac = WME_AC_BE; ac < WME_NUM_AC; ac++) {
15927453645fSAndriy Voskoboinyk 		/* AIFS[AC] = AIFSN[AC] * aSlotTime + aSIFSTime. */
15937453645fSAndriy Voskoboinyk 		aifs = wmep[ac].wmep_aifsn * slottime +
15947453645fSAndriy Voskoboinyk 		    (IEEE80211_IS_CHAN_5GHZ(c) ?
15957453645fSAndriy Voskoboinyk 			IEEE80211_DUR_OFDM_SIFS : IEEE80211_DUR_SIFS);
15967453645fSAndriy Voskoboinyk 		rtwn_write_4(sc, wme2reg[ac],
15977453645fSAndriy Voskoboinyk 		    SM(R92C_EDCA_PARAM_TXOP, wmep[ac].wmep_txopLimit) |
15987453645fSAndriy Voskoboinyk 		    SM(R92C_EDCA_PARAM_ECWMIN, wmep[ac].wmep_logcwmin) |
15997453645fSAndriy Voskoboinyk 		    SM(R92C_EDCA_PARAM_ECWMAX, wmep[ac].wmep_logcwmax) |
16007453645fSAndriy Voskoboinyk 		    SM(R92C_EDCA_PARAM_AIFS, aifs));
16017453645fSAndriy Voskoboinyk 		if (ac != WME_AC_BE)
16027453645fSAndriy Voskoboinyk 			acm |= wmep[ac].wmep_acm << ac;
16037453645fSAndriy Voskoboinyk 	}
16047453645fSAndriy Voskoboinyk 
16057453645fSAndriy Voskoboinyk 	if (acm != 0)
16067453645fSAndriy Voskoboinyk 		acm |= R92C_ACMHWCTRL_EN;
16077453645fSAndriy Voskoboinyk 	rtwn_setbits_1(sc, R92C_ACMHWCTRL, R92C_ACMHWCTRL_ACM_MASK, acm);
16087453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
16097453645fSAndriy Voskoboinyk 
16107453645fSAndriy Voskoboinyk 	return 0;
16117453645fSAndriy Voskoboinyk }
16127453645fSAndriy Voskoboinyk 
16137453645fSAndriy Voskoboinyk static void
rtwn_update_slot(struct ieee80211com * ic)16147453645fSAndriy Voskoboinyk rtwn_update_slot(struct ieee80211com *ic)
16157453645fSAndriy Voskoboinyk {
16167453645fSAndriy Voskoboinyk 	rtwn_cmd_sleepable(ic->ic_softc, NULL, 0, rtwn_update_slot_cb);
16177453645fSAndriy Voskoboinyk }
16187453645fSAndriy Voskoboinyk 
16197453645fSAndriy Voskoboinyk static void
rtwn_update_slot_cb(struct rtwn_softc * sc,union sec_param * data)16207453645fSAndriy Voskoboinyk rtwn_update_slot_cb(struct rtwn_softc *sc, union sec_param *data)
16217453645fSAndriy Voskoboinyk {
16227453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
16237453645fSAndriy Voskoboinyk 	uint8_t slottime;
16247453645fSAndriy Voskoboinyk 
16257453645fSAndriy Voskoboinyk 	slottime = IEEE80211_GET_SLOTTIME(ic);
16267453645fSAndriy Voskoboinyk 
16277453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_STATE, "%s: setting slot time to %uus\n",
16287453645fSAndriy Voskoboinyk 	    __func__, slottime);
16297453645fSAndriy Voskoboinyk 
16307453645fSAndriy Voskoboinyk 	rtwn_write_1(sc, R92C_SLOT, slottime);
16317453645fSAndriy Voskoboinyk 	rtwn_update_aifs(sc, slottime);
16327453645fSAndriy Voskoboinyk }
16337453645fSAndriy Voskoboinyk 
16347453645fSAndriy Voskoboinyk static void
rtwn_update_aifs(struct rtwn_softc * sc,uint8_t slottime)16357453645fSAndriy Voskoboinyk rtwn_update_aifs(struct rtwn_softc *sc, uint8_t slottime)
16367453645fSAndriy Voskoboinyk {
16377453645fSAndriy Voskoboinyk 	struct ieee80211_channel *c = sc->sc_ic.ic_curchan;
16387453645fSAndriy Voskoboinyk 	const struct wmeParams *wmep = sc->cap_wmeParams;
16397453645fSAndriy Voskoboinyk 	uint8_t aifs, ac;
16407453645fSAndriy Voskoboinyk 
16417453645fSAndriy Voskoboinyk 	for (ac = WME_AC_BE; ac < WME_NUM_AC; ac++) {
16427453645fSAndriy Voskoboinyk 		/* AIFS[AC] = AIFSN[AC] * aSlotTime + aSIFSTime. */
16437453645fSAndriy Voskoboinyk 		aifs = wmep[ac].wmep_aifsn * slottime +
16447453645fSAndriy Voskoboinyk 		    (IEEE80211_IS_CHAN_5GHZ(c) ?
16457453645fSAndriy Voskoboinyk 			IEEE80211_DUR_OFDM_SIFS : IEEE80211_DUR_SIFS);
16467453645fSAndriy Voskoboinyk 		rtwn_write_1(sc, wme2reg[ac], aifs);
16477453645fSAndriy Voskoboinyk 	}
16487453645fSAndriy Voskoboinyk }
16497453645fSAndriy Voskoboinyk 
16507453645fSAndriy Voskoboinyk static void
rtwn_update_promisc(struct ieee80211com * ic)16517453645fSAndriy Voskoboinyk rtwn_update_promisc(struct ieee80211com *ic)
16527453645fSAndriy Voskoboinyk {
16537453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = ic->ic_softc;
16547453645fSAndriy Voskoboinyk 
16557453645fSAndriy Voskoboinyk 	RTWN_LOCK(sc);
16567453645fSAndriy Voskoboinyk 	if (sc->sc_flags & RTWN_RUNNING)
16577453645fSAndriy Voskoboinyk 		rtwn_set_promisc(sc);
1658bcaed14bSAdrian Chadd 	RTWN_UNLOCK(sc);
1659bcaed14bSAdrian Chadd }
1660bcaed14bSAdrian Chadd 
1661bcaed14bSAdrian Chadd static void
rtwn_update_mcast(struct ieee80211com * ic)1662bcaed14bSAdrian Chadd rtwn_update_mcast(struct ieee80211com *ic)
1663bcaed14bSAdrian Chadd {
16647453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = ic->ic_softc;
1665bcaed14bSAdrian Chadd 
16667453645fSAndriy Voskoboinyk 	RTWN_LOCK(sc);
16677453645fSAndriy Voskoboinyk 	if (sc->sc_flags & RTWN_RUNNING)
16687453645fSAndriy Voskoboinyk 		rtwn_set_multi(sc);
16697453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
1670bcaed14bSAdrian Chadd }
1671bcaed14bSAdrian Chadd 
1672bcaed14bSAdrian Chadd static int
rtwn_set_bssid(struct rtwn_softc * sc,const uint8_t * bssid,int id)16737453645fSAndriy Voskoboinyk rtwn_set_bssid(struct rtwn_softc *sc, const uint8_t *bssid, int id)
1674bcaed14bSAdrian Chadd {
16757453645fSAndriy Voskoboinyk 	int error;
1676bcaed14bSAdrian Chadd 
16777453645fSAndriy Voskoboinyk 	error = rtwn_write_4(sc, R92C_BSSID(id), le32dec(&bssid[0]));
16787453645fSAndriy Voskoboinyk 	if (error != 0)
16797453645fSAndriy Voskoboinyk 		return (error);
16807453645fSAndriy Voskoboinyk 	error = rtwn_write_2(sc, R92C_BSSID(id) + 4, le16dec(&bssid[4]));
1681bcaed14bSAdrian Chadd 
16827453645fSAndriy Voskoboinyk 	return (error);
1683bcaed14bSAdrian Chadd }
1684bcaed14bSAdrian Chadd 
1685bcaed14bSAdrian Chadd static int
rtwn_set_macaddr(struct rtwn_softc * sc,const uint8_t * addr,int id)16867453645fSAndriy Voskoboinyk rtwn_set_macaddr(struct rtwn_softc *sc, const uint8_t *addr, int id)
1687bcaed14bSAdrian Chadd {
16887453645fSAndriy Voskoboinyk 	int error;
1689bcaed14bSAdrian Chadd 
16907453645fSAndriy Voskoboinyk 	error = rtwn_write_4(sc, R92C_MACID(id), le32dec(&addr[0]));
16917453645fSAndriy Voskoboinyk 	if (error != 0)
16927453645fSAndriy Voskoboinyk 		return (error);
16937453645fSAndriy Voskoboinyk 	error = rtwn_write_2(sc, R92C_MACID(id) + 4, le16dec(&addr[4]));
1694bcaed14bSAdrian Chadd 
16957453645fSAndriy Voskoboinyk 	return (error);
1696bcaed14bSAdrian Chadd }
1697bcaed14bSAdrian Chadd 
16987453645fSAndriy Voskoboinyk static struct ieee80211_node *
rtwn_node_alloc(struct ieee80211vap * vap,const uint8_t mac[IEEE80211_ADDR_LEN])16997453645fSAndriy Voskoboinyk rtwn_node_alloc(struct ieee80211vap *vap,
17007453645fSAndriy Voskoboinyk     const uint8_t mac[IEEE80211_ADDR_LEN])
17017453645fSAndriy Voskoboinyk {
17027453645fSAndriy Voskoboinyk 	struct rtwn_node *un;
17037453645fSAndriy Voskoboinyk 
17047453645fSAndriy Voskoboinyk 	un = malloc(sizeof (struct rtwn_node), M_80211_NODE,
17057453645fSAndriy Voskoboinyk 	    M_NOWAIT | M_ZERO);
17067453645fSAndriy Voskoboinyk 
17077453645fSAndriy Voskoboinyk 	if (un == NULL)
17087453645fSAndriy Voskoboinyk 		return NULL;
17097453645fSAndriy Voskoboinyk 
17107453645fSAndriy Voskoboinyk 	un->id = RTWN_MACID_UNDEFINED;
17117453645fSAndriy Voskoboinyk 	un->avg_pwdb = -1;
17127453645fSAndriy Voskoboinyk 
17137453645fSAndriy Voskoboinyk 	return &un->ni;
1714bcaed14bSAdrian Chadd }
1715bcaed14bSAdrian Chadd 
1716bcaed14bSAdrian Chadd static void
rtwn_newassoc(struct ieee80211_node * ni,int isnew __unused)171790589b90SAndriy Voskoboinyk rtwn_newassoc(struct ieee80211_node *ni, int isnew __unused)
1718bcaed14bSAdrian Chadd {
17197453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = ni->ni_ic->ic_softc;
17207453645fSAndriy Voskoboinyk 	struct rtwn_node *un = RTWN_NODE(ni);
17217453645fSAndriy Voskoboinyk 	int id;
1722bcaed14bSAdrian Chadd 
172390589b90SAndriy Voskoboinyk 	if (un->id != RTWN_MACID_UNDEFINED)
1724bcaed14bSAdrian Chadd 		return;
1725bcaed14bSAdrian Chadd 
17267453645fSAndriy Voskoboinyk 	RTWN_NT_LOCK(sc);
17277453645fSAndriy Voskoboinyk 	for (id = 0; id <= sc->macid_limit; id++) {
17287453645fSAndriy Voskoboinyk 		if (id != RTWN_MACID_BC && sc->node_list[id] == NULL) {
17297453645fSAndriy Voskoboinyk 			un->id = id;
17307453645fSAndriy Voskoboinyk 			sc->node_list[id] = ni;
1731bcaed14bSAdrian Chadd 			break;
1732bcaed14bSAdrian Chadd 		}
1733bcaed14bSAdrian Chadd 	}
17347453645fSAndriy Voskoboinyk 	RTWN_NT_UNLOCK(sc);
17357453645fSAndriy Voskoboinyk 
17367453645fSAndriy Voskoboinyk 	if (id > sc->macid_limit) {
17377453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev, "%s: node table is full\n",
17387453645fSAndriy Voskoboinyk 		    __func__);
17397453645fSAndriy Voskoboinyk 		return;
1740bcaed14bSAdrian Chadd 	}
1741bcaed14bSAdrian Chadd 
17427453645fSAndriy Voskoboinyk 	/* Notify firmware. */
17437453645fSAndriy Voskoboinyk 	id |= RTWN_MACID_VALID;
17447453645fSAndriy Voskoboinyk 	rtwn_cmd_sleepable(sc, &id, sizeof(id), rtwn_set_media_status);
1745bcaed14bSAdrian Chadd }
1746bcaed14bSAdrian Chadd 
1747bcaed14bSAdrian Chadd static void
rtwn_node_free(struct ieee80211_node * ni)17487453645fSAndriy Voskoboinyk rtwn_node_free(struct ieee80211_node *ni)
1749bcaed14bSAdrian Chadd {
17507453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = ni->ni_ic->ic_softc;
17517453645fSAndriy Voskoboinyk 	struct rtwn_node *un = RTWN_NODE(ni);
1752bcaed14bSAdrian Chadd 
17537453645fSAndriy Voskoboinyk 	RTWN_NT_LOCK(sc);
17547453645fSAndriy Voskoboinyk 	if (un->id != RTWN_MACID_UNDEFINED) {
17557453645fSAndriy Voskoboinyk 		sc->node_list[un->id] = NULL;
17567453645fSAndriy Voskoboinyk 		rtwn_cmd_sleepable(sc, &un->id, sizeof(un->id),
17577453645fSAndriy Voskoboinyk 		    rtwn_set_media_status);
1758bcaed14bSAdrian Chadd 	}
17597453645fSAndriy Voskoboinyk 	RTWN_NT_UNLOCK(sc);
1760bcaed14bSAdrian Chadd 
17617453645fSAndriy Voskoboinyk 	sc->sc_node_free(ni);
1762bcaed14bSAdrian Chadd }
17637453645fSAndriy Voskoboinyk 
17647453645fSAndriy Voskoboinyk static void
rtwn_init_beacon_reg(struct rtwn_softc * sc)17657453645fSAndriy Voskoboinyk rtwn_init_beacon_reg(struct rtwn_softc *sc)
17667453645fSAndriy Voskoboinyk {
17677453645fSAndriy Voskoboinyk 	rtwn_write_1(sc, R92C_BCN_CTRL(0), R92C_BCN_CTRL_DIS_TSF_UDT0);
17687453645fSAndriy Voskoboinyk 	rtwn_write_1(sc, R92C_BCN_CTRL(1), R92C_BCN_CTRL_DIS_TSF_UDT0);
17697453645fSAndriy Voskoboinyk 	rtwn_write_2(sc, R92C_TBTT_PROHIBIT, 0x6404);
17707453645fSAndriy Voskoboinyk 	rtwn_write_1(sc, R92C_DRVERLYINT, 0x05);
17717453645fSAndriy Voskoboinyk 	rtwn_write_1(sc, R92C_BCNDMATIM, 0x02);
17727453645fSAndriy Voskoboinyk 	rtwn_write_2(sc, R92C_BCNTCFG, 0x660f);
1773bcaed14bSAdrian Chadd }
1774bcaed14bSAdrian Chadd 
1775798e1ce3SAndriy Voskoboinyk static int
rtwn_init(struct rtwn_softc * sc)1776798e1ce3SAndriy Voskoboinyk rtwn_init(struct rtwn_softc *sc)
1777bcaed14bSAdrian Chadd {
1778bcaed14bSAdrian Chadd 	struct ieee80211com *ic = &sc->sc_ic;
1779bcaed14bSAdrian Chadd 	int i, error;
1780bcaed14bSAdrian Chadd 
1781798e1ce3SAndriy Voskoboinyk 	RTWN_LOCK(sc);
1782798e1ce3SAndriy Voskoboinyk 	if (sc->sc_flags & RTWN_RUNNING) {
1783798e1ce3SAndriy Voskoboinyk 		RTWN_UNLOCK(sc);
17847453645fSAndriy Voskoboinyk 		return (0);
1785798e1ce3SAndriy Voskoboinyk 	}
17867453645fSAndriy Voskoboinyk 	sc->sc_flags |= RTWN_STARTED;
1787bcaed14bSAdrian Chadd 
1788bcaed14bSAdrian Chadd 	/* Power on adapter. */
1789bcaed14bSAdrian Chadd 	error = rtwn_power_on(sc);
17907453645fSAndriy Voskoboinyk 	if (error != 0)
17917453645fSAndriy Voskoboinyk 		goto fail;
17927453645fSAndriy Voskoboinyk 
17937453645fSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE
17947453645fSAndriy Voskoboinyk 	/* Load 8051 microcode. */
17957453645fSAndriy Voskoboinyk 	error = rtwn_load_firmware(sc);
17967453645fSAndriy Voskoboinyk 	if (error == 0)
17977453645fSAndriy Voskoboinyk 		sc->sc_flags |= RTWN_FW_LOADED;
17987453645fSAndriy Voskoboinyk 
17997453645fSAndriy Voskoboinyk 	/* Init firmware commands ring. */
18007453645fSAndriy Voskoboinyk 	sc->fwcur = 0;
18017453645fSAndriy Voskoboinyk #endif
18027453645fSAndriy Voskoboinyk 
18037453645fSAndriy Voskoboinyk 	/* Initialize MAC block. */
18047453645fSAndriy Voskoboinyk 	error = rtwn_mac_init(sc);
1805bcaed14bSAdrian Chadd 	if (error != 0) {
18067453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev,
18077453645fSAndriy Voskoboinyk 		    "%s: error while initializing MAC block\n", __func__);
1808bcaed14bSAdrian Chadd 		goto fail;
1809bcaed14bSAdrian Chadd 	}
1810bcaed14bSAdrian Chadd 
1811bcaed14bSAdrian Chadd 	/* Initialize DMA. */
1812bcaed14bSAdrian Chadd 	error = rtwn_dma_init(sc);
18137453645fSAndriy Voskoboinyk 	if (error != 0)
18147453645fSAndriy Voskoboinyk 		goto fail;
18157453645fSAndriy Voskoboinyk 
18167453645fSAndriy Voskoboinyk 	/* Drop incorrect TX (USB). */
18177453645fSAndriy Voskoboinyk 	rtwn_drop_incorrect_tx(sc);
18187453645fSAndriy Voskoboinyk 
18197453645fSAndriy Voskoboinyk 	/* Set info size in Rx descriptors (in 64-bit words). */
18207453645fSAndriy Voskoboinyk 	rtwn_write_1(sc, R92C_RX_DRVINFO_SZ, R92C_RX_DRVINFO_SZ_DEF);
18217453645fSAndriy Voskoboinyk 
18227453645fSAndriy Voskoboinyk 	/* Init interrupts. */
18237453645fSAndriy Voskoboinyk 	rtwn_init_intr(sc);
18247453645fSAndriy Voskoboinyk 
18257453645fSAndriy Voskoboinyk 	for (i = 0; i < nitems(sc->vaps); i++) {
18267453645fSAndriy Voskoboinyk 		struct rtwn_vap *uvp = sc->vaps[i];
18277453645fSAndriy Voskoboinyk 
18287453645fSAndriy Voskoboinyk 		/* Set initial network type. */
18297453645fSAndriy Voskoboinyk 		rtwn_set_mode(sc, R92C_MSR_NOLINK, i);
18307453645fSAndriy Voskoboinyk 
18317453645fSAndriy Voskoboinyk 		if (uvp == NULL)
18327453645fSAndriy Voskoboinyk 			continue;
18337453645fSAndriy Voskoboinyk 
18347453645fSAndriy Voskoboinyk 		/* Set MAC address. */
18357453645fSAndriy Voskoboinyk 		error = rtwn_set_macaddr(sc, uvp->vap.iv_myaddr, uvp->id);
18367453645fSAndriy Voskoboinyk 		if (error != 0)
1837bcaed14bSAdrian Chadd 			goto fail;
1838bcaed14bSAdrian Chadd 	}
1839bcaed14bSAdrian Chadd 
18407453645fSAndriy Voskoboinyk 	/* Initialize Rx filter. */
1841bcaed14bSAdrian Chadd 	rtwn_rxfilter_init(sc);
1842bcaed14bSAdrian Chadd 
1843bcaed14bSAdrian Chadd 	/* Set short/long retry limits. */
1844bcaed14bSAdrian Chadd 	rtwn_write_2(sc, R92C_RL,
18457453645fSAndriy Voskoboinyk 	    SM(R92C_RL_SRL, 0x30) | SM(R92C_RL_LRL, 0x30));
1846bcaed14bSAdrian Chadd 
1847bcaed14bSAdrian Chadd 	/* Initialize EDCA parameters. */
18487453645fSAndriy Voskoboinyk 	rtwn_init_edca(sc);
1849bcaed14bSAdrian Chadd 
18507453645fSAndriy Voskoboinyk 	rtwn_setbits_1(sc, R92C_FWHW_TXQ_CTRL, 0,
18517453645fSAndriy Voskoboinyk 	    R92C_FWHW_TXQ_CTRL_AMPDU_RTY_NEW);
1852bcaed14bSAdrian Chadd 	/* Set ACK timeout. */
18537453645fSAndriy Voskoboinyk 	rtwn_write_1(sc, R92C_ACKTO, sc->ackto);
18547453645fSAndriy Voskoboinyk 
18557453645fSAndriy Voskoboinyk 	/* Setup aggregation. */
18567453645fSAndriy Voskoboinyk 	/* Tx aggregation. */
18577453645fSAndriy Voskoboinyk 	rtwn_init_tx_agg(sc);
18587453645fSAndriy Voskoboinyk 	rtwn_init_rx_agg(sc);
1859bcaed14bSAdrian Chadd 
1860bcaed14bSAdrian Chadd 	/* Initialize beacon parameters. */
18617453645fSAndriy Voskoboinyk 	rtwn_init_beacon_reg(sc);
1862bcaed14bSAdrian Chadd 
18637453645fSAndriy Voskoboinyk 	/* Init A-MPDU parameters. */
18647453645fSAndriy Voskoboinyk 	rtwn_init_ampdu(sc);
1865bcaed14bSAdrian Chadd 
18667453645fSAndriy Voskoboinyk 	/* Init MACTXEN / MACRXEN after setting RxFF boundary. */
18677453645fSAndriy Voskoboinyk 	rtwn_setbits_1(sc, R92C_CR, 0, R92C_CR_MACTXEN | R92C_CR_MACRXEN);
1868bcaed14bSAdrian Chadd 
18697453645fSAndriy Voskoboinyk 	/* Initialize BB/RF blocks. */
18707453645fSAndriy Voskoboinyk 	rtwn_init_bb(sc);
18717453645fSAndriy Voskoboinyk 	rtwn_init_rf(sc);
1872bcaed14bSAdrian Chadd 
18737453645fSAndriy Voskoboinyk 	/* Initialize wireless band. */
18747453645fSAndriy Voskoboinyk 	rtwn_set_chan(sc, ic->ic_curchan);
1875bcaed14bSAdrian Chadd 
1876bcaed14bSAdrian Chadd 	/* Clear per-station keys table. */
18777453645fSAndriy Voskoboinyk 	rtwn_init_cam(sc);
1878bcaed14bSAdrian Chadd 
18797453645fSAndriy Voskoboinyk 	/* Enable decryption / encryption. */
18807453645fSAndriy Voskoboinyk 	rtwn_init_seccfg(sc);
1881bcaed14bSAdrian Chadd 
18827453645fSAndriy Voskoboinyk 	/* Install static keys (if any). */
18837453645fSAndriy Voskoboinyk 	for (i = 0; i < nitems(sc->vaps); i++) {
18847453645fSAndriy Voskoboinyk 		if (sc->vaps[i] != NULL) {
18857453645fSAndriy Voskoboinyk 			error = rtwn_init_static_keys(sc, sc->vaps[i]);
1886798e1ce3SAndriy Voskoboinyk 			if (error != 0)
18877453645fSAndriy Voskoboinyk 				goto fail;
18887453645fSAndriy Voskoboinyk 		}
1889bcaed14bSAdrian Chadd 	}
1890bcaed14bSAdrian Chadd 
18917453645fSAndriy Voskoboinyk 	/* Initialize antenna selection. */
18927453645fSAndriy Voskoboinyk 	rtwn_init_antsel(sc);
1893bcaed14bSAdrian Chadd 
18947453645fSAndriy Voskoboinyk 	/* Enable hardware sequence numbering. */
18957453645fSAndriy Voskoboinyk 	rtwn_write_1(sc, R92C_HWSEQ_CTRL, R92C_TX_QUEUE_ALL);
1896bcaed14bSAdrian Chadd 
18977453645fSAndriy Voskoboinyk 	/* Disable BAR. */
18987453645fSAndriy Voskoboinyk 	rtwn_write_4(sc, R92C_BAR_MODE_CTRL, 0x0201ffff);
1899798e1ce3SAndriy Voskoboinyk 
19007453645fSAndriy Voskoboinyk 	/* NAV limit. */
19017453645fSAndriy Voskoboinyk 	rtwn_write_1(sc, R92C_NAV_UPPER, 0);
1902bcaed14bSAdrian Chadd 
19037453645fSAndriy Voskoboinyk 	/* Initialize GPIO setting. */
19047453645fSAndriy Voskoboinyk 	rtwn_setbits_1(sc, R92C_GPIO_MUXCFG, R92C_GPIO_MUXCFG_ENBT, 0);
1905bcaed14bSAdrian Chadd 
19067453645fSAndriy Voskoboinyk 	/* Initialize MRR. */
19077453645fSAndriy Voskoboinyk 	rtwn_mrr_init(sc);
1908bcaed14bSAdrian Chadd 
19097453645fSAndriy Voskoboinyk 	/* Device-specific post initialization. */
19107453645fSAndriy Voskoboinyk 	rtwn_post_init(sc);
19117453645fSAndriy Voskoboinyk 
19127453645fSAndriy Voskoboinyk 	rtwn_start_xfers(sc);
19137453645fSAndriy Voskoboinyk 
19147453645fSAndriy Voskoboinyk #ifndef D4054
19157453645fSAndriy Voskoboinyk 	callout_reset(&sc->sc_watchdog_to, hz, rtwn_watchdog, sc);
19167453645fSAndriy Voskoboinyk #endif
19177453645fSAndriy Voskoboinyk 
19187453645fSAndriy Voskoboinyk 	sc->sc_flags |= RTWN_RUNNING;
19197453645fSAndriy Voskoboinyk fail:
19207453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
19217453645fSAndriy Voskoboinyk 
19227453645fSAndriy Voskoboinyk 	return (error);
1923bcaed14bSAdrian Chadd }
1924bcaed14bSAdrian Chadd 
1925bcaed14bSAdrian Chadd static void
rtwn_stop(struct rtwn_softc * sc)1926bcaed14bSAdrian Chadd rtwn_stop(struct rtwn_softc *sc)
1927bcaed14bSAdrian Chadd {
1928bcaed14bSAdrian Chadd 
1929bcaed14bSAdrian Chadd 	RTWN_LOCK(sc);
19307453645fSAndriy Voskoboinyk 	if (!(sc->sc_flags & RTWN_STARTED)) {
1931bcaed14bSAdrian Chadd 		RTWN_UNLOCK(sc);
1932bcaed14bSAdrian Chadd 		return;
1933bcaed14bSAdrian Chadd 	}
1934bcaed14bSAdrian Chadd 
19357453645fSAndriy Voskoboinyk #ifndef D4054
19367453645fSAndriy Voskoboinyk 	callout_stop(&sc->sc_watchdog_to);
19371318032eSAndriy Voskoboinyk 	sc->sc_tx_timer = 0;
19387453645fSAndriy Voskoboinyk #endif
19397453645fSAndriy Voskoboinyk 	sc->sc_flags &= ~(RTWN_STARTED | RTWN_RUNNING | RTWN_FW_LOADED);
19407453645fSAndriy Voskoboinyk 	sc->sc_flags &= ~RTWN_TEMP_MEASURED;
19417453645fSAndriy Voskoboinyk 	sc->fwver = 0;
19427453645fSAndriy Voskoboinyk 	sc->thcal_temp = 0;
19437453645fSAndriy Voskoboinyk 	sc->cur_bcnq_id = RTWN_VAP_ID_INVALID;
194409606165SAndriy Voskoboinyk 	bzero(&sc->last_physt, sizeof(sc->last_physt));
1945bcaed14bSAdrian Chadd 
19467453645fSAndriy Voskoboinyk #ifdef D4054
19477453645fSAndriy Voskoboinyk 	ieee80211_tx_watchdog_stop(&sc->sc_ic);
19487453645fSAndriy Voskoboinyk #endif
1949bcaed14bSAdrian Chadd 
19507453645fSAndriy Voskoboinyk 	rtwn_abort_xfers(sc);
19517453645fSAndriy Voskoboinyk 	rtwn_drain_mbufq(sc);
19527453645fSAndriy Voskoboinyk 	rtwn_power_off(sc);
19537453645fSAndriy Voskoboinyk 	rtwn_reset_lists(sc, NULL);
1954bcaed14bSAdrian Chadd 	RTWN_UNLOCK(sc);
1955bcaed14bSAdrian Chadd }
19567453645fSAndriy Voskoboinyk 
19577453645fSAndriy Voskoboinyk MODULE_VERSION(rtwn, 2);
19587453645fSAndriy Voskoboinyk MODULE_DEPEND(rtwn, wlan, 1, 1, 1);
19597453645fSAndriy Voskoboinyk #ifndef RTWN_WITHOUT_UCODE
19607453645fSAndriy Voskoboinyk MODULE_DEPEND(rtwn, firmware, 1, 1, 1);
19617453645fSAndriy Voskoboinyk #endif
1962