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