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