xref: /openbsd/sys/dev/ic/bwfm.c (revision c9aa06c9)
1*c9aa06c9Spatrick /* $OpenBSD: bwfm.c,v 1.36 2018/02/06 02:23:04 patrick 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 
3132b2494eSpatrick #if NBPFILTER > 0
3232b2494eSpatrick #include <net/bpf.h>
3332b2494eSpatrick #endif
3432b2494eSpatrick #include <net/if.h>
3532b2494eSpatrick #include <net/if_dl.h>
3632b2494eSpatrick #include <net/if_media.h>
3732b2494eSpatrick 
3832b2494eSpatrick #include <netinet/in.h>
3932b2494eSpatrick #include <netinet/if_ether.h>
4032b2494eSpatrick 
4132b2494eSpatrick #include <net80211/ieee80211_var.h>
4232b2494eSpatrick 
4332b2494eSpatrick #include <dev/ic/bwfmvar.h>
4432b2494eSpatrick #include <dev/ic/bwfmreg.h>
4532b2494eSpatrick 
4632b2494eSpatrick /* #define BWFM_DEBUG */
4732b2494eSpatrick #ifdef BWFM_DEBUG
4832b2494eSpatrick #define DPRINTF(x)	do { if (bwfm_debug > 0) printf x; } while (0)
4932b2494eSpatrick #define DPRINTFN(n, x)	do { if (bwfm_debug >= (n)) printf x; } while (0)
5032b2494eSpatrick static int bwfm_debug = 1;
5132b2494eSpatrick #else
5232b2494eSpatrick #define DPRINTF(x)	do { ; } while (0)
5332b2494eSpatrick #define DPRINTFN(n, x)	do { ; } while (0)
5432b2494eSpatrick #endif
5532b2494eSpatrick 
5632b2494eSpatrick #define DEVNAME(sc)	((sc)->sc_dev.dv_xname)
5732b2494eSpatrick 
5832b2494eSpatrick void	 bwfm_start(struct ifnet *);
5932b2494eSpatrick void	 bwfm_init(struct ifnet *);
6032b2494eSpatrick void	 bwfm_stop(struct ifnet *);
6132b2494eSpatrick void	 bwfm_watchdog(struct ifnet *);
6232b2494eSpatrick int	 bwfm_ioctl(struct ifnet *, u_long, caddr_t);
6332b2494eSpatrick int	 bwfm_media_change(struct ifnet *);
6432b2494eSpatrick 
6532b2494eSpatrick int	 bwfm_chip_attach(struct bwfm_softc *);
6632b2494eSpatrick int	 bwfm_chip_detach(struct bwfm_softc *, int);
6732b2494eSpatrick struct bwfm_core *bwfm_chip_get_core(struct bwfm_softc *, int);
6832b2494eSpatrick struct bwfm_core *bwfm_chip_get_pmu(struct bwfm_softc *);
6932b2494eSpatrick int	 bwfm_chip_ai_isup(struct bwfm_softc *, struct bwfm_core *);
7032b2494eSpatrick void	 bwfm_chip_ai_disable(struct bwfm_softc *, struct bwfm_core *,
7132b2494eSpatrick 	     uint32_t, uint32_t);
7232b2494eSpatrick void	 bwfm_chip_ai_reset(struct bwfm_softc *, struct bwfm_core *,
7332b2494eSpatrick 	     uint32_t, uint32_t, uint32_t);
7432b2494eSpatrick void	 bwfm_chip_dmp_erom_scan(struct bwfm_softc *);
7532b2494eSpatrick int	 bwfm_chip_dmp_get_regaddr(struct bwfm_softc *, uint32_t *,
7632b2494eSpatrick 	     uint32_t *, uint32_t *);
77e2612299Spatrick int	 bwfm_chip_cr4_set_active(struct bwfm_softc *, uint32_t);
7832b2494eSpatrick void	 bwfm_chip_cr4_set_passive(struct bwfm_softc *);
79e2612299Spatrick int	 bwfm_chip_ca7_set_active(struct bwfm_softc *, uint32_t);
8032b2494eSpatrick void	 bwfm_chip_ca7_set_passive(struct bwfm_softc *);
81e2612299Spatrick int	 bwfm_chip_cm3_set_active(struct bwfm_softc *);
8232b2494eSpatrick void	 bwfm_chip_cm3_set_passive(struct bwfm_softc *);
8385d32364Spatrick void	 bwfm_chip_socram_ramsize(struct bwfm_softc *, struct bwfm_core *);
8485d32364Spatrick void	 bwfm_chip_sysmem_ramsize(struct bwfm_softc *, struct bwfm_core *);
8585d32364Spatrick void	 bwfm_chip_tcm_ramsize(struct bwfm_softc *, struct bwfm_core *);
8685d32364Spatrick void	 bwfm_chip_tcm_rambase(struct bwfm_softc *);
8732b2494eSpatrick 
8832b2494eSpatrick int	 bwfm_proto_bcdc_query_dcmd(struct bwfm_softc *, int,
8932b2494eSpatrick 	     int, char *, size_t *);
9032b2494eSpatrick int	 bwfm_proto_bcdc_set_dcmd(struct bwfm_softc *, int,
9132b2494eSpatrick 	     int, char *, size_t);
9232b2494eSpatrick 
9332b2494eSpatrick int	 bwfm_fwvar_cmd_get_data(struct bwfm_softc *, int, void *, size_t);
9432b2494eSpatrick int	 bwfm_fwvar_cmd_set_data(struct bwfm_softc *, int, void *, size_t);
9532b2494eSpatrick int	 bwfm_fwvar_cmd_get_int(struct bwfm_softc *, int, uint32_t *);
9632b2494eSpatrick int	 bwfm_fwvar_cmd_set_int(struct bwfm_softc *, int, uint32_t);
9732b2494eSpatrick int	 bwfm_fwvar_var_get_data(struct bwfm_softc *, char *, void *, size_t);
9832b2494eSpatrick int	 bwfm_fwvar_var_set_data(struct bwfm_softc *, char *, void *, size_t);
9932b2494eSpatrick int	 bwfm_fwvar_var_get_int(struct bwfm_softc *, char *, uint32_t *);
10032b2494eSpatrick int	 bwfm_fwvar_var_set_int(struct bwfm_softc *, char *, uint32_t);
10132b2494eSpatrick 
1028b2458cfSpatrick uint32_t bwfm_spec2chan(struct bwfm_softc *, uint32_t);
1038b2458cfSpatrick uint32_t bwfm_spec2chan_d11n(struct bwfm_softc *, uint32_t);
1048b2458cfSpatrick uint32_t bwfm_spec2chan_d11ac(struct bwfm_softc *, uint32_t);
1058b2458cfSpatrick 
10663498aa8Spatrick void	 bwfm_connect(struct bwfm_softc *);
107c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
108c11618f6Spatrick void	 bwfm_hostap(struct bwfm_softc *);
109c11618f6Spatrick #endif
11032b2494eSpatrick void	 bwfm_scan(struct bwfm_softc *);
11163498aa8Spatrick 
11263498aa8Spatrick void	 bwfm_task(void *);
11363498aa8Spatrick void	 bwfm_do_async(struct bwfm_softc *,
11463498aa8Spatrick 	     void (*)(struct bwfm_softc *, void *), void *, int);
11563498aa8Spatrick 
11663498aa8Spatrick int	 bwfm_set_key(struct ieee80211com *, struct ieee80211_node *,
11763498aa8Spatrick 	     struct ieee80211_key *);
11863498aa8Spatrick void	 bwfm_delete_key(struct ieee80211com *, struct ieee80211_node *,
11963498aa8Spatrick 	     struct ieee80211_key *);
12063498aa8Spatrick int	 bwfm_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
12163498aa8Spatrick 	     int, int, int);
12263498aa8Spatrick int	 bwfm_newstate(struct ieee80211com *, enum ieee80211_state, int);
12363498aa8Spatrick 
12463498aa8Spatrick void	 bwfm_set_key_cb(struct bwfm_softc *, void *);
12563498aa8Spatrick void	 bwfm_delete_key_cb(struct bwfm_softc *, void *);
126f4e8af02Spatrick void	 bwfm_rx_event_cb(struct bwfm_softc *, void *);
12732b2494eSpatrick 
128c11618f6Spatrick struct mbuf *bwfm_newbuf(void);
129f37fc236Spatrick void	 bwfm_rx(struct bwfm_softc *, struct mbuf *);
130c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
131c11618f6Spatrick void	 bwfm_rx_auth_ind(struct bwfm_softc *, struct bwfm_event *, size_t);
132c11618f6Spatrick void	 bwfm_rx_assoc_ind(struct bwfm_softc *, struct bwfm_event *, size_t, int);
133c11618f6Spatrick void	 bwfm_rx_deauth_ind(struct bwfm_softc *, struct bwfm_event *, size_t);
134c11618f6Spatrick void	 bwfm_rx_disassoc_ind(struct bwfm_softc *, struct bwfm_event *, size_t);
135c11618f6Spatrick void	 bwfm_rx_leave_ind(struct bwfm_softc *, struct bwfm_event *, size_t, int);
136c11618f6Spatrick #endif
137f4e8af02Spatrick void	 bwfm_rx_event(struct bwfm_softc *, struct mbuf *);
13832b2494eSpatrick void	 bwfm_scan_node(struct bwfm_softc *, struct bwfm_bss_info *, size_t);
13932b2494eSpatrick 
14032b2494eSpatrick extern void ieee80211_node2req(struct ieee80211com *,
14132b2494eSpatrick 	     const struct ieee80211_node *, struct ieee80211_nodereq *);
14232b2494eSpatrick extern void ieee80211_req2node(struct ieee80211com *,
14332b2494eSpatrick 	     const struct ieee80211_nodereq *, struct ieee80211_node *);
14432b2494eSpatrick 
14532b2494eSpatrick uint8_t bwfm_2ghz_channels[] = {
14632b2494eSpatrick 	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14732b2494eSpatrick };
14832b2494eSpatrick uint8_t bwfm_5ghz_channels[] = {
14932b2494eSpatrick 	34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64, 100, 104, 108, 112,
15032b2494eSpatrick 	116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165,
15132b2494eSpatrick };
15232b2494eSpatrick 
15332b2494eSpatrick struct bwfm_proto_ops bwfm_proto_bcdc_ops = {
15432b2494eSpatrick 	.proto_query_dcmd = bwfm_proto_bcdc_query_dcmd,
15532b2494eSpatrick 	.proto_set_dcmd = bwfm_proto_bcdc_set_dcmd,
15632b2494eSpatrick };
15732b2494eSpatrick 
15832b2494eSpatrick struct cfdriver bwfm_cd = {
15932b2494eSpatrick 	NULL, "bwfm", DV_IFNET
16032b2494eSpatrick };
16132b2494eSpatrick 
16232b2494eSpatrick void
16332b2494eSpatrick bwfm_attach(struct bwfm_softc *sc)
16432b2494eSpatrick {
16532b2494eSpatrick 	struct ieee80211com *ic = &sc->sc_ic;
16632b2494eSpatrick 	struct ifnet *ifp = &ic->ic_if;
1679c7e455cSpatrick 	uint32_t bandlist[3], tmp;
16833e6a401Spatrick 	int i, j, nbands, nmode, vhtmode;
16932b2494eSpatrick 
17032b2494eSpatrick 	if (bwfm_fwvar_cmd_get_int(sc, BWFM_C_GET_VERSION, &tmp)) {
17132b2494eSpatrick 		printf("%s: could not read io type\n", DEVNAME(sc));
17232b2494eSpatrick 		return;
17332b2494eSpatrick 	} else
17432b2494eSpatrick 		sc->sc_io_type = tmp;
17532b2494eSpatrick 	if (bwfm_fwvar_var_get_data(sc, "cur_etheraddr", ic->ic_myaddr,
17632b2494eSpatrick 	    sizeof(ic->ic_myaddr))) {
17732b2494eSpatrick 		printf("%s: could not read mac address\n", DEVNAME(sc));
17832b2494eSpatrick 		return;
17932b2494eSpatrick 	}
18032b2494eSpatrick 	printf("%s: address %s\n", DEVNAME(sc), ether_sprintf(ic->ic_myaddr));
18132b2494eSpatrick 
18263498aa8Spatrick 	/* Init host async commands ring. */
18363498aa8Spatrick 	sc->sc_cmdq.cur = sc->sc_cmdq.next = sc->sc_cmdq.queued = 0;
18463498aa8Spatrick 	sc->sc_taskq = taskq_create(DEVNAME(sc), 1, IPL_SOFTNET, 0);
18563498aa8Spatrick 	task_set(&sc->sc_task, bwfm_task, sc);
18632b2494eSpatrick 
18763498aa8Spatrick 	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
18863498aa8Spatrick 	ic->ic_opmode = IEEE80211_M_STA;	/* default to BSS mode */
18963498aa8Spatrick 	ic->ic_state = IEEE80211_S_INIT;
19063498aa8Spatrick 
19163498aa8Spatrick 	ic->ic_caps =
192c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
193c11618f6Spatrick 	    IEEE80211_C_HOSTAP |	/* Access Point */
194c11618f6Spatrick #endif
19563498aa8Spatrick 	    IEEE80211_C_RSN | 		/* WPA/RSN */
19663498aa8Spatrick 	    IEEE80211_C_SCANALL |	/* device scans all channels at once */
19763498aa8Spatrick 	    IEEE80211_C_SCANALLBAND;	/* device scans all bands at once */
19827fa8129Spatrick 
1999c7e455cSpatrick 	if (bwfm_fwvar_var_get_int(sc, "nmode", &nmode))
2009c7e455cSpatrick 		nmode = 0;
2019c7e455cSpatrick 	if (bwfm_fwvar_var_get_int(sc, "vhtmode", &vhtmode))
2029c7e455cSpatrick 		vhtmode = 0;
2039c7e455cSpatrick 	if (bwfm_fwvar_cmd_get_data(sc, BWFM_C_GET_BANDLIST, bandlist,
2049c7e455cSpatrick 	    sizeof(bandlist))) {
2059c7e455cSpatrick 		printf("%s: couldn't get supported band list\n", DEVNAME(sc));
2069c7e455cSpatrick 		return;
2079c7e455cSpatrick 	}
2089c7e455cSpatrick 	nbands = letoh32(bandlist[0]);
2099c7e455cSpatrick 	for (i = 1; i <= nbands && i < nitems(bandlist); i++) {
2109c7e455cSpatrick 		switch (letoh32(bandlist[i])) {
2119c7e455cSpatrick 		case BWFM_BAND_2G:
2129c7e455cSpatrick 			DPRINTF(("%s: 2G HT %d VHT %d\n",
2139c7e455cSpatrick 			    DEVNAME(sc), nmode, vhtmode));
2149c7e455cSpatrick 			ic->ic_sup_rates[IEEE80211_MODE_11B] =
2159c7e455cSpatrick 			    ieee80211_std_rateset_11b;
2169c7e455cSpatrick 			ic->ic_sup_rates[IEEE80211_MODE_11G] =
2179c7e455cSpatrick 			    ieee80211_std_rateset_11g;
21832b2494eSpatrick 
21933e6a401Spatrick 			for (j = 0; j < nitems(bwfm_2ghz_channels); j++) {
22033e6a401Spatrick 				uint8_t chan = bwfm_2ghz_channels[j];
22132b2494eSpatrick 				ic->ic_channels[chan].ic_freq =
22232b2494eSpatrick 				    ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ);
22332b2494eSpatrick 				ic->ic_channels[chan].ic_flags =
22432b2494eSpatrick 				    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
22532b2494eSpatrick 				    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
2269c7e455cSpatrick 				if (nmode)
2279c7e455cSpatrick 					ic->ic_channels[chan].ic_flags |=
2289c7e455cSpatrick 					    IEEE80211_CHAN_HT;
22932b2494eSpatrick 			}
2309c7e455cSpatrick 			break;
2319c7e455cSpatrick 		case BWFM_BAND_5G:
2329c7e455cSpatrick 			DPRINTF(("%s: 5G HT %d VHT %d\n",
2339c7e455cSpatrick 			    DEVNAME(sc), nmode, vhtmode));
2349c7e455cSpatrick 			ic->ic_sup_rates[IEEE80211_MODE_11A] =
2359c7e455cSpatrick 			    ieee80211_std_rateset_11a;
2369c7e455cSpatrick 
23733e6a401Spatrick 			for (j = 0; j < nitems(bwfm_5ghz_channels); j++) {
23833e6a401Spatrick 				uint8_t chan = bwfm_5ghz_channels[j];
23932b2494eSpatrick 				ic->ic_channels[chan].ic_freq =
24032b2494eSpatrick 				    ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ);
24132b2494eSpatrick 				ic->ic_channels[chan].ic_flags =
24232b2494eSpatrick 				    IEEE80211_CHAN_A;
2439c7e455cSpatrick 				if (nmode)
2449c7e455cSpatrick 					ic->ic_channels[chan].ic_flags |=
2459c7e455cSpatrick 					    IEEE80211_CHAN_HT;
24632b2494eSpatrick 			}
2479c7e455cSpatrick 			break;
2489c7e455cSpatrick 		default:
2499c7e455cSpatrick 			printf("%s: unsupported band 0x%x\n", DEVNAME(sc),
2509c7e455cSpatrick 			    letoh32(bandlist[i]));
2519c7e455cSpatrick 			break;
2529c7e455cSpatrick 		}
2539c7e455cSpatrick 	}
2549c7e455cSpatrick 
2559c7e455cSpatrick 	/* IBSS channel undefined for now. */
2569c7e455cSpatrick 	ic->ic_ibss_chan = &ic->ic_channels[0];
25732b2494eSpatrick 
25832b2494eSpatrick 	ifp->if_softc = sc;
25932b2494eSpatrick 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
26032b2494eSpatrick 	ifp->if_ioctl = bwfm_ioctl;
26132b2494eSpatrick 	ifp->if_start = bwfm_start;
26232b2494eSpatrick 	ifp->if_watchdog = bwfm_watchdog;
26332b2494eSpatrick 	memcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ);
26432b2494eSpatrick 
26532b2494eSpatrick 	if_attach(ifp);
26663498aa8Spatrick 	ieee80211_ifattach(ifp);
26763498aa8Spatrick 
26863498aa8Spatrick 	sc->sc_newstate = ic->ic_newstate;
26963498aa8Spatrick 	ic->ic_newstate = bwfm_newstate;
27063498aa8Spatrick 	ic->ic_send_mgmt = bwfm_send_mgmt;
27163498aa8Spatrick 	ic->ic_set_key = bwfm_set_key;
27263498aa8Spatrick 	ic->ic_delete_key = bwfm_delete_key;
27363498aa8Spatrick 
27463498aa8Spatrick 	ieee80211_media_init(ifp, bwfm_media_change, ieee80211_media_status);
27532b2494eSpatrick }
27632b2494eSpatrick 
27732b2494eSpatrick int
27832b2494eSpatrick bwfm_detach(struct bwfm_softc *sc, int flags)
27932b2494eSpatrick {
28032b2494eSpatrick 	struct ieee80211com *ic = &sc->sc_ic;
28132b2494eSpatrick 	struct ifnet *ifp = &ic->ic_if;
28263498aa8Spatrick 	task_del(sc->sc_taskq, &sc->sc_task);
28363498aa8Spatrick 	taskq_destroy(sc->sc_taskq);
28463498aa8Spatrick 	ieee80211_ifdetach(ifp);
28532b2494eSpatrick 	if_detach(ifp);
28632b2494eSpatrick 	return 0;
28732b2494eSpatrick }
28832b2494eSpatrick 
28932b2494eSpatrick void
29032b2494eSpatrick bwfm_start(struct ifnet *ifp)
29132b2494eSpatrick {
29232b2494eSpatrick 	struct bwfm_softc *sc = ifp->if_softc;
29332b2494eSpatrick 	struct mbuf *m;
29432b2494eSpatrick 	int error;
29532b2494eSpatrick 
29632b2494eSpatrick 	if (!(ifp->if_flags & IFF_RUNNING))
29732b2494eSpatrick 		return;
29832b2494eSpatrick 	if (ifq_is_oactive(&ifp->if_snd))
29932b2494eSpatrick 		return;
30032b2494eSpatrick 	if (IFQ_IS_EMPTY(&ifp->if_snd))
30132b2494eSpatrick 		return;
30232b2494eSpatrick 
30332b2494eSpatrick 	/* TODO: return if no link? */
30432b2494eSpatrick 
30532b2494eSpatrick 	m = ifq_deq_begin(&ifp->if_snd);
30632b2494eSpatrick 	while (m != NULL) {
30732b2494eSpatrick 		error = sc->sc_bus_ops->bs_txdata(sc, m);
30832b2494eSpatrick 		if (error == ENOBUFS) {
30932b2494eSpatrick 			ifq_deq_rollback(&ifp->if_snd, m);
31032b2494eSpatrick 			ifq_set_oactive(&ifp->if_snd);
31132b2494eSpatrick 			break;
31232b2494eSpatrick 		}
31332b2494eSpatrick 		if (error == EFBIG) {
31432b2494eSpatrick 			ifq_deq_commit(&ifp->if_snd, m);
31532b2494eSpatrick 			m_freem(m); /* give up: drop it */
31632b2494eSpatrick 			ifp->if_oerrors++;
31732b2494eSpatrick 			continue;
31832b2494eSpatrick 		}
31932b2494eSpatrick 
32032b2494eSpatrick 		/* Now we are committed to transmit the packet. */
32132b2494eSpatrick 		ifq_deq_commit(&ifp->if_snd, m);
32232b2494eSpatrick 
32332b2494eSpatrick #if NBPFILTER > 0
32432b2494eSpatrick 		if (ifp->if_bpf)
32532b2494eSpatrick 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
32632b2494eSpatrick #endif
32732b2494eSpatrick 
32832b2494eSpatrick 		m = ifq_deq_begin(&ifp->if_snd);
32932b2494eSpatrick 	}
33032b2494eSpatrick }
33132b2494eSpatrick 
33232b2494eSpatrick void
33332b2494eSpatrick bwfm_init(struct ifnet *ifp)
33432b2494eSpatrick {
33532b2494eSpatrick 	struct bwfm_softc *sc = ifp->if_softc;
336c11618f6Spatrick 	struct ieee80211com *ic = &sc->sc_ic;
33732b2494eSpatrick 	uint8_t evmask[BWFM_EVENT_MASK_LEN];
3381179aa9cSpatrick 	struct bwfm_join_pref_params join_pref[2];
339c11618f6Spatrick 	int pm;
34032b2494eSpatrick 
341a2c6ff8bSpatrick 	if (sc->sc_bus_ops->bs_init)
342a2c6ff8bSpatrick 		sc->sc_bus_ops->bs_init(sc);
343a2c6ff8bSpatrick 
34432b2494eSpatrick 	if (bwfm_fwvar_var_set_int(sc, "mpc", 1)) {
34532b2494eSpatrick 		printf("%s: could not set mpc\n", DEVNAME(sc));
34632b2494eSpatrick 		return;
34732b2494eSpatrick 	}
34832b2494eSpatrick 
3491179aa9cSpatrick 	/* Select target by RSSI (boost on 5GHz) */
3501179aa9cSpatrick 	join_pref[0].type = BWFM_JOIN_PREF_RSSI_DELTA;
3511179aa9cSpatrick 	join_pref[0].len = 2;
3521179aa9cSpatrick 	join_pref[0].rssi_gain = BWFM_JOIN_PREF_RSSI_BOOST;
3531179aa9cSpatrick 	join_pref[0].band = BWFM_JOIN_PREF_BAND_5G;
3541179aa9cSpatrick 	join_pref[1].type = BWFM_JOIN_PREF_RSSI;
3551179aa9cSpatrick 	join_pref[1].len = 2;
3561179aa9cSpatrick 	join_pref[1].rssi_gain = 0;
3571179aa9cSpatrick 	join_pref[1].band = 0;
3581179aa9cSpatrick 	if (bwfm_fwvar_var_set_data(sc, "join_pref", join_pref,
3591179aa9cSpatrick 	    sizeof(join_pref))) {
3601179aa9cSpatrick 		printf("%s: could not set join pref\n", DEVNAME(sc));
3611179aa9cSpatrick 		return;
3621179aa9cSpatrick 	}
36332b2494eSpatrick 
364c11618f6Spatrick #define BWFM_EVENT(event) evmask[(event) / 8] |= 1 << ((event) % 8)
365c11618f6Spatrick 	memset(evmask, 0, sizeof(evmask));
366c11618f6Spatrick 	switch (ic->ic_opmode) {
367c11618f6Spatrick 	case IEEE80211_M_STA:
368c11618f6Spatrick 		BWFM_EVENT(BWFM_E_IF);
369c11618f6Spatrick 		BWFM_EVENT(BWFM_E_LINK);
370c11618f6Spatrick 		BWFM_EVENT(BWFM_E_AUTH);
371c11618f6Spatrick 		BWFM_EVENT(BWFM_E_ASSOC);
372c11618f6Spatrick 		BWFM_EVENT(BWFM_E_DEAUTH);
373c11618f6Spatrick 		BWFM_EVENT(BWFM_E_DISASSOC);
374c11618f6Spatrick 		BWFM_EVENT(BWFM_E_SET_SSID);
375c11618f6Spatrick 		BWFM_EVENT(BWFM_E_ESCAN_RESULT);
376c11618f6Spatrick 		break;
377c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
378c11618f6Spatrick 	case IEEE80211_M_HOSTAP:
379c11618f6Spatrick 		BWFM_EVENT(BWFM_E_AUTH_IND);
380c11618f6Spatrick 		BWFM_EVENT(BWFM_E_ASSOC_IND);
381c11618f6Spatrick 		BWFM_EVENT(BWFM_E_REASSOC_IND);
382c11618f6Spatrick 		BWFM_EVENT(BWFM_E_DEAUTH_IND);
383c11618f6Spatrick 		BWFM_EVENT(BWFM_E_DISASSOC_IND);
384c11618f6Spatrick 		BWFM_EVENT(BWFM_E_SET_SSID);
385c11618f6Spatrick 		BWFM_EVENT(BWFM_E_ESCAN_RESULT);
386c11618f6Spatrick 		break;
387c11618f6Spatrick #endif
388c11618f6Spatrick 	default:
389c11618f6Spatrick 		break;
39032b2494eSpatrick 	}
391c11618f6Spatrick #undef BWFM_EVENT
392c11618f6Spatrick 
39332b2494eSpatrick 	if (bwfm_fwvar_var_set_data(sc, "event_msgs", evmask, sizeof(evmask))) {
39432b2494eSpatrick 		printf("%s: could not set event mask\n", DEVNAME(sc));
39532b2494eSpatrick 		return;
39632b2494eSpatrick 	}
39732b2494eSpatrick 
39832b2494eSpatrick 	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_CHANNEL_TIME,
39932b2494eSpatrick 	    BWFM_DEFAULT_SCAN_CHANNEL_TIME)) {
40032b2494eSpatrick 		printf("%s: could not set scan channel time\n", DEVNAME(sc));
40132b2494eSpatrick 		return;
40232b2494eSpatrick 	}
40332b2494eSpatrick 	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_UNASSOC_TIME,
40432b2494eSpatrick 	    BWFM_DEFAULT_SCAN_UNASSOC_TIME)) {
40532b2494eSpatrick 		printf("%s: could not set scan unassoc time\n", DEVNAME(sc));
40632b2494eSpatrick 		return;
40732b2494eSpatrick 	}
40832b2494eSpatrick 	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_PASSIVE_TIME,
40932b2494eSpatrick 	    BWFM_DEFAULT_SCAN_PASSIVE_TIME)) {
41032b2494eSpatrick 		printf("%s: could not set scan passive time\n", DEVNAME(sc));
41132b2494eSpatrick 		return;
41232b2494eSpatrick 	}
41332b2494eSpatrick 
414c11618f6Spatrick 	/*
415c11618f6Spatrick 	 * Use CAM (constantly awake) when we are running as AP,
416c11618f6Spatrick 	 * otherwise use fast power saving.
417c11618f6Spatrick 	 */
418c11618f6Spatrick 	pm = BWFM_PM_FAST_PS;
419c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
420c11618f6Spatrick 	if (ic->ic_opmode == IEEE80211_M_HOSTAP)
421c11618f6Spatrick 		pm = BWFM_PM_CAM;
422c11618f6Spatrick #endif
423c11618f6Spatrick 	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, pm)) {
42432b2494eSpatrick 		printf("%s: could not set power\n", DEVNAME(sc));
42532b2494eSpatrick 		return;
42632b2494eSpatrick 	}
42732b2494eSpatrick 
42832b2494eSpatrick 	bwfm_fwvar_var_set_int(sc, "txbf", 1);
42932b2494eSpatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_UP, 0);
43032b2494eSpatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_INFRA, 1);
43132b2494eSpatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_AP, 0);
43232b2494eSpatrick 
43332b2494eSpatrick 	/* Disable all offloading (ARP, NDP, TCP/UDP cksum). */
43432b2494eSpatrick 	bwfm_fwvar_var_set_int(sc, "arp_ol", 0);
43532b2494eSpatrick 	bwfm_fwvar_var_set_int(sc, "arpoe", 0);
43632b2494eSpatrick 	bwfm_fwvar_var_set_int(sc, "ndoe", 0);
43732b2494eSpatrick 	bwfm_fwvar_var_set_int(sc, "toe", 0);
43832b2494eSpatrick 
439d346a16fSpatrick 	/*
44063498aa8Spatrick 	 * The firmware supplicant can handle the WPA handshake for
44163498aa8Spatrick 	 * us, but we honestly want to do this ourselves, so disable
44263498aa8Spatrick 	 * the firmware supplicant and let our stack handle it.
443d346a16fSpatrick 	 */
44463498aa8Spatrick 	bwfm_fwvar_var_set_int(sc, "sup_wpa", 0);
44532b2494eSpatrick 
4469f68d685Spatrick #if 0
4479f68d685Spatrick 	/* TODO: set these on proper ioctl */
44832b2494eSpatrick 	bwfm_fwvar_var_set_int(sc, "allmulti", 1);
44932b2494eSpatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PROMISC, 1);
4509f68d685Spatrick #endif
45132b2494eSpatrick 
45232b2494eSpatrick 	ifp->if_flags |= IFF_RUNNING;
45332b2494eSpatrick 	ifq_clr_oactive(&ifp->if_snd);
45463498aa8Spatrick 
45563498aa8Spatrick 	ieee80211_begin_scan(ifp);
45632b2494eSpatrick }
45732b2494eSpatrick 
45832b2494eSpatrick void
45932b2494eSpatrick bwfm_stop(struct ifnet *ifp)
46032b2494eSpatrick {
46132b2494eSpatrick 	struct bwfm_softc *sc = ifp->if_softc;
46232b2494eSpatrick 	struct ieee80211com *ic = &sc->sc_ic;
46332b2494eSpatrick 
46432b2494eSpatrick 	sc->sc_tx_timer = 0;
46532b2494eSpatrick 	ifp->if_timer = 0;
46632b2494eSpatrick 	ifp->if_flags &= ~IFF_RUNNING;
46732b2494eSpatrick 	ifq_clr_oactive(&ifp->if_snd);
46832b2494eSpatrick 
46932b2494eSpatrick 	/* In case we were scanning, release the scan "lock". */
47032b2494eSpatrick 	ic->ic_scan_lock = IEEE80211_SCAN_UNLOCKED;
47132b2494eSpatrick 
47263498aa8Spatrick 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
47363498aa8Spatrick 
47432b2494eSpatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_DOWN, 1);
475c11618f6Spatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, BWFM_PM_CAM);
476a2c6ff8bSpatrick 
477a2c6ff8bSpatrick 	if (sc->sc_bus_ops->bs_stop)
478a2c6ff8bSpatrick 		sc->sc_bus_ops->bs_stop(sc);
47932b2494eSpatrick }
48032b2494eSpatrick 
48132b2494eSpatrick void
48232b2494eSpatrick bwfm_watchdog(struct ifnet *ifp)
48332b2494eSpatrick {
48432b2494eSpatrick 	struct bwfm_softc *sc = ifp->if_softc;
48532b2494eSpatrick 
48632b2494eSpatrick 	ifp->if_timer = 0;
48732b2494eSpatrick 
48832b2494eSpatrick 	if (sc->sc_tx_timer > 0) {
48932b2494eSpatrick 		if (--sc->sc_tx_timer == 0) {
49032b2494eSpatrick 			printf("%s: device timeout\n", DEVNAME(sc));
49132b2494eSpatrick 			ifp->if_oerrors++;
49232b2494eSpatrick 			return;
49332b2494eSpatrick 		}
49432b2494eSpatrick 		ifp->if_timer = 1;
49532b2494eSpatrick 	}
49632b2494eSpatrick 	ieee80211_watchdog(ifp);
49732b2494eSpatrick }
49832b2494eSpatrick 
49932b2494eSpatrick int
50032b2494eSpatrick bwfm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
50132b2494eSpatrick {
50263498aa8Spatrick 	int s, error = 0;
50332b2494eSpatrick 
50432b2494eSpatrick 	s = splnet();
50532b2494eSpatrick 	switch (cmd) {
50632b2494eSpatrick 	case SIOCSIFADDR:
50732b2494eSpatrick 		ifp->if_flags |= IFF_UP;
50863498aa8Spatrick 		/* FALLTHROUGH */
50932b2494eSpatrick 	case SIOCSIFFLAGS:
51032b2494eSpatrick 		if (ifp->if_flags & IFF_UP) {
51132b2494eSpatrick 			if (!(ifp->if_flags & IFF_RUNNING))
51232b2494eSpatrick 				bwfm_init(ifp);
51332b2494eSpatrick 		} else {
51432b2494eSpatrick 			if (ifp->if_flags & IFF_RUNNING)
51532b2494eSpatrick 				bwfm_stop(ifp);
51632b2494eSpatrick 		}
51732b2494eSpatrick 		break;
51832b2494eSpatrick 	default:
51963498aa8Spatrick 		error = ieee80211_ioctl(ifp, cmd, data);
52032b2494eSpatrick 	}
52132b2494eSpatrick 	if (error == ENETRESET) {
52232b2494eSpatrick 		if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
52332b2494eSpatrick 		    (IFF_UP | IFF_RUNNING)) {
52432b2494eSpatrick 			bwfm_stop(ifp);
52532b2494eSpatrick 			bwfm_init(ifp);
52632b2494eSpatrick 		}
52732b2494eSpatrick 		error = 0;
52832b2494eSpatrick 	}
52932b2494eSpatrick 	splx(s);
53032b2494eSpatrick 	return error;
53132b2494eSpatrick }
53232b2494eSpatrick 
53332b2494eSpatrick int
53432b2494eSpatrick bwfm_media_change(struct ifnet *ifp)
53532b2494eSpatrick {
53663498aa8Spatrick 	int error;
53732b2494eSpatrick 
53863498aa8Spatrick 	error = ieee80211_media_change(ifp);
53963498aa8Spatrick 	if (error != ENETRESET)
54063498aa8Spatrick 		return error;
54163498aa8Spatrick 
54263498aa8Spatrick 	if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
54363498aa8Spatrick 	    (IFF_UP | IFF_RUNNING)) {
54463498aa8Spatrick 		bwfm_stop(ifp);
54563498aa8Spatrick 		bwfm_init(ifp);
54663498aa8Spatrick 	}
54763498aa8Spatrick 	return 0;
54832b2494eSpatrick }
54932b2494eSpatrick 
55032b2494eSpatrick /* Chip initialization (SDIO, PCIe) */
55132b2494eSpatrick int
55232b2494eSpatrick bwfm_chip_attach(struct bwfm_softc *sc)
55332b2494eSpatrick {
55432b2494eSpatrick 	struct bwfm_core *core;
55532b2494eSpatrick 	int need_socram = 0;
55632b2494eSpatrick 	int has_socram = 0;
55732b2494eSpatrick 	int cpu_found = 0;
55832b2494eSpatrick 	uint32_t val;
55932b2494eSpatrick 
56032b2494eSpatrick 	LIST_INIT(&sc->sc_chip.ch_list);
56132b2494eSpatrick 
56232b2494eSpatrick 	if (sc->sc_buscore_ops->bc_prepare(sc) != 0) {
56332b2494eSpatrick 		printf("%s: failed buscore prepare\n", DEVNAME(sc));
56432b2494eSpatrick 		return 1;
56532b2494eSpatrick 	}
56632b2494eSpatrick 
56732b2494eSpatrick 	val = sc->sc_buscore_ops->bc_read(sc,
56832b2494eSpatrick 	    BWFM_CHIP_BASE + BWFM_CHIP_REG_CHIPID);
56932b2494eSpatrick 	sc->sc_chip.ch_chip = BWFM_CHIP_CHIPID_ID(val);
57032b2494eSpatrick 	sc->sc_chip.ch_chiprev = BWFM_CHIP_CHIPID_REV(val);
57132b2494eSpatrick 
57232b2494eSpatrick 	if ((sc->sc_chip.ch_chip > 0xa000) || (sc->sc_chip.ch_chip < 0x4000))
57332b2494eSpatrick 		snprintf(sc->sc_chip.ch_name, sizeof(sc->sc_chip.ch_name),
57432b2494eSpatrick 		    "%d", sc->sc_chip.ch_chip);
57532b2494eSpatrick 	else
57632b2494eSpatrick 		snprintf(sc->sc_chip.ch_name, sizeof(sc->sc_chip.ch_name),
57732b2494eSpatrick 		    "%x", sc->sc_chip.ch_chip);
57832b2494eSpatrick 
57932b2494eSpatrick 	switch (BWFM_CHIP_CHIPID_TYPE(val))
58032b2494eSpatrick 	{
58132b2494eSpatrick 	case BWFM_CHIP_CHIPID_TYPE_SOCI_SB:
58232b2494eSpatrick 		printf("%s: SoC interconnect SB not implemented\n",
58332b2494eSpatrick 		    DEVNAME(sc));
58432b2494eSpatrick 		return 1;
58532b2494eSpatrick 	case BWFM_CHIP_CHIPID_TYPE_SOCI_AI:
58632b2494eSpatrick 		sc->sc_chip.ch_core_isup = bwfm_chip_ai_isup;
58732b2494eSpatrick 		sc->sc_chip.ch_core_disable = bwfm_chip_ai_disable;
58832b2494eSpatrick 		sc->sc_chip.ch_core_reset = bwfm_chip_ai_reset;
58932b2494eSpatrick 		bwfm_chip_dmp_erom_scan(sc);
59032b2494eSpatrick 		break;
59132b2494eSpatrick 	default:
59232b2494eSpatrick 		printf("%s: SoC interconnect %d unknown\n",
59332b2494eSpatrick 		    DEVNAME(sc), BWFM_CHIP_CHIPID_TYPE(val));
59432b2494eSpatrick 		return 1;
59532b2494eSpatrick 	}
59632b2494eSpatrick 
59732b2494eSpatrick 	LIST_FOREACH(core, &sc->sc_chip.ch_list, co_link) {
59832b2494eSpatrick 		DPRINTF(("%s: 0x%x:%-2d base 0x%08x wrap 0x%08x\n",
59932b2494eSpatrick 		    DEVNAME(sc), core->co_id, core->co_rev,
60032b2494eSpatrick 		    core->co_base, core->co_wrapbase));
60132b2494eSpatrick 
60232b2494eSpatrick 		switch (core->co_id) {
60332b2494eSpatrick 		case BWFM_AGENT_CORE_ARM_CM3:
604de919281Spatrick 			need_socram = 1;
60532b2494eSpatrick 			/* FALLTHROUGH */
60632b2494eSpatrick 		case BWFM_AGENT_CORE_ARM_CR4:
60732b2494eSpatrick 		case BWFM_AGENT_CORE_ARM_CA7:
608de919281Spatrick 			cpu_found = 1;
60932b2494eSpatrick 			break;
61032b2494eSpatrick 		case BWFM_AGENT_INTERNAL_MEM:
611de919281Spatrick 			has_socram = 1;
61232b2494eSpatrick 			break;
61332b2494eSpatrick 		default:
61432b2494eSpatrick 			break;
61532b2494eSpatrick 		}
61632b2494eSpatrick 	}
61732b2494eSpatrick 
61832b2494eSpatrick 	if (!cpu_found) {
61932b2494eSpatrick 		printf("%s: CPU core not detected\n", DEVNAME(sc));
62032b2494eSpatrick 		return 1;
62132b2494eSpatrick 	}
62232b2494eSpatrick 	if (need_socram && !has_socram) {
62332b2494eSpatrick 		printf("%s: RAM core not provided\n", DEVNAME(sc));
62432b2494eSpatrick 		return 1;
62532b2494eSpatrick 	}
62632b2494eSpatrick 
627e2612299Spatrick 	bwfm_chip_set_passive(sc);
62832b2494eSpatrick 
62932b2494eSpatrick 	if (sc->sc_buscore_ops->bc_reset) {
63032b2494eSpatrick 		sc->sc_buscore_ops->bc_reset(sc);
631e2612299Spatrick 		bwfm_chip_set_passive(sc);
63232b2494eSpatrick 	}
63332b2494eSpatrick 
63485d32364Spatrick 	if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4)) != NULL) {
63585d32364Spatrick 		bwfm_chip_tcm_ramsize(sc, core);
63685d32364Spatrick 		bwfm_chip_tcm_rambase(sc);
63785d32364Spatrick 	} else if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_SYS_MEM)) != NULL) {
63885d32364Spatrick 		bwfm_chip_sysmem_ramsize(sc, core);
63985d32364Spatrick 		bwfm_chip_tcm_rambase(sc);
64085d32364Spatrick 	} else if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM)) != NULL) {
64185d32364Spatrick 		bwfm_chip_socram_ramsize(sc, core);
64285d32364Spatrick 	}
64332b2494eSpatrick 
64432b2494eSpatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
64532b2494eSpatrick 	sc->sc_chip.ch_cc_caps = sc->sc_buscore_ops->bc_read(sc,
64632b2494eSpatrick 	    core->co_base + BWFM_CHIP_REG_CAPABILITIES);
64732b2494eSpatrick 	sc->sc_chip.ch_cc_caps_ext = sc->sc_buscore_ops->bc_read(sc,
64832b2494eSpatrick 	    core->co_base + BWFM_CHIP_REG_CAPABILITIES_EXT);
64932b2494eSpatrick 
65032b2494eSpatrick 	core = bwfm_chip_get_pmu(sc);
65132b2494eSpatrick 	if (sc->sc_chip.ch_cc_caps & BWFM_CHIP_REG_CAPABILITIES_PMU) {
65232b2494eSpatrick 		sc->sc_chip.ch_pmucaps = sc->sc_buscore_ops->bc_read(sc,
65332b2494eSpatrick 		    core->co_base + BWFM_CHIP_REG_PMUCAPABILITIES);
65432b2494eSpatrick 		sc->sc_chip.ch_pmurev = sc->sc_chip.ch_pmucaps &
65532b2494eSpatrick 		    BWFM_CHIP_REG_PMUCAPABILITIES_REV_MASK;
65632b2494eSpatrick 	}
65732b2494eSpatrick 
65832b2494eSpatrick 	if (sc->sc_buscore_ops->bc_setup)
65932b2494eSpatrick 		sc->sc_buscore_ops->bc_setup(sc);
66032b2494eSpatrick 
66132b2494eSpatrick 	return 0;
66232b2494eSpatrick }
66332b2494eSpatrick 
66432b2494eSpatrick struct bwfm_core *
66532b2494eSpatrick bwfm_chip_get_core(struct bwfm_softc *sc, int id)
66632b2494eSpatrick {
66732b2494eSpatrick 	struct bwfm_core *core;
66832b2494eSpatrick 
66932b2494eSpatrick 	LIST_FOREACH(core, &sc->sc_chip.ch_list, co_link) {
67032b2494eSpatrick 		if (core->co_id == id)
67132b2494eSpatrick 			return core;
67232b2494eSpatrick 	}
67332b2494eSpatrick 
67432b2494eSpatrick 	return NULL;
67532b2494eSpatrick }
67632b2494eSpatrick 
67732b2494eSpatrick struct bwfm_core *
67832b2494eSpatrick bwfm_chip_get_pmu(struct bwfm_softc *sc)
67932b2494eSpatrick {
68032b2494eSpatrick 	struct bwfm_core *cc, *pmu;
68132b2494eSpatrick 
68232b2494eSpatrick 	cc = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
68332b2494eSpatrick 	if (cc->co_rev >= 35 && sc->sc_chip.ch_cc_caps_ext &
68432b2494eSpatrick 	    BWFM_CHIP_REG_CAPABILITIES_EXT_AOB_PRESENT) {
68532b2494eSpatrick 		pmu = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_PMU);
68632b2494eSpatrick 		if (pmu)
68732b2494eSpatrick 			return pmu;
68832b2494eSpatrick 	}
68932b2494eSpatrick 
69032b2494eSpatrick 	return cc;
69132b2494eSpatrick }
69232b2494eSpatrick 
69332b2494eSpatrick /* Functions for the AI interconnect */
69432b2494eSpatrick int
69532b2494eSpatrick bwfm_chip_ai_isup(struct bwfm_softc *sc, struct bwfm_core *core)
69632b2494eSpatrick {
69732b2494eSpatrick 	uint32_t ioctl, reset;
69832b2494eSpatrick 
69932b2494eSpatrick 	ioctl = sc->sc_buscore_ops->bc_read(sc,
70032b2494eSpatrick 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
70132b2494eSpatrick 	reset = sc->sc_buscore_ops->bc_read(sc,
70232b2494eSpatrick 	    core->co_wrapbase + BWFM_AGENT_RESET_CTL);
70332b2494eSpatrick 
70432b2494eSpatrick 	if (((ioctl & (BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK)) ==
70532b2494eSpatrick 	    BWFM_AGENT_IOCTL_CLK) &&
70632b2494eSpatrick 	    ((reset & BWFM_AGENT_RESET_CTL_RESET) == 0))
70732b2494eSpatrick 		return 1;
70832b2494eSpatrick 
70932b2494eSpatrick 	return 0;
71032b2494eSpatrick }
71132b2494eSpatrick 
71232b2494eSpatrick void
71332b2494eSpatrick bwfm_chip_ai_disable(struct bwfm_softc *sc, struct bwfm_core *core,
71432b2494eSpatrick     uint32_t prereset, uint32_t reset)
71532b2494eSpatrick {
71632b2494eSpatrick 	uint32_t val;
71732b2494eSpatrick 	int i;
71832b2494eSpatrick 
71932b2494eSpatrick 	val = sc->sc_buscore_ops->bc_read(sc,
72032b2494eSpatrick 	    core->co_wrapbase + BWFM_AGENT_RESET_CTL);
72132b2494eSpatrick 	if ((val & BWFM_AGENT_RESET_CTL_RESET) == 0) {
72232b2494eSpatrick 
72332b2494eSpatrick 		sc->sc_buscore_ops->bc_write(sc,
72432b2494eSpatrick 		    core->co_wrapbase + BWFM_AGENT_IOCTL,
72532b2494eSpatrick 		    prereset | BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK);
72632b2494eSpatrick 		sc->sc_buscore_ops->bc_read(sc,
72732b2494eSpatrick 		    core->co_wrapbase + BWFM_AGENT_IOCTL);
72832b2494eSpatrick 
72932b2494eSpatrick 		sc->sc_buscore_ops->bc_write(sc,
73032b2494eSpatrick 		    core->co_wrapbase + BWFM_AGENT_RESET_CTL,
73132b2494eSpatrick 		    BWFM_AGENT_RESET_CTL_RESET);
73232b2494eSpatrick 		delay(20);
73332b2494eSpatrick 
73432b2494eSpatrick 		for (i = 300; i > 0; i--) {
73532b2494eSpatrick 			if (sc->sc_buscore_ops->bc_read(sc,
73632b2494eSpatrick 			    core->co_wrapbase + BWFM_AGENT_RESET_CTL) ==
73732b2494eSpatrick 			    BWFM_AGENT_RESET_CTL_RESET)
73832b2494eSpatrick 				break;
73932b2494eSpatrick 		}
74032b2494eSpatrick 		if (i == 0)
74132b2494eSpatrick 			printf("%s: timeout on core reset\n", DEVNAME(sc));
74232b2494eSpatrick 	}
74332b2494eSpatrick 
74432b2494eSpatrick 	sc->sc_buscore_ops->bc_write(sc,
74532b2494eSpatrick 	    core->co_wrapbase + BWFM_AGENT_IOCTL,
74632b2494eSpatrick 	    reset | BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK);
74732b2494eSpatrick 	sc->sc_buscore_ops->bc_read(sc,
74832b2494eSpatrick 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
74932b2494eSpatrick }
75032b2494eSpatrick 
75132b2494eSpatrick void
75232b2494eSpatrick bwfm_chip_ai_reset(struct bwfm_softc *sc, struct bwfm_core *core,
75332b2494eSpatrick     uint32_t prereset, uint32_t reset, uint32_t postreset)
75432b2494eSpatrick {
75532b2494eSpatrick 	int i;
75632b2494eSpatrick 
75732b2494eSpatrick 	bwfm_chip_ai_disable(sc, core, prereset, reset);
75832b2494eSpatrick 
75932b2494eSpatrick 	for (i = 50; i > 0; i--) {
76032b2494eSpatrick 		if ((sc->sc_buscore_ops->bc_read(sc,
76132b2494eSpatrick 		    core->co_wrapbase + BWFM_AGENT_RESET_CTL) &
76232b2494eSpatrick 		    BWFM_AGENT_RESET_CTL_RESET) == 0)
76332b2494eSpatrick 			break;
76432b2494eSpatrick 		sc->sc_buscore_ops->bc_write(sc,
76532b2494eSpatrick 		    core->co_wrapbase + BWFM_AGENT_RESET_CTL, 0);
76632b2494eSpatrick 		delay(60);
76732b2494eSpatrick 	}
76832b2494eSpatrick 	if (i == 0)
76932b2494eSpatrick 		printf("%s: timeout on core reset\n", DEVNAME(sc));
77032b2494eSpatrick 
77132b2494eSpatrick 	sc->sc_buscore_ops->bc_write(sc,
77232b2494eSpatrick 	    core->co_wrapbase + BWFM_AGENT_IOCTL,
77332b2494eSpatrick 	    postreset | BWFM_AGENT_IOCTL_CLK);
77432b2494eSpatrick 	sc->sc_buscore_ops->bc_read(sc,
77532b2494eSpatrick 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
77632b2494eSpatrick }
77732b2494eSpatrick 
77832b2494eSpatrick void
77932b2494eSpatrick bwfm_chip_dmp_erom_scan(struct bwfm_softc *sc)
78032b2494eSpatrick {
78132b2494eSpatrick 	uint32_t erom, val, base, wrap;
78232b2494eSpatrick 	uint8_t type = 0;
78332b2494eSpatrick 	uint16_t id;
78432b2494eSpatrick 	uint8_t nmw, nsw, rev;
78532b2494eSpatrick 	struct bwfm_core *core;
78632b2494eSpatrick 
78732b2494eSpatrick 	erom = sc->sc_buscore_ops->bc_read(sc,
78832b2494eSpatrick 	    BWFM_CHIP_BASE + BWFM_CHIP_REG_EROMPTR);
78932b2494eSpatrick 	while (type != BWFM_DMP_DESC_EOT) {
79032b2494eSpatrick 		val = sc->sc_buscore_ops->bc_read(sc, erom);
79132b2494eSpatrick 		type = val & BWFM_DMP_DESC_MASK;
79232b2494eSpatrick 		erom += 4;
79332b2494eSpatrick 
79432b2494eSpatrick 		if (type != BWFM_DMP_DESC_COMPONENT)
79532b2494eSpatrick 			continue;
79632b2494eSpatrick 
79732b2494eSpatrick 		id = (val & BWFM_DMP_COMP_PARTNUM)
79832b2494eSpatrick 		    >> BWFM_DMP_COMP_PARTNUM_S;
79932b2494eSpatrick 
80032b2494eSpatrick 		val = sc->sc_buscore_ops->bc_read(sc, erom);
80132b2494eSpatrick 		type = val & BWFM_DMP_DESC_MASK;
80232b2494eSpatrick 		erom += 4;
80332b2494eSpatrick 
80432b2494eSpatrick 		if (type != BWFM_DMP_DESC_COMPONENT) {
80532b2494eSpatrick 			printf("%s: not component descriptor\n", DEVNAME(sc));
80632b2494eSpatrick 			return;
80732b2494eSpatrick 		}
80832b2494eSpatrick 
80932b2494eSpatrick 		nmw = (val & BWFM_DMP_COMP_NUM_MWRAP)
81032b2494eSpatrick 		    >> BWFM_DMP_COMP_NUM_MWRAP_S;
81132b2494eSpatrick 		nsw = (val & BWFM_DMP_COMP_NUM_SWRAP)
81232b2494eSpatrick 		    >> BWFM_DMP_COMP_NUM_SWRAP_S;
81332b2494eSpatrick 		rev = (val & BWFM_DMP_COMP_REVISION)
81432b2494eSpatrick 		    >> BWFM_DMP_COMP_REVISION_S;
81532b2494eSpatrick 
81632b2494eSpatrick 		if (nmw + nsw == 0 && id != BWFM_AGENT_CORE_PMU)
81732b2494eSpatrick 			continue;
81832b2494eSpatrick 
81932b2494eSpatrick 		if (bwfm_chip_dmp_get_regaddr(sc, &erom, &base, &wrap))
82032b2494eSpatrick 			continue;
82132b2494eSpatrick 
82232b2494eSpatrick 		core = malloc(sizeof(*core), M_DEVBUF, M_WAITOK);
82332b2494eSpatrick 		core->co_id = id;
82432b2494eSpatrick 		core->co_base = base;
82532b2494eSpatrick 		core->co_wrapbase = wrap;
82632b2494eSpatrick 		core->co_rev = rev;
82732b2494eSpatrick 		LIST_INSERT_HEAD(&sc->sc_chip.ch_list, core, co_link);
82832b2494eSpatrick 	}
82932b2494eSpatrick }
83032b2494eSpatrick 
83132b2494eSpatrick int
83232b2494eSpatrick bwfm_chip_dmp_get_regaddr(struct bwfm_softc *sc, uint32_t *erom,
83332b2494eSpatrick     uint32_t *base, uint32_t *wrap)
83432b2494eSpatrick {
83532b2494eSpatrick 	uint8_t type = 0, mpnum = 0;
83632b2494eSpatrick 	uint8_t stype, sztype, wraptype;
83732b2494eSpatrick 	uint32_t val;
83832b2494eSpatrick 
83932b2494eSpatrick 	*base = 0;
84032b2494eSpatrick 	*wrap = 0;
84132b2494eSpatrick 
84232b2494eSpatrick 	val = sc->sc_buscore_ops->bc_read(sc, *erom);
84332b2494eSpatrick 	type = val & BWFM_DMP_DESC_MASK;
84432b2494eSpatrick 	if (type == BWFM_DMP_DESC_MASTER_PORT) {
84532b2494eSpatrick 		mpnum = (val & BWFM_DMP_MASTER_PORT_NUM)
84632b2494eSpatrick 		    >> BWFM_DMP_MASTER_PORT_NUM_S;
84732b2494eSpatrick 		wraptype = BWFM_DMP_SLAVE_TYPE_MWRAP;
84832b2494eSpatrick 		*erom += 4;
84932b2494eSpatrick 	} else if ((type & ~BWFM_DMP_DESC_ADDRSIZE_GT32) ==
85032b2494eSpatrick 	    BWFM_DMP_DESC_ADDRESS)
85132b2494eSpatrick 		wraptype = BWFM_DMP_SLAVE_TYPE_SWRAP;
85232b2494eSpatrick 	else
85332b2494eSpatrick 		return 1;
85432b2494eSpatrick 
85532b2494eSpatrick 	do {
85632b2494eSpatrick 		do {
85732b2494eSpatrick 			val = sc->sc_buscore_ops->bc_read(sc, *erom);
85832b2494eSpatrick 			type = val & BWFM_DMP_DESC_MASK;
85932b2494eSpatrick 			if (type == BWFM_DMP_DESC_COMPONENT)
86032b2494eSpatrick 				return 0;
86132b2494eSpatrick 			if (type == BWFM_DMP_DESC_EOT)
86232b2494eSpatrick 				return 1;
86332b2494eSpatrick 			*erom += 4;
86432b2494eSpatrick 		} while ((type & ~BWFM_DMP_DESC_ADDRSIZE_GT32) !=
86532b2494eSpatrick 		     BWFM_DMP_DESC_ADDRESS);
86632b2494eSpatrick 
86732b2494eSpatrick 		if (type & BWFM_DMP_DESC_ADDRSIZE_GT32)
86832b2494eSpatrick 			*erom += 4;
86932b2494eSpatrick 
87032b2494eSpatrick 		sztype = (val & BWFM_DMP_SLAVE_SIZE_TYPE)
87132b2494eSpatrick 		    >> BWFM_DMP_SLAVE_SIZE_TYPE_S;
87232b2494eSpatrick 		if (sztype == BWFM_DMP_SLAVE_SIZE_DESC) {
87332b2494eSpatrick 			val = sc->sc_buscore_ops->bc_read(sc, *erom);
87432b2494eSpatrick 			type = val & BWFM_DMP_DESC_MASK;
87532b2494eSpatrick 			if (type & BWFM_DMP_DESC_ADDRSIZE_GT32)
87632b2494eSpatrick 				*erom += 8;
87732b2494eSpatrick 			else
87832b2494eSpatrick 				*erom += 4;
87932b2494eSpatrick 		}
88032b2494eSpatrick 		if (sztype != BWFM_DMP_SLAVE_SIZE_4K)
88132b2494eSpatrick 			continue;
88232b2494eSpatrick 
88332b2494eSpatrick 		stype = (val & BWFM_DMP_SLAVE_TYPE) >> BWFM_DMP_SLAVE_TYPE_S;
88432b2494eSpatrick 		if (*base == 0 && stype == BWFM_DMP_SLAVE_TYPE_SLAVE)
88532b2494eSpatrick 			*base = val & BWFM_DMP_SLAVE_ADDR_BASE;
88632b2494eSpatrick 		if (*wrap == 0 && stype == wraptype)
88732b2494eSpatrick 			*wrap = val & BWFM_DMP_SLAVE_ADDR_BASE;
88832b2494eSpatrick 	} while (*base == 0 || *wrap == 0);
88932b2494eSpatrick 
89032b2494eSpatrick 	return 0;
89132b2494eSpatrick }
89232b2494eSpatrick 
89332b2494eSpatrick /* Core configuration */
894e2612299Spatrick int
895e2612299Spatrick bwfm_chip_set_active(struct bwfm_softc *sc, uint32_t rstvec)
896e2612299Spatrick {
897e2612299Spatrick 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL)
898e2612299Spatrick 		return bwfm_chip_cr4_set_active(sc, rstvec);
899e2612299Spatrick 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL)
900e2612299Spatrick 		return bwfm_chip_ca7_set_active(sc, rstvec);
901e2612299Spatrick 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL)
902e2612299Spatrick 		return bwfm_chip_cm3_set_active(sc);
903e2612299Spatrick 	return 1;
904e2612299Spatrick }
905e2612299Spatrick 
906e2612299Spatrick void
907e2612299Spatrick bwfm_chip_set_passive(struct bwfm_softc *sc)
908e2612299Spatrick {
909e2612299Spatrick 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL) {
910e2612299Spatrick 		bwfm_chip_cr4_set_passive(sc);
911e2612299Spatrick 		return;
912e2612299Spatrick 	}
913e2612299Spatrick 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL) {
914e2612299Spatrick 		bwfm_chip_ca7_set_passive(sc);
915e2612299Spatrick 		return;
916e2612299Spatrick 	}
917e2612299Spatrick 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL) {
918e2612299Spatrick 		bwfm_chip_cm3_set_passive(sc);
919e2612299Spatrick 		return;
920e2612299Spatrick 	}
921e2612299Spatrick }
922e2612299Spatrick 
923e2612299Spatrick int
924e2612299Spatrick bwfm_chip_cr4_set_active(struct bwfm_softc *sc, uint32_t rstvec)
925e2612299Spatrick {
926e2612299Spatrick 	struct bwfm_core *core;
927e2612299Spatrick 
928608f8b14Spatrick 	sc->sc_buscore_ops->bc_activate(sc, rstvec);
929e2612299Spatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4);
930e2612299Spatrick 	sc->sc_chip.ch_core_reset(sc, core,
931e2612299Spatrick 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT, 0, 0);
932e2612299Spatrick 
933e2612299Spatrick 	return 0;
934e2612299Spatrick }
935e2612299Spatrick 
93632b2494eSpatrick void
93732b2494eSpatrick bwfm_chip_cr4_set_passive(struct bwfm_softc *sc)
93832b2494eSpatrick {
939c6962397Spatrick 	struct bwfm_core *core;
9403e129b89Spatrick 	uint32_t val;
941c6962397Spatrick 
942c6962397Spatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4);
9433e129b89Spatrick 	val = sc->sc_buscore_ops->bc_read(sc,
9443e129b89Spatrick 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
9453e129b89Spatrick 	sc->sc_chip.ch_core_reset(sc, core,
9463e129b89Spatrick 	    val & BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
9473e129b89Spatrick 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
9483e129b89Spatrick 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT);
9493e129b89Spatrick 
950c6962397Spatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_80211);
951c6962397Spatrick 	sc->sc_chip.ch_core_reset(sc, core, BWFM_AGENT_D11_IOCTL_PHYRESET |
952c6962397Spatrick 	    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN, BWFM_AGENT_D11_IOCTL_PHYCLOCKEN,
953c6962397Spatrick 	    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN);
95432b2494eSpatrick }
95532b2494eSpatrick 
956e2612299Spatrick int
957e2612299Spatrick bwfm_chip_ca7_set_active(struct bwfm_softc *sc, uint32_t rstvec)
958e2612299Spatrick {
9593e129b89Spatrick 	struct bwfm_core *core;
9603e129b89Spatrick 
9613e129b89Spatrick 	sc->sc_buscore_ops->bc_activate(sc, rstvec);
9623e129b89Spatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7);
9633e129b89Spatrick 	sc->sc_chip.ch_core_reset(sc, core,
9643e129b89Spatrick 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT, 0, 0);
9653e129b89Spatrick 
9663e129b89Spatrick 	return 0;
967e2612299Spatrick }
968e2612299Spatrick 
96932b2494eSpatrick void
97032b2494eSpatrick bwfm_chip_ca7_set_passive(struct bwfm_softc *sc)
97132b2494eSpatrick {
9723e129b89Spatrick 	struct bwfm_core *core;
9733e129b89Spatrick 	uint32_t val;
9743e129b89Spatrick 
9753e129b89Spatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7);
9763e129b89Spatrick 	val = sc->sc_buscore_ops->bc_read(sc,
9773e129b89Spatrick 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
9783e129b89Spatrick 	sc->sc_chip.ch_core_reset(sc, core,
9793e129b89Spatrick 	    val & BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
9803e129b89Spatrick 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
9813e129b89Spatrick 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT);
9823e129b89Spatrick 
9833e129b89Spatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_80211);
9843e129b89Spatrick 	sc->sc_chip.ch_core_reset(sc, core, BWFM_AGENT_D11_IOCTL_PHYRESET |
9853e129b89Spatrick 	    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN, BWFM_AGENT_D11_IOCTL_PHYCLOCKEN,
9863e129b89Spatrick 	    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN);
98732b2494eSpatrick }
98832b2494eSpatrick 
989e2612299Spatrick int
990e2612299Spatrick bwfm_chip_cm3_set_active(struct bwfm_softc *sc)
991e2612299Spatrick {
992b5537148Spatrick 	struct bwfm_core *core;
993b5537148Spatrick 
994b5537148Spatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM);
995b5537148Spatrick 	if (!sc->sc_chip.ch_core_isup(sc, core))
996b5537148Spatrick 		return 1;
997b5537148Spatrick 
998b5537148Spatrick 	sc->sc_buscore_ops->bc_activate(sc, 0);
999b5537148Spatrick 
1000b5537148Spatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3);
1001b5537148Spatrick 	sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
1002b5537148Spatrick 
1003b5537148Spatrick 	return 0;
1004e2612299Spatrick }
1005e2612299Spatrick 
100632b2494eSpatrick void
100732b2494eSpatrick bwfm_chip_cm3_set_passive(struct bwfm_softc *sc)
100832b2494eSpatrick {
100932b2494eSpatrick 	struct bwfm_core *core;
101032b2494eSpatrick 
101132b2494eSpatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3);
101232b2494eSpatrick 	sc->sc_chip.ch_core_disable(sc, core, 0, 0);
101332b2494eSpatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_80211);
101432b2494eSpatrick 	sc->sc_chip.ch_core_reset(sc, core, BWFM_AGENT_D11_IOCTL_PHYRESET |
101532b2494eSpatrick 	    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN, BWFM_AGENT_D11_IOCTL_PHYCLOCKEN,
101632b2494eSpatrick 	    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN);
101732b2494eSpatrick 	core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM);
101832b2494eSpatrick 	sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
101932b2494eSpatrick 
102032b2494eSpatrick 	if (sc->sc_chip.ch_chip == BRCM_CC_43430_CHIP_ID) {
102132b2494eSpatrick 		sc->sc_buscore_ops->bc_write(sc,
102232b2494eSpatrick 		    core->co_base + BWFM_SOCRAM_BANKIDX, 3);
102332b2494eSpatrick 		sc->sc_buscore_ops->bc_write(sc,
102432b2494eSpatrick 		    core->co_base + BWFM_SOCRAM_BANKPDA, 0);
102532b2494eSpatrick 	}
102632b2494eSpatrick }
102732b2494eSpatrick 
102885d32364Spatrick /* RAM size helpers */
102985d32364Spatrick void
103085d32364Spatrick bwfm_chip_socram_ramsize(struct bwfm_softc *sc, struct bwfm_core *core)
103185d32364Spatrick {
10325212e703Spatrick 	uint32_t coreinfo, nb, lss, banksize, bankinfo;
10335212e703Spatrick 	uint32_t ramsize = 0, srsize = 0;
10345212e703Spatrick 	int i;
10355212e703Spatrick 
10365212e703Spatrick 	if (!sc->sc_chip.ch_core_isup(sc, core))
10375212e703Spatrick 		sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
10385212e703Spatrick 
10395212e703Spatrick 	coreinfo = sc->sc_buscore_ops->bc_read(sc,
10405212e703Spatrick 	    core->co_base + BWFM_SOCRAM_COREINFO);
10415212e703Spatrick 	nb = (coreinfo & BWFM_SOCRAM_COREINFO_SRNB_MASK)
10425212e703Spatrick 	    >> BWFM_SOCRAM_COREINFO_SRNB_SHIFT;
10435212e703Spatrick 
10445212e703Spatrick 	if (core->co_rev <= 7 || core->co_rev == 12) {
10455212e703Spatrick 		banksize = coreinfo & BWFM_SOCRAM_COREINFO_SRBSZ_MASK;
10465212e703Spatrick 		lss = (coreinfo & BWFM_SOCRAM_COREINFO_LSS_MASK)
10475212e703Spatrick 		    >> BWFM_SOCRAM_COREINFO_LSS_SHIFT;
10485212e703Spatrick 		if (lss != 0)
10495212e703Spatrick 			nb--;
10505212e703Spatrick 		ramsize = nb * (1 << (banksize + BWFM_SOCRAM_COREINFO_SRBSZ_BASE));
10515212e703Spatrick 		if (lss != 0)
10525212e703Spatrick 			ramsize += (1 << ((lss - 1) + BWFM_SOCRAM_COREINFO_SRBSZ_BASE));
10535212e703Spatrick 	} else {
10545212e703Spatrick 		for (i = 0; i < nb; i++) {
10555212e703Spatrick 			sc->sc_buscore_ops->bc_write(sc,
10565212e703Spatrick 			    core->co_base + BWFM_SOCRAM_BANKIDX,
10575212e703Spatrick 			    (BWFM_SOCRAM_BANKIDX_MEMTYPE_RAM <<
10585212e703Spatrick 			    BWFM_SOCRAM_BANKIDX_MEMTYPE_SHIFT) | i);
10595212e703Spatrick 			bankinfo = sc->sc_buscore_ops->bc_read(sc,
10605212e703Spatrick 			    core->co_base + BWFM_SOCRAM_BANKINFO);
10615212e703Spatrick 			banksize = ((bankinfo & BWFM_SOCRAM_BANKINFO_SZMASK) + 1)
10625212e703Spatrick 			    * BWFM_SOCRAM_BANKINFO_SZBASE;
10635212e703Spatrick 			ramsize += banksize;
10645212e703Spatrick 			if (bankinfo & BWFM_SOCRAM_BANKINFO_RETNTRAM_MASK)
10655212e703Spatrick 				srsize += banksize;
10665212e703Spatrick 		}
10675212e703Spatrick 	}
10685212e703Spatrick 
10695212e703Spatrick 	switch (sc->sc_chip.ch_chip) {
10705212e703Spatrick 	case BRCM_CC_4334_CHIP_ID:
10715212e703Spatrick 		if (sc->sc_chip.ch_chiprev < 2)
10725212e703Spatrick 			srsize = 32 * 1024;
10735212e703Spatrick 		break;
10745212e703Spatrick 	case BRCM_CC_43430_CHIP_ID:
10755212e703Spatrick 		srsize = 64 * 1024;
10765212e703Spatrick 		break;
10775212e703Spatrick 	default:
10785212e703Spatrick 		break;
10795212e703Spatrick 	}
10805212e703Spatrick 
10815212e703Spatrick 	sc->sc_chip.ch_ramsize = ramsize;
10825212e703Spatrick 	sc->sc_chip.ch_srsize = srsize;
108385d32364Spatrick }
108485d32364Spatrick 
108585d32364Spatrick void
108685d32364Spatrick bwfm_chip_sysmem_ramsize(struct bwfm_softc *sc, struct bwfm_core *core)
108785d32364Spatrick {
1088*c9aa06c9Spatrick 	uint32_t coreinfo, nb, banksize, bankinfo;
1089*c9aa06c9Spatrick 	uint32_t ramsize = 0;
1090*c9aa06c9Spatrick 	int i;
1091*c9aa06c9Spatrick 
1092*c9aa06c9Spatrick 	if (!sc->sc_chip.ch_core_isup(sc, core))
1093*c9aa06c9Spatrick 		sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
1094*c9aa06c9Spatrick 
1095*c9aa06c9Spatrick 	coreinfo = sc->sc_buscore_ops->bc_read(sc,
1096*c9aa06c9Spatrick 	    core->co_base + BWFM_SOCRAM_COREINFO);
1097*c9aa06c9Spatrick 	nb = (coreinfo & BWFM_SOCRAM_COREINFO_SRNB_MASK)
1098*c9aa06c9Spatrick 	    >> BWFM_SOCRAM_COREINFO_SRNB_SHIFT;
1099*c9aa06c9Spatrick 
1100*c9aa06c9Spatrick 	for (i = 0; i < nb; i++) {
1101*c9aa06c9Spatrick 		sc->sc_buscore_ops->bc_write(sc,
1102*c9aa06c9Spatrick 		    core->co_base + BWFM_SOCRAM_BANKIDX,
1103*c9aa06c9Spatrick 		    (BWFM_SOCRAM_BANKIDX_MEMTYPE_RAM <<
1104*c9aa06c9Spatrick 		    BWFM_SOCRAM_BANKIDX_MEMTYPE_SHIFT) | i);
1105*c9aa06c9Spatrick 		bankinfo = sc->sc_buscore_ops->bc_read(sc,
1106*c9aa06c9Spatrick 		    core->co_base + BWFM_SOCRAM_BANKINFO);
1107*c9aa06c9Spatrick 		banksize = ((bankinfo & BWFM_SOCRAM_BANKINFO_SZMASK) + 1)
1108*c9aa06c9Spatrick 		    * BWFM_SOCRAM_BANKINFO_SZBASE;
1109*c9aa06c9Spatrick 		ramsize += banksize;
1110*c9aa06c9Spatrick 	}
1111*c9aa06c9Spatrick 
1112*c9aa06c9Spatrick 	sc->sc_chip.ch_ramsize = ramsize;
111385d32364Spatrick }
111485d32364Spatrick 
111585d32364Spatrick void
111685d32364Spatrick bwfm_chip_tcm_ramsize(struct bwfm_softc *sc, struct bwfm_core *core)
111785d32364Spatrick {
11185212e703Spatrick 	uint32_t cap, nab, nbb, totb, bxinfo, ramsize = 0;
111985d32364Spatrick 	int i;
112085d32364Spatrick 
112185d32364Spatrick 	cap = sc->sc_buscore_ops->bc_read(sc, core->co_base + BWFM_ARMCR4_CAP);
112285d32364Spatrick 	nab = (cap & BWFM_ARMCR4_CAP_TCBANB_MASK) >> BWFM_ARMCR4_CAP_TCBANB_SHIFT;
112385d32364Spatrick 	nbb = (cap & BWFM_ARMCR4_CAP_TCBBNB_MASK) >> BWFM_ARMCR4_CAP_TCBBNB_SHIFT;
112485d32364Spatrick 	totb = nab + nbb;
112585d32364Spatrick 
112685d32364Spatrick 	for (i = 0; i < totb; i++) {
112785d32364Spatrick 		sc->sc_buscore_ops->bc_write(sc,
112885d32364Spatrick 		    core->co_base + BWFM_ARMCR4_BANKIDX, i);
112985d32364Spatrick 		bxinfo = sc->sc_buscore_ops->bc_read(sc,
113085d32364Spatrick 		    core->co_base + BWFM_ARMCR4_BANKINFO);
11315212e703Spatrick 		ramsize += ((bxinfo & BWFM_ARMCR4_BANKINFO_BSZ_MASK) + 1) *
113285d32364Spatrick 		    BWFM_ARMCR4_BANKINFO_BSZ_MULT;
113385d32364Spatrick 	}
113485d32364Spatrick 
11355212e703Spatrick 	sc->sc_chip.ch_ramsize = ramsize;
113685d32364Spatrick }
113785d32364Spatrick 
113885d32364Spatrick void
113985d32364Spatrick bwfm_chip_tcm_rambase(struct bwfm_softc *sc)
114085d32364Spatrick {
114185d32364Spatrick 	switch (sc->sc_chip.ch_chip) {
114285d32364Spatrick 	case BRCM_CC_4345_CHIP_ID:
114385d32364Spatrick 		sc->sc_chip.ch_rambase = 0x198000;
114485d32364Spatrick 		break;
114585d32364Spatrick 	case BRCM_CC_4335_CHIP_ID:
114685d32364Spatrick 	case BRCM_CC_4339_CHIP_ID:
114785d32364Spatrick 	case BRCM_CC_4350_CHIP_ID:
114885d32364Spatrick 	case BRCM_CC_4354_CHIP_ID:
114985d32364Spatrick 	case BRCM_CC_4356_CHIP_ID:
115085d32364Spatrick 	case BRCM_CC_43567_CHIP_ID:
115185d32364Spatrick 	case BRCM_CC_43569_CHIP_ID:
115285d32364Spatrick 	case BRCM_CC_43570_CHIP_ID:
115385d32364Spatrick 	case BRCM_CC_4358_CHIP_ID:
115485d32364Spatrick 	case BRCM_CC_4359_CHIP_ID:
115585d32364Spatrick 	case BRCM_CC_43602_CHIP_ID:
115685d32364Spatrick 	case BRCM_CC_4371_CHIP_ID:
115785d32364Spatrick 		sc->sc_chip.ch_rambase = 0x180000;
115885d32364Spatrick 		break;
115985d32364Spatrick 	case BRCM_CC_43465_CHIP_ID:
116085d32364Spatrick 	case BRCM_CC_43525_CHIP_ID:
116185d32364Spatrick 	case BRCM_CC_4365_CHIP_ID:
116285d32364Spatrick 	case BRCM_CC_4366_CHIP_ID:
116385d32364Spatrick 		sc->sc_chip.ch_rambase = 0x200000;
116485d32364Spatrick 		break;
116585d32364Spatrick 	case CY_CC_4373_CHIP_ID:
116685d32364Spatrick 		sc->sc_chip.ch_rambase = 0x160000;
116785d32364Spatrick 		break;
116885d32364Spatrick 	default:
116985d32364Spatrick 		printf("%s: unknown chip: %d\n", DEVNAME(sc),
117085d32364Spatrick 		    sc->sc_chip.ch_chip);
117185d32364Spatrick 		break;
117285d32364Spatrick 	}
117385d32364Spatrick }
117485d32364Spatrick 
117532b2494eSpatrick /* BCDC protocol implementation */
117632b2494eSpatrick int
117732b2494eSpatrick bwfm_proto_bcdc_query_dcmd(struct bwfm_softc *sc, int ifidx,
117832b2494eSpatrick     int cmd, char *buf, size_t *len)
117932b2494eSpatrick {
118032b2494eSpatrick 	struct bwfm_proto_bcdc_dcmd *dcmd;
118132b2494eSpatrick 	size_t size = sizeof(dcmd->hdr) + *len;
118232b2494eSpatrick 	static int reqid = 0;
118332b2494eSpatrick 	int ret = 1;
118432b2494eSpatrick 
118532b2494eSpatrick 	reqid++;
118632b2494eSpatrick 
118732b2494eSpatrick 	dcmd = malloc(sizeof(*dcmd), M_TEMP, M_WAITOK | M_ZERO);
118832b2494eSpatrick 	if (*len > sizeof(dcmd->buf))
118932b2494eSpatrick 		goto err;
119032b2494eSpatrick 
119132b2494eSpatrick 	dcmd->hdr.cmd = htole32(cmd);
119232b2494eSpatrick 	dcmd->hdr.len = htole32(*len);
119332b2494eSpatrick 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_GET;
119432b2494eSpatrick 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_ID_SET(reqid);
119532b2494eSpatrick 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_IF_SET(ifidx);
119632b2494eSpatrick 	dcmd->hdr.flags = htole32(dcmd->hdr.flags);
119732b2494eSpatrick 	memcpy(&dcmd->buf, buf, *len);
119832b2494eSpatrick 
119932b2494eSpatrick 	if (sc->sc_bus_ops->bs_txctl(sc, (void *)dcmd,
120032b2494eSpatrick 	     sizeof(dcmd->hdr) + *len)) {
120132b2494eSpatrick 		DPRINTF(("%s: tx failed\n", DEVNAME(sc)));
120232b2494eSpatrick 		goto err;
120332b2494eSpatrick 	}
120432b2494eSpatrick 
120532b2494eSpatrick 	do {
120632b2494eSpatrick 		if (sc->sc_bus_ops->bs_rxctl(sc, (void *)dcmd, &size)) {
120732b2494eSpatrick 			DPRINTF(("%s: rx failed\n", DEVNAME(sc)));
120832b2494eSpatrick 			goto err;
120932b2494eSpatrick 		}
121032b2494eSpatrick 		dcmd->hdr.cmd = letoh32(dcmd->hdr.cmd);
121132b2494eSpatrick 		dcmd->hdr.len = letoh32(dcmd->hdr.len);
121232b2494eSpatrick 		dcmd->hdr.flags = letoh32(dcmd->hdr.flags);
121332b2494eSpatrick 		dcmd->hdr.status = letoh32(dcmd->hdr.status);
121432b2494eSpatrick 	} while (BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags) != reqid);
121532b2494eSpatrick 
121632b2494eSpatrick 	if (BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags) != reqid) {
121732b2494eSpatrick 		printf("%s: unexpected request id\n", DEVNAME(sc));
121832b2494eSpatrick 		goto err;
121932b2494eSpatrick 	}
122032b2494eSpatrick 
122132b2494eSpatrick 	if (buf) {
122232b2494eSpatrick 		if (size > *len)
122332b2494eSpatrick 			size = *len;
122432b2494eSpatrick 		if (size < *len)
122532b2494eSpatrick 			*len = size;
122632b2494eSpatrick 		memcpy(buf, dcmd->buf, *len);
122732b2494eSpatrick 	}
122832b2494eSpatrick 
122932b2494eSpatrick 	if (dcmd->hdr.flags & BWFM_BCDC_DCMD_ERROR)
123032b2494eSpatrick 		ret = dcmd->hdr.status;
123132b2494eSpatrick 	else
123232b2494eSpatrick 		ret = 0;
123332b2494eSpatrick err:
123432b2494eSpatrick 	free(dcmd, M_TEMP, sizeof(*dcmd));
123532b2494eSpatrick 	return ret;
123632b2494eSpatrick }
123732b2494eSpatrick 
123832b2494eSpatrick int
123932b2494eSpatrick bwfm_proto_bcdc_set_dcmd(struct bwfm_softc *sc, int ifidx,
124032b2494eSpatrick     int cmd, char *buf, size_t len)
124132b2494eSpatrick {
124232b2494eSpatrick 	struct bwfm_proto_bcdc_dcmd *dcmd;
124332b2494eSpatrick 	size_t size = sizeof(dcmd->hdr) + len;
124432b2494eSpatrick 	int reqid = 0;
124532b2494eSpatrick 	int ret = 1;
124632b2494eSpatrick 
124732b2494eSpatrick 	reqid++;
124832b2494eSpatrick 
124932b2494eSpatrick 	dcmd = malloc(sizeof(*dcmd), M_TEMP, M_WAITOK | M_ZERO);
125032b2494eSpatrick 	if (len > sizeof(dcmd->buf))
125132b2494eSpatrick 		goto err;
125232b2494eSpatrick 
125332b2494eSpatrick 	dcmd->hdr.cmd = htole32(cmd);
125432b2494eSpatrick 	dcmd->hdr.len = htole32(len);
125532b2494eSpatrick 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_SET;
125632b2494eSpatrick 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_ID_SET(reqid);
125732b2494eSpatrick 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_IF_SET(ifidx);
125832b2494eSpatrick 	dcmd->hdr.flags = htole32(dcmd->hdr.flags);
125932b2494eSpatrick 	memcpy(&dcmd->buf, buf, len);
126032b2494eSpatrick 
126132b2494eSpatrick 	if (sc->sc_bus_ops->bs_txctl(sc, (void *)dcmd, size)) {
126232b2494eSpatrick 		DPRINTF(("%s: tx failed\n", DEVNAME(sc)));
126332b2494eSpatrick 		goto err;
126432b2494eSpatrick 	}
126532b2494eSpatrick 
126632b2494eSpatrick 	do {
126732b2494eSpatrick 		if (sc->sc_bus_ops->bs_rxctl(sc, (void *)dcmd, &size)) {
126832b2494eSpatrick 			DPRINTF(("%s: rx failed\n", DEVNAME(sc)));
126932b2494eSpatrick 			goto err;
127032b2494eSpatrick 		}
127132b2494eSpatrick 		dcmd->hdr.cmd = letoh32(dcmd->hdr.cmd);
127232b2494eSpatrick 		dcmd->hdr.len = letoh32(dcmd->hdr.len);
127332b2494eSpatrick 		dcmd->hdr.flags = letoh32(dcmd->hdr.flags);
127432b2494eSpatrick 		dcmd->hdr.status = letoh32(dcmd->hdr.status);
127532b2494eSpatrick 	} while (BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags) != reqid);
127632b2494eSpatrick 
127732b2494eSpatrick 	if (BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags) != reqid) {
127832b2494eSpatrick 		printf("%s: unexpected request id\n", DEVNAME(sc));
127932b2494eSpatrick 		goto err;
128032b2494eSpatrick 	}
128132b2494eSpatrick 
128232b2494eSpatrick 	if (dcmd->hdr.flags & BWFM_BCDC_DCMD_ERROR)
128332b2494eSpatrick 		return dcmd->hdr.status;
128432b2494eSpatrick 
128532b2494eSpatrick 	ret = 0;
128632b2494eSpatrick err:
128732b2494eSpatrick 	free(dcmd, M_TEMP, sizeof(*dcmd));
128832b2494eSpatrick 	return ret;
128932b2494eSpatrick }
129032b2494eSpatrick 
129132b2494eSpatrick /* FW Variable code */
129232b2494eSpatrick int
129332b2494eSpatrick bwfm_fwvar_cmd_get_data(struct bwfm_softc *sc, int cmd, void *data, size_t len)
129432b2494eSpatrick {
129532b2494eSpatrick 	return sc->sc_proto_ops->proto_query_dcmd(sc, 0, cmd, data, &len);
129632b2494eSpatrick }
129732b2494eSpatrick 
129832b2494eSpatrick int
129932b2494eSpatrick bwfm_fwvar_cmd_set_data(struct bwfm_softc *sc, int cmd, void *data, size_t len)
130032b2494eSpatrick {
130132b2494eSpatrick 	return sc->sc_proto_ops->proto_set_dcmd(sc, 0, cmd, data, len);
130232b2494eSpatrick }
130332b2494eSpatrick 
130432b2494eSpatrick int
130532b2494eSpatrick bwfm_fwvar_cmd_get_int(struct bwfm_softc *sc, int cmd, uint32_t *data)
130632b2494eSpatrick {
130732b2494eSpatrick 	int ret;
130832b2494eSpatrick 	ret = bwfm_fwvar_cmd_get_data(sc, cmd, data, sizeof(*data));
130932b2494eSpatrick 	*data = letoh32(*data);
131032b2494eSpatrick 	return ret;
131132b2494eSpatrick }
131232b2494eSpatrick 
131332b2494eSpatrick int
131432b2494eSpatrick bwfm_fwvar_cmd_set_int(struct bwfm_softc *sc, int cmd, uint32_t data)
131532b2494eSpatrick {
131632b2494eSpatrick 	data = htole32(data);
131732b2494eSpatrick 	return bwfm_fwvar_cmd_set_data(sc, cmd, &data, sizeof(data));
131832b2494eSpatrick }
131932b2494eSpatrick 
132032b2494eSpatrick int
132132b2494eSpatrick bwfm_fwvar_var_get_data(struct bwfm_softc *sc, char *name, void *data, size_t len)
132232b2494eSpatrick {
132332b2494eSpatrick 	char *buf;
132432b2494eSpatrick 	int ret;
132532b2494eSpatrick 
132632b2494eSpatrick 	buf = malloc(strlen(name) + 1 + len, M_TEMP, M_WAITOK);
132732b2494eSpatrick 	memcpy(buf, name, strlen(name) + 1);
132832b2494eSpatrick 	memcpy(buf + strlen(name) + 1, data, len);
132932b2494eSpatrick 	ret = bwfm_fwvar_cmd_get_data(sc, BWFM_C_GET_VAR,
133032b2494eSpatrick 	    buf, strlen(name) + 1 + len);
133132b2494eSpatrick 	memcpy(data, buf, len);
133232b2494eSpatrick 	free(buf, M_TEMP, strlen(name) + 1 + len);
133332b2494eSpatrick 	return ret;
133432b2494eSpatrick }
133532b2494eSpatrick 
133632b2494eSpatrick int
133732b2494eSpatrick bwfm_fwvar_var_set_data(struct bwfm_softc *sc, char *name, void *data, size_t len)
133832b2494eSpatrick {
133932b2494eSpatrick 	char *buf;
134032b2494eSpatrick 	int ret;
134132b2494eSpatrick 
134232b2494eSpatrick 	buf = malloc(strlen(name) + 1 + len, M_TEMP, M_WAITOK);
134332b2494eSpatrick 	memcpy(buf, name, strlen(name) + 1);
134432b2494eSpatrick 	memcpy(buf + strlen(name) + 1, data, len);
134532b2494eSpatrick 	ret = bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_VAR,
134632b2494eSpatrick 	    buf, strlen(name) + 1 + len);
134732b2494eSpatrick 	free(buf, M_TEMP, strlen(name) + 1 + len);
134832b2494eSpatrick 	return ret;
134932b2494eSpatrick }
135032b2494eSpatrick 
135132b2494eSpatrick int
135232b2494eSpatrick bwfm_fwvar_var_get_int(struct bwfm_softc *sc, char *name, uint32_t *data)
135332b2494eSpatrick {
135432b2494eSpatrick 	int ret;
135532b2494eSpatrick 	ret = bwfm_fwvar_var_get_data(sc, name, data, sizeof(*data));
135632b2494eSpatrick 	*data = letoh32(*data);
135732b2494eSpatrick 	return ret;
135832b2494eSpatrick }
135932b2494eSpatrick 
136032b2494eSpatrick int
136132b2494eSpatrick bwfm_fwvar_var_set_int(struct bwfm_softc *sc, char *name, uint32_t data)
136232b2494eSpatrick {
136332b2494eSpatrick 	data = htole32(data);
136432b2494eSpatrick 	return bwfm_fwvar_var_set_data(sc, name, &data, sizeof(data));
136532b2494eSpatrick }
136632b2494eSpatrick 
13678b2458cfSpatrick /* Channel parameters */
13688b2458cfSpatrick uint32_t
13698b2458cfSpatrick bwfm_spec2chan(struct bwfm_softc *sc, uint32_t chanspec)
13708b2458cfSpatrick {
13718b2458cfSpatrick 	if (sc->sc_io_type == BWFM_IO_TYPE_D11N)
13728b2458cfSpatrick 		return bwfm_spec2chan_d11n(sc, chanspec);
13738b2458cfSpatrick 	else
13748b2458cfSpatrick 		return bwfm_spec2chan_d11ac(sc, chanspec);
13758b2458cfSpatrick }
13768b2458cfSpatrick 
13778b2458cfSpatrick uint32_t
13788b2458cfSpatrick bwfm_spec2chan_d11n(struct bwfm_softc *sc, uint32_t chanspec)
13798b2458cfSpatrick {
13808b2458cfSpatrick 	uint32_t chanidx;
13818b2458cfSpatrick 
13828b2458cfSpatrick 	chanidx = chanspec & BWFM_CHANSPEC_CHAN_MASK;
13838b2458cfSpatrick 
13848b2458cfSpatrick 	switch (chanspec & BWFM_CHANSPEC_D11N_BW_MASK) {
13858b2458cfSpatrick 	case BWFM_CHANSPEC_D11N_BW_40:
13868b2458cfSpatrick 		switch (chanspec & BWFM_CHANSPEC_D11N_SB_MASK) {
13878b2458cfSpatrick 		case BWFM_CHANSPEC_D11N_SB_L:
13888b2458cfSpatrick 			chanidx -= 2;
13898b2458cfSpatrick 			break;
13908b2458cfSpatrick 		case BWFM_CHANSPEC_D11N_SB_U:
13918b2458cfSpatrick 			chanidx += 2;
13928b2458cfSpatrick 			break;
13938b2458cfSpatrick 		default:
13948b2458cfSpatrick 			break;
13958b2458cfSpatrick 		}
13968b2458cfSpatrick 		break;
13978b2458cfSpatrick 	default:
13988b2458cfSpatrick 		break;
13998b2458cfSpatrick 	}
14008b2458cfSpatrick 
14018b2458cfSpatrick 	return chanidx;
14028b2458cfSpatrick }
14038b2458cfSpatrick 
14048b2458cfSpatrick uint32_t
14058b2458cfSpatrick bwfm_spec2chan_d11ac(struct bwfm_softc *sc, uint32_t chanspec)
14068b2458cfSpatrick {
14078b2458cfSpatrick 	uint32_t chanidx;
14088b2458cfSpatrick 
14098b2458cfSpatrick 	chanidx = chanspec & BWFM_CHANSPEC_CHAN_MASK;
14108b2458cfSpatrick 
14118b2458cfSpatrick 	switch (chanspec & BWFM_CHANSPEC_D11AC_BW_MASK) {
14128b2458cfSpatrick 	case BWFM_CHANSPEC_D11AC_BW_40:
14138b2458cfSpatrick 		switch (chanspec & BWFM_CHANSPEC_D11AC_SB_MASK) {
14148b2458cfSpatrick 		case BWFM_CHANSPEC_D11AC_SB_LLL:
14158b2458cfSpatrick 			chanidx -= 2;
14168b2458cfSpatrick 			break;
14178b2458cfSpatrick 		case BWFM_CHANSPEC_D11AC_SB_LLU:
14188b2458cfSpatrick 			chanidx += 2;
14198b2458cfSpatrick 			break;
14208b2458cfSpatrick 		default:
14218b2458cfSpatrick 			break;
14228b2458cfSpatrick 		}
14238b2458cfSpatrick 		break;
14248b2458cfSpatrick 	case BWFM_CHANSPEC_D11AC_BW_80:
14258b2458cfSpatrick 		switch (chanspec & BWFM_CHANSPEC_D11AC_SB_MASK) {
14268b2458cfSpatrick 		case BWFM_CHANSPEC_D11AC_SB_LLL:
14278b2458cfSpatrick 			chanidx -= 6;
14288b2458cfSpatrick 			break;
14298b2458cfSpatrick 		case BWFM_CHANSPEC_D11AC_SB_LLU:
14308b2458cfSpatrick 			chanidx -= 2;
14318b2458cfSpatrick 			break;
14328b2458cfSpatrick 		case BWFM_CHANSPEC_D11AC_SB_LUL:
14338b2458cfSpatrick 			chanidx += 2;
14348b2458cfSpatrick 			break;
14358b2458cfSpatrick 		case BWFM_CHANSPEC_D11AC_SB_LUU:
14368b2458cfSpatrick 			chanidx += 6;
14378b2458cfSpatrick 			break;
14388b2458cfSpatrick 		default:
14398b2458cfSpatrick 			break;
14408b2458cfSpatrick 		}
14418b2458cfSpatrick 		break;
14428b2458cfSpatrick 	default:
14438b2458cfSpatrick 		break;
14448b2458cfSpatrick 	}
14458b2458cfSpatrick 
14468b2458cfSpatrick 	return chanidx;
14478b2458cfSpatrick }
14488b2458cfSpatrick 
144932b2494eSpatrick /* 802.11 code */
145032b2494eSpatrick void
145163498aa8Spatrick bwfm_connect(struct bwfm_softc *sc)
145232b2494eSpatrick {
145332b2494eSpatrick 	struct ieee80211com *ic = &sc->sc_ic;
145463498aa8Spatrick 	struct bwfm_ext_join_params *params;
145563498aa8Spatrick 	uint8_t buf[64];	/* XXX max WPA/RSN/WMM IE length */
145663498aa8Spatrick 	uint8_t *frm;
145763498aa8Spatrick 
145863498aa8Spatrick 	/*
145963498aa8Spatrick 	 * OPEN: Open or WPA/WPA2 on newer Chips/Firmware.
146063498aa8Spatrick 	 * SHARED KEY: WEP.
146163498aa8Spatrick 	 * AUTO: Automatic, probably for older Chips/Firmware.
146263498aa8Spatrick 	 */
146363498aa8Spatrick 	if (ic->ic_flags & IEEE80211_F_RSNON) {
146463498aa8Spatrick 		uint32_t wsec = 0;
146563498aa8Spatrick 		uint32_t wpa = 0;
146663498aa8Spatrick 
146763498aa8Spatrick 		/* tell firmware to add WPA/RSN IE to (re)assoc request */
146863498aa8Spatrick 		if (ic->ic_bss->ni_rsnprotos == IEEE80211_PROTO_RSN)
146963498aa8Spatrick 			frm = ieee80211_add_rsn(buf, ic, ic->ic_bss);
147063498aa8Spatrick 		else
147163498aa8Spatrick 			frm = ieee80211_add_wpa(buf, ic, ic->ic_bss);
147263498aa8Spatrick 		bwfm_fwvar_var_set_data(sc, "wpaie", buf, frm - buf);
147363498aa8Spatrick 
147463498aa8Spatrick 		if (ic->ic_rsnprotos & IEEE80211_PROTO_WPA) {
147563498aa8Spatrick 			if (ic->ic_rsnakms & IEEE80211_AKM_PSK)
147663498aa8Spatrick 				wpa |= BWFM_WPA_AUTH_WPA_PSK;
147763498aa8Spatrick 			if (ic->ic_rsnakms & IEEE80211_AKM_8021X)
147863498aa8Spatrick 				wpa |= BWFM_WPA_AUTH_WPA_UNSPECIFIED;
147963498aa8Spatrick 		}
148063498aa8Spatrick 		if (ic->ic_rsnprotos & IEEE80211_PROTO_RSN) {
148163498aa8Spatrick 			if (ic->ic_rsnakms & IEEE80211_AKM_PSK)
148263498aa8Spatrick 				wpa |= BWFM_WPA_AUTH_WPA2_PSK;
148363498aa8Spatrick 			if (ic->ic_rsnakms & IEEE80211_AKM_SHA256_PSK)
148463498aa8Spatrick 				wpa |= BWFM_WPA_AUTH_WPA2_PSK_SHA256;
148563498aa8Spatrick 			if (ic->ic_rsnakms & IEEE80211_AKM_8021X)
148663498aa8Spatrick 				wpa |= BWFM_WPA_AUTH_WPA2_UNSPECIFIED;
148763498aa8Spatrick 			if (ic->ic_rsnakms & IEEE80211_AKM_SHA256_8021X)
148863498aa8Spatrick 				wpa |= BWFM_WPA_AUTH_WPA2_1X_SHA256;
148963498aa8Spatrick 		}
149063498aa8Spatrick 		if (ic->ic_rsnciphers & IEEE80211_WPA_CIPHER_TKIP ||
149163498aa8Spatrick 		    ic->ic_rsngroupcipher & IEEE80211_WPA_CIPHER_TKIP)
149263498aa8Spatrick 			wsec |= BWFM_WSEC_TKIP;
149363498aa8Spatrick 		if (ic->ic_rsnciphers & IEEE80211_WPA_CIPHER_CCMP ||
149463498aa8Spatrick 		    ic->ic_rsngroupcipher & IEEE80211_WPA_CIPHER_CCMP)
149563498aa8Spatrick 			wsec |= BWFM_WSEC_AES;
149663498aa8Spatrick 
149763498aa8Spatrick 		bwfm_fwvar_var_set_int(sc, "wpa_auth", wpa);
149863498aa8Spatrick 		bwfm_fwvar_var_set_int(sc, "wsec", wsec);
149963498aa8Spatrick 	} else {
150063498aa8Spatrick 		bwfm_fwvar_var_set_int(sc, "wpa_auth", BWFM_WPA_AUTH_DISABLED);
150163498aa8Spatrick 		bwfm_fwvar_var_set_int(sc, "wsec", BWFM_WSEC_NONE);
150263498aa8Spatrick 	}
150363498aa8Spatrick 	bwfm_fwvar_var_set_int(sc, "auth", BWFM_AUTH_OPEN);
150463498aa8Spatrick 	bwfm_fwvar_var_set_int(sc, "mfp", BWFM_MFP_NONE);
150563498aa8Spatrick 
150663498aa8Spatrick 	if (ic->ic_des_esslen && ic->ic_des_esslen < BWFM_MAX_SSID_LEN) {
150763498aa8Spatrick 		params = malloc(sizeof(*params), M_TEMP, M_WAITOK | M_ZERO);
150863498aa8Spatrick 		memcpy(params->ssid.ssid, ic->ic_des_essid, ic->ic_des_esslen);
150963498aa8Spatrick 		params->ssid.len = htole32(ic->ic_des_esslen);
151002662318Spatrick 		memcpy(params->assoc.bssid, ic->ic_bss->ni_bssid,
151102662318Spatrick 		    sizeof(params->assoc.bssid));
151263498aa8Spatrick 		params->scan.scan_type = -1;
151363498aa8Spatrick 		params->scan.nprobes = htole32(-1);
151463498aa8Spatrick 		params->scan.active_time = htole32(-1);
151563498aa8Spatrick 		params->scan.passive_time = htole32(-1);
151663498aa8Spatrick 		params->scan.home_time = htole32(-1);
151763498aa8Spatrick 		if (bwfm_fwvar_var_set_data(sc, "join", params, sizeof(*params))) {
151863498aa8Spatrick 			struct bwfm_join_params join;
151963498aa8Spatrick 			memset(&join, 0, sizeof(join));
152002662318Spatrick 			memcpy(join.ssid.ssid, ic->ic_des_essid,
152102662318Spatrick 			    ic->ic_des_esslen);
152263498aa8Spatrick 			join.ssid.len = htole32(ic->ic_des_esslen);
152302662318Spatrick 			memcpy(join.assoc.bssid, ic->ic_bss->ni_bssid,
152402662318Spatrick 			    sizeof(join.assoc.bssid));
152563498aa8Spatrick 			bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_SSID, &join,
152663498aa8Spatrick 			    sizeof(join));
152763498aa8Spatrick 		}
152863498aa8Spatrick 		free(params, M_TEMP, sizeof(*params));
152963498aa8Spatrick 	}
153063498aa8Spatrick }
153163498aa8Spatrick 
1532c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
1533c11618f6Spatrick void
1534c11618f6Spatrick bwfm_hostap(struct bwfm_softc *sc)
1535c11618f6Spatrick {
1536c11618f6Spatrick 	struct ieee80211com *ic = &sc->sc_ic;
1537c11618f6Spatrick 	struct ieee80211_node *ni = ic->ic_bss;
1538c11618f6Spatrick 	struct bwfm_join_params join;
1539c11618f6Spatrick 
1540c11618f6Spatrick 	/*
1541c11618f6Spatrick 	 * OPEN: Open or WPA/WPA2 on newer Chips/Firmware.
1542c11618f6Spatrick 	 * SHARED KEY: WEP.
1543c11618f6Spatrick 	 * AUTO: Automatic, probably for older Chips/Firmware.
1544c11618f6Spatrick 	 */
1545c11618f6Spatrick 	if (ic->ic_flags & IEEE80211_F_RSNON) {
1546c11618f6Spatrick 		uint32_t wsec = 0;
1547c11618f6Spatrick 		uint32_t wpa = 0;
1548c11618f6Spatrick 
1549c11618f6Spatrick 		/* TODO: Turn off if replay counter set */
1550c11618f6Spatrick 		if (ni->ni_rsnprotos & IEEE80211_PROTO_RSN)
1551c11618f6Spatrick 			bwfm_fwvar_var_set_int(sc, "wme_bss_disable", 1);
1552c11618f6Spatrick 
1553c11618f6Spatrick 		if (ni->ni_rsnprotos & IEEE80211_PROTO_WPA) {
1554c11618f6Spatrick 			if (ni->ni_rsnakms & IEEE80211_AKM_PSK)
1555c11618f6Spatrick 				wpa |= BWFM_WPA_AUTH_WPA_PSK;
1556c11618f6Spatrick 			if (ni->ni_rsnakms & IEEE80211_AKM_8021X)
1557c11618f6Spatrick 				wpa |= BWFM_WPA_AUTH_WPA_UNSPECIFIED;
1558c11618f6Spatrick 		}
1559c11618f6Spatrick 		if (ni->ni_rsnprotos & IEEE80211_PROTO_RSN) {
1560c11618f6Spatrick 			if (ni->ni_rsnakms & IEEE80211_AKM_PSK)
1561c11618f6Spatrick 				wpa |= BWFM_WPA_AUTH_WPA2_PSK;
1562c11618f6Spatrick 			if (ni->ni_rsnakms & IEEE80211_AKM_SHA256_PSK)
1563c11618f6Spatrick 				wpa |= BWFM_WPA_AUTH_WPA2_PSK_SHA256;
1564c11618f6Spatrick 			if (ni->ni_rsnakms & IEEE80211_AKM_8021X)
1565c11618f6Spatrick 				wpa |= BWFM_WPA_AUTH_WPA2_UNSPECIFIED;
1566c11618f6Spatrick 			if (ni->ni_rsnakms & IEEE80211_AKM_SHA256_8021X)
1567c11618f6Spatrick 				wpa |= BWFM_WPA_AUTH_WPA2_1X_SHA256;
1568c11618f6Spatrick 		}
1569c11618f6Spatrick 		if (ni->ni_rsnciphers & IEEE80211_WPA_CIPHER_TKIP ||
1570c11618f6Spatrick 		    ni->ni_rsngroupcipher & IEEE80211_WPA_CIPHER_TKIP)
1571c11618f6Spatrick 			wsec |= BWFM_WSEC_TKIP;
1572c11618f6Spatrick 		if (ni->ni_rsnciphers & IEEE80211_WPA_CIPHER_CCMP ||
1573c11618f6Spatrick 		    ni->ni_rsngroupcipher & IEEE80211_WPA_CIPHER_CCMP)
1574c11618f6Spatrick 			wsec |= BWFM_WSEC_AES;
1575c11618f6Spatrick 
1576c11618f6Spatrick 		bwfm_fwvar_var_set_int(sc, "wpa_auth", wpa);
1577c11618f6Spatrick 		bwfm_fwvar_var_set_int(sc, "wsec", wsec);
1578c11618f6Spatrick 	} else {
1579c11618f6Spatrick 		bwfm_fwvar_var_set_int(sc, "wpa_auth", BWFM_WPA_AUTH_DISABLED);
1580c11618f6Spatrick 		bwfm_fwvar_var_set_int(sc, "wsec", BWFM_WSEC_NONE);
1581c11618f6Spatrick 	}
1582c11618f6Spatrick 	bwfm_fwvar_var_set_int(sc, "auth", BWFM_AUTH_OPEN);
1583c11618f6Spatrick 	bwfm_fwvar_var_set_int(sc, "mfp", BWFM_MFP_NONE);
1584c11618f6Spatrick 
1585c11618f6Spatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_INFRA, 1);
1586c11618f6Spatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_AP, 1);
1587c11618f6Spatrick 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_UP, 1);
1588c11618f6Spatrick 
1589c11618f6Spatrick 	memset(&join, 0, sizeof(join));
1590c11618f6Spatrick 	memcpy(join.ssid.ssid, ic->ic_des_essid, ic->ic_des_esslen);
1591c11618f6Spatrick 	join.ssid.len = htole32(ic->ic_des_esslen);
1592c11618f6Spatrick 	memset(join.assoc.bssid, 0xff, sizeof(join.assoc.bssid));
1593c11618f6Spatrick 	bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_SSID, &join, sizeof(join));
1594c11618f6Spatrick 	bwfm_fwvar_var_set_int(sc, "closednet",
1595c11618f6Spatrick 	    (ic->ic_flags & IEEE80211_F_HIDENWID) != 0);
1596c11618f6Spatrick }
1597c11618f6Spatrick #endif
1598c11618f6Spatrick 
159963498aa8Spatrick void
160063498aa8Spatrick bwfm_scan(struct bwfm_softc *sc)
160163498aa8Spatrick {
160232b2494eSpatrick 	struct bwfm_escan_params *params;
160332b2494eSpatrick 	uint32_t nssid = 0, nchannel = 0;
160432b2494eSpatrick 	size_t params_size;
160532b2494eSpatrick 
160632b2494eSpatrick 	params_size = sizeof(*params);
160732b2494eSpatrick 	params_size += sizeof(uint32_t) * ((nchannel + 1) / 2);
160832b2494eSpatrick 	params_size += sizeof(struct bwfm_ssid) * nssid;
160932b2494eSpatrick 
161032b2494eSpatrick 	params = malloc(params_size, M_TEMP, M_WAITOK | M_ZERO);
161132b2494eSpatrick 	memset(params->scan_params.bssid, 0xff,
161232b2494eSpatrick 	    sizeof(params->scan_params.bssid));
161332b2494eSpatrick 	params->scan_params.bss_type = 2;
16141d29b516Spatrick 	params->scan_params.scan_type = BWFM_SCANTYPE_PASSIVE;
161532b2494eSpatrick 	params->scan_params.nprobes = htole32(-1);
161632b2494eSpatrick 	params->scan_params.active_time = htole32(-1);
161732b2494eSpatrick 	params->scan_params.passive_time = htole32(-1);
161832b2494eSpatrick 	params->scan_params.home_time = htole32(-1);
161932b2494eSpatrick 	params->version = htole32(BWFM_ESCAN_REQ_VERSION);
162032b2494eSpatrick 	params->action = htole16(WL_ESCAN_ACTION_START);
162132b2494eSpatrick 	params->sync_id = htole16(0x1234);
162232b2494eSpatrick 
162332b2494eSpatrick #if 0
162432b2494eSpatrick 	/* Scan a specific channel */
162532b2494eSpatrick 	params->scan_params.channel_list[0] = htole16(
162632b2494eSpatrick 	    (1 & 0xff) << 0 |
162732b2494eSpatrick 	    (3 & 0x3) << 8 |
162832b2494eSpatrick 	    (2 & 0x3) << 10 |
162932b2494eSpatrick 	    (2 & 0x3) << 12
163032b2494eSpatrick 	    );
163132b2494eSpatrick 	params->scan_params.channel_num = htole32(
163232b2494eSpatrick 	    (1 & 0xffff) << 0
163332b2494eSpatrick 	    );
163432b2494eSpatrick #endif
163532b2494eSpatrick 
163632b2494eSpatrick 	bwfm_fwvar_var_set_data(sc, "escan", params, params_size);
163732b2494eSpatrick 	free(params, M_TEMP, params_size);
163832b2494eSpatrick }
163932b2494eSpatrick 
1640c11618f6Spatrick struct mbuf *
1641c11618f6Spatrick bwfm_newbuf(void)
1642c11618f6Spatrick {
1643c11618f6Spatrick 	struct mbuf *m;
1644c11618f6Spatrick 
1645c11618f6Spatrick 	MGETHDR(m, M_DONTWAIT, MT_DATA);
1646c11618f6Spatrick 	if (m == NULL)
1647c11618f6Spatrick 		return (NULL);
1648c11618f6Spatrick 
1649c11618f6Spatrick 	MCLGET(m, M_DONTWAIT);
1650c11618f6Spatrick 	if (!(m->m_flags & M_EXT)) {
1651c11618f6Spatrick 		m_freem(m);
1652c11618f6Spatrick 		return (NULL);
1653c11618f6Spatrick 	}
1654c11618f6Spatrick 
1655c11618f6Spatrick 	m->m_len = m->m_pkthdr.len = MCLBYTES;
1656c11618f6Spatrick 
1657c11618f6Spatrick 	return (m);
1658c11618f6Spatrick }
1659c11618f6Spatrick 
166032b2494eSpatrick void
1661f37fc236Spatrick bwfm_rx(struct bwfm_softc *sc, struct mbuf *m)
166232b2494eSpatrick {
166332b2494eSpatrick 	struct ieee80211com *ic = &sc->sc_ic;
166432b2494eSpatrick 	struct ifnet *ifp = &ic->ic_if;
1665f37fc236Spatrick 	struct bwfm_event *e = mtod(m, struct bwfm_event *);
166632b2494eSpatrick 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
1667c11618f6Spatrick 	struct ieee80211_node *ni;
166832b2494eSpatrick 
1669f37fc236Spatrick 	if (m->m_len >= sizeof(e->ehdr) &&
167032b2494eSpatrick 	    ntohs(e->ehdr.ether_type) == BWFM_ETHERTYPE_LINK_CTL &&
167132b2494eSpatrick 	    memcmp(BWFM_BRCM_OUI, e->hdr.oui, sizeof(e->hdr.oui)) == 0 &&
1672010fb2f1Spatrick 	    ntohs(e->hdr.usr_subtype) == BWFM_BRCM_SUBTYPE_EVENT) {
1673f4e8af02Spatrick 		bwfm_rx_event(sc, m);
1674010fb2f1Spatrick 		return;
1675010fb2f1Spatrick 	}
1676010fb2f1Spatrick 
1677010fb2f1Spatrick 	/* Drop network packets if we are not in RUN state. */
1678010fb2f1Spatrick 	if (ic->ic_state != IEEE80211_S_RUN) {
1679010fb2f1Spatrick 		m_freem(m);
1680010fb2f1Spatrick 		return;
1681010fb2f1Spatrick 	}
168232b2494eSpatrick 
168363498aa8Spatrick 	if ((ic->ic_flags & IEEE80211_F_RSNON) &&
1684f37fc236Spatrick 	    m->m_len >= sizeof(e->ehdr) &&
168563498aa8Spatrick 	    ntohs(e->ehdr.ether_type) == ETHERTYPE_PAE) {
168663498aa8Spatrick 		ifp->if_ipackets++;
168763498aa8Spatrick #if NBPFILTER > 0
168863498aa8Spatrick 		if (ifp->if_bpf)
168963498aa8Spatrick 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
169063498aa8Spatrick #endif
1691c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
1692c11618f6Spatrick 		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
1693c11618f6Spatrick 			ni = ieee80211_find_node(ic,
1694c11618f6Spatrick 			    (void *)&e->ehdr.ether_shost);
1695c11618f6Spatrick 			if (ni == NULL) {
1696c11618f6Spatrick 				m_free(m);
1697c11618f6Spatrick 				return;
1698c11618f6Spatrick 			}
1699c11618f6Spatrick 		} else
1700c11618f6Spatrick #endif
1701c11618f6Spatrick 			ni = ic->ic_bss;
1702c11618f6Spatrick 		ieee80211_eapol_key_input(ic, m, ni);
170363498aa8Spatrick 	} else {
170432b2494eSpatrick 		ml_enqueue(&ml, m);
170532b2494eSpatrick 		if_input(ifp, &ml);
170632b2494eSpatrick 	}
170763498aa8Spatrick }
170832b2494eSpatrick 
1709c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
1710c11618f6Spatrick void
1711c11618f6Spatrick bwfm_rx_auth_ind(struct bwfm_softc *sc, struct bwfm_event *e, size_t len)
1712c11618f6Spatrick {
1713c11618f6Spatrick 	struct ieee80211com *ic = &sc->sc_ic;
1714c11618f6Spatrick 	struct ifnet *ifp = &ic->ic_if;
1715c11618f6Spatrick 	struct ieee80211_rxinfo rxi;
1716c11618f6Spatrick 	struct ieee80211_frame *wh;
1717c11618f6Spatrick 	struct ieee80211_node *ni;
1718c11618f6Spatrick 	struct mbuf *m;
1719c11618f6Spatrick 	uint32_t pktlen, ieslen;
1720c11618f6Spatrick 
1721c11618f6Spatrick 	/* Build a fake beacon frame to let net80211 do all the parsing. */
1722c11618f6Spatrick 	ieslen = betoh32(e->msg.datalen);
1723c11618f6Spatrick 	pktlen = sizeof(*wh) + ieslen + 6;
1724c11618f6Spatrick 	if (pktlen > MCLBYTES)
1725c11618f6Spatrick 		return;
1726c11618f6Spatrick 	m = bwfm_newbuf();
1727c11618f6Spatrick 	if (m == NULL)
1728c11618f6Spatrick 		return;
1729c11618f6Spatrick 	wh = mtod(m, struct ieee80211_frame *);
1730c11618f6Spatrick 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
1731c11618f6Spatrick 	    IEEE80211_FC0_SUBTYPE_AUTH;
1732c11618f6Spatrick 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
1733c11618f6Spatrick 	*(uint16_t *)wh->i_dur = 0;
1734c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
1735c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr2, &e->msg.addr);
1736c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid);
1737c11618f6Spatrick 	*(uint16_t *)wh->i_seq = 0;
1738c11618f6Spatrick 	((uint16_t *)(&wh[1]))[0] = IEEE80211_AUTH_ALG_OPEN;
1739c11618f6Spatrick 	((uint16_t *)(&wh[1]))[1] = IEEE80211_AUTH_OPEN_REQUEST;
1740c11618f6Spatrick 	((uint16_t *)(&wh[1]))[2] = 0;
1741c11618f6Spatrick 
1742c11618f6Spatrick 	/* Finalize mbuf. */
1743c11618f6Spatrick 	m->m_pkthdr.len = m->m_len = pktlen;
1744c11618f6Spatrick 	ni = ieee80211_find_node(ic, wh->i_addr2);
1745c11618f6Spatrick 	if (ni == NULL)
1746c11618f6Spatrick 		ni = ieee80211_alloc_node(ic, wh->i_addr2);
1747c11618f6Spatrick 	if (ni == NULL) {
1748c11618f6Spatrick 		m_free(m);
1749c11618f6Spatrick 		return;
1750c11618f6Spatrick 	}
1751c11618f6Spatrick 	ni->ni_chan = &ic->ic_channels[0];
1752c11618f6Spatrick 	rxi.rxi_flags = 0;
1753c11618f6Spatrick 	rxi.rxi_rssi = 0;
1754c11618f6Spatrick 	rxi.rxi_tstamp = 0;
1755c11618f6Spatrick 	ieee80211_input(ifp, m, ni, &rxi);
1756c11618f6Spatrick }
1757c11618f6Spatrick 
1758c11618f6Spatrick void
1759c11618f6Spatrick bwfm_rx_assoc_ind(struct bwfm_softc *sc, struct bwfm_event *e, size_t len,
1760c11618f6Spatrick     int reassoc)
1761c11618f6Spatrick {
1762c11618f6Spatrick 	struct ieee80211com *ic = &sc->sc_ic;
1763c11618f6Spatrick 	struct ifnet *ifp = &ic->ic_if;
1764c11618f6Spatrick 	struct ieee80211_rxinfo rxi;
1765c11618f6Spatrick 	struct ieee80211_frame *wh;
1766c11618f6Spatrick 	struct ieee80211_node *ni;
1767c11618f6Spatrick 	struct mbuf *m;
1768c11618f6Spatrick 	uint32_t pktlen, ieslen;
1769c11618f6Spatrick 
1770c11618f6Spatrick 	/* Build a fake beacon frame to let net80211 do all the parsing. */
1771c11618f6Spatrick 	ieslen = betoh32(e->msg.datalen);
1772c11618f6Spatrick 	pktlen = sizeof(*wh) + ieslen + 4;
1773c11618f6Spatrick 	if (reassoc)
1774c11618f6Spatrick 		pktlen += IEEE80211_ADDR_LEN;
1775c11618f6Spatrick 	if (pktlen > MCLBYTES)
1776c11618f6Spatrick 		return;
1777c11618f6Spatrick 	m = bwfm_newbuf();
1778c11618f6Spatrick 	if (m == NULL)
1779c11618f6Spatrick 		return;
1780c11618f6Spatrick 	wh = mtod(m, struct ieee80211_frame *);
1781c11618f6Spatrick 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT;
1782c11618f6Spatrick 	if (reassoc)
1783c11618f6Spatrick 	    wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_REASSOC_REQ;
1784c11618f6Spatrick 	else
1785c11618f6Spatrick 	    wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_ASSOC_REQ;
1786c11618f6Spatrick 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
1787c11618f6Spatrick 	*(uint16_t *)wh->i_dur = 0;
1788c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
1789c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr2, &e->msg.addr);
1790c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid);
1791c11618f6Spatrick 	*(uint16_t *)wh->i_seq = 0;
1792c11618f6Spatrick 	((uint16_t *)(&wh[1]))[0] = IEEE80211_CAPINFO_ESS; /* XXX */
1793c11618f6Spatrick 	((uint16_t *)(&wh[1]))[1] = 100; /* XXX */
1794c11618f6Spatrick 	if (reassoc) {
1795c11618f6Spatrick 		memset(&wh[1] + 4, 0, IEEE80211_ADDR_LEN);
1796c11618f6Spatrick 		memcpy(((uint8_t *)&wh[1]) + 4 + IEEE80211_ADDR_LEN,
1797c11618f6Spatrick 		    &e[1], ieslen);
1798c11618f6Spatrick 	} else
1799c11618f6Spatrick 		memcpy(((uint8_t *)&wh[1]) + 4, &e[1], ieslen);
1800c11618f6Spatrick 
1801c11618f6Spatrick 	/* Finalize mbuf. */
1802c11618f6Spatrick 	m->m_pkthdr.len = m->m_len = pktlen;
1803c11618f6Spatrick 	ni = ieee80211_find_node(ic, wh->i_addr2);
1804c11618f6Spatrick 	if (ni == NULL)
1805c11618f6Spatrick 		ni = ieee80211_alloc_node(ic, wh->i_addr2);
1806c11618f6Spatrick 	if (ni == NULL) {
1807c11618f6Spatrick 		m_free(m);
1808c11618f6Spatrick 		return;
1809c11618f6Spatrick 	}
1810c11618f6Spatrick 	ni->ni_chan = &ic->ic_channels[0];
1811c11618f6Spatrick 	rxi.rxi_flags = 0;
1812c11618f6Spatrick 	rxi.rxi_rssi = 0;
1813c11618f6Spatrick 	rxi.rxi_tstamp = 0;
1814c11618f6Spatrick 	ieee80211_input(ifp, m, ni, &rxi);
1815c11618f6Spatrick }
1816c11618f6Spatrick 
1817c11618f6Spatrick void
1818c11618f6Spatrick bwfm_rx_deauth_ind(struct bwfm_softc *sc, struct bwfm_event *e, size_t len)
1819c11618f6Spatrick {
1820c11618f6Spatrick 	bwfm_rx_leave_ind(sc, e, len, IEEE80211_FC0_SUBTYPE_DEAUTH);
1821c11618f6Spatrick }
1822c11618f6Spatrick 
1823c11618f6Spatrick void
1824c11618f6Spatrick bwfm_rx_disassoc_ind(struct bwfm_softc *sc, struct bwfm_event *e, size_t len)
1825c11618f6Spatrick {
1826c11618f6Spatrick 	bwfm_rx_leave_ind(sc, e, len, IEEE80211_FC0_SUBTYPE_DISASSOC);
1827c11618f6Spatrick }
1828c11618f6Spatrick 
1829c11618f6Spatrick void
1830c11618f6Spatrick bwfm_rx_leave_ind(struct bwfm_softc *sc, struct bwfm_event *e, size_t len,
1831c11618f6Spatrick     int subtype)
1832c11618f6Spatrick {
1833c11618f6Spatrick 	struct ieee80211com *ic = &sc->sc_ic;
1834c11618f6Spatrick 	struct ifnet *ifp = &ic->ic_if;
1835c11618f6Spatrick 	struct ieee80211_rxinfo rxi;
1836c11618f6Spatrick 	struct ieee80211_frame *wh;
1837c11618f6Spatrick 	struct ieee80211_node *ni;
1838c11618f6Spatrick 	struct mbuf *m;
1839c11618f6Spatrick 	uint32_t pktlen;
1840c11618f6Spatrick 
1841c11618f6Spatrick 	/* Build a fake beacon frame to let net80211 do all the parsing. */
1842c11618f6Spatrick 	pktlen = sizeof(*wh) + 2;
1843c11618f6Spatrick 	if (pktlen > MCLBYTES)
1844c11618f6Spatrick 		return;
1845c11618f6Spatrick 	m = bwfm_newbuf();
1846c11618f6Spatrick 	if (m == NULL)
1847c11618f6Spatrick 		return;
1848c11618f6Spatrick 	wh = mtod(m, struct ieee80211_frame *);
1849c11618f6Spatrick 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
1850c11618f6Spatrick 	    subtype;
1851c11618f6Spatrick 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
1852c11618f6Spatrick 	*(uint16_t *)wh->i_dur = 0;
1853c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
1854c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr2, &e->msg.addr);
1855c11618f6Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid);
1856c11618f6Spatrick 	*(uint16_t *)wh->i_seq = 0;
1857c11618f6Spatrick 	memset(&wh[1], 0, 2);
1858c11618f6Spatrick 
1859c11618f6Spatrick 	/* Finalize mbuf. */
1860c11618f6Spatrick 	m->m_pkthdr.len = m->m_len = pktlen;
1861c11618f6Spatrick 	ni = ieee80211_find_node(ic, wh->i_addr2);
1862c11618f6Spatrick 	if (ni == NULL) {
1863c11618f6Spatrick 		m_free(m);
1864c11618f6Spatrick 		return;
1865c11618f6Spatrick 	}
1866c11618f6Spatrick 	rxi.rxi_flags = 0;
1867c11618f6Spatrick 	rxi.rxi_rssi = 0;
1868c11618f6Spatrick 	rxi.rxi_tstamp = 0;
1869c11618f6Spatrick 	ieee80211_input(ifp, m, ni, &rxi);
1870c11618f6Spatrick }
1871c11618f6Spatrick #endif
1872c11618f6Spatrick 
187332b2494eSpatrick void
1874f4e8af02Spatrick bwfm_rx_event(struct bwfm_softc *sc, struct mbuf *m)
1875f4e8af02Spatrick {
1876f4e8af02Spatrick 	struct bwfm_cmd_mbuf cmd;
1877f4e8af02Spatrick 
1878f4e8af02Spatrick 	cmd.m = m;
1879f4e8af02Spatrick 	bwfm_do_async(sc, bwfm_rx_event_cb, &cmd, sizeof(cmd));
1880f4e8af02Spatrick }
1881f4e8af02Spatrick 
1882f4e8af02Spatrick void
1883f4e8af02Spatrick bwfm_rx_event_cb(struct bwfm_softc *sc, void *arg)
188432b2494eSpatrick {
188532b2494eSpatrick 	struct ieee80211com *ic = &sc->sc_ic;
188663498aa8Spatrick 	struct ifnet *ifp = &ic->ic_if;
1887f4e8af02Spatrick 	struct bwfm_cmd_mbuf *cmd = arg;
1888f4e8af02Spatrick 	struct mbuf *m = cmd->m;
1889f4e8af02Spatrick 	struct bwfm_event *e = mtod(m, void *);
1890f4e8af02Spatrick 	size_t len = m->m_len;
189132b2494eSpatrick 
1892f4e8af02Spatrick 	if (ntohl(e->msg.event_type) >= BWFM_E_LAST) {
1893f4e8af02Spatrick 		m_freem(m);
189432b2494eSpatrick 		return;
1895f4e8af02Spatrick 	}
189632b2494eSpatrick 
189732b2494eSpatrick 	switch (ntohl(e->msg.event_type)) {
189832b2494eSpatrick 	case BWFM_E_ESCAN_RESULT: {
1899f4e8af02Spatrick 		struct bwfm_escan_results *res = (void *)&e[1];
190032b2494eSpatrick 		struct bwfm_bss_info *bss;
190132b2494eSpatrick 		int i;
190232b2494eSpatrick 		if (ntohl(e->msg.status) != BWFM_E_STATUS_PARTIAL) {
190363498aa8Spatrick 			ieee80211_end_scan(ifp);
190432b2494eSpatrick 			break;
190532b2494eSpatrick 		}
190632b2494eSpatrick 		len -= sizeof(*e);
190732b2494eSpatrick 		if (len < sizeof(*res) || len < letoh32(res->buflen)) {
190832b2494eSpatrick 			printf("%s: results too small\n", DEVNAME(sc));
1909f4e8af02Spatrick 			m_freem(m);
191032b2494eSpatrick 			return;
191132b2494eSpatrick 		}
191232b2494eSpatrick 		len -= sizeof(*res);
191332b2494eSpatrick 		if (len < letoh16(res->bss_count) * sizeof(struct bwfm_bss_info)) {
191432b2494eSpatrick 			printf("%s: results too small\n", DEVNAME(sc));
1915f4e8af02Spatrick 			m_freem(m);
191632b2494eSpatrick 			return;
191732b2494eSpatrick 		}
191832b2494eSpatrick 		bss = &res->bss_info[0];
191932b2494eSpatrick 		for (i = 0; i < letoh16(res->bss_count); i++) {
192032b2494eSpatrick 			bwfm_scan_node(sc, &res->bss_info[i], len);
192132b2494eSpatrick 			len -= sizeof(*bss) + letoh32(bss->length);
192232b2494eSpatrick 			bss = (void *)((char *)bss) + letoh32(bss->length);
192332b2494eSpatrick 			if (len <= 0)
192432b2494eSpatrick 				break;
192532b2494eSpatrick 		}
192632b2494eSpatrick 		break;
192732b2494eSpatrick 		}
192863498aa8Spatrick 	case BWFM_E_SET_SSID:
19297a818b26Spatrick 		if (ntohl(e->msg.status) != BWFM_E_STATUS_SUCCESS)
19307a818b26Spatrick 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
19317a818b26Spatrick 		break;
19327a818b26Spatrick 	case BWFM_E_AUTH:
1933750cbd98Spatrick 		if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS &&
1934750cbd98Spatrick 		    ic->ic_state == IEEE80211_S_AUTH)
19357a818b26Spatrick 			ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
193663498aa8Spatrick 		else
193763498aa8Spatrick 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
193863498aa8Spatrick 		break;
193963498aa8Spatrick 	case BWFM_E_ASSOC:
1940750cbd98Spatrick 		if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS &&
1941750cbd98Spatrick 		    ic->ic_state == IEEE80211_S_ASSOC)
19427a818b26Spatrick 			ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
194363498aa8Spatrick 		else
194463498aa8Spatrick 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
194563498aa8Spatrick 		break;
1946750cbd98Spatrick 	case BWFM_E_DEAUTH:
1947750cbd98Spatrick 	case BWFM_E_DISASSOC:
1948750cbd98Spatrick 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
1949750cbd98Spatrick 		break;
195063498aa8Spatrick 	case BWFM_E_LINK:
195163498aa8Spatrick 		if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS &&
195263498aa8Spatrick 		    ntohl(e->msg.reason) == 0)
195363498aa8Spatrick 			break;
195463498aa8Spatrick 		/* Link status has changed */
195563498aa8Spatrick 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
195663498aa8Spatrick 		break;
1957c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
1958c11618f6Spatrick 	case BWFM_E_AUTH_IND:
1959c11618f6Spatrick 		bwfm_rx_auth_ind(sc, e, len);
1960c11618f6Spatrick 		break;
1961c11618f6Spatrick 	case BWFM_E_ASSOC_IND:
1962c11618f6Spatrick 		bwfm_rx_assoc_ind(sc, e, len, 0);
1963c11618f6Spatrick 		break;
1964c11618f6Spatrick 	case BWFM_E_REASSOC_IND:
1965c11618f6Spatrick 		bwfm_rx_assoc_ind(sc, e, len, 1);
1966c11618f6Spatrick 		break;
1967c11618f6Spatrick 	case BWFM_E_DEAUTH_IND:
1968c11618f6Spatrick 		bwfm_rx_deauth_ind(sc, e, len);
1969c11618f6Spatrick 		break;
1970c11618f6Spatrick 	case BWFM_E_DISASSOC_IND:
1971c11618f6Spatrick 		bwfm_rx_disassoc_ind(sc, e, len);
1972c11618f6Spatrick 		break;
1973c11618f6Spatrick #endif
197432b2494eSpatrick 	default:
1975cd99bb20Spatrick 		DPRINTF(("%s: len %lu datalen %u code %u status %u"
1976cd99bb20Spatrick 		    " reason %u\n", __func__, len, ntohl(e->msg.datalen),
197732b2494eSpatrick 		    ntohl(e->msg.event_type), ntohl(e->msg.status),
1978cd99bb20Spatrick 		    ntohl(e->msg.reason)));
197932b2494eSpatrick 		break;
198032b2494eSpatrick 	}
1981f4e8af02Spatrick 
1982f4e8af02Spatrick 	m_freem(m);
198332b2494eSpatrick }
198432b2494eSpatrick 
198532b2494eSpatrick void
198632b2494eSpatrick bwfm_scan_node(struct bwfm_softc *sc, struct bwfm_bss_info *bss, size_t len)
198732b2494eSpatrick {
198832b2494eSpatrick 	struct ieee80211com *ic = &sc->sc_ic;
198963498aa8Spatrick 	struct ifnet *ifp = &ic->ic_if;
199063498aa8Spatrick 	struct ieee80211_frame *wh;
199132b2494eSpatrick 	struct ieee80211_node *ni;
19928b2458cfSpatrick 	struct ieee80211_rxinfo rxi;
19938b2458cfSpatrick 	struct ieee80211_channel *bss_chan;
199463498aa8Spatrick 	struct mbuf *m;
199563498aa8Spatrick 	uint32_t pktlen, ieslen;
199663498aa8Spatrick 	uint16_t iesoff;
19978b2458cfSpatrick 	int chanidx;
199832b2494eSpatrick 
199963498aa8Spatrick 	iesoff = letoh16(bss->ie_offset);
200063498aa8Spatrick 	ieslen = letoh32(bss->ie_length);
200163498aa8Spatrick 	if (ieslen > len - iesoff)
200263498aa8Spatrick 		return;
200363498aa8Spatrick 
200463498aa8Spatrick 	/* Build a fake beacon frame to let net80211 do all the parsing. */
200563498aa8Spatrick 	pktlen = sizeof(*wh) + ieslen + 12;
2006c11618f6Spatrick 	if (pktlen > MCLBYTES)
200763498aa8Spatrick 		return;
2008c11618f6Spatrick 	m = bwfm_newbuf();
2009c11618f6Spatrick 	if (m == NULL)
201063498aa8Spatrick 		return;
201163498aa8Spatrick 	wh = mtod(m, struct ieee80211_frame *);
201263498aa8Spatrick 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
201363498aa8Spatrick 	    IEEE80211_FC0_SUBTYPE_BEACON;
201463498aa8Spatrick 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
201563498aa8Spatrick 	*(uint16_t *)wh->i_dur = 0;
201663498aa8Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
201763498aa8Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr2, bss->bssid);
201863498aa8Spatrick 	IEEE80211_ADDR_COPY(wh->i_addr3, bss->bssid);
201963498aa8Spatrick 	*(uint16_t *)wh->i_seq = 0;
202063498aa8Spatrick 	memset(&wh[1], 0, 12);
202163498aa8Spatrick 	((uint16_t *)(&wh[1]))[4] = bss->beacon_period;
202263498aa8Spatrick 	((uint16_t *)(&wh[1]))[5] = bss->capability;
202363498aa8Spatrick 	memcpy(((uint8_t *)&wh[1]) + 12, ((uint8_t *)bss) + iesoff, ieslen);
202463498aa8Spatrick 
202563498aa8Spatrick 	/* Finalize mbuf. */
202663498aa8Spatrick 	m->m_pkthdr.len = m->m_len = pktlen;
202763498aa8Spatrick 	ni = ieee80211_find_rxnode(ic, wh);
20288b2458cfSpatrick 	/*
20298b2458cfSpatrick 	 * We may switch ic_bss's channel during scans.
20308b2458cfSpatrick 	 * Record the current channel so we can restore it later.
20318b2458cfSpatrick 	 */
20328b2458cfSpatrick 	bss_chan = NULL;
20338b2458cfSpatrick 	if (ni == ic->ic_bss)
20348b2458cfSpatrick 		bss_chan = ni->ni_chan;
20358b2458cfSpatrick 	/* Channel mask equals IEEE80211_CHAN_MAX */
20368b2458cfSpatrick 	chanidx = bwfm_spec2chan(sc, letoh32(bss->chanspec));
20378b2458cfSpatrick 	ni->ni_chan = &ic->ic_channels[chanidx];
20388b2458cfSpatrick 	/* Supply RSSI */
203963498aa8Spatrick 	rxi.rxi_flags = 0;
204063498aa8Spatrick 	rxi.rxi_rssi = letoh32(bss->rssi);
204163498aa8Spatrick 	rxi.rxi_tstamp = 0;
204263498aa8Spatrick 	ieee80211_input(ifp, m, ni, &rxi);
20438b2458cfSpatrick 	/* Restore channel */
20448b2458cfSpatrick 	if (bss_chan)
20458b2458cfSpatrick 		ni->ni_chan = bss_chan;
204663498aa8Spatrick 	/* Node is no longer needed. */
204763498aa8Spatrick 	ieee80211_release_node(ic, ni);
204863498aa8Spatrick }
204963498aa8Spatrick 
205063498aa8Spatrick void
205163498aa8Spatrick bwfm_task(void *arg)
205263498aa8Spatrick {
205363498aa8Spatrick 	struct bwfm_softc *sc = arg;
205463498aa8Spatrick 	struct bwfm_host_cmd_ring *ring = &sc->sc_cmdq;
205563498aa8Spatrick 	struct bwfm_host_cmd *cmd;
205663498aa8Spatrick 	int s;
205763498aa8Spatrick 
205863498aa8Spatrick 	s = splsoftnet();
205963498aa8Spatrick 	while (ring->next != ring->cur) {
206063498aa8Spatrick 		cmd = &ring->cmd[ring->next];
206163498aa8Spatrick 		splx(s);
206263498aa8Spatrick 		cmd->cb(sc, cmd->data);
206363498aa8Spatrick 		s = splsoftnet();
206463498aa8Spatrick 		ring->queued--;
206563498aa8Spatrick 		ring->next = (ring->next + 1) % BWFM_HOST_CMD_RING_COUNT;
206663498aa8Spatrick 	}
206763498aa8Spatrick 	splx(s);
206863498aa8Spatrick }
206963498aa8Spatrick 
207063498aa8Spatrick void
207163498aa8Spatrick bwfm_do_async(struct bwfm_softc *sc,
207263498aa8Spatrick     void (*cb)(struct bwfm_softc *, void *), void *arg, int len)
207363498aa8Spatrick {
207463498aa8Spatrick 	struct bwfm_host_cmd_ring *ring = &sc->sc_cmdq;
207563498aa8Spatrick 	struct bwfm_host_cmd *cmd;
207663498aa8Spatrick 	int s;
207763498aa8Spatrick 
207863498aa8Spatrick 	s = splsoftnet();
2079f4e8af02Spatrick 	if (ring->queued >= BWFM_HOST_CMD_RING_COUNT) {
2080f4e8af02Spatrick 		splx(s);
2081f4e8af02Spatrick 		return;
2082f4e8af02Spatrick 	}
208363498aa8Spatrick 	cmd = &ring->cmd[ring->cur];
208463498aa8Spatrick 	cmd->cb = cb;
208563498aa8Spatrick 	KASSERT(len <= sizeof(cmd->data));
208663498aa8Spatrick 	memcpy(cmd->data, arg, len);
208763498aa8Spatrick 	ring->cur = (ring->cur + 1) % BWFM_HOST_CMD_RING_COUNT;
2088f4e8af02Spatrick 	ring->queued++;
208963498aa8Spatrick 	task_add(sc->sc_taskq, &sc->sc_task);
209063498aa8Spatrick 	splx(s);
209163498aa8Spatrick }
209263498aa8Spatrick 
209363498aa8Spatrick int
209463498aa8Spatrick bwfm_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
209563498aa8Spatrick     int type, int arg1, int arg2)
209663498aa8Spatrick {
209763498aa8Spatrick #ifdef BWFM_DEBUG
209863498aa8Spatrick 	struct bwfm_softc *sc = ic->ic_softc;
209963498aa8Spatrick 	DPRINTF(("%s: %s\n", DEVNAME(sc), __func__));
210063498aa8Spatrick #endif
210163498aa8Spatrick 	return 0;
210263498aa8Spatrick }
210363498aa8Spatrick 
210463498aa8Spatrick int
210563498aa8Spatrick bwfm_set_key(struct ieee80211com *ic, struct ieee80211_node *ni,
210663498aa8Spatrick     struct ieee80211_key *k)
210763498aa8Spatrick {
210863498aa8Spatrick 	struct bwfm_softc *sc = ic->ic_softc;
210963498aa8Spatrick 	struct bwfm_cmd_key cmd;
211063498aa8Spatrick 
211163498aa8Spatrick 	cmd.ni = ni;
211263498aa8Spatrick 	cmd.k = k;
211363498aa8Spatrick 	bwfm_do_async(sc, bwfm_set_key_cb, &cmd, sizeof(cmd));
211463498aa8Spatrick 	return 0;
211563498aa8Spatrick }
211663498aa8Spatrick 
211763498aa8Spatrick void
211863498aa8Spatrick bwfm_set_key_cb(struct bwfm_softc *sc, void *arg)
211963498aa8Spatrick {
212063498aa8Spatrick 	struct bwfm_cmd_key *cmd = arg;
212163498aa8Spatrick 	struct ieee80211_key *k = cmd->k;
212263498aa8Spatrick 	struct ieee80211_node *ni = cmd->ni;
212363498aa8Spatrick 	struct bwfm_wsec_key key;
212463498aa8Spatrick 	uint32_t wsec, wsec_enable;
212563498aa8Spatrick 	int ext_key = 0;
212663498aa8Spatrick 
212763498aa8Spatrick 	if ((k->k_flags & IEEE80211_KEY_GROUP) == 0 &&
212863498aa8Spatrick 	    k->k_cipher != IEEE80211_CIPHER_WEP40 &&
212963498aa8Spatrick 	    k->k_cipher != IEEE80211_CIPHER_WEP104)
213063498aa8Spatrick 		ext_key = 1;
213163498aa8Spatrick 
213263498aa8Spatrick 	memset(&key, 0, sizeof(key));
2133c11618f6Spatrick 	if (ext_key && !IEEE80211_IS_MULTICAST(ni->ni_macaddr))
2134c11618f6Spatrick 		memcpy(key.ea, ni->ni_macaddr, sizeof(key.ea));
213563498aa8Spatrick 	key.index = htole32(k->k_id);
213663498aa8Spatrick 	key.len = htole32(k->k_len);
213763498aa8Spatrick 	memcpy(key.data, k->k_key, sizeof(key.data));
213863498aa8Spatrick 	if (!ext_key)
213963498aa8Spatrick 		key.flags = htole32(BWFM_WSEC_PRIMARY_KEY);
214063498aa8Spatrick 
214163498aa8Spatrick 	switch (k->k_cipher) {
214263498aa8Spatrick 	case IEEE80211_CIPHER_WEP40:
214363498aa8Spatrick 		key.algo = htole32(BWFM_CRYPTO_ALGO_WEP1);
214463498aa8Spatrick 		wsec_enable = BWFM_WSEC_WEP;
214563498aa8Spatrick 		break;
214663498aa8Spatrick 	case IEEE80211_CIPHER_WEP104:
214763498aa8Spatrick 		key.algo = htole32(BWFM_CRYPTO_ALGO_WEP128);
214863498aa8Spatrick 		wsec_enable = BWFM_WSEC_WEP;
214963498aa8Spatrick 		break;
215063498aa8Spatrick 	case IEEE80211_CIPHER_TKIP:
215163498aa8Spatrick 		key.algo = htole32(BWFM_CRYPTO_ALGO_TKIP);
215263498aa8Spatrick 		wsec_enable = BWFM_WSEC_TKIP;
215363498aa8Spatrick 		break;
215463498aa8Spatrick 	case IEEE80211_CIPHER_CCMP:
215563498aa8Spatrick 		key.algo = htole32(BWFM_CRYPTO_ALGO_AES_CCM);
215663498aa8Spatrick 		wsec_enable = BWFM_WSEC_AES;
215763498aa8Spatrick 		break;
215863498aa8Spatrick 	default:
215963498aa8Spatrick 		printf("%s: cipher %x not supported\n", DEVNAME(sc),
216063498aa8Spatrick 		    k->k_cipher);
216163498aa8Spatrick 		return;
216263498aa8Spatrick 	}
216363498aa8Spatrick 
216463498aa8Spatrick 	bwfm_fwvar_var_set_data(sc, "wsec_key", &key, sizeof(key));
216563498aa8Spatrick 	bwfm_fwvar_var_get_int(sc, "wsec", &wsec);
216663498aa8Spatrick 	wsec |= wsec_enable;
216763498aa8Spatrick 	bwfm_fwvar_var_set_int(sc, "wsec", wsec);
216863498aa8Spatrick }
216963498aa8Spatrick 
217063498aa8Spatrick void
217163498aa8Spatrick bwfm_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni,
217263498aa8Spatrick     struct ieee80211_key *k)
217363498aa8Spatrick {
217463498aa8Spatrick 	struct bwfm_softc *sc = ic->ic_softc;
217563498aa8Spatrick 	struct bwfm_cmd_key cmd;
217663498aa8Spatrick 
217763498aa8Spatrick 	cmd.ni = ni;
217863498aa8Spatrick 	cmd.k = k;
217963498aa8Spatrick 	bwfm_do_async(sc, bwfm_delete_key_cb, &cmd, sizeof(cmd));
218063498aa8Spatrick }
218163498aa8Spatrick 
218263498aa8Spatrick void
218363498aa8Spatrick bwfm_delete_key_cb(struct bwfm_softc *sc, void *arg)
218463498aa8Spatrick {
218563498aa8Spatrick 	struct bwfm_cmd_key *cmd = arg;
218663498aa8Spatrick 	struct ieee80211_key *k = cmd->k;
218763498aa8Spatrick 	struct bwfm_wsec_key key;
218863498aa8Spatrick 
218963498aa8Spatrick 	memset(&key, 0, sizeof(key));
219063498aa8Spatrick 	key.index = htole32(k->k_id);
219163498aa8Spatrick 	key.flags = htole32(BWFM_WSEC_PRIMARY_KEY);
219263498aa8Spatrick 	bwfm_fwvar_var_set_data(sc, "wsec_key", &key, sizeof(key));
219363498aa8Spatrick }
219463498aa8Spatrick 
219563498aa8Spatrick int
219663498aa8Spatrick bwfm_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
219763498aa8Spatrick {
219863498aa8Spatrick 	struct bwfm_softc *sc = ic->ic_softc;
21995d1a01feSstsp 	struct ifnet *ifp = &ic->ic_if;
220063498aa8Spatrick 	int s;
220163498aa8Spatrick 
220263498aa8Spatrick 	s = splnet();
220363498aa8Spatrick 
220463498aa8Spatrick 	switch (nstate) {
220563498aa8Spatrick 	case IEEE80211_S_SCAN:
220663498aa8Spatrick 		bwfm_scan(sc);
22075d1a01feSstsp 		if (ifp->if_flags & IFF_DEBUG)
22085d1a01feSstsp 			printf("%s: %s -> %s\n", DEVNAME(sc),
22095d1a01feSstsp 			    ieee80211_state_name[ic->ic_state],
22105d1a01feSstsp 			    ieee80211_state_name[nstate]);
221163498aa8Spatrick 		ic->ic_state = nstate;
221263498aa8Spatrick 		splx(s);
2213f4e8af02Spatrick 		return 0;
221463498aa8Spatrick 	case IEEE80211_S_AUTH:
221563498aa8Spatrick 		ic->ic_bss->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE;
221663498aa8Spatrick 		bwfm_connect(sc);
22175d1a01feSstsp 		if (ifp->if_flags & IFF_DEBUG)
22185d1a01feSstsp 			printf("%s: %s -> %s\n", DEVNAME(sc),
22195d1a01feSstsp 			    ieee80211_state_name[ic->ic_state],
22205d1a01feSstsp 			    ieee80211_state_name[nstate]);
22215d1a01feSstsp 		ic->ic_state = nstate;
222263498aa8Spatrick 		if (ic->ic_flags & IEEE80211_F_RSNON)
222363498aa8Spatrick 			ic->ic_bss->ni_rsn_supp_state = RSNA_SUPP_PTKSTART;
222463498aa8Spatrick 		splx(s);
2225f4e8af02Spatrick 		return 0;
2226c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
2227c11618f6Spatrick 	case IEEE80211_S_RUN:
2228c11618f6Spatrick 		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
2229c11618f6Spatrick 			bwfm_hostap(sc);
2230c11618f6Spatrick 		break;
2231c11618f6Spatrick #endif
223263498aa8Spatrick 	default:
223363498aa8Spatrick 		break;
223463498aa8Spatrick 	}
2235f4e8af02Spatrick 	sc->sc_newstate(ic, nstate, arg);
223663498aa8Spatrick 	splx(s);
2237f4e8af02Spatrick 	return 0;
223832b2494eSpatrick }
2239