Home | History | Annotate | Line # | Download | only in ic
bwfm.c revision 1.19
      1 /* $NetBSD: bwfm.c,v 1.19 2019/12/27 09:22:20 msaitoh Exp $ */
      2 /* $OpenBSD: bwfm.c,v 1.5 2017/10/16 22:27:16 patrick Exp $ */
      3 /*
      4  * Copyright (c) 2010-2016 Broadcom Corporation
      5  * Copyright (c) 2016,2017 Patrick Wildt <patrick (at) blueri.se>
      6  *
      7  * Permission to use, copy, modify, and/or distribute this software for any
      8  * purpose with or without fee is hereby granted, provided that the above
      9  * copyright notice and this permission notice appear in all copies.
     10  *
     11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18  */
     19 
     20 #include <sys/param.h>
     21 #include <sys/systm.h>
     22 #include <sys/buf.h>
     23 #include <sys/kernel.h>
     24 #include <sys/device.h>
     25 #include <sys/queue.h>
     26 #include <sys/socket.h>
     27 #include <sys/kmem.h>
     28 #include <sys/workqueue.h>
     29 #include <sys/pcq.h>
     30 
     31 #include <net/bpf.h>
     32 #include <net/if.h>
     33 #include <net/if_dl.h>
     34 #include <net/if_media.h>
     35 #include <net/if_ether.h>
     36 
     37 #include <netinet/in.h>
     38 
     39 #include <net80211/ieee80211_var.h>
     40 
     41 #include <dev/ic/bwfmvar.h>
     42 #include <dev/ic/bwfmreg.h>
     43 
     44 /* #define BWFM_DEBUG */
     45 #ifdef BWFM_DEBUG
     46 #define DPRINTF(x)	do { if (bwfm_debug > 0) printf x; } while (0)
     47 #define DPRINTFN(n, x)	do { if (bwfm_debug >= (n)) printf x; } while (0)
     48 static int bwfm_debug = 1;
     49 #else
     50 #define DPRINTF(x)	do { ; } while (0)
     51 #define DPRINTFN(n, x)	do { ; } while (0)
     52 #endif
     53 
     54 #define DEVNAME(sc)	device_xname((sc)->sc_dev)
     55 
     56 void	 bwfm_start(struct ifnet *);
     57 int	 bwfm_init(struct ifnet *);
     58 void	 bwfm_stop(struct ifnet *, int);
     59 void	 bwfm_watchdog(struct ifnet *);
     60 int	 bwfm_ioctl(struct ifnet *, u_long, void *);
     61 int	 bwfm_media_change(struct ifnet *);
     62 
     63 int	 bwfm_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
     64 	     int, int);
     65 void	 bwfm_recv_mgmt(struct ieee80211com *, struct mbuf *,
     66 	     struct ieee80211_node *, int, int, uint32_t);
     67 int	 bwfm_key_set(struct ieee80211com *, const struct ieee80211_key *,
     68 	     const uint8_t *);
     69 int	 bwfm_key_delete(struct ieee80211com *, const struct ieee80211_key *);
     70 int	 bwfm_newstate(struct ieee80211com *, enum ieee80211_state, int);
     71 void	 bwfm_newstate_cb(struct bwfm_softc *, struct bwfm_cmd_newstate *);
     72 void	 bwfm_newassoc(struct ieee80211_node *, int);
     73 void	 bwfm_task(struct work *, void *);
     74 
     75 int	 bwfm_chip_attach(struct bwfm_softc *);
     76 int	 bwfm_chip_detach(struct bwfm_softc *, int);
     77 struct bwfm_core *bwfm_chip_get_core(struct bwfm_softc *, int);
     78 struct bwfm_core *bwfm_chip_get_pmu(struct bwfm_softc *);
     79 int	 bwfm_chip_ai_isup(struct bwfm_softc *, struct bwfm_core *);
     80 void	 bwfm_chip_ai_disable(struct bwfm_softc *, struct bwfm_core *,
     81 	     uint32_t, uint32_t);
     82 void	 bwfm_chip_ai_reset(struct bwfm_softc *, struct bwfm_core *,
     83 	     uint32_t, uint32_t, uint32_t);
     84 void	 bwfm_chip_dmp_erom_scan(struct bwfm_softc *);
     85 int	 bwfm_chip_dmp_get_regaddr(struct bwfm_softc *, uint32_t *,
     86 	     uint32_t *, uint32_t *);
     87 int	 bwfm_chip_cr4_set_active(struct bwfm_softc *, const uint32_t);
     88 void	 bwfm_chip_cr4_set_passive(struct bwfm_softc *);
     89 int	 bwfm_chip_ca7_set_active(struct bwfm_softc *, const uint32_t);
     90 void	 bwfm_chip_ca7_set_passive(struct bwfm_softc *);
     91 int	 bwfm_chip_cm3_set_active(struct bwfm_softc *);
     92 void	 bwfm_chip_cm3_set_passive(struct bwfm_softc *);
     93 void	 bwfm_chip_socram_ramsize(struct bwfm_softc *, struct bwfm_core *);
     94 void	 bwfm_chip_sysmem_ramsize(struct bwfm_softc *, struct bwfm_core *);
     95 void	 bwfm_chip_tcm_ramsize(struct bwfm_softc *, struct bwfm_core *);
     96 void	 bwfm_chip_tcm_rambase(struct bwfm_softc *);
     97 
     98 int	 bwfm_proto_bcdc_query_dcmd(struct bwfm_softc *, int,
     99 	     int, char *, size_t *);
    100 int	 bwfm_proto_bcdc_set_dcmd(struct bwfm_softc *, int,
    101 	     int, char *, size_t);
    102 
    103 int	 bwfm_fwvar_cmd_get_data(struct bwfm_softc *, int, void *, size_t);
    104 int	 bwfm_fwvar_cmd_set_data(struct bwfm_softc *, int, void *, size_t);
    105 int	 bwfm_fwvar_cmd_get_int(struct bwfm_softc *, int, uint32_t *);
    106 int	 bwfm_fwvar_cmd_set_int(struct bwfm_softc *, int, uint32_t);
    107 int	 bwfm_fwvar_var_get_data(struct bwfm_softc *, const char *, void *, size_t);
    108 int	 bwfm_fwvar_var_set_data(struct bwfm_softc *, const char *, void *, size_t);
    109 int	 bwfm_fwvar_var_get_int(struct bwfm_softc *, const char *, uint32_t *);
    110 int	 bwfm_fwvar_var_set_int(struct bwfm_softc *, const char *, uint32_t);
    111 
    112 struct ieee80211_channel *bwfm_bss2chan(struct bwfm_softc *, struct bwfm_bss_info *);
    113 void	 bwfm_scan(struct bwfm_softc *);
    114 void	 bwfm_connect(struct bwfm_softc *);
    115 void	 bwfm_get_sta_info(struct bwfm_softc *, struct ifmediareq *);
    116 
    117 void	 bwfm_rx(struct bwfm_softc *, struct mbuf *);
    118 void	 bwfm_rx_event(struct bwfm_softc *, struct mbuf *);
    119 void	 bwfm_rx_event_cb(struct bwfm_softc *, struct mbuf *);
    120 void	 bwfm_scan_node(struct bwfm_softc *, struct bwfm_bss_info *, size_t);
    121 
    122 uint8_t bwfm_2ghz_channels[] = {
    123 	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
    124 };
    125 uint8_t bwfm_5ghz_channels[] = {
    126 	34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64, 100, 104, 108, 112,
    127 	116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165,
    128 };
    129 
    130 struct bwfm_proto_ops bwfm_proto_bcdc_ops = {
    131 	.proto_query_dcmd = bwfm_proto_bcdc_query_dcmd,
    132 	.proto_set_dcmd = bwfm_proto_bcdc_set_dcmd,
    133 };
    134 
    135 void
    136 bwfm_attach(struct bwfm_softc *sc)
    137 {
    138 	struct ieee80211com *ic = &sc->sc_ic;
    139 	struct ifnet *ifp = &sc->sc_if;
    140 	struct bwfm_task *t;
    141 	char fw_version[BWFM_DCMD_SMLEN];
    142 	uint32_t bandlist[3];
    143 	uint32_t tmp;
    144 	int i, j, error;
    145 
    146 	error = workqueue_create(&sc->sc_taskq, DEVNAME(sc),
    147 	    bwfm_task, sc, PRI_NONE, IPL_NET, 0);
    148 	if (error != 0) {
    149 		printf("%s: could not create workqueue\n", DEVNAME(sc));
    150 		return;
    151 	}
    152 	sc->sc_freetask = pcq_create(BWFM_TASK_COUNT, KM_SLEEP);
    153 	for (i = 0; i < BWFM_TASK_COUNT; i++) {
    154 		t = &sc->sc_task[i];
    155 		t->t_sc = sc;
    156 		pcq_put(sc->sc_freetask, t);
    157 	}
    158 
    159 	/* Stop the device in case it was previously initialized */
    160 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_DOWN, 1);
    161 
    162 	if (bwfm_fwvar_cmd_get_int(sc, BWFM_C_GET_VERSION, &tmp)) {
    163 		printf("%s: could not read io type\n", DEVNAME(sc));
    164 		return;
    165 	} else
    166 		sc->sc_io_type = tmp;
    167 	if (bwfm_fwvar_var_get_data(sc, "cur_etheraddr", ic->ic_myaddr,
    168 	    sizeof(ic->ic_myaddr))) {
    169 		printf("%s: could not read mac address\n", DEVNAME(sc));
    170 		return;
    171 	}
    172 
    173 	memset(fw_version, 0, sizeof(fw_version));
    174 	if (bwfm_fwvar_var_get_data(sc, "ver", fw_version, sizeof(fw_version)) == 0)
    175 		printf("%s: %s", DEVNAME(sc), fw_version);
    176 	printf("%s: address %s\n", DEVNAME(sc), ether_sprintf(ic->ic_myaddr));
    177 
    178 	ic->ic_ifp = ifp;
    179 	ic->ic_phytype = IEEE80211_T_OFDM;
    180 	ic->ic_opmode = IEEE80211_M_STA;
    181 	ic->ic_state = IEEE80211_S_INIT;
    182 
    183 	ic->ic_caps =
    184 	    IEEE80211_C_WEP |
    185 	    IEEE80211_C_TKIP |
    186 	    IEEE80211_C_AES |
    187 	    IEEE80211_C_AES_CCM |
    188 #if notyet
    189 	    IEEE80211_C_MONITOR |		/* monitor mode supported */
    190 	    IEEE80211_C_IBSS |
    191 	    IEEE80211_C_TXPMGT |
    192 	    IEEE80211_C_WME |
    193 #endif
    194 	    IEEE80211_C_SHSLOT |		/* short slot time supported */
    195 	    IEEE80211_C_SHPREAMBLE |		/* short preamble supported */
    196 	    IEEE80211_C_WPA |			/* 802.11i */
    197 	    /* IEEE80211_C_WPA_4WAY */0;		/* WPA 4-way handshake in hw */
    198 
    199 	/* IBSS channel undefined for now. */
    200 	ic->ic_ibss_chan = &ic->ic_channels[0];
    201 
    202 	if (bwfm_fwvar_cmd_get_data(sc, BWFM_C_GET_BANDLIST, bandlist,
    203 	    sizeof(bandlist))) {
    204 		printf("%s: couldn't get supported band list\n", DEVNAME(sc));
    205 		return;
    206 	}
    207 	const u_int nbands = le32toh(bandlist[0]);
    208 	for (i = 1; i <= MIN(nbands, __arraycount(bandlist) - 1); i++) {
    209 		switch (le32toh(bandlist[i])) {
    210 		case BWFM_BAND_2G:
    211 			ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
    212 			ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g;
    213 
    214 			for (j = 0; j < __arraycount(bwfm_2ghz_channels); j++) {
    215 				uint8_t chan = bwfm_2ghz_channels[j];
    216 				ic->ic_channels[chan].ic_freq =
    217 				    ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ);
    218 				ic->ic_channels[chan].ic_flags =
    219 				    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
    220 				    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
    221 			}
    222 			break;
    223 		case BWFM_BAND_5G:
    224 			ic->ic_sup_rates[IEEE80211_MODE_11A] = ieee80211_std_rateset_11a;
    225 
    226 			for (j = 0; j < __arraycount(bwfm_5ghz_channels); j++) {
    227 				uint8_t chan = bwfm_5ghz_channels[j];
    228 				ic->ic_channels[chan].ic_freq =
    229 				    ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ);
    230 				ic->ic_channels[chan].ic_flags =
    231 				    IEEE80211_CHAN_A;
    232 			}
    233 			break;
    234 		}
    235 	}
    236 
    237 	ifp->if_softc = sc;
    238 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
    239 	ifp->if_init = bwfm_init;
    240 	ifp->if_ioctl = bwfm_ioctl;
    241 	ifp->if_start = bwfm_start;
    242 	ifp->if_stop = bwfm_stop;
    243 	ifp->if_watchdog = bwfm_watchdog;
    244 	IFQ_SET_READY(&ifp->if_snd);
    245 	memcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ);
    246 
    247 	error = if_initialize(ifp);
    248 	if (error != 0) {
    249 		printf("%s: if_initialize failed(%d)\n", DEVNAME(sc), error);
    250 		pcq_destroy(sc->sc_freetask);
    251 		workqueue_destroy(sc->sc_taskq);
    252 
    253 		return; /* Error */
    254 	}
    255 
    256 	ieee80211_ifattach(ic);
    257 	ifp->if_percpuq = if_percpuq_create(ifp);
    258 	if_deferred_start_init(ifp, NULL);
    259 	if_register(ifp);
    260 
    261 	sc->sc_newstate = ic->ic_newstate;
    262 	ic->ic_newstate = bwfm_newstate;
    263 	ic->ic_newassoc = bwfm_newassoc;
    264 	ic->ic_send_mgmt = bwfm_send_mgmt;
    265 	ic->ic_recv_mgmt = bwfm_recv_mgmt;
    266 	ic->ic_crypto.cs_key_set = bwfm_key_set;
    267 	ic->ic_crypto.cs_key_delete = bwfm_key_delete;
    268 	ieee80211_media_init(ic, bwfm_media_change, ieee80211_media_status);
    269 
    270 	ieee80211_announce(ic);
    271 
    272 	sc->sc_if_attached = true;
    273 }
    274 
    275 int
    276 bwfm_detach(struct bwfm_softc *sc, int flags)
    277 {
    278 	struct ieee80211com *ic = &sc->sc_ic;
    279 	struct ifnet *ifp = ic->ic_ifp;
    280 
    281 	if (sc->sc_if_attached) {
    282 		bpf_detach(ifp);
    283 		ieee80211_ifdetach(ic);
    284 		if_detach(ifp);
    285 	}
    286 
    287 	if (sc->sc_taskq)
    288 		workqueue_destroy(sc->sc_taskq);
    289 	if (sc->sc_freetask)
    290 		pcq_destroy(sc->sc_freetask);
    291 
    292 	return 0;
    293 }
    294 
    295 void
    296 bwfm_start(struct ifnet *ifp)
    297 {
    298 	struct bwfm_softc *sc = ifp->if_softc;
    299 	struct ieee80211com *ic = &sc->sc_ic;
    300 	struct mbuf *m;
    301 	int error;
    302 
    303 	if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
    304 		return;
    305 
    306 	/* TODO: return if no link? */
    307 
    308 	for (;;) {
    309 		/* Discard management packets (fw handles this for us) */
    310 		IF_DEQUEUE(&ic->ic_mgtq, m);
    311 		if (m != NULL) {
    312 			m_freem(m);
    313 			continue;
    314 		}
    315 
    316 		if (sc->sc_bus_ops->bs_txcheck(sc)) {
    317 			ifp->if_flags |= IFF_OACTIVE;
    318 			break;
    319 		}
    320 
    321 		IFQ_DEQUEUE(&ifp->if_snd, m);
    322 		if (m == NULL)
    323 			break;
    324 
    325 		error = sc->sc_bus_ops->bs_txdata(sc, &m);
    326 		if (error == ENOBUFS) {
    327 			IF_PREPEND(&ifp->if_snd, m);
    328 			ifp->if_flags |= IFF_OACTIVE;
    329 			break;
    330 		}
    331 		if (error != 0) {
    332 			ifp->if_oerrors++;
    333 			m_freem(m);
    334 			continue;
    335 		}
    336 
    337 		bpf_mtap(ifp, m, BPF_D_OUT);
    338 	}
    339 }
    340 
    341 int
    342 bwfm_init(struct ifnet *ifp)
    343 {
    344 	struct bwfm_softc *sc = ifp->if_softc;
    345 	struct ieee80211com *ic = &sc->sc_ic;
    346 	uint8_t evmask[BWFM_EVENT_MASK_LEN];
    347 	struct bwfm_join_pref_params join_pref[2];
    348 	int pm;
    349 
    350 	if (bwfm_fwvar_var_set_int(sc, "mpc", 1)) {
    351 		printf("%s: could not set mpc\n", DEVNAME(sc));
    352 		return EIO;
    353 	}
    354 
    355 	/* Select target by RSSI (boost on 5GHz) */
    356 	join_pref[0].type = BWFM_JOIN_PREF_RSSI_DELTA;
    357 	join_pref[0].len = 2;
    358 	join_pref[0].rssi_gain = BWFM_JOIN_PREF_RSSI_BOOST;
    359 	join_pref[0].band = BWFM_JOIN_PREF_BAND_5G;
    360 	join_pref[1].type = BWFM_JOIN_PREF_RSSI;
    361 	join_pref[1].len = 2;
    362 	join_pref[1].rssi_gain = 0;
    363 	join_pref[1].band = 0;
    364 	if (bwfm_fwvar_var_set_data(sc, "join_pref", join_pref,
    365 	    sizeof(join_pref))) {
    366 		printf("%s: could not set join pref\n", DEVNAME(sc));
    367 		return EIO;
    368 	}
    369 
    370 	memset(evmask, 0, sizeof(evmask));
    371 
    372 #define	ENABLE_EVENT(e)		evmask[(e) / 8] |= 1 << ((e) % 8)
    373 	/* Events used to drive the state machine */
    374 	switch (ic->ic_opmode) {
    375 	case IEEE80211_M_STA:
    376 		ENABLE_EVENT(BWFM_E_IF);
    377 		ENABLE_EVENT(BWFM_E_LINK);
    378 		ENABLE_EVENT(BWFM_E_AUTH);
    379 		ENABLE_EVENT(BWFM_E_ASSOC);
    380 		ENABLE_EVENT(BWFM_E_DEAUTH);
    381 		ENABLE_EVENT(BWFM_E_DISASSOC);
    382 		ENABLE_EVENT(BWFM_E_SET_SSID);
    383 		ENABLE_EVENT(BWFM_E_ESCAN_RESULT);
    384 		break;
    385 #ifndef IEEE80211_STA_ONLY
    386 	case IEEE80211_M_HOSTAP:
    387 		ENABLE_EVENT(BWFM_E_AUTH_IND);
    388 		ENABLE_EVENT(BWFM_E_ASSOC_IND);
    389 		ENABLE_EVENT(BWFM_E_REASSOC_IND);
    390 		ENABLE_EVENT(BWFM_E_DEAUTH_IND);
    391 		ENABLE_EVENT(BWFM_E_DISASSOC_IND);
    392 		ENABLE_EVENT(BWFM_E_ESCAN_RESULT);
    393 		ENABLE_EVENT(BWFM_E_ESCAN_RESULT);
    394 		break;
    395 #endif
    396 	default:
    397 		break;
    398 	}
    399 #undef	ENABLE_EVENT
    400 
    401 #ifdef BWFM_DEBUG
    402 	memset(evmask, 0xff, sizeof(evmask));
    403 #endif
    404 
    405 	if (bwfm_fwvar_var_set_data(sc, "event_msgs", evmask, sizeof(evmask))) {
    406 		printf("%s: could not set event mask\n", DEVNAME(sc));
    407 		return EIO;
    408 	}
    409 
    410 	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_CHANNEL_TIME,
    411 	    BWFM_DEFAULT_SCAN_CHANNEL_TIME)) {
    412 		printf("%s: could not set scan channel time\n", DEVNAME(sc));
    413 		return EIO;
    414 	}
    415 	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_UNASSOC_TIME,
    416 	    BWFM_DEFAULT_SCAN_UNASSOC_TIME)) {
    417 		printf("%s: could not set scan unassoc time\n", DEVNAME(sc));
    418 		return EIO;
    419 	}
    420 	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_PASSIVE_TIME,
    421 	    BWFM_DEFAULT_SCAN_PASSIVE_TIME)) {
    422 		printf("%s: could not set scan passive time\n", DEVNAME(sc));
    423 		return EIO;
    424 	}
    425 
    426         /*
    427          * Use CAM (constantly awake) when we are running as AP
    428          * otherwise use fast power saving.
    429          */
    430 	pm = BWFM_PM_FAST_PS;
    431 #ifndef IEEE80211_STA_ONLY
    432 	if (ic->ic_opmode == IEEE80211_M_HOSTAP)
    433 		pm = BWFM_PM_CAM;
    434 #endif
    435 	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, pm)) {
    436 		printf("%s: could not set power\n", DEVNAME(sc));
    437 		return EIO;
    438 	}
    439 
    440 	bwfm_fwvar_var_set_int(sc, "txbf", 1);
    441 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_UP, 0);
    442 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_INFRA, 1);
    443 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_AP, 0);
    444 
    445 	/* Disable all offloading (ARP, NDP, TCP/UDP cksum). */
    446 	bwfm_fwvar_var_set_int(sc, "arp_ol", 0);
    447 	bwfm_fwvar_var_set_int(sc, "arpoe", 0);
    448 	bwfm_fwvar_var_set_int(sc, "ndoe", 0);
    449 	bwfm_fwvar_var_set_int(sc, "toe", 0);
    450 
    451 	/* Accept all multicast frames. */
    452 	bwfm_fwvar_var_set_int(sc, "allmulti", 1);
    453 
    454 	/* Setup promiscuous mode */
    455 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PROMISC,
    456 	    (ifp->if_flags & IFF_PROMISC) ? 1 : 0);
    457 
    458 	/*
    459 	 * Tell the firmware supplicant that we are going to handle the
    460 	 * WPA handshake ourselves.
    461 	 */
    462 	bwfm_fwvar_var_set_int(sc, "sup_wpa", 0);
    463 
    464 	ifp->if_flags |= IFF_RUNNING;
    465 	ifp->if_flags &= ~IFF_OACTIVE;
    466 
    467 	if (ic->ic_opmode != IEEE80211_M_MONITOR) {
    468 		if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
    469 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
    470 	} else {
    471 		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
    472 	}
    473 
    474 	return 0;
    475 }
    476 
    477 void
    478 bwfm_stop(struct ifnet *ifp, int disable)
    479 {
    480 	struct bwfm_softc *sc = ifp->if_softc;
    481 	struct ieee80211com *ic = &sc->sc_ic;
    482 	struct bwfm_join_params join;
    483 
    484 	sc->sc_tx_timer = 0;
    485 	ifp->if_timer = 0;
    486 	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
    487 
    488 	memset(&join, 0, sizeof(join));
    489 	bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_SSID, &join, sizeof(join));
    490 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_DOWN, 1);
    491 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, 0);
    492 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_AP, 0);
    493 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_INFRA, 0);
    494 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_UP, 1);
    495 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, BWFM_PM_FAST_PS);
    496 
    497 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
    498 
    499 	if (sc->sc_bus_ops->bs_stop)
    500 		sc->sc_bus_ops->bs_stop(sc);
    501 }
    502 
    503 void
    504 bwfm_watchdog(struct ifnet *ifp)
    505 {
    506 	struct bwfm_softc *sc = ifp->if_softc;
    507 	struct ieee80211com *ic = &sc->sc_ic;
    508 
    509 	ifp->if_timer = 0;
    510 
    511 	if (sc->sc_tx_timer > 0) {
    512 		if (--sc->sc_tx_timer == 0) {
    513 			printf("%s: device timeout\n", DEVNAME(sc));
    514 			ifp->if_oerrors++;
    515 			return;
    516 		}
    517 		ifp->if_timer = 1;
    518 	}
    519 	ieee80211_watchdog(ic);
    520 }
    521 
    522 int
    523 bwfm_ioctl(struct ifnet *ifp, u_long cmd, void *data)
    524 {
    525 	struct bwfm_softc *sc = ifp->if_softc;
    526 	struct ieee80211com *ic = &sc->sc_ic;
    527 	int s, error = 0;
    528 
    529 	s = splnet();
    530 
    531 	switch (cmd) {
    532 	case SIOCSIFFLAGS:
    533 		if ((error = ifioctl_common(ifp, cmd, data)) != 0)
    534 			break;
    535 		switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
    536 		case IFF_UP | IFF_RUNNING:
    537 			break;
    538 		case IFF_UP:
    539 			bwfm_init(ifp);
    540 			break;
    541 		case IFF_RUNNING:
    542 			bwfm_stop(ifp, 1);
    543 			break;
    544 		case 0:
    545 			break;
    546 		}
    547 		break;
    548 
    549 	case SIOCADDMULTI:
    550 	case SIOCDELMULTI:
    551 		if ((error = ether_ioctl(ifp, cmd, data)) == ENETRESET) {
    552 			/* setup multicast filter, etc */
    553 			error = 0;
    554 		}
    555 		break;
    556 
    557 	case SIOCGIFMEDIA:
    558 		error = ieee80211_ioctl(ic, cmd, data);
    559 		if (error == 0 && ic->ic_state == IEEE80211_S_RUN)
    560 			bwfm_get_sta_info(sc, (struct ifmediareq *)data);
    561 		break;
    562 
    563 	default:
    564 		error = ieee80211_ioctl(ic, cmd, data);
    565 	}
    566 
    567 	if (error == ENETRESET) {
    568 		if ((ifp->if_flags & IFF_UP) != 0 &&
    569 		    (ifp->if_flags & IFF_RUNNING) != 0 &&
    570 		    ic->ic_roaming != IEEE80211_ROAMING_MANUAL) {
    571 			bwfm_init(ifp);
    572 		}
    573 		error = 0;
    574 	}
    575 
    576 	splx(s);
    577 
    578 	return error;
    579 }
    580 
    581 int
    582 bwfm_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
    583     int type, int arg)
    584 {
    585 	return 0;
    586 }
    587 
    588 void
    589 bwfm_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
    590     struct ieee80211_node *ni, int subtype, int rssi, uint32_t rstamp)
    591 {
    592 }
    593 
    594 int
    595 bwfm_key_set(struct ieee80211com *ic, const struct ieee80211_key *wk,
    596     const uint8_t mac[IEEE80211_ADDR_LEN])
    597 {
    598 	struct bwfm_softc *sc = ic->ic_ifp->if_softc;
    599 	struct bwfm_task *t;
    600 
    601 	t = pcq_get(sc->sc_freetask);
    602 	if (t == NULL) {
    603 		printf("%s: no free tasks\n", DEVNAME(sc));
    604 		return 0;
    605 	}
    606 
    607 	t->t_cmd = BWFM_TASK_KEY_SET;
    608 	t->t_key.key = wk;
    609 	memcpy(t->t_key.mac, mac, sizeof(t->t_key.mac));
    610 	workqueue_enqueue(sc->sc_taskq, (struct work *)t, NULL);
    611 	return 1;
    612 }
    613 
    614 static void
    615 bwfm_key_set_cb(struct bwfm_softc *sc, struct bwfm_cmd_key *ck)
    616 {
    617 	const struct ieee80211_key *wk = ck->key;
    618 	const uint8_t *mac = ck->mac;
    619 	struct bwfm_wsec_key wsec_key;
    620 	uint32_t wsec_enable, wsec;
    621 	bool ext_key;
    622 
    623 #ifdef BWFM_DEBUG
    624 	printf("key_set: key cipher %s len %d: ", wk->wk_cipher->ic_name, wk->wk_keylen);
    625 	for (int j = 0; j < sizeof(wk->wk_key); j++)
    626 		printf("%02x", wk->wk_key[j]);
    627 #endif
    628 
    629 	if ((wk->wk_flags & IEEE80211_KEY_GROUP) == 0 &&
    630 	    wk->wk_cipher->ic_cipher != IEEE80211_CIPHER_WEP) {
    631 		ext_key = true;
    632 	} else {
    633 		ext_key = false;
    634 	}
    635 
    636 #ifdef BWFM_DEBUG
    637 	printf(", ext_key = %d", ext_key);
    638 	printf(", mac = %02x:%02x:%02x:%02x:%02x:%02x",
    639 	    mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    640 	printf("\n");
    641 #endif
    642 
    643 	memset(&wsec_key, 0, sizeof(wsec_key));
    644 	if (ext_key && !IEEE80211_IS_MULTICAST(mac))
    645 		memcpy(wsec_key.ea, mac, sizeof(wsec_key.ea));
    646 	wsec_key.index = htole32(wk->wk_keyix);
    647 	wsec_key.len = htole32(wk->wk_keylen);
    648 	memcpy(wsec_key.data, wk->wk_key, sizeof(wsec_key.data));
    649 	if (!ext_key)
    650 		wsec_key.flags = htole32(BWFM_WSEC_PRIMARY_KEY);
    651 
    652 	switch (wk->wk_cipher->ic_cipher) {
    653 	case IEEE80211_CIPHER_WEP:
    654 		if (wk->wk_keylen == 5)
    655 			wsec_key.algo = htole32(BWFM_CRYPTO_ALGO_WEP1);
    656 		else if (wk->wk_keylen == 13)
    657 			wsec_key.algo = htole32(BWFM_CRYPTO_ALGO_WEP128);
    658 		else
    659 			return;
    660 		wsec_enable = BWFM_WSEC_WEP;
    661 		break;
    662 	case IEEE80211_CIPHER_TKIP:
    663 		wsec_key.algo = htole32(BWFM_CRYPTO_ALGO_TKIP);
    664 		wsec_enable = BWFM_WSEC_TKIP;
    665 		break;
    666 	case IEEE80211_CIPHER_AES_CCM:
    667 		wsec_key.algo = htole32(BWFM_CRYPTO_ALGO_AES_CCM);
    668 		wsec_enable = BWFM_WSEC_AES;
    669 		break;
    670 	default:
    671 		printf("%s: %s: cipher %s not supported\n", DEVNAME(sc),
    672 		    __func__, wk->wk_cipher->ic_name);
    673 		return;
    674 	}
    675 
    676 	if (bwfm_fwvar_var_set_data(sc, "wsec_key", &wsec_key, sizeof(wsec_key)))
    677 		return;
    678 
    679 	bwfm_fwvar_var_set_int(sc, "wpa_auth", BWFM_WPA_AUTH_WPA2_PSK);
    680 
    681 	bwfm_fwvar_var_get_int(sc, "wsec", &wsec);
    682 	wsec |= wsec_enable;
    683 	bwfm_fwvar_var_set_int(sc, "wsec", wsec);
    684 }
    685 
    686 int
    687 bwfm_key_delete(struct ieee80211com *ic, const struct ieee80211_key *wk)
    688 {
    689 	struct bwfm_softc *sc = ic->ic_ifp->if_softc;
    690 	struct bwfm_task *t;
    691 
    692 	t = pcq_get(sc->sc_freetask);
    693 	if (t == NULL) {
    694 		printf("%s: no free tasks\n", DEVNAME(sc));
    695 		return 0;
    696 	}
    697 
    698 	t->t_cmd = BWFM_TASK_KEY_DELETE;
    699 	t->t_key.key = wk;
    700 	memset(t->t_key.mac, 0, sizeof(t->t_key.mac));
    701 	workqueue_enqueue(sc->sc_taskq, (struct work *)t, NULL);
    702 
    703 	return 1;
    704 }
    705 
    706 static void
    707 bwfm_key_delete_cb(struct bwfm_softc *sc, struct bwfm_cmd_key *ck)
    708 {
    709 	const struct ieee80211_key *wk = ck->key;
    710 	struct bwfm_wsec_key wsec_key;
    711 
    712 	memset(&wsec_key, 0, sizeof(wsec_key));
    713 	wsec_key.index = htole32(wk->wk_keyix);
    714 	wsec_key.flags = htole32(BWFM_WSEC_PRIMARY_KEY);
    715 
    716 	if (bwfm_fwvar_var_set_data(sc, "wsec_key", &wsec_key, sizeof(wsec_key)))
    717 		return;
    718 }
    719 
    720 int
    721 bwfm_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
    722 {
    723 	struct bwfm_softc *sc = ic->ic_ifp->if_softc;
    724 	struct bwfm_task *t;
    725 
    726 	t = pcq_get(sc->sc_freetask);
    727 	if (t == NULL) {
    728 		printf("%s: no free tasks\n", DEVNAME(sc));
    729 		return EIO;
    730 	}
    731 
    732 	t->t_cmd = BWFM_TASK_NEWSTATE;
    733 	t->t_newstate.state = nstate;
    734 	t->t_newstate.arg = arg;
    735 	workqueue_enqueue(sc->sc_taskq, (struct work *)t, NULL);
    736 
    737 	return 0;
    738 }
    739 
    740 void
    741 bwfm_newstate_cb(struct bwfm_softc *sc, struct bwfm_cmd_newstate *cmd)
    742 {
    743 	struct ieee80211com *ic = &sc->sc_ic;
    744 	enum ieee80211_state ostate = ic->ic_state;
    745 	enum ieee80211_state nstate = cmd->state;
    746 	int s;
    747 
    748 	DPRINTF(("%s: newstate %d -> %d\n", DEVNAME(sc), ostate, nstate));
    749 
    750 	s = splnet();
    751 
    752 	switch (nstate) {
    753 	case IEEE80211_S_INIT:
    754 		break;
    755 
    756 	case IEEE80211_S_SCAN:
    757 		if (ostate != IEEE80211_S_SCAN) {
    758 			/* Start of scanning */
    759 			bwfm_scan(sc);
    760 		}
    761 		break;
    762 
    763 	case IEEE80211_S_AUTH:
    764 		bwfm_connect(sc);
    765 		break;
    766 
    767 	case IEEE80211_S_ASSOC:
    768 		break;
    769 
    770 	case IEEE80211_S_RUN:
    771 		break;
    772 	}
    773 
    774 	sc->sc_newstate(ic, nstate, cmd->arg);
    775 
    776 	splx(s);
    777 }
    778 
    779 void
    780 bwfm_newassoc(struct ieee80211_node *ni, int isnew)
    781 {
    782 	/* Firmware handles rate adaptation for us */
    783 	ni->ni_txrate = 0;
    784 }
    785 
    786 void
    787 bwfm_task(struct work *wk, void *arg)
    788 {
    789 	struct bwfm_task *t = (struct bwfm_task *)wk;
    790 	struct bwfm_softc *sc = t->t_sc;
    791 
    792 	switch (t->t_cmd) {
    793 	case BWFM_TASK_NEWSTATE:
    794 		bwfm_newstate_cb(sc, &t->t_newstate);
    795 		break;
    796 	case BWFM_TASK_KEY_SET:
    797 		bwfm_key_set_cb(sc, &t->t_key);
    798 		break;
    799 	case BWFM_TASK_KEY_DELETE:
    800 		bwfm_key_delete_cb(sc, &t->t_key);
    801 		break;
    802 	case BWFM_TASK_RX_EVENT:
    803 		bwfm_rx_event_cb(sc, t->t_mbuf);
    804 		break;
    805 	default:
    806 		panic("bwfm: unknown task command %d", t->t_cmd);
    807 	}
    808 
    809 	pcq_put(sc->sc_freetask, t);
    810 }
    811 
    812 int
    813 bwfm_media_change(struct ifnet *ifp)
    814 {
    815 	return 0;
    816 }
    817 
    818 /* Chip initialization (SDIO, PCIe) */
    819 int
    820 bwfm_chip_attach(struct bwfm_softc *sc)
    821 {
    822 	struct bwfm_core *core;
    823 	int need_socram = 0;
    824 	int has_socram = 0;
    825 	int cpu_found = 0;
    826 	uint32_t val;
    827 
    828 	LIST_INIT(&sc->sc_chip.ch_list);
    829 
    830 	if (sc->sc_buscore_ops->bc_prepare(sc) != 0) {
    831 		printf("%s: failed buscore prepare\n", DEVNAME(sc));
    832 		return 1;
    833 	}
    834 
    835 	val = sc->sc_buscore_ops->bc_read(sc,
    836 	    BWFM_CHIP_BASE + BWFM_CHIP_REG_CHIPID);
    837 	sc->sc_chip.ch_chip = BWFM_CHIP_CHIPID_ID(val);
    838 	sc->sc_chip.ch_chiprev = BWFM_CHIP_CHIPID_REV(val);
    839 
    840 	if ((sc->sc_chip.ch_chip > 0xa000) || (sc->sc_chip.ch_chip < 0x4000))
    841 		snprintf(sc->sc_chip.ch_name, sizeof(sc->sc_chip.ch_name),
    842 		    "%d", sc->sc_chip.ch_chip);
    843 	else
    844 		snprintf(sc->sc_chip.ch_name, sizeof(sc->sc_chip.ch_name),
    845 		    "%x", sc->sc_chip.ch_chip);
    846 
    847 	switch (BWFM_CHIP_CHIPID_TYPE(val))
    848 	{
    849 	case BWFM_CHIP_CHIPID_TYPE_SOCI_SB:
    850 		printf("%s: SoC interconnect SB not implemented\n",
    851 		    DEVNAME(sc));
    852 		return 1;
    853 	case BWFM_CHIP_CHIPID_TYPE_SOCI_AI:
    854 		sc->sc_chip.ch_core_isup = bwfm_chip_ai_isup;
    855 		sc->sc_chip.ch_core_disable = bwfm_chip_ai_disable;
    856 		sc->sc_chip.ch_core_reset = bwfm_chip_ai_reset;
    857 		bwfm_chip_dmp_erom_scan(sc);
    858 		break;
    859 	default:
    860 		printf("%s: SoC interconnect %d unknown\n",
    861 		    DEVNAME(sc), BWFM_CHIP_CHIPID_TYPE(val));
    862 		return 1;
    863 	}
    864 
    865 	LIST_FOREACH(core, &sc->sc_chip.ch_list, co_link) {
    866 		DPRINTF(("%s: 0x%x:%-2d base 0x%08x wrap 0x%08x\n",
    867 		    DEVNAME(sc), core->co_id, core->co_rev,
    868 		    core->co_base, core->co_wrapbase));
    869 
    870 		switch (core->co_id) {
    871 		case BWFM_AGENT_CORE_ARM_CM3:
    872 			need_socram = true;
    873 			/* FALLTHROUGH */
    874 		case BWFM_AGENT_CORE_ARM_CR4:
    875 		case BWFM_AGENT_CORE_ARM_CA7:
    876 			cpu_found = true;
    877 			break;
    878 		case BWFM_AGENT_INTERNAL_MEM:
    879 			has_socram = true;
    880 			break;
    881 		default:
    882 			break;
    883 		}
    884 	}
    885 
    886 	if (!cpu_found) {
    887 		printf("%s: CPU core not detected\n", DEVNAME(sc));
    888 		return 1;
    889 	}
    890 	if (need_socram && !has_socram) {
    891 		printf("%s: RAM core not provided\n", DEVNAME(sc));
    892 		return 1;
    893 	}
    894 
    895 	bwfm_chip_set_passive(sc);
    896 
    897 	if (sc->sc_buscore_ops->bc_reset) {
    898 		sc->sc_buscore_ops->bc_reset(sc);
    899 		bwfm_chip_set_passive(sc);
    900 	}
    901 
    902 	if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4)) != NULL) {
    903 		bwfm_chip_tcm_ramsize(sc, core);
    904 		bwfm_chip_tcm_rambase(sc);
    905 	} else if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_SYS_MEM)) != NULL) {
    906 		bwfm_chip_sysmem_ramsize(sc, core);
    907 		bwfm_chip_tcm_rambase(sc);
    908 	} else if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM)) != NULL) {
    909 		bwfm_chip_socram_ramsize(sc, core);
    910 	}
    911 
    912 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
    913 	sc->sc_chip.ch_cc_caps = sc->sc_buscore_ops->bc_read(sc,
    914 	    core->co_base + BWFM_CHIP_REG_CAPABILITIES);
    915 	sc->sc_chip.ch_cc_caps_ext = sc->sc_buscore_ops->bc_read(sc,
    916 	    core->co_base + BWFM_CHIP_REG_CAPABILITIES_EXT);
    917 
    918 	core = bwfm_chip_get_pmu(sc);
    919 	if (sc->sc_chip.ch_cc_caps & BWFM_CHIP_REG_CAPABILITIES_PMU) {
    920 		sc->sc_chip.ch_pmucaps = sc->sc_buscore_ops->bc_read(sc,
    921 		    core->co_base + BWFM_CHIP_REG_PMUCAPABILITIES);
    922 		sc->sc_chip.ch_pmurev = sc->sc_chip.ch_pmucaps &
    923 		    BWFM_CHIP_REG_PMUCAPABILITIES_REV_MASK;
    924 	}
    925 
    926 	if (sc->sc_buscore_ops->bc_setup)
    927 		sc->sc_buscore_ops->bc_setup(sc);
    928 
    929 	return 0;
    930 }
    931 
    932 struct bwfm_core *
    933 bwfm_chip_get_core(struct bwfm_softc *sc, int id)
    934 {
    935 	struct bwfm_core *core;
    936 
    937 	LIST_FOREACH(core, &sc->sc_chip.ch_list, co_link) {
    938 		if (core->co_id == id)
    939 			return core;
    940 	}
    941 
    942 	return NULL;
    943 }
    944 
    945 struct bwfm_core *
    946 bwfm_chip_get_pmu(struct bwfm_softc *sc)
    947 {
    948 	struct bwfm_core *cc, *pmu;
    949 
    950 	cc = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
    951 	if (cc->co_rev >= 35 && sc->sc_chip.ch_cc_caps_ext &
    952 	    BWFM_CHIP_REG_CAPABILITIES_EXT_AOB_PRESENT) {
    953 		pmu = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_PMU);
    954 		if (pmu)
    955 			return pmu;
    956 	}
    957 
    958 	return cc;
    959 }
    960 
    961 /* Functions for the AI interconnect */
    962 int
    963 bwfm_chip_ai_isup(struct bwfm_softc *sc, struct bwfm_core *core)
    964 {
    965 	uint32_t ioctl, reset;
    966 
    967 	ioctl = sc->sc_buscore_ops->bc_read(sc,
    968 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
    969 	reset = sc->sc_buscore_ops->bc_read(sc,
    970 	    core->co_wrapbase + BWFM_AGENT_RESET_CTL);
    971 
    972 	if (((ioctl & (BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK)) ==
    973 	    BWFM_AGENT_IOCTL_CLK) &&
    974 	    ((reset & BWFM_AGENT_RESET_CTL_RESET) == 0))
    975 		return 1;
    976 
    977 	return 0;
    978 }
    979 
    980 void
    981 bwfm_chip_ai_disable(struct bwfm_softc *sc, struct bwfm_core *core,
    982     uint32_t prereset, uint32_t reset)
    983 {
    984 	uint32_t val;
    985 	int i;
    986 
    987 	val = sc->sc_buscore_ops->bc_read(sc,
    988 	    core->co_wrapbase + BWFM_AGENT_RESET_CTL);
    989 	if ((val & BWFM_AGENT_RESET_CTL_RESET) == 0) {
    990 
    991 		sc->sc_buscore_ops->bc_write(sc,
    992 		    core->co_wrapbase + BWFM_AGENT_IOCTL,
    993 		    prereset | BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK);
    994 		sc->sc_buscore_ops->bc_read(sc,
    995 		    core->co_wrapbase + BWFM_AGENT_IOCTL);
    996 
    997 		sc->sc_buscore_ops->bc_write(sc,
    998 		    core->co_wrapbase + BWFM_AGENT_RESET_CTL,
    999 		    BWFM_AGENT_RESET_CTL_RESET);
   1000 		delay(20);
   1001 
   1002 		for (i = 300; i > 0; i--) {
   1003 			if (sc->sc_buscore_ops->bc_read(sc,
   1004 			    core->co_wrapbase + BWFM_AGENT_RESET_CTL) ==
   1005 			    BWFM_AGENT_RESET_CTL_RESET)
   1006 				break;
   1007 		}
   1008 		if (i == 0)
   1009 			printf("%s: timeout on core reset\n", DEVNAME(sc));
   1010 	}
   1011 
   1012 	sc->sc_buscore_ops->bc_write(sc,
   1013 	    core->co_wrapbase + BWFM_AGENT_IOCTL,
   1014 	    reset | BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK);
   1015 	sc->sc_buscore_ops->bc_read(sc,
   1016 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
   1017 }
   1018 
   1019 void
   1020 bwfm_chip_ai_reset(struct bwfm_softc *sc, struct bwfm_core *core,
   1021     uint32_t prereset, uint32_t reset, uint32_t postreset)
   1022 {
   1023 	int i;
   1024 
   1025 	bwfm_chip_ai_disable(sc, core, prereset, reset);
   1026 
   1027 	for (i = 50; i > 0; i--) {
   1028 		if ((sc->sc_buscore_ops->bc_read(sc,
   1029 		    core->co_wrapbase + BWFM_AGENT_RESET_CTL) &
   1030 		    BWFM_AGENT_RESET_CTL_RESET) == 0)
   1031 			break;
   1032 		sc->sc_buscore_ops->bc_write(sc,
   1033 		    core->co_wrapbase + BWFM_AGENT_RESET_CTL, 0);
   1034 		delay(60);
   1035 	}
   1036 	if (i == 0)
   1037 		printf("%s: timeout on core reset\n", DEVNAME(sc));
   1038 
   1039 	sc->sc_buscore_ops->bc_write(sc,
   1040 	    core->co_wrapbase + BWFM_AGENT_IOCTL,
   1041 	    postreset | BWFM_AGENT_IOCTL_CLK);
   1042 	sc->sc_buscore_ops->bc_read(sc,
   1043 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
   1044 }
   1045 
   1046 void
   1047 bwfm_chip_dmp_erom_scan(struct bwfm_softc *sc)
   1048 {
   1049 	uint32_t erom, val, base, wrap;
   1050 	uint8_t type = 0;
   1051 	uint16_t id;
   1052 	uint8_t nmw, nsw, rev;
   1053 	struct bwfm_core *core;
   1054 
   1055 	erom = sc->sc_buscore_ops->bc_read(sc,
   1056 	    BWFM_CHIP_BASE + BWFM_CHIP_REG_EROMPTR);
   1057 	while (type != BWFM_DMP_DESC_EOT) {
   1058 		val = sc->sc_buscore_ops->bc_read(sc, erom);
   1059 		type = val & BWFM_DMP_DESC_MASK;
   1060 		erom += 4;
   1061 
   1062 		if (type != BWFM_DMP_DESC_COMPONENT)
   1063 			continue;
   1064 
   1065 		id = (val & BWFM_DMP_COMP_PARTNUM)
   1066 		    >> BWFM_DMP_COMP_PARTNUM_S;
   1067 
   1068 		val = sc->sc_buscore_ops->bc_read(sc, erom);
   1069 		type = val & BWFM_DMP_DESC_MASK;
   1070 		erom += 4;
   1071 
   1072 		if (type != BWFM_DMP_DESC_COMPONENT) {
   1073 			printf("%s: not component descriptor\n", DEVNAME(sc));
   1074 			return;
   1075 		}
   1076 
   1077 		nmw = (val & BWFM_DMP_COMP_NUM_MWRAP)
   1078 		    >> BWFM_DMP_COMP_NUM_MWRAP_S;
   1079 		nsw = (val & BWFM_DMP_COMP_NUM_SWRAP)
   1080 		    >> BWFM_DMP_COMP_NUM_SWRAP_S;
   1081 		rev = (val & BWFM_DMP_COMP_REVISION)
   1082 		    >> BWFM_DMP_COMP_REVISION_S;
   1083 
   1084 		if (nmw + nsw == 0 && id != BWFM_AGENT_CORE_PMU)
   1085 			continue;
   1086 
   1087 		if (bwfm_chip_dmp_get_regaddr(sc, &erom, &base, &wrap))
   1088 			continue;
   1089 
   1090 		core = kmem_alloc(sizeof(*core), KM_SLEEP);
   1091 		core->co_id = id;
   1092 		core->co_base = base;
   1093 		core->co_wrapbase = wrap;
   1094 		core->co_rev = rev;
   1095 		LIST_INSERT_HEAD(&sc->sc_chip.ch_list, core, co_link);
   1096 	}
   1097 }
   1098 
   1099 int
   1100 bwfm_chip_dmp_get_regaddr(struct bwfm_softc *sc, uint32_t *erom,
   1101     uint32_t *base, uint32_t *wrap)
   1102 {
   1103 	uint8_t type = 0, mpnum __unused = 0;
   1104 	uint8_t stype, sztype, wraptype;
   1105 	uint32_t val;
   1106 
   1107 	*base = 0;
   1108 	*wrap = 0;
   1109 
   1110 	val = sc->sc_buscore_ops->bc_read(sc, *erom);
   1111 	type = val & BWFM_DMP_DESC_MASK;
   1112 	if (type == BWFM_DMP_DESC_MASTER_PORT) {
   1113 		mpnum = (val & BWFM_DMP_MASTER_PORT_NUM)
   1114 		    >> BWFM_DMP_MASTER_PORT_NUM_S;
   1115 		wraptype = BWFM_DMP_SLAVE_TYPE_MWRAP;
   1116 		*erom += 4;
   1117 	} else if ((type & ~BWFM_DMP_DESC_ADDRSIZE_GT32) ==
   1118 	    BWFM_DMP_DESC_ADDRESS)
   1119 		wraptype = BWFM_DMP_SLAVE_TYPE_SWRAP;
   1120 	else
   1121 		return 1;
   1122 
   1123 	do {
   1124 		do {
   1125 			val = sc->sc_buscore_ops->bc_read(sc, *erom);
   1126 			type = val & BWFM_DMP_DESC_MASK;
   1127 			if (type == BWFM_DMP_DESC_COMPONENT)
   1128 				return 0;
   1129 			if (type == BWFM_DMP_DESC_EOT)
   1130 				return 1;
   1131 			*erom += 4;
   1132 		} while ((type & ~BWFM_DMP_DESC_ADDRSIZE_GT32) !=
   1133 		     BWFM_DMP_DESC_ADDRESS);
   1134 
   1135 		if (type & BWFM_DMP_DESC_ADDRSIZE_GT32)
   1136 			*erom += 4;
   1137 
   1138 		sztype = (val & BWFM_DMP_SLAVE_SIZE_TYPE)
   1139 		    >> BWFM_DMP_SLAVE_SIZE_TYPE_S;
   1140 		if (sztype == BWFM_DMP_SLAVE_SIZE_DESC) {
   1141 			val = sc->sc_buscore_ops->bc_read(sc, *erom);
   1142 			type = val & BWFM_DMP_DESC_MASK;
   1143 			if (type & BWFM_DMP_DESC_ADDRSIZE_GT32)
   1144 				*erom += 8;
   1145 			else
   1146 				*erom += 4;
   1147 		}
   1148 		if (sztype != BWFM_DMP_SLAVE_SIZE_4K)
   1149 			continue;
   1150 
   1151 		stype = (val & BWFM_DMP_SLAVE_TYPE) >> BWFM_DMP_SLAVE_TYPE_S;
   1152 		if (*base == 0 && stype == BWFM_DMP_SLAVE_TYPE_SLAVE)
   1153 			*base = val & BWFM_DMP_SLAVE_ADDR_BASE;
   1154 		if (*wrap == 0 && stype == wraptype)
   1155 			*wrap = val & BWFM_DMP_SLAVE_ADDR_BASE;
   1156 	} while (*base == 0 || *wrap == 0);
   1157 
   1158 	return 0;
   1159 }
   1160 
   1161 /* Core configuration */
   1162 int
   1163 bwfm_chip_set_active(struct bwfm_softc *sc, const uint32_t rstvec)
   1164 {
   1165 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL)
   1166 		return bwfm_chip_cr4_set_active(sc, rstvec);
   1167 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL)
   1168 		return bwfm_chip_ca7_set_active(sc, rstvec);
   1169 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL)
   1170 		return bwfm_chip_cm3_set_active(sc);
   1171 	return 1;
   1172 }
   1173 
   1174 void
   1175 bwfm_chip_set_passive(struct bwfm_softc *sc)
   1176 {
   1177 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL) {
   1178 		bwfm_chip_cr4_set_passive(sc);
   1179 		return;
   1180 	}
   1181 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL) {
   1182 		bwfm_chip_ca7_set_passive(sc);
   1183 		return;
   1184 	}
   1185 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL) {
   1186 		bwfm_chip_cm3_set_passive(sc);
   1187 		return;
   1188 	}
   1189 }
   1190 
   1191 int
   1192 bwfm_chip_cr4_set_active(struct bwfm_softc *sc, const uint32_t rstvec)
   1193 {
   1194 	struct bwfm_core *core;
   1195 
   1196 	sc->sc_buscore_ops->bc_activate(sc, rstvec);
   1197 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4);
   1198 	sc->sc_chip.ch_core_reset(sc, core,
   1199 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT, 0, 0);
   1200 
   1201 	return 0;
   1202 }
   1203 
   1204 void
   1205 bwfm_chip_cr4_set_passive(struct bwfm_softc *sc)
   1206 {
   1207 	struct bwfm_core *core;
   1208 	uint32_t val;
   1209 
   1210 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4);
   1211 	val = sc->sc_buscore_ops->bc_read(sc,
   1212 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
   1213 	sc->sc_chip.ch_core_reset(sc, core,
   1214 	    val & BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
   1215 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
   1216 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT);
   1217 
   1218 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_80211);
   1219 	sc->sc_chip.ch_core_reset(sc, core, BWFM_AGENT_D11_IOCTL_PHYRESET |
   1220 	    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN, BWFM_AGENT_D11_IOCTL_PHYCLOCKEN,
   1221 	    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN);
   1222 }
   1223 
   1224 int
   1225 bwfm_chip_ca7_set_active(struct bwfm_softc *sc, const uint32_t rstvec)
   1226 {
   1227 	struct bwfm_core *core;
   1228 
   1229 	sc->sc_buscore_ops->bc_activate(sc, rstvec);
   1230 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7);
   1231 	sc->sc_chip.ch_core_reset(sc, core,
   1232 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT, 0, 0);
   1233 
   1234 	return 0;
   1235 }
   1236 
   1237 void
   1238 bwfm_chip_ca7_set_passive(struct bwfm_softc *sc)
   1239 {
   1240 	struct bwfm_core *core;
   1241 	uint32_t val;
   1242 
   1243 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7);
   1244 	val = sc->sc_buscore_ops->bc_read(sc,
   1245 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
   1246 	sc->sc_chip.ch_core_reset(sc, core,
   1247 	    val & BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
   1248 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
   1249 	    BWFM_AGENT_IOCTL_ARMCR4_CPUHALT);
   1250 
   1251 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_80211);
   1252 	sc->sc_chip.ch_core_reset(sc, core, BWFM_AGENT_D11_IOCTL_PHYRESET |
   1253 	    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN, BWFM_AGENT_D11_IOCTL_PHYCLOCKEN,
   1254 	    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN);
   1255 }
   1256 
   1257 int
   1258 bwfm_chip_cm3_set_active(struct bwfm_softc *sc)
   1259 {
   1260 	struct bwfm_core *core;
   1261 
   1262 	core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM);
   1263 	if (!sc->sc_chip.ch_core_isup(sc, core))
   1264 		return 1;
   1265 
   1266 	sc->sc_buscore_ops->bc_activate(sc, 0);
   1267 
   1268 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3);
   1269 	sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
   1270 
   1271 	return 0;
   1272 }
   1273 
   1274 void
   1275 bwfm_chip_cm3_set_passive(struct bwfm_softc *sc)
   1276 {
   1277 	struct bwfm_core *core;
   1278 
   1279 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3);
   1280 	sc->sc_chip.ch_core_disable(sc, core, 0, 0);
   1281 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_80211);
   1282 	sc->sc_chip.ch_core_reset(sc, core, BWFM_AGENT_D11_IOCTL_PHYRESET |
   1283 	    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN, BWFM_AGENT_D11_IOCTL_PHYCLOCKEN,
   1284 	    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN);
   1285 	core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM);
   1286 	sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
   1287 
   1288 	if (sc->sc_chip.ch_chip == BRCM_CC_43430_CHIP_ID) {
   1289 		sc->sc_buscore_ops->bc_write(sc,
   1290 		    core->co_base + BWFM_SOCRAM_BANKIDX, 3);
   1291 		sc->sc_buscore_ops->bc_write(sc,
   1292 		    core->co_base + BWFM_SOCRAM_BANKPDA, 0);
   1293 	}
   1294 }
   1295 
   1296 int
   1297 bwfm_chip_sr_capable(struct bwfm_softc *sc)
   1298 {
   1299 	struct bwfm_core *core;
   1300 	uint32_t reg;
   1301 
   1302 	if (sc->sc_chip.ch_pmurev < 17)
   1303 		return 0;
   1304 
   1305 	switch (sc->sc_chip.ch_chip) {
   1306 	case BRCM_CC_4345_CHIP_ID:
   1307 	case BRCM_CC_4354_CHIP_ID:
   1308 	case BRCM_CC_4356_CHIP_ID:
   1309 		core = bwfm_chip_get_pmu(sc);
   1310 		sc->sc_buscore_ops->bc_write(sc, core->co_base +
   1311 		    BWFM_CHIP_REG_CHIPCONTROL_ADDR, 3);
   1312 		reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
   1313 		    BWFM_CHIP_REG_CHIPCONTROL_DATA);
   1314 		return (reg & (1 << 2)) != 0;
   1315 	case BRCM_CC_43241_CHIP_ID:
   1316 	case BRCM_CC_4335_CHIP_ID:
   1317 	case BRCM_CC_4339_CHIP_ID:
   1318 		core = bwfm_chip_get_pmu(sc);
   1319 		sc->sc_buscore_ops->bc_write(sc, core->co_base +
   1320 		    BWFM_CHIP_REG_CHIPCONTROL_ADDR, 3);
   1321 		reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
   1322 		    BWFM_CHIP_REG_CHIPCONTROL_DATA);
   1323 		return reg != 0;
   1324 	case BRCM_CC_43430_CHIP_ID:
   1325 		core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
   1326 		reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
   1327 		    BWFM_CHIP_REG_SR_CONTROL1);
   1328 		return reg != 0;
   1329 	default:
   1330 		core = bwfm_chip_get_pmu(sc);
   1331 		reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
   1332 		    BWFM_CHIP_REG_PMUCAPABILITIES_EXT);
   1333 		if ((reg & BWFM_CHIP_REG_PMUCAPABILITIES_SR_SUPP) == 0)
   1334 			return 0;
   1335 		reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
   1336 		    BWFM_CHIP_REG_RETENTION_CTL);
   1337 		return (reg & (BWFM_CHIP_REG_RETENTION_CTL_MACPHY_DIS |
   1338 		               BWFM_CHIP_REG_RETENTION_CTL_LOGIC_DIS)) == 0;
   1339 	}
   1340 }
   1341 
   1342 /* RAM size helpers */
   1343 void
   1344 bwfm_chip_socram_ramsize(struct bwfm_softc *sc, struct bwfm_core *core)
   1345 {
   1346 	uint32_t coreinfo, nb, lss, banksize, bankinfo;
   1347 	uint32_t ramsize = 0, srsize = 0;
   1348 	int i;
   1349 
   1350 	if (!sc->sc_chip.ch_core_isup(sc, core))
   1351 		sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
   1352 
   1353 	coreinfo = sc->sc_buscore_ops->bc_read(sc,
   1354 	    core->co_base + BWFM_SOCRAM_COREINFO);
   1355 	nb = (coreinfo & BWFM_SOCRAM_COREINFO_SRNB_MASK)
   1356 	    >> BWFM_SOCRAM_COREINFO_SRNB_SHIFT;
   1357 
   1358 	if (core->co_rev <= 7 || core->co_rev == 12) {
   1359 		banksize = coreinfo & BWFM_SOCRAM_COREINFO_SRBSZ_MASK;
   1360 		lss = (coreinfo & BWFM_SOCRAM_COREINFO_LSS_MASK)
   1361 		    >> BWFM_SOCRAM_COREINFO_LSS_SHIFT;
   1362 		if (lss != 0)
   1363 			nb--;
   1364 		ramsize = nb * (1 << (banksize + BWFM_SOCRAM_COREINFO_SRBSZ_BASE));
   1365 		if (lss != 0)
   1366 			ramsize += (1 << ((lss - 1) + BWFM_SOCRAM_COREINFO_SRBSZ_BASE));
   1367 	} else {
   1368 		for (i = 0; i < nb; i++) {
   1369 			sc->sc_buscore_ops->bc_write(sc,
   1370 			    core->co_base + BWFM_SOCRAM_BANKIDX,
   1371 			    (BWFM_SOCRAM_BANKIDX_MEMTYPE_RAM <<
   1372 			    BWFM_SOCRAM_BANKIDX_MEMTYPE_SHIFT) | i);
   1373 			bankinfo = sc->sc_buscore_ops->bc_read(sc,
   1374 			    core->co_base + BWFM_SOCRAM_BANKINFO);
   1375 			banksize = ((bankinfo & BWFM_SOCRAM_BANKINFO_SZMASK) + 1)
   1376 			    * BWFM_SOCRAM_BANKINFO_SZBASE;
   1377 			ramsize += banksize;
   1378 			if (bankinfo & BWFM_SOCRAM_BANKINFO_RETNTRAM_MASK)
   1379 				srsize += banksize;
   1380 		}
   1381 	}
   1382 
   1383 	switch (sc->sc_chip.ch_chip) {
   1384 	case BRCM_CC_4334_CHIP_ID:
   1385 		if (sc->sc_chip.ch_chiprev < 2)
   1386 			srsize = 32 * 1024;
   1387 		break;
   1388 	case BRCM_CC_43430_CHIP_ID:
   1389 		srsize = 64 * 1024;
   1390 		break;
   1391 	default:
   1392 		break;
   1393 	}
   1394 
   1395 	sc->sc_chip.ch_ramsize = ramsize;
   1396 	sc->sc_chip.ch_srsize = srsize;
   1397 }
   1398 
   1399 void
   1400 bwfm_chip_sysmem_ramsize(struct bwfm_softc *sc, struct bwfm_core *core)
   1401 {
   1402 	uint32_t coreinfo, nb, banksize, bankinfo;
   1403 	uint32_t ramsize = 0;
   1404 	int i;
   1405 
   1406 	if (!sc->sc_chip.ch_core_isup(sc, core))
   1407 		sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
   1408 
   1409 	coreinfo = sc->sc_buscore_ops->bc_read(sc,
   1410 	    core->co_base + BWFM_SOCRAM_COREINFO);
   1411 	nb = (coreinfo & BWFM_SOCRAM_COREINFO_SRNB_MASK)
   1412 	    >> BWFM_SOCRAM_COREINFO_SRNB_SHIFT;
   1413 
   1414 	for (i = 0; i < nb; i++) {
   1415 		sc->sc_buscore_ops->bc_write(sc,
   1416 		    core->co_base + BWFM_SOCRAM_BANKIDX,
   1417 		    (BWFM_SOCRAM_BANKIDX_MEMTYPE_RAM <<
   1418 		    BWFM_SOCRAM_BANKIDX_MEMTYPE_SHIFT) | i);
   1419 		bankinfo = sc->sc_buscore_ops->bc_read(sc,
   1420 		    core->co_base + BWFM_SOCRAM_BANKINFO);
   1421 		banksize = ((bankinfo & BWFM_SOCRAM_BANKINFO_SZMASK) + 1)
   1422 		    * BWFM_SOCRAM_BANKINFO_SZBASE;
   1423 		ramsize += banksize;
   1424 	}
   1425 
   1426 	sc->sc_chip.ch_ramsize = ramsize;
   1427 }
   1428 
   1429 void
   1430 bwfm_chip_tcm_ramsize(struct bwfm_softc *sc, struct bwfm_core *core)
   1431 {
   1432 	uint32_t cap, nab, nbb, totb, bxinfo, ramsize = 0;
   1433 	int i;
   1434 
   1435 	cap = sc->sc_buscore_ops->bc_read(sc, core->co_base + BWFM_ARMCR4_CAP);
   1436 	nab = (cap & BWFM_ARMCR4_CAP_TCBANB_MASK) >> BWFM_ARMCR4_CAP_TCBANB_SHIFT;
   1437 	nbb = (cap & BWFM_ARMCR4_CAP_TCBBNB_MASK) >> BWFM_ARMCR4_CAP_TCBBNB_SHIFT;
   1438 	totb = nab + nbb;
   1439 
   1440 	for (i = 0; i < totb; i++) {
   1441 		sc->sc_buscore_ops->bc_write(sc,
   1442 		    core->co_base + BWFM_ARMCR4_BANKIDX, i);
   1443 		bxinfo = sc->sc_buscore_ops->bc_read(sc,
   1444 		    core->co_base + BWFM_ARMCR4_BANKINFO);
   1445 		ramsize += ((bxinfo & BWFM_ARMCR4_BANKINFO_BSZ_MASK) + 1) *
   1446 		    BWFM_ARMCR4_BANKINFO_BSZ_MULT;
   1447 	}
   1448 
   1449 	sc->sc_chip.ch_ramsize = ramsize;
   1450 }
   1451 
   1452 void
   1453 bwfm_chip_tcm_rambase(struct bwfm_softc *sc)
   1454 {
   1455 	switch (sc->sc_chip.ch_chip) {
   1456 	case BRCM_CC_4345_CHIP_ID:
   1457 		sc->sc_chip.ch_rambase = 0x198000;
   1458 		break;
   1459 	case BRCM_CC_4335_CHIP_ID:
   1460 	case BRCM_CC_4339_CHIP_ID:
   1461 	case BRCM_CC_4350_CHIP_ID:
   1462 	case BRCM_CC_4354_CHIP_ID:
   1463 	case BRCM_CC_4356_CHIP_ID:
   1464 	case BRCM_CC_43567_CHIP_ID:
   1465 	case BRCM_CC_43569_CHIP_ID:
   1466 	case BRCM_CC_43570_CHIP_ID:
   1467 	case BRCM_CC_4358_CHIP_ID:
   1468 	case BRCM_CC_4359_CHIP_ID:
   1469 	case BRCM_CC_43602_CHIP_ID:
   1470 	case BRCM_CC_4371_CHIP_ID:
   1471 		sc->sc_chip.ch_rambase = 0x180000;
   1472 		break;
   1473 	case BRCM_CC_43465_CHIP_ID:
   1474 	case BRCM_CC_43525_CHIP_ID:
   1475 	case BRCM_CC_4365_CHIP_ID:
   1476 	case BRCM_CC_4366_CHIP_ID:
   1477 		sc->sc_chip.ch_rambase = 0x200000;
   1478 		break;
   1479 	case CY_CC_4373_CHIP_ID:
   1480 		sc->sc_chip.ch_rambase = 0x160000;
   1481 		break;
   1482 	default:
   1483 		printf("%s: unknown chip: %d\n", DEVNAME(sc),
   1484 		    sc->sc_chip.ch_chip);
   1485 		break;
   1486 	}
   1487 }
   1488 
   1489 /* BCDC protocol implementation */
   1490 int
   1491 bwfm_proto_bcdc_query_dcmd(struct bwfm_softc *sc, int ifidx,
   1492     int cmd, char *buf, size_t *len)
   1493 {
   1494 	struct bwfm_proto_bcdc_dcmd *dcmd;
   1495 	size_t size = sizeof(dcmd->hdr) + *len;
   1496 	int reqid;
   1497 	int ret = 1;
   1498 
   1499 	reqid = sc->sc_bcdc_reqid++;
   1500 
   1501 	dcmd = kmem_zalloc(sizeof(*dcmd), KM_SLEEP);
   1502 	if (*len > sizeof(dcmd->buf))
   1503 		goto err;
   1504 
   1505 	dcmd->hdr.cmd = htole32(cmd);
   1506 	dcmd->hdr.len = htole32(*len);
   1507 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_GET;
   1508 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_ID_SET(reqid);
   1509 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_IF_SET(ifidx);
   1510 	dcmd->hdr.flags = htole32(dcmd->hdr.flags);
   1511 	memcpy(&dcmd->buf, buf, *len);
   1512 
   1513 	if (sc->sc_bus_ops->bs_txctl(sc, (void *)dcmd,
   1514 	     sizeof(dcmd->hdr) + *len)) {
   1515 		DPRINTF(("%s: tx failed\n", DEVNAME(sc)));
   1516 		goto err;
   1517 	}
   1518 
   1519 	do {
   1520 		if (sc->sc_bus_ops->bs_rxctl(sc, (void *)dcmd, &size)) {
   1521 			DPRINTF(("%s: rx failed\n", DEVNAME(sc)));
   1522 			goto err;
   1523 		}
   1524 		dcmd->hdr.cmd = le32toh(dcmd->hdr.cmd);
   1525 		dcmd->hdr.len = le32toh(dcmd->hdr.len);
   1526 		dcmd->hdr.flags = le32toh(dcmd->hdr.flags);
   1527 		dcmd->hdr.status = le32toh(dcmd->hdr.status);
   1528 	} while (BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags) != reqid);
   1529 
   1530 	if (BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags) != reqid) {
   1531 		printf("%s: unexpected request id\n", DEVNAME(sc));
   1532 		goto err;
   1533 	}
   1534 
   1535 	if (buf) {
   1536 		if (size < *len)
   1537 			*len = size;
   1538 		memcpy(buf, dcmd->buf, *len);
   1539 	}
   1540 
   1541 	if (dcmd->hdr.flags & BWFM_BCDC_DCMD_ERROR)
   1542 		ret = dcmd->hdr.status;
   1543 	else
   1544 		ret = 0;
   1545 err:
   1546 	kmem_free(dcmd, sizeof(*dcmd));
   1547 	return ret;
   1548 }
   1549 
   1550 int
   1551 bwfm_proto_bcdc_set_dcmd(struct bwfm_softc *sc, int ifidx,
   1552     int cmd, char *buf, size_t len)
   1553 {
   1554 	struct bwfm_proto_bcdc_dcmd *dcmd;
   1555 	size_t size = sizeof(dcmd->hdr) + len;
   1556 	int ret = 1, reqid;
   1557 
   1558 	reqid = sc->sc_bcdc_reqid++;
   1559 
   1560 	dcmd = kmem_zalloc(sizeof(*dcmd), KM_SLEEP);
   1561 	if (len > sizeof(dcmd->buf))
   1562 		goto err;
   1563 
   1564 	dcmd->hdr.cmd = htole32(cmd);
   1565 	dcmd->hdr.len = htole32(len);
   1566 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_SET;
   1567 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_ID_SET(reqid);
   1568 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_IF_SET(ifidx);
   1569 	dcmd->hdr.flags = htole32(dcmd->hdr.flags);
   1570 	memcpy(&dcmd->buf, buf, len);
   1571 
   1572 	if (sc->sc_bus_ops->bs_txctl(sc, (void *)dcmd, size)) {
   1573 		DPRINTF(("%s: tx failed\n", DEVNAME(sc)));
   1574 		goto err;
   1575 	}
   1576 
   1577 	do {
   1578 		if (sc->sc_bus_ops->bs_rxctl(sc, (void *)dcmd, &size)) {
   1579 			DPRINTF(("%s: rx failed\n", DEVNAME(sc)));
   1580 			goto err;
   1581 		}
   1582 		dcmd->hdr.cmd = le32toh(dcmd->hdr.cmd);
   1583 		dcmd->hdr.len = le32toh(dcmd->hdr.len);
   1584 		dcmd->hdr.flags = le32toh(dcmd->hdr.flags);
   1585 		dcmd->hdr.status = le32toh(dcmd->hdr.status);
   1586 	} while (BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags) != reqid);
   1587 
   1588 	if (BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags) != reqid) {
   1589 		printf("%s: unexpected request id\n", DEVNAME(sc));
   1590 		goto err;
   1591 	}
   1592 
   1593 	if (dcmd->hdr.flags & BWFM_BCDC_DCMD_ERROR)
   1594 		return dcmd->hdr.status;
   1595 
   1596 	ret = 0;
   1597 err:
   1598 	kmem_free(dcmd, sizeof(*dcmd));
   1599 	return ret;
   1600 }
   1601 
   1602 /* FW Variable code */
   1603 int
   1604 bwfm_fwvar_cmd_get_data(struct bwfm_softc *sc, int cmd, void *data, size_t len)
   1605 {
   1606 	return sc->sc_proto_ops->proto_query_dcmd(sc, 0, cmd, data, &len);
   1607 }
   1608 
   1609 int
   1610 bwfm_fwvar_cmd_set_data(struct bwfm_softc *sc, int cmd, void *data, size_t len)
   1611 {
   1612 	return sc->sc_proto_ops->proto_set_dcmd(sc, 0, cmd, data, len);
   1613 }
   1614 
   1615 int
   1616 bwfm_fwvar_cmd_get_int(struct bwfm_softc *sc, int cmd, uint32_t *data)
   1617 {
   1618 	int ret;
   1619 	ret = bwfm_fwvar_cmd_get_data(sc, cmd, data, sizeof(*data));
   1620 	*data = le32toh(*data);
   1621 	return ret;
   1622 }
   1623 
   1624 int
   1625 bwfm_fwvar_cmd_set_int(struct bwfm_softc *sc, int cmd, uint32_t data)
   1626 {
   1627 	data = htole32(data);
   1628 	return bwfm_fwvar_cmd_set_data(sc, cmd, &data, sizeof(data));
   1629 }
   1630 
   1631 int
   1632 bwfm_fwvar_var_get_data(struct bwfm_softc *sc, const char *name, void *data, size_t len)
   1633 {
   1634 	char *buf;
   1635 	int ret;
   1636 
   1637 	buf = kmem_alloc(strlen(name) + 1 + len, KM_SLEEP);
   1638 	memcpy(buf, name, strlen(name) + 1);
   1639 	memcpy(buf + strlen(name) + 1, data, len);
   1640 	ret = bwfm_fwvar_cmd_get_data(sc, BWFM_C_GET_VAR,
   1641 	    buf, strlen(name) + 1 + len);
   1642 	memcpy(data, buf, len);
   1643 	kmem_free(buf, strlen(name) + 1 + len);
   1644 	return ret;
   1645 }
   1646 
   1647 int
   1648 bwfm_fwvar_var_set_data(struct bwfm_softc *sc, const char *name, void *data, size_t len)
   1649 {
   1650 	char *buf;
   1651 	int ret;
   1652 
   1653 	buf = kmem_alloc(strlen(name) + 1 + len, KM_SLEEP);
   1654 	memcpy(buf, name, strlen(name) + 1);
   1655 	memcpy(buf + strlen(name) + 1, data, len);
   1656 	ret = bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_VAR,
   1657 	    buf, strlen(name) + 1 + len);
   1658 	kmem_free(buf, strlen(name) + 1 + len);
   1659 	return ret;
   1660 }
   1661 
   1662 int
   1663 bwfm_fwvar_var_get_int(struct bwfm_softc *sc, const char *name, uint32_t *data)
   1664 {
   1665 	int ret;
   1666 	ret = bwfm_fwvar_var_get_data(sc, name, data, sizeof(*data));
   1667 	*data = le32toh(*data);
   1668 	return ret;
   1669 }
   1670 
   1671 int
   1672 bwfm_fwvar_var_set_int(struct bwfm_softc *sc, const char *name, uint32_t data)
   1673 {
   1674 	data = htole32(data);
   1675 	return bwfm_fwvar_var_set_data(sc, name, &data, sizeof(data));
   1676 }
   1677 
   1678 /* 802.11 code */
   1679 void
   1680 bwfm_scan(struct bwfm_softc *sc)
   1681 {
   1682 	struct bwfm_escan_params *params;
   1683 	uint32_t nssid = 0, nchannel = 0;
   1684 	size_t params_size;
   1685 
   1686 #if 0
   1687 	/* Active scan is used for scanning for an SSID */
   1688 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PASSIVE_SCAN, 0);
   1689 #endif
   1690 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PASSIVE_SCAN, 1);
   1691 
   1692 	params_size = sizeof(*params);
   1693 	params_size += sizeof(uint32_t) * ((nchannel + 1) / 2);
   1694 	params_size += sizeof(struct bwfm_ssid) * nssid;
   1695 
   1696 	params = kmem_zalloc(params_size, KM_SLEEP);
   1697 	memset(params->scan_params.bssid, 0xff,
   1698 	    sizeof(params->scan_params.bssid));
   1699 	params->scan_params.bss_type = 2;
   1700 	params->scan_params.nprobes = htole32(-1);
   1701 	params->scan_params.active_time = htole32(-1);
   1702 	params->scan_params.passive_time = htole32(-1);
   1703 	params->scan_params.home_time = htole32(-1);
   1704 	params->version = htole32(BWFM_ESCAN_REQ_VERSION);
   1705 	params->action = htole16(WL_ESCAN_ACTION_START);
   1706 	params->sync_id = htole16(0x1234);
   1707 
   1708 #if 0
   1709 	/* Scan a specific channel */
   1710 	params->scan_params.channel_list[0] = htole16(
   1711 	    (1 & 0xff) << 0 |
   1712 	    (3 & 0x3) << 8 |
   1713 	    (2 & 0x3) << 10 |
   1714 	    (2 & 0x3) << 12
   1715 	    );
   1716 	params->scan_params.channel_num = htole32(
   1717 	    (1 & 0xffff) << 0
   1718 	    );
   1719 #endif
   1720 
   1721 	bwfm_fwvar_var_set_data(sc, "escan", params, params_size);
   1722 	kmem_free(params, params_size);
   1723 }
   1724 
   1725 static __inline int
   1726 bwfm_iswpaoui(const uint8_t *frm)
   1727 {
   1728 	return frm[1] > 3 && le32dec(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
   1729 }
   1730 
   1731 /*
   1732  * Derive wireless security settings from WPA/RSN IE.
   1733  */
   1734 static uint32_t
   1735 bwfm_get_wsec(struct bwfm_softc *sc)
   1736 {
   1737 	struct ieee80211com *ic = &sc->sc_ic;
   1738 	uint8_t *wpa = ic->ic_opt_ie;
   1739 
   1740 	KASSERT(ic->ic_opt_ie_len > 0);
   1741 
   1742 	if (wpa[0] != IEEE80211_ELEMID_RSN) {
   1743 		if (ic->ic_opt_ie_len < 12)
   1744 			return BWFM_WSEC_NONE;
   1745 
   1746 		/* non-RSN IE, expect that we are doing WPA1 */
   1747 		if ((ic->ic_flags & IEEE80211_F_WPA1) == 0)
   1748 			return BWFM_WSEC_NONE;
   1749 
   1750 		/* Must contain WPA OUI */
   1751 		if (!bwfm_iswpaoui(wpa))
   1752 			return BWFM_WSEC_NONE;
   1753 
   1754 		switch (le32dec(wpa + 8)) {
   1755 		case ((WPA_CSE_TKIP<<24)|WPA_OUI):
   1756 			return BWFM_WSEC_TKIP;
   1757 		case ((WPA_CSE_CCMP<<24)|WPA_OUI):
   1758 			return BWFM_WSEC_AES;
   1759 		default:
   1760 			return BWFM_WSEC_NONE;
   1761 		}
   1762 	} else {
   1763 		if (ic->ic_opt_ie_len < 14)
   1764 			return BWFM_WSEC_NONE;
   1765 
   1766 		/* RSN IE, expect that we are doing WPA2 */
   1767 		if ((ic->ic_flags & IEEE80211_F_WPA2) == 0)
   1768 			return BWFM_WSEC_NONE;
   1769 
   1770 		switch (le32dec(wpa + 10)) {
   1771 		case ((RSN_CSE_TKIP<<24)|RSN_OUI):
   1772 			return BWFM_WSEC_TKIP;
   1773 		case ((RSN_CSE_CCMP<<24)|RSN_OUI):
   1774 			return BWFM_WSEC_AES;
   1775 		default:
   1776 			return BWFM_WSEC_NONE;
   1777 		}
   1778 	}
   1779 }
   1780 
   1781 void
   1782 bwfm_connect(struct bwfm_softc *sc)
   1783 {
   1784 	struct ieee80211com *ic = &sc->sc_ic;
   1785 	struct ieee80211_node *ni = ic->ic_bss;
   1786 	struct bwfm_ext_join_params *params;
   1787 
   1788 	if (ic->ic_flags & IEEE80211_F_WPA) {
   1789 		uint32_t wsec = 0;
   1790 		uint32_t wpa = 0;
   1791 
   1792 		if (ic->ic_opt_ie_len)
   1793 			bwfm_fwvar_var_set_data(sc, "wpaie", ic->ic_opt_ie, ic->ic_opt_ie_len);
   1794 
   1795 		if (ic->ic_flags & IEEE80211_F_WPA1)
   1796 			wpa |= BWFM_WPA_AUTH_WPA_PSK;
   1797 		if (ic->ic_flags & IEEE80211_F_WPA2)
   1798 			wpa |= BWFM_WPA_AUTH_WPA2_PSK;
   1799 
   1800 		wsec |= bwfm_get_wsec(sc);
   1801 
   1802 		DPRINTF(("%s: WPA enabled, ic_flags = 0x%x, wpa 0x%x, wsec 0x%x\n",
   1803 		    DEVNAME(sc), ic->ic_flags, wpa, wsec));
   1804 
   1805 		bwfm_fwvar_var_set_int(sc, "wpa_auth", wpa);
   1806 		bwfm_fwvar_var_set_int(sc, "wsec", wsec);
   1807 	} else {
   1808 		bwfm_fwvar_var_set_int(sc, "wpa_auth", BWFM_WPA_AUTH_DISABLED);
   1809 		bwfm_fwvar_var_set_int(sc, "wsec", BWFM_WSEC_NONE);
   1810 	}
   1811 
   1812 	bwfm_fwvar_var_set_int(sc, "auth", BWFM_AUTH_OPEN);
   1813 	bwfm_fwvar_var_set_int(sc, "mfp", BWFM_MFP_NONE);
   1814 
   1815 	if (ni->ni_esslen && ni->ni_esslen < BWFM_MAX_SSID_LEN) {
   1816 		params = kmem_zalloc(sizeof(*params), KM_SLEEP);
   1817 		memcpy(params->ssid.ssid, ni->ni_essid, ni->ni_esslen);
   1818 		params->ssid.len = htole32(ni->ni_esslen);
   1819 		memcpy(params->assoc.bssid, ni->ni_bssid, sizeof(params->assoc.bssid));
   1820 		params->scan.scan_type = -1;
   1821 		params->scan.nprobes = htole32(-1);
   1822 		params->scan.active_time = htole32(-1);
   1823 		params->scan.passive_time = htole32(-1);
   1824 		params->scan.home_time = htole32(-1);
   1825 		if (bwfm_fwvar_var_set_data(sc, "join", params, sizeof(*params))) {
   1826 			struct bwfm_join_params join;
   1827 			memset(&join, 0, sizeof(join));
   1828 			memcpy(join.ssid.ssid, ni->ni_essid, ni->ni_esslen);
   1829 			join.ssid.len = htole32(ni->ni_esslen);
   1830 			memcpy(join.assoc.bssid, ni->ni_bssid, sizeof(join.assoc.bssid));
   1831 			bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_SSID, &join,
   1832 			    sizeof(join));
   1833 		}
   1834 		kmem_free(params, sizeof(*params));
   1835 	}
   1836 }
   1837 
   1838 void
   1839 bwfm_get_sta_info(struct bwfm_softc *sc, struct ifmediareq *ifmr)
   1840 {
   1841 	struct ieee80211com *ic = &sc->sc_ic;
   1842 	struct ieee80211_node *ni = ic->ic_bss;
   1843 	struct bwfm_sta_info sta;
   1844 	uint32_t flags, txrate;
   1845 
   1846 	memset(&sta, 0, sizeof(sta));
   1847 	memcpy(&sta, ni->ni_macaddr, sizeof(ni->ni_macaddr));
   1848 
   1849 	if (bwfm_fwvar_var_get_data(sc, "sta_info", &sta, sizeof(sta)))
   1850 		return;
   1851 
   1852 	if (!IEEE80211_ADDR_EQ(ni->ni_macaddr, sta.ea))
   1853 		return;
   1854 
   1855 	if (le16toh(sta.ver) < 4)
   1856 		return;
   1857 
   1858 	flags = le32toh(sta.flags);
   1859 	if ((flags & BWFM_STA_SCBSTATS) == 0)
   1860 		return;
   1861 
   1862 	txrate = le32toh(sta.tx_rate);
   1863 	if (txrate == 0xffffffff)
   1864 		return;
   1865 
   1866 	if ((flags & BWFM_STA_VHT_CAP) != 0) {
   1867 		ifmr->ifm_active &= ~IFM_TMASK;
   1868 		ifmr->ifm_active |= IFM_IEEE80211_VHT;
   1869 		ifmr->ifm_active &= ~IFM_MMASK;
   1870 		ifmr->ifm_active |= IFM_IEEE80211_11AC;
   1871 	} else if ((flags & BWFM_STA_N_CAP) != 0) {
   1872 		ifmr->ifm_active &= ~IFM_TMASK;
   1873 		ifmr->ifm_active |= IFM_IEEE80211_MCS;
   1874 		ifmr->ifm_active &= ~IFM_MMASK;
   1875 		if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
   1876 			ifmr->ifm_active |= IFM_IEEE80211_11NG;
   1877 		else
   1878 			ifmr->ifm_active |= IFM_IEEE80211_11NA;
   1879 	}
   1880 }
   1881 
   1882 void
   1883 bwfm_rx(struct bwfm_softc *sc, struct mbuf *m)
   1884 {
   1885 	struct ieee80211com *ic = &sc->sc_ic;
   1886 	struct ifnet *ifp = ic->ic_ifp;
   1887 	struct bwfm_event *e = mtod(m, struct bwfm_event *);
   1888 
   1889 	if (m->m_len >= sizeof(e->ehdr) &&
   1890 	    ntohs(e->ehdr.ether_type) == BWFM_ETHERTYPE_LINK_CTL &&
   1891 	    memcmp(BWFM_BRCM_OUI, e->hdr.oui, sizeof(e->hdr.oui)) == 0 &&
   1892 	    ntohs(e->hdr.usr_subtype) == BWFM_BRCM_SUBTYPE_EVENT) {
   1893 		bwfm_rx_event(sc, m);
   1894 		// m_freem(m);
   1895 		return;
   1896 	}
   1897 
   1898 	m_set_rcvif(m, ifp);
   1899 	if_percpuq_enqueue(ifp->if_percpuq, m);
   1900 }
   1901 
   1902 void
   1903 bwfm_rx_event(struct bwfm_softc *sc, struct mbuf *m)
   1904 {
   1905 	struct bwfm_task *t;
   1906 
   1907 	t = pcq_get(sc->sc_freetask);
   1908 	if (t == NULL) {
   1909 		m_freem(m);
   1910 		printf("%s: no free tasks\n", DEVNAME(sc));
   1911 		return;
   1912 	}
   1913 
   1914 	t->t_cmd = BWFM_TASK_RX_EVENT;
   1915 	t->t_mbuf = m;
   1916 	workqueue_enqueue(sc->sc_taskq, (struct work*)t, NULL);
   1917 }
   1918 
   1919 void
   1920 bwfm_rx_event_cb(struct bwfm_softc *sc, struct mbuf *m)
   1921 {
   1922 	struct ieee80211com *ic = &sc->sc_ic;
   1923 	struct bwfm_event *e = mtod(m, void *);
   1924 	size_t len = m->m_len;
   1925 	int s;
   1926 
   1927 	DPRINTF(("%s: event %p len %lu datalen %u code %u status %u"
   1928 	    " reason %u\n", __func__, e, len, ntohl(e->msg.datalen),
   1929 	    ntohl(e->msg.event_type), ntohl(e->msg.status),
   1930 	    ntohl(e->msg.reason)));
   1931 
   1932 	if (ntohl(e->msg.event_type) >= BWFM_E_LAST) {
   1933 		m_freem(m);
   1934 		return;
   1935 	}
   1936 
   1937 	switch (ntohl(e->msg.event_type)) {
   1938 	case BWFM_E_ESCAN_RESULT: {
   1939 		struct bwfm_escan_results *res = (void *)&e[1];
   1940 		struct bwfm_bss_info *bss;
   1941 		int i;
   1942 		if (ntohl(e->msg.status) != BWFM_E_STATUS_PARTIAL) {
   1943 			/* Scan complete */
   1944 			s = splnet();
   1945 			if (ic->ic_opmode != IEEE80211_M_MONITOR)
   1946 				ieee80211_end_scan(ic);
   1947 			splx(s);
   1948 			break;
   1949 		}
   1950 		len -= sizeof(*e);
   1951 		if (len < sizeof(*res) || len < le32toh(res->buflen)) {
   1952 			m_freem(m);
   1953 			printf("%s: results too small\n", DEVNAME(sc));
   1954 			return;
   1955 		}
   1956 		len -= sizeof(*res);
   1957 		if (len < le16toh(res->bss_count) * sizeof(struct bwfm_bss_info)) {
   1958 			m_freem(m);
   1959 			printf("%s: results too small\n", DEVNAME(sc));
   1960 			return;
   1961 		}
   1962 		bss = &res->bss_info[0];
   1963 		for (i = 0; i < le16toh(res->bss_count); i++) {
   1964 			/* Fix alignment of bss_info */
   1965 			union {
   1966 				struct bwfm_bss_info bss_info;
   1967 				uint8_t padding[BWFM_BSS_INFO_BUFLEN];
   1968 			} bss_buf;
   1969 			if (len > sizeof(bss_buf)) {
   1970 				printf("%s: bss_info buffer too big\n", DEVNAME(sc));
   1971 			} else {
   1972 				memcpy(&bss_buf, &res->bss_info[i], len);
   1973 				bwfm_scan_node(sc, &bss_buf.bss_info, len);
   1974 			}
   1975 			len -= sizeof(*bss) + le32toh(bss->length);
   1976 			bss = (void *)(((uintptr_t)bss) + le32toh(bss->length));
   1977 			if (len <= 0)
   1978 				break;
   1979 		}
   1980 		break;
   1981 		}
   1982 
   1983 	case BWFM_E_SET_SSID:
   1984 		if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS) {
   1985 			ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
   1986 		} else {
   1987 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
   1988 		}
   1989 		break;
   1990 
   1991 	case BWFM_E_ASSOC:
   1992 		if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS) {
   1993 			ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
   1994 		} else {
   1995 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
   1996 		}
   1997 		break;
   1998 
   1999 	case BWFM_E_LINK:
   2000 		if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS &&
   2001 		    ntohl(e->msg.reason) == 0)
   2002 			break;
   2003 
   2004 		/* Link status has changed */
   2005 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
   2006 		break;
   2007 
   2008 	default:
   2009 		break;
   2010 	}
   2011 
   2012 	m_freem(m);
   2013 }
   2014 
   2015 void
   2016 bwfm_scan_node(struct bwfm_softc *sc, struct bwfm_bss_info *bss, size_t len)
   2017 {
   2018 	struct ieee80211com *ic = &sc->sc_ic;
   2019 	struct ieee80211_frame wh;
   2020 	struct ieee80211_scanparams scan;
   2021 	uint8_t rates[sizeof(bss->rates) + 2];
   2022 	uint8_t ssid[sizeof(bss->ssid) + 2];
   2023 	uint8_t *frm, *sfrm, *efrm;
   2024 	uint64_t tsf;
   2025 
   2026 	tsf = 0;
   2027 	sfrm = ((uint8_t *)bss) + le16toh(bss->ie_offset);
   2028 	efrm = sfrm + le32toh(bss->ie_length);
   2029 
   2030 	/* Fake a wireless header with the scan result's BSSID */
   2031 	memset(&wh, 0, sizeof(wh));
   2032 	IEEE80211_ADDR_COPY(wh.i_addr2, bss->bssid);
   2033 	IEEE80211_ADDR_COPY(wh.i_addr3, bss->bssid);
   2034 
   2035 	if (efrm - sfrm < 12) {
   2036 		ic->ic_stats.is_rx_elem_toosmall++;
   2037 		return;
   2038 	}
   2039 
   2040 	rates[0] = 0;
   2041 	rates[1] = le32toh(bss->nrates);
   2042 	memcpy(&rates[2], bss->rates, sizeof(bss->rates));
   2043 
   2044 	ssid[0] = 0;
   2045 	ssid[1] = bss->ssid_len;
   2046 	memcpy(&ssid[2], bss->ssid, sizeof(bss->ssid));
   2047 
   2048 	/* Build scan result */
   2049 	memset(&scan, 0, sizeof(scan));
   2050 	scan.sp_tstamp  = (uint8_t *)&tsf;
   2051 	scan.sp_bintval = le16toh(bss->beacon_period);
   2052 	scan.sp_capinfo = le16toh(bss->capability);
   2053 	scan.sp_bchan   = ieee80211_chan2ieee(ic, ic->ic_curchan);
   2054 	scan.sp_chan    = scan.sp_bchan;
   2055 	scan.sp_rates   = rates;
   2056 	scan.sp_ssid    = ssid;
   2057 
   2058 	for (frm = sfrm; frm < efrm; frm += frm[1] + 2) {
   2059 		switch (frm[0]) {
   2060 		case IEEE80211_ELEMID_COUNTRY:
   2061 			scan.sp_country = frm;
   2062 			break;
   2063 		case IEEE80211_ELEMID_FHPARMS:
   2064 			if (ic->ic_phytype == IEEE80211_T_FH) {
   2065 				if (frm + 6 >= efrm)
   2066 					break;
   2067 				scan.sp_fhdwell = le16dec(&frm[2]);
   2068 				scan.sp_chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
   2069 				scan.sp_fhindex = frm[6];
   2070 			}
   2071 			break;
   2072 		case IEEE80211_ELEMID_DSPARMS:
   2073 			if (ic->ic_phytype != IEEE80211_T_FH) {
   2074 				if (frm + 2 >= efrm)
   2075 					break;
   2076 				scan.sp_chan = frm[2];
   2077 			}
   2078 			break;
   2079 		case IEEE80211_ELEMID_TIM:
   2080 			scan.sp_tim = frm;
   2081 			scan.sp_timoff = frm - sfrm;
   2082 			break;
   2083 		case IEEE80211_ELEMID_XRATES:
   2084 			scan.sp_xrates = frm;
   2085 			break;
   2086 		case IEEE80211_ELEMID_ERP:
   2087 			if (frm + 1 >= efrm)
   2088 				break;
   2089 			if (frm[1] != 1) {
   2090 				ic->ic_stats.is_rx_elem_toobig++;
   2091 				break;
   2092 			}
   2093 			scan.sp_erp = frm[2];
   2094 			break;
   2095 		case IEEE80211_ELEMID_RSN:
   2096 			scan.sp_wpa = frm;
   2097 			break;
   2098 		case IEEE80211_ELEMID_VENDOR:
   2099 			if (frm + 1 >= efrm)
   2100 				break;
   2101 			if (frm + frm[1] + 2 >= efrm)
   2102 				break;
   2103 			if (bwfm_iswpaoui(frm))
   2104 				scan.sp_wpa = frm;
   2105 			break;
   2106 		}
   2107 		if (frm + 1 >= efrm)
   2108 			break;
   2109 	}
   2110 
   2111 	if (ic->ic_flags & IEEE80211_F_SCAN)
   2112 		ieee80211_add_scan(ic, &scan, &wh, IEEE80211_FC0_SUBTYPE_BEACON,
   2113 		    le32toh(bss->rssi), 0);
   2114 }
   2115