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