Home | History | Annotate | Line # | Download | only in ic
bwfm.c revision 1.3
      1 /* $NetBSD: bwfm.c,v 1.3 2017/10/23 09:31:17 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/malloc.h>
     25 #include <sys/device.h>
     26 #include <sys/queue.h>
     27 #include <sys/socket.h>
     28 #include <sys/kmem.h>
     29 #include <sys/workqueue.h>
     30 #include <sys/pcq.h>
     31 
     32 #include <net/bpf.h>
     33 #include <net/if.h>
     34 #include <net/if_dl.h>
     35 #include <net/if_media.h>
     36 #include <net/if_ether.h>
     37 
     38 #include <netinet/in.h>
     39 
     40 #include <net80211/ieee80211_var.h>
     41 
     42 #include <dev/ic/bwfmvar.h>
     43 #include <dev/ic/bwfmreg.h>
     44 
     45 /* #define BWFM_DEBUG */
     46 #ifdef BWFM_DEBUG
     47 #define DPRINTF(x)	do { if (bwfm_debug > 0) printf x; } while (0)
     48 #define DPRINTFN(n, x)	do { if (bwfm_debug >= (n)) printf x; } while (0)
     49 static int bwfm_debug = 1;
     50 #else
     51 #define DPRINTF(x)	do { ; } while (0)
     52 #define DPRINTFN(n, x)	do { ; } while (0)
     53 #endif
     54 
     55 #define DEVNAME(sc)	device_xname((sc)->sc_dev)
     56 
     57 void	 bwfm_start(struct ifnet *);
     58 int	 bwfm_init(struct ifnet *);
     59 void	 bwfm_stop(struct ifnet *, int);
     60 void	 bwfm_watchdog(struct ifnet *);
     61 int	 bwfm_ioctl(struct ifnet *, u_long, void *);
     62 int	 bwfm_media_change(struct ifnet *);
     63 void	 bwfm_media_status(struct ifnet *, struct ifmediareq *);
     64 
     65 int	 bwfm_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
     66 	     int, int);
     67 void	 bwfm_recv_mgmt(struct ieee80211com *, struct mbuf *,
     68 	     struct ieee80211_node *, int, int, uint32_t);
     69 int	 bwfm_key_set(struct ieee80211com *, const struct ieee80211_key *,
     70 	     const uint8_t *);
     71 int	 bwfm_key_delete(struct ieee80211com *, const struct ieee80211_key *);
     72 int	 bwfm_newstate(struct ieee80211com *, enum ieee80211_state, int);
     73 void	 bwfm_newstate_cb(struct bwfm_softc *, struct bwfm_cmd_newstate *);
     74 void	 bwfm_task(struct work *, void *);
     75 
     76 int	 bwfm_chip_attach(struct bwfm_softc *);
     77 int	 bwfm_chip_detach(struct bwfm_softc *, int);
     78 struct bwfm_core *bwfm_chip_get_core(struct bwfm_softc *, int);
     79 struct bwfm_core *bwfm_chip_get_pmu(struct bwfm_softc *);
     80 int	 bwfm_chip_ai_isup(struct bwfm_softc *, struct bwfm_core *);
     81 void	 bwfm_chip_ai_disable(struct bwfm_softc *, struct bwfm_core *,
     82 	     uint32_t, uint32_t);
     83 void	 bwfm_chip_ai_reset(struct bwfm_softc *, struct bwfm_core *,
     84 	     uint32_t, uint32_t, uint32_t);
     85 void	 bwfm_chip_dmp_erom_scan(struct bwfm_softc *);
     86 int	 bwfm_chip_dmp_get_regaddr(struct bwfm_softc *, uint32_t *,
     87 	     uint32_t *, uint32_t *);
     88 void	 bwfm_chip_cr4_set_passive(struct bwfm_softc *);
     89 void	 bwfm_chip_ca7_set_passive(struct bwfm_softc *);
     90 void	 bwfm_chip_cm3_set_passive(struct bwfm_softc *);
     91 
     92 int	 bwfm_proto_bcdc_query_dcmd(struct bwfm_softc *, int,
     93 	     int, char *, size_t *);
     94 int	 bwfm_proto_bcdc_set_dcmd(struct bwfm_softc *, int,
     95 	     int, char *, size_t);
     96 
     97 int	 bwfm_fwvar_cmd_get_data(struct bwfm_softc *, int, void *, size_t);
     98 int	 bwfm_fwvar_cmd_set_data(struct bwfm_softc *, int, void *, size_t);
     99 int	 bwfm_fwvar_cmd_get_int(struct bwfm_softc *, int, uint32_t *);
    100 int	 bwfm_fwvar_cmd_set_int(struct bwfm_softc *, int, uint32_t);
    101 int	 bwfm_fwvar_var_get_data(struct bwfm_softc *, const char *, void *, size_t);
    102 int	 bwfm_fwvar_var_set_data(struct bwfm_softc *, const char *, void *, size_t);
    103 int	 bwfm_fwvar_var_get_int(struct bwfm_softc *, const char *, uint32_t *);
    104 int	 bwfm_fwvar_var_set_int(struct bwfm_softc *, const char *, uint32_t);
    105 
    106 struct ieee80211_channel *bwfm_bss2chan(struct bwfm_softc *, struct bwfm_bss_info *);
    107 void	 bwfm_scan(struct bwfm_softc *);
    108 void	 bwfm_connect(struct bwfm_softc *);
    109 
    110 void	 bwfm_rx(struct bwfm_softc *, char *, size_t);
    111 void	 bwfm_rx_event(struct bwfm_softc *, char *, size_t);
    112 void	 bwfm_scan_node(struct bwfm_softc *, struct bwfm_bss_info *, size_t);
    113 
    114 uint8_t bwfm_2ghz_channels[] = {
    115 	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
    116 };
    117 uint8_t bwfm_5ghz_channels[] = {
    118 	34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64, 100, 104, 108, 112,
    119 	116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165,
    120 };
    121 
    122 struct bwfm_proto_ops bwfm_proto_bcdc_ops = {
    123 	.proto_query_dcmd = bwfm_proto_bcdc_query_dcmd,
    124 	.proto_set_dcmd = bwfm_proto_bcdc_set_dcmd,
    125 };
    126 
    127 void
    128 bwfm_attach(struct bwfm_softc *sc)
    129 {
    130 	struct ieee80211com *ic = &sc->sc_ic;
    131 	struct ifnet *ifp = &sc->sc_if;
    132 	struct bwfm_task *t;
    133 	char fw_version[BWFM_DCMD_SMLEN];
    134 	uint32_t bandlist[3];
    135 	uint32_t tmp;
    136 	int i, error;
    137 
    138 	error = workqueue_create(&sc->sc_taskq, DEVNAME(sc),
    139 	    bwfm_task, sc, PRI_NONE, IPL_NET, 0);
    140 	if (error != 0) {
    141 		printf("%s: could not create workqueue\n", DEVNAME(sc));
    142 		return;
    143 	}
    144 	sc->sc_freetask = pcq_create(BWFM_TASK_COUNT, KM_SLEEP);
    145 	for (i = 0; i < BWFM_TASK_COUNT; i++) {
    146 		t = &sc->sc_task[i];
    147 		t->t_sc = sc;
    148 		pcq_put(sc->sc_freetask, t);
    149 	}
    150 
    151 	if (bwfm_fwvar_cmd_get_int(sc, BWFM_C_GET_VERSION, &tmp)) {
    152 		printf("%s: could not read io type\n", DEVNAME(sc));
    153 		return;
    154 	} else
    155 		sc->sc_io_type = tmp;
    156 	if (bwfm_fwvar_var_get_data(sc, "cur_etheraddr", ic->ic_myaddr,
    157 	    sizeof(ic->ic_myaddr))) {
    158 		printf("%s: could not read mac address\n", DEVNAME(sc));
    159 		return;
    160 	}
    161 
    162 	memset(fw_version, 0, sizeof(fw_version));
    163 	if (bwfm_fwvar_var_get_data(sc, "ver", fw_version, sizeof(fw_version)) == 0)
    164 		printf("%s: %s", DEVNAME(sc), fw_version);
    165 	printf("%s: address %s\n", DEVNAME(sc), ether_sprintf(ic->ic_myaddr));
    166 
    167 	ic->ic_ifp = ifp;
    168 	ic->ic_phytype = IEEE80211_T_OFDM;
    169 	ic->ic_opmode = IEEE80211_M_STA;
    170 	ic->ic_state = IEEE80211_S_INIT;
    171 
    172 	ic->ic_caps =
    173 	    IEEE80211_C_WEP |
    174 	    IEEE80211_C_TKIP |
    175 	    IEEE80211_C_AES |
    176 	    IEEE80211_C_AES_CCM |
    177 #if notyet
    178 	    IEEE80211_C_MONITOR |		/* monitor mode suported */
    179 	    IEEE80211_C_IBSS |
    180 	    IEEE80211_C_TXPMGT |
    181 	    IEEE80211_C_WME |
    182 #endif
    183 	    IEEE80211_C_SHSLOT |		/* short slot time supported */
    184 	    IEEE80211_C_SHPREAMBLE |		/* short preamble supported */
    185 	    IEEE80211_C_WPA |			/* 802.11i */
    186 	    /* IEEE80211_C_WPA_4WAY */0;		/* WPA 4-way handshake in hw */
    187 
    188 	/* IBSS channel undefined for now. */
    189 	ic->ic_ibss_chan = &ic->ic_channels[0];
    190 
    191 	if (bwfm_fwvar_cmd_get_data(sc, BWFM_C_GET_BANDLIST, bandlist,
    192 	    sizeof(bandlist))) {
    193 		printf("%s: couldn't get supported band list\n", DEVNAME(sc));
    194 		return;
    195 	}
    196 	const u_int nbands = le32toh(bandlist[0]);
    197 	for (i = 1; i <= MIN(nbands, __arraycount(bandlist) - 1); i++) {
    198 		switch (le32toh(bandlist[i])) {
    199 		case BWFM_BAND_2G:
    200 			ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
    201 			ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g;
    202 
    203 			for (i = 0; i < __arraycount(bwfm_2ghz_channels); i++) {
    204 				uint8_t chan = bwfm_2ghz_channels[i];
    205 				ic->ic_channels[chan].ic_freq =
    206 				    ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ);
    207 				ic->ic_channels[chan].ic_flags =
    208 				    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
    209 				    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
    210 			}
    211 			break;
    212 		case BWFM_BAND_5G:
    213 			ic->ic_sup_rates[IEEE80211_MODE_11A] = ieee80211_std_rateset_11a;
    214 
    215 			for (i = 0; i < __arraycount(bwfm_5ghz_channels); i++) {
    216 				uint8_t chan = bwfm_5ghz_channels[i];
    217 				ic->ic_channels[chan].ic_freq =
    218 				    ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ);
    219 				ic->ic_channels[chan].ic_flags =
    220 				    IEEE80211_CHAN_A;
    221 			}
    222 			break;
    223 		}
    224 	}
    225 
    226 	ifp->if_softc = sc;
    227 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
    228 	ifp->if_init = bwfm_init;
    229 	ifp->if_ioctl = bwfm_ioctl;
    230 	ifp->if_start = bwfm_start;
    231 	ifp->if_watchdog = bwfm_watchdog;
    232 	IFQ_SET_READY(&ifp->if_snd);
    233 	memcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ);
    234 
    235 	error = if_initialize(ifp);
    236 	if (error != 0) {
    237 		printf("%s: if_initialize failed(%d)\n", DEVNAME(sc), error);
    238 		pcq_destroy(sc->sc_freetask);
    239 		workqueue_destroy(sc->sc_taskq);
    240 
    241 		return; /* Error */
    242 	}
    243 
    244 	ieee80211_ifattach(ic);
    245 	ifp->if_percpuq = if_percpuq_create(ifp);
    246 	if_deferred_start_init(ifp, NULL);
    247 	if_register(ifp);
    248 
    249 	sc->sc_newstate = ic->ic_newstate;
    250 	ic->ic_newstate = bwfm_newstate;
    251 	ic->ic_send_mgmt = bwfm_send_mgmt;
    252 	ic->ic_recv_mgmt = bwfm_recv_mgmt;
    253 	ic->ic_crypto.cs_key_set = bwfm_key_set;
    254 	ic->ic_crypto.cs_key_delete = bwfm_key_delete;
    255 	ieee80211_media_init(ic, bwfm_media_change, bwfm_media_status);
    256 
    257 	ieee80211_announce(ic);
    258 
    259 	sc->sc_if_attached = true;
    260 }
    261 
    262 int
    263 bwfm_detach(struct bwfm_softc *sc, int flags)
    264 {
    265 	struct ieee80211com *ic = &sc->sc_ic;
    266 	struct ifnet *ifp = ic->ic_ifp;
    267 
    268 	if (sc->sc_if_attached) {
    269 		bpf_detach(ifp);
    270 		ieee80211_ifdetach(ic);
    271 		if_detach(ifp);
    272 	}
    273 
    274 	if (sc->sc_taskq)
    275 		workqueue_destroy(sc->sc_taskq);
    276 	if (sc->sc_freetask)
    277 		pcq_destroy(sc->sc_freetask);
    278 
    279 	return 0;
    280 }
    281 
    282 void
    283 bwfm_start(struct ifnet *ifp)
    284 {
    285 	struct bwfm_softc *sc = ifp->if_softc;
    286 	struct ieee80211com *ic = &sc->sc_ic;
    287 	struct mbuf *m;
    288 	int error;
    289 
    290 	if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
    291 		return;
    292 
    293 	/* TODO: return if no link? */
    294 
    295 	for (;;) {
    296 		struct ieee80211_node *ni;
    297 		struct ether_header *eh;
    298 
    299 		/* Discard management packets (fw handles this for us) */
    300 		IF_DEQUEUE(&ic->ic_mgtq, m);
    301 		if (m != NULL) {
    302 			m_freem(m);
    303 			continue;
    304 		}
    305 
    306 		IFQ_DEQUEUE(&ifp->if_snd, m);
    307 		if (m == NULL)
    308 			break;
    309 
    310 		eh = mtod(m, struct ether_header *);
    311 		ni = ieee80211_find_txnode(ic, eh->ether_dhost);
    312 		if (ni == NULL) {
    313 			ifp->if_oerrors++;
    314 			m_freem(m);
    315 			continue;
    316 		}
    317 
    318 		if (ieee80211_classify(ic, m, ni) != 0) {
    319 			ifp->if_oerrors++;
    320 			m_freem(m);
    321 			ieee80211_free_node(ni);
    322 			continue;
    323 		}
    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 
    332 		if (error != 0) {
    333 			ifp->if_oerrors++;
    334 			m_freem(m);
    335 			if (ni != NULL)
    336 				ieee80211_free_node(ni);
    337 		} else {
    338 			bpf_mtap3(ic->ic_rawbpf, m);
    339 		}
    340 	}
    341 }
    342 
    343 int
    344 bwfm_init(struct ifnet *ifp)
    345 {
    346 	struct bwfm_softc *sc = ifp->if_softc;
    347 	struct ieee80211com *ic = &sc->sc_ic;
    348 	uint8_t evmask[BWFM_EVENT_MASK_LEN];
    349 	struct bwfm_join_pref_params join_pref[2];
    350 
    351 	if (bwfm_fwvar_var_set_int(sc, "mpc", 1)) {
    352 		printf("%s: could not set mpc\n", DEVNAME(sc));
    353 		return EIO;
    354 	}
    355 
    356 	/* Select target by RSSI (boost on 5GHz) */
    357 	join_pref[0].type = BWFM_JOIN_PREF_RSSI_DELTA;
    358 	join_pref[0].len = 2;
    359 	join_pref[0].rssi_gain = BWFM_JOIN_PREF_RSSI_BOOST;
    360 	join_pref[0].band = BWFM_JOIN_PREF_BAND_5G;
    361 	join_pref[1].type = BWFM_JOIN_PREF_RSSI;
    362 	join_pref[1].len = 2;
    363 	join_pref[1].rssi_gain = 0;
    364 	join_pref[1].band = 0;
    365 	if (bwfm_fwvar_var_set_data(sc, "join_pref", join_pref,
    366 	    sizeof(join_pref))) {
    367 		printf("%s: could not set join pref\n", DEVNAME(sc));
    368 		return EIO;
    369 	}
    370 
    371 	memset(evmask, 0, sizeof(evmask));
    372 
    373 #define	ENABLE_EVENT(e)		evmask[(e) / 8] |= 1 << ((e) % 8)
    374 	/* Events used to drive the state machine */
    375 	ENABLE_EVENT(BWFM_E_ASSOC);
    376 	ENABLE_EVENT(BWFM_E_ESCAN_RESULT);
    377 	ENABLE_EVENT(BWFM_E_SET_SSID);
    378 	ENABLE_EVENT(BWFM_E_LINK);
    379 #undef	ENABLE_EVENT
    380 
    381 #ifdef BWFM_DEBUG
    382 	memset(evmask, 0xff, sizeof(evmask));
    383 #endif
    384 
    385 	if (bwfm_fwvar_var_set_data(sc, "event_msgs", evmask, sizeof(evmask))) {
    386 		printf("%s: could not set event mask\n", DEVNAME(sc));
    387 		return EIO;
    388 	}
    389 
    390 	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_CHANNEL_TIME,
    391 	    BWFM_DEFAULT_SCAN_CHANNEL_TIME)) {
    392 		printf("%s: could not set scan channel time\n", DEVNAME(sc));
    393 		return EIO;
    394 	}
    395 	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_UNASSOC_TIME,
    396 	    BWFM_DEFAULT_SCAN_UNASSOC_TIME)) {
    397 		printf("%s: could not set scan unassoc time\n", DEVNAME(sc));
    398 		return EIO;
    399 	}
    400 	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_PASSIVE_TIME,
    401 	    BWFM_DEFAULT_SCAN_PASSIVE_TIME)) {
    402 		printf("%s: could not set scan passive time\n", DEVNAME(sc));
    403 		return EIO;
    404 	}
    405 
    406 	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, 2)) {
    407 		printf("%s: could not set power\n", DEVNAME(sc));
    408 		return EIO;
    409 	}
    410 
    411 	bwfm_fwvar_var_set_int(sc, "txbf", 1);
    412 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_UP, 0);
    413 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_INFRA, 1);
    414 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_AP, 0);
    415 
    416 	/* Disable all offloading (ARP, NDP, TCP/UDP cksum). */
    417 	bwfm_fwvar_var_set_int(sc, "arp_ol", 0);
    418 	bwfm_fwvar_var_set_int(sc, "arpoe", 0);
    419 	bwfm_fwvar_var_set_int(sc, "ndoe", 0);
    420 	bwfm_fwvar_var_set_int(sc, "toe", 0);
    421 
    422 	/*
    423 	 * Tell the firmware supplicant that we are going to handle the
    424 	 * WPA handshake ourselves.
    425 	 */
    426 	bwfm_fwvar_var_set_int(sc, "sup_wpa", 0);
    427 
    428 	ifp->if_flags |= IFF_RUNNING;
    429 	ifp->if_flags &= ~IFF_OACTIVE;
    430 
    431 	if (ic->ic_opmode != IEEE80211_M_MONITOR) {
    432 		if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
    433 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
    434 	} else {
    435 		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
    436 	}
    437 
    438 	return 0;
    439 }
    440 
    441 void
    442 bwfm_stop(struct ifnet *ifp, int disable)
    443 {
    444 	struct bwfm_softc *sc = ifp->if_softc;
    445 	struct ieee80211com *ic = &sc->sc_ic;
    446 
    447 	sc->sc_tx_timer = 0;
    448 	ifp->if_timer = 0;
    449 	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
    450 
    451 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_DOWN, 1);
    452 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, 0);
    453 
    454 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
    455 }
    456 
    457 void
    458 bwfm_watchdog(struct ifnet *ifp)
    459 {
    460 	struct bwfm_softc *sc = ifp->if_softc;
    461 	struct ieee80211com *ic = &sc->sc_ic;
    462 
    463 	ifp->if_timer = 0;
    464 
    465 	if (sc->sc_tx_timer > 0) {
    466 		if (--sc->sc_tx_timer == 0) {
    467 			printf("%s: device timeout\n", DEVNAME(sc));
    468 			ifp->if_oerrors++;
    469 			return;
    470 		}
    471 		ifp->if_timer = 1;
    472 	}
    473 	ieee80211_watchdog(ic);
    474 }
    475 
    476 int
    477 bwfm_ioctl(struct ifnet *ifp, u_long cmd, void *data)
    478 {
    479 	struct bwfm_softc *sc = ifp->if_softc;
    480 	struct ieee80211com *ic = &sc->sc_ic;
    481 	int s, error = 0;
    482 
    483 	s = splnet();
    484 
    485 	switch (cmd) {
    486 	case SIOCSIFFLAGS:
    487 		if ((error = ifioctl_common(ifp, cmd, data)) != 0)
    488 			break;
    489 		switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
    490 		case IFF_UP | IFF_RUNNING:
    491 			break;
    492 		case IFF_UP:
    493 			bwfm_init(ifp);
    494 			break;
    495 		case IFF_RUNNING:
    496 			bwfm_stop(ifp, 1);
    497 			break;
    498 		case 0:
    499 			break;
    500 		}
    501 		break;
    502 
    503 	case SIOCADDMULTI:
    504 	case SIOCDELMULTI:
    505 		if ((error = ether_ioctl(ifp, cmd, data)) == ENETRESET) {
    506 			/* setup multicast filter, etc */
    507 			error = 0;
    508 		}
    509 		break;
    510 
    511 	default:
    512 		error = ieee80211_ioctl(ic, cmd, data);
    513 	}
    514 
    515 	if (error == ENETRESET) {
    516 		if ((ifp->if_flags & IFF_UP) != 0 &&
    517 		    (ifp->if_flags & IFF_RUNNING) != 0 &&
    518 		    ic->ic_roaming != IEEE80211_ROAMING_MANUAL) {
    519 			bwfm_init(ifp);
    520 		}
    521 		error = 0;
    522 	}
    523 
    524 	splx(s);
    525 
    526 	return error;
    527 }
    528 
    529 int
    530 bwfm_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
    531     int type, int arg)
    532 {
    533 	return 0;
    534 }
    535 
    536 void
    537 bwfm_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
    538     struct ieee80211_node *ni, int subtype, int rssi, uint32_t rstamp)
    539 {
    540 }
    541 
    542 int
    543 bwfm_key_set(struct ieee80211com *ic, const struct ieee80211_key *wk,
    544     const uint8_t mac[IEEE80211_ADDR_LEN])
    545 {
    546 	struct bwfm_softc *sc = ic->ic_ifp->if_softc;
    547 	struct bwfm_task *t;
    548 
    549 	t = pcq_get(sc->sc_freetask);
    550 	if (t == NULL) {
    551 		printf("%s: no free tasks\n", DEVNAME(sc));
    552 		return 0;
    553 	}
    554 
    555 	t->t_cmd = BWFM_TASK_KEY_SET;
    556 	t->t_key.key = wk;
    557 	memcpy(t->t_key.mac, mac, sizeof(t->t_key.mac));
    558 	workqueue_enqueue(sc->sc_taskq, (struct work *)t, NULL);
    559 	return 1;
    560 }
    561 
    562 static void
    563 bwfm_key_set_cb(struct bwfm_softc *sc, struct bwfm_cmd_key *ck)
    564 {
    565 	const struct ieee80211_key *wk = ck->key;
    566 	const uint8_t *mac = ck->mac;
    567 	struct bwfm_wsec_key wsec_key;
    568 	uint32_t wsec_enable, wsec;
    569 	bool ext_key;
    570 
    571 #ifdef BWFM_DEBUG
    572 	printf("key_set: key cipher %s len %d: ", wk->wk_cipher->ic_name, wk->wk_keylen);
    573 	for (int j = 0; j < sizeof(wk->wk_key); j++)
    574 		printf("%02x", wk->wk_key[j]);
    575 #endif
    576 
    577 	if ((wk->wk_flags & IEEE80211_KEY_GROUP) == 0 &&
    578 	    wk->wk_cipher->ic_cipher != IEEE80211_CIPHER_WEP) {
    579 		ext_key = true;
    580 	} else {
    581 		ext_key = false;
    582 	}
    583 
    584 #ifdef BWFM_DEBUG
    585 	printf(", ext_key = %d", ext_key);
    586 	printf(", mac = %02x:%02x:%02x:%02x:%02x:%02x",
    587 	    mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    588 	printf("\n");
    589 #endif
    590 
    591 	memset(&wsec_key, 0, sizeof(wsec_key));
    592 	if (ext_key && !IEEE80211_IS_MULTICAST(mac))
    593 		memcpy(wsec_key.ea, mac, sizeof(wsec_key.ea));
    594 	wsec_key.index = htole32(wk->wk_keyix);
    595 	wsec_key.len = htole32(wk->wk_keylen);
    596 	memcpy(wsec_key.data, wk->wk_key, sizeof(wsec_key.data));
    597 	if (!ext_key)
    598 		wsec_key.flags = htole32(BWFM_PRIMARY_KEY);
    599 
    600 	switch (wk->wk_cipher->ic_cipher) {
    601 	case IEEE80211_CIPHER_WEP:
    602 		if (wk->wk_keylen == 5)
    603 			wsec_key.algo = htole32(BWFM_CRYPTO_ALGO_WEP1);
    604 		else if (wk->wk_keylen == 13)
    605 			wsec_key.algo = htole32(BWFM_CRYPTO_ALGO_WEP128);
    606 		else
    607 			return;
    608 		wsec_enable = BWFM_WSEC_WEP;
    609 		break;
    610 	case IEEE80211_CIPHER_TKIP:
    611 		wsec_key.algo = htole32(BWFM_CRYPTO_ALGO_TKIP);
    612 		wsec_enable = BWFM_WSEC_TKIP;
    613 		break;
    614 	case IEEE80211_CIPHER_AES_CCM:
    615 		wsec_key.algo = htole32(BWFM_CRYPTO_ALGO_AES_CCM);
    616 		wsec_enable = BWFM_WSEC_AES;
    617 		break;
    618 	default:
    619 		printf("%s: %s: cipher %s not supported\n", DEVNAME(sc),
    620 		    __func__, wk->wk_cipher->ic_name);
    621 		return;
    622 	}
    623 
    624 	if (bwfm_fwvar_var_set_data(sc, "wsec_key", &wsec_key, sizeof(wsec_key)))
    625 		return;
    626 
    627 	bwfm_fwvar_var_set_int(sc, "wpa_auth", BWFM_WPA_AUTH_WPA2_PSK);
    628 
    629 	bwfm_fwvar_var_get_int(sc, "wsec", &wsec);
    630 	wsec |= wsec_enable;
    631 	bwfm_fwvar_var_set_int(sc, "wsec", wsec);
    632 }
    633 
    634 int
    635 bwfm_key_delete(struct ieee80211com *ic, const struct ieee80211_key *wk)
    636 {
    637 	struct bwfm_softc *sc = ic->ic_ifp->if_softc;
    638 	struct bwfm_task *t;
    639 
    640 	t = pcq_get(sc->sc_freetask);
    641 	if (t == NULL) {
    642 		printf("%s: no free tasks\n", DEVNAME(sc));
    643 		return 0;
    644 	}
    645 
    646 	t->t_cmd = BWFM_TASK_KEY_DELETE;
    647 	t->t_key.key = wk;
    648 	memset(t->t_key.mac, 0, sizeof(t->t_key.mac));
    649 	workqueue_enqueue(sc->sc_taskq, (struct work *)t, NULL);
    650 
    651 	return 1;
    652 }
    653 
    654 static void
    655 bwfm_key_delete_cb(struct bwfm_softc *sc, struct bwfm_cmd_key *ck)
    656 {
    657 	const struct ieee80211_key *wk = ck->key;
    658 	struct bwfm_wsec_key wsec_key;
    659 
    660 	memset(&wsec_key, 0, sizeof(wsec_key));
    661 	wsec_key.index = htole32(wk->wk_keyix);
    662 	wsec_key.flags = htole32(BWFM_PRIMARY_KEY);
    663 
    664 	if (bwfm_fwvar_var_set_data(sc, "wsec_key", &wsec_key, sizeof(wsec_key)))
    665 		return;
    666 }
    667 
    668 int
    669 bwfm_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
    670 {
    671 	struct bwfm_softc *sc = ic->ic_ifp->if_softc;
    672 	struct bwfm_task *t;
    673 
    674 	t = pcq_get(sc->sc_freetask);
    675 	if (t == NULL) {
    676 		printf("%s: no free tasks\n", DEVNAME(sc));
    677 		return EIO;
    678 	}
    679 
    680 	t->t_cmd = BWFM_TASK_NEWSTATE;
    681 	t->t_newstate.state = nstate;
    682 	t->t_newstate.arg = arg;
    683 	workqueue_enqueue(sc->sc_taskq, (struct work *)t, NULL);
    684 
    685 	return 0;
    686 }
    687 
    688 void
    689 bwfm_newstate_cb(struct bwfm_softc *sc, struct bwfm_cmd_newstate *cmd)
    690 {
    691 	struct ieee80211com *ic = &sc->sc_ic;
    692 	enum ieee80211_state ostate = ic->ic_state;
    693 	enum ieee80211_state nstate = cmd->state;
    694 	int s;
    695 
    696 	DPRINTF(("%s: newstate %d -> %d\n", DEVNAME(sc), ostate, nstate));
    697 
    698 	s = splnet();
    699 
    700 	switch (nstate) {
    701 	case IEEE80211_S_INIT:
    702 		break;
    703 
    704 	case IEEE80211_S_SCAN:
    705 		if (ostate != IEEE80211_S_SCAN) {
    706 			/* Start of scanning */
    707 			bwfm_scan(sc);
    708 		}
    709 		break;
    710 
    711 	case IEEE80211_S_AUTH:
    712 		bwfm_connect(sc);
    713 		break;
    714 
    715 	case IEEE80211_S_ASSOC:
    716 		break;
    717 
    718 	case IEEE80211_S_RUN:
    719 		break;
    720 	}
    721 
    722 	sc->sc_newstate(ic, nstate, cmd->arg);
    723 
    724 	splx(s);
    725 }
    726 
    727 void
    728 bwfm_task(struct work *wk, void *arg)
    729 {
    730 	struct bwfm_task *t = (struct bwfm_task *)wk;
    731 	struct bwfm_softc *sc = t->t_sc;
    732 
    733 	switch (t->t_cmd) {
    734 	case BWFM_TASK_NEWSTATE:
    735 		bwfm_newstate_cb(sc, &t->t_newstate);
    736 		break;
    737 	case BWFM_TASK_KEY_SET:
    738 		bwfm_key_set_cb(sc, &t->t_key);
    739 		break;
    740 	case BWFM_TASK_KEY_DELETE:
    741 		bwfm_key_delete_cb(sc, &t->t_key);
    742 		break;
    743 	default:
    744 		panic("bwfm: unknown task command %d", t->t_cmd);
    745 	}
    746 
    747 	pcq_put(sc->sc_freetask, t);
    748 }
    749 
    750 int
    751 bwfm_media_change(struct ifnet *ifp)
    752 {
    753 	return 0;
    754 }
    755 
    756 void
    757 bwfm_media_status(struct ifnet *ifp, struct ifmediareq *imr)
    758 {
    759 }
    760 
    761 /* Chip initialization (SDIO, PCIe) */
    762 int
    763 bwfm_chip_attach(struct bwfm_softc *sc)
    764 {
    765 	struct bwfm_core *core;
    766 	int need_socram = 0;
    767 	int has_socram = 0;
    768 	int cpu_found = 0;
    769 	uint32_t val;
    770 
    771 	LIST_INIT(&sc->sc_chip.ch_list);
    772 
    773 	if (sc->sc_buscore_ops->bc_prepare(sc) != 0) {
    774 		printf("%s: failed buscore prepare\n", DEVNAME(sc));
    775 		return 1;
    776 	}
    777 
    778 	val = sc->sc_buscore_ops->bc_read(sc,
    779 	    BWFM_CHIP_BASE + BWFM_CHIP_REG_CHIPID);
    780 	sc->sc_chip.ch_chip = BWFM_CHIP_CHIPID_ID(val);
    781 	sc->sc_chip.ch_chiprev = BWFM_CHIP_CHIPID_REV(val);
    782 
    783 	if ((sc->sc_chip.ch_chip > 0xa000) || (sc->sc_chip.ch_chip < 0x4000))
    784 		snprintf(sc->sc_chip.ch_name, sizeof(sc->sc_chip.ch_name),
    785 		    "%d", sc->sc_chip.ch_chip);
    786 	else
    787 		snprintf(sc->sc_chip.ch_name, sizeof(sc->sc_chip.ch_name),
    788 		    "%x", sc->sc_chip.ch_chip);
    789 
    790 	switch (BWFM_CHIP_CHIPID_TYPE(val))
    791 	{
    792 	case BWFM_CHIP_CHIPID_TYPE_SOCI_SB:
    793 		printf("%s: SoC interconnect SB not implemented\n",
    794 		    DEVNAME(sc));
    795 		return 1;
    796 	case BWFM_CHIP_CHIPID_TYPE_SOCI_AI:
    797 		sc->sc_chip.ch_core_isup = bwfm_chip_ai_isup;
    798 		sc->sc_chip.ch_core_disable = bwfm_chip_ai_disable;
    799 		sc->sc_chip.ch_core_reset = bwfm_chip_ai_reset;
    800 		bwfm_chip_dmp_erom_scan(sc);
    801 		break;
    802 	default:
    803 		printf("%s: SoC interconnect %d unknown\n",
    804 		    DEVNAME(sc), BWFM_CHIP_CHIPID_TYPE(val));
    805 		return 1;
    806 	}
    807 
    808 	LIST_FOREACH(core, &sc->sc_chip.ch_list, co_link) {
    809 		DPRINTF(("%s: 0x%x:%-2d base 0x%08x wrap 0x%08x\n",
    810 		    DEVNAME(sc), core->co_id, core->co_rev,
    811 		    core->co_base, core->co_wrapbase));
    812 
    813 		switch (core->co_id) {
    814 		case BWFM_AGENT_CORE_ARM_CM3:
    815 			need_socram = true;
    816 			/* FALLTHROUGH */
    817 		case BWFM_AGENT_CORE_ARM_CR4:
    818 		case BWFM_AGENT_CORE_ARM_CA7:
    819 			cpu_found = true;
    820 			break;
    821 		case BWFM_AGENT_INTERNAL_MEM:
    822 			has_socram = true;
    823 			break;
    824 		default:
    825 			break;
    826 		}
    827 	}
    828 
    829 	if (!cpu_found) {
    830 		printf("%s: CPU core not detected\n", DEVNAME(sc));
    831 		return 1;
    832 	}
    833 	if (need_socram && !has_socram) {
    834 		printf("%s: RAM core not provided\n", DEVNAME(sc));
    835 		return 1;
    836 	}
    837 
    838 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL)
    839 		bwfm_chip_cr4_set_passive(sc);
    840 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL)
    841 		bwfm_chip_ca7_set_passive(sc);
    842 	if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL)
    843 		bwfm_chip_cm3_set_passive(sc);
    844 
    845 	if (sc->sc_buscore_ops->bc_reset) {
    846 		sc->sc_buscore_ops->bc_reset(sc);
    847 		if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL)
    848 			bwfm_chip_cr4_set_passive(sc);
    849 		if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL)
    850 			bwfm_chip_ca7_set_passive(sc);
    851 		if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL)
    852 			bwfm_chip_cm3_set_passive(sc);
    853 	}
    854 
    855 	/* TODO: get raminfo */
    856 
    857 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
    858 	sc->sc_chip.ch_cc_caps = sc->sc_buscore_ops->bc_read(sc,
    859 	    core->co_base + BWFM_CHIP_REG_CAPABILITIES);
    860 	sc->sc_chip.ch_cc_caps_ext = sc->sc_buscore_ops->bc_read(sc,
    861 	    core->co_base + BWFM_CHIP_REG_CAPABILITIES_EXT);
    862 
    863 	core = bwfm_chip_get_pmu(sc);
    864 	if (sc->sc_chip.ch_cc_caps & BWFM_CHIP_REG_CAPABILITIES_PMU) {
    865 		sc->sc_chip.ch_pmucaps = sc->sc_buscore_ops->bc_read(sc,
    866 		    core->co_base + BWFM_CHIP_REG_PMUCAPABILITIES);
    867 		sc->sc_chip.ch_pmurev = sc->sc_chip.ch_pmucaps &
    868 		    BWFM_CHIP_REG_PMUCAPABILITIES_REV_MASK;
    869 	}
    870 
    871 	if (sc->sc_buscore_ops->bc_setup)
    872 		sc->sc_buscore_ops->bc_setup(sc);
    873 
    874 	return 0;
    875 }
    876 
    877 struct bwfm_core *
    878 bwfm_chip_get_core(struct bwfm_softc *sc, int id)
    879 {
    880 	struct bwfm_core *core;
    881 
    882 	LIST_FOREACH(core, &sc->sc_chip.ch_list, co_link) {
    883 		if (core->co_id == id)
    884 			return core;
    885 	}
    886 
    887 	return NULL;
    888 }
    889 
    890 struct bwfm_core *
    891 bwfm_chip_get_pmu(struct bwfm_softc *sc)
    892 {
    893 	struct bwfm_core *cc, *pmu;
    894 
    895 	cc = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
    896 	if (cc->co_rev >= 35 && sc->sc_chip.ch_cc_caps_ext &
    897 	    BWFM_CHIP_REG_CAPABILITIES_EXT_AOB_PRESENT) {
    898 		pmu = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_PMU);
    899 		if (pmu)
    900 			return pmu;
    901 	}
    902 
    903 	return cc;
    904 }
    905 
    906 /* Functions for the AI interconnect */
    907 int
    908 bwfm_chip_ai_isup(struct bwfm_softc *sc, struct bwfm_core *core)
    909 {
    910 	uint32_t ioctl, reset;
    911 
    912 	ioctl = sc->sc_buscore_ops->bc_read(sc,
    913 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
    914 	reset = sc->sc_buscore_ops->bc_read(sc,
    915 	    core->co_wrapbase + BWFM_AGENT_RESET_CTL);
    916 
    917 	if (((ioctl & (BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK)) ==
    918 	    BWFM_AGENT_IOCTL_CLK) &&
    919 	    ((reset & BWFM_AGENT_RESET_CTL_RESET) == 0))
    920 		return 1;
    921 
    922 	return 0;
    923 }
    924 
    925 void
    926 bwfm_chip_ai_disable(struct bwfm_softc *sc, struct bwfm_core *core,
    927     uint32_t prereset, uint32_t reset)
    928 {
    929 	uint32_t val;
    930 	int i;
    931 
    932 	val = sc->sc_buscore_ops->bc_read(sc,
    933 	    core->co_wrapbase + BWFM_AGENT_RESET_CTL);
    934 	if ((val & BWFM_AGENT_RESET_CTL_RESET) == 0) {
    935 
    936 		sc->sc_buscore_ops->bc_write(sc,
    937 		    core->co_wrapbase + BWFM_AGENT_IOCTL,
    938 		    prereset | BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK);
    939 		sc->sc_buscore_ops->bc_read(sc,
    940 		    core->co_wrapbase + BWFM_AGENT_IOCTL);
    941 
    942 		sc->sc_buscore_ops->bc_write(sc,
    943 		    core->co_wrapbase + BWFM_AGENT_RESET_CTL,
    944 		    BWFM_AGENT_RESET_CTL_RESET);
    945 		delay(20);
    946 
    947 		for (i = 300; i > 0; i--) {
    948 			if (sc->sc_buscore_ops->bc_read(sc,
    949 			    core->co_wrapbase + BWFM_AGENT_RESET_CTL) ==
    950 			    BWFM_AGENT_RESET_CTL_RESET)
    951 				break;
    952 		}
    953 		if (i == 0)
    954 			printf("%s: timeout on core reset\n", DEVNAME(sc));
    955 	}
    956 
    957 	sc->sc_buscore_ops->bc_write(sc,
    958 	    core->co_wrapbase + BWFM_AGENT_IOCTL,
    959 	    reset | BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK);
    960 	sc->sc_buscore_ops->bc_read(sc,
    961 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
    962 }
    963 
    964 void
    965 bwfm_chip_ai_reset(struct bwfm_softc *sc, struct bwfm_core *core,
    966     uint32_t prereset, uint32_t reset, uint32_t postreset)
    967 {
    968 	int i;
    969 
    970 	bwfm_chip_ai_disable(sc, core, prereset, reset);
    971 
    972 	for (i = 50; i > 0; i--) {
    973 		if ((sc->sc_buscore_ops->bc_read(sc,
    974 		    core->co_wrapbase + BWFM_AGENT_RESET_CTL) &
    975 		    BWFM_AGENT_RESET_CTL_RESET) == 0)
    976 			break;
    977 		sc->sc_buscore_ops->bc_write(sc,
    978 		    core->co_wrapbase + BWFM_AGENT_RESET_CTL, 0);
    979 		delay(60);
    980 	}
    981 	if (i == 0)
    982 		printf("%s: timeout on core reset\n", DEVNAME(sc));
    983 
    984 	sc->sc_buscore_ops->bc_write(sc,
    985 	    core->co_wrapbase + BWFM_AGENT_IOCTL,
    986 	    postreset | BWFM_AGENT_IOCTL_CLK);
    987 	sc->sc_buscore_ops->bc_read(sc,
    988 	    core->co_wrapbase + BWFM_AGENT_IOCTL);
    989 }
    990 
    991 void
    992 bwfm_chip_dmp_erom_scan(struct bwfm_softc *sc)
    993 {
    994 	uint32_t erom, val, base, wrap;
    995 	uint8_t type = 0;
    996 	uint16_t id;
    997 	uint8_t nmw, nsw, rev;
    998 	struct bwfm_core *core;
    999 
   1000 	erom = sc->sc_buscore_ops->bc_read(sc,
   1001 	    BWFM_CHIP_BASE + BWFM_CHIP_REG_EROMPTR);
   1002 	while (type != BWFM_DMP_DESC_EOT) {
   1003 		val = sc->sc_buscore_ops->bc_read(sc, erom);
   1004 		type = val & BWFM_DMP_DESC_MASK;
   1005 		erom += 4;
   1006 
   1007 		if (type != BWFM_DMP_DESC_COMPONENT)
   1008 			continue;
   1009 
   1010 		id = (val & BWFM_DMP_COMP_PARTNUM)
   1011 		    >> BWFM_DMP_COMP_PARTNUM_S;
   1012 
   1013 		val = sc->sc_buscore_ops->bc_read(sc, erom);
   1014 		type = val & BWFM_DMP_DESC_MASK;
   1015 		erom += 4;
   1016 
   1017 		if (type != BWFM_DMP_DESC_COMPONENT) {
   1018 			printf("%s: not component descriptor\n", DEVNAME(sc));
   1019 			return;
   1020 		}
   1021 
   1022 		nmw = (val & BWFM_DMP_COMP_NUM_MWRAP)
   1023 		    >> BWFM_DMP_COMP_NUM_MWRAP_S;
   1024 		nsw = (val & BWFM_DMP_COMP_NUM_SWRAP)
   1025 		    >> BWFM_DMP_COMP_NUM_SWRAP_S;
   1026 		rev = (val & BWFM_DMP_COMP_REVISION)
   1027 		    >> BWFM_DMP_COMP_REVISION_S;
   1028 
   1029 		if (nmw + nsw == 0 && id != BWFM_AGENT_CORE_PMU)
   1030 			continue;
   1031 
   1032 		if (bwfm_chip_dmp_get_regaddr(sc, &erom, &base, &wrap))
   1033 			continue;
   1034 
   1035 		core = kmem_alloc(sizeof(*core), KM_SLEEP);
   1036 		core->co_id = id;
   1037 		core->co_base = base;
   1038 		core->co_wrapbase = wrap;
   1039 		core->co_rev = rev;
   1040 		LIST_INSERT_HEAD(&sc->sc_chip.ch_list, core, co_link);
   1041 	}
   1042 }
   1043 
   1044 int
   1045 bwfm_chip_dmp_get_regaddr(struct bwfm_softc *sc, uint32_t *erom,
   1046     uint32_t *base, uint32_t *wrap)
   1047 {
   1048 	uint8_t type = 0, mpnum __unused = 0;
   1049 	uint8_t stype, sztype, wraptype;
   1050 	uint32_t val;
   1051 
   1052 	*base = 0;
   1053 	*wrap = 0;
   1054 
   1055 	val = sc->sc_buscore_ops->bc_read(sc, *erom);
   1056 	type = val & BWFM_DMP_DESC_MASK;
   1057 	if (type == BWFM_DMP_DESC_MASTER_PORT) {
   1058 		mpnum = (val & BWFM_DMP_MASTER_PORT_NUM)
   1059 		    >> BWFM_DMP_MASTER_PORT_NUM_S;
   1060 		wraptype = BWFM_DMP_SLAVE_TYPE_MWRAP;
   1061 		*erom += 4;
   1062 	} else if ((type & ~BWFM_DMP_DESC_ADDRSIZE_GT32) ==
   1063 	    BWFM_DMP_DESC_ADDRESS)
   1064 		wraptype = BWFM_DMP_SLAVE_TYPE_SWRAP;
   1065 	else
   1066 		return 1;
   1067 
   1068 	do {
   1069 		do {
   1070 			val = sc->sc_buscore_ops->bc_read(sc, *erom);
   1071 			type = val & BWFM_DMP_DESC_MASK;
   1072 			if (type == BWFM_DMP_DESC_COMPONENT)
   1073 				return 0;
   1074 			if (type == BWFM_DMP_DESC_EOT)
   1075 				return 1;
   1076 			*erom += 4;
   1077 		} while ((type & ~BWFM_DMP_DESC_ADDRSIZE_GT32) !=
   1078 		     BWFM_DMP_DESC_ADDRESS);
   1079 
   1080 		if (type & BWFM_DMP_DESC_ADDRSIZE_GT32)
   1081 			*erom += 4;
   1082 
   1083 		sztype = (val & BWFM_DMP_SLAVE_SIZE_TYPE)
   1084 		    >> BWFM_DMP_SLAVE_SIZE_TYPE_S;
   1085 		if (sztype == BWFM_DMP_SLAVE_SIZE_DESC) {
   1086 			val = sc->sc_buscore_ops->bc_read(sc, *erom);
   1087 			type = val & BWFM_DMP_DESC_MASK;
   1088 			if (type & BWFM_DMP_DESC_ADDRSIZE_GT32)
   1089 				*erom += 8;
   1090 			else
   1091 				*erom += 4;
   1092 		}
   1093 		if (sztype != BWFM_DMP_SLAVE_SIZE_4K)
   1094 			continue;
   1095 
   1096 		stype = (val & BWFM_DMP_SLAVE_TYPE) >> BWFM_DMP_SLAVE_TYPE_S;
   1097 		if (*base == 0 && stype == BWFM_DMP_SLAVE_TYPE_SLAVE)
   1098 			*base = val & BWFM_DMP_SLAVE_ADDR_BASE;
   1099 		if (*wrap == 0 && stype == wraptype)
   1100 			*wrap = val & BWFM_DMP_SLAVE_ADDR_BASE;
   1101 	} while (*base == 0 || *wrap == 0);
   1102 
   1103 	return 0;
   1104 }
   1105 
   1106 /* Core configuration */
   1107 void
   1108 bwfm_chip_cr4_set_passive(struct bwfm_softc *sc)
   1109 {
   1110 	panic("%s: CR4 not supported", DEVNAME(sc));
   1111 }
   1112 
   1113 void
   1114 bwfm_chip_ca7_set_passive(struct bwfm_softc *sc)
   1115 {
   1116 	panic("%s: CA7 not supported", DEVNAME(sc));
   1117 }
   1118 
   1119 void
   1120 bwfm_chip_cm3_set_passive(struct bwfm_softc *sc)
   1121 {
   1122 	struct bwfm_core *core;
   1123 
   1124 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3);
   1125 	sc->sc_chip.ch_core_disable(sc, core, 0, 0);
   1126 	core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_80211);
   1127 	sc->sc_chip.ch_core_reset(sc, core, BWFM_AGENT_D11_IOCTL_PHYRESET |
   1128 	    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN, BWFM_AGENT_D11_IOCTL_PHYCLOCKEN,
   1129 	    BWFM_AGENT_D11_IOCTL_PHYCLOCKEN);
   1130 	core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM);
   1131 	sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
   1132 
   1133 	if (sc->sc_chip.ch_chip == BRCM_CC_43430_CHIP_ID) {
   1134 		sc->sc_buscore_ops->bc_write(sc,
   1135 		    core->co_base + BWFM_SOCRAM_BANKIDX, 3);
   1136 		sc->sc_buscore_ops->bc_write(sc,
   1137 		    core->co_base + BWFM_SOCRAM_BANKPDA, 0);
   1138 	}
   1139 }
   1140 
   1141 /* BCDC protocol implementation */
   1142 int
   1143 bwfm_proto_bcdc_query_dcmd(struct bwfm_softc *sc, int ifidx,
   1144     int cmd, char *buf, size_t *len)
   1145 {
   1146 	struct bwfm_proto_bcdc_dcmd *dcmd;
   1147 	size_t size = sizeof(dcmd->hdr) + *len;
   1148 	static int reqid = 0;
   1149 	int ret = 1;
   1150 
   1151 	reqid++;
   1152 
   1153 	dcmd = kmem_zalloc(sizeof(*dcmd), KM_SLEEP);
   1154 	if (*len > sizeof(dcmd->buf))
   1155 		goto err;
   1156 
   1157 	dcmd->hdr.cmd = htole32(cmd);
   1158 	dcmd->hdr.len = htole32(*len);
   1159 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_GET;
   1160 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_ID_SET(reqid);
   1161 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_IF_SET(ifidx);
   1162 	dcmd->hdr.flags = htole32(dcmd->hdr.flags);
   1163 	memcpy(&dcmd->buf, buf, *len);
   1164 
   1165 	if (sc->sc_bus_ops->bs_txctl(sc, (void *)dcmd,
   1166 	     sizeof(dcmd->hdr) + *len)) {
   1167 		DPRINTF(("%s: tx failed\n", DEVNAME(sc)));
   1168 		goto err;
   1169 	}
   1170 
   1171 	do {
   1172 		if (sc->sc_bus_ops->bs_rxctl(sc, (void *)dcmd, &size)) {
   1173 			DPRINTF(("%s: rx failed\n", DEVNAME(sc)));
   1174 			goto err;
   1175 		}
   1176 		dcmd->hdr.cmd = le32toh(dcmd->hdr.cmd);
   1177 		dcmd->hdr.len = le32toh(dcmd->hdr.len);
   1178 		dcmd->hdr.flags = le32toh(dcmd->hdr.flags);
   1179 		dcmd->hdr.status = le32toh(dcmd->hdr.status);
   1180 	} while (BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags) != reqid);
   1181 
   1182 	if (BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags) != reqid) {
   1183 		printf("%s: unexpected request id\n", DEVNAME(sc));
   1184 		goto err;
   1185 	}
   1186 
   1187 	if (buf) {
   1188 		if (size > *len)
   1189 			size = *len;
   1190 		if (size < *len)
   1191 			*len = size;
   1192 		memcpy(buf, dcmd->buf, *len);
   1193 	}
   1194 
   1195 	if (dcmd->hdr.flags & BWFM_BCDC_DCMD_ERROR)
   1196 		ret = dcmd->hdr.status;
   1197 	else
   1198 		ret = 0;
   1199 err:
   1200 	kmem_free(dcmd, sizeof(*dcmd));
   1201 	return ret;
   1202 }
   1203 
   1204 int
   1205 bwfm_proto_bcdc_set_dcmd(struct bwfm_softc *sc, int ifidx,
   1206     int cmd, char *buf, size_t len)
   1207 {
   1208 	struct bwfm_proto_bcdc_dcmd *dcmd;
   1209 	size_t size = sizeof(dcmd->hdr) + len;
   1210 	int reqid = 0;
   1211 	int ret = 1;
   1212 
   1213 	reqid++;
   1214 
   1215 	dcmd = kmem_zalloc(sizeof(*dcmd), KM_SLEEP);
   1216 	if (len > sizeof(dcmd->buf))
   1217 		goto err;
   1218 
   1219 	dcmd->hdr.cmd = htole32(cmd);
   1220 	dcmd->hdr.len = htole32(len);
   1221 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_SET;
   1222 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_ID_SET(reqid);
   1223 	dcmd->hdr.flags |= BWFM_BCDC_DCMD_IF_SET(ifidx);
   1224 	dcmd->hdr.flags = htole32(dcmd->hdr.flags);
   1225 	memcpy(&dcmd->buf, buf, len);
   1226 
   1227 	if (sc->sc_bus_ops->bs_txctl(sc, (void *)dcmd, size)) {
   1228 		DPRINTF(("%s: tx failed\n", DEVNAME(sc)));
   1229 		goto err;
   1230 	}
   1231 
   1232 	do {
   1233 		if (sc->sc_bus_ops->bs_rxctl(sc, (void *)dcmd, &size)) {
   1234 			DPRINTF(("%s: rx failed\n", DEVNAME(sc)));
   1235 			goto err;
   1236 		}
   1237 		dcmd->hdr.cmd = le32toh(dcmd->hdr.cmd);
   1238 		dcmd->hdr.len = le32toh(dcmd->hdr.len);
   1239 		dcmd->hdr.flags = le32toh(dcmd->hdr.flags);
   1240 		dcmd->hdr.status = le32toh(dcmd->hdr.status);
   1241 	} while (BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags) != reqid);
   1242 
   1243 	if (BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags) != reqid) {
   1244 		printf("%s: unexpected request id\n", DEVNAME(sc));
   1245 		goto err;
   1246 	}
   1247 
   1248 	if (dcmd->hdr.flags & BWFM_BCDC_DCMD_ERROR)
   1249 		return dcmd->hdr.status;
   1250 
   1251 	ret = 0;
   1252 err:
   1253 	kmem_free(dcmd, sizeof(*dcmd));
   1254 	return ret;
   1255 }
   1256 
   1257 /* FW Variable code */
   1258 int
   1259 bwfm_fwvar_cmd_get_data(struct bwfm_softc *sc, int cmd, void *data, size_t len)
   1260 {
   1261 	return sc->sc_proto_ops->proto_query_dcmd(sc, 0, cmd, data, &len);
   1262 }
   1263 
   1264 int
   1265 bwfm_fwvar_cmd_set_data(struct bwfm_softc *sc, int cmd, void *data, size_t len)
   1266 {
   1267 	return sc->sc_proto_ops->proto_set_dcmd(sc, 0, cmd, data, len);
   1268 }
   1269 
   1270 int
   1271 bwfm_fwvar_cmd_get_int(struct bwfm_softc *sc, int cmd, uint32_t *data)
   1272 {
   1273 	int ret;
   1274 	ret = bwfm_fwvar_cmd_get_data(sc, cmd, data, sizeof(*data));
   1275 	*data = le32toh(*data);
   1276 	return ret;
   1277 }
   1278 
   1279 int
   1280 bwfm_fwvar_cmd_set_int(struct bwfm_softc *sc, int cmd, uint32_t data)
   1281 {
   1282 	data = htole32(data);
   1283 	return bwfm_fwvar_cmd_set_data(sc, cmd, &data, sizeof(data));
   1284 }
   1285 
   1286 int
   1287 bwfm_fwvar_var_get_data(struct bwfm_softc *sc, const char *name, void *data, size_t len)
   1288 {
   1289 	char *buf;
   1290 	int ret;
   1291 
   1292 	buf = kmem_alloc(strlen(name) + 1 + len, KM_SLEEP);
   1293 	memcpy(buf, name, strlen(name) + 1);
   1294 	memcpy(buf + strlen(name) + 1, data, len);
   1295 	ret = bwfm_fwvar_cmd_get_data(sc, BWFM_C_GET_VAR,
   1296 	    buf, strlen(name) + 1 + len);
   1297 	memcpy(data, buf, len);
   1298 	kmem_free(buf, strlen(name) + 1 + len);
   1299 	return ret;
   1300 }
   1301 
   1302 int
   1303 bwfm_fwvar_var_set_data(struct bwfm_softc *sc, const char *name, void *data, size_t len)
   1304 {
   1305 	char *buf;
   1306 	int ret;
   1307 
   1308 	buf = kmem_alloc(strlen(name) + 1 + len, KM_SLEEP);
   1309 	memcpy(buf, name, strlen(name) + 1);
   1310 	memcpy(buf + strlen(name) + 1, data, len);
   1311 	ret = bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_VAR,
   1312 	    buf, strlen(name) + 1 + len);
   1313 	kmem_free(buf, strlen(name) + 1 + len);
   1314 	return ret;
   1315 }
   1316 
   1317 int
   1318 bwfm_fwvar_var_get_int(struct bwfm_softc *sc, const char *name, uint32_t *data)
   1319 {
   1320 	int ret;
   1321 	ret = bwfm_fwvar_var_get_data(sc, name, data, sizeof(*data));
   1322 	*data = le32toh(*data);
   1323 	return ret;
   1324 }
   1325 
   1326 int
   1327 bwfm_fwvar_var_set_int(struct bwfm_softc *sc, const char *name, uint32_t data)
   1328 {
   1329 	data = htole32(data);
   1330 	return bwfm_fwvar_var_set_data(sc, name, &data, sizeof(data));
   1331 }
   1332 
   1333 /* 802.11 code */
   1334 void
   1335 bwfm_scan(struct bwfm_softc *sc)
   1336 {
   1337 	struct bwfm_escan_params *params;
   1338 	uint32_t nssid = 0, nchannel = 0;
   1339 	size_t params_size;
   1340 
   1341 #if 0
   1342 	/* Active scan is used for scanning for an SSID */
   1343 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PASSIVE_SCAN, 0);
   1344 #endif
   1345 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PASSIVE_SCAN, 1);
   1346 
   1347 	params_size = sizeof(*params);
   1348 	params_size += sizeof(uint32_t) * ((nchannel + 1) / 2);
   1349 	params_size += sizeof(struct bwfm_ssid) * nssid;
   1350 
   1351 	params = kmem_zalloc(params_size, KM_SLEEP);
   1352 	memset(params->scan_params.bssid, 0xff,
   1353 	    sizeof(params->scan_params.bssid));
   1354 	params->scan_params.bss_type = 2;
   1355 	params->scan_params.nprobes = htole32(-1);
   1356 	params->scan_params.active_time = htole32(-1);
   1357 	params->scan_params.passive_time = htole32(-1);
   1358 	params->scan_params.home_time = htole32(-1);
   1359 	params->version = htole32(BWFM_ESCAN_REQ_VERSION);
   1360 	params->action = htole16(WL_ESCAN_ACTION_START);
   1361 	params->sync_id = htole16(0x1234);
   1362 
   1363 #if 0
   1364 	/* Scan a specific channel */
   1365 	params->scan_params.channel_list[0] = htole16(
   1366 	    (1 & 0xff) << 0 |
   1367 	    (3 & 0x3) << 8 |
   1368 	    (2 & 0x3) << 10 |
   1369 	    (2 & 0x3) << 12
   1370 	    );
   1371 	params->scan_params.channel_num = htole32(
   1372 	    (1 & 0xffff) << 0
   1373 	    );
   1374 #endif
   1375 
   1376 	bwfm_fwvar_var_set_data(sc, "escan", params, params_size);
   1377 	kmem_free(params, params_size);
   1378 }
   1379 
   1380 static __inline int
   1381 bwfm_iswpaoui(const uint8_t *frm)
   1382 {
   1383 	return frm[1] > 3 && le32dec(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
   1384 }
   1385 
   1386 /*
   1387  * Derive wireless security settings from WPA/RSN IE.
   1388  */
   1389 static uint32_t
   1390 bwfm_get_wsec(struct bwfm_softc *sc)
   1391 {
   1392 	struct ieee80211com *ic = &sc->sc_ic;
   1393 	uint8_t *wpa = ic->ic_opt_ie;
   1394 
   1395 	KASSERT(ic->ic_opt_ie_len > 0);
   1396 
   1397 	if (wpa[0] != IEEE80211_ELEMID_RSN) {
   1398 		if (ic->ic_opt_ie_len < 12)
   1399 			return BWFM_WSEC_NONE;
   1400 
   1401 		/* non-RSN IE, expect that we are doing WPA1 */
   1402 		if ((ic->ic_flags & IEEE80211_F_WPA1) == 0)
   1403 			return BWFM_WSEC_NONE;
   1404 
   1405 		/* Must contain WPA OUI */
   1406 		if (!bwfm_iswpaoui(wpa))
   1407 			return BWFM_WSEC_NONE;
   1408 
   1409 		switch (le32dec(wpa + 8)) {
   1410 		case ((WPA_CSE_TKIP<<24)|WPA_OUI):
   1411 			return BWFM_WSEC_TKIP;
   1412 		case ((WPA_CSE_CCMP<<24)|WPA_OUI):
   1413 			return BWFM_WSEC_AES;
   1414 		default:
   1415 			return BWFM_WSEC_NONE;
   1416 		}
   1417 	} else {
   1418 		if (ic->ic_opt_ie_len < 14)
   1419 			return BWFM_WSEC_NONE;
   1420 
   1421 		/* RSN IE, expect that we are doing WPA2 */
   1422 		if ((ic->ic_flags & IEEE80211_F_WPA2) == 0)
   1423 			return BWFM_WSEC_NONE;
   1424 
   1425 		switch (le32dec(wpa + 10)) {
   1426 		case ((RSN_CSE_TKIP<<24)|RSN_OUI):
   1427 			return BWFM_WSEC_TKIP;
   1428 		case ((RSN_CSE_CCMP<<24)|RSN_OUI):
   1429 			return BWFM_WSEC_AES;
   1430 		default:
   1431 			return BWFM_WSEC_NONE;
   1432 		}
   1433 	}
   1434 }
   1435 
   1436 void
   1437 bwfm_connect(struct bwfm_softc *sc)
   1438 {
   1439 	struct ieee80211com *ic = &sc->sc_ic;
   1440 	struct ieee80211_node *ni = ic->ic_bss;
   1441 	struct bwfm_ext_join_params *params;
   1442 
   1443 	if (ic->ic_flags & IEEE80211_F_WPA) {
   1444 		uint32_t wsec = 0;
   1445 		uint32_t wpa = 0;
   1446 
   1447 		if (ic->ic_opt_ie_len)
   1448 			bwfm_fwvar_var_set_data(sc, "wpaie", ic->ic_opt_ie, ic->ic_opt_ie_len);
   1449 
   1450 		if (ic->ic_flags & IEEE80211_F_WPA1)
   1451 			wpa |= BWFM_WPA_AUTH_WPA_PSK;
   1452 		if (ic->ic_flags & IEEE80211_F_WPA2)
   1453 			wpa |= BWFM_WPA_AUTH_WPA2_PSK;
   1454 
   1455 		wsec |= bwfm_get_wsec(sc);
   1456 
   1457 		DPRINTF(("%s: WPA enabled, ic_flags = 0x%x, wpa 0x%x, wsec 0x%x\n",
   1458 		    DEVNAME(sc), ic->ic_flags, wpa, wsec));
   1459 
   1460 		bwfm_fwvar_var_set_int(sc, "wpa_auth", wpa);
   1461 		bwfm_fwvar_var_set_int(sc, "wsec", wsec);
   1462 	} else {
   1463 		bwfm_fwvar_var_set_int(sc, "wpa_auth", BWFM_WPA_AUTH_DISABLED);
   1464 		bwfm_fwvar_var_set_int(sc, "wsec", BWFM_WSEC_NONE);
   1465 	}
   1466 
   1467 	bwfm_fwvar_var_set_int(sc, "auth", BWFM_AUTH_OPEN);
   1468 	bwfm_fwvar_var_set_int(sc, "mfp", BWFM_MFP_NONE);
   1469 
   1470 	if (ni->ni_esslen && ni->ni_esslen < BWFM_MAX_SSID_LEN) {
   1471 		params = kmem_zalloc(sizeof(*params), KM_SLEEP);
   1472 		memcpy(params->ssid.ssid, ni->ni_essid, ni->ni_esslen);
   1473 		params->ssid.len = htole32(ni->ni_esslen);
   1474 		memcpy(params->assoc.bssid, ni->ni_bssid, sizeof(params->assoc.bssid));
   1475 		params->scan.scan_type = -1;
   1476 		params->scan.nprobes = htole32(-1);
   1477 		params->scan.active_time = htole32(-1);
   1478 		params->scan.passive_time = htole32(-1);
   1479 		params->scan.home_time = htole32(-1);
   1480 		if (bwfm_fwvar_var_set_data(sc, "join", params, sizeof(*params))) {
   1481 			struct bwfm_join_params join;
   1482 			memset(&join, 0, sizeof(join));
   1483 			memcpy(join.ssid.ssid, ni->ni_essid, ni->ni_esslen);
   1484 			join.ssid.len = htole32(ni->ni_esslen);
   1485 			memcpy(join.assoc.bssid, ni->ni_bssid, sizeof(join.assoc.bssid));
   1486 			bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_SSID, &join,
   1487 			    sizeof(join));
   1488 		}
   1489 		kmem_free(params, sizeof(*params));
   1490 	}
   1491 
   1492 	/* XXX: added for testing only, remove */
   1493 	bwfm_fwvar_var_set_int(sc, "allmulti", 1);
   1494 #if 0
   1495 	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PROMISC, 1);
   1496 #endif
   1497 }
   1498 
   1499 void
   1500 bwfm_rx(struct bwfm_softc *sc, char *buf, size_t len)
   1501 {
   1502 	struct ieee80211com *ic = &sc->sc_ic;
   1503 	struct ifnet *ifp = ic->ic_ifp;
   1504 	struct bwfm_event *e = (void *)buf;
   1505 	struct mbuf *m;
   1506 	char *mb;
   1507 	int s;
   1508 
   1509 	DPRINTF(("%s: buf %p len %lu\n", __func__, buf, len));
   1510 
   1511 	if (len >= sizeof(e->ehdr) &&
   1512 	    ntohs(e->ehdr.ether_type) == BWFM_ETHERTYPE_LINK_CTL &&
   1513 	    memcmp(BWFM_BRCM_OUI, e->hdr.oui, sizeof(e->hdr.oui)) == 0 &&
   1514 	    ntohs(e->hdr.usr_subtype) == BWFM_BRCM_SUBTYPE_EVENT)
   1515 		bwfm_rx_event(sc, buf, len);
   1516 
   1517 	if (__predict_false(len > MCLBYTES || len == 0))
   1518 		return;
   1519 	MGETHDR(m, M_DONTWAIT, MT_DATA);
   1520 	if (__predict_false(m == NULL))
   1521 		return;
   1522 	if (len > MHLEN) {
   1523 		MCLGET(m, M_DONTWAIT);
   1524 		if (!(m->m_flags & M_EXT)) {
   1525 			m_free(m);
   1526 			return;
   1527 		}
   1528 	}
   1529 
   1530 	s = splnet();
   1531 
   1532 	if ((ifp->if_flags & IFF_RUNNING) != 0) {
   1533 		mb = mtod(m, char *);
   1534 		memcpy(mb, buf, len);
   1535 		m->m_pkthdr.len = m->m_len = len;
   1536 		m_set_rcvif(m, ifp);
   1537 
   1538 		if_percpuq_enqueue(ifp->if_percpuq, m);
   1539 	}
   1540 
   1541 	splx(s);
   1542 }
   1543 
   1544 void
   1545 bwfm_rx_event(struct bwfm_softc *sc, char *buf, size_t len)
   1546 {
   1547 	struct ieee80211com *ic = &sc->sc_ic;
   1548 	struct bwfm_event *e = (void *)buf;
   1549 	int s;
   1550 
   1551 	DPRINTF(("%s: buf %p len %lu datalen %u code %u status %u"
   1552 	    " reason %u\n", __func__, buf, len, ntohl(e->msg.datalen),
   1553 	    ntohl(e->msg.event_type), ntohl(e->msg.status),
   1554 	    ntohl(e->msg.reason)));
   1555 
   1556 	if (ntohl(e->msg.event_type) >= BWFM_E_LAST)
   1557 		return;
   1558 
   1559 	switch (ntohl(e->msg.event_type)) {
   1560 	case BWFM_E_ESCAN_RESULT: {
   1561 		struct bwfm_escan_results *res = (void *)(buf + sizeof(*e));
   1562 		struct bwfm_bss_info *bss;
   1563 		int i;
   1564 		if (ntohl(e->msg.status) != BWFM_E_STATUS_PARTIAL) {
   1565 			/* Scan complete */
   1566 			s = splnet();
   1567 			if (ic->ic_opmode != IEEE80211_M_MONITOR)
   1568 				ieee80211_end_scan(ic);
   1569 			splx(s);
   1570 			break;
   1571 		}
   1572 		len -= sizeof(*e);
   1573 		if (len < sizeof(*res) || len < le32toh(res->buflen)) {
   1574 			printf("%s: results too small\n", DEVNAME(sc));
   1575 			return;
   1576 		}
   1577 		len -= sizeof(*res);
   1578 		if (len < le16toh(res->bss_count) * sizeof(struct bwfm_bss_info)) {
   1579 			printf("%s: results too small\n", DEVNAME(sc));
   1580 			return;
   1581 		}
   1582 		bss = &res->bss_info[0];
   1583 		for (i = 0; i < le16toh(res->bss_count); i++) {
   1584 			/* Fix alignment of bss_info */
   1585 			union {
   1586 				struct bwfm_bss_info bss_info;
   1587 				uint8_t padding[BWFM_BSS_INFO_BUFLEN];
   1588 			} bss_buf;
   1589 			if (len > sizeof(bss_buf)) {
   1590 				printf("%s: bss_info buffer too big\n", DEVNAME(sc));
   1591 			} else {
   1592 				memcpy(&bss_buf, &res->bss_info[i], len);
   1593 				bwfm_scan_node(sc, &bss_buf.bss_info, len);
   1594 			}
   1595 			len -= sizeof(*bss) + le32toh(bss->length);
   1596 			bss = (void *)(((uintptr_t)bss) + le32toh(bss->length));
   1597 			if (len <= 0)
   1598 				break;
   1599 		}
   1600 		break;
   1601 		}
   1602 
   1603 	case BWFM_E_SET_SSID:
   1604 		if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS) {
   1605 			ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
   1606 		} else {
   1607 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
   1608 		}
   1609 		break;
   1610 
   1611 	case BWFM_E_ASSOC:
   1612 		if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS) {
   1613 			ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
   1614 		} else {
   1615 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
   1616 		}
   1617 		break;
   1618 
   1619 	case BWFM_E_LINK:
   1620 		if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS &&
   1621 		    ntohl(e->msg.reason) == 0)
   1622 			break;
   1623 
   1624 		/* Link status has changed */
   1625 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
   1626 		break;
   1627 
   1628 	default:
   1629 		break;
   1630 	}
   1631 }
   1632 
   1633 void
   1634 bwfm_scan_node(struct bwfm_softc *sc, struct bwfm_bss_info *bss, size_t len)
   1635 {
   1636 	struct ieee80211com *ic = &sc->sc_ic;
   1637 	struct ieee80211_frame wh;
   1638 	struct ieee80211_scanparams scan;
   1639 	uint8_t rates[sizeof(bss->rates) + 2];
   1640 	uint8_t ssid[sizeof(bss->ssid) + 2];
   1641 	uint8_t *frm, *sfrm, *efrm;
   1642 	uint64_t tsf;
   1643 
   1644 	tsf = 0;
   1645 	sfrm = ((uint8_t *)bss) + le16toh(bss->ie_offset);
   1646 	efrm = sfrm + le32toh(bss->ie_length);
   1647 
   1648 	/* Fake a wireless header with the scan result's BSSID */
   1649 	memset(&wh, 0, sizeof(wh));
   1650 	IEEE80211_ADDR_COPY(wh.i_addr2, bss->bssid);
   1651 	IEEE80211_ADDR_COPY(wh.i_addr3, bss->bssid);
   1652 
   1653 	if (efrm - sfrm < 12) {
   1654 		ic->ic_stats.is_rx_elem_toosmall++;
   1655 		return;
   1656 	}
   1657 
   1658 	rates[0] = 0;
   1659 	rates[1] = le32toh(bss->nrates);
   1660 	memcpy(&rates[2], bss->rates, sizeof(bss->rates));
   1661 
   1662 	ssid[0] = 0;
   1663 	ssid[1] = bss->ssid_len;
   1664 	memcpy(&ssid[2], bss->ssid, sizeof(bss->ssid));
   1665 
   1666 	/* Build scan result */
   1667 	memset(&scan, 0, sizeof(scan));
   1668 	scan.tstamp  = (uint8_t *)&tsf;
   1669 	scan.bintval = le16toh(bss->beacon_period);
   1670 	scan.capinfo = le16toh(bss->capability);
   1671 	scan.bchan   = ieee80211_chan2ieee(ic, ic->ic_curchan);
   1672 	scan.chan    = scan.bchan;
   1673 	scan.rates   = rates;
   1674 	scan.ssid    = ssid;
   1675 
   1676 	for (frm = sfrm; frm < efrm; frm += frm[1] + 2) {
   1677 		switch (frm[0]) {
   1678 		case IEEE80211_ELEMID_COUNTRY:
   1679 			scan.country = frm;
   1680 			break;
   1681 		case IEEE80211_ELEMID_FHPARMS:
   1682 			if (ic->ic_phytype == IEEE80211_T_FH) {
   1683 				scan.fhdwell = le16dec(&frm[2]);
   1684 				scan.chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
   1685 				scan.fhindex = frm[6];
   1686 			}
   1687 			break;
   1688 		case IEEE80211_ELEMID_DSPARMS:
   1689 			if (ic->ic_phytype != IEEE80211_T_FH)
   1690 				scan.chan = frm[2];
   1691 			break;
   1692 		case IEEE80211_ELEMID_TIM:
   1693 			scan.tim = frm;
   1694 			scan.timoff = frm - sfrm;
   1695 			break;
   1696 		case IEEE80211_ELEMID_XRATES:
   1697 			scan.xrates = frm;
   1698 			break;
   1699 		case IEEE80211_ELEMID_ERP:
   1700 			if (frm[1] != 1) {
   1701 				ic->ic_stats.is_rx_elem_toobig++;
   1702 				break;
   1703 			}
   1704 			scan.erp = frm[2];
   1705 			break;
   1706 		case IEEE80211_ELEMID_RSN:
   1707 			scan.wpa = frm;
   1708 			break;
   1709 		case IEEE80211_ELEMID_VENDOR:
   1710 			if (bwfm_iswpaoui(frm))
   1711 				scan.wpa = frm;
   1712 			break;
   1713 		}
   1714 	}
   1715 
   1716 	if (ic->ic_flags & IEEE80211_F_SCAN)
   1717 		ieee80211_add_scan(ic, &scan, &wh, IEEE80211_FC0_SUBTYPE_BEACON,
   1718 		    le32toh(bss->rssi), 0);
   1719 }
   1720