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