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