xref: /netbsd-src/sys/dev/ic/bwfm.c (revision c38e7cc395b1472a774ff828e46123de44c628e9)
1 /* $NetBSD: bwfm.c,v 1.10 2018/01/16 18:42:43 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@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.sp_tstamp  = (uint8_t *)&tsf;
1676 	scan.sp_bintval = le16toh(bss->beacon_period);
1677 	scan.sp_capinfo = le16toh(bss->capability);
1678 	scan.sp_bchan   = ieee80211_chan2ieee(ic, ic->ic_curchan);
1679 	scan.sp_chan    = scan.sp_bchan;
1680 	scan.sp_rates   = rates;
1681 	scan.sp_ssid    = ssid;
1682 
1683 	for (frm = sfrm; frm < efrm; frm += frm[1] + 2) {
1684 		switch (frm[0]) {
1685 		case IEEE80211_ELEMID_COUNTRY:
1686 			scan.sp_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.sp_fhdwell = le16dec(&frm[2]);
1693 				scan.sp_chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
1694 				scan.sp_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.sp_chan = frm[2];
1702 			}
1703 			break;
1704 		case IEEE80211_ELEMID_TIM:
1705 			scan.sp_tim = frm;
1706 			scan.sp_timoff = frm - sfrm;
1707 			break;
1708 		case IEEE80211_ELEMID_XRATES:
1709 			scan.sp_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.sp_erp = frm[2];
1719 			break;
1720 		case IEEE80211_ELEMID_RSN:
1721 			scan.sp_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.sp_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