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