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