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