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