xref: /openbsd/sys/dev/ic/bwfm.c (revision 68edea43)
1*68edea43Sstsp /* $OpenBSD: bwfm.c,v 1.111 2024/02/19 21:23:02 stsp Exp $ */
232b2494eSpatrick /*
332b2494eSpatrick  * Copyright (c) 2010-2016 Broadcom Corporation
432b2494eSpatrick  * Copyright (c) 2016,2017 Patrick Wildt <patrick@blueri.se>
532b2494eSpatrick  *
632b2494eSpatrick  * Permission to use, copy, modify, and/or distribute this software for any
732b2494eSpatrick  * purpose with or without fee is hereby granted, provided that the above
832b2494eSpatrick  * copyright notice and this permission notice appear in all copies.
932b2494eSpatrick  *
1032b2494eSpatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1132b2494eSpatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1232b2494eSpatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1332b2494eSpatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1432b2494eSpatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1532b2494eSpatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1632b2494eSpatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1732b2494eSpatrick  */
1832b2494eSpatrick 
1932b2494eSpatrick #include "bpfilter.h"
2032b2494eSpatrick 
2132b2494eSpatrick #include <sys/param.h>
2232b2494eSpatrick #include <sys/systm.h>
2332b2494eSpatrick #include <sys/buf.h>
2432b2494eSpatrick #include <sys/kernel.h>
2532b2494eSpatrick #include <sys/malloc.h>
2632b2494eSpatrick #include <sys/device.h>
2732b2494eSpatrick #include <sys/queue.h>
2832b2494eSpatrick #include <sys/socket.h>
2932b2494eSpatrick #include <sys/sockio.h>
3032b2494eSpatrick 
3194e4747cSpatrick #if defined(__HAVE_FDT)
3294e4747cSpatrick #include <machine/fdt.h>
3394e4747cSpatrick #include <dev/ofw/openfirm.h>
3494e4747cSpatrick #endif
3594e4747cSpatrick 
3632b2494eSpatrick #if NBPFILTER > 0
3732b2494eSpatrick #include <net/bpf.h>
3832b2494eSpatrick #endif
3932b2494eSpatrick #include <net/if.h>
4032b2494eSpatrick #include <net/if_dl.h>
4132b2494eSpatrick #include <net/if_media.h>
4232b2494eSpatrick 
4332b2494eSpatrick #include <netinet/in.h>
4432b2494eSpatrick #include <netinet/if_ether.h>
4532b2494eSpatrick 
4632b2494eSpatrick #include <net80211/ieee80211_var.h>
4732b2494eSpatrick 
4832b2494eSpatrick #include <dev/ic/bwfmvar.h>
4932b2494eSpatrick #include <dev/ic/bwfmreg.h>
5032b2494eSpatrick 
5132b2494eSpatrick /* #define BWFM_DEBUG */
5232b2494eSpatrick #ifdef BWFM_DEBUG
5332b2494eSpatrick #define DPRINTF(x)	do { if (bwfm_debug > 0) printf x; } while (0)
5432b2494eSpatrick #define DPRINTFN(n, x)	do { if (bwfm_debug >= (n)) printf x; } while (0)
5532b2494eSpatrick static int bwfm_debug = 1;
5632b2494eSpatrick #else
5732b2494eSpatrick #define DPRINTF(x)	do { ; } while (0)
5832b2494eSpatrick #define DPRINTFN(n, x)	do { ; } while (0)
5932b2494eSpatrick #endif
6032b2494eSpatrick 
6132b2494eSpatrick #define DEVNAME(sc)	((sc)->sc_dev.dv_xname)
6232b2494eSpatrick 
6332b2494eSpatrick void	 bwfm_start(struct ifnet *);
6432b2494eSpatrick void	 bwfm_init(struct ifnet *);
6532b2494eSpatrick void	 bwfm_stop(struct ifnet *);
663c08fed4Spatrick void	 bwfm_iff(struct bwfm_softc *);
6732b2494eSpatrick void	 bwfm_watchdog(struct ifnet *);
68decdd79fSstsp void	 bwfm_update_node(void *, struct ieee80211_node *);
69decdd79fSstsp void	 bwfm_update_nodes(struct bwfm_softc *);
7032b2494eSpatrick int	 bwfm_ioctl(struct ifnet *, u_long, caddr_t);
7132b2494eSpatrick int	 bwfm_media_change(struct ifnet *);
7232b2494eSpatrick 
73c6a4ab0dSkettenis void	 bwfm_init_board_type(struct bwfm_softc *);
74da5a4af2Spatrick void	 bwfm_process_blob(struct bwfm_softc *, char *, u_char **, size_t *);
756c2f85e4Spatrick 
7632b2494eSpatrick int	 bwfm_chip_attach(struct bwfm_softc *);
77821b2e31Spatrick void	 bwfm_chip_detach(struct bwfm_softc *);
783c53ddefSpatrick struct bwfm_core *bwfm_chip_get_core_idx(struct bwfm_softc *, int, int);
7932b2494eSpatrick struct bwfm_core *bwfm_chip_get_core(struct bwfm_softc *, int);
8032b2494eSpatrick struct bwfm_core *bwfm_chip_get_pmu(struct bwfm_softc *);
8132b2494eSpatrick int	 bwfm_chip_ai_isup(struct bwfm_softc *, struct bwfm_core *);
8232b2494eSpatrick void	 bwfm_chip_ai_disable(struct bwfm_softc *, struct bwfm_core *,
8332b2494eSpatrick 	     uint32_t, uint32_t);
8432b2494eSpatrick void	 bwfm_chip_ai_reset(struct bwfm_softc *, struct bwfm_core *,
8532b2494eSpatrick 	     uint32_t, uint32_t, uint32_t);
8632b2494eSpatrick void	 bwfm_chip_dmp_erom_scan(struct bwfm_softc *);
8732b2494eSpatrick int	 bwfm_chip_dmp_get_regaddr(struct bwfm_softc *, uint32_t *,
8832b2494eSpatrick 	     uint32_t *, uint32_t *);
89e2612299Spatrick int	 bwfm_chip_cr4_set_active(struct bwfm_softc *, uint32_t);
9032b2494eSpatrick void	 bwfm_chip_cr4_set_passive(struct bwfm_softc *);
91e2612299Spatrick int	 bwfm_chip_ca7_set_active(struct bwfm_softc *, uint32_t);
9232b2494eSpatrick void	 bwfm_chip_ca7_set_passive(struct bwfm_softc *);
93e2612299Spatrick int	 bwfm_chip_cm3_set_active(struct bwfm_softc *);
9432b2494eSpatrick void	 bwfm_chip_cm3_set_passive(struct bwfm_softc *);
9585d32364Spatrick void	 bwfm_chip_socram_ramsize(struct bwfm_softc *, struct bwfm_core *);
9685d32364Spatrick void	 bwfm_chip_sysmem_ramsize(struct bwfm_softc *, struct bwfm_core *);
9785d32364Spatrick void	 bwfm_chip_tcm_ramsize(struct bwfm_softc *, struct bwfm_core *);
9885d32364Spatrick void	 bwfm_chip_tcm_rambase(struct bwfm_softc *);
9932b2494eSpatrick 
10032b2494eSpatrick int	 bwfm_proto_bcdc_query_dcmd(struct bwfm_softc *, int,
10132b2494eSpatrick 	     int, char *, size_t *);
10232b2494eSpatrick int	 bwfm_proto_bcdc_set_dcmd(struct bwfm_softc *, int,
10332b2494eSpatrick 	     int, char *, size_t);
1046f241297Spatrick void	 bwfm_proto_bcdc_rx(struct bwfm_softc *, struct mbuf *,
1056f241297Spatrick 	     struct mbuf_list *);
106029d6dd5Spatrick int	 bwfm_proto_bcdc_txctl(struct bwfm_softc *, int, char *, size_t *);
107029d6dd5Spatrick void	 bwfm_proto_bcdc_rxctl(struct bwfm_softc *, char *, size_t);
10832b2494eSpatrick 
10932b2494eSpatrick int	 bwfm_fwvar_cmd_get_data(struct bwfm_softc *, int, void *, size_t);
11032b2494eSpatrick int	 bwfm_fwvar_cmd_set_data(struct bwfm_softc *, int, void *, size_t);
11132b2494eSpatrick int	 bwfm_fwvar_cmd_get_int(struct bwfm_softc *, int, uint32_t *);
11232b2494eSpatrick int	 bwfm_fwvar_cmd_set_int(struct bwfm_softc *, int, uint32_t);
11332b2494eSpatrick int	 bwfm_fwvar_var_get_data(struct bwfm_softc *, char *, void *, size_t);
11432b2494eSpatrick int	 bwfm_fwvar_var_set_data(struct bwfm_softc *, char *, void *, size_t);
11532b2494eSpatrick int	 bwfm_fwvar_var_get_int(struct bwfm_softc *, char *, uint32_t *);
11632b2494eSpatrick int	 bwfm_fwvar_var_set_int(struct bwfm_softc *, char *, uint32_t);
11732b2494eSpatrick 
11873b11a26Spatrick uint32_t bwfm_chan2spec(struct bwfm_softc *, struct ieee80211_channel *);
11973b11a26Spatrick uint32_t bwfm_chan2spec_d11n(struct bwfm_softc *, struct ieee80211_channel *);
12073b11a26Spatrick uint32_t bwfm_chan2spec_d11ac(struct bwfm_softc *, struct ieee80211_channel *);
1218b2458cfSpatrick uint32_t bwfm_spec2chan(struct bwfm_softc *, uint32_t);
1228b2458cfSpatrick uint32_t bwfm_spec2chan_d11n(struct bwfm_softc *, uint32_t);
1238b2458cfSpatrick uint32_t bwfm_spec2chan_d11ac(struct bwfm_softc *, uint32_t);
1248b2458cfSpatrick 
12563498aa8Spatrick void	 bwfm_connect(struct bwfm_softc *);
126c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
127c11618f6Spatrick void	 bwfm_hostap(struct bwfm_softc *);
128c11618f6Spatrick #endif
12932b2494eSpatrick void	 bwfm_scan(struct bwfm_softc *);
13025440f89Spatrick void	 bwfm_scan_abort(struct bwfm_softc *);
13163498aa8Spatrick 
13263498aa8Spatrick void	 bwfm_task(void *);
13363498aa8Spatrick void	 bwfm_do_async(struct bwfm_softc *,
13463498aa8Spatrick 	     void (*)(struct bwfm_softc *, void *), void *, int);
13563498aa8Spatrick 
13663498aa8Spatrick int	 bwfm_set_key(struct ieee80211com *, struct ieee80211_node *,
13763498aa8Spatrick 	     struct ieee80211_key *);
13863498aa8Spatrick void	 bwfm_delete_key(struct ieee80211com *, struct ieee80211_node *,
13963498aa8Spatrick 	     struct ieee80211_key *);
14063498aa8Spatrick int	 bwfm_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
14163498aa8Spatrick 	     int, int, int);
14263498aa8Spatrick int	 bwfm_newstate(struct ieee80211com *, enum ieee80211_state, int);
14363498aa8Spatrick 
14463498aa8Spatrick void	 bwfm_set_key_cb(struct bwfm_softc *, void *);
14563498aa8Spatrick void	 bwfm_delete_key_cb(struct bwfm_softc *, void *);
146625a64d2Spatrick void	 bwfm_rx_event_cb(struct bwfm_softc *, struct mbuf *);
14732b2494eSpatrick 
148c11618f6Spatrick struct mbuf *bwfm_newbuf(void);
149c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
150c11618f6Spatrick void	 bwfm_rx_auth_ind(struct bwfm_softc *, struct bwfm_event *, size_t);
151c11618f6Spatrick void	 bwfm_rx_assoc_ind(struct bwfm_softc *, struct bwfm_event *, size_t, int);
152c11618f6Spatrick void	 bwfm_rx_deauth_ind(struct bwfm_softc *, struct bwfm_event *, size_t);
153c11618f6Spatrick void	 bwfm_rx_disassoc_ind(struct bwfm_softc *, struct bwfm_event *, size_t);
154c11618f6Spatrick void	 bwfm_rx_leave_ind(struct bwfm_softc *, struct bwfm_event *, size_t, int);
155c11618f6Spatrick #endif
156f4e8af02Spatrick void	 bwfm_rx_event(struct bwfm_softc *, struct mbuf *);
15732b2494eSpatrick void	 bwfm_scan_node(struct bwfm_softc *, struct bwfm_bss_info *, size_t);
15832b2494eSpatrick 
15932b2494eSpatrick extern void ieee80211_node2req(struct ieee80211com *,
16032b2494eSpatrick 	     const struct ieee80211_node *, struct ieee80211_nodereq *);
16132b2494eSpatrick extern void ieee80211_req2node(struct ieee80211com *,
16232b2494eSpatrick 	     const struct ieee80211_nodereq *, struct ieee80211_node *);
16332b2494eSpatrick 
16432b2494eSpatrick uint8_t bwfm_2ghz_channels[] = {
16532b2494eSpatrick 	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
16632b2494eSpatrick };
16732b2494eSpatrick uint8_t bwfm_5ghz_channels[] = {
16832b2494eSpatrick 	34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64, 100, 104, 108, 112,
16932b2494eSpatrick 	116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165,
17032b2494eSpatrick };
17132b2494eSpatrick 
17232b2494eSpatrick struct bwfm_proto_ops bwfm_proto_bcdc_ops = {
17332b2494eSpatrick 	.proto_query_dcmd = bwfm_proto_bcdc_query_dcmd,
17432b2494eSpatrick 	.proto_set_dcmd = bwfm_proto_bcdc_set_dcmd,
1757dfc0b73Spatrick 	.proto_rx = bwfm_proto_bcdc_rx,
176029d6dd5Spatrick 	.proto_rxctl = bwfm_proto_bcdc_rxctl,
17732b2494eSpatrick };
17832b2494eSpatrick 
17932b2494eSpatrick struct cfdriver bwfm_cd = {
18032b2494eSpatrick 	NULL, "bwfm", DV_IFNET
18132b2494eSpatrick };
18232b2494eSpatrick 
18332b2494eSpatrick void
bwfm_attach(struct bwfm_softc * sc)18432b2494eSpatrick bwfm_attach(struct bwfm_softc *sc)
18532b2494eSpatrick {
18632b2494eSpatrick 	struct ieee80211com *ic = &sc->sc_ic;
18732b2494eSpatrick 	struct ifnet *ifp = &ic->ic_if;
18832b2494eSpatrick 
189029d6dd5Spatrick 	TAILQ_INIT(&sc->sc_bcdc_rxctlq);
190029d6dd5Spatrick 
19163498aa8Spatrick 	/* Init host async commands ring. */
19263498aa8Spatrick 	sc->sc_cmdq.cur = sc->sc_cmdq.next = sc->sc_cmdq.queued = 0;
19363498aa8Spatrick 	sc->sc_taskq = taskq_create(DEVNAME(sc), 1, IPL_SOFTNET, 0);
19463498aa8Spatrick 	task_set(&sc->sc_task, bwfm_task, sc);
195625a64d2Spatrick 	ml_init(&sc->sc_evml);
19632b2494eSpatrick 
19763498aa8Spatrick 	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
19863498aa8Spatrick 	ic->ic_opmode = IEEE80211_M_STA;	/* default to BSS mode */
19963498aa8Spatrick 	ic->ic_state = IEEE80211_S_INIT;
20063498aa8Spatrick 
20163498aa8Spatrick 	ic->ic_caps =
202c8e9388cSstsp 	    IEEE80211_C_WEP |
203c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
204c11618f6Spatrick 	    IEEE80211_C_HOSTAP |	/* Access Point */
205c11618f6Spatrick #endif
20663498aa8Spatrick 	    IEEE80211_C_RSN | 		/* WPA/RSN */
20763498aa8Spatrick 	    IEEE80211_C_SCANALL |	/* device scans all channels at once */
20863498aa8Spatrick 	    IEEE80211_C_SCANALLBAND;	/* device scans all bands at once */
20927fa8129Spatrick 
210972218f3Spatrick 	/* IBSS channel undefined for now. */
211972218f3Spatrick 	ic->ic_ibss_chan = &ic->ic_channels[0];
212972218f3Spatrick 
213972218f3Spatrick 	ifp->if_softc = sc;
214972218f3Spatrick 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
215972218f3Spatrick 	ifp->if_ioctl = bwfm_ioctl;
216972218f3Spatrick 	ifp->if_start = bwfm_start;
217972218f3Spatrick 	ifp->if_watchdog = bwfm_watchdog;
218972218f3Spatrick 	memcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ);
219972218f3Spatrick 
220972218f3Spatrick 	if_attach(ifp);
221972218f3Spatrick 	ieee80211_ifattach(ifp);
222972218f3Spatrick 
223972218f3Spatrick 	sc->sc_newstate = ic->ic_newstate;
224972218f3Spatrick 	ic->ic_newstate = bwfm_newstate;
225972218f3Spatrick 	ic->ic_send_mgmt = bwfm_send_mgmt;
226972218f3Spatrick 	ic->ic_set_key = bwfm_set_key;
227972218f3Spatrick 	ic->ic_delete_key = bwfm_delete_key;
228972218f3Spatrick 
229972218f3Spatrick 	ieee80211_media_init(ifp, bwfm_media_change, ieee80211_media_status);
230972218f3Spatrick }
231972218f3Spatrick 
232972218f3Spatrick void
bwfm_attachhook(struct device * self)233972218f3Spatrick bwfm_attachhook(struct device *self)
234972218f3Spatrick {
235972218f3Spatrick 	struct bwfm_softc *sc = (struct bwfm_softc *)self;
236972218f3Spatrick 
237972218f3Spatrick 	if (sc->sc_bus_ops->bs_preinit != NULL &&
238972218f3Spatrick 	    sc->sc_bus_ops->bs_preinit(sc))
239972218f3Spatrick 		return;
240972218f3Spatrick 	if (bwfm_preinit(sc))
241972218f3Spatrick 		return;
242972218f3Spatrick 	sc->sc_initialized = 1;
243972218f3Spatrick }
244972218f3Spatrick 
245972218f3Spatrick int
bwfm_preinit(struct bwfm_softc * sc)246972218f3Spatrick bwfm_preinit(struct bwfm_softc *sc)
247972218f3Spatrick {
248972218f3Spatrick 	struct ieee80211com *ic = &sc->sc_ic;
249972218f3Spatrick 	struct ifnet *ifp = &ic->ic_if;
250972218f3Spatrick 	int i, j, nbands, nmode, vhtmode;
251972218f3Spatrick 	uint32_t bandlist[3], tmp;
252972218f3Spatrick 
253972218f3Spatrick 	if (sc->sc_initialized)
254972218f3Spatrick 		return 0;
255972218f3Spatrick 
256972218f3Spatrick 	if (bwfm_fwvar_cmd_get_int(sc, BWFM_C_GET_VERSION, &tmp)) {
257972218f3Spatrick 		printf("%s: could not read io type\n", DEVNAME(sc));
258972218f3Spatrick 		return 1;
259972218f3Spatrick 	} else
260972218f3Spatrick 		sc->sc_io_type = tmp;
261972218f3Spatrick 	if (bwfm_fwvar_var_get_data(sc, "cur_etheraddr", ic->ic_myaddr,
262972218f3Spatrick 	    sizeof(ic->ic_myaddr))) {
263972218f3Spatrick 		printf("%s: could not read mac address\n", DEVNAME(sc));
264972218f3Spatrick 		return 1;
265972218f3Spatrick 	}
266972218f3Spatrick 
2673e99d227Spatrick 	printf("%s: address %s\n", DEVNAME(sc), ether_sprintf(ic->ic_myaddr));
2683e99d227Spatrick 
269da5a4af2Spatrick 	bwfm_process_blob(sc, "clmload", &sc->sc_clm, &sc->sc_clmsize);
270da5a4af2Spatrick 	bwfm_process_blob(sc, "txcapload", &sc->sc_txcap, &sc->sc_txcapsize);
271da5a4af2Spatrick 	bwfm_process_blob(sc, "calload", &sc->sc_cal, &sc->sc_calsize);
2726c2f85e4Spatrick 
2739c7e455cSpatrick 	if (bwfm_fwvar_var_get_int(sc, "nmode", &nmode))
2749c7e455cSpatrick 		nmode = 0;
2759c7e455cSpatrick 	if (bwfm_fwvar_var_get_int(sc, "vhtmode", &vhtmode))
2769c7e455cSpatrick 		vhtmode = 0;
2779b183917Skettenis 	if (bwfm_fwvar_var_get_int(sc, "scan_ver", &sc->sc_scan_ver))
2789b183917Skettenis 		sc->sc_scan_ver = 0;
2799c7e455cSpatrick 	if (bwfm_fwvar_cmd_get_data(sc, BWFM_C_GET_BANDLIST, bandlist,
2809c7e455cSpatrick 	    sizeof(bandlist))) {
2819c7e455cSpatrick 		printf("%s: couldn't get supported band list\n", DEVNAME(sc));
282972218f3Spatrick 		return 1;
2839c7e455cSpatrick 	}
2849c7e455cSpatrick 	nbands = letoh32(bandlist[0]);
2859c7e455cSpatrick 	for (i = 1; i <= nbands && i < nitems(bandlist); i++) {
2869c7e455cSpatrick 		switch (letoh32(bandlist[i])) {
2879c7e455cSpatrick 		case BWFM_BAND_2G:
2889c7e455cSpatrick 			DPRINTF(("%s: 2G HT %d VHT %d\n",
2899c7e455cSpatrick 			    DEVNAME(sc), nmode, vhtmode));
2909c7e455cSpatrick 			ic->ic_sup_rates[IEEE80211_MODE_11B] =
2919c7e455cSpatrick 			    ieee80211_std_rateset_11b;
2929c7e455cSpatrick 			ic->ic_sup_rates[IEEE80211_MODE_11G] =
2939c7e455cSpatrick 			    ieee80211_std_rateset_11g;
29432b2494eSpatrick 
29533e6a401Spatrick 			for (j = 0; j < nitems(bwfm_2ghz_channels); j++) {
29633e6a401Spatrick 				uint8_t chan = bwfm_2ghz_channels[j];
29732b2494eSpatrick 				ic->ic_channels[chan].ic_freq =
29832b2494eSpatrick 				    ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ);
29932b2494eSpatrick 				ic->ic_channels[chan].ic_flags =
30032b2494eSpatrick 				    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
30132b2494eSpatrick 				    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
3029c7e455cSpatrick 				if (nmode)
3039c7e455cSpatrick 					ic->ic_channels[chan].ic_flags |=
3049c7e455cSpatrick 					    IEEE80211_CHAN_HT;
30550d1a4aeSstsp 				/* VHT is 5GHz only */
30632b2494eSpatrick 			}
3079c7e455cSpatrick 			break;
3089c7e455cSpatrick 		case BWFM_BAND_5G:
3099c7e455cSpatrick 			DPRINTF(("%s: 5G HT %d VHT %d\n",
3109c7e455cSpatrick 			    DEVNAME(sc), nmode, vhtmode));
3119c7e455cSpatrick 			ic->ic_sup_rates[IEEE80211_MODE_11A] =
3129c7e455cSpatrick 			    ieee80211_std_rateset_11a;
3139c7e455cSpatrick 
31433e6a401Spatrick 			for (j = 0; j < nitems(bwfm_5ghz_channels); j++) {
31533e6a401Spatrick 				uint8_t chan = bwfm_5ghz_channels[j];
31632b2494eSpatrick 				ic->ic_channels[chan].ic_freq =
31732b2494eSpatrick 				    ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ);
31832b2494eSpatrick 				ic->ic_channels[chan].ic_flags =
31932b2494eSpatrick 				    IEEE80211_CHAN_A;
3209c7e455cSpatrick 				if (nmode)
3219c7e455cSpatrick 					ic->ic_channels[chan].ic_flags |=
3229c7e455cSpatrick 					    IEEE80211_CHAN_HT;
32350d1a4aeSstsp 				if (vhtmode)
32450d1a4aeSstsp 					ic->ic_channels[chan].ic_flags |=
32550d1a4aeSstsp 					    IEEE80211_CHAN_VHT;
32632b2494eSpatrick 			}
3279c7e455cSpatrick 			break;
3289c7e455cSpatrick 		default:
3299c7e455cSpatrick 			printf("%s: unsupported band 0x%x\n", DEVNAME(sc),
3309c7e455cSpatrick 			    letoh32(bandlist[i]));
3319c7e455cSpatrick 			break;
3329c7e455cSpatrick 		}
3339c7e455cSpatrick 	}
3349c7e455cSpatrick 
335972218f3Spatrick 	/* Configure channel information obtained from firmware. */
336972218f3Spatrick 	ieee80211_channel_init(ifp);
33732b2494eSpatrick 
338972218f3Spatrick 	/* Configure MAC address. */
339972218f3Spatrick 	if (if_setlladdr(ifp, ic->ic_myaddr))
340972218f3Spatrick 		printf("%s: could not set MAC address\n", DEVNAME(sc));
34163498aa8Spatrick 
34263498aa8Spatrick 	ieee80211_media_init(ifp, bwfm_media_change, ieee80211_media_status);
343972218f3Spatrick 	return 0;
34432b2494eSpatrick }
34532b2494eSpatrick 
3461a93a9bdSpatrick void
bwfm_cleanup(struct bwfm_softc * sc)3471a93a9bdSpatrick bwfm_cleanup(struct bwfm_softc *sc)
3481a93a9bdSpatrick {
3491a93a9bdSpatrick 	bwfm_chip_detach(sc);
3501a93a9bdSpatrick 	sc->sc_initialized = 0;
3511a93a9bdSpatrick }
3521a93a9bdSpatrick 
35332b2494eSpatrick int
bwfm_detach(struct bwfm_softc * sc,int flags)35432b2494eSpatrick bwfm_detach(struct bwfm_softc *sc, int flags)
35532b2494eSpatrick {
35632b2494eSpatrick 	struct ieee80211com *ic = &sc->sc_ic;
35732b2494eSpatrick 	struct ifnet *ifp = &ic->ic_if;
358821b2e31Spatrick 
35963498aa8Spatrick 	task_del(sc->sc_taskq, &sc->sc_task);
36063498aa8Spatrick 	ieee80211_ifdetach(ifp);
36116ddeba7Sstsp 	taskq_barrier(sc->sc_taskq);
36232b2494eSpatrick 	if_detach(ifp);
36316ddeba7Sstsp 	taskq_destroy(sc->sc_taskq);
364821b2e31Spatrick 
3651a93a9bdSpatrick 	bwfm_cleanup(sc);
3661a93a9bdSpatrick 	return 0;
3671a93a9bdSpatrick }
3681a93a9bdSpatrick 
3691a93a9bdSpatrick int
bwfm_activate(struct bwfm_softc * sc,int act)3701a93a9bdSpatrick bwfm_activate(struct bwfm_softc *sc, int act)
3711a93a9bdSpatrick {
3721a93a9bdSpatrick 	struct ieee80211com *ic = &sc->sc_ic;
3731a93a9bdSpatrick 	struct ifnet *ifp = &ic->ic_if;
3741a93a9bdSpatrick 
3751a93a9bdSpatrick 	switch (act) {
3761a93a9bdSpatrick 	case DVACT_QUIESCE:
3771a93a9bdSpatrick 		if (ifp->if_flags & IFF_UP)
3781a93a9bdSpatrick 			bwfm_stop(ifp);
3791a93a9bdSpatrick 		break;
3801a93a9bdSpatrick 	case DVACT_WAKEUP:
3811a93a9bdSpatrick 		if (ifp->if_flags & IFF_UP)
3821a93a9bdSpatrick 			bwfm_init(ifp);
3831a93a9bdSpatrick 		break;
3841a93a9bdSpatrick 	default:
3851a93a9bdSpatrick 		break;
3861a93a9bdSpatrick 	}
3871a93a9bdSpatrick 
38832b2494eSpatrick 	return 0;
38932b2494eSpatrick }
39032b2494eSpatrick 
39132b2494eSpatrick void
bwfm_start(struct ifnet * ifp)39232b2494eSpatrick bwfm_start(struct ifnet *ifp)
39332b2494eSpatrick {
39432b2494eSpatrick 	struct bwfm_softc *sc = ifp->if_softc;
395f549aee3Skettenis 	struct ieee80211com *ic = &sc->sc_ic;
39632b2494eSpatrick 	struct mbuf *m;
39732b2494eSpatrick 
39832b2494eSpatrick 	if (!(ifp->if_flags & IFF_RUNNING))
39932b2494eSpatrick 		return;
40032b2494eSpatrick 	if (ifq_is_oactive(&ifp->if_snd))
40132b2494eSpatrick 		return;
4020cae21bdSpatrick 	if (ifq_empty(&ifp->if_snd))
40332b2494eSpatrick 		return;
40432b2494eSpatrick 
40532b2494eSpatrick 	/* TODO: return if no link? */
40632b2494eSpatrick 
40702ee7d07Spatrick 	for (;;) {
40802ee7d07Spatrick 		if (sc->sc_bus_ops->bs_txcheck(sc)) {
40932b2494eSpatrick 			ifq_set_oactive(&ifp->if_snd);
41032b2494eSpatrick 			break;
41132b2494eSpatrick 		}
41202ee7d07Spatrick 
413f549aee3Skettenis 		if (ic->ic_state != IEEE80211_S_RUN ||
414f549aee3Skettenis 		    (ic->ic_xflags & IEEE80211_F_TX_MGMT_ONLY))
415f549aee3Skettenis 			break;
416f549aee3Skettenis 
41702ee7d07Spatrick 		m = ifq_dequeue(&ifp->if_snd);
41802ee7d07Spatrick 		if (m == NULL)
41902ee7d07Spatrick 			break;
42002ee7d07Spatrick 
42102ee7d07Spatrick 		if (sc->sc_bus_ops->bs_txdata(sc, m) != 0) {
42232b2494eSpatrick 			ifp->if_oerrors++;
42302ee7d07Spatrick 			m_freem(m);
42432b2494eSpatrick 			continue;
42532b2494eSpatrick 		}
42632b2494eSpatrick 
42732b2494eSpatrick #if NBPFILTER > 0
42832b2494eSpatrick 		if (ifp->if_bpf)
42932b2494eSpatrick 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
43032b2494eSpatrick #endif
43132b2494eSpatrick 	}
43232b2494eSpatrick }
43332b2494eSpatrick 
43432b2494eSpatrick void
bwfm_init(struct ifnet * ifp)43532b2494eSpatrick bwfm_init(struct ifnet *ifp)
43632b2494eSpatrick {
43732b2494eSpatrick 	struct bwfm_softc *sc = ifp->if_softc;
438c11618f6Spatrick 	struct ieee80211com *ic = &sc->sc_ic;
43932b2494eSpatrick 	uint8_t evmask[BWFM_EVENT_MASK_LEN];
4401179aa9cSpatrick 	struct bwfm_join_pref_params join_pref[2];
441c11618f6Spatrick 	int pm;
44232b2494eSpatrick 
443972218f3Spatrick 	if (!sc->sc_initialized) {
444972218f3Spatrick 		if (sc->sc_bus_ops->bs_preinit != NULL &&
445972218f3Spatrick 		    sc->sc_bus_ops->bs_preinit(sc)) {
446972218f3Spatrick 			printf("%s: could not init bus\n", DEVNAME(sc));
447972218f3Spatrick 			return;
448972218f3Spatrick 		}
449972218f3Spatrick 		if (bwfm_preinit(sc)) {
450972218f3Spatrick 			printf("%s: could not init\n", DEVNAME(sc));
451972218f3Spatrick 			return;
452972218f3Spatrick 		}
453972218f3Spatrick 		sc->sc_initialized = 1;
454*68edea43Sstsp 	} else {
455*68edea43Sstsp 		/* Update MAC in case the upper layers changed it. */
456*68edea43Sstsp 		IEEE80211_ADDR_COPY(ic->ic_myaddr,
457*68edea43Sstsp 		    ((struct arpcom *)ifp)->ac_enaddr);
458*68edea43Sstsp 		if (bwfm_fwvar_var_set_data(sc, "cur_etheraddr",
459*68edea43Sstsp 		    ic->ic_myaddr, sizeof(ic->ic_myaddr))) {
460*68edea43Sstsp 			printf("%s: could not write MAC address\n",
461*68edea43Sstsp 			    DEVNAME(sc));
462*68edea43Sstsp 			return;
463*68edea43Sstsp 		}
464972218f3Spatrick 	}
465a2c6ff8bSpatrick 
46673b11a26Spatrick 	/* Select default channel */
46773b11a26Spatrick 	ic->ic_bss->ni_chan = ic->ic_ibss_chan;
46873b11a26Spatrick 
46932b2494eSpatrick 	if (bwfm_fwvar_var_set_int(sc, "mpc", 1)) {
47032b2494eSpatrick 		printf("%s: could not set mpc\n", DEVNAME(sc));
47132b2494eSpatrick 		return;
47232b2494eSpatrick 	}
47332b2494eSpatrick 
4741179aa9cSpatrick 	/* Select target by RSSI (boost on 5GHz) */
4751179aa9cSpatrick 	join_pref[0].type = BWFM_JOIN_PREF_RSSI_DELTA;
4761179aa9cSpatrick 	join_pref[0].len = 2;
4771179aa9cSpatrick 	join_pref[0].rssi_gain = BWFM_JOIN_PREF_RSSI_BOOST;
4781179aa9cSpatrick 	join_pref[0].band = BWFM_JOIN_PREF_BAND_5G;
4791179aa9cSpatrick 	join_pref[1].type = BWFM_JOIN_PREF_RSSI;
4801179aa9cSpatrick 	join_pref[1].len = 2;
4811179aa9cSpatrick 	join_pref[1].rssi_gain = 0;
4821179aa9cSpatrick 	join_pref[1].band = 0;
4831179aa9cSpatrick 	if (bwfm_fwvar_var_set_data(sc, "join_pref", join_pref,
4841179aa9cSpatrick 	    sizeof(join_pref))) {
4851179aa9cSpatrick 		printf("%s: could not set join pref\n", DEVNAME(sc));
4861179aa9cSpatrick 		return;
4871179aa9cSpatrick 	}
48832b2494eSpatrick 
489c11618f6Spatrick #define BWFM_EVENT(event) evmask[(event) / 8] |= 1 << ((event) % 8)
490c11618f6Spatrick 	memset(evmask, 0, sizeof(evmask));
491c11618f6Spatrick 	switch (ic->ic_opmode) {
492c11618f6Spatrick 	case IEEE80211_M_STA:
493c11618f6Spatrick 		BWFM_EVENT(BWFM_E_IF);
494c11618f6Spatrick 		BWFM_EVENT(BWFM_E_LINK);
495c11618f6Spatrick 		BWFM_EVENT(BWFM_E_AUTH);
496c11618f6Spatrick 		BWFM_EVENT(BWFM_E_ASSOC);
497c11618f6Spatrick 		BWFM_EVENT(BWFM_E_DEAUTH);
498c11618f6Spatrick 		BWFM_EVENT(BWFM_E_DISASSOC);
499c11618f6Spatrick 		BWFM_EVENT(BWFM_E_ESCAN_RESULT);
500c11618f6Spatrick 		break;
501c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
502c11618f6Spatrick 	case IEEE80211_M_HOSTAP:
503c11618f6Spatrick 		BWFM_EVENT(BWFM_E_AUTH_IND);
504c11618f6Spatrick 		BWFM_EVENT(BWFM_E_ASSOC_IND);
505c11618f6Spatrick 		BWFM_EVENT(BWFM_E_REASSOC_IND);
506c11618f6Spatrick 		BWFM_EVENT(BWFM_E_DEAUTH_IND);
507c11618f6Spatrick 		BWFM_EVENT(BWFM_E_DISASSOC_IND);
508c11618f6Spatrick 		BWFM_EVENT(BWFM_E_ESCAN_RESULT);
509c11618f6Spatrick 		break;
510c11618f6Spatrick #endif
511c11618f6Spatrick 	default:
512c11618f6Spatrick 		break;
51332b2494eSpatrick 	}
514c11618f6Spatrick #undef BWFM_EVENT
515c11618f6Spatrick 
51632b2494eSpatrick 	if (bwfm_fwvar_var_set_data(sc, "event_msgs", evmask, sizeof(evmask))) {
51732b2494eSpatrick 		printf("%s: could not set event mask\n", DEVNAME(sc));
51832b2494eSpatrick 		return;
51932b2494eSpatrick 	}
52032b2494eSpatrick 
52132b2494eSpatrick 	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_CHANNEL_TIME,
52232b2494eSpatrick 	    BWFM_DEFAULT_SCAN_CHANNEL_TIME)) {
52332b2494eSpatrick 		printf("%s: could not set scan channel time\n", DEVNAME(sc));
52432b2494eSpatrick 		return;
52532b2494eSpatrick 	}
52632b2494eSpatrick 	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_UNASSOC_TIME,
52732b2494eSpatrick 	    BWFM_DEFAULT_SCAN_UNASSOC_TIME)) {
52832b2494eSpatrick 		printf("%s: could not set scan unassoc time\n", DEVNAME(sc));
52932b2494eSpatrick 		return;
53032b2494eSpatrick 	}
53132b2494eSpatrick 	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_PASSIVE_TIME,
53232b2494eSpatrick 	    BWFM_DEFAULT_SCAN_PASSIVE_TIME)) {
53332b2494eSpatrick 		printf("%s: could not set scan passive time\n", DEVNAME(sc));
53432b2494eSpatrick 		return;
53532b2494eSpatrick 	}
53632b2494eSpatrick 
537c11618f6Spatrick 	/*
538c11618f6Spatrick 	 * Use CAM (constantly awake) when we are running as AP,
539c11618f6Spatrick 	 * otherwise use fast power saving.
540c11618f6Spatrick 	 */
541c11618f6Spatrick 	pm = BWFM_PM_FAST_PS;
542c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
543c11618f6Spatrick 	if (ic->ic_opmode == IEEE80211_M_HOSTAP)
544c11618f6Spatrick 		pm = BWFM_PM_CAM;
545c11618f6Spatrick #endif
546c11618f6Spatrick 	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, pm)) {
54732b2494eSpatrick 		printf("%s: could not set power\n", DEVNAME(sc));
54832b2494eSpatrick 		return;
54932b2494eSpatrick 	}
55032b2494eSpatrick 
55132b2494eSpatrick 	bwfm_fwvar_var_set_int(sc, "txbf", 1);
55232b2494eSpatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_UP, 0);
55332b2494eSpatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_INFRA, 1);
55432b2494eSpatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_AP, 0);
55532b2494eSpatrick 
55632b2494eSpatrick 	/* Disable all offloading (ARP, NDP, TCP/UDP cksum). */
55732b2494eSpatrick 	bwfm_fwvar_var_set_int(sc, "arp_ol", 0);
55832b2494eSpatrick 	bwfm_fwvar_var_set_int(sc, "arpoe", 0);
55932b2494eSpatrick 	bwfm_fwvar_var_set_int(sc, "ndoe", 0);
56032b2494eSpatrick 	bwfm_fwvar_var_set_int(sc, "toe", 0);
56132b2494eSpatrick 
562d346a16fSpatrick 	/*
56363498aa8Spatrick 	 * The firmware supplicant can handle the WPA handshake for
56463498aa8Spatrick 	 * us, but we honestly want to do this ourselves, so disable
56563498aa8Spatrick 	 * the firmware supplicant and let our stack handle it.
566d346a16fSpatrick 	 */
56763498aa8Spatrick 	bwfm_fwvar_var_set_int(sc, "sup_wpa", 0);
56832b2494eSpatrick 
5693c08fed4Spatrick 	bwfm_iff(sc);
57032b2494eSpatrick 
57132b2494eSpatrick 	ifp->if_flags |= IFF_RUNNING;
57232b2494eSpatrick 	ifq_clr_oactive(&ifp->if_snd);
57363498aa8Spatrick 
57463498aa8Spatrick 	ieee80211_begin_scan(ifp);
57532b2494eSpatrick }
57632b2494eSpatrick 
57732b2494eSpatrick void
bwfm_stop(struct ifnet * ifp)57832b2494eSpatrick bwfm_stop(struct ifnet *ifp)
57932b2494eSpatrick {
58032b2494eSpatrick 	struct bwfm_softc *sc = ifp->if_softc;
58132b2494eSpatrick 	struct ieee80211com *ic = &sc->sc_ic;
58273b11a26Spatrick 	struct bwfm_join_params join;
58332b2494eSpatrick 
58432b2494eSpatrick 	sc->sc_tx_timer = 0;
58532b2494eSpatrick 	ifp->if_timer = 0;
58632b2494eSpatrick 	ifp->if_flags &= ~IFF_RUNNING;
58732b2494eSpatrick 	ifq_clr_oactive(&ifp->if_snd);
58832b2494eSpatrick 
58963498aa8Spatrick 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
59063498aa8Spatrick 
59173b11a26Spatrick 	memset(&join, 0, sizeof(join));
59273b11a26Spatrick 	bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_SSID, &join, sizeof(join));
59332b2494eSpatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_DOWN, 1);
59473b11a26Spatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_AP, 0);
59573b11a26Spatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_INFRA, 0);
59673b11a26Spatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_UP, 1);
59773b11a26Spatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, BWFM_PM_FAST_PS);
5981b8f8c22Stobhe 	bwfm_fwvar_var_set_int(sc, "mpc", 1);
599a2c6ff8bSpatrick 
600a2c6ff8bSpatrick 	if (sc->sc_bus_ops->bs_stop)
601a2c6ff8bSpatrick 		sc->sc_bus_ops->bs_stop(sc);
60232b2494eSpatrick }
60332b2494eSpatrick 
60432b2494eSpatrick void
bwfm_iff(struct bwfm_softc * sc)6053c08fed4Spatrick bwfm_iff(struct bwfm_softc *sc)
6063c08fed4Spatrick {
6073c08fed4Spatrick 	struct arpcom *ac = &sc->sc_ic.ic_ac;
6083c08fed4Spatrick 	struct ifnet *ifp = &ac->ac_if;
6093c08fed4Spatrick 	struct ether_multi *enm;
6103c08fed4Spatrick 	struct ether_multistep step;
6113c08fed4Spatrick 	size_t mcastlen;
6123c08fed4Spatrick 	char *mcast;
6133c08fed4Spatrick 	int i = 0;
6143c08fed4Spatrick 
6153c08fed4Spatrick 	mcastlen = sizeof(uint32_t) + ac->ac_multicnt * ETHER_ADDR_LEN;
6163c08fed4Spatrick 	mcast = malloc(mcastlen, M_TEMP, M_WAITOK);
6171d328f71Spatrick 	htolem32((uint32_t *)mcast, ac->ac_multicnt);
6183c08fed4Spatrick 
6193c08fed4Spatrick 	ifp->if_flags &= ~IFF_ALLMULTI;
6203c08fed4Spatrick 	if (ifp->if_flags & IFF_PROMISC || ac->ac_multirangecnt > 0) {
6213c08fed4Spatrick 		ifp->if_flags |= IFF_ALLMULTI;
6223c08fed4Spatrick 	} else {
6233c08fed4Spatrick 		ETHER_FIRST_MULTI(step, ac, enm);
6243c08fed4Spatrick 		while (enm != NULL) {
6253c08fed4Spatrick 			memcpy(mcast + sizeof(uint32_t) + i * ETHER_ADDR_LEN,
6263c08fed4Spatrick 			    enm->enm_addrlo, ETHER_ADDR_LEN);
6273c08fed4Spatrick 			ETHER_NEXT_MULTI(step, enm);
6283c08fed4Spatrick 			i++;
6293c08fed4Spatrick 		}
6303c08fed4Spatrick 	}
6313c08fed4Spatrick 
6323c08fed4Spatrick 	bwfm_fwvar_var_set_data(sc, "mcast_list", mcast, mcastlen);
6333c08fed4Spatrick 	bwfm_fwvar_var_set_int(sc, "allmulti",
6343c08fed4Spatrick 	    !!(ifp->if_flags & IFF_ALLMULTI));
6353c08fed4Spatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PROMISC,
6363c08fed4Spatrick 	    !!(ifp->if_flags & IFF_PROMISC));
6373c08fed4Spatrick 
6383c08fed4Spatrick 	free(mcast, M_TEMP, mcastlen);
6393c08fed4Spatrick }
6403c08fed4Spatrick 
6413c08fed4Spatrick void
bwfm_watchdog(struct ifnet * ifp)64232b2494eSpatrick bwfm_watchdog(struct ifnet *ifp)
64332b2494eSpatrick {
64432b2494eSpatrick 	struct bwfm_softc *sc = ifp->if_softc;
64532b2494eSpatrick 
64632b2494eSpatrick 	ifp->if_timer = 0;
64732b2494eSpatrick 
64832b2494eSpatrick 	if (sc->sc_tx_timer > 0) {
64932b2494eSpatrick 		if (--sc->sc_tx_timer == 0) {
65032b2494eSpatrick 			printf("%s: device timeout\n", DEVNAME(sc));
65132b2494eSpatrick 			ifp->if_oerrors++;
65232b2494eSpatrick 			return;
65332b2494eSpatrick 		}
65432b2494eSpatrick 		ifp->if_timer = 1;
65532b2494eSpatrick 	}
65632b2494eSpatrick 	ieee80211_watchdog(ifp);
65732b2494eSpatrick }
65832b2494eSpatrick 
659decdd79fSstsp /*
660decdd79fSstsp  * Tx-rate to MCS conversion might lie since some rates map to multiple MCS.
661decdd79fSstsp  * But this is the best we can do given that firmware only reports kbit/s.
662decdd79fSstsp  */
663decdd79fSstsp 
66450d1a4aeSstsp void
bwfm_rate2vhtmcs(int * mcs,int * ss,uint32_t txrate)66550d1a4aeSstsp bwfm_rate2vhtmcs(int *mcs, int *ss, uint32_t txrate)
666decdd79fSstsp {
66750d1a4aeSstsp 	const struct ieee80211_vht_rateset *rs;
66850d1a4aeSstsp 	int i, j;
66950d1a4aeSstsp 
67050d1a4aeSstsp 	*mcs = -1;
67150d1a4aeSstsp 	*ss = -1;
67250d1a4aeSstsp 	/* TODO: Select specific ratesets based on BSS channel width. */
67350d1a4aeSstsp 	for (i = 0; i < IEEE80211_VHT_NUM_RATESETS; i++) {
67450d1a4aeSstsp 		rs = &ieee80211_std_ratesets_11ac[i];
67550d1a4aeSstsp 		for (j = 0; j < rs->nrates; j++) {
67650d1a4aeSstsp 			if (rs->rates[j] == txrate / 500) {
67750d1a4aeSstsp 				*mcs = j;
67850d1a4aeSstsp 				*ss = rs->num_ss;
67950d1a4aeSstsp 				return;
68050d1a4aeSstsp 			}
68150d1a4aeSstsp 		}
68250d1a4aeSstsp 	}
683decdd79fSstsp }
684decdd79fSstsp 
685decdd79fSstsp int
bwfm_rate2htmcs(uint32_t txrate)686decdd79fSstsp bwfm_rate2htmcs(uint32_t txrate)
687decdd79fSstsp {
688decdd79fSstsp 	const struct ieee80211_ht_rateset *rs;
689decdd79fSstsp 	int i, j;
690decdd79fSstsp 
69150d1a4aeSstsp 	/* TODO: Select specific ratesets based on BSS channel width. */
692decdd79fSstsp 	for (i = 0; i < IEEE80211_HT_NUM_RATESETS; i++) {
693decdd79fSstsp 		rs = &ieee80211_std_ratesets_11n[i];
694decdd79fSstsp 		for (j = 0; j < rs->nrates; j++) {
695decdd79fSstsp 			if (rs->rates[j] == txrate / 500)
696decdd79fSstsp 				return rs->min_mcs + j;
697decdd79fSstsp 		}
698decdd79fSstsp 	}
699decdd79fSstsp 
700decdd79fSstsp 	return -1;
701decdd79fSstsp }
702decdd79fSstsp 
703decdd79fSstsp void
bwfm_update_node(void * arg,struct ieee80211_node * ni)704decdd79fSstsp bwfm_update_node(void *arg, struct ieee80211_node *ni)
705decdd79fSstsp {
706decdd79fSstsp 	struct bwfm_softc *sc = arg;
707decdd79fSstsp 	struct ieee80211com *ic = &sc->sc_ic;
708decdd79fSstsp 	struct bwfm_sta_info sta;
709decdd79fSstsp 	uint32_t flags;
710decdd79fSstsp 	int8_t rssi;
711decdd79fSstsp 	uint32_t txrate;
71250d1a4aeSstsp 	int i;
713decdd79fSstsp 
714decdd79fSstsp 	memset(&sta, 0, sizeof(sta));
715decdd79fSstsp 	memcpy((uint8_t *)&sta, ni->ni_macaddr, sizeof(ni->ni_macaddr));
716decdd79fSstsp 
717decdd79fSstsp 	if (bwfm_fwvar_var_get_data(sc, "sta_info", &sta, sizeof(sta)))
718decdd79fSstsp 		return;
719decdd79fSstsp 
720decdd79fSstsp 	if (!IEEE80211_ADDR_EQ(ni->ni_macaddr, sta.ea))
721decdd79fSstsp 		return;
722decdd79fSstsp 
72304982c3bSstsp 	if (le16toh(sta.ver) < 3)
724decdd79fSstsp 		return;
725decdd79fSstsp 
726decdd79fSstsp 	flags = le32toh(sta.flags);
727decdd79fSstsp 	if ((flags & BWFM_STA_SCBSTATS) == 0)
728decdd79fSstsp 		return;
729decdd79fSstsp 
73004982c3bSstsp 	if (le16toh(sta.ver) >= 4) {
731decdd79fSstsp 		rssi = 0;
732decdd79fSstsp 		for (i = 0; i < BWFM_ANT_MAX; i++) {
733decdd79fSstsp 			if (sta.rssi[i] >= 0)
734decdd79fSstsp 				continue;
735decdd79fSstsp 			if (rssi == 0 || sta.rssi[i] > rssi)
736decdd79fSstsp 				rssi = sta.rssi[i];
737decdd79fSstsp 		}
738decdd79fSstsp 		if (rssi)
739decdd79fSstsp 			ni->ni_rssi = rssi;
74004982c3bSstsp 	}
741decdd79fSstsp 
742decdd79fSstsp 	txrate = le32toh(sta.tx_rate); /* in kbit/s */
743decdd79fSstsp 	if (txrate == 0xffffffff) /* Seen this happening during association. */
744decdd79fSstsp 		return;
745decdd79fSstsp 
74650d1a4aeSstsp 	if ((le32toh(sta.flags) & BWFM_STA_VHT_CAP)) {
74750d1a4aeSstsp 		int mcs, ss;
74850d1a4aeSstsp 		/* Tell net80211 that firmware has negotiated 11ac. */
74950d1a4aeSstsp 		ni->ni_flags |= IEEE80211_NODE_VHT;
75050d1a4aeSstsp 		ni->ni_flags |= IEEE80211_NODE_HT; /* VHT implies HT support */
75150d1a4aeSstsp 		if (ic->ic_curmode < IEEE80211_MODE_11AC)
75250d1a4aeSstsp 			ieee80211_setmode(ic, IEEE80211_MODE_11AC);
75350d1a4aeSstsp 	    	bwfm_rate2vhtmcs(&mcs, &ss, txrate);
75450d1a4aeSstsp 		if (mcs >= 0) {
75550d1a4aeSstsp 			ni->ni_txmcs = mcs;
75650d1a4aeSstsp 			ni->ni_vht_ss = ss;
75750d1a4aeSstsp 		} else {
75850d1a4aeSstsp 			ni->ni_txmcs = 0;
75950d1a4aeSstsp 			ni->ni_vht_ss = 1;
76050d1a4aeSstsp 		}
76150d1a4aeSstsp 	} else if ((le32toh(sta.flags) & BWFM_STA_N_CAP)) {
76250d1a4aeSstsp 		int mcs;
763decdd79fSstsp 		/* Tell net80211 that firmware has negotiated 11n. */
764decdd79fSstsp 		ni->ni_flags |= IEEE80211_NODE_HT;
765decdd79fSstsp 		if (ic->ic_curmode < IEEE80211_MODE_11N)
766decdd79fSstsp 			ieee80211_setmode(ic, IEEE80211_MODE_11N);
76750d1a4aeSstsp 	    	mcs = bwfm_rate2htmcs(txrate);
76850d1a4aeSstsp 		ni->ni_txmcs = (mcs >= 0 ? mcs : 0);
769decdd79fSstsp 	} else {
770decdd79fSstsp 		/* We're in 11a/g mode. Map to a legacy rate. */
771decdd79fSstsp 		for (i = 0; i < ni->ni_rates.rs_nrates; i++) {
772decdd79fSstsp 			uint8_t rate = ni->ni_rates.rs_rates[i];
773decdd79fSstsp 			rate &= IEEE80211_RATE_VAL;
774decdd79fSstsp 			if (rate == txrate / 500) {
775decdd79fSstsp 				ni->ni_txrate = i;
776decdd79fSstsp 				break;
777decdd79fSstsp 			}
778decdd79fSstsp 		}
779decdd79fSstsp 	}
780decdd79fSstsp }
781decdd79fSstsp 
782decdd79fSstsp void
bwfm_update_nodes(struct bwfm_softc * sc)783decdd79fSstsp bwfm_update_nodes(struct bwfm_softc *sc)
784decdd79fSstsp {
785decdd79fSstsp 	struct ieee80211com *ic = &sc->sc_ic;
786decdd79fSstsp 	struct ieee80211_node *ni;
787decdd79fSstsp 
788decdd79fSstsp 	switch (ic->ic_opmode) {
789decdd79fSstsp 	case IEEE80211_M_STA:
790decdd79fSstsp 		bwfm_update_node(sc, ic->ic_bss);
791decdd79fSstsp 		/* Update cached copy in the nodes tree as well. */
792decdd79fSstsp 		ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr);
793decdd79fSstsp 		if (ni) {
794decdd79fSstsp 			ni->ni_rssi = ic->ic_bss->ni_rssi;
795decdd79fSstsp 		}
796decdd79fSstsp 		break;
797decdd79fSstsp #ifndef IEEE80211_STA_ONLY
798decdd79fSstsp 	case IEEE80211_M_HOSTAP:
799decdd79fSstsp 		ieee80211_iterate_nodes(ic, bwfm_update_node, sc);
800decdd79fSstsp 		break;
801decdd79fSstsp #endif
802decdd79fSstsp 	default:
803decdd79fSstsp 		break;
804decdd79fSstsp 	}
805decdd79fSstsp }
806decdd79fSstsp 
80732b2494eSpatrick int
bwfm_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)80832b2494eSpatrick bwfm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
80932b2494eSpatrick {
810decdd79fSstsp 	struct bwfm_softc *sc = ifp->if_softc;
811decdd79fSstsp 	struct ieee80211com *ic = &sc->sc_ic;
8123c08fed4Spatrick 	struct ifreq *ifr;
81363498aa8Spatrick 	int s, error = 0;
81432b2494eSpatrick 
81532b2494eSpatrick 	s = splnet();
81632b2494eSpatrick 	switch (cmd) {
81732b2494eSpatrick 	case SIOCSIFADDR:
81832b2494eSpatrick 		ifp->if_flags |= IFF_UP;
81963498aa8Spatrick 		/* FALLTHROUGH */
82032b2494eSpatrick 	case SIOCSIFFLAGS:
82132b2494eSpatrick 		if (ifp->if_flags & IFF_UP) {
82232b2494eSpatrick 			if (!(ifp->if_flags & IFF_RUNNING))
82332b2494eSpatrick 				bwfm_init(ifp);
82432b2494eSpatrick 		} else {
82532b2494eSpatrick 			if (ifp->if_flags & IFF_RUNNING)
82632b2494eSpatrick 				bwfm_stop(ifp);
82732b2494eSpatrick 		}
82832b2494eSpatrick 		break;
8293c08fed4Spatrick 	case SIOCADDMULTI:
8303c08fed4Spatrick 	case SIOCDELMULTI:
8313c08fed4Spatrick 		ifr = (struct ifreq *)data;
8323c08fed4Spatrick 		error = (cmd == SIOCADDMULTI) ?
8333c08fed4Spatrick 		    ether_addmulti(ifr, &ic->ic_ac) :
8343c08fed4Spatrick 		    ether_delmulti(ifr, &ic->ic_ac);
8353c08fed4Spatrick 		if (error == ENETRESET) {
8363c08fed4Spatrick 			bwfm_iff(sc);
8373c08fed4Spatrick 			error = 0;
8383c08fed4Spatrick 		}
8393c08fed4Spatrick 		break;
840decdd79fSstsp 	case SIOCGIFMEDIA:
841decdd79fSstsp 	case SIOCG80211NODE:
842decdd79fSstsp 	case SIOCG80211ALLNODES:
843decdd79fSstsp 		if (ic->ic_state == IEEE80211_S_RUN)
844decdd79fSstsp 			bwfm_update_nodes(sc);
845decdd79fSstsp 		/* fall through */
84632b2494eSpatrick 	default:
84763498aa8Spatrick 		error = ieee80211_ioctl(ifp, cmd, data);
84832b2494eSpatrick 	}
84932b2494eSpatrick 	if (error == ENETRESET) {
85032b2494eSpatrick 		if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
85132b2494eSpatrick 		    (IFF_UP | IFF_RUNNING)) {
85232b2494eSpatrick 			bwfm_stop(ifp);
85332b2494eSpatrick 			bwfm_init(ifp);
85432b2494eSpatrick 		}
85532b2494eSpatrick 		error = 0;
85632b2494eSpatrick 	}
85732b2494eSpatrick 	splx(s);
85832b2494eSpatrick 	return error;
85932b2494eSpatrick }
86032b2494eSpatrick 
86132b2494eSpatrick int
bwfm_media_change(struct ifnet * ifp)86232b2494eSpatrick bwfm_media_change(struct ifnet *ifp)
86332b2494eSpatrick {
86463498aa8Spatrick 	int error;
86532b2494eSpatrick 
86663498aa8Spatrick 	error = ieee80211_media_change(ifp);
86763498aa8Spatrick 	if (error != ENETRESET)
86863498aa8Spatrick 		return error;
86963498aa8Spatrick 
87063498aa8Spatrick 	if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
87163498aa8Spatrick 	    (IFF_UP | IFF_RUNNING)) {
87263498aa8Spatrick 		bwfm_stop(ifp);
87363498aa8Spatrick 		bwfm_init(ifp);
87463498aa8Spatrick 	}
8752e342c84Skevlo 	return error;
87632b2494eSpatrick }
87732b2494eSpatrick 
87832b2494eSpatrick /* Chip initialization (SDIO, PCIe) */
87932b2494eSpatrick int
bwfm_chip_attach(struct bwfm_softc * sc)88032b2494eSpatrick bwfm_chip_attach(struct bwfm_softc *sc)
88132b2494eSpatrick {
88232b2494eSpatrick 	struct bwfm_core *core;
88332b2494eSpatrick 	int need_socram = 0;
88432b2494eSpatrick 	int has_socram = 0;
88532b2494eSpatrick 	int cpu_found = 0;
88632b2494eSpatrick 	uint32_t val;
88732b2494eSpatrick 
88832b2494eSpatrick 	LIST_INIT(&sc->sc_chip.ch_list);
88932b2494eSpatrick 
89032b2494eSpatrick 	if (sc->sc_buscore_ops->bc_prepare(sc) != 0) {
89132b2494eSpatrick 		printf("%s: failed buscore prepare\n", DEVNAME(sc));
89232b2494eSpatrick 		return 1;
89332b2494eSpatrick 	}
89432b2494eSpatrick 
89532b2494eSpatrick 	val = sc->sc_buscore_ops->bc_read(sc,
89632b2494eSpatrick 	    BWFM_CHIP_BASE + BWFM_CHIP_REG_CHIPID);
89732b2494eSpatrick 	sc->sc_chip.ch_chip = BWFM_CHIP_CHIPID_ID(val);
89832b2494eSpatrick 	sc->sc_chip.ch_chiprev = BWFM_CHIP_CHIPID_REV(val);
89932b2494eSpatrick 
90032b2494eSpatrick 	if ((sc->sc_chip.ch_chip > 0xa000) || (sc->sc_chip.ch_chip < 0x4000))
90132b2494eSpatrick 		snprintf(sc->sc_chip.ch_name, sizeof(sc->sc_chip.ch_name),
90232b2494eSpatrick 		    "%d", sc->sc_chip.ch_chip);
90332b2494eSpatrick 	else
90432b2494eSpatrick 		snprintf(sc->sc_chip.ch_name, sizeof(sc->sc_chip.ch_name),
90532b2494eSpatrick 		    "%x", sc->sc_chip.ch_chip);
90632b2494eSpatrick 
90732b2494eSpatrick 	switch (BWFM_CHIP_CHIPID_TYPE(val))
90832b2494eSpatrick 	{
90932b2494eSpatrick 	case BWFM_CHIP_CHIPID_TYPE_SOCI_SB:
91032b2494eSpatrick 		printf("%s: SoC interconnect SB not implemented\n",
91132b2494eSpatrick 		    DEVNAME(sc));
91232b2494eSpatrick 		return 1;
91332b2494eSpatrick 	case BWFM_CHIP_CHIPID_TYPE_SOCI_AI:
91432b2494eSpatrick 		sc->sc_chip.ch_core_isup = bwfm_chip_ai_isup;
91532b2494eSpatrick 		sc->sc_chip.ch_core_disable = bwfm_chip_ai_disable;
91632b2494eSpatrick 		sc->sc_chip.ch_core_reset = bwfm_chip_ai_reset;
91732b2494eSpatrick 		bwfm_chip_dmp_erom_scan(sc);
91832b2494eSpatrick 		break;
91932b2494eSpatrick 	default:
92032b2494eSpatrick 		printf("%s: SoC interconnect %d unknown\n",
92132b2494eSpatrick 		    DEVNAME(sc), BWFM_CHIP_CHIPID_TYPE(val));
92232b2494eSpatrick 		return 1;
92332b2494eSpatrick 	}
92432b2494eSpatrick 
92532b2494eSpatrick 	LIST_FOREACH(core, &sc->sc_chip.ch_list, co_link) {
92632b2494eSpatrick 		DPRINTF(("%s: 0x%x:%-2d base 0x%08x wrap 0x%08x\n",
92732b2494eSpatrick 		    DEVNAME(sc), core->co_id, core->co_rev,
92832b2494eSpatrick 		    core->co_base, core->co_wrapbase));
92932b2494eSpatrick 
93032b2494eSpatrick 		switch (core->co_id) {
93132b2494eSpatrick 		case BWFM_AGENT_CORE_ARM_CM3:
932de919281Spatrick 			need_socram = 1;
93332b2494eSpatrick 			/* FALLTHROUGH */
93432b2494eSpatrick 		case BWFM_AGENT_CORE_ARM_CR4:
93532b2494eSpatrick 		case BWFM_AGENT_CORE_ARM_CA7:
936de919281Spatrick 			cpu_found = 1;
93732b2494eSpatrick 			break;
93832b2494eSpatrick 		case BWFM_AGENT_INTERNAL_MEM:
939de919281Spatrick 			has_socram = 1;
94032b2494eSpatrick 			break;
94132b2494eSpatrick 		default:
94232b2494eSpatrick 			break;
94332b2494eSpatrick 		}
94432b2494eSpatrick 	}
94532b2494eSpatrick 
94632b2494eSpatrick 	if (!cpu_found) {
94732b2494eSpatrick 		printf("%s: CPU core not detected\n", DEVNAME(sc));
94832b2494eSpatrick 		return 1;
94932b2494eSpatrick 	}
95032b2494eSpatrick 	if (need_socram && !has_socram) {
95132b2494eSpatrick 		printf("%s: RAM core not provided\n", DEVNAME(sc));
95232b2494eSpatrick 		return 1;
95332b2494eSpatrick 	}
95432b2494eSpatrick 
955e2612299Spatrick 	bwfm_chip_set_passive(sc);
95632b2494eSpatrick 
95732b2494eSpatrick 	if (sc->sc_buscore_ops->bc_reset) {
95832b2494eSpatrick 		sc->sc_buscore_ops->bc_reset(sc);
959e2612299Spatrick 		bwfm_chip_set_passive(sc);
96032b2494eSpatrick 	}
96132b2494eSpatrick 
96285d32364Spatrick 	if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4)) != NULL) {
96385d32364Spatrick 		bwfm_chip_tcm_ramsize(sc, core);
96485d32364Spatrick 		bwfm_chip_tcm_rambase(sc);
96585d32364Spatrick 	} else if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_SYS_MEM)) != NULL) {
96685d32364Spatrick 		bwfm_chip_sysmem_ramsize(sc, core);
96785d32364Spatrick 		bwfm_chip_tcm_rambase(sc);
96885d32364Spatrick 	} else if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM)) != NULL) {
96985d32364Spatrick 		bwfm_chip_socram_ramsize(sc, core);
97085d32364Spatrick 	}
97132b2494eSpatrick 
97232b2494eSpatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
97332b2494eSpatrick 	sc->sc_chip.ch_cc_caps = sc->sc_buscore_ops->bc_read(sc,
97432b2494eSpatrick 	    core->co_base + BWFM_CHIP_REG_CAPABILITIES);
97532b2494eSpatrick 	sc->sc_chip.ch_cc_caps_ext = sc->sc_buscore_ops->bc_read(sc,
97632b2494eSpatrick 	    core->co_base + BWFM_CHIP_REG_CAPABILITIES_EXT);
97732b2494eSpatrick 
97832b2494eSpatrick 	core = bwfm_chip_get_pmu(sc);
97932b2494eSpatrick 	if (sc->sc_chip.ch_cc_caps & BWFM_CHIP_REG_CAPABILITIES_PMU) {
98032b2494eSpatrick 		sc->sc_chip.ch_pmucaps = sc->sc_buscore_ops->bc_read(sc,
98132b2494eSpatrick 		    core->co_base + BWFM_CHIP_REG_PMUCAPABILITIES);
98232b2494eSpatrick 		sc->sc_chip.ch_pmurev = sc->sc_chip.ch_pmucaps &
98332b2494eSpatrick 		    BWFM_CHIP_REG_PMUCAPABILITIES_REV_MASK;
98432b2494eSpatrick 	}
98532b2494eSpatrick 
98632b2494eSpatrick 	if (sc->sc_buscore_ops->bc_setup)
98732b2494eSpatrick 		sc->sc_buscore_ops->bc_setup(sc);
98832b2494eSpatrick 
989c6a4ab0dSkettenis 	bwfm_init_board_type(sc);
990c6a4ab0dSkettenis 
99132b2494eSpatrick 	return 0;
99232b2494eSpatrick }
99332b2494eSpatrick 
994821b2e31Spatrick void
bwfm_chip_detach(struct bwfm_softc * sc)995821b2e31Spatrick bwfm_chip_detach(struct bwfm_softc *sc)
996821b2e31Spatrick {
997821b2e31Spatrick 	struct bwfm_core *core, *tmp;
998821b2e31Spatrick 
999821b2e31Spatrick 	LIST_FOREACH_SAFE(core, &sc->sc_chip.ch_list, co_link, tmp) {
1000821b2e31Spatrick 		LIST_REMOVE(core, co_link);
1001821b2e31Spatrick 		free(core, M_DEVBUF, sizeof(*core));
1002821b2e31Spatrick 	}
1003821b2e31Spatrick }
1004821b2e31Spatrick 
100532b2494eSpatrick struct bwfm_core *
bwfm_chip_get_core_idx(struct bwfm_softc * sc,int id,int idx)10063c53ddefSpatrick bwfm_chip_get_core_idx(struct bwfm_softc *sc, int id, int idx)
100732b2494eSpatrick {
100832b2494eSpatrick 	struct bwfm_core *core;
100932b2494eSpatrick 
101032b2494eSpatrick 	LIST_FOREACH(core, &sc->sc_chip.ch_list, co_link) {
10113c53ddefSpatrick 		if (core->co_id == id && idx-- == 0)
101232b2494eSpatrick 			return core;
101332b2494eSpatrick 	}
101432b2494eSpatrick 
101532b2494eSpatrick 	return NULL;
101632b2494eSpatrick }
101732b2494eSpatrick 
101832b2494eSpatrick struct bwfm_core *
bwfm_chip_get_core(struct bwfm_softc * sc,int id)10193c53ddefSpatrick bwfm_chip_get_core(struct bwfm_softc *sc, int id)
10203c53ddefSpatrick {
10213c53ddefSpatrick 	return bwfm_chip_get_core_idx(sc, id, 0);
10223c53ddefSpatrick }
10233c53ddefSpatrick 
10243c53ddefSpatrick struct bwfm_core *
bwfm_chip_get_pmu(struct bwfm_softc * sc)102532b2494eSpatrick bwfm_chip_get_pmu(struct bwfm_softc *sc)
102632b2494eSpatrick {
102732b2494eSpatrick 	struct bwfm_core *cc, *pmu;
102832b2494eSpatrick 
102932b2494eSpatrick 	cc = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
103032b2494eSpatrick 	if (cc->co_rev >= 35 && sc->sc_chip.ch_cc_caps_ext &
103132b2494eSpatrick 	    BWFM_CHIP_REG_CAPABILITIES_EXT_AOB_PRESENT) {
103232b2494eSpatrick 		pmu = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_PMU);
103332b2494eSpatrick 		if (pmu)
103432b2494eSpatrick 			return pmu;
103532b2494eSpatrick 	}
103632b2494eSpatrick 
103732b2494eSpatrick 	return cc;
103832b2494eSpatrick }
103932b2494eSpatrick 
104032b2494eSpatrick /* Functions for the AI interconnect */
104132b2494eSpatrick int
bwfm_chip_ai_isup(struct bwfm_softc * sc,struct bwfm_core * core)104232b2494eSpatrick bwfm_chip_ai_isup(struct bwfm_softc *sc, struct bwfm_core *core)
104332b2494eSpatrick {
104432b2494eSpatrick 	uint32_t ioctl, reset;
104532b2494eSpatrick 
104632b2494eSpatrick 	ioctl = sc->sc_buscore_ops->bc_read(sc,
104732b2494eSpatrick 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
104832b2494eSpatrick 	reset = sc->sc_buscore_ops->bc_read(sc,
104932b2494eSpatrick 	    core->co_wrapbase + BWFM_AGENT_RESET_CTL);
105032b2494eSpatrick 
105132b2494eSpatrick 	if (((ioctl & (BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK)) ==
105232b2494eSpatrick 	    BWFM_AGENT_IOCTL_CLK) &&
105332b2494eSpatrick 	    ((reset & BWFM_AGENT_RESET_CTL_RESET) == 0))
105432b2494eSpatrick 		return 1;
105532b2494eSpatrick 
105632b2494eSpatrick 	return 0;
105732b2494eSpatrick }
105832b2494eSpatrick 
105932b2494eSpatrick void
bwfm_chip_ai_disable(struct bwfm_softc * sc,struct bwfm_core * core,uint32_t prereset,uint32_t reset)106032b2494eSpatrick bwfm_chip_ai_disable(struct bwfm_softc *sc, struct bwfm_core *core,
106132b2494eSpatrick     uint32_t prereset, uint32_t reset)
106232b2494eSpatrick {
106332b2494eSpatrick 	uint32_t val;
106432b2494eSpatrick 	int i;
106532b2494eSpatrick 
106632b2494eSpatrick 	val = sc->sc_buscore_ops->bc_read(sc,
106732b2494eSpatrick 	    core->co_wrapbase + BWFM_AGENT_RESET_CTL);
106832b2494eSpatrick 	if ((val & BWFM_AGENT_RESET_CTL_RESET) == 0) {
106932b2494eSpatrick 
107032b2494eSpatrick 		sc->sc_buscore_ops->bc_write(sc,
107132b2494eSpatrick 		    core->co_wrapbase + BWFM_AGENT_IOCTL,
107232b2494eSpatrick 		    prereset | BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK);
107332b2494eSpatrick 		sc->sc_buscore_ops->bc_read(sc,
107432b2494eSpatrick 		    core->co_wrapbase + BWFM_AGENT_IOCTL);
107532b2494eSpatrick 
107632b2494eSpatrick 		sc->sc_buscore_ops->bc_write(sc,
107732b2494eSpatrick 		    core->co_wrapbase + BWFM_AGENT_RESET_CTL,
107832b2494eSpatrick 		    BWFM_AGENT_RESET_CTL_RESET);
107932b2494eSpatrick 		delay(20);
108032b2494eSpatrick 
108132b2494eSpatrick 		for (i = 300; i > 0; i--) {
108232b2494eSpatrick 			if (sc->sc_buscore_ops->bc_read(sc,
108332b2494eSpatrick 			    core->co_wrapbase + BWFM_AGENT_RESET_CTL) ==
108432b2494eSpatrick 			    BWFM_AGENT_RESET_CTL_RESET)
108532b2494eSpatrick 				break;
108632b2494eSpatrick 		}
108732b2494eSpatrick 		if (i == 0)
108832b2494eSpatrick 			printf("%s: timeout on core reset\n", DEVNAME(sc));
108932b2494eSpatrick 	}
109032b2494eSpatrick 
109132b2494eSpatrick 	sc->sc_buscore_ops->bc_write(sc,
109232b2494eSpatrick 	    core->co_wrapbase + BWFM_AGENT_IOCTL,
109332b2494eSpatrick 	    reset | BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK);
109432b2494eSpatrick 	sc->sc_buscore_ops->bc_read(sc,
109532b2494eSpatrick 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
109632b2494eSpatrick }
109732b2494eSpatrick 
109832b2494eSpatrick void
bwfm_chip_ai_reset(struct bwfm_softc * sc,struct bwfm_core * core,uint32_t prereset,uint32_t reset,uint32_t postreset)109932b2494eSpatrick bwfm_chip_ai_reset(struct bwfm_softc *sc, struct bwfm_core *core,
110032b2494eSpatrick     uint32_t prereset, uint32_t reset, uint32_t postreset)
110132b2494eSpatrick {
110232b2494eSpatrick 	int i;
110332b2494eSpatrick 
110432b2494eSpatrick 	bwfm_chip_ai_disable(sc, core, prereset, reset);
110532b2494eSpatrick 
110632b2494eSpatrick 	for (i = 50; i > 0; i--) {
110732b2494eSpatrick 		if ((sc->sc_buscore_ops->bc_read(sc,
110832b2494eSpatrick 		    core->co_wrapbase + BWFM_AGENT_RESET_CTL) &
110932b2494eSpatrick 		    BWFM_AGENT_RESET_CTL_RESET) == 0)
111032b2494eSpatrick 			break;
111132b2494eSpatrick 		sc->sc_buscore_ops->bc_write(sc,
111232b2494eSpatrick 		    core->co_wrapbase + BWFM_AGENT_RESET_CTL, 0);
111332b2494eSpatrick 		delay(60);
111432b2494eSpatrick 	}
111532b2494eSpatrick 	if (i == 0)
111632b2494eSpatrick 		printf("%s: timeout on core reset\n", DEVNAME(sc));
111732b2494eSpatrick 
111832b2494eSpatrick 	sc->sc_buscore_ops->bc_write(sc,
111932b2494eSpatrick 	    core->co_wrapbase + BWFM_AGENT_IOCTL,
112032b2494eSpatrick 	    postreset | BWFM_AGENT_IOCTL_CLK);
112132b2494eSpatrick 	sc->sc_buscore_ops->bc_read(sc,
112232b2494eSpatrick 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
112332b2494eSpatrick }
112432b2494eSpatrick 
112532b2494eSpatrick void
bwfm_chip_dmp_erom_scan(struct bwfm_softc * sc)112632b2494eSpatrick bwfm_chip_dmp_erom_scan(struct bwfm_softc *sc)
112732b2494eSpatrick {
112832b2494eSpatrick 	uint32_t erom, val, base, wrap;
112932b2494eSpatrick 	uint8_t type = 0;
113032b2494eSpatrick 	uint16_t id;
113132b2494eSpatrick 	uint8_t nmw, nsw, rev;
113232b2494eSpatrick 	struct bwfm_core *core;
113332b2494eSpatrick 
113432b2494eSpatrick 	erom = sc->sc_buscore_ops->bc_read(sc,
113532b2494eSpatrick 	    BWFM_CHIP_BASE + BWFM_CHIP_REG_EROMPTR);
113632b2494eSpatrick 	while (type != BWFM_DMP_DESC_EOT) {
113732b2494eSpatrick 		val = sc->sc_buscore_ops->bc_read(sc, erom);
113832b2494eSpatrick 		type = val & BWFM_DMP_DESC_MASK;
113932b2494eSpatrick 		erom += 4;
114032b2494eSpatrick 
114132b2494eSpatrick 		if (type != BWFM_DMP_DESC_COMPONENT)
114232b2494eSpatrick 			continue;
114332b2494eSpatrick 
114432b2494eSpatrick 		id = (val & BWFM_DMP_COMP_PARTNUM)
114532b2494eSpatrick 		    >> BWFM_DMP_COMP_PARTNUM_S;
114632b2494eSpatrick 
114732b2494eSpatrick 		val = sc->sc_buscore_ops->bc_read(sc, erom);
114832b2494eSpatrick 		type = val & BWFM_DMP_DESC_MASK;
114932b2494eSpatrick 		erom += 4;
115032b2494eSpatrick 
115132b2494eSpatrick 		if (type != BWFM_DMP_DESC_COMPONENT) {
115232b2494eSpatrick 			printf("%s: not component descriptor\n", DEVNAME(sc));
115332b2494eSpatrick 			return;
115432b2494eSpatrick 		}
115532b2494eSpatrick 
115632b2494eSpatrick 		nmw = (val & BWFM_DMP_COMP_NUM_MWRAP)
115732b2494eSpatrick 		    >> BWFM_DMP_COMP_NUM_MWRAP_S;
115832b2494eSpatrick 		nsw = (val & BWFM_DMP_COMP_NUM_SWRAP)
115932b2494eSpatrick 		    >> BWFM_DMP_COMP_NUM_SWRAP_S;
116032b2494eSpatrick 		rev = (val & BWFM_DMP_COMP_REVISION)
116132b2494eSpatrick 		    >> BWFM_DMP_COMP_REVISION_S;
116232b2494eSpatrick 
11639d277f82Spatrick 		if (nmw + nsw == 0 && id != BWFM_AGENT_CORE_PMU &&
11649d277f82Spatrick 		    id != BWFM_AGENT_CORE_GCI)
116532b2494eSpatrick 			continue;
116632b2494eSpatrick 
116732b2494eSpatrick 		if (bwfm_chip_dmp_get_regaddr(sc, &erom, &base, &wrap))
116832b2494eSpatrick 			continue;
116932b2494eSpatrick 
117032b2494eSpatrick 		core = malloc(sizeof(*core), M_DEVBUF, M_WAITOK);
117132b2494eSpatrick 		core->co_id = id;
117232b2494eSpatrick 		core->co_base = base;
117332b2494eSpatrick 		core->co_wrapbase = wrap;
117432b2494eSpatrick 		core->co_rev = rev;
117532b2494eSpatrick 		LIST_INSERT_HEAD(&sc->sc_chip.ch_list, core, co_link);
117632b2494eSpatrick 	}
117732b2494eSpatrick }
117832b2494eSpatrick 
117932b2494eSpatrick int
bwfm_chip_dmp_get_regaddr(struct bwfm_softc * sc,uint32_t * erom,uint32_t * base,uint32_t * wrap)118032b2494eSpatrick bwfm_chip_dmp_get_regaddr(struct bwfm_softc *sc, uint32_t *erom,
118132b2494eSpatrick     uint32_t *base, uint32_t *wrap)
118232b2494eSpatrick {
118332b2494eSpatrick 	uint8_t type = 0, mpnum = 0;
118432b2494eSpatrick 	uint8_t stype, sztype, wraptype;
118532b2494eSpatrick 	uint32_t val;
118632b2494eSpatrick 
118732b2494eSpatrick 	*base = 0;
118832b2494eSpatrick 	*wrap = 0;
118932b2494eSpatrick 
119032b2494eSpatrick 	val = sc->sc_buscore_ops->bc_read(sc, *erom);
119132b2494eSpatrick 	type = val & BWFM_DMP_DESC_MASK;
119232b2494eSpatrick 	if (type == BWFM_DMP_DESC_MASTER_PORT) {
119332b2494eSpatrick 		mpnum = (val & BWFM_DMP_MASTER_PORT_NUM)
119432b2494eSpatrick 		    >> BWFM_DMP_MASTER_PORT_NUM_S;
119532b2494eSpatrick 		wraptype = BWFM_DMP_SLAVE_TYPE_MWRAP;
119632b2494eSpatrick 		*erom += 4;
119732b2494eSpatrick 	} else if ((type & ~BWFM_DMP_DESC_ADDRSIZE_GT32) ==
119832b2494eSpatrick 	    BWFM_DMP_DESC_ADDRESS)
119932b2494eSpatrick 		wraptype = BWFM_DMP_SLAVE_TYPE_SWRAP;
120032b2494eSpatrick 	else
120132b2494eSpatrick 		return 1;
120232b2494eSpatrick 
120332b2494eSpatrick 	do {
120432b2494eSpatrick 		do {
120532b2494eSpatrick 			val = sc->sc_buscore_ops->bc_read(sc, *erom);
120632b2494eSpatrick 			type = val & BWFM_DMP_DESC_MASK;
120732b2494eSpatrick 			if (type == BWFM_DMP_DESC_COMPONENT)
120832b2494eSpatrick 				return 0;
120932b2494eSpatrick 			if (type == BWFM_DMP_DESC_EOT)
121032b2494eSpatrick 				return 1;
121132b2494eSpatrick 			*erom += 4;
121232b2494eSpatrick 		} while ((type & ~BWFM_DMP_DESC_ADDRSIZE_GT32) !=
121332b2494eSpatrick 		     BWFM_DMP_DESC_ADDRESS);
121432b2494eSpatrick 
121532b2494eSpatrick 		if (type & BWFM_DMP_DESC_ADDRSIZE_GT32)
121632b2494eSpatrick 			*erom += 4;
121732b2494eSpatrick 
121832b2494eSpatrick 		sztype = (val & BWFM_DMP_SLAVE_SIZE_TYPE)
121932b2494eSpatrick 		    >> BWFM_DMP_SLAVE_SIZE_TYPE_S;
122032b2494eSpatrick 		if (sztype == BWFM_DMP_SLAVE_SIZE_DESC) {
122132b2494eSpatrick 			val = sc->sc_buscore_ops->bc_read(sc, *erom);
122232b2494eSpatrick 			type = val & BWFM_DMP_DESC_MASK;
122332b2494eSpatrick 			if (type & BWFM_DMP_DESC_ADDRSIZE_GT32)
122432b2494eSpatrick 				*erom += 8;
122532b2494eSpatrick 			else
122632b2494eSpatrick 				*erom += 4;
122732b2494eSpatrick 		}
12289d277f82Spatrick 		if (sztype != BWFM_DMP_SLAVE_SIZE_4K &&
12299d277f82Spatrick 		    sztype != BWFM_DMP_SLAVE_SIZE_8K)
123032b2494eSpatrick 			continue;
123132b2494eSpatrick 
123232b2494eSpatrick 		stype = (val & BWFM_DMP_SLAVE_TYPE) >> BWFM_DMP_SLAVE_TYPE_S;
123332b2494eSpatrick 		if (*base == 0 && stype == BWFM_DMP_SLAVE_TYPE_SLAVE)
123432b2494eSpatrick 			*base = val & BWFM_DMP_SLAVE_ADDR_BASE;
123532b2494eSpatrick 		if (*wrap == 0 && stype == wraptype)
123632b2494eSpatrick 			*wrap = val & BWFM_DMP_SLAVE_ADDR_BASE;
123732b2494eSpatrick 	} while (*base == 0 || *wrap == 0);
123832b2494eSpatrick 
123932b2494eSpatrick 	return 0;
124032b2494eSpatrick }
124132b2494eSpatrick 
124232b2494eSpatrick /* Core configuration */
1243e2612299Spatrick int
bwfm_chip_set_active(struct bwfm_softc * sc,uint32_t rstvec)1244e2612299Spatrick bwfm_chip_set_active(struct bwfm_softc *sc, uint32_t rstvec)
1245e2612299Spatrick {
1246e2612299Spatrick 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL)
1247e2612299Spatrick 		return bwfm_chip_cr4_set_active(sc, rstvec);
1248e2612299Spatrick 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL)
1249e2612299Spatrick 		return bwfm_chip_ca7_set_active(sc, rstvec);
1250e2612299Spatrick 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL)
1251e2612299Spatrick 		return bwfm_chip_cm3_set_active(sc);
1252e2612299Spatrick 	return 1;
1253e2612299Spatrick }
1254e2612299Spatrick 
1255e2612299Spatrick void
bwfm_chip_set_passive(struct bwfm_softc * sc)1256e2612299Spatrick bwfm_chip_set_passive(struct bwfm_softc *sc)
1257e2612299Spatrick {
1258e2612299Spatrick 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL) {
1259e2612299Spatrick 		bwfm_chip_cr4_set_passive(sc);
1260e2612299Spatrick 		return;
1261e2612299Spatrick 	}
1262e2612299Spatrick 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL) {
1263e2612299Spatrick 		bwfm_chip_ca7_set_passive(sc);
1264e2612299Spatrick 		return;
1265e2612299Spatrick 	}
1266e2612299Spatrick 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL) {
1267e2612299Spatrick 		bwfm_chip_cm3_set_passive(sc);
1268e2612299Spatrick 		return;
1269e2612299Spatrick 	}
1270e2612299Spatrick }
1271e2612299Spatrick 
1272e2612299Spatrick int
bwfm_chip_cr4_set_active(struct bwfm_softc * sc,uint32_t rstvec)1273e2612299Spatrick bwfm_chip_cr4_set_active(struct bwfm_softc *sc, uint32_t rstvec)
1274e2612299Spatrick {
1275e2612299Spatrick 	struct bwfm_core *core;
1276e2612299Spatrick 
1277608f8b14Spatrick 	sc->sc_buscore_ops->bc_activate(sc, rstvec);
1278e2612299Spatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4);
1279e2612299Spatrick 	sc->sc_chip.ch_core_reset(sc, core,
1280e2612299Spatrick 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT, 0, 0);
1281e2612299Spatrick 
1282e2612299Spatrick 	return 0;
1283e2612299Spatrick }
1284e2612299Spatrick 
128532b2494eSpatrick void
bwfm_chip_cr4_set_passive(struct bwfm_softc * sc)128632b2494eSpatrick bwfm_chip_cr4_set_passive(struct bwfm_softc *sc)
128732b2494eSpatrick {
1288c6962397Spatrick 	struct bwfm_core *core;
12893e129b89Spatrick 	uint32_t val;
129064be38baSpatrick 	int i = 0;
1291c6962397Spatrick 
1292c6962397Spatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4);
12933e129b89Spatrick 	val = sc->sc_buscore_ops->bc_read(sc,
12943e129b89Spatrick 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
12953e129b89Spatrick 	sc->sc_chip.ch_core_reset(sc, core,
12963e129b89Spatrick 	    val & BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
12973e129b89Spatrick 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
12983e129b89Spatrick 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT);
12993e129b89Spatrick 
130064be38baSpatrick 	while ((core = bwfm_chip_get_core_idx(sc, BWFM_AGENT_CORE_80211, i++)))
130164be38baSpatrick 		sc->sc_chip.ch_core_disable(sc, core,
130264be38baSpatrick 		    BWFM_AGENT_D11_IOCTL_PHYRESET |
130364be38baSpatrick 		    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN,
1304c6962397Spatrick 		    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN);
130532b2494eSpatrick }
130632b2494eSpatrick 
1307e2612299Spatrick int
bwfm_chip_ca7_set_active(struct bwfm_softc * sc,uint32_t rstvec)1308e2612299Spatrick bwfm_chip_ca7_set_active(struct bwfm_softc *sc, uint32_t rstvec)
1309e2612299Spatrick {
13103e129b89Spatrick 	struct bwfm_core *core;
13113e129b89Spatrick 
13123e129b89Spatrick 	sc->sc_buscore_ops->bc_activate(sc, rstvec);
13133e129b89Spatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7);
13143e129b89Spatrick 	sc->sc_chip.ch_core_reset(sc, core,
13153e129b89Spatrick 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT, 0, 0);
13163e129b89Spatrick 
13173e129b89Spatrick 	return 0;
1318e2612299Spatrick }
1319e2612299Spatrick 
132032b2494eSpatrick void
bwfm_chip_ca7_set_passive(struct bwfm_softc * sc)132132b2494eSpatrick bwfm_chip_ca7_set_passive(struct bwfm_softc *sc)
132232b2494eSpatrick {
13233e129b89Spatrick 	struct bwfm_core *core;
13243e129b89Spatrick 	uint32_t val;
1325edb884eaSkettenis 	int i = 0;
13263e129b89Spatrick 
13273e129b89Spatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7);
13283e129b89Spatrick 	val = sc->sc_buscore_ops->bc_read(sc,
13293e129b89Spatrick 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
13303e129b89Spatrick 	sc->sc_chip.ch_core_reset(sc, core,
13313e129b89Spatrick 	    val & BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
13323e129b89Spatrick 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
13333e129b89Spatrick 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT);
13343e129b89Spatrick 
1335edb884eaSkettenis 	while ((core = bwfm_chip_get_core_idx(sc, BWFM_AGENT_CORE_80211, i++)))
1336edb884eaSkettenis 		sc->sc_chip.ch_core_disable(sc, core,
1337edb884eaSkettenis 		    BWFM_AGENT_D11_IOCTL_PHYRESET |
1338edb884eaSkettenis 		    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN,
13393e129b89Spatrick 		    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN);
134032b2494eSpatrick }
134132b2494eSpatrick 
1342e2612299Spatrick int
bwfm_chip_cm3_set_active(struct bwfm_softc * sc)1343e2612299Spatrick bwfm_chip_cm3_set_active(struct bwfm_softc *sc)
1344e2612299Spatrick {
1345b5537148Spatrick 	struct bwfm_core *core;
1346b5537148Spatrick 
1347b5537148Spatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM);
1348b5537148Spatrick 	if (!sc->sc_chip.ch_core_isup(sc, core))
1349b5537148Spatrick 		return 1;
1350b5537148Spatrick 
1351b5537148Spatrick 	sc->sc_buscore_ops->bc_activate(sc, 0);
1352b5537148Spatrick 
1353b5537148Spatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3);
1354b5537148Spatrick 	sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
1355b5537148Spatrick 
1356b5537148Spatrick 	return 0;
1357e2612299Spatrick }
1358e2612299Spatrick 
135932b2494eSpatrick void
bwfm_chip_cm3_set_passive(struct bwfm_softc * sc)136032b2494eSpatrick bwfm_chip_cm3_set_passive(struct bwfm_softc *sc)
136132b2494eSpatrick {
136232b2494eSpatrick 	struct bwfm_core *core;
136332b2494eSpatrick 
136432b2494eSpatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3);
136532b2494eSpatrick 	sc->sc_chip.ch_core_disable(sc, core, 0, 0);
136632b2494eSpatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_80211);
136732b2494eSpatrick 	sc->sc_chip.ch_core_reset(sc, core, BWFM_AGENT_D11_IOCTL_PHYRESET |
136832b2494eSpatrick 	    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN, BWFM_AGENT_D11_IOCTL_PHYCLOCKEN,
136932b2494eSpatrick 	    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN);
137032b2494eSpatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM);
137132b2494eSpatrick 	sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
137232b2494eSpatrick 
137332b2494eSpatrick 	if (sc->sc_chip.ch_chip == BRCM_CC_43430_CHIP_ID) {
137432b2494eSpatrick 		sc->sc_buscore_ops->bc_write(sc,
137532b2494eSpatrick 		    core->co_base + BWFM_SOCRAM_BANKIDX, 3);
137632b2494eSpatrick 		sc->sc_buscore_ops->bc_write(sc,
137732b2494eSpatrick 		    core->co_base + BWFM_SOCRAM_BANKPDA, 0);
137832b2494eSpatrick 	}
137932b2494eSpatrick }
138032b2494eSpatrick 
1381a4c8e2b6Spatrick int
bwfm_chip_sr_capable(struct bwfm_softc * sc)1382a4c8e2b6Spatrick bwfm_chip_sr_capable(struct bwfm_softc *sc)
1383a4c8e2b6Spatrick {
1384a4c8e2b6Spatrick 	struct bwfm_core *core;
1385a4c8e2b6Spatrick 	uint32_t reg;
1386a4c8e2b6Spatrick 
1387a4c8e2b6Spatrick 	if (sc->sc_chip.ch_pmurev < 17)
1388a4c8e2b6Spatrick 		return 0;
1389a4c8e2b6Spatrick 
1390a4c8e2b6Spatrick 	switch (sc->sc_chip.ch_chip) {
1391315a0156Spatrick 	case BRCM_CC_4345_CHIP_ID:
1392a4c8e2b6Spatrick 	case BRCM_CC_4354_CHIP_ID:
1393a4c8e2b6Spatrick 	case BRCM_CC_4356_CHIP_ID:
13945152d48cSpatrick 		core = bwfm_chip_get_pmu(sc);
1395a4c8e2b6Spatrick 		sc->sc_buscore_ops->bc_write(sc, core->co_base +
1396a4c8e2b6Spatrick 		    BWFM_CHIP_REG_CHIPCONTROL_ADDR, 3);
1397a4c8e2b6Spatrick 		reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
1398a4c8e2b6Spatrick 		    BWFM_CHIP_REG_CHIPCONTROL_DATA);
1399a4c8e2b6Spatrick 		return (reg & (1 << 2)) != 0;
1400a4c8e2b6Spatrick 	case BRCM_CC_43241_CHIP_ID:
1401a4c8e2b6Spatrick 	case BRCM_CC_4335_CHIP_ID:
1402a4c8e2b6Spatrick 	case BRCM_CC_4339_CHIP_ID:
14035152d48cSpatrick 		core = bwfm_chip_get_pmu(sc);
1404a4c8e2b6Spatrick 		sc->sc_buscore_ops->bc_write(sc, core->co_base +
1405a4c8e2b6Spatrick 		    BWFM_CHIP_REG_CHIPCONTROL_ADDR, 3);
1406a4c8e2b6Spatrick 		reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
1407a4c8e2b6Spatrick 		    BWFM_CHIP_REG_CHIPCONTROL_DATA);
1408a4c8e2b6Spatrick 		return reg != 0;
1409a4c8e2b6Spatrick 	case BRCM_CC_43430_CHIP_ID:
14105152d48cSpatrick 		core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
1411a4c8e2b6Spatrick 		reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
1412a4c8e2b6Spatrick 		    BWFM_CHIP_REG_SR_CONTROL1);
1413a4c8e2b6Spatrick 		return reg != 0;
14148db7132fSpatrick 	case CY_CC_4373_CHIP_ID:
14158db7132fSpatrick 		core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
14168db7132fSpatrick 		reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
14178db7132fSpatrick 		    BWFM_CHIP_REG_SR_CONTROL0);
14188db7132fSpatrick 		return (reg & BWFM_CHIP_REG_SR_CONTROL0_ENABLE) != 0;
14198db7132fSpatrick 	case BRCM_CC_4359_CHIP_ID:
14208db7132fSpatrick 	case CY_CC_43752_CHIP_ID:
14218db7132fSpatrick 	case CY_CC_43012_CHIP_ID:
14228db7132fSpatrick 		core = bwfm_chip_get_pmu(sc);
14238db7132fSpatrick 		reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
14248db7132fSpatrick 		    BWFM_CHIP_REG_RETENTION_CTL);
14258db7132fSpatrick 		return (reg & (BWFM_CHIP_REG_RETENTION_CTL_MACPHY_DIS |
14268db7132fSpatrick 			       BWFM_CHIP_REG_RETENTION_CTL_LOGIC_DIS)) == 0;
1427c38a9bc9Spatrick 	case BRCM_CC_4378_CHIP_ID:
1428c38a9bc9Spatrick 		return 0;
1429a4c8e2b6Spatrick 	default:
14305152d48cSpatrick 		core = bwfm_chip_get_pmu(sc);
1431a4c8e2b6Spatrick 		reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
1432a4c8e2b6Spatrick 		    BWFM_CHIP_REG_PMUCAPABILITIES_EXT);
1433a4c8e2b6Spatrick 		if ((reg & BWFM_CHIP_REG_PMUCAPABILITIES_SR_SUPP) == 0)
1434a4c8e2b6Spatrick 			return 0;
1435a4c8e2b6Spatrick 
1436a4c8e2b6Spatrick 		reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
1437a4c8e2b6Spatrick 		    BWFM_CHIP_REG_RETENTION_CTL);
1438a4c8e2b6Spatrick 		return (reg & (BWFM_CHIP_REG_RETENTION_CTL_MACPHY_DIS |
1439a4c8e2b6Spatrick 			       BWFM_CHIP_REG_RETENTION_CTL_LOGIC_DIS)) == 0;
1440a4c8e2b6Spatrick 	}
1441a4c8e2b6Spatrick }
1442a4c8e2b6Spatrick 
144385d32364Spatrick /* RAM size helpers */
144485d32364Spatrick void
bwfm_chip_socram_ramsize(struct bwfm_softc * sc,struct bwfm_core * core)144585d32364Spatrick bwfm_chip_socram_ramsize(struct bwfm_softc *sc, struct bwfm_core *core)
144685d32364Spatrick {
14475212e703Spatrick 	uint32_t coreinfo, nb, lss, banksize, bankinfo;
14485212e703Spatrick 	uint32_t ramsize = 0, srsize = 0;
14495212e703Spatrick 	int i;
14505212e703Spatrick 
14515212e703Spatrick 	if (!sc->sc_chip.ch_core_isup(sc, core))
14525212e703Spatrick 		sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
14535212e703Spatrick 
14545212e703Spatrick 	coreinfo = sc->sc_buscore_ops->bc_read(sc,
14555212e703Spatrick 	    core->co_base + BWFM_SOCRAM_COREINFO);
14565212e703Spatrick 	nb = (coreinfo & BWFM_SOCRAM_COREINFO_SRNB_MASK)
14575212e703Spatrick 	    >> BWFM_SOCRAM_COREINFO_SRNB_SHIFT;
14585212e703Spatrick 
14595212e703Spatrick 	if (core->co_rev <= 7 || core->co_rev == 12) {
14605212e703Spatrick 		banksize = coreinfo & BWFM_SOCRAM_COREINFO_SRBSZ_MASK;
14615212e703Spatrick 		lss = (coreinfo & BWFM_SOCRAM_COREINFO_LSS_MASK)
14625212e703Spatrick 		    >> BWFM_SOCRAM_COREINFO_LSS_SHIFT;
14635212e703Spatrick 		if (lss != 0)
14645212e703Spatrick 			nb--;
14655212e703Spatrick 		ramsize = nb * (1 << (banksize + BWFM_SOCRAM_COREINFO_SRBSZ_BASE));
14665212e703Spatrick 		if (lss != 0)
14675212e703Spatrick 			ramsize += (1 << ((lss - 1) + BWFM_SOCRAM_COREINFO_SRBSZ_BASE));
14685212e703Spatrick 	} else {
14695212e703Spatrick 		for (i = 0; i < nb; i++) {
14705212e703Spatrick 			sc->sc_buscore_ops->bc_write(sc,
14715212e703Spatrick 			    core->co_base + BWFM_SOCRAM_BANKIDX,
14725212e703Spatrick 			    (BWFM_SOCRAM_BANKIDX_MEMTYPE_RAM <<
14735212e703Spatrick 			    BWFM_SOCRAM_BANKIDX_MEMTYPE_SHIFT) | i);
14745212e703Spatrick 			bankinfo = sc->sc_buscore_ops->bc_read(sc,
14755212e703Spatrick 			    core->co_base + BWFM_SOCRAM_BANKINFO);
14765212e703Spatrick 			banksize = ((bankinfo & BWFM_SOCRAM_BANKINFO_SZMASK) + 1)
14775212e703Spatrick 			    * BWFM_SOCRAM_BANKINFO_SZBASE;
14785212e703Spatrick 			ramsize += banksize;
14795212e703Spatrick 			if (bankinfo & BWFM_SOCRAM_BANKINFO_RETNTRAM_MASK)
14805212e703Spatrick 				srsize += banksize;
14815212e703Spatrick 		}
14825212e703Spatrick 	}
14835212e703Spatrick 
14845212e703Spatrick 	switch (sc->sc_chip.ch_chip) {
14855212e703Spatrick 	case BRCM_CC_4334_CHIP_ID:
14865212e703Spatrick 		if (sc->sc_chip.ch_chiprev < 2)
14875212e703Spatrick 			srsize = 32 * 1024;
14885212e703Spatrick 		break;
14895212e703Spatrick 	case BRCM_CC_43430_CHIP_ID:
14905212e703Spatrick 		srsize = 64 * 1024;
14915212e703Spatrick 		break;
14925212e703Spatrick 	default:
14935212e703Spatrick 		break;
14945212e703Spatrick 	}
14955212e703Spatrick 
14965212e703Spatrick 	sc->sc_chip.ch_ramsize = ramsize;
14975212e703Spatrick 	sc->sc_chip.ch_srsize = srsize;
149885d32364Spatrick }
149985d32364Spatrick 
150085d32364Spatrick void
bwfm_chip_sysmem_ramsize(struct bwfm_softc * sc,struct bwfm_core * core)150185d32364Spatrick bwfm_chip_sysmem_ramsize(struct bwfm_softc *sc, struct bwfm_core *core)
150285d32364Spatrick {
1503c9aa06c9Spatrick 	uint32_t coreinfo, nb, banksize, bankinfo;
1504c9aa06c9Spatrick 	uint32_t ramsize = 0;
1505c9aa06c9Spatrick 	int i;
1506c9aa06c9Spatrick 
1507c9aa06c9Spatrick 	if (!sc->sc_chip.ch_core_isup(sc, core))
1508c9aa06c9Spatrick 		sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
1509c9aa06c9Spatrick 
1510c9aa06c9Spatrick 	coreinfo = sc->sc_buscore_ops->bc_read(sc,
1511c9aa06c9Spatrick 	    core->co_base + BWFM_SOCRAM_COREINFO);
1512c9aa06c9Spatrick 	nb = (coreinfo & BWFM_SOCRAM_COREINFO_SRNB_MASK)
1513c9aa06c9Spatrick 	    >> BWFM_SOCRAM_COREINFO_SRNB_SHIFT;
1514c9aa06c9Spatrick 
1515c9aa06c9Spatrick 	for (i = 0; i < nb; i++) {
1516c9aa06c9Spatrick 		sc->sc_buscore_ops->bc_write(sc,
1517c9aa06c9Spatrick 		    core->co_base + BWFM_SOCRAM_BANKIDX,
1518c9aa06c9Spatrick 		    (BWFM_SOCRAM_BANKIDX_MEMTYPE_RAM <<
1519c9aa06c9Spatrick 		    BWFM_SOCRAM_BANKIDX_MEMTYPE_SHIFT) | i);
1520c9aa06c9Spatrick 		bankinfo = sc->sc_buscore_ops->bc_read(sc,
1521c9aa06c9Spatrick 		    core->co_base + BWFM_SOCRAM_BANKINFO);
1522c9aa06c9Spatrick 		banksize = ((bankinfo & BWFM_SOCRAM_BANKINFO_SZMASK) + 1)
1523c9aa06c9Spatrick 		    * BWFM_SOCRAM_BANKINFO_SZBASE;
1524c9aa06c9Spatrick 		ramsize += banksize;
1525c9aa06c9Spatrick 	}
1526c9aa06c9Spatrick 
1527c9aa06c9Spatrick 	sc->sc_chip.ch_ramsize = ramsize;
152885d32364Spatrick }
152985d32364Spatrick 
153085d32364Spatrick void
bwfm_chip_tcm_ramsize(struct bwfm_softc * sc,struct bwfm_core * core)153185d32364Spatrick bwfm_chip_tcm_ramsize(struct bwfm_softc *sc, struct bwfm_core *core)
153285d32364Spatrick {
15335ea0cc51Spatrick 	uint32_t cap, nab, nbb, totb, bxinfo, blksize, ramsize = 0;
153485d32364Spatrick 	int i;
153585d32364Spatrick 
153685d32364Spatrick 	cap = sc->sc_buscore_ops->bc_read(sc, core->co_base + BWFM_ARMCR4_CAP);
153785d32364Spatrick 	nab = (cap & BWFM_ARMCR4_CAP_TCBANB_MASK) >> BWFM_ARMCR4_CAP_TCBANB_SHIFT;
153885d32364Spatrick 	nbb = (cap & BWFM_ARMCR4_CAP_TCBBNB_MASK) >> BWFM_ARMCR4_CAP_TCBBNB_SHIFT;
153985d32364Spatrick 	totb = nab + nbb;
154085d32364Spatrick 
154185d32364Spatrick 	for (i = 0; i < totb; i++) {
154285d32364Spatrick 		sc->sc_buscore_ops->bc_write(sc,
154385d32364Spatrick 		    core->co_base + BWFM_ARMCR4_BANKIDX, i);
154485d32364Spatrick 		bxinfo = sc->sc_buscore_ops->bc_read(sc,
154585d32364Spatrick 		    core->co_base + BWFM_ARMCR4_BANKINFO);
15465ea0cc51Spatrick 		if (bxinfo & BWFM_ARMCR4_BANKINFO_BLK_1K_MASK)
15475ea0cc51Spatrick 			blksize = 1024;
15485ea0cc51Spatrick 		else
15495ea0cc51Spatrick 			blksize = 8192;
15505212e703Spatrick 		ramsize += ((bxinfo & BWFM_ARMCR4_BANKINFO_BSZ_MASK) + 1) *
15515ea0cc51Spatrick 		    blksize;
155285d32364Spatrick 	}
155385d32364Spatrick 
15545212e703Spatrick 	sc->sc_chip.ch_ramsize = ramsize;
155585d32364Spatrick }
155685d32364Spatrick 
155785d32364Spatrick void
bwfm_chip_tcm_rambase(struct bwfm_softc * sc)155885d32364Spatrick bwfm_chip_tcm_rambase(struct bwfm_softc *sc)
155985d32364Spatrick {
156085d32364Spatrick 	switch (sc->sc_chip.ch_chip) {
156185d32364Spatrick 	case BRCM_CC_4345_CHIP_ID:
156285d32364Spatrick 		sc->sc_chip.ch_rambase = 0x198000;
156385d32364Spatrick 		break;
156485d32364Spatrick 	case BRCM_CC_4335_CHIP_ID:
156585d32364Spatrick 	case BRCM_CC_4339_CHIP_ID:
156685d32364Spatrick 	case BRCM_CC_4350_CHIP_ID:
156785d32364Spatrick 	case BRCM_CC_4354_CHIP_ID:
156885d32364Spatrick 	case BRCM_CC_4356_CHIP_ID:
156985d32364Spatrick 	case BRCM_CC_43567_CHIP_ID:
157085d32364Spatrick 	case BRCM_CC_43569_CHIP_ID:
157185d32364Spatrick 	case BRCM_CC_43570_CHIP_ID:
157285d32364Spatrick 	case BRCM_CC_4358_CHIP_ID:
157385d32364Spatrick 	case BRCM_CC_43602_CHIP_ID:
157485d32364Spatrick 	case BRCM_CC_4371_CHIP_ID:
157585d32364Spatrick 		sc->sc_chip.ch_rambase = 0x180000;
157685d32364Spatrick 		break;
15778db7132fSpatrick 	case BRCM_CC_43465_CHIP_ID:
15788db7132fSpatrick 	case BRCM_CC_43525_CHIP_ID:
15798db7132fSpatrick 	case BRCM_CC_4365_CHIP_ID:
15808db7132fSpatrick 	case BRCM_CC_4366_CHIP_ID:
15818db7132fSpatrick 	case BRCM_CC_43664_CHIP_ID:
15828db7132fSpatrick 	case BRCM_CC_43666_CHIP_ID:
15838db7132fSpatrick 		sc->sc_chip.ch_rambase = 0x200000;
15848db7132fSpatrick 		break;
15850d580645Skettenis 	case BRCM_CC_4359_CHIP_ID:
15860d580645Skettenis 		if (sc->sc_chip.ch_chiprev < 9)
15870d580645Skettenis 			sc->sc_chip.ch_rambase = 0x180000;
15880d580645Skettenis 		else
15890d580645Skettenis 			sc->sc_chip.ch_rambase = 0x160000;
15900d580645Skettenis 		break;
15918db7132fSpatrick 	case BRCM_CC_4355_CHIP_ID:
15928db7132fSpatrick 	case BRCM_CC_4364_CHIP_ID:
159385d32364Spatrick 	case CY_CC_4373_CHIP_ID:
159485d32364Spatrick 		sc->sc_chip.ch_rambase = 0x160000;
159585d32364Spatrick 		break;
15968db7132fSpatrick 	case BRCM_CC_4377_CHIP_ID:
15978db7132fSpatrick 	case CY_CC_43752_CHIP_ID:
15988db7132fSpatrick 		sc->sc_chip.ch_rambase = 0x170000;
15998db7132fSpatrick 		break;
1600c38a9bc9Spatrick 	case BRCM_CC_4378_CHIP_ID:
1601c38a9bc9Spatrick 		sc->sc_chip.ch_rambase = 0x352000;
1602c38a9bc9Spatrick 		break;
16038db7132fSpatrick 	case BRCM_CC_4387_CHIP_ID:
16048db7132fSpatrick 		sc->sc_chip.ch_rambase = 0x740000;
16058db7132fSpatrick 		break;
160685d32364Spatrick 	default:
160785d32364Spatrick 		printf("%s: unknown chip: %d\n", DEVNAME(sc),
160885d32364Spatrick 		    sc->sc_chip.ch_chip);
160985d32364Spatrick 		break;
161085d32364Spatrick 	}
161185d32364Spatrick }
161285d32364Spatrick 
161332b2494eSpatrick /* BCDC protocol implementation */
161432b2494eSpatrick int
bwfm_proto_bcdc_query_dcmd(struct bwfm_softc * sc,int ifidx,int cmd,char * buf,size_t * len)161532b2494eSpatrick bwfm_proto_bcdc_query_dcmd(struct bwfm_softc *sc, int ifidx,
161632b2494eSpatrick     int cmd, char *buf, size_t *len)
161732b2494eSpatrick {
161832b2494eSpatrick 	struct bwfm_proto_bcdc_dcmd *dcmd;
161932b2494eSpatrick 	size_t size = sizeof(dcmd->hdr) + *len;
1620029d6dd5Spatrick 	int ret = 1, reqid;
162132b2494eSpatrick 
1622029d6dd5Spatrick 	reqid = sc->sc_bcdc_reqid++;
162332b2494eSpatrick 
162432b2494eSpatrick 	if (*len > sizeof(dcmd->buf))
1625029d6dd5Spatrick 		return ret;
162632b2494eSpatrick 
1627029d6dd5Spatrick 	dcmd = malloc(size, M_TEMP, M_WAITOK | M_ZERO);
162832b2494eSpatrick 	dcmd->hdr.cmd = htole32(cmd);
162932b2494eSpatrick 	dcmd->hdr.len = htole32(*len);
163032b2494eSpatrick 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_GET;
163132b2494eSpatrick 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_ID_SET(reqid);
163232b2494eSpatrick 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_IF_SET(ifidx);
163332b2494eSpatrick 	dcmd->hdr.flags = htole32(dcmd->hdr.flags);
163432b2494eSpatrick 	memcpy(&dcmd->buf, buf, *len);
163532b2494eSpatrick 
1636029d6dd5Spatrick 	if (bwfm_proto_bcdc_txctl(sc, reqid, (char *)dcmd, &size)) {
163732b2494eSpatrick 		DPRINTF(("%s: tx failed\n", DEVNAME(sc)));
1638029d6dd5Spatrick 		return ret;
163932b2494eSpatrick 	}
164032b2494eSpatrick 
164132b2494eSpatrick 	if (buf) {
1642029d6dd5Spatrick 		*len = min(*len, size);
164332b2494eSpatrick 		memcpy(buf, dcmd->buf, *len);
164432b2494eSpatrick 	}
164532b2494eSpatrick 
164632b2494eSpatrick 	if (dcmd->hdr.flags & BWFM_BCDC_DCMD_ERROR)
164732b2494eSpatrick 		ret = dcmd->hdr.status;
164832b2494eSpatrick 	else
164932b2494eSpatrick 		ret = 0;
1650029d6dd5Spatrick 	free(dcmd, M_TEMP, size);
165132b2494eSpatrick 	return ret;
165232b2494eSpatrick }
165332b2494eSpatrick 
165432b2494eSpatrick int
bwfm_proto_bcdc_set_dcmd(struct bwfm_softc * sc,int ifidx,int cmd,char * buf,size_t len)165532b2494eSpatrick bwfm_proto_bcdc_set_dcmd(struct bwfm_softc *sc, int ifidx,
165632b2494eSpatrick     int cmd, char *buf, size_t len)
165732b2494eSpatrick {
165832b2494eSpatrick 	struct bwfm_proto_bcdc_dcmd *dcmd;
165932b2494eSpatrick 	size_t size = sizeof(dcmd->hdr) + len;
1660029d6dd5Spatrick 	int ret = 1, reqid;
166132b2494eSpatrick 
1662029d6dd5Spatrick 	reqid = sc->sc_bcdc_reqid++;
166332b2494eSpatrick 
166432b2494eSpatrick 	if (len > sizeof(dcmd->buf))
1665029d6dd5Spatrick 		return ret;
166632b2494eSpatrick 
166777b542adSpatrick 	dcmd = malloc(size, M_TEMP, M_WAITOK | M_ZERO);
166832b2494eSpatrick 	dcmd->hdr.cmd = htole32(cmd);
166932b2494eSpatrick 	dcmd->hdr.len = htole32(len);
167032b2494eSpatrick 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_SET;
167132b2494eSpatrick 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_ID_SET(reqid);
167232b2494eSpatrick 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_IF_SET(ifidx);
167332b2494eSpatrick 	dcmd->hdr.flags = htole32(dcmd->hdr.flags);
167432b2494eSpatrick 	memcpy(&dcmd->buf, buf, len);
167532b2494eSpatrick 
1676029d6dd5Spatrick 	if (bwfm_proto_bcdc_txctl(sc, reqid, (char *)dcmd, &size)) {
1677029d6dd5Spatrick 		DPRINTF(("%s: txctl failed\n", DEVNAME(sc)));
1678029d6dd5Spatrick 		return ret;
167932b2494eSpatrick 	}
168032b2494eSpatrick 
1681029d6dd5Spatrick 	if (dcmd->hdr.flags & BWFM_BCDC_DCMD_ERROR)
1682029d6dd5Spatrick 		ret = dcmd->hdr.status;
1683029d6dd5Spatrick 	else
1684029d6dd5Spatrick 		ret = 0;
1685029d6dd5Spatrick 	free(dcmd, M_TEMP, size);
1686029d6dd5Spatrick 	return ret;
168732b2494eSpatrick }
1688029d6dd5Spatrick 
1689029d6dd5Spatrick int
bwfm_proto_bcdc_txctl(struct bwfm_softc * sc,int reqid,char * buf,size_t * len)1690029d6dd5Spatrick bwfm_proto_bcdc_txctl(struct bwfm_softc *sc, int reqid, char *buf, size_t *len)
1691029d6dd5Spatrick {
1692029d6dd5Spatrick 	struct bwfm_proto_bcdc_ctl *ctl, *tmp;
1693029d6dd5Spatrick 	int timeout = 0;
1694029d6dd5Spatrick 
1695029d6dd5Spatrick 	ctl = malloc(sizeof(*ctl), M_TEMP, M_WAITOK|M_ZERO);
1696029d6dd5Spatrick 	ctl->reqid = reqid;
1697029d6dd5Spatrick 	ctl->buf = buf;
1698029d6dd5Spatrick 	ctl->len = *len;
1699029d6dd5Spatrick 
17002802c178Spatrick 	if (sc->sc_bus_ops->bs_txctl(sc, ctl)) {
1701029d6dd5Spatrick 		DPRINTF(("%s: tx failed\n", DEVNAME(sc)));
1702029d6dd5Spatrick 		return 1;
1703029d6dd5Spatrick 	}
1704029d6dd5Spatrick 
17058544fed6Smpi 	if (tsleep_nsec(ctl, PWAIT, "bwfm", SEC_TO_NSEC(1)))
1706029d6dd5Spatrick 		timeout = 1;
1707029d6dd5Spatrick 
1708029d6dd5Spatrick 	TAILQ_FOREACH_SAFE(ctl, &sc->sc_bcdc_rxctlq, next, tmp) {
1709029d6dd5Spatrick 		if (ctl->reqid != reqid)
1710029d6dd5Spatrick 			continue;
1711029d6dd5Spatrick 		if (ctl->done) {
1712029d6dd5Spatrick 			TAILQ_REMOVE(&sc->sc_bcdc_rxctlq, ctl, next);
1713029d6dd5Spatrick 			*len = ctl->len;
1714029d6dd5Spatrick 			free(ctl, M_TEMP, sizeof(*ctl));
1715029d6dd5Spatrick 			return 0;
1716029d6dd5Spatrick 		}
1717029d6dd5Spatrick 		if (timeout) {
1718029d6dd5Spatrick 			TAILQ_REMOVE(&sc->sc_bcdc_rxctlq, ctl, next);
1719029d6dd5Spatrick 			DPRINTF(("%s: timeout waiting for txctl response\n",
1720029d6dd5Spatrick 			    DEVNAME(sc)));
1721029d6dd5Spatrick 			free(ctl->buf, M_TEMP, ctl->len);
1722029d6dd5Spatrick 			free(ctl, M_TEMP, sizeof(*ctl));
1723029d6dd5Spatrick 			return 1;
1724029d6dd5Spatrick 		}
1725029d6dd5Spatrick 		break;
1726029d6dd5Spatrick 	}
1727029d6dd5Spatrick 
1728029d6dd5Spatrick 	DPRINTF(("%s: did%s find txctl metadata (timeout %d)\n",
1729029d6dd5Spatrick 	    DEVNAME(sc), ctl == NULL ? " not": "", timeout));
1730029d6dd5Spatrick 	return 1;
1731029d6dd5Spatrick }
1732029d6dd5Spatrick 
1733029d6dd5Spatrick void
bwfm_proto_bcdc_rxctl(struct bwfm_softc * sc,char * buf,size_t len)1734029d6dd5Spatrick bwfm_proto_bcdc_rxctl(struct bwfm_softc *sc, char *buf, size_t len)
1735029d6dd5Spatrick {
1736029d6dd5Spatrick 	struct bwfm_proto_bcdc_dcmd *dcmd;
1737029d6dd5Spatrick 	struct bwfm_proto_bcdc_ctl *ctl, *tmp;
1738029d6dd5Spatrick 
1739029d6dd5Spatrick 	if (len < sizeof(dcmd->hdr))
1740029d6dd5Spatrick 		return;
1741029d6dd5Spatrick 
1742029d6dd5Spatrick 	dcmd = (struct bwfm_proto_bcdc_dcmd *)buf;
174332b2494eSpatrick 	dcmd->hdr.cmd = letoh32(dcmd->hdr.cmd);
174432b2494eSpatrick 	dcmd->hdr.len = letoh32(dcmd->hdr.len);
174532b2494eSpatrick 	dcmd->hdr.flags = letoh32(dcmd->hdr.flags);
174632b2494eSpatrick 	dcmd->hdr.status = letoh32(dcmd->hdr.status);
174732b2494eSpatrick 
1748029d6dd5Spatrick 	TAILQ_FOREACH_SAFE(ctl, &sc->sc_bcdc_rxctlq, next, tmp) {
1749029d6dd5Spatrick 		if (ctl->reqid != BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags))
1750029d6dd5Spatrick 			continue;
1751029d6dd5Spatrick 		if (ctl->len != len) {
1752029d6dd5Spatrick 			free(ctl->buf, M_TEMP, ctl->len);
1753029d6dd5Spatrick 			free(ctl, M_TEMP, sizeof(*ctl));
1754029d6dd5Spatrick 			return;
175532b2494eSpatrick 		}
1756029d6dd5Spatrick 		memcpy(ctl->buf, buf, len);
1757029d6dd5Spatrick 		ctl->done = 1;
1758029d6dd5Spatrick 		wakeup(ctl);
1759029d6dd5Spatrick 		return;
1760029d6dd5Spatrick 	}
176132b2494eSpatrick }
176232b2494eSpatrick 
17637dfc0b73Spatrick void
bwfm_proto_bcdc_rx(struct bwfm_softc * sc,struct mbuf * m,struct mbuf_list * ml)17646f241297Spatrick bwfm_proto_bcdc_rx(struct bwfm_softc *sc, struct mbuf *m, struct mbuf_list *ml)
17657dfc0b73Spatrick {
17667dfc0b73Spatrick 	struct bwfm_proto_bcdc_hdr *hdr;
17677dfc0b73Spatrick 
17687dfc0b73Spatrick 	hdr = mtod(m, struct bwfm_proto_bcdc_hdr *);
17697dfc0b73Spatrick 	if (m->m_len < sizeof(*hdr)) {
17707dfc0b73Spatrick 		m_freem(m);
17717dfc0b73Spatrick 		return;
17727dfc0b73Spatrick 	}
17737dfc0b73Spatrick 	if (m->m_len < sizeof(*hdr) + (hdr->data_offset << 2)) {
17747dfc0b73Spatrick 		m_freem(m);
17757dfc0b73Spatrick 		return;
17767dfc0b73Spatrick 	}
17777dfc0b73Spatrick 	m_adj(m, sizeof(*hdr) + (hdr->data_offset << 2));
17788878a15eSpatrick 
17796f241297Spatrick 	bwfm_rx(sc, m, ml);
17807dfc0b73Spatrick }
17817dfc0b73Spatrick 
178232b2494eSpatrick /* FW Variable code */
178332b2494eSpatrick int
bwfm_fwvar_cmd_get_data(struct bwfm_softc * sc,int cmd,void * data,size_t len)178432b2494eSpatrick bwfm_fwvar_cmd_get_data(struct bwfm_softc *sc, int cmd, void *data, size_t len)
178532b2494eSpatrick {
178632b2494eSpatrick 	return sc->sc_proto_ops->proto_query_dcmd(sc, 0, cmd, data, &len);
178732b2494eSpatrick }
178832b2494eSpatrick 
178932b2494eSpatrick int
bwfm_fwvar_cmd_set_data(struct bwfm_softc * sc,int cmd,void * data,size_t len)179032b2494eSpatrick bwfm_fwvar_cmd_set_data(struct bwfm_softc *sc, int cmd, void *data, size_t len)
179132b2494eSpatrick {
179232b2494eSpatrick 	return sc->sc_proto_ops->proto_set_dcmd(sc, 0, cmd, data, len);
179332b2494eSpatrick }
179432b2494eSpatrick 
179532b2494eSpatrick int
bwfm_fwvar_cmd_get_int(struct bwfm_softc * sc,int cmd,uint32_t * data)179632b2494eSpatrick bwfm_fwvar_cmd_get_int(struct bwfm_softc *sc, int cmd, uint32_t *data)
179732b2494eSpatrick {
179832b2494eSpatrick 	int ret;
179932b2494eSpatrick 	ret = bwfm_fwvar_cmd_get_data(sc, cmd, data, sizeof(*data));
180032b2494eSpatrick 	*data = letoh32(*data);
180132b2494eSpatrick 	return ret;
180232b2494eSpatrick }
180332b2494eSpatrick 
180432b2494eSpatrick int
bwfm_fwvar_cmd_set_int(struct bwfm_softc * sc,int cmd,uint32_t data)180532b2494eSpatrick bwfm_fwvar_cmd_set_int(struct bwfm_softc *sc, int cmd, uint32_t data)
180632b2494eSpatrick {
180732b2494eSpatrick 	data = htole32(data);
180832b2494eSpatrick 	return bwfm_fwvar_cmd_set_data(sc, cmd, &data, sizeof(data));
180932b2494eSpatrick }
181032b2494eSpatrick 
181132b2494eSpatrick int
bwfm_fwvar_var_get_data(struct bwfm_softc * sc,char * name,void * data,size_t len)181232b2494eSpatrick bwfm_fwvar_var_get_data(struct bwfm_softc *sc, char *name, void *data, size_t len)
181332b2494eSpatrick {
181432b2494eSpatrick 	char *buf;
181532b2494eSpatrick 	int ret;
181632b2494eSpatrick 
181732b2494eSpatrick 	buf = malloc(strlen(name) + 1 + len, M_TEMP, M_WAITOK);
181832b2494eSpatrick 	memcpy(buf, name, strlen(name) + 1);
181932b2494eSpatrick 	memcpy(buf + strlen(name) + 1, data, len);
182032b2494eSpatrick 	ret = bwfm_fwvar_cmd_get_data(sc, BWFM_C_GET_VAR,
182132b2494eSpatrick 	    buf, strlen(name) + 1 + len);
182232b2494eSpatrick 	memcpy(data, buf, len);
182332b2494eSpatrick 	free(buf, M_TEMP, strlen(name) + 1 + len);
182432b2494eSpatrick 	return ret;
182532b2494eSpatrick }
182632b2494eSpatrick 
182732b2494eSpatrick int
bwfm_fwvar_var_set_data(struct bwfm_softc * sc,char * name,void * data,size_t len)182832b2494eSpatrick bwfm_fwvar_var_set_data(struct bwfm_softc *sc, char *name, void *data, size_t len)
182932b2494eSpatrick {
183032b2494eSpatrick 	char *buf;
183132b2494eSpatrick 	int ret;
183232b2494eSpatrick 
183332b2494eSpatrick 	buf = malloc(strlen(name) + 1 + len, M_TEMP, M_WAITOK);
183432b2494eSpatrick 	memcpy(buf, name, strlen(name) + 1);
183532b2494eSpatrick 	memcpy(buf + strlen(name) + 1, data, len);
183632b2494eSpatrick 	ret = bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_VAR,
183732b2494eSpatrick 	    buf, strlen(name) + 1 + len);
183832b2494eSpatrick 	free(buf, M_TEMP, strlen(name) + 1 + len);
183932b2494eSpatrick 	return ret;
184032b2494eSpatrick }
184132b2494eSpatrick 
184232b2494eSpatrick int
bwfm_fwvar_var_get_int(struct bwfm_softc * sc,char * name,uint32_t * data)184332b2494eSpatrick bwfm_fwvar_var_get_int(struct bwfm_softc *sc, char *name, uint32_t *data)
184432b2494eSpatrick {
184532b2494eSpatrick 	int ret;
184632b2494eSpatrick 	ret = bwfm_fwvar_var_get_data(sc, name, data, sizeof(*data));
184732b2494eSpatrick 	*data = letoh32(*data);
184832b2494eSpatrick 	return ret;
184932b2494eSpatrick }
185032b2494eSpatrick 
185132b2494eSpatrick int
bwfm_fwvar_var_set_int(struct bwfm_softc * sc,char * name,uint32_t data)185232b2494eSpatrick bwfm_fwvar_var_set_int(struct bwfm_softc *sc, char *name, uint32_t data)
185332b2494eSpatrick {
185432b2494eSpatrick 	data = htole32(data);
185532b2494eSpatrick 	return bwfm_fwvar_var_set_data(sc, name, &data, sizeof(data));
185632b2494eSpatrick }
185732b2494eSpatrick 
18588b2458cfSpatrick /* Channel parameters */
18598b2458cfSpatrick uint32_t
bwfm_chan2spec(struct bwfm_softc * sc,struct ieee80211_channel * c)186073b11a26Spatrick bwfm_chan2spec(struct bwfm_softc *sc, struct ieee80211_channel *c)
186173b11a26Spatrick {
186273b11a26Spatrick 	if (sc->sc_io_type == BWFM_IO_TYPE_D11N)
186373b11a26Spatrick 		return bwfm_chan2spec_d11n(sc, c);
186473b11a26Spatrick 	else
186573b11a26Spatrick 		return bwfm_chan2spec_d11ac(sc, c);
186673b11a26Spatrick }
186773b11a26Spatrick 
186873b11a26Spatrick uint32_t
bwfm_chan2spec_d11n(struct bwfm_softc * sc,struct ieee80211_channel * c)186973b11a26Spatrick bwfm_chan2spec_d11n(struct bwfm_softc *sc, struct ieee80211_channel *c)
187073b11a26Spatrick {
187173b11a26Spatrick 	uint32_t chanspec;
187273b11a26Spatrick 
187373b11a26Spatrick 	chanspec = ieee80211_mhz2ieee(c->ic_freq, 0) & BWFM_CHANSPEC_CHAN_MASK;
187473b11a26Spatrick 	chanspec |= BWFM_CHANSPEC_D11N_SB_N;
187573b11a26Spatrick 	chanspec |= BWFM_CHANSPEC_D11N_BW_20;
187673b11a26Spatrick 	if (IEEE80211_IS_CHAN_2GHZ(c))
187773b11a26Spatrick 		chanspec |= BWFM_CHANSPEC_D11N_BND_2G;
187873b11a26Spatrick 	if (IEEE80211_IS_CHAN_5GHZ(c))
187973b11a26Spatrick 		chanspec |= BWFM_CHANSPEC_D11N_BND_5G;
188073b11a26Spatrick 
188173b11a26Spatrick 	return chanspec;
188273b11a26Spatrick }
188373b11a26Spatrick 
188473b11a26Spatrick uint32_t
bwfm_chan2spec_d11ac(struct bwfm_softc * sc,struct ieee80211_channel * c)188573b11a26Spatrick bwfm_chan2spec_d11ac(struct bwfm_softc *sc, struct ieee80211_channel *c)
188673b11a26Spatrick {
188773b11a26Spatrick 	uint32_t chanspec;
188873b11a26Spatrick 
188973b11a26Spatrick 	chanspec = ieee80211_mhz2ieee(c->ic_freq, 0) & BWFM_CHANSPEC_CHAN_MASK;
189073b11a26Spatrick 	chanspec |= BWFM_CHANSPEC_D11AC_SB_LLL;
189173b11a26Spatrick 	chanspec |= BWFM_CHANSPEC_D11AC_BW_20;
189273b11a26Spatrick 	if (IEEE80211_IS_CHAN_2GHZ(c))
189373b11a26Spatrick 		chanspec |= BWFM_CHANSPEC_D11AC_BND_2G;
189473b11a26Spatrick 	if (IEEE80211_IS_CHAN_5GHZ(c))
189573b11a26Spatrick 		chanspec |= BWFM_CHANSPEC_D11AC_BND_5G;
189673b11a26Spatrick 
189773b11a26Spatrick 	return chanspec;
189873b11a26Spatrick }
189973b11a26Spatrick 
190073b11a26Spatrick uint32_t
bwfm_spec2chan(struct bwfm_softc * sc,uint32_t chanspec)19018b2458cfSpatrick bwfm_spec2chan(struct bwfm_softc *sc, uint32_t chanspec)
19028b2458cfSpatrick {
19038b2458cfSpatrick 	if (sc->sc_io_type == BWFM_IO_TYPE_D11N)
19048b2458cfSpatrick 		return bwfm_spec2chan_d11n(sc, chanspec);
19058b2458cfSpatrick 	else
19068b2458cfSpatrick 		return bwfm_spec2chan_d11ac(sc, chanspec);
19078b2458cfSpatrick }
19088b2458cfSpatrick 
19098b2458cfSpatrick uint32_t
bwfm_spec2chan_d11n(struct bwfm_softc * sc,uint32_t chanspec)19108b2458cfSpatrick bwfm_spec2chan_d11n(struct bwfm_softc *sc, uint32_t chanspec)
19118b2458cfSpatrick {
19128b2458cfSpatrick 	uint32_t chanidx;
19138b2458cfSpatrick 
19148b2458cfSpatrick 	chanidx = chanspec & BWFM_CHANSPEC_CHAN_MASK;
19158b2458cfSpatrick 
19168b2458cfSpatrick 	switch (chanspec & BWFM_CHANSPEC_D11N_BW_MASK) {
19178b2458cfSpatrick 	case BWFM_CHANSPEC_D11N_BW_40:
19188b2458cfSpatrick 		switch (chanspec & BWFM_CHANSPEC_D11N_SB_MASK) {
19198b2458cfSpatrick 		case BWFM_CHANSPEC_D11N_SB_L:
19208b2458cfSpatrick 			chanidx -= 2;
19218b2458cfSpatrick 			break;
19228b2458cfSpatrick 		case BWFM_CHANSPEC_D11N_SB_U:
19238b2458cfSpatrick 			chanidx += 2;
19248b2458cfSpatrick 			break;
19258b2458cfSpatrick 		default:
19268b2458cfSpatrick 			break;
19278b2458cfSpatrick 		}
19288b2458cfSpatrick 		break;
19298b2458cfSpatrick 	default:
19308b2458cfSpatrick 		break;
19318b2458cfSpatrick 	}
19328b2458cfSpatrick 
19338b2458cfSpatrick 	return chanidx;
19348b2458cfSpatrick }
19358b2458cfSpatrick 
19368b2458cfSpatrick uint32_t
bwfm_spec2chan_d11ac(struct bwfm_softc * sc,uint32_t chanspec)19378b2458cfSpatrick bwfm_spec2chan_d11ac(struct bwfm_softc *sc, uint32_t chanspec)
19388b2458cfSpatrick {
19398b2458cfSpatrick 	uint32_t chanidx;
19408b2458cfSpatrick 
19418b2458cfSpatrick 	chanidx = chanspec & BWFM_CHANSPEC_CHAN_MASK;
19428b2458cfSpatrick 
19438b2458cfSpatrick 	switch (chanspec & BWFM_CHANSPEC_D11AC_BW_MASK) {
19448b2458cfSpatrick 	case BWFM_CHANSPEC_D11AC_BW_40:
19458b2458cfSpatrick 		switch (chanspec & BWFM_CHANSPEC_D11AC_SB_MASK) {
19468b2458cfSpatrick 		case BWFM_CHANSPEC_D11AC_SB_LLL:
19478b2458cfSpatrick 			chanidx -= 2;
19488b2458cfSpatrick 			break;
19498b2458cfSpatrick 		case BWFM_CHANSPEC_D11AC_SB_LLU:
19508b2458cfSpatrick 			chanidx += 2;
19518b2458cfSpatrick 			break;
19528b2458cfSpatrick 		default:
19538b2458cfSpatrick 			break;
19548b2458cfSpatrick 		}
19558b2458cfSpatrick 		break;
19568b2458cfSpatrick 	case BWFM_CHANSPEC_D11AC_BW_80:
19578b2458cfSpatrick 		switch (chanspec & BWFM_CHANSPEC_D11AC_SB_MASK) {
19588b2458cfSpatrick 		case BWFM_CHANSPEC_D11AC_SB_LLL:
19598b2458cfSpatrick 			chanidx -= 6;
19608b2458cfSpatrick 			break;
19618b2458cfSpatrick 		case BWFM_CHANSPEC_D11AC_SB_LLU:
19628b2458cfSpatrick 			chanidx -= 2;
19638b2458cfSpatrick 			break;
19648b2458cfSpatrick 		case BWFM_CHANSPEC_D11AC_SB_LUL:
19658b2458cfSpatrick 			chanidx += 2;
19668b2458cfSpatrick 			break;
19678b2458cfSpatrick 		case BWFM_CHANSPEC_D11AC_SB_LUU:
19688b2458cfSpatrick 			chanidx += 6;
19698b2458cfSpatrick 			break;
19708b2458cfSpatrick 		default:
19718b2458cfSpatrick 			break;
19728b2458cfSpatrick 		}
19738b2458cfSpatrick 		break;
19748b2458cfSpatrick 	default:
19758b2458cfSpatrick 		break;
19768b2458cfSpatrick 	}
19778b2458cfSpatrick 
19788b2458cfSpatrick 	return chanidx;
19798b2458cfSpatrick }
19808b2458cfSpatrick 
198132b2494eSpatrick /* 802.11 code */
198232b2494eSpatrick void
bwfm_connect(struct bwfm_softc * sc)198363498aa8Spatrick bwfm_connect(struct bwfm_softc *sc)
198432b2494eSpatrick {
198532b2494eSpatrick 	struct ieee80211com *ic = &sc->sc_ic;
198663498aa8Spatrick 	struct bwfm_ext_join_params *params;
198763498aa8Spatrick 	uint8_t buf[64];	/* XXX max WPA/RSN/WMM IE length */
198863498aa8Spatrick 	uint8_t *frm;
198963498aa8Spatrick 
199063498aa8Spatrick 	/*
1991c8e9388cSstsp 	 * OPEN: Open or WEP or WPA/WPA2 on newer Chips/Firmware.
199263498aa8Spatrick 	 * AUTO: Automatic, probably for older Chips/Firmware.
199363498aa8Spatrick 	 */
199463498aa8Spatrick 	if (ic->ic_flags & IEEE80211_F_RSNON) {
199563498aa8Spatrick 		uint32_t wsec = 0;
199663498aa8Spatrick 		uint32_t wpa = 0;
199763498aa8Spatrick 
199863498aa8Spatrick 		/* tell firmware to add WPA/RSN IE to (re)assoc request */
199963498aa8Spatrick 		if (ic->ic_bss->ni_rsnprotos == IEEE80211_PROTO_RSN)
200063498aa8Spatrick 			frm = ieee80211_add_rsn(buf, ic, ic->ic_bss);
200163498aa8Spatrick 		else
200263498aa8Spatrick 			frm = ieee80211_add_wpa(buf, ic, ic->ic_bss);
200363498aa8Spatrick 		bwfm_fwvar_var_set_data(sc, "wpaie", buf, frm - buf);
200463498aa8Spatrick 
200563498aa8Spatrick 		if (ic->ic_rsnprotos & IEEE80211_PROTO_WPA) {
200663498aa8Spatrick 			if (ic->ic_rsnakms & IEEE80211_AKM_PSK)
200763498aa8Spatrick 				wpa |= BWFM_WPA_AUTH_WPA_PSK;
200863498aa8Spatrick 			if (ic->ic_rsnakms & IEEE80211_AKM_8021X)
200963498aa8Spatrick 				wpa |= BWFM_WPA_AUTH_WPA_UNSPECIFIED;
201063498aa8Spatrick 		}
201163498aa8Spatrick 		if (ic->ic_rsnprotos & IEEE80211_PROTO_RSN) {
201263498aa8Spatrick 			if (ic->ic_rsnakms & IEEE80211_AKM_PSK)
201363498aa8Spatrick 				wpa |= BWFM_WPA_AUTH_WPA2_PSK;
201463498aa8Spatrick 			if (ic->ic_rsnakms & IEEE80211_AKM_SHA256_PSK)
201563498aa8Spatrick 				wpa |= BWFM_WPA_AUTH_WPA2_PSK_SHA256;
201663498aa8Spatrick 			if (ic->ic_rsnakms & IEEE80211_AKM_8021X)
201763498aa8Spatrick 				wpa |= BWFM_WPA_AUTH_WPA2_UNSPECIFIED;
201863498aa8Spatrick 			if (ic->ic_rsnakms & IEEE80211_AKM_SHA256_8021X)
201963498aa8Spatrick 				wpa |= BWFM_WPA_AUTH_WPA2_1X_SHA256;
202063498aa8Spatrick 		}
202163498aa8Spatrick 		if (ic->ic_rsnciphers & IEEE80211_WPA_CIPHER_TKIP ||
202263498aa8Spatrick 		    ic->ic_rsngroupcipher & IEEE80211_WPA_CIPHER_TKIP)
202363498aa8Spatrick 			wsec |= BWFM_WSEC_TKIP;
202463498aa8Spatrick 		if (ic->ic_rsnciphers & IEEE80211_WPA_CIPHER_CCMP ||
202563498aa8Spatrick 		    ic->ic_rsngroupcipher & IEEE80211_WPA_CIPHER_CCMP)
202663498aa8Spatrick 			wsec |= BWFM_WSEC_AES;
202763498aa8Spatrick 
202863498aa8Spatrick 		bwfm_fwvar_var_set_int(sc, "wpa_auth", wpa);
202963498aa8Spatrick 		bwfm_fwvar_var_set_int(sc, "wsec", wsec);
2030c8e9388cSstsp 	} else if (ic->ic_flags & IEEE80211_F_WEPON) {
2031c8e9388cSstsp 		bwfm_fwvar_var_set_int(sc, "wpa_auth", BWFM_WPA_AUTH_DISABLED);
2032c8e9388cSstsp 		bwfm_fwvar_var_set_int(sc, "wsec", BWFM_WSEC_WEP);
203363498aa8Spatrick 	} else {
203463498aa8Spatrick 		bwfm_fwvar_var_set_int(sc, "wpa_auth", BWFM_WPA_AUTH_DISABLED);
203563498aa8Spatrick 		bwfm_fwvar_var_set_int(sc, "wsec", BWFM_WSEC_NONE);
203663498aa8Spatrick 	}
203763498aa8Spatrick 	bwfm_fwvar_var_set_int(sc, "auth", BWFM_AUTH_OPEN);
203863498aa8Spatrick 	bwfm_fwvar_var_set_int(sc, "mfp", BWFM_MFP_NONE);
203963498aa8Spatrick 
20404ba67829Sstsp 	if (ic->ic_des_esslen && ic->ic_des_esslen <= BWFM_MAX_SSID_LEN) {
204163498aa8Spatrick 		params = malloc(sizeof(*params), M_TEMP, M_WAITOK | M_ZERO);
204263498aa8Spatrick 		memcpy(params->ssid.ssid, ic->ic_des_essid, ic->ic_des_esslen);
204363498aa8Spatrick 		params->ssid.len = htole32(ic->ic_des_esslen);
204402662318Spatrick 		memcpy(params->assoc.bssid, ic->ic_bss->ni_bssid,
204502662318Spatrick 		    sizeof(params->assoc.bssid));
204663498aa8Spatrick 		params->scan.scan_type = -1;
204763498aa8Spatrick 		params->scan.nprobes = htole32(-1);
204863498aa8Spatrick 		params->scan.active_time = htole32(-1);
204963498aa8Spatrick 		params->scan.passive_time = htole32(-1);
205063498aa8Spatrick 		params->scan.home_time = htole32(-1);
205163498aa8Spatrick 		if (bwfm_fwvar_var_set_data(sc, "join", params, sizeof(*params))) {
205263498aa8Spatrick 			struct bwfm_join_params join;
205363498aa8Spatrick 			memset(&join, 0, sizeof(join));
205402662318Spatrick 			memcpy(join.ssid.ssid, ic->ic_des_essid,
205502662318Spatrick 			    ic->ic_des_esslen);
205663498aa8Spatrick 			join.ssid.len = htole32(ic->ic_des_esslen);
205702662318Spatrick 			memcpy(join.assoc.bssid, ic->ic_bss->ni_bssid,
205802662318Spatrick 			    sizeof(join.assoc.bssid));
205963498aa8Spatrick 			bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_SSID, &join,
206063498aa8Spatrick 			    sizeof(join));
206163498aa8Spatrick 		}
206263498aa8Spatrick 		free(params, M_TEMP, sizeof(*params));
206363498aa8Spatrick 	}
206463498aa8Spatrick }
206563498aa8Spatrick 
2066c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
2067c11618f6Spatrick void
bwfm_hostap(struct bwfm_softc * sc)2068c11618f6Spatrick bwfm_hostap(struct bwfm_softc *sc)
2069c11618f6Spatrick {
2070c11618f6Spatrick 	struct ieee80211com *ic = &sc->sc_ic;
2071c11618f6Spatrick 	struct ieee80211_node *ni = ic->ic_bss;
2072c11618f6Spatrick 	struct bwfm_join_params join;
2073c11618f6Spatrick 
2074c11618f6Spatrick 	/*
2075c8e9388cSstsp 	 * OPEN: Open or WEP or WPA/WPA2 on newer Chips/Firmware.
2076c11618f6Spatrick 	 * AUTO: Automatic, probably for older Chips/Firmware.
2077c11618f6Spatrick 	 */
2078c11618f6Spatrick 	if (ic->ic_flags & IEEE80211_F_RSNON) {
2079c11618f6Spatrick 		uint32_t wsec = 0;
2080c11618f6Spatrick 		uint32_t wpa = 0;
2081c11618f6Spatrick 
2082c11618f6Spatrick 		/* TODO: Turn off if replay counter set */
2083c11618f6Spatrick 		if (ni->ni_rsnprotos & IEEE80211_PROTO_RSN)
2084c11618f6Spatrick 			bwfm_fwvar_var_set_int(sc, "wme_bss_disable", 1);
2085c11618f6Spatrick 
2086c11618f6Spatrick 		if (ni->ni_rsnprotos & IEEE80211_PROTO_WPA) {
2087c11618f6Spatrick 			if (ni->ni_rsnakms & IEEE80211_AKM_PSK)
2088c11618f6Spatrick 				wpa |= BWFM_WPA_AUTH_WPA_PSK;
2089c11618f6Spatrick 			if (ni->ni_rsnakms & IEEE80211_AKM_8021X)
2090c11618f6Spatrick 				wpa |= BWFM_WPA_AUTH_WPA_UNSPECIFIED;
2091c11618f6Spatrick 		}
2092c11618f6Spatrick 		if (ni->ni_rsnprotos & IEEE80211_PROTO_RSN) {
2093c11618f6Spatrick 			if (ni->ni_rsnakms & IEEE80211_AKM_PSK)
2094c11618f6Spatrick 				wpa |= BWFM_WPA_AUTH_WPA2_PSK;
2095c11618f6Spatrick 			if (ni->ni_rsnakms & IEEE80211_AKM_SHA256_PSK)
2096c11618f6Spatrick 				wpa |= BWFM_WPA_AUTH_WPA2_PSK_SHA256;
2097c11618f6Spatrick 			if (ni->ni_rsnakms & IEEE80211_AKM_8021X)
2098c11618f6Spatrick 				wpa |= BWFM_WPA_AUTH_WPA2_UNSPECIFIED;
2099c11618f6Spatrick 			if (ni->ni_rsnakms & IEEE80211_AKM_SHA256_8021X)
2100c11618f6Spatrick 				wpa |= BWFM_WPA_AUTH_WPA2_1X_SHA256;
2101c11618f6Spatrick 		}
2102c11618f6Spatrick 		if (ni->ni_rsnciphers & IEEE80211_WPA_CIPHER_TKIP ||
2103c11618f6Spatrick 		    ni->ni_rsngroupcipher & IEEE80211_WPA_CIPHER_TKIP)
2104c11618f6Spatrick 			wsec |= BWFM_WSEC_TKIP;
2105c11618f6Spatrick 		if (ni->ni_rsnciphers & IEEE80211_WPA_CIPHER_CCMP ||
2106c11618f6Spatrick 		    ni->ni_rsngroupcipher & IEEE80211_WPA_CIPHER_CCMP)
2107c11618f6Spatrick 			wsec |= BWFM_WSEC_AES;
2108c11618f6Spatrick 
2109c11618f6Spatrick 		bwfm_fwvar_var_set_int(sc, "wpa_auth", wpa);
2110c11618f6Spatrick 		bwfm_fwvar_var_set_int(sc, "wsec", wsec);
2111c11618f6Spatrick 	} else {
2112c11618f6Spatrick 		bwfm_fwvar_var_set_int(sc, "wpa_auth", BWFM_WPA_AUTH_DISABLED);
2113c11618f6Spatrick 		bwfm_fwvar_var_set_int(sc, "wsec", BWFM_WSEC_NONE);
2114c11618f6Spatrick 	}
2115c11618f6Spatrick 	bwfm_fwvar_var_set_int(sc, "auth", BWFM_AUTH_OPEN);
2116c11618f6Spatrick 	bwfm_fwvar_var_set_int(sc, "mfp", BWFM_MFP_NONE);
2117c11618f6Spatrick 
21181b8f8c22Stobhe 	bwfm_fwvar_var_set_int(sc, "mpc", 0);
2119c11618f6Spatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_INFRA, 1);
2120c11618f6Spatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_AP, 1);
212173b11a26Spatrick 	bwfm_fwvar_var_set_int(sc, "chanspec",
212273b11a26Spatrick 	    bwfm_chan2spec(sc, ic->ic_bss->ni_chan));
2123c11618f6Spatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_UP, 1);
2124c11618f6Spatrick 
2125c11618f6Spatrick 	memset(&join, 0, sizeof(join));
2126c11618f6Spatrick 	memcpy(join.ssid.ssid, ic->ic_des_essid, ic->ic_des_esslen);
2127c11618f6Spatrick 	join.ssid.len = htole32(ic->ic_des_esslen);
2128c11618f6Spatrick 	memset(join.assoc.bssid, 0xff, sizeof(join.assoc.bssid));
2129c11618f6Spatrick 	bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_SSID, &join, sizeof(join));
2130c11618f6Spatrick 	bwfm_fwvar_var_set_int(sc, "closednet",
2131eb9f82c0Sstsp 	    (ic->ic_userflags & IEEE80211_F_HIDENWID) != 0);
2132c11618f6Spatrick }
2133c11618f6Spatrick #endif
2134c11618f6Spatrick 
213563498aa8Spatrick void
bwfm_scan_v0(struct bwfm_softc * sc)21369b183917Skettenis bwfm_scan_v0(struct bwfm_softc *sc)
213763498aa8Spatrick {
213836cbf554Spatrick 	struct ieee80211com *ic = &sc->sc_ic;
21399b183917Skettenis 	struct bwfm_escan_params_v0 *params;
214036cbf554Spatrick 	uint32_t nssid = 0, nchan = 0;
214136cbf554Spatrick 	size_t params_size, chan_size, ssid_size;
214236cbf554Spatrick 	struct bwfm_ssid *ssid;
214332b2494eSpatrick 
214436cbf554Spatrick 	if (ic->ic_flags & IEEE80211_F_ASCAN &&
21454ba67829Sstsp 	    ic->ic_des_esslen && ic->ic_des_esslen <= BWFM_MAX_SSID_LEN)
214636cbf554Spatrick 		nssid = 1;
214736cbf554Spatrick 
214836cbf554Spatrick 	chan_size = roundup(nchan * sizeof(uint16_t), sizeof(uint32_t));
214936cbf554Spatrick 	ssid_size = sizeof(struct bwfm_ssid) * nssid;
215036cbf554Spatrick 	params_size = sizeof(*params) + chan_size + ssid_size;
215132b2494eSpatrick 
215232b2494eSpatrick 	params = malloc(params_size, M_TEMP, M_WAITOK | M_ZERO);
215336cbf554Spatrick 	ssid = (struct bwfm_ssid *)
215436cbf554Spatrick 	    (((uint8_t *)params) + sizeof(*params) + chan_size);
215536cbf554Spatrick 
215632b2494eSpatrick 	memset(params->scan_params.bssid, 0xff,
215732b2494eSpatrick 	    sizeof(params->scan_params.bssid));
215832b2494eSpatrick 	params->scan_params.bss_type = 2;
21591d29b516Spatrick 	params->scan_params.scan_type = BWFM_SCANTYPE_PASSIVE;
216032b2494eSpatrick 	params->scan_params.nprobes = htole32(-1);
216132b2494eSpatrick 	params->scan_params.active_time = htole32(-1);
216232b2494eSpatrick 	params->scan_params.passive_time = htole32(-1);
216332b2494eSpatrick 	params->scan_params.home_time = htole32(-1);
216432b2494eSpatrick 	params->version = htole32(BWFM_ESCAN_REQ_VERSION);
216532b2494eSpatrick 	params->action = htole16(WL_ESCAN_ACTION_START);
216632b2494eSpatrick 	params->sync_id = htole16(0x1234);
216732b2494eSpatrick 
216836cbf554Spatrick 	if (ic->ic_flags & IEEE80211_F_ASCAN &&
21694ba67829Sstsp 	    ic->ic_des_esslen && ic->ic_des_esslen <= BWFM_MAX_SSID_LEN) {
217036cbf554Spatrick 		params->scan_params.scan_type = BWFM_SCANTYPE_ACTIVE;
217136cbf554Spatrick 		ssid->len = htole32(ic->ic_des_esslen);
217236cbf554Spatrick 		memcpy(ssid->ssid, ic->ic_des_essid, ic->ic_des_esslen);
217336cbf554Spatrick 	}
217436cbf554Spatrick 
217536cbf554Spatrick 	params->scan_params.channel_num = htole32(
217636cbf554Spatrick 	    nssid << BWFM_CHANNUM_NSSID_SHIFT |
217736cbf554Spatrick 	    nchan << BWFM_CHANNUM_NCHAN_SHIFT);
217836cbf554Spatrick 
217932b2494eSpatrick #if 0
218032b2494eSpatrick 	/* Scan a specific channel */
218132b2494eSpatrick 	params->scan_params.channel_list[0] = htole16(
218232b2494eSpatrick 	    (1 & 0xff) << 0 |
218332b2494eSpatrick 	    (3 & 0x3) << 8 |
218432b2494eSpatrick 	    (2 & 0x3) << 10 |
218532b2494eSpatrick 	    (2 & 0x3) << 12
218632b2494eSpatrick 	    );
218732b2494eSpatrick #endif
218832b2494eSpatrick 
218932b2494eSpatrick 	bwfm_fwvar_var_set_data(sc, "escan", params, params_size);
219032b2494eSpatrick 	free(params, M_TEMP, params_size);
219132b2494eSpatrick }
219232b2494eSpatrick 
219325440f89Spatrick void
bwfm_scan_v2(struct bwfm_softc * sc)21949b183917Skettenis bwfm_scan_v2(struct bwfm_softc *sc)
219525440f89Spatrick {
21969b183917Skettenis 	struct ieee80211com *ic = &sc->sc_ic;
21979b183917Skettenis 	struct bwfm_escan_params_v2 *params;
21989b183917Skettenis 	uint32_t nssid = 0, nchan = 0;
21999b183917Skettenis 	size_t params_size, chan_size, ssid_size;
22009b183917Skettenis 	struct bwfm_ssid *ssid;
22019b183917Skettenis 
22029b183917Skettenis 	if (ic->ic_flags & IEEE80211_F_ASCAN &&
22039b183917Skettenis 	    ic->ic_des_esslen && ic->ic_des_esslen <= BWFM_MAX_SSID_LEN)
22049b183917Skettenis 		nssid = 1;
22059b183917Skettenis 
22069b183917Skettenis 	chan_size = roundup(nchan * sizeof(uint16_t), sizeof(uint32_t));
22079b183917Skettenis 	ssid_size = sizeof(struct bwfm_ssid) * nssid;
22089b183917Skettenis 	params_size = sizeof(*params) + chan_size + ssid_size;
22099b183917Skettenis 
22109b183917Skettenis 	params = malloc(params_size, M_TEMP, M_WAITOK | M_ZERO);
22119b183917Skettenis 	ssid = (struct bwfm_ssid *)
22129b183917Skettenis 	    (((uint8_t *)params) + sizeof(*params) + chan_size);
22139b183917Skettenis 
22149b183917Skettenis 	params->scan_params.version = 2;
22159b183917Skettenis 	params->scan_params.length = params_size;
22169b183917Skettenis 	memset(params->scan_params.bssid, 0xff,
22179b183917Skettenis 	    sizeof(params->scan_params.bssid));
22189b183917Skettenis 	params->scan_params.bss_type = 2;
22199b183917Skettenis 	params->scan_params.scan_type = BWFM_SCANTYPE_PASSIVE;
22209b183917Skettenis 	params->scan_params.nprobes = htole32(-1);
22219b183917Skettenis 	params->scan_params.active_time = htole32(-1);
22229b183917Skettenis 	params->scan_params.passive_time = htole32(-1);
22239b183917Skettenis 	params->scan_params.home_time = htole32(-1);
22249b183917Skettenis 	params->version = htole32(BWFM_ESCAN_REQ_VERSION_V2);
22259b183917Skettenis 	params->action = htole16(WL_ESCAN_ACTION_START);
22269b183917Skettenis 	params->sync_id = htole16(0x1234);
22279b183917Skettenis 
22289b183917Skettenis 	if (ic->ic_flags & IEEE80211_F_ASCAN &&
22299b183917Skettenis 	    ic->ic_des_esslen && ic->ic_des_esslen <= BWFM_MAX_SSID_LEN) {
22309b183917Skettenis 		params->scan_params.scan_type = BWFM_SCANTYPE_ACTIVE;
22319b183917Skettenis 		ssid->len = htole32(ic->ic_des_esslen);
22329b183917Skettenis 		memcpy(ssid->ssid, ic->ic_des_essid, ic->ic_des_esslen);
22339b183917Skettenis 	}
22349b183917Skettenis 
22359b183917Skettenis 	params->scan_params.channel_num = htole32(
22369b183917Skettenis 	    nssid << BWFM_CHANNUM_NSSID_SHIFT |
22379b183917Skettenis 	    nchan << BWFM_CHANNUM_NCHAN_SHIFT);
22389b183917Skettenis 
22399b183917Skettenis #if 0
22409b183917Skettenis 	/* Scan a specific channel */
22419b183917Skettenis 	params->scan_params.channel_list[0] = htole16(
22429b183917Skettenis 	    (1 & 0xff) << 0 |
22439b183917Skettenis 	    (3 & 0x3) << 8 |
22449b183917Skettenis 	    (2 & 0x3) << 10 |
22459b183917Skettenis 	    (2 & 0x3) << 12
22469b183917Skettenis 	    );
22479b183917Skettenis #endif
22489b183917Skettenis 
22499b183917Skettenis 	bwfm_fwvar_var_set_data(sc, "escan", params, params_size);
22509b183917Skettenis 	free(params, M_TEMP, params_size);
22519b183917Skettenis }
22529b183917Skettenis 
22539b183917Skettenis void
bwfm_scan(struct bwfm_softc * sc)22549b183917Skettenis bwfm_scan(struct bwfm_softc *sc)
22559b183917Skettenis {
22569b183917Skettenis 	if (sc->sc_scan_ver == 0)
22579b183917Skettenis 		bwfm_scan_v0(sc);
22589b183917Skettenis 	else
22599b183917Skettenis 		bwfm_scan_v2(sc);
22609b183917Skettenis }
22619b183917Skettenis 
22629b183917Skettenis void
bwfm_scan_abort_v0(struct bwfm_softc * sc)22639b183917Skettenis bwfm_scan_abort_v0(struct bwfm_softc *sc)
22649b183917Skettenis {
22659b183917Skettenis 	struct bwfm_escan_params_v0 *params;
226625440f89Spatrick 	size_t params_size;
226725440f89Spatrick 
226825440f89Spatrick 	params_size = sizeof(*params) + sizeof(uint16_t);
226925440f89Spatrick 	params = malloc(params_size, M_TEMP, M_WAITOK | M_ZERO);
227025440f89Spatrick 	memset(params->scan_params.bssid, 0xff,
227125440f89Spatrick 	    sizeof(params->scan_params.bssid));
227225440f89Spatrick 	params->scan_params.bss_type = 2;
227325440f89Spatrick 	params->scan_params.scan_type = BWFM_SCANTYPE_PASSIVE;
227425440f89Spatrick 	params->scan_params.nprobes = htole32(-1);
227525440f89Spatrick 	params->scan_params.active_time = htole32(-1);
227625440f89Spatrick 	params->scan_params.passive_time = htole32(-1);
227725440f89Spatrick 	params->scan_params.home_time = htole32(-1);
227825440f89Spatrick 	params->version = htole32(BWFM_ESCAN_REQ_VERSION);
227925440f89Spatrick 	params->action = htole16(WL_ESCAN_ACTION_START);
228025440f89Spatrick 	params->sync_id = htole16(0x1234);
228125440f89Spatrick 	params->scan_params.channel_num = htole32(1);
228225440f89Spatrick 	params->scan_params.channel_list[0] = htole16(-1);
228325440f89Spatrick 	bwfm_fwvar_var_set_data(sc, "escan", params, params_size);
228425440f89Spatrick 	free(params, M_TEMP, params_size);
228525440f89Spatrick }
228625440f89Spatrick 
22879b183917Skettenis void
bwfm_scan_abort_v2(struct bwfm_softc * sc)22889b183917Skettenis bwfm_scan_abort_v2(struct bwfm_softc *sc)
22899b183917Skettenis {
22909b183917Skettenis 	struct bwfm_escan_params_v2 *params;
22919b183917Skettenis 	size_t params_size;
22929b183917Skettenis 
22939b183917Skettenis 	params_size = sizeof(*params) + sizeof(uint16_t);
22949b183917Skettenis 	params = malloc(params_size, M_TEMP, M_WAITOK | M_ZERO);
22959b183917Skettenis 	params->scan_params.version = 2;
22969b183917Skettenis 	params->scan_params.length = params_size;
22979b183917Skettenis 	memset(params->scan_params.bssid, 0xff,
22989b183917Skettenis 	    sizeof(params->scan_params.bssid));
22999b183917Skettenis 	params->scan_params.bss_type = 2;
23009b183917Skettenis 	params->scan_params.scan_type = BWFM_SCANTYPE_PASSIVE;
23019b183917Skettenis 	params->scan_params.nprobes = htole32(-1);
23029b183917Skettenis 	params->scan_params.active_time = htole32(-1);
23039b183917Skettenis 	params->scan_params.passive_time = htole32(-1);
23049b183917Skettenis 	params->scan_params.home_time = htole32(-1);
23059b183917Skettenis 	params->version = htole32(BWFM_ESCAN_REQ_VERSION_V2);
23069b183917Skettenis 	params->action = htole16(WL_ESCAN_ACTION_START);
23079b183917Skettenis 	params->sync_id = htole16(0x1234);
23089b183917Skettenis 	params->scan_params.channel_num = htole32(1);
23099b183917Skettenis 	params->scan_params.channel_list[0] = htole16(-1);
23109b183917Skettenis 	bwfm_fwvar_var_set_data(sc, "escan", params, params_size);
23119b183917Skettenis 	free(params, M_TEMP, params_size);
23129b183917Skettenis }
23139b183917Skettenis 
23149b183917Skettenis void
bwfm_scan_abort(struct bwfm_softc * sc)23159b183917Skettenis bwfm_scan_abort(struct bwfm_softc *sc)
23169b183917Skettenis {
23179b183917Skettenis 	if (sc->sc_scan_ver == 0)
23189b183917Skettenis 		bwfm_scan_abort_v0(sc);
23199b183917Skettenis 	else
23209b183917Skettenis 		bwfm_scan_abort_v2(sc);
23219b183917Skettenis }
23229b183917Skettenis 
2323c11618f6Spatrick struct mbuf *
bwfm_newbuf(void)2324c11618f6Spatrick bwfm_newbuf(void)
2325c11618f6Spatrick {
2326c11618f6Spatrick 	struct mbuf *m;
2327c11618f6Spatrick 
2328c11618f6Spatrick 	MGETHDR(m, M_DONTWAIT, MT_DATA);
2329c11618f6Spatrick 	if (m == NULL)
2330c11618f6Spatrick 		return (NULL);
2331c11618f6Spatrick 
2332c11618f6Spatrick 	MCLGET(m, M_DONTWAIT);
2333c11618f6Spatrick 	if (!(m->m_flags & M_EXT)) {
2334c11618f6Spatrick 		m_freem(m);
2335c11618f6Spatrick 		return (NULL);
2336c11618f6Spatrick 	}
2337c11618f6Spatrick 
2338c11618f6Spatrick 	m->m_len = m->m_pkthdr.len = MCLBYTES;
2339c11618f6Spatrick 
2340c11618f6Spatrick 	return (m);
2341c11618f6Spatrick }
2342c11618f6Spatrick 
234332b2494eSpatrick void
bwfm_rx(struct bwfm_softc * sc,struct mbuf * m,struct mbuf_list * ml)23446f241297Spatrick bwfm_rx(struct bwfm_softc *sc, struct mbuf *m, struct mbuf_list *ml)
234532b2494eSpatrick {
234632b2494eSpatrick 	struct ieee80211com *ic = &sc->sc_ic;
234732b2494eSpatrick 	struct ifnet *ifp = &ic->ic_if;
2348c11618f6Spatrick 	struct ieee80211_node *ni;
2349b0079d05Spatrick 	struct bwfm_event *e;
235032b2494eSpatrick 
23519614a7fdSpatrick #ifdef __STRICT_ALIGNMENT
23529614a7fdSpatrick 	/* Remaining data is an ethernet packet, so align. */
23539614a7fdSpatrick 	if ((mtod(m, paddr_t) & 0x3) != ETHER_ALIGN) {
23549614a7fdSpatrick 		struct mbuf *m0;
23559614a7fdSpatrick 		m0 = m_dup_pkt(m, ETHER_ALIGN, M_WAITOK);
23569614a7fdSpatrick 		m_freem(m);
23579614a7fdSpatrick 		if (m0 == NULL) {
23589614a7fdSpatrick 			ifp->if_ierrors++;
23599614a7fdSpatrick 			return;
23609614a7fdSpatrick 		}
23619614a7fdSpatrick 		m = m0;
23629614a7fdSpatrick 	}
23639614a7fdSpatrick #endif
23649614a7fdSpatrick 
2365b0079d05Spatrick 	e = mtod(m, struct bwfm_event *);
2366f37fc236Spatrick 	if (m->m_len >= sizeof(e->ehdr) &&
236732b2494eSpatrick 	    ntohs(e->ehdr.ether_type) == BWFM_ETHERTYPE_LINK_CTL &&
236832b2494eSpatrick 	    memcmp(BWFM_BRCM_OUI, e->hdr.oui, sizeof(e->hdr.oui)) == 0 &&
2369010fb2f1Spatrick 	    ntohs(e->hdr.usr_subtype) == BWFM_BRCM_SUBTYPE_EVENT) {
2370f4e8af02Spatrick 		bwfm_rx_event(sc, m);
2371010fb2f1Spatrick 		return;
2372010fb2f1Spatrick 	}
2373010fb2f1Spatrick 
2374010fb2f1Spatrick 	/* Drop network packets if we are not in RUN state. */
2375010fb2f1Spatrick 	if (ic->ic_state != IEEE80211_S_RUN) {
2376010fb2f1Spatrick 		m_freem(m);
2377010fb2f1Spatrick 		return;
2378010fb2f1Spatrick 	}
237932b2494eSpatrick 
238063498aa8Spatrick 	if ((ic->ic_flags & IEEE80211_F_RSNON) &&
2381f37fc236Spatrick 	    m->m_len >= sizeof(e->ehdr) &&
238283aa0ba6Sdlg 	    ntohs(e->ehdr.ether_type) == ETHERTYPE_EAPOL) {
238363498aa8Spatrick 		ifp->if_ipackets++;
238463498aa8Spatrick #if NBPFILTER > 0
238563498aa8Spatrick 		if (ifp->if_bpf)
238663498aa8Spatrick 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
238763498aa8Spatrick #endif
2388c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
2389c11618f6Spatrick 		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
2390c11618f6Spatrick 			ni = ieee80211_find_node(ic,
2391c11618f6Spatrick 			    (void *)&e->ehdr.ether_shost);
2392c11618f6Spatrick 			if (ni == NULL) {
2393cb2afc74Spatrick 				m_freem(m);
2394c11618f6Spatrick 				return;
2395c11618f6Spatrick 			}
2396c11618f6Spatrick 		} else
2397c11618f6Spatrick #endif
2398c11618f6Spatrick 			ni = ic->ic_bss;
2399c11618f6Spatrick 		ieee80211_eapol_key_input(ic, m, ni);
24006f241297Spatrick 	} else
24016f241297Spatrick 		ml_enqueue(ml, m);
240263498aa8Spatrick }
240332b2494eSpatrick 
2404c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
2405c11618f6Spatrick void
bwfm_rx_auth_ind(struct bwfm_softc * sc,struct bwfm_event * e,size_t len)2406c11618f6Spatrick bwfm_rx_auth_ind(struct bwfm_softc *sc, struct bwfm_event *e, size_t len)
2407c11618f6Spatrick {
2408c11618f6Spatrick 	struct ieee80211com *ic = &sc->sc_ic;
2409c11618f6Spatrick 	struct ifnet *ifp = &ic->ic_if;
2410c11618f6Spatrick 	struct ieee80211_rxinfo rxi;
2411c11618f6Spatrick 	struct ieee80211_frame *wh;
2412c11618f6Spatrick 	struct mbuf *m;
2413c11618f6Spatrick 	uint32_t pktlen, ieslen;
2414c11618f6Spatrick 
2415c11618f6Spatrick 	/* Build a fake beacon frame to let net80211 do all the parsing. */
2416c11618f6Spatrick 	ieslen = betoh32(e->msg.datalen);
2417c11618f6Spatrick 	pktlen = sizeof(*wh) + ieslen + 6;
2418c11618f6Spatrick 	if (pktlen > MCLBYTES)
2419c11618f6Spatrick 		return;
2420c11618f6Spatrick 	m = bwfm_newbuf();
2421c11618f6Spatrick 	if (m == NULL)
2422c11618f6Spatrick 		return;
2423c11618f6Spatrick 	wh = mtod(m, struct ieee80211_frame *);
2424c11618f6Spatrick 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
2425c11618f6Spatrick 	    IEEE80211_FC0_SUBTYPE_AUTH;
2426c11618f6Spatrick 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
2427c11618f6Spatrick 	*(uint16_t *)wh->i_dur = 0;
2428c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
2429c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr2, &e->msg.addr);
2430c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid);
2431c11618f6Spatrick 	*(uint16_t *)wh->i_seq = 0;
2432c11618f6Spatrick 	((uint16_t *)(&wh[1]))[0] = IEEE80211_AUTH_ALG_OPEN;
2433c11618f6Spatrick 	((uint16_t *)(&wh[1]))[1] = IEEE80211_AUTH_OPEN_REQUEST;
2434c11618f6Spatrick 	((uint16_t *)(&wh[1]))[2] = 0;
2435c11618f6Spatrick 
2436c11618f6Spatrick 	/* Finalize mbuf. */
2437c11618f6Spatrick 	m->m_pkthdr.len = m->m_len = pktlen;
243852a13037Sstsp 	memset(&rxi, 0, sizeof(rxi));
24396605539cSpatrick 	ieee80211_input(ifp, m, ic->ic_bss, &rxi);
2440c11618f6Spatrick }
2441c11618f6Spatrick 
2442c11618f6Spatrick void
bwfm_rx_assoc_ind(struct bwfm_softc * sc,struct bwfm_event * e,size_t len,int reassoc)2443c11618f6Spatrick bwfm_rx_assoc_ind(struct bwfm_softc *sc, struct bwfm_event *e, size_t len,
2444c11618f6Spatrick     int reassoc)
2445c11618f6Spatrick {
2446c11618f6Spatrick 	struct ieee80211com *ic = &sc->sc_ic;
2447c11618f6Spatrick 	struct ifnet *ifp = &ic->ic_if;
2448c11618f6Spatrick 	struct ieee80211_rxinfo rxi;
2449c11618f6Spatrick 	struct ieee80211_frame *wh;
2450c11618f6Spatrick 	struct ieee80211_node *ni;
2451c11618f6Spatrick 	struct mbuf *m;
2452c11618f6Spatrick 	uint32_t pktlen, ieslen;
2453c11618f6Spatrick 
2454c11618f6Spatrick 	/* Build a fake beacon frame to let net80211 do all the parsing. */
2455c11618f6Spatrick 	ieslen = betoh32(e->msg.datalen);
2456c11618f6Spatrick 	pktlen = sizeof(*wh) + ieslen + 4;
2457c11618f6Spatrick 	if (reassoc)
2458c11618f6Spatrick 		pktlen += IEEE80211_ADDR_LEN;
2459c11618f6Spatrick 	if (pktlen > MCLBYTES)
2460c11618f6Spatrick 		return;
2461c11618f6Spatrick 	m = bwfm_newbuf();
2462c11618f6Spatrick 	if (m == NULL)
2463c11618f6Spatrick 		return;
2464c11618f6Spatrick 	wh = mtod(m, struct ieee80211_frame *);
2465c11618f6Spatrick 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT;
2466c11618f6Spatrick 	if (reassoc)
2467c11618f6Spatrick 	    wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_REASSOC_REQ;
2468c11618f6Spatrick 	else
2469c11618f6Spatrick 	    wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_ASSOC_REQ;
2470c11618f6Spatrick 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
2471c11618f6Spatrick 	*(uint16_t *)wh->i_dur = 0;
2472c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
2473c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr2, &e->msg.addr);
2474c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid);
2475c11618f6Spatrick 	*(uint16_t *)wh->i_seq = 0;
2476c11618f6Spatrick 	((uint16_t *)(&wh[1]))[0] = IEEE80211_CAPINFO_ESS; /* XXX */
2477c11618f6Spatrick 	((uint16_t *)(&wh[1]))[1] = 100; /* XXX */
2478c11618f6Spatrick 	if (reassoc) {
247977b542adSpatrick 		memset(((uint8_t *)&wh[1]) + 4, 0, IEEE80211_ADDR_LEN);
2480c11618f6Spatrick 		memcpy(((uint8_t *)&wh[1]) + 4 + IEEE80211_ADDR_LEN,
2481c11618f6Spatrick 		    &e[1], ieslen);
2482c11618f6Spatrick 	} else
2483c11618f6Spatrick 		memcpy(((uint8_t *)&wh[1]) + 4, &e[1], ieslen);
2484c11618f6Spatrick 
2485c11618f6Spatrick 	/* Finalize mbuf. */
2486c11618f6Spatrick 	m->m_pkthdr.len = m->m_len = pktlen;
2487c11618f6Spatrick 	ni = ieee80211_find_node(ic, wh->i_addr2);
2488c11618f6Spatrick 	if (ni == NULL) {
2489cb2afc74Spatrick 		m_freem(m);
2490c11618f6Spatrick 		return;
2491c11618f6Spatrick 	}
249252a13037Sstsp 	memset(&rxi, 0, sizeof(rxi));
2493c11618f6Spatrick 	ieee80211_input(ifp, m, ni, &rxi);
2494c11618f6Spatrick }
2495c11618f6Spatrick 
2496c11618f6Spatrick void
bwfm_rx_deauth_ind(struct bwfm_softc * sc,struct bwfm_event * e,size_t len)2497c11618f6Spatrick bwfm_rx_deauth_ind(struct bwfm_softc *sc, struct bwfm_event *e, size_t len)
2498c11618f6Spatrick {
2499c11618f6Spatrick 	bwfm_rx_leave_ind(sc, e, len, IEEE80211_FC0_SUBTYPE_DEAUTH);
2500c11618f6Spatrick }
2501c11618f6Spatrick 
2502c11618f6Spatrick void
bwfm_rx_disassoc_ind(struct bwfm_softc * sc,struct bwfm_event * e,size_t len)2503c11618f6Spatrick bwfm_rx_disassoc_ind(struct bwfm_softc *sc, struct bwfm_event *e, size_t len)
2504c11618f6Spatrick {
2505c11618f6Spatrick 	bwfm_rx_leave_ind(sc, e, len, IEEE80211_FC0_SUBTYPE_DISASSOC);
2506c11618f6Spatrick }
2507c11618f6Spatrick 
2508c11618f6Spatrick void
bwfm_rx_leave_ind(struct bwfm_softc * sc,struct bwfm_event * e,size_t len,int subtype)2509c11618f6Spatrick bwfm_rx_leave_ind(struct bwfm_softc *sc, struct bwfm_event *e, size_t len,
2510c11618f6Spatrick     int subtype)
2511c11618f6Spatrick {
2512c11618f6Spatrick 	struct ieee80211com *ic = &sc->sc_ic;
2513c11618f6Spatrick 	struct ifnet *ifp = &ic->ic_if;
2514c11618f6Spatrick 	struct ieee80211_rxinfo rxi;
2515c11618f6Spatrick 	struct ieee80211_frame *wh;
2516c11618f6Spatrick 	struct ieee80211_node *ni;
2517c11618f6Spatrick 	struct mbuf *m;
2518c11618f6Spatrick 	uint32_t pktlen;
2519c11618f6Spatrick 
2520c11618f6Spatrick 	/* Build a fake beacon frame to let net80211 do all the parsing. */
2521c11618f6Spatrick 	pktlen = sizeof(*wh) + 2;
2522c11618f6Spatrick 	if (pktlen > MCLBYTES)
2523c11618f6Spatrick 		return;
2524c11618f6Spatrick 	m = bwfm_newbuf();
2525c11618f6Spatrick 	if (m == NULL)
2526c11618f6Spatrick 		return;
2527c11618f6Spatrick 	wh = mtod(m, struct ieee80211_frame *);
2528c11618f6Spatrick 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
2529c11618f6Spatrick 	    subtype;
2530c11618f6Spatrick 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
2531c11618f6Spatrick 	*(uint16_t *)wh->i_dur = 0;
2532c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
2533c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr2, &e->msg.addr);
2534c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid);
2535c11618f6Spatrick 	*(uint16_t *)wh->i_seq = 0;
253677b542adSpatrick 	memset((uint8_t *)&wh[1], 0, 2);
2537c11618f6Spatrick 
2538c11618f6Spatrick 	/* Finalize mbuf. */
2539c11618f6Spatrick 	m->m_pkthdr.len = m->m_len = pktlen;
2540c11618f6Spatrick 	ni = ieee80211_find_node(ic, wh->i_addr2);
2541c11618f6Spatrick 	if (ni == NULL) {
2542cb2afc74Spatrick 		m_freem(m);
2543c11618f6Spatrick 		return;
2544c11618f6Spatrick 	}
254552a13037Sstsp 	memset(&rxi, 0, sizeof(rxi));
2546c11618f6Spatrick 	ieee80211_input(ifp, m, ni, &rxi);
2547c11618f6Spatrick }
2548c11618f6Spatrick #endif
2549c11618f6Spatrick 
255032b2494eSpatrick void
bwfm_rx_event(struct bwfm_softc * sc,struct mbuf * m)2551f4e8af02Spatrick bwfm_rx_event(struct bwfm_softc *sc, struct mbuf *m)
2552f4e8af02Spatrick {
2553625a64d2Spatrick 	int s;
2554f4e8af02Spatrick 
2555560a2bb3Sclaudio 	s = splnet();
2556625a64d2Spatrick 	ml_enqueue(&sc->sc_evml, m);
2557625a64d2Spatrick 	splx(s);
2558625a64d2Spatrick 
2559625a64d2Spatrick 	task_add(sc->sc_taskq, &sc->sc_task);
2560f4e8af02Spatrick }
2561f4e8af02Spatrick 
2562f4e8af02Spatrick void
bwfm_rx_event_cb(struct bwfm_softc * sc,struct mbuf * m)2563625a64d2Spatrick bwfm_rx_event_cb(struct bwfm_softc *sc, struct mbuf *m)
256432b2494eSpatrick {
256532b2494eSpatrick 	struct ieee80211com *ic = &sc->sc_ic;
256663498aa8Spatrick 	struct ifnet *ifp = &ic->ic_if;
2567f4e8af02Spatrick 	struct bwfm_event *e = mtod(m, void *);
2568f4e8af02Spatrick 	size_t len = m->m_len;
256932b2494eSpatrick 
2570f4e8af02Spatrick 	if (ntohl(e->msg.event_type) >= BWFM_E_LAST) {
2571f4e8af02Spatrick 		m_freem(m);
257232b2494eSpatrick 		return;
2573f4e8af02Spatrick 	}
257432b2494eSpatrick 
257532b2494eSpatrick 	switch (ntohl(e->msg.event_type)) {
257632b2494eSpatrick 	case BWFM_E_ESCAN_RESULT: {
2577cc283629Spatrick 		struct bwfm_escan_results *res;
257832b2494eSpatrick 		struct bwfm_bss_info *bss;
2579cc283629Spatrick 		size_t reslen;
258032b2494eSpatrick 		int i;
258125440f89Spatrick 		/* Abort event triggered by SCAN -> INIT */
258225440f89Spatrick 		if (ic->ic_state == IEEE80211_S_INIT &&
258325440f89Spatrick 		    ntohl(e->msg.status) == BWFM_E_STATUS_ABORT)
258425440f89Spatrick 			break;
258525440f89Spatrick 		if (ic->ic_state != IEEE80211_S_SCAN) {
258625440f89Spatrick 			DPRINTF(("%s: scan result (%u) while not in SCAN\n",
258725440f89Spatrick 			    DEVNAME(sc), ntohl(e->msg.status)));
258825440f89Spatrick 			break;
258925440f89Spatrick 		}
259025440f89Spatrick 		if (ntohl(e->msg.status) != BWFM_E_STATUS_SUCCESS &&
259125440f89Spatrick 		    ntohl(e->msg.status) != BWFM_E_STATUS_PARTIAL) {
259225440f89Spatrick 			DPRINTF(("%s: unexpected scan result (%u)\n",
259325440f89Spatrick 			    DEVNAME(sc), ntohl(e->msg.status)));
259425440f89Spatrick 			break;
259525440f89Spatrick 		}
259625440f89Spatrick 		if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS) {
259763498aa8Spatrick 			ieee80211_end_scan(ifp);
259832b2494eSpatrick 			break;
259932b2494eSpatrick 		}
260032b2494eSpatrick 		len -= sizeof(*e);
2601cc283629Spatrick 		if (len < sizeof(*res)) {
2602029d6dd5Spatrick 			DPRINTF(("%s: results too small\n", DEVNAME(sc)));
2603f4e8af02Spatrick 			m_freem(m);
260432b2494eSpatrick 			return;
260532b2494eSpatrick 		}
2606cc283629Spatrick 		reslen = len;
2607cc283629Spatrick 		res = malloc(len, M_TEMP, M_WAITOK);
2608cc283629Spatrick 		memcpy(res, (void *)&e[1], len);
2609cc283629Spatrick 		if (len < letoh32(res->buflen)) {
2610029d6dd5Spatrick 			DPRINTF(("%s: results too small\n", DEVNAME(sc)));
2611cc283629Spatrick 			free(res, M_TEMP, reslen);
2612cc283629Spatrick 			m_freem(m);
2613cc283629Spatrick 			return;
2614cc283629Spatrick 		}
261532b2494eSpatrick 		len -= sizeof(*res);
261632b2494eSpatrick 		if (len < letoh16(res->bss_count) * sizeof(struct bwfm_bss_info)) {
2617029d6dd5Spatrick 			DPRINTF(("%s: results too small\n", DEVNAME(sc)));
2618cc283629Spatrick 			free(res, M_TEMP, reslen);
2619f4e8af02Spatrick 			m_freem(m);
262032b2494eSpatrick 			return;
262132b2494eSpatrick 		}
262232b2494eSpatrick 		bss = &res->bss_info[0];
262332b2494eSpatrick 		for (i = 0; i < letoh16(res->bss_count); i++) {
262432b2494eSpatrick 			bwfm_scan_node(sc, &res->bss_info[i], len);
262532b2494eSpatrick 			len -= sizeof(*bss) + letoh32(bss->length);
262632b2494eSpatrick 			bss = (void *)((char *)bss) + letoh32(bss->length);
262732b2494eSpatrick 			if (len <= 0)
262832b2494eSpatrick 				break;
262932b2494eSpatrick 		}
2630cc283629Spatrick 		free(res, M_TEMP, reslen);
263132b2494eSpatrick 		break;
263232b2494eSpatrick 		}
26337a818b26Spatrick 	case BWFM_E_AUTH:
2634750cbd98Spatrick 		if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS &&
2635750cbd98Spatrick 		    ic->ic_state == IEEE80211_S_AUTH)
26367a818b26Spatrick 			ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
263763498aa8Spatrick 		else
263825440f89Spatrick 			ieee80211_begin_scan(ifp);
263963498aa8Spatrick 		break;
264063498aa8Spatrick 	case BWFM_E_ASSOC:
2641750cbd98Spatrick 		if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS &&
2642750cbd98Spatrick 		    ic->ic_state == IEEE80211_S_ASSOC)
26437a818b26Spatrick 			ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
2644e5148d43Spatrick 		else if (ntohl(e->msg.status) != BWFM_E_STATUS_UNSOLICITED)
264525440f89Spatrick 			ieee80211_begin_scan(ifp);
264663498aa8Spatrick 		break;
2647750cbd98Spatrick 	case BWFM_E_DEAUTH:
2648750cbd98Spatrick 	case BWFM_E_DISASSOC:
26491f820e28Spatrick 		if (ic->ic_state > IEEE80211_S_SCAN)
26501f820e28Spatrick 			ieee80211_begin_scan(ifp);
2651750cbd98Spatrick 		break;
265263498aa8Spatrick 	case BWFM_E_LINK:
265363498aa8Spatrick 		if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS &&
265463498aa8Spatrick 		    ntohl(e->msg.reason) == 0)
265563498aa8Spatrick 			break;
265663498aa8Spatrick 		/* Link status has changed */
26571f820e28Spatrick 		if (ic->ic_state > IEEE80211_S_SCAN)
26581f820e28Spatrick 			ieee80211_begin_scan(ifp);
265963498aa8Spatrick 		break;
2660c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
2661c11618f6Spatrick 	case BWFM_E_AUTH_IND:
2662c11618f6Spatrick 		bwfm_rx_auth_ind(sc, e, len);
2663c11618f6Spatrick 		break;
2664c11618f6Spatrick 	case BWFM_E_ASSOC_IND:
2665c11618f6Spatrick 		bwfm_rx_assoc_ind(sc, e, len, 0);
2666c11618f6Spatrick 		break;
2667c11618f6Spatrick 	case BWFM_E_REASSOC_IND:
2668c11618f6Spatrick 		bwfm_rx_assoc_ind(sc, e, len, 1);
2669c11618f6Spatrick 		break;
2670c11618f6Spatrick 	case BWFM_E_DEAUTH_IND:
2671c11618f6Spatrick 		bwfm_rx_deauth_ind(sc, e, len);
2672c11618f6Spatrick 		break;
2673c11618f6Spatrick 	case BWFM_E_DISASSOC_IND:
2674c11618f6Spatrick 		bwfm_rx_disassoc_ind(sc, e, len);
2675c11618f6Spatrick 		break;
2676c11618f6Spatrick #endif
267732b2494eSpatrick 	default:
2678cd99bb20Spatrick 		DPRINTF(("%s: len %lu datalen %u code %u status %u"
2679cd99bb20Spatrick 		    " reason %u\n", __func__, len, ntohl(e->msg.datalen),
268032b2494eSpatrick 		    ntohl(e->msg.event_type), ntohl(e->msg.status),
2681cd99bb20Spatrick 		    ntohl(e->msg.reason)));
268232b2494eSpatrick 		break;
268332b2494eSpatrick 	}
2684f4e8af02Spatrick 
2685f4e8af02Spatrick 	m_freem(m);
268632b2494eSpatrick }
268732b2494eSpatrick 
268832b2494eSpatrick void
bwfm_scan_node(struct bwfm_softc * sc,struct bwfm_bss_info * bss,size_t len)268932b2494eSpatrick bwfm_scan_node(struct bwfm_softc *sc, struct bwfm_bss_info *bss, size_t len)
269032b2494eSpatrick {
269132b2494eSpatrick 	struct ieee80211com *ic = &sc->sc_ic;
269263498aa8Spatrick 	struct ifnet *ifp = &ic->ic_if;
269363498aa8Spatrick 	struct ieee80211_frame *wh;
269432b2494eSpatrick 	struct ieee80211_node *ni;
26958b2458cfSpatrick 	struct ieee80211_rxinfo rxi;
269663498aa8Spatrick 	struct mbuf *m;
269763498aa8Spatrick 	uint32_t pktlen, ieslen;
269863498aa8Spatrick 	uint16_t iesoff;
26998b2458cfSpatrick 	int chanidx;
270032b2494eSpatrick 
270163498aa8Spatrick 	iesoff = letoh16(bss->ie_offset);
270263498aa8Spatrick 	ieslen = letoh32(bss->ie_length);
270363498aa8Spatrick 	if (ieslen > len - iesoff)
270463498aa8Spatrick 		return;
270563498aa8Spatrick 
270663498aa8Spatrick 	/* Build a fake beacon frame to let net80211 do all the parsing. */
270763498aa8Spatrick 	pktlen = sizeof(*wh) + ieslen + 12;
2708c11618f6Spatrick 	if (pktlen > MCLBYTES)
270963498aa8Spatrick 		return;
2710c11618f6Spatrick 	m = bwfm_newbuf();
2711c11618f6Spatrick 	if (m == NULL)
271263498aa8Spatrick 		return;
271363498aa8Spatrick 	wh = mtod(m, struct ieee80211_frame *);
271463498aa8Spatrick 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
271563498aa8Spatrick 	    IEEE80211_FC0_SUBTYPE_BEACON;
271663498aa8Spatrick 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
271763498aa8Spatrick 	*(uint16_t *)wh->i_dur = 0;
271863498aa8Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
271963498aa8Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr2, bss->bssid);
272063498aa8Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr3, bss->bssid);
272163498aa8Spatrick 	*(uint16_t *)wh->i_seq = 0;
272263498aa8Spatrick 	memset(&wh[1], 0, 12);
272363498aa8Spatrick 	((uint16_t *)(&wh[1]))[4] = bss->beacon_period;
272463498aa8Spatrick 	((uint16_t *)(&wh[1]))[5] = bss->capability;
272563498aa8Spatrick 	memcpy(((uint8_t *)&wh[1]) + 12, ((uint8_t *)bss) + iesoff, ieslen);
272663498aa8Spatrick 
272763498aa8Spatrick 	/* Finalize mbuf. */
272863498aa8Spatrick 	m->m_pkthdr.len = m->m_len = pktlen;
272963498aa8Spatrick 	ni = ieee80211_find_rxnode(ic, wh);
27308b2458cfSpatrick 	/* Channel mask equals IEEE80211_CHAN_MAX */
27318b2458cfSpatrick 	chanidx = bwfm_spec2chan(sc, letoh32(bss->chanspec));
27328b2458cfSpatrick 	/* Supply RSSI */
273352a13037Sstsp 	memset(&rxi, 0, sizeof(rxi));
27346e91f165Spatrick 	rxi.rxi_rssi = (int16_t)letoh16(bss->rssi);
27357307575aSstsp 	rxi.rxi_chan = chanidx;
273663498aa8Spatrick 	ieee80211_input(ifp, m, ni, &rxi);
273763498aa8Spatrick 	/* Node is no longer needed. */
273863498aa8Spatrick 	ieee80211_release_node(ic, ni);
273963498aa8Spatrick }
274063498aa8Spatrick 
274163498aa8Spatrick void
bwfm_task(void * arg)274263498aa8Spatrick bwfm_task(void *arg)
274363498aa8Spatrick {
274463498aa8Spatrick 	struct bwfm_softc *sc = arg;
274563498aa8Spatrick 	struct bwfm_host_cmd_ring *ring = &sc->sc_cmdq;
274663498aa8Spatrick 	struct bwfm_host_cmd *cmd;
2747625a64d2Spatrick 	struct mbuf *m;
274863498aa8Spatrick 	int s;
274963498aa8Spatrick 
2750560a2bb3Sclaudio 	s = splnet();
275163498aa8Spatrick 	while (ring->next != ring->cur) {
275263498aa8Spatrick 		cmd = &ring->cmd[ring->next];
275363498aa8Spatrick 		splx(s);
275463498aa8Spatrick 		cmd->cb(sc, cmd->data);
2755560a2bb3Sclaudio 		s = splnet();
275663498aa8Spatrick 		ring->queued--;
275763498aa8Spatrick 		ring->next = (ring->next + 1) % BWFM_HOST_CMD_RING_COUNT;
275863498aa8Spatrick 	}
275963498aa8Spatrick 	splx(s);
2760625a64d2Spatrick 
2761560a2bb3Sclaudio 	s = splnet();
2762625a64d2Spatrick 	while ((m = ml_dequeue(&sc->sc_evml)) != NULL) {
2763625a64d2Spatrick 		splx(s);
2764625a64d2Spatrick 		bwfm_rx_event_cb(sc, m);
2765560a2bb3Sclaudio 		s = splnet();
2766625a64d2Spatrick 	}
2767625a64d2Spatrick 	splx(s);
276863498aa8Spatrick }
276963498aa8Spatrick 
277063498aa8Spatrick void
bwfm_do_async(struct bwfm_softc * sc,void (* cb)(struct bwfm_softc *,void *),void * arg,int len)277163498aa8Spatrick bwfm_do_async(struct bwfm_softc *sc,
277263498aa8Spatrick     void (*cb)(struct bwfm_softc *, void *), void *arg, int len)
277363498aa8Spatrick {
277463498aa8Spatrick 	struct bwfm_host_cmd_ring *ring = &sc->sc_cmdq;
277563498aa8Spatrick 	struct bwfm_host_cmd *cmd;
277663498aa8Spatrick 	int s;
277763498aa8Spatrick 
2778560a2bb3Sclaudio 	s = splnet();
2779b72aacb1Spatrick 	KASSERT(ring->queued < BWFM_HOST_CMD_RING_COUNT);
2780f4e8af02Spatrick 	if (ring->queued >= BWFM_HOST_CMD_RING_COUNT) {
2781f4e8af02Spatrick 		splx(s);
2782f4e8af02Spatrick 		return;
2783f4e8af02Spatrick 	}
278463498aa8Spatrick 	cmd = &ring->cmd[ring->cur];
278563498aa8Spatrick 	cmd->cb = cb;
278663498aa8Spatrick 	KASSERT(len <= sizeof(cmd->data));
278763498aa8Spatrick 	memcpy(cmd->data, arg, len);
278863498aa8Spatrick 	ring->cur = (ring->cur + 1) % BWFM_HOST_CMD_RING_COUNT;
2789f4e8af02Spatrick 	ring->queued++;
279063498aa8Spatrick 	task_add(sc->sc_taskq, &sc->sc_task);
279163498aa8Spatrick 	splx(s);
279263498aa8Spatrick }
279363498aa8Spatrick 
279463498aa8Spatrick int
bwfm_send_mgmt(struct ieee80211com * ic,struct ieee80211_node * ni,int type,int arg1,int arg2)279563498aa8Spatrick bwfm_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
279663498aa8Spatrick     int type, int arg1, int arg2)
279763498aa8Spatrick {
279863498aa8Spatrick #ifdef BWFM_DEBUG
279963498aa8Spatrick 	struct bwfm_softc *sc = ic->ic_softc;
280063498aa8Spatrick 	DPRINTF(("%s: %s\n", DEVNAME(sc), __func__));
280163498aa8Spatrick #endif
280263498aa8Spatrick 	return 0;
280363498aa8Spatrick }
280463498aa8Spatrick 
280563498aa8Spatrick int
bwfm_set_key(struct ieee80211com * ic,struct ieee80211_node * ni,struct ieee80211_key * k)280663498aa8Spatrick bwfm_set_key(struct ieee80211com *ic, struct ieee80211_node *ni,
280763498aa8Spatrick     struct ieee80211_key *k)
280863498aa8Spatrick {
280963498aa8Spatrick 	struct bwfm_softc *sc = ic->ic_softc;
281063498aa8Spatrick 	struct bwfm_cmd_key cmd;
281163498aa8Spatrick 
281263498aa8Spatrick 	cmd.ni = ni;
281363498aa8Spatrick 	cmd.k = k;
281463498aa8Spatrick 	bwfm_do_async(sc, bwfm_set_key_cb, &cmd, sizeof(cmd));
2815cbb32f9cSkrw 	sc->sc_key_tasks++;
2816cbb32f9cSkrw 	return EBUSY;
281763498aa8Spatrick }
281863498aa8Spatrick 
281963498aa8Spatrick void
bwfm_set_key_cb(struct bwfm_softc * sc,void * arg)282063498aa8Spatrick bwfm_set_key_cb(struct bwfm_softc *sc, void *arg)
282163498aa8Spatrick {
282263498aa8Spatrick 	struct bwfm_cmd_key *cmd = arg;
282363498aa8Spatrick 	struct ieee80211_key *k = cmd->k;
282463498aa8Spatrick 	struct ieee80211_node *ni = cmd->ni;
2825cbb32f9cSkrw 	struct ieee80211com *ic = &sc->sc_ic;
282663498aa8Spatrick 	struct bwfm_wsec_key key;
282763498aa8Spatrick 	uint32_t wsec, wsec_enable;
282863498aa8Spatrick 	int ext_key = 0;
282963498aa8Spatrick 
2830cbb32f9cSkrw 	sc->sc_key_tasks--;
2831cbb32f9cSkrw 
283263498aa8Spatrick 	if ((k->k_flags & IEEE80211_KEY_GROUP) == 0 &&
283363498aa8Spatrick 	    k->k_cipher != IEEE80211_CIPHER_WEP40 &&
283463498aa8Spatrick 	    k->k_cipher != IEEE80211_CIPHER_WEP104)
283563498aa8Spatrick 		ext_key = 1;
283663498aa8Spatrick 
283763498aa8Spatrick 	memset(&key, 0, sizeof(key));
2838c11618f6Spatrick 	if (ext_key && !IEEE80211_IS_MULTICAST(ni->ni_macaddr))
2839c11618f6Spatrick 		memcpy(key.ea, ni->ni_macaddr, sizeof(key.ea));
284063498aa8Spatrick 	key.index = htole32(k->k_id);
284163498aa8Spatrick 	key.len = htole32(k->k_len);
284263498aa8Spatrick 	memcpy(key.data, k->k_key, sizeof(key.data));
284363498aa8Spatrick 	if (!ext_key)
284463498aa8Spatrick 		key.flags = htole32(BWFM_WSEC_PRIMARY_KEY);
284563498aa8Spatrick 
284663498aa8Spatrick 	switch (k->k_cipher) {
284763498aa8Spatrick 	case IEEE80211_CIPHER_WEP40:
284863498aa8Spatrick 		key.algo = htole32(BWFM_CRYPTO_ALGO_WEP1);
284963498aa8Spatrick 		wsec_enable = BWFM_WSEC_WEP;
285063498aa8Spatrick 		break;
285163498aa8Spatrick 	case IEEE80211_CIPHER_WEP104:
285263498aa8Spatrick 		key.algo = htole32(BWFM_CRYPTO_ALGO_WEP128);
285363498aa8Spatrick 		wsec_enable = BWFM_WSEC_WEP;
285463498aa8Spatrick 		break;
285563498aa8Spatrick 	case IEEE80211_CIPHER_TKIP:
285663498aa8Spatrick 		key.algo = htole32(BWFM_CRYPTO_ALGO_TKIP);
285763498aa8Spatrick 		wsec_enable = BWFM_WSEC_TKIP;
285863498aa8Spatrick 		break;
285963498aa8Spatrick 	case IEEE80211_CIPHER_CCMP:
286063498aa8Spatrick 		key.algo = htole32(BWFM_CRYPTO_ALGO_AES_CCM);
286163498aa8Spatrick 		wsec_enable = BWFM_WSEC_AES;
286263498aa8Spatrick 		break;
286363498aa8Spatrick 	default:
286463498aa8Spatrick 		printf("%s: cipher %x not supported\n", DEVNAME(sc),
286563498aa8Spatrick 		    k->k_cipher);
2866cbb32f9cSkrw 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
286763498aa8Spatrick 		return;
286863498aa8Spatrick 	}
286963498aa8Spatrick 
287018460ee7Sjcs 	delay(100);
287118460ee7Sjcs 
287263498aa8Spatrick 	bwfm_fwvar_var_set_data(sc, "wsec_key", &key, sizeof(key));
287363498aa8Spatrick 	bwfm_fwvar_var_get_int(sc, "wsec", &wsec);
2874c8e9388cSstsp 	wsec &= ~(BWFM_WSEC_WEP | BWFM_WSEC_TKIP | BWFM_WSEC_AES);
287563498aa8Spatrick 	wsec |= wsec_enable;
287663498aa8Spatrick 	bwfm_fwvar_var_set_int(sc, "wsec", wsec);
2877cbb32f9cSkrw 
2878c8e9388cSstsp 	if (wsec_enable != BWFM_WSEC_WEP && cmd->ni != NULL &&
2879c8e9388cSstsp 	    sc->sc_key_tasks == 0) {
2880cbb32f9cSkrw 		DPRINTF(("%s: marking port %s valid\n", DEVNAME(sc),
2881cbb32f9cSkrw 		    ether_sprintf(cmd->ni->ni_macaddr)));
2882cbb32f9cSkrw 		cmd->ni->ni_port_valid = 1;
2883cbb32f9cSkrw 		ieee80211_set_link_state(ic, LINK_STATE_UP);
2884cbb32f9cSkrw 	}
288563498aa8Spatrick }
288663498aa8Spatrick 
288763498aa8Spatrick void
bwfm_delete_key(struct ieee80211com * ic,struct ieee80211_node * ni,struct ieee80211_key * k)288863498aa8Spatrick bwfm_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni,
288963498aa8Spatrick     struct ieee80211_key *k)
289063498aa8Spatrick {
289163498aa8Spatrick 	struct bwfm_softc *sc = ic->ic_softc;
289263498aa8Spatrick 	struct bwfm_cmd_key cmd;
289363498aa8Spatrick 
289463498aa8Spatrick 	cmd.ni = ni;
289563498aa8Spatrick 	cmd.k = k;
289663498aa8Spatrick 	bwfm_do_async(sc, bwfm_delete_key_cb, &cmd, sizeof(cmd));
289763498aa8Spatrick }
289863498aa8Spatrick 
289963498aa8Spatrick void
bwfm_delete_key_cb(struct bwfm_softc * sc,void * arg)290063498aa8Spatrick bwfm_delete_key_cb(struct bwfm_softc *sc, void *arg)
290163498aa8Spatrick {
290263498aa8Spatrick 	struct bwfm_cmd_key *cmd = arg;
290363498aa8Spatrick 	struct ieee80211_key *k = cmd->k;
290463498aa8Spatrick 	struct bwfm_wsec_key key;
290563498aa8Spatrick 
290663498aa8Spatrick 	memset(&key, 0, sizeof(key));
290763498aa8Spatrick 	key.index = htole32(k->k_id);
290863498aa8Spatrick 	key.flags = htole32(BWFM_WSEC_PRIMARY_KEY);
290963498aa8Spatrick 	bwfm_fwvar_var_set_data(sc, "wsec_key", &key, sizeof(key));
291063498aa8Spatrick }
291163498aa8Spatrick 
291263498aa8Spatrick int
bwfm_newstate(struct ieee80211com * ic,enum ieee80211_state nstate,int arg)291363498aa8Spatrick bwfm_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
291463498aa8Spatrick {
291563498aa8Spatrick 	struct bwfm_softc *sc = ic->ic_softc;
29165d1a01feSstsp 	struct ifnet *ifp = &ic->ic_if;
291763498aa8Spatrick 	int s;
291863498aa8Spatrick 
291963498aa8Spatrick 	s = splnet();
292063498aa8Spatrick 
292163498aa8Spatrick 	switch (nstate) {
292225440f89Spatrick 	case IEEE80211_S_INIT:
292325440f89Spatrick 		if (ic->ic_state == IEEE80211_S_SCAN)
292425440f89Spatrick 			bwfm_scan_abort(sc);
292525440f89Spatrick 		break;
292663498aa8Spatrick 	case IEEE80211_S_SCAN:
292773b11a26Spatrick #ifndef IEEE80211_STA_ONLY
292873b11a26Spatrick 		/* Don't start a scan if we already have a channel. */
292973b11a26Spatrick 		if (ic->ic_state == IEEE80211_S_INIT &&
293073b11a26Spatrick 		    ic->ic_opmode == IEEE80211_M_HOSTAP &&
293173b11a26Spatrick 		    ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
293273b11a26Spatrick 			break;
293373b11a26Spatrick 		}
293473b11a26Spatrick #endif
293525440f89Spatrick 		/* If we tried to connect, abort. */
293625440f89Spatrick 		if (ic->ic_state > IEEE80211_S_SCAN)
293725440f89Spatrick 			bwfm_fwvar_cmd_set_data(sc, BWFM_C_DISASSOC, NULL, 0);
293825440f89Spatrick 		/* Initiate scan. */
293963498aa8Spatrick 		bwfm_scan(sc);
29405d1a01feSstsp 		if (ifp->if_flags & IFF_DEBUG)
29415d1a01feSstsp 			printf("%s: %s -> %s\n", DEVNAME(sc),
29425d1a01feSstsp 			    ieee80211_state_name[ic->ic_state],
29435d1a01feSstsp 			    ieee80211_state_name[nstate]);
2944341abe6fSpatrick 		/* No need to do this again. */
2945341abe6fSpatrick 		if (ic->ic_state == IEEE80211_S_SCAN) {
2946341abe6fSpatrick 			splx(s);
2947341abe6fSpatrick 			return 0;
2948341abe6fSpatrick 		}
29492346aae2Sphessler 		ieee80211_set_link_state(ic, LINK_STATE_DOWN);
2950732f0a59Spatrick 		ieee80211_free_allnodes(ic, 1);
295163498aa8Spatrick 		ic->ic_state = nstate;
295263498aa8Spatrick 		splx(s);
2953f4e8af02Spatrick 		return 0;
295463498aa8Spatrick 	case IEEE80211_S_AUTH:
295563498aa8Spatrick 		ic->ic_bss->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE;
295663498aa8Spatrick 		bwfm_connect(sc);
29575d1a01feSstsp 		if (ifp->if_flags & IFF_DEBUG)
29585d1a01feSstsp 			printf("%s: %s -> %s\n", DEVNAME(sc),
29595d1a01feSstsp 			    ieee80211_state_name[ic->ic_state],
29605d1a01feSstsp 			    ieee80211_state_name[nstate]);
29615d1a01feSstsp 		ic->ic_state = nstate;
296263498aa8Spatrick 		if (ic->ic_flags & IEEE80211_F_RSNON)
296363498aa8Spatrick 			ic->ic_bss->ni_rsn_supp_state = RSNA_SUPP_PTKSTART;
296463498aa8Spatrick 		splx(s);
2965f4e8af02Spatrick 		return 0;
2966c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
2967c11618f6Spatrick 	case IEEE80211_S_RUN:
2968c11618f6Spatrick 		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
2969c11618f6Spatrick 			bwfm_hostap(sc);
2970c11618f6Spatrick 		break;
2971c11618f6Spatrick #endif
297263498aa8Spatrick 	default:
297363498aa8Spatrick 		break;
297463498aa8Spatrick 	}
2975f4e8af02Spatrick 	sc->sc_newstate(ic, nstate, arg);
297663498aa8Spatrick 	splx(s);
2977f4e8af02Spatrick 	return 0;
297832b2494eSpatrick }
2979632f7369Spatrick 
2980632f7369Spatrick int
bwfm_nvram_convert(int node,u_char ** bufp,size_t * sizep,size_t * newlenp)2981db31205aSkettenis bwfm_nvram_convert(int node, u_char **bufp, size_t *sizep, size_t *newlenp)
2982632f7369Spatrick {
2983db31205aSkettenis 	u_char *src, *dst, *end = *bufp + *sizep, *newbuf;
2984db31205aSkettenis 	size_t count = 0, newsize, pad;
2985632f7369Spatrick 	uint32_t token;
2986632f7369Spatrick 	int skip = 0;
2987632f7369Spatrick 
2988db31205aSkettenis 	/*
2989db31205aSkettenis 	 * Allocate a new buffer with enough space for the MAC
2990db31205aSkettenis 	 * address, padding and final token.
2991db31205aSkettenis 	 */
2992db31205aSkettenis 	newsize = *sizep + 64;
2993db31205aSkettenis 	newbuf = malloc(newsize, M_DEVBUF, M_NOWAIT);
2994db31205aSkettenis 	if (newbuf == NULL)
2995db31205aSkettenis 		return 1;
2996db31205aSkettenis 
2997db31205aSkettenis 	for (src = *bufp, dst = newbuf; src != end; ++src) {
2998632f7369Spatrick 		if (*src == '\n') {
2999632f7369Spatrick 			if (count > 0)
3000632f7369Spatrick 				*dst++ = '\0';
3001632f7369Spatrick 			count = 0;
3002632f7369Spatrick 			skip = 0;
3003632f7369Spatrick 			continue;
3004632f7369Spatrick 		}
3005632f7369Spatrick 		if (skip)
3006632f7369Spatrick 			continue;
3007632f7369Spatrick 		if (*src == '#' && count == 0) {
3008632f7369Spatrick 			skip = 1;
3009632f7369Spatrick 			continue;
3010632f7369Spatrick 		}
3011632f7369Spatrick 		if (*src == '\r')
3012632f7369Spatrick 			continue;
3013632f7369Spatrick 		*dst++ = *src;
3014632f7369Spatrick 		++count;
3015632f7369Spatrick 	}
3016632f7369Spatrick 
3017db31205aSkettenis #if defined(__HAVE_FDT)
3018db31205aSkettenis 	/*
3019db31205aSkettenis 	 * Append MAC address if one is provided in the device tree.
3020db31205aSkettenis 	 * This is needed on Apple Silicon Macs.
3021db31205aSkettenis 	 */
3022db31205aSkettenis 	if (node) {
3023db31205aSkettenis 		u_char enaddr[ETHER_ADDR_LEN];
3024db31205aSkettenis 		char macaddr[32];
3025632f7369Spatrick 
3026db31205aSkettenis 		if (OF_getprop(node, "local-mac-address",
3027db31205aSkettenis 		    enaddr, sizeof(enaddr))) {
3028db31205aSkettenis 			snprintf(macaddr, sizeof(macaddr),
3029db31205aSkettenis 			    "macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
3030db31205aSkettenis 			    enaddr[0], enaddr[1], enaddr[2], enaddr[3],
3031db31205aSkettenis 			    enaddr[4], enaddr[5]);
3032db31205aSkettenis 			if (*dst)
3033db31205aSkettenis 				*dst++ = '\0';
3034db31205aSkettenis 			memcpy(dst, macaddr, strlen(macaddr));
3035db31205aSkettenis 			dst += strlen(macaddr);
3036db31205aSkettenis 		}
3037db31205aSkettenis 	}
3038db31205aSkettenis #endif
3039db31205aSkettenis 
3040db31205aSkettenis 	count = dst - newbuf;
3041db31205aSkettenis 	pad = roundup(count + 1, 4) - count;
3042632f7369Spatrick 
3043632f7369Spatrick 	memset(dst, 0, pad);
3044632f7369Spatrick 	count += pad;
3045632f7369Spatrick 	dst += pad;
3046632f7369Spatrick 
3047632f7369Spatrick 	token = (count / 4) & 0xffff;
3048632f7369Spatrick 	token |= ~token << 16;
3049632f7369Spatrick 	token = htole32(token);
3050632f7369Spatrick 
3051632f7369Spatrick 	memcpy(dst, &token, sizeof(token));
3052632f7369Spatrick 	count += sizeof(token);
3053632f7369Spatrick 
3054db31205aSkettenis 	free(*bufp, M_DEVBUF, *sizep);
3055db31205aSkettenis 	*bufp = newbuf;
3056db31205aSkettenis 	*sizep = newsize;
3057632f7369Spatrick 	*newlenp = count;
3058632f7369Spatrick 	return 0;
3059632f7369Spatrick }
30606c2f85e4Spatrick 
30616c2f85e4Spatrick void
bwfm_process_blob(struct bwfm_softc * sc,char * var,u_char ** blob,size_t * blobsize)3062da5a4af2Spatrick bwfm_process_blob(struct bwfm_softc *sc, char *var, u_char **blob,
3063da5a4af2Spatrick     size_t *blobsize)
30646c2f85e4Spatrick {
30656c2f85e4Spatrick 	struct bwfm_dload_data *data;
30666c2f85e4Spatrick 	size_t off, remain, len;
30676c2f85e4Spatrick 
3068da5a4af2Spatrick 	if (*blob == NULL || *blobsize == 0)
30696c2f85e4Spatrick 		return;
30706c2f85e4Spatrick 
30716c2f85e4Spatrick 	off = 0;
3072da5a4af2Spatrick 	remain = *blobsize;
30736c2f85e4Spatrick 	data = malloc(sizeof(*data) + BWFM_DLOAD_MAX_LEN, M_TEMP, M_WAITOK);
30746c2f85e4Spatrick 
30756c2f85e4Spatrick 	while (remain) {
30766c2f85e4Spatrick 		len = min(remain, BWFM_DLOAD_MAX_LEN);
30776c2f85e4Spatrick 
30786c2f85e4Spatrick 		data->flag = htole16(BWFM_DLOAD_FLAG_HANDLER_VER_1);
30796c2f85e4Spatrick 		if (off == 0)
30806c2f85e4Spatrick 			data->flag |= htole16(BWFM_DLOAD_FLAG_BEGIN);
30810813c1e9Spatrick 		if (remain <= BWFM_DLOAD_MAX_LEN)
30826c2f85e4Spatrick 			data->flag |= htole16(BWFM_DLOAD_FLAG_END);
30836c2f85e4Spatrick 		data->type = htole16(BWFM_DLOAD_TYPE_CLM);
30846c2f85e4Spatrick 		data->len = htole32(len);
30856c2f85e4Spatrick 		data->crc = 0;
3086da5a4af2Spatrick 		memcpy(data->data, *blob + off, len);
30876c2f85e4Spatrick 
3088da5a4af2Spatrick 		if (bwfm_fwvar_var_set_data(sc, var, data,
30896c2f85e4Spatrick 		    sizeof(*data) + len)) {
3090da5a4af2Spatrick 			printf("%s: could not load blob (%s)\n", DEVNAME(sc),
3091da5a4af2Spatrick 			    var);
30926c2f85e4Spatrick 			goto out;
30936c2f85e4Spatrick 		}
30946c2f85e4Spatrick 
30956c2f85e4Spatrick 		off += len;
30966c2f85e4Spatrick 		remain -= len;
30976c2f85e4Spatrick 	}
30986c2f85e4Spatrick 
30996c2f85e4Spatrick out:
31006c2f85e4Spatrick 	free(data, M_TEMP, sizeof(*data) + BWFM_DLOAD_MAX_LEN);
3101da5a4af2Spatrick 	free(*blob, M_DEVBUF, *blobsize);
3102da5a4af2Spatrick 	*blob = NULL;
3103da5a4af2Spatrick 	*blobsize = 0;
31046c2f85e4Spatrick }
310594e4747cSpatrick 
3106c6a4ab0dSkettenis void
bwfm_init_board_type(struct bwfm_softc * sc)3107c6a4ab0dSkettenis bwfm_init_board_type(struct bwfm_softc *sc)
310894e4747cSpatrick {
3109c6a4ab0dSkettenis #if defined(__HAVE_FDT)
3110c6a4ab0dSkettenis 	char compat[128];
311194e4747cSpatrick 	int len;
311294e4747cSpatrick 	char *p;
311394e4747cSpatrick 
3114c6a4ab0dSkettenis 	len = OF_getprop(OF_peer(0), "compatible", compat, sizeof(compat));
3115c6a4ab0dSkettenis 	if (len > 0 && len < sizeof(compat)) {
3116c6a4ab0dSkettenis 		compat[len] = '\0';
3117c6a4ab0dSkettenis 		if ((p = strchr(compat, '/')) != NULL)
311894e4747cSpatrick 			*p = '\0';
3119c6a4ab0dSkettenis 		strlcpy(sc->sc_board_type, compat, sizeof(sc->sc_board_type));
312094e4747cSpatrick 	}
312194e4747cSpatrick #endif
3122c6a4ab0dSkettenis }
312394e4747cSpatrick 
312494e4747cSpatrick int
bwfm_loadfirmware(struct bwfm_softc * sc,const char * chip,const char * bus,u_char ** ucode,size_t * size,u_char ** nvram,size_t * nvsize,size_t * nvlen)312594e4747cSpatrick bwfm_loadfirmware(struct bwfm_softc *sc, const char *chip, const char *bus,
312694e4747cSpatrick     u_char **ucode, size_t *size, u_char **nvram, size_t *nvsize, size_t *nvlen)
312794e4747cSpatrick {
3128c6a4ab0dSkettenis 	const char *board_type = NULL;
312994e4747cSpatrick 	char name[128];
313094e4747cSpatrick 	int r;
313194e4747cSpatrick 
313294e4747cSpatrick 	*ucode = *nvram = NULL;
313394e4747cSpatrick 	*size = *nvsize = *nvlen = 0;
313494e4747cSpatrick 
3135c6a4ab0dSkettenis 	if (strlen(sc->sc_board_type) > 0)
3136c6a4ab0dSkettenis 		board_type = sc->sc_board_type;
313794e4747cSpatrick 
3138c6a4ab0dSkettenis 	if (board_type != NULL) {
3139ec9994a1Skettenis 		r = snprintf(name, sizeof(name), "%sbrcmfmac%s%s.%s.bin",
3140ec9994a1Skettenis 		    sc->sc_fwdir, chip, bus, board_type);
314194e4747cSpatrick 		if ((r > 0 && r < sizeof(name)) &&
314294e4747cSpatrick 		    loadfirmware(name, ucode, size) != 0)
314394e4747cSpatrick 			*size = 0;
314494e4747cSpatrick 	}
314594e4747cSpatrick 	if (*size == 0) {
3146ec9994a1Skettenis 		snprintf(name, sizeof(name), "%sbrcmfmac%s%s.bin",
3147ec9994a1Skettenis 		    sc->sc_fwdir, chip, bus);
314894e4747cSpatrick 		if (loadfirmware(name, ucode, size) != 0) {
3149ec9994a1Skettenis 			snprintf(name, sizeof(name), "%sbrcmfmac%s%s%s%s.bin",
3150ec9994a1Skettenis 			    sc->sc_fwdir, chip, bus, board_type ? "." : "",
3151c6a4ab0dSkettenis 			    board_type ? board_type : "");
315294e4747cSpatrick 			printf("%s: failed loadfirmware of file %s\n",
315394e4747cSpatrick 			    DEVNAME(sc), name);
315494e4747cSpatrick 			return 1;
315594e4747cSpatrick 		}
315694e4747cSpatrick 	}
315794e4747cSpatrick 
315894e4747cSpatrick 	/* .txt needs to be processed first */
3159c6a4ab0dSkettenis 	if (strlen(sc->sc_modrev) > 0) {
3160c6a4ab0dSkettenis 		r = snprintf(name, sizeof(name),
3161ec9994a1Skettenis 		    "%sbrcmfmac%s%s.%s-%s-%s-%s.txt", sc->sc_fwdir, chip, bus,
3162ec9994a1Skettenis 		    board_type, sc->sc_module, sc->sc_vendor, sc->sc_modrev);
3163c6a4ab0dSkettenis 		if (r > 0 && r < sizeof(name))
3164c6a4ab0dSkettenis 			loadfirmware(name, nvram, nvsize);
3165c6a4ab0dSkettenis 	}
3166c6a4ab0dSkettenis 	if (*nvsize == 0 && strlen(sc->sc_vendor) > 0) {
3167c6a4ab0dSkettenis 		r = snprintf(name, sizeof(name),
3168ec9994a1Skettenis 		    "%sbrcmfmac%s%s.%s-%s-%s.txt", sc->sc_fwdir, chip, bus,
3169ec9994a1Skettenis 		    board_type, sc->sc_module, sc->sc_vendor);
3170c6a4ab0dSkettenis 		if (r > 0 && r < sizeof(name))
3171c6a4ab0dSkettenis 			loadfirmware(name, nvram, nvsize);
3172c6a4ab0dSkettenis 	}
3173c6a4ab0dSkettenis 
3174c6a4ab0dSkettenis 	if (*nvsize == 0 && board_type != NULL) {
3175ec9994a1Skettenis 		r = snprintf(name, sizeof(name), "%sbrcmfmac%s%s.%s.txt",
3176ec9994a1Skettenis 		    sc->sc_fwdir, chip, bus, board_type);
3177db31205aSkettenis 		if (r > 0 && r < sizeof(name))
3178db31205aSkettenis 			loadfirmware(name, nvram, nvsize);
317994e4747cSpatrick 	}
318094e4747cSpatrick 
3181db31205aSkettenis 	if (*nvsize == 0) {
3182ec9994a1Skettenis 		snprintf(name, sizeof(name), "%sbrcmfmac%s%s.txt",
3183ec9994a1Skettenis 		    sc->sc_fwdir, chip, bus);
3184db31205aSkettenis 		loadfirmware(name, nvram, nvsize);
3185db31205aSkettenis 	}
3186db31205aSkettenis 
3187db31205aSkettenis 	if (*nvsize != 0) {
3188db31205aSkettenis 		if (bwfm_nvram_convert(sc->sc_node, nvram, nvsize, nvlen)) {
318994e4747cSpatrick 			printf("%s: failed to process file %s\n",
319094e4747cSpatrick 			    DEVNAME(sc), name);
319194e4747cSpatrick 			free(*ucode, M_DEVBUF, *size);
319294e4747cSpatrick 			free(*nvram, M_DEVBUF, *nvsize);
319394e4747cSpatrick 			return 1;
319494e4747cSpatrick 		}
319594e4747cSpatrick 	}
319694e4747cSpatrick 
319794e4747cSpatrick 	/* .nvram is the pre-processed version */
319894e4747cSpatrick 	if (*nvlen == 0) {
3199ec9994a1Skettenis 		snprintf(name, sizeof(name), "%sbrcmfmac%s%s.nvram",
3200ec9994a1Skettenis 		    sc->sc_fwdir, chip, bus);
320194e4747cSpatrick 		if (loadfirmware(name, nvram, nvsize) == 0)
320294e4747cSpatrick 			*nvlen = *nvsize;
320394e4747cSpatrick 	}
320494e4747cSpatrick 
320594e4747cSpatrick 	if (*nvlen == 0 && strcmp(bus, "-sdio") == 0) {
3206ec9994a1Skettenis 		snprintf(name, sizeof(name), "%sbrcmfmac%s%s%s%s.txt",
3207ec9994a1Skettenis 		    sc->sc_fwdir, chip, bus, board_type ? "." : "",
3208ec9994a1Skettenis 		    board_type ? board_type : "");
320994e4747cSpatrick 		printf("%s: failed loadfirmware of file %s\n",
321094e4747cSpatrick 		    DEVNAME(sc), name);
321194e4747cSpatrick 		free(*ucode, M_DEVBUF, *size);
321294e4747cSpatrick 		return 1;
321394e4747cSpatrick 	}
321494e4747cSpatrick 
3215c6a4ab0dSkettenis 	if (board_type != NULL) {
3216ec9994a1Skettenis 		r = snprintf(name, sizeof(name), "%sbrcmfmac%s%s.%s.clm_blob",
3217ec9994a1Skettenis 		    sc->sc_fwdir, chip, bus, board_type);
321894e4747cSpatrick 		if (r > 0 && r < sizeof(name))
321994e4747cSpatrick 			loadfirmware(name, &sc->sc_clm, &sc->sc_clmsize);
322094e4747cSpatrick 	}
322194e4747cSpatrick 	if (sc->sc_clmsize == 0) {
3222ec9994a1Skettenis 		snprintf(name, sizeof(name), "%sbrcmfmac%s%s.clm_blob",
3223ec9994a1Skettenis 		    sc->sc_fwdir, chip, bus);
322494e4747cSpatrick 		loadfirmware(name, &sc->sc_clm, &sc->sc_clmsize);
322594e4747cSpatrick 	}
322694e4747cSpatrick 
3227c6a4ab0dSkettenis 	if (board_type != NULL) {
3228ec9994a1Skettenis 		r = snprintf(name, sizeof(name),
3229ec9994a1Skettenis 		    "%sbrcmfmac%s%s.%s.txcap_blob", sc->sc_fwdir,
3230c6a4ab0dSkettenis 		    chip, bus, board_type);
3231da5a4af2Spatrick 		if (r > 0 && r < sizeof(name))
3232da5a4af2Spatrick 			loadfirmware(name, &sc->sc_txcap, &sc->sc_txcapsize);
3233da5a4af2Spatrick 	}
3234da5a4af2Spatrick 	if (sc->sc_txcapsize == 0) {
3235ec9994a1Skettenis 		snprintf(name, sizeof(name), "%sbrcmfmac%s%s.txcap_blob",
3236ec9994a1Skettenis 		    sc->sc_fwdir, chip, bus);
3237da5a4af2Spatrick 		loadfirmware(name, &sc->sc_txcap, &sc->sc_txcapsize);
3238da5a4af2Spatrick 	}
3239da5a4af2Spatrick 
324094e4747cSpatrick 	return 0;
324194e4747cSpatrick }
3242