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