1*68edea43Sstsp /* $OpenBSD: bwfm.c,v 1.111 2024/02/19 21:23:02 stsp Exp $ */
232b2494eSpatrick /*
332b2494eSpatrick * Copyright (c) 2010-2016 Broadcom Corporation
432b2494eSpatrick * Copyright (c) 2016,2017 Patrick Wildt <patrick@blueri.se>
532b2494eSpatrick *
632b2494eSpatrick * Permission to use, copy, modify, and/or distribute this software for any
732b2494eSpatrick * purpose with or without fee is hereby granted, provided that the above
832b2494eSpatrick * copyright notice and this permission notice appear in all copies.
932b2494eSpatrick *
1032b2494eSpatrick * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1132b2494eSpatrick * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1232b2494eSpatrick * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1332b2494eSpatrick * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1432b2494eSpatrick * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1532b2494eSpatrick * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1632b2494eSpatrick * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1732b2494eSpatrick */
1832b2494eSpatrick
1932b2494eSpatrick #include "bpfilter.h"
2032b2494eSpatrick
2132b2494eSpatrick #include <sys/param.h>
2232b2494eSpatrick #include <sys/systm.h>
2332b2494eSpatrick #include <sys/buf.h>
2432b2494eSpatrick #include <sys/kernel.h>
2532b2494eSpatrick #include <sys/malloc.h>
2632b2494eSpatrick #include <sys/device.h>
2732b2494eSpatrick #include <sys/queue.h>
2832b2494eSpatrick #include <sys/socket.h>
2932b2494eSpatrick #include <sys/sockio.h>
3032b2494eSpatrick
3194e4747cSpatrick #if defined(__HAVE_FDT)
3294e4747cSpatrick #include <machine/fdt.h>
3394e4747cSpatrick #include <dev/ofw/openfirm.h>
3494e4747cSpatrick #endif
3594e4747cSpatrick
3632b2494eSpatrick #if NBPFILTER > 0
3732b2494eSpatrick #include <net/bpf.h>
3832b2494eSpatrick #endif
3932b2494eSpatrick #include <net/if.h>
4032b2494eSpatrick #include <net/if_dl.h>
4132b2494eSpatrick #include <net/if_media.h>
4232b2494eSpatrick
4332b2494eSpatrick #include <netinet/in.h>
4432b2494eSpatrick #include <netinet/if_ether.h>
4532b2494eSpatrick
4632b2494eSpatrick #include <net80211/ieee80211_var.h>
4732b2494eSpatrick
4832b2494eSpatrick #include <dev/ic/bwfmvar.h>
4932b2494eSpatrick #include <dev/ic/bwfmreg.h>
5032b2494eSpatrick
5132b2494eSpatrick /* #define BWFM_DEBUG */
5232b2494eSpatrick #ifdef BWFM_DEBUG
5332b2494eSpatrick #define DPRINTF(x) do { if (bwfm_debug > 0) printf x; } while (0)
5432b2494eSpatrick #define DPRINTFN(n, x) do { if (bwfm_debug >= (n)) printf x; } while (0)
5532b2494eSpatrick static int bwfm_debug = 1;
5632b2494eSpatrick #else
5732b2494eSpatrick #define DPRINTF(x) do { ; } while (0)
5832b2494eSpatrick #define DPRINTFN(n, x) do { ; } while (0)
5932b2494eSpatrick #endif
6032b2494eSpatrick
6132b2494eSpatrick #define DEVNAME(sc) ((sc)->sc_dev.dv_xname)
6232b2494eSpatrick
6332b2494eSpatrick void bwfm_start(struct ifnet *);
6432b2494eSpatrick void bwfm_init(struct ifnet *);
6532b2494eSpatrick void bwfm_stop(struct ifnet *);
663c08fed4Spatrick void bwfm_iff(struct bwfm_softc *);
6732b2494eSpatrick void bwfm_watchdog(struct ifnet *);
68decdd79fSstsp void bwfm_update_node(void *, struct ieee80211_node *);
69decdd79fSstsp void bwfm_update_nodes(struct bwfm_softc *);
7032b2494eSpatrick int bwfm_ioctl(struct ifnet *, u_long, caddr_t);
7132b2494eSpatrick int bwfm_media_change(struct ifnet *);
7232b2494eSpatrick
73c6a4ab0dSkettenis void bwfm_init_board_type(struct bwfm_softc *);
74da5a4af2Spatrick void bwfm_process_blob(struct bwfm_softc *, char *, u_char **, size_t *);
756c2f85e4Spatrick
7632b2494eSpatrick int bwfm_chip_attach(struct bwfm_softc *);
77821b2e31Spatrick void bwfm_chip_detach(struct bwfm_softc *);
783c53ddefSpatrick struct bwfm_core *bwfm_chip_get_core_idx(struct bwfm_softc *, int, int);
7932b2494eSpatrick struct bwfm_core *bwfm_chip_get_core(struct bwfm_softc *, int);
8032b2494eSpatrick struct bwfm_core *bwfm_chip_get_pmu(struct bwfm_softc *);
8132b2494eSpatrick int bwfm_chip_ai_isup(struct bwfm_softc *, struct bwfm_core *);
8232b2494eSpatrick void bwfm_chip_ai_disable(struct bwfm_softc *, struct bwfm_core *,
8332b2494eSpatrick uint32_t, uint32_t);
8432b2494eSpatrick void bwfm_chip_ai_reset(struct bwfm_softc *, struct bwfm_core *,
8532b2494eSpatrick uint32_t, uint32_t, uint32_t);
8632b2494eSpatrick void bwfm_chip_dmp_erom_scan(struct bwfm_softc *);
8732b2494eSpatrick int bwfm_chip_dmp_get_regaddr(struct bwfm_softc *, uint32_t *,
8832b2494eSpatrick uint32_t *, uint32_t *);
89e2612299Spatrick int bwfm_chip_cr4_set_active(struct bwfm_softc *, uint32_t);
9032b2494eSpatrick void bwfm_chip_cr4_set_passive(struct bwfm_softc *);
91e2612299Spatrick int bwfm_chip_ca7_set_active(struct bwfm_softc *, uint32_t);
9232b2494eSpatrick void bwfm_chip_ca7_set_passive(struct bwfm_softc *);
93e2612299Spatrick int bwfm_chip_cm3_set_active(struct bwfm_softc *);
9432b2494eSpatrick void bwfm_chip_cm3_set_passive(struct bwfm_softc *);
9585d32364Spatrick void bwfm_chip_socram_ramsize(struct bwfm_softc *, struct bwfm_core *);
9685d32364Spatrick void bwfm_chip_sysmem_ramsize(struct bwfm_softc *, struct bwfm_core *);
9785d32364Spatrick void bwfm_chip_tcm_ramsize(struct bwfm_softc *, struct bwfm_core *);
9885d32364Spatrick void bwfm_chip_tcm_rambase(struct bwfm_softc *);
9932b2494eSpatrick
10032b2494eSpatrick int bwfm_proto_bcdc_query_dcmd(struct bwfm_softc *, int,
10132b2494eSpatrick int, char *, size_t *);
10232b2494eSpatrick int bwfm_proto_bcdc_set_dcmd(struct bwfm_softc *, int,
10332b2494eSpatrick int, char *, size_t);
1046f241297Spatrick void bwfm_proto_bcdc_rx(struct bwfm_softc *, struct mbuf *,
1056f241297Spatrick struct mbuf_list *);
106029d6dd5Spatrick int bwfm_proto_bcdc_txctl(struct bwfm_softc *, int, char *, size_t *);
107029d6dd5Spatrick void bwfm_proto_bcdc_rxctl(struct bwfm_softc *, char *, size_t);
10832b2494eSpatrick
10932b2494eSpatrick int bwfm_fwvar_cmd_get_data(struct bwfm_softc *, int, void *, size_t);
11032b2494eSpatrick int bwfm_fwvar_cmd_set_data(struct bwfm_softc *, int, void *, size_t);
11132b2494eSpatrick int bwfm_fwvar_cmd_get_int(struct bwfm_softc *, int, uint32_t *);
11232b2494eSpatrick int bwfm_fwvar_cmd_set_int(struct bwfm_softc *, int, uint32_t);
11332b2494eSpatrick int bwfm_fwvar_var_get_data(struct bwfm_softc *, char *, void *, size_t);
11432b2494eSpatrick int bwfm_fwvar_var_set_data(struct bwfm_softc *, char *, void *, size_t);
11532b2494eSpatrick int bwfm_fwvar_var_get_int(struct bwfm_softc *, char *, uint32_t *);
11632b2494eSpatrick int bwfm_fwvar_var_set_int(struct bwfm_softc *, char *, uint32_t);
11732b2494eSpatrick
11873b11a26Spatrick uint32_t bwfm_chan2spec(struct bwfm_softc *, struct ieee80211_channel *);
11973b11a26Spatrick uint32_t bwfm_chan2spec_d11n(struct bwfm_softc *, struct ieee80211_channel *);
12073b11a26Spatrick uint32_t bwfm_chan2spec_d11ac(struct bwfm_softc *, struct ieee80211_channel *);
1218b2458cfSpatrick uint32_t bwfm_spec2chan(struct bwfm_softc *, uint32_t);
1228b2458cfSpatrick uint32_t bwfm_spec2chan_d11n(struct bwfm_softc *, uint32_t);
1238b2458cfSpatrick uint32_t bwfm_spec2chan_d11ac(struct bwfm_softc *, uint32_t);
1248b2458cfSpatrick
12563498aa8Spatrick void bwfm_connect(struct bwfm_softc *);
126c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
127c11618f6Spatrick void bwfm_hostap(struct bwfm_softc *);
128c11618f6Spatrick #endif
12932b2494eSpatrick void bwfm_scan(struct bwfm_softc *);
13025440f89Spatrick void bwfm_scan_abort(struct bwfm_softc *);
13163498aa8Spatrick
13263498aa8Spatrick void bwfm_task(void *);
13363498aa8Spatrick void bwfm_do_async(struct bwfm_softc *,
13463498aa8Spatrick void (*)(struct bwfm_softc *, void *), void *, int);
13563498aa8Spatrick
13663498aa8Spatrick int bwfm_set_key(struct ieee80211com *, struct ieee80211_node *,
13763498aa8Spatrick struct ieee80211_key *);
13863498aa8Spatrick void bwfm_delete_key(struct ieee80211com *, struct ieee80211_node *,
13963498aa8Spatrick struct ieee80211_key *);
14063498aa8Spatrick int bwfm_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
14163498aa8Spatrick int, int, int);
14263498aa8Spatrick int bwfm_newstate(struct ieee80211com *, enum ieee80211_state, int);
14363498aa8Spatrick
14463498aa8Spatrick void bwfm_set_key_cb(struct bwfm_softc *, void *);
14563498aa8Spatrick void bwfm_delete_key_cb(struct bwfm_softc *, void *);
146625a64d2Spatrick void bwfm_rx_event_cb(struct bwfm_softc *, struct mbuf *);
14732b2494eSpatrick
148c11618f6Spatrick struct mbuf *bwfm_newbuf(void);
149c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
150c11618f6Spatrick void bwfm_rx_auth_ind(struct bwfm_softc *, struct bwfm_event *, size_t);
151c11618f6Spatrick void bwfm_rx_assoc_ind(struct bwfm_softc *, struct bwfm_event *, size_t, int);
152c11618f6Spatrick void bwfm_rx_deauth_ind(struct bwfm_softc *, struct bwfm_event *, size_t);
153c11618f6Spatrick void bwfm_rx_disassoc_ind(struct bwfm_softc *, struct bwfm_event *, size_t);
154c11618f6Spatrick void bwfm_rx_leave_ind(struct bwfm_softc *, struct bwfm_event *, size_t, int);
155c11618f6Spatrick #endif
156f4e8af02Spatrick void bwfm_rx_event(struct bwfm_softc *, struct mbuf *);
15732b2494eSpatrick void bwfm_scan_node(struct bwfm_softc *, struct bwfm_bss_info *, size_t);
15832b2494eSpatrick
15932b2494eSpatrick extern void ieee80211_node2req(struct ieee80211com *,
16032b2494eSpatrick const struct ieee80211_node *, struct ieee80211_nodereq *);
16132b2494eSpatrick extern void ieee80211_req2node(struct ieee80211com *,
16232b2494eSpatrick const struct ieee80211_nodereq *, struct ieee80211_node *);
16332b2494eSpatrick
16432b2494eSpatrick uint8_t bwfm_2ghz_channels[] = {
16532b2494eSpatrick 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
16632b2494eSpatrick };
16732b2494eSpatrick uint8_t bwfm_5ghz_channels[] = {
16832b2494eSpatrick 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64, 100, 104, 108, 112,
16932b2494eSpatrick 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165,
17032b2494eSpatrick };
17132b2494eSpatrick
17232b2494eSpatrick struct bwfm_proto_ops bwfm_proto_bcdc_ops = {
17332b2494eSpatrick .proto_query_dcmd = bwfm_proto_bcdc_query_dcmd,
17432b2494eSpatrick .proto_set_dcmd = bwfm_proto_bcdc_set_dcmd,
1757dfc0b73Spatrick .proto_rx = bwfm_proto_bcdc_rx,
176029d6dd5Spatrick .proto_rxctl = bwfm_proto_bcdc_rxctl,
17732b2494eSpatrick };
17832b2494eSpatrick
17932b2494eSpatrick struct cfdriver bwfm_cd = {
18032b2494eSpatrick NULL, "bwfm", DV_IFNET
18132b2494eSpatrick };
18232b2494eSpatrick
18332b2494eSpatrick void
bwfm_attach(struct bwfm_softc * sc)18432b2494eSpatrick bwfm_attach(struct bwfm_softc *sc)
18532b2494eSpatrick {
18632b2494eSpatrick struct ieee80211com *ic = &sc->sc_ic;
18732b2494eSpatrick struct ifnet *ifp = &ic->ic_if;
18832b2494eSpatrick
189029d6dd5Spatrick TAILQ_INIT(&sc->sc_bcdc_rxctlq);
190029d6dd5Spatrick
19163498aa8Spatrick /* Init host async commands ring. */
19263498aa8Spatrick sc->sc_cmdq.cur = sc->sc_cmdq.next = sc->sc_cmdq.queued = 0;
19363498aa8Spatrick sc->sc_taskq = taskq_create(DEVNAME(sc), 1, IPL_SOFTNET, 0);
19463498aa8Spatrick task_set(&sc->sc_task, bwfm_task, sc);
195625a64d2Spatrick ml_init(&sc->sc_evml);
19632b2494eSpatrick
19763498aa8Spatrick ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
19863498aa8Spatrick ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
19963498aa8Spatrick ic->ic_state = IEEE80211_S_INIT;
20063498aa8Spatrick
20163498aa8Spatrick ic->ic_caps =
202c8e9388cSstsp IEEE80211_C_WEP |
203c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
204c11618f6Spatrick IEEE80211_C_HOSTAP | /* Access Point */
205c11618f6Spatrick #endif
20663498aa8Spatrick IEEE80211_C_RSN | /* WPA/RSN */
20763498aa8Spatrick IEEE80211_C_SCANALL | /* device scans all channels at once */
20863498aa8Spatrick IEEE80211_C_SCANALLBAND; /* device scans all bands at once */
20927fa8129Spatrick
210972218f3Spatrick /* IBSS channel undefined for now. */
211972218f3Spatrick ic->ic_ibss_chan = &ic->ic_channels[0];
212972218f3Spatrick
213972218f3Spatrick ifp->if_softc = sc;
214972218f3Spatrick ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
215972218f3Spatrick ifp->if_ioctl = bwfm_ioctl;
216972218f3Spatrick ifp->if_start = bwfm_start;
217972218f3Spatrick ifp->if_watchdog = bwfm_watchdog;
218972218f3Spatrick memcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ);
219972218f3Spatrick
220972218f3Spatrick if_attach(ifp);
221972218f3Spatrick ieee80211_ifattach(ifp);
222972218f3Spatrick
223972218f3Spatrick sc->sc_newstate = ic->ic_newstate;
224972218f3Spatrick ic->ic_newstate = bwfm_newstate;
225972218f3Spatrick ic->ic_send_mgmt = bwfm_send_mgmt;
226972218f3Spatrick ic->ic_set_key = bwfm_set_key;
227972218f3Spatrick ic->ic_delete_key = bwfm_delete_key;
228972218f3Spatrick
229972218f3Spatrick ieee80211_media_init(ifp, bwfm_media_change, ieee80211_media_status);
230972218f3Spatrick }
231972218f3Spatrick
232972218f3Spatrick void
bwfm_attachhook(struct device * self)233972218f3Spatrick bwfm_attachhook(struct device *self)
234972218f3Spatrick {
235972218f3Spatrick struct bwfm_softc *sc = (struct bwfm_softc *)self;
236972218f3Spatrick
237972218f3Spatrick if (sc->sc_bus_ops->bs_preinit != NULL &&
238972218f3Spatrick sc->sc_bus_ops->bs_preinit(sc))
239972218f3Spatrick return;
240972218f3Spatrick if (bwfm_preinit(sc))
241972218f3Spatrick return;
242972218f3Spatrick sc->sc_initialized = 1;
243972218f3Spatrick }
244972218f3Spatrick
245972218f3Spatrick int
bwfm_preinit(struct bwfm_softc * sc)246972218f3Spatrick bwfm_preinit(struct bwfm_softc *sc)
247972218f3Spatrick {
248972218f3Spatrick struct ieee80211com *ic = &sc->sc_ic;
249972218f3Spatrick struct ifnet *ifp = &ic->ic_if;
250972218f3Spatrick int i, j, nbands, nmode, vhtmode;
251972218f3Spatrick uint32_t bandlist[3], tmp;
252972218f3Spatrick
253972218f3Spatrick if (sc->sc_initialized)
254972218f3Spatrick return 0;
255972218f3Spatrick
256972218f3Spatrick if (bwfm_fwvar_cmd_get_int(sc, BWFM_C_GET_VERSION, &tmp)) {
257972218f3Spatrick printf("%s: could not read io type\n", DEVNAME(sc));
258972218f3Spatrick return 1;
259972218f3Spatrick } else
260972218f3Spatrick sc->sc_io_type = tmp;
261972218f3Spatrick if (bwfm_fwvar_var_get_data(sc, "cur_etheraddr", ic->ic_myaddr,
262972218f3Spatrick sizeof(ic->ic_myaddr))) {
263972218f3Spatrick printf("%s: could not read mac address\n", DEVNAME(sc));
264972218f3Spatrick return 1;
265972218f3Spatrick }
266972218f3Spatrick
2673e99d227Spatrick printf("%s: address %s\n", DEVNAME(sc), ether_sprintf(ic->ic_myaddr));
2683e99d227Spatrick
269da5a4af2Spatrick bwfm_process_blob(sc, "clmload", &sc->sc_clm, &sc->sc_clmsize);
270da5a4af2Spatrick bwfm_process_blob(sc, "txcapload", &sc->sc_txcap, &sc->sc_txcapsize);
271da5a4af2Spatrick bwfm_process_blob(sc, "calload", &sc->sc_cal, &sc->sc_calsize);
2726c2f85e4Spatrick
2739c7e455cSpatrick if (bwfm_fwvar_var_get_int(sc, "nmode", &nmode))
2749c7e455cSpatrick nmode = 0;
2759c7e455cSpatrick if (bwfm_fwvar_var_get_int(sc, "vhtmode", &vhtmode))
2769c7e455cSpatrick vhtmode = 0;
2779b183917Skettenis if (bwfm_fwvar_var_get_int(sc, "scan_ver", &sc->sc_scan_ver))
2789b183917Skettenis sc->sc_scan_ver = 0;
2799c7e455cSpatrick if (bwfm_fwvar_cmd_get_data(sc, BWFM_C_GET_BANDLIST, bandlist,
2809c7e455cSpatrick sizeof(bandlist))) {
2819c7e455cSpatrick printf("%s: couldn't get supported band list\n", DEVNAME(sc));
282972218f3Spatrick return 1;
2839c7e455cSpatrick }
2849c7e455cSpatrick nbands = letoh32(bandlist[0]);
2859c7e455cSpatrick for (i = 1; i <= nbands && i < nitems(bandlist); i++) {
2869c7e455cSpatrick switch (letoh32(bandlist[i])) {
2879c7e455cSpatrick case BWFM_BAND_2G:
2889c7e455cSpatrick DPRINTF(("%s: 2G HT %d VHT %d\n",
2899c7e455cSpatrick DEVNAME(sc), nmode, vhtmode));
2909c7e455cSpatrick ic->ic_sup_rates[IEEE80211_MODE_11B] =
2919c7e455cSpatrick ieee80211_std_rateset_11b;
2929c7e455cSpatrick ic->ic_sup_rates[IEEE80211_MODE_11G] =
2939c7e455cSpatrick ieee80211_std_rateset_11g;
29432b2494eSpatrick
29533e6a401Spatrick for (j = 0; j < nitems(bwfm_2ghz_channels); j++) {
29633e6a401Spatrick uint8_t chan = bwfm_2ghz_channels[j];
29732b2494eSpatrick ic->ic_channels[chan].ic_freq =
29832b2494eSpatrick ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ);
29932b2494eSpatrick ic->ic_channels[chan].ic_flags =
30032b2494eSpatrick IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
30132b2494eSpatrick IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
3029c7e455cSpatrick if (nmode)
3039c7e455cSpatrick ic->ic_channels[chan].ic_flags |=
3049c7e455cSpatrick IEEE80211_CHAN_HT;
30550d1a4aeSstsp /* VHT is 5GHz only */
30632b2494eSpatrick }
3079c7e455cSpatrick break;
3089c7e455cSpatrick case BWFM_BAND_5G:
3099c7e455cSpatrick DPRINTF(("%s: 5G HT %d VHT %d\n",
3109c7e455cSpatrick DEVNAME(sc), nmode, vhtmode));
3119c7e455cSpatrick ic->ic_sup_rates[IEEE80211_MODE_11A] =
3129c7e455cSpatrick ieee80211_std_rateset_11a;
3139c7e455cSpatrick
31433e6a401Spatrick for (j = 0; j < nitems(bwfm_5ghz_channels); j++) {
31533e6a401Spatrick uint8_t chan = bwfm_5ghz_channels[j];
31632b2494eSpatrick ic->ic_channels[chan].ic_freq =
31732b2494eSpatrick ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ);
31832b2494eSpatrick ic->ic_channels[chan].ic_flags =
31932b2494eSpatrick IEEE80211_CHAN_A;
3209c7e455cSpatrick if (nmode)
3219c7e455cSpatrick ic->ic_channels[chan].ic_flags |=
3229c7e455cSpatrick IEEE80211_CHAN_HT;
32350d1a4aeSstsp if (vhtmode)
32450d1a4aeSstsp ic->ic_channels[chan].ic_flags |=
32550d1a4aeSstsp IEEE80211_CHAN_VHT;
32632b2494eSpatrick }
3279c7e455cSpatrick break;
3289c7e455cSpatrick default:
3299c7e455cSpatrick printf("%s: unsupported band 0x%x\n", DEVNAME(sc),
3309c7e455cSpatrick letoh32(bandlist[i]));
3319c7e455cSpatrick break;
3329c7e455cSpatrick }
3339c7e455cSpatrick }
3349c7e455cSpatrick
335972218f3Spatrick /* Configure channel information obtained from firmware. */
336972218f3Spatrick ieee80211_channel_init(ifp);
33732b2494eSpatrick
338972218f3Spatrick /* Configure MAC address. */
339972218f3Spatrick if (if_setlladdr(ifp, ic->ic_myaddr))
340972218f3Spatrick printf("%s: could not set MAC address\n", DEVNAME(sc));
34163498aa8Spatrick
34263498aa8Spatrick ieee80211_media_init(ifp, bwfm_media_change, ieee80211_media_status);
343972218f3Spatrick return 0;
34432b2494eSpatrick }
34532b2494eSpatrick
3461a93a9bdSpatrick void
bwfm_cleanup(struct bwfm_softc * sc)3471a93a9bdSpatrick bwfm_cleanup(struct bwfm_softc *sc)
3481a93a9bdSpatrick {
3491a93a9bdSpatrick bwfm_chip_detach(sc);
3501a93a9bdSpatrick sc->sc_initialized = 0;
3511a93a9bdSpatrick }
3521a93a9bdSpatrick
35332b2494eSpatrick int
bwfm_detach(struct bwfm_softc * sc,int flags)35432b2494eSpatrick bwfm_detach(struct bwfm_softc *sc, int flags)
35532b2494eSpatrick {
35632b2494eSpatrick struct ieee80211com *ic = &sc->sc_ic;
35732b2494eSpatrick struct ifnet *ifp = &ic->ic_if;
358821b2e31Spatrick
35963498aa8Spatrick task_del(sc->sc_taskq, &sc->sc_task);
36063498aa8Spatrick ieee80211_ifdetach(ifp);
36116ddeba7Sstsp taskq_barrier(sc->sc_taskq);
36232b2494eSpatrick if_detach(ifp);
36316ddeba7Sstsp taskq_destroy(sc->sc_taskq);
364821b2e31Spatrick
3651a93a9bdSpatrick bwfm_cleanup(sc);
3661a93a9bdSpatrick return 0;
3671a93a9bdSpatrick }
3681a93a9bdSpatrick
3691a93a9bdSpatrick int
bwfm_activate(struct bwfm_softc * sc,int act)3701a93a9bdSpatrick bwfm_activate(struct bwfm_softc *sc, int act)
3711a93a9bdSpatrick {
3721a93a9bdSpatrick struct ieee80211com *ic = &sc->sc_ic;
3731a93a9bdSpatrick struct ifnet *ifp = &ic->ic_if;
3741a93a9bdSpatrick
3751a93a9bdSpatrick switch (act) {
3761a93a9bdSpatrick case DVACT_QUIESCE:
3771a93a9bdSpatrick if (ifp->if_flags & IFF_UP)
3781a93a9bdSpatrick bwfm_stop(ifp);
3791a93a9bdSpatrick break;
3801a93a9bdSpatrick case DVACT_WAKEUP:
3811a93a9bdSpatrick if (ifp->if_flags & IFF_UP)
3821a93a9bdSpatrick bwfm_init(ifp);
3831a93a9bdSpatrick break;
3841a93a9bdSpatrick default:
3851a93a9bdSpatrick break;
3861a93a9bdSpatrick }
3871a93a9bdSpatrick
38832b2494eSpatrick return 0;
38932b2494eSpatrick }
39032b2494eSpatrick
39132b2494eSpatrick void
bwfm_start(struct ifnet * ifp)39232b2494eSpatrick bwfm_start(struct ifnet *ifp)
39332b2494eSpatrick {
39432b2494eSpatrick struct bwfm_softc *sc = ifp->if_softc;
395f549aee3Skettenis struct ieee80211com *ic = &sc->sc_ic;
39632b2494eSpatrick struct mbuf *m;
39732b2494eSpatrick
39832b2494eSpatrick if (!(ifp->if_flags & IFF_RUNNING))
39932b2494eSpatrick return;
40032b2494eSpatrick if (ifq_is_oactive(&ifp->if_snd))
40132b2494eSpatrick return;
4020cae21bdSpatrick if (ifq_empty(&ifp->if_snd))
40332b2494eSpatrick return;
40432b2494eSpatrick
40532b2494eSpatrick /* TODO: return if no link? */
40632b2494eSpatrick
40702ee7d07Spatrick for (;;) {
40802ee7d07Spatrick if (sc->sc_bus_ops->bs_txcheck(sc)) {
40932b2494eSpatrick ifq_set_oactive(&ifp->if_snd);
41032b2494eSpatrick break;
41132b2494eSpatrick }
41202ee7d07Spatrick
413f549aee3Skettenis if (ic->ic_state != IEEE80211_S_RUN ||
414f549aee3Skettenis (ic->ic_xflags & IEEE80211_F_TX_MGMT_ONLY))
415f549aee3Skettenis break;
416f549aee3Skettenis
41702ee7d07Spatrick m = ifq_dequeue(&ifp->if_snd);
41802ee7d07Spatrick if (m == NULL)
41902ee7d07Spatrick break;
42002ee7d07Spatrick
42102ee7d07Spatrick if (sc->sc_bus_ops->bs_txdata(sc, m) != 0) {
42232b2494eSpatrick ifp->if_oerrors++;
42302ee7d07Spatrick m_freem(m);
42432b2494eSpatrick continue;
42532b2494eSpatrick }
42632b2494eSpatrick
42732b2494eSpatrick #if NBPFILTER > 0
42832b2494eSpatrick if (ifp->if_bpf)
42932b2494eSpatrick bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
43032b2494eSpatrick #endif
43132b2494eSpatrick }
43232b2494eSpatrick }
43332b2494eSpatrick
43432b2494eSpatrick void
bwfm_init(struct ifnet * ifp)43532b2494eSpatrick bwfm_init(struct ifnet *ifp)
43632b2494eSpatrick {
43732b2494eSpatrick struct bwfm_softc *sc = ifp->if_softc;
438c11618f6Spatrick struct ieee80211com *ic = &sc->sc_ic;
43932b2494eSpatrick uint8_t evmask[BWFM_EVENT_MASK_LEN];
4401179aa9cSpatrick struct bwfm_join_pref_params join_pref[2];
441c11618f6Spatrick int pm;
44232b2494eSpatrick
443972218f3Spatrick if (!sc->sc_initialized) {
444972218f3Spatrick if (sc->sc_bus_ops->bs_preinit != NULL &&
445972218f3Spatrick sc->sc_bus_ops->bs_preinit(sc)) {
446972218f3Spatrick printf("%s: could not init bus\n", DEVNAME(sc));
447972218f3Spatrick return;
448972218f3Spatrick }
449972218f3Spatrick if (bwfm_preinit(sc)) {
450972218f3Spatrick printf("%s: could not init\n", DEVNAME(sc));
451972218f3Spatrick return;
452972218f3Spatrick }
453972218f3Spatrick sc->sc_initialized = 1;
454*68edea43Sstsp } else {
455*68edea43Sstsp /* Update MAC in case the upper layers changed it. */
456*68edea43Sstsp IEEE80211_ADDR_COPY(ic->ic_myaddr,
457*68edea43Sstsp ((struct arpcom *)ifp)->ac_enaddr);
458*68edea43Sstsp if (bwfm_fwvar_var_set_data(sc, "cur_etheraddr",
459*68edea43Sstsp ic->ic_myaddr, sizeof(ic->ic_myaddr))) {
460*68edea43Sstsp printf("%s: could not write MAC address\n",
461*68edea43Sstsp DEVNAME(sc));
462*68edea43Sstsp return;
463*68edea43Sstsp }
464972218f3Spatrick }
465a2c6ff8bSpatrick
46673b11a26Spatrick /* Select default channel */
46773b11a26Spatrick ic->ic_bss->ni_chan = ic->ic_ibss_chan;
46873b11a26Spatrick
46932b2494eSpatrick if (bwfm_fwvar_var_set_int(sc, "mpc", 1)) {
47032b2494eSpatrick printf("%s: could not set mpc\n", DEVNAME(sc));
47132b2494eSpatrick return;
47232b2494eSpatrick }
47332b2494eSpatrick
4741179aa9cSpatrick /* Select target by RSSI (boost on 5GHz) */
4751179aa9cSpatrick join_pref[0].type = BWFM_JOIN_PREF_RSSI_DELTA;
4761179aa9cSpatrick join_pref[0].len = 2;
4771179aa9cSpatrick join_pref[0].rssi_gain = BWFM_JOIN_PREF_RSSI_BOOST;
4781179aa9cSpatrick join_pref[0].band = BWFM_JOIN_PREF_BAND_5G;
4791179aa9cSpatrick join_pref[1].type = BWFM_JOIN_PREF_RSSI;
4801179aa9cSpatrick join_pref[1].len = 2;
4811179aa9cSpatrick join_pref[1].rssi_gain = 0;
4821179aa9cSpatrick join_pref[1].band = 0;
4831179aa9cSpatrick if (bwfm_fwvar_var_set_data(sc, "join_pref", join_pref,
4841179aa9cSpatrick sizeof(join_pref))) {
4851179aa9cSpatrick printf("%s: could not set join pref\n", DEVNAME(sc));
4861179aa9cSpatrick return;
4871179aa9cSpatrick }
48832b2494eSpatrick
489c11618f6Spatrick #define BWFM_EVENT(event) evmask[(event) / 8] |= 1 << ((event) % 8)
490c11618f6Spatrick memset(evmask, 0, sizeof(evmask));
491c11618f6Spatrick switch (ic->ic_opmode) {
492c11618f6Spatrick case IEEE80211_M_STA:
493c11618f6Spatrick BWFM_EVENT(BWFM_E_IF);
494c11618f6Spatrick BWFM_EVENT(BWFM_E_LINK);
495c11618f6Spatrick BWFM_EVENT(BWFM_E_AUTH);
496c11618f6Spatrick BWFM_EVENT(BWFM_E_ASSOC);
497c11618f6Spatrick BWFM_EVENT(BWFM_E_DEAUTH);
498c11618f6Spatrick BWFM_EVENT(BWFM_E_DISASSOC);
499c11618f6Spatrick BWFM_EVENT(BWFM_E_ESCAN_RESULT);
500c11618f6Spatrick break;
501c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
502c11618f6Spatrick case IEEE80211_M_HOSTAP:
503c11618f6Spatrick BWFM_EVENT(BWFM_E_AUTH_IND);
504c11618f6Spatrick BWFM_EVENT(BWFM_E_ASSOC_IND);
505c11618f6Spatrick BWFM_EVENT(BWFM_E_REASSOC_IND);
506c11618f6Spatrick BWFM_EVENT(BWFM_E_DEAUTH_IND);
507c11618f6Spatrick BWFM_EVENT(BWFM_E_DISASSOC_IND);
508c11618f6Spatrick BWFM_EVENT(BWFM_E_ESCAN_RESULT);
509c11618f6Spatrick break;
510c11618f6Spatrick #endif
511c11618f6Spatrick default:
512c11618f6Spatrick break;
51332b2494eSpatrick }
514c11618f6Spatrick #undef BWFM_EVENT
515c11618f6Spatrick
51632b2494eSpatrick if (bwfm_fwvar_var_set_data(sc, "event_msgs", evmask, sizeof(evmask))) {
51732b2494eSpatrick printf("%s: could not set event mask\n", DEVNAME(sc));
51832b2494eSpatrick return;
51932b2494eSpatrick }
52032b2494eSpatrick
52132b2494eSpatrick if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_CHANNEL_TIME,
52232b2494eSpatrick BWFM_DEFAULT_SCAN_CHANNEL_TIME)) {
52332b2494eSpatrick printf("%s: could not set scan channel time\n", DEVNAME(sc));
52432b2494eSpatrick return;
52532b2494eSpatrick }
52632b2494eSpatrick if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_UNASSOC_TIME,
52732b2494eSpatrick BWFM_DEFAULT_SCAN_UNASSOC_TIME)) {
52832b2494eSpatrick printf("%s: could not set scan unassoc time\n", DEVNAME(sc));
52932b2494eSpatrick return;
53032b2494eSpatrick }
53132b2494eSpatrick if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_PASSIVE_TIME,
53232b2494eSpatrick BWFM_DEFAULT_SCAN_PASSIVE_TIME)) {
53332b2494eSpatrick printf("%s: could not set scan passive time\n", DEVNAME(sc));
53432b2494eSpatrick return;
53532b2494eSpatrick }
53632b2494eSpatrick
537c11618f6Spatrick /*
538c11618f6Spatrick * Use CAM (constantly awake) when we are running as AP,
539c11618f6Spatrick * otherwise use fast power saving.
540c11618f6Spatrick */
541c11618f6Spatrick pm = BWFM_PM_FAST_PS;
542c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
543c11618f6Spatrick if (ic->ic_opmode == IEEE80211_M_HOSTAP)
544c11618f6Spatrick pm = BWFM_PM_CAM;
545c11618f6Spatrick #endif
546c11618f6Spatrick if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, pm)) {
54732b2494eSpatrick printf("%s: could not set power\n", DEVNAME(sc));
54832b2494eSpatrick return;
54932b2494eSpatrick }
55032b2494eSpatrick
55132b2494eSpatrick bwfm_fwvar_var_set_int(sc, "txbf", 1);
55232b2494eSpatrick bwfm_fwvar_cmd_set_int(sc, BWFM_C_UP, 0);
55332b2494eSpatrick bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_INFRA, 1);
55432b2494eSpatrick bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_AP, 0);
55532b2494eSpatrick
55632b2494eSpatrick /* Disable all offloading (ARP, NDP, TCP/UDP cksum). */
55732b2494eSpatrick bwfm_fwvar_var_set_int(sc, "arp_ol", 0);
55832b2494eSpatrick bwfm_fwvar_var_set_int(sc, "arpoe", 0);
55932b2494eSpatrick bwfm_fwvar_var_set_int(sc, "ndoe", 0);
56032b2494eSpatrick bwfm_fwvar_var_set_int(sc, "toe", 0);
56132b2494eSpatrick
562d346a16fSpatrick /*
56363498aa8Spatrick * The firmware supplicant can handle the WPA handshake for
56463498aa8Spatrick * us, but we honestly want to do this ourselves, so disable
56563498aa8Spatrick * the firmware supplicant and let our stack handle it.
566d346a16fSpatrick */
56763498aa8Spatrick bwfm_fwvar_var_set_int(sc, "sup_wpa", 0);
56832b2494eSpatrick
5693c08fed4Spatrick bwfm_iff(sc);
57032b2494eSpatrick
57132b2494eSpatrick ifp->if_flags |= IFF_RUNNING;
57232b2494eSpatrick ifq_clr_oactive(&ifp->if_snd);
57363498aa8Spatrick
57463498aa8Spatrick ieee80211_begin_scan(ifp);
57532b2494eSpatrick }
57632b2494eSpatrick
57732b2494eSpatrick void
bwfm_stop(struct ifnet * ifp)57832b2494eSpatrick bwfm_stop(struct ifnet *ifp)
57932b2494eSpatrick {
58032b2494eSpatrick struct bwfm_softc *sc = ifp->if_softc;
58132b2494eSpatrick struct ieee80211com *ic = &sc->sc_ic;
58273b11a26Spatrick struct bwfm_join_params join;
58332b2494eSpatrick
58432b2494eSpatrick sc->sc_tx_timer = 0;
58532b2494eSpatrick ifp->if_timer = 0;
58632b2494eSpatrick ifp->if_flags &= ~IFF_RUNNING;
58732b2494eSpatrick ifq_clr_oactive(&ifp->if_snd);
58832b2494eSpatrick
58963498aa8Spatrick ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
59063498aa8Spatrick
59173b11a26Spatrick memset(&join, 0, sizeof(join));
59273b11a26Spatrick bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_SSID, &join, sizeof(join));
59332b2494eSpatrick bwfm_fwvar_cmd_set_int(sc, BWFM_C_DOWN, 1);
59473b11a26Spatrick bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_AP, 0);
59573b11a26Spatrick bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_INFRA, 0);
59673b11a26Spatrick bwfm_fwvar_cmd_set_int(sc, BWFM_C_UP, 1);
59773b11a26Spatrick bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, BWFM_PM_FAST_PS);
5981b8f8c22Stobhe bwfm_fwvar_var_set_int(sc, "mpc", 1);
599a2c6ff8bSpatrick
600a2c6ff8bSpatrick if (sc->sc_bus_ops->bs_stop)
601a2c6ff8bSpatrick sc->sc_bus_ops->bs_stop(sc);
60232b2494eSpatrick }
60332b2494eSpatrick
60432b2494eSpatrick void
bwfm_iff(struct bwfm_softc * sc)6053c08fed4Spatrick bwfm_iff(struct bwfm_softc *sc)
6063c08fed4Spatrick {
6073c08fed4Spatrick struct arpcom *ac = &sc->sc_ic.ic_ac;
6083c08fed4Spatrick struct ifnet *ifp = &ac->ac_if;
6093c08fed4Spatrick struct ether_multi *enm;
6103c08fed4Spatrick struct ether_multistep step;
6113c08fed4Spatrick size_t mcastlen;
6123c08fed4Spatrick char *mcast;
6133c08fed4Spatrick int i = 0;
6143c08fed4Spatrick
6153c08fed4Spatrick mcastlen = sizeof(uint32_t) + ac->ac_multicnt * ETHER_ADDR_LEN;
6163c08fed4Spatrick mcast = malloc(mcastlen, M_TEMP, M_WAITOK);
6171d328f71Spatrick htolem32((uint32_t *)mcast, ac->ac_multicnt);
6183c08fed4Spatrick
6193c08fed4Spatrick ifp->if_flags &= ~IFF_ALLMULTI;
6203c08fed4Spatrick if (ifp->if_flags & IFF_PROMISC || ac->ac_multirangecnt > 0) {
6213c08fed4Spatrick ifp->if_flags |= IFF_ALLMULTI;
6223c08fed4Spatrick } else {
6233c08fed4Spatrick ETHER_FIRST_MULTI(step, ac, enm);
6243c08fed4Spatrick while (enm != NULL) {
6253c08fed4Spatrick memcpy(mcast + sizeof(uint32_t) + i * ETHER_ADDR_LEN,
6263c08fed4Spatrick enm->enm_addrlo, ETHER_ADDR_LEN);
6273c08fed4Spatrick ETHER_NEXT_MULTI(step, enm);
6283c08fed4Spatrick i++;
6293c08fed4Spatrick }
6303c08fed4Spatrick }
6313c08fed4Spatrick
6323c08fed4Spatrick bwfm_fwvar_var_set_data(sc, "mcast_list", mcast, mcastlen);
6333c08fed4Spatrick bwfm_fwvar_var_set_int(sc, "allmulti",
6343c08fed4Spatrick !!(ifp->if_flags & IFF_ALLMULTI));
6353c08fed4Spatrick bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PROMISC,
6363c08fed4Spatrick !!(ifp->if_flags & IFF_PROMISC));
6373c08fed4Spatrick
6383c08fed4Spatrick free(mcast, M_TEMP, mcastlen);
6393c08fed4Spatrick }
6403c08fed4Spatrick
6413c08fed4Spatrick void
bwfm_watchdog(struct ifnet * ifp)64232b2494eSpatrick bwfm_watchdog(struct ifnet *ifp)
64332b2494eSpatrick {
64432b2494eSpatrick struct bwfm_softc *sc = ifp->if_softc;
64532b2494eSpatrick
64632b2494eSpatrick ifp->if_timer = 0;
64732b2494eSpatrick
64832b2494eSpatrick if (sc->sc_tx_timer > 0) {
64932b2494eSpatrick if (--sc->sc_tx_timer == 0) {
65032b2494eSpatrick printf("%s: device timeout\n", DEVNAME(sc));
65132b2494eSpatrick ifp->if_oerrors++;
65232b2494eSpatrick return;
65332b2494eSpatrick }
65432b2494eSpatrick ifp->if_timer = 1;
65532b2494eSpatrick }
65632b2494eSpatrick ieee80211_watchdog(ifp);
65732b2494eSpatrick }
65832b2494eSpatrick
659decdd79fSstsp /*
660decdd79fSstsp * Tx-rate to MCS conversion might lie since some rates map to multiple MCS.
661decdd79fSstsp * But this is the best we can do given that firmware only reports kbit/s.
662decdd79fSstsp */
663decdd79fSstsp
66450d1a4aeSstsp void
bwfm_rate2vhtmcs(int * mcs,int * ss,uint32_t txrate)66550d1a4aeSstsp bwfm_rate2vhtmcs(int *mcs, int *ss, uint32_t txrate)
666decdd79fSstsp {
66750d1a4aeSstsp const struct ieee80211_vht_rateset *rs;
66850d1a4aeSstsp int i, j;
66950d1a4aeSstsp
67050d1a4aeSstsp *mcs = -1;
67150d1a4aeSstsp *ss = -1;
67250d1a4aeSstsp /* TODO: Select specific ratesets based on BSS channel width. */
67350d1a4aeSstsp for (i = 0; i < IEEE80211_VHT_NUM_RATESETS; i++) {
67450d1a4aeSstsp rs = &ieee80211_std_ratesets_11ac[i];
67550d1a4aeSstsp for (j = 0; j < rs->nrates; j++) {
67650d1a4aeSstsp if (rs->rates[j] == txrate / 500) {
67750d1a4aeSstsp *mcs = j;
67850d1a4aeSstsp *ss = rs->num_ss;
67950d1a4aeSstsp return;
68050d1a4aeSstsp }
68150d1a4aeSstsp }
68250d1a4aeSstsp }
683decdd79fSstsp }
684decdd79fSstsp
685decdd79fSstsp int
bwfm_rate2htmcs(uint32_t txrate)686decdd79fSstsp bwfm_rate2htmcs(uint32_t txrate)
687decdd79fSstsp {
688decdd79fSstsp const struct ieee80211_ht_rateset *rs;
689decdd79fSstsp int i, j;
690decdd79fSstsp
69150d1a4aeSstsp /* TODO: Select specific ratesets based on BSS channel width. */
692decdd79fSstsp for (i = 0; i < IEEE80211_HT_NUM_RATESETS; i++) {
693decdd79fSstsp rs = &ieee80211_std_ratesets_11n[i];
694decdd79fSstsp for (j = 0; j < rs->nrates; j++) {
695decdd79fSstsp if (rs->rates[j] == txrate / 500)
696decdd79fSstsp return rs->min_mcs + j;
697decdd79fSstsp }
698decdd79fSstsp }
699decdd79fSstsp
700decdd79fSstsp return -1;
701decdd79fSstsp }
702decdd79fSstsp
703decdd79fSstsp void
bwfm_update_node(void * arg,struct ieee80211_node * ni)704decdd79fSstsp bwfm_update_node(void *arg, struct ieee80211_node *ni)
705decdd79fSstsp {
706decdd79fSstsp struct bwfm_softc *sc = arg;
707decdd79fSstsp struct ieee80211com *ic = &sc->sc_ic;
708decdd79fSstsp struct bwfm_sta_info sta;
709decdd79fSstsp uint32_t flags;
710decdd79fSstsp int8_t rssi;
711decdd79fSstsp uint32_t txrate;
71250d1a4aeSstsp int i;
713decdd79fSstsp
714decdd79fSstsp memset(&sta, 0, sizeof(sta));
715decdd79fSstsp memcpy((uint8_t *)&sta, ni->ni_macaddr, sizeof(ni->ni_macaddr));
716decdd79fSstsp
717decdd79fSstsp if (bwfm_fwvar_var_get_data(sc, "sta_info", &sta, sizeof(sta)))
718decdd79fSstsp return;
719decdd79fSstsp
720decdd79fSstsp if (!IEEE80211_ADDR_EQ(ni->ni_macaddr, sta.ea))
721decdd79fSstsp return;
722decdd79fSstsp
72304982c3bSstsp if (le16toh(sta.ver) < 3)
724decdd79fSstsp return;
725decdd79fSstsp
726decdd79fSstsp flags = le32toh(sta.flags);
727decdd79fSstsp if ((flags & BWFM_STA_SCBSTATS) == 0)
728decdd79fSstsp return;
729decdd79fSstsp
73004982c3bSstsp if (le16toh(sta.ver) >= 4) {
731decdd79fSstsp rssi = 0;
732decdd79fSstsp for (i = 0; i < BWFM_ANT_MAX; i++) {
733decdd79fSstsp if (sta.rssi[i] >= 0)
734decdd79fSstsp continue;
735decdd79fSstsp if (rssi == 0 || sta.rssi[i] > rssi)
736decdd79fSstsp rssi = sta.rssi[i];
737decdd79fSstsp }
738decdd79fSstsp if (rssi)
739decdd79fSstsp ni->ni_rssi = rssi;
74004982c3bSstsp }
741decdd79fSstsp
742decdd79fSstsp txrate = le32toh(sta.tx_rate); /* in kbit/s */
743decdd79fSstsp if (txrate == 0xffffffff) /* Seen this happening during association. */
744decdd79fSstsp return;
745decdd79fSstsp
74650d1a4aeSstsp if ((le32toh(sta.flags) & BWFM_STA_VHT_CAP)) {
74750d1a4aeSstsp int mcs, ss;
74850d1a4aeSstsp /* Tell net80211 that firmware has negotiated 11ac. */
74950d1a4aeSstsp ni->ni_flags |= IEEE80211_NODE_VHT;
75050d1a4aeSstsp ni->ni_flags |= IEEE80211_NODE_HT; /* VHT implies HT support */
75150d1a4aeSstsp if (ic->ic_curmode < IEEE80211_MODE_11AC)
75250d1a4aeSstsp ieee80211_setmode(ic, IEEE80211_MODE_11AC);
75350d1a4aeSstsp bwfm_rate2vhtmcs(&mcs, &ss, txrate);
75450d1a4aeSstsp if (mcs >= 0) {
75550d1a4aeSstsp ni->ni_txmcs = mcs;
75650d1a4aeSstsp ni->ni_vht_ss = ss;
75750d1a4aeSstsp } else {
75850d1a4aeSstsp ni->ni_txmcs = 0;
75950d1a4aeSstsp ni->ni_vht_ss = 1;
76050d1a4aeSstsp }
76150d1a4aeSstsp } else if ((le32toh(sta.flags) & BWFM_STA_N_CAP)) {
76250d1a4aeSstsp int mcs;
763decdd79fSstsp /* Tell net80211 that firmware has negotiated 11n. */
764decdd79fSstsp ni->ni_flags |= IEEE80211_NODE_HT;
765decdd79fSstsp if (ic->ic_curmode < IEEE80211_MODE_11N)
766decdd79fSstsp ieee80211_setmode(ic, IEEE80211_MODE_11N);
76750d1a4aeSstsp mcs = bwfm_rate2htmcs(txrate);
76850d1a4aeSstsp ni->ni_txmcs = (mcs >= 0 ? mcs : 0);
769decdd79fSstsp } else {
770decdd79fSstsp /* We're in 11a/g mode. Map to a legacy rate. */
771decdd79fSstsp for (i = 0; i < ni->ni_rates.rs_nrates; i++) {
772decdd79fSstsp uint8_t rate = ni->ni_rates.rs_rates[i];
773decdd79fSstsp rate &= IEEE80211_RATE_VAL;
774decdd79fSstsp if (rate == txrate / 500) {
775decdd79fSstsp ni->ni_txrate = i;
776decdd79fSstsp break;
777decdd79fSstsp }
778decdd79fSstsp }
779decdd79fSstsp }
780decdd79fSstsp }
781decdd79fSstsp
782decdd79fSstsp void
bwfm_update_nodes(struct bwfm_softc * sc)783decdd79fSstsp bwfm_update_nodes(struct bwfm_softc *sc)
784decdd79fSstsp {
785decdd79fSstsp struct ieee80211com *ic = &sc->sc_ic;
786decdd79fSstsp struct ieee80211_node *ni;
787decdd79fSstsp
788decdd79fSstsp switch (ic->ic_opmode) {
789decdd79fSstsp case IEEE80211_M_STA:
790decdd79fSstsp bwfm_update_node(sc, ic->ic_bss);
791decdd79fSstsp /* Update cached copy in the nodes tree as well. */
792decdd79fSstsp ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr);
793decdd79fSstsp if (ni) {
794decdd79fSstsp ni->ni_rssi = ic->ic_bss->ni_rssi;
795decdd79fSstsp }
796decdd79fSstsp break;
797decdd79fSstsp #ifndef IEEE80211_STA_ONLY
798decdd79fSstsp case IEEE80211_M_HOSTAP:
799decdd79fSstsp ieee80211_iterate_nodes(ic, bwfm_update_node, sc);
800decdd79fSstsp break;
801decdd79fSstsp #endif
802decdd79fSstsp default:
803decdd79fSstsp break;
804decdd79fSstsp }
805decdd79fSstsp }
806decdd79fSstsp
80732b2494eSpatrick int
bwfm_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)80832b2494eSpatrick bwfm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
80932b2494eSpatrick {
810decdd79fSstsp struct bwfm_softc *sc = ifp->if_softc;
811decdd79fSstsp struct ieee80211com *ic = &sc->sc_ic;
8123c08fed4Spatrick struct ifreq *ifr;
81363498aa8Spatrick int s, error = 0;
81432b2494eSpatrick
81532b2494eSpatrick s = splnet();
81632b2494eSpatrick switch (cmd) {
81732b2494eSpatrick case SIOCSIFADDR:
81832b2494eSpatrick ifp->if_flags |= IFF_UP;
81963498aa8Spatrick /* FALLTHROUGH */
82032b2494eSpatrick case SIOCSIFFLAGS:
82132b2494eSpatrick if (ifp->if_flags & IFF_UP) {
82232b2494eSpatrick if (!(ifp->if_flags & IFF_RUNNING))
82332b2494eSpatrick bwfm_init(ifp);
82432b2494eSpatrick } else {
82532b2494eSpatrick if (ifp->if_flags & IFF_RUNNING)
82632b2494eSpatrick bwfm_stop(ifp);
82732b2494eSpatrick }
82832b2494eSpatrick break;
8293c08fed4Spatrick case SIOCADDMULTI:
8303c08fed4Spatrick case SIOCDELMULTI:
8313c08fed4Spatrick ifr = (struct ifreq *)data;
8323c08fed4Spatrick error = (cmd == SIOCADDMULTI) ?
8333c08fed4Spatrick ether_addmulti(ifr, &ic->ic_ac) :
8343c08fed4Spatrick ether_delmulti(ifr, &ic->ic_ac);
8353c08fed4Spatrick if (error == ENETRESET) {
8363c08fed4Spatrick bwfm_iff(sc);
8373c08fed4Spatrick error = 0;
8383c08fed4Spatrick }
8393c08fed4Spatrick break;
840decdd79fSstsp case SIOCGIFMEDIA:
841decdd79fSstsp case SIOCG80211NODE:
842decdd79fSstsp case SIOCG80211ALLNODES:
843decdd79fSstsp if (ic->ic_state == IEEE80211_S_RUN)
844decdd79fSstsp bwfm_update_nodes(sc);
845decdd79fSstsp /* fall through */
84632b2494eSpatrick default:
84763498aa8Spatrick error = ieee80211_ioctl(ifp, cmd, data);
84832b2494eSpatrick }
84932b2494eSpatrick if (error == ENETRESET) {
85032b2494eSpatrick if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
85132b2494eSpatrick (IFF_UP | IFF_RUNNING)) {
85232b2494eSpatrick bwfm_stop(ifp);
85332b2494eSpatrick bwfm_init(ifp);
85432b2494eSpatrick }
85532b2494eSpatrick error = 0;
85632b2494eSpatrick }
85732b2494eSpatrick splx(s);
85832b2494eSpatrick return error;
85932b2494eSpatrick }
86032b2494eSpatrick
86132b2494eSpatrick int
bwfm_media_change(struct ifnet * ifp)86232b2494eSpatrick bwfm_media_change(struct ifnet *ifp)
86332b2494eSpatrick {
86463498aa8Spatrick int error;
86532b2494eSpatrick
86663498aa8Spatrick error = ieee80211_media_change(ifp);
86763498aa8Spatrick if (error != ENETRESET)
86863498aa8Spatrick return error;
86963498aa8Spatrick
87063498aa8Spatrick if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
87163498aa8Spatrick (IFF_UP | IFF_RUNNING)) {
87263498aa8Spatrick bwfm_stop(ifp);
87363498aa8Spatrick bwfm_init(ifp);
87463498aa8Spatrick }
8752e342c84Skevlo return error;
87632b2494eSpatrick }
87732b2494eSpatrick
87832b2494eSpatrick /* Chip initialization (SDIO, PCIe) */
87932b2494eSpatrick int
bwfm_chip_attach(struct bwfm_softc * sc)88032b2494eSpatrick bwfm_chip_attach(struct bwfm_softc *sc)
88132b2494eSpatrick {
88232b2494eSpatrick struct bwfm_core *core;
88332b2494eSpatrick int need_socram = 0;
88432b2494eSpatrick int has_socram = 0;
88532b2494eSpatrick int cpu_found = 0;
88632b2494eSpatrick uint32_t val;
88732b2494eSpatrick
88832b2494eSpatrick LIST_INIT(&sc->sc_chip.ch_list);
88932b2494eSpatrick
89032b2494eSpatrick if (sc->sc_buscore_ops->bc_prepare(sc) != 0) {
89132b2494eSpatrick printf("%s: failed buscore prepare\n", DEVNAME(sc));
89232b2494eSpatrick return 1;
89332b2494eSpatrick }
89432b2494eSpatrick
89532b2494eSpatrick val = sc->sc_buscore_ops->bc_read(sc,
89632b2494eSpatrick BWFM_CHIP_BASE + BWFM_CHIP_REG_CHIPID);
89732b2494eSpatrick sc->sc_chip.ch_chip = BWFM_CHIP_CHIPID_ID(val);
89832b2494eSpatrick sc->sc_chip.ch_chiprev = BWFM_CHIP_CHIPID_REV(val);
89932b2494eSpatrick
90032b2494eSpatrick if ((sc->sc_chip.ch_chip > 0xa000) || (sc->sc_chip.ch_chip < 0x4000))
90132b2494eSpatrick snprintf(sc->sc_chip.ch_name, sizeof(sc->sc_chip.ch_name),
90232b2494eSpatrick "%d", sc->sc_chip.ch_chip);
90332b2494eSpatrick else
90432b2494eSpatrick snprintf(sc->sc_chip.ch_name, sizeof(sc->sc_chip.ch_name),
90532b2494eSpatrick "%x", sc->sc_chip.ch_chip);
90632b2494eSpatrick
90732b2494eSpatrick switch (BWFM_CHIP_CHIPID_TYPE(val))
90832b2494eSpatrick {
90932b2494eSpatrick case BWFM_CHIP_CHIPID_TYPE_SOCI_SB:
91032b2494eSpatrick printf("%s: SoC interconnect SB not implemented\n",
91132b2494eSpatrick DEVNAME(sc));
91232b2494eSpatrick return 1;
91332b2494eSpatrick case BWFM_CHIP_CHIPID_TYPE_SOCI_AI:
91432b2494eSpatrick sc->sc_chip.ch_core_isup = bwfm_chip_ai_isup;
91532b2494eSpatrick sc->sc_chip.ch_core_disable = bwfm_chip_ai_disable;
91632b2494eSpatrick sc->sc_chip.ch_core_reset = bwfm_chip_ai_reset;
91732b2494eSpatrick bwfm_chip_dmp_erom_scan(sc);
91832b2494eSpatrick break;
91932b2494eSpatrick default:
92032b2494eSpatrick printf("%s: SoC interconnect %d unknown\n",
92132b2494eSpatrick DEVNAME(sc), BWFM_CHIP_CHIPID_TYPE(val));
92232b2494eSpatrick return 1;
92332b2494eSpatrick }
92432b2494eSpatrick
92532b2494eSpatrick LIST_FOREACH(core, &sc->sc_chip.ch_list, co_link) {
92632b2494eSpatrick DPRINTF(("%s: 0x%x:%-2d base 0x%08x wrap 0x%08x\n",
92732b2494eSpatrick DEVNAME(sc), core->co_id, core->co_rev,
92832b2494eSpatrick core->co_base, core->co_wrapbase));
92932b2494eSpatrick
93032b2494eSpatrick switch (core->co_id) {
93132b2494eSpatrick case BWFM_AGENT_CORE_ARM_CM3:
932de919281Spatrick need_socram = 1;
93332b2494eSpatrick /* FALLTHROUGH */
93432b2494eSpatrick case BWFM_AGENT_CORE_ARM_CR4:
93532b2494eSpatrick case BWFM_AGENT_CORE_ARM_CA7:
936de919281Spatrick cpu_found = 1;
93732b2494eSpatrick break;
93832b2494eSpatrick case BWFM_AGENT_INTERNAL_MEM:
939de919281Spatrick has_socram = 1;
94032b2494eSpatrick break;
94132b2494eSpatrick default:
94232b2494eSpatrick break;
94332b2494eSpatrick }
94432b2494eSpatrick }
94532b2494eSpatrick
94632b2494eSpatrick if (!cpu_found) {
94732b2494eSpatrick printf("%s: CPU core not detected\n", DEVNAME(sc));
94832b2494eSpatrick return 1;
94932b2494eSpatrick }
95032b2494eSpatrick if (need_socram && !has_socram) {
95132b2494eSpatrick printf("%s: RAM core not provided\n", DEVNAME(sc));
95232b2494eSpatrick return 1;
95332b2494eSpatrick }
95432b2494eSpatrick
955e2612299Spatrick bwfm_chip_set_passive(sc);
95632b2494eSpatrick
95732b2494eSpatrick if (sc->sc_buscore_ops->bc_reset) {
95832b2494eSpatrick sc->sc_buscore_ops->bc_reset(sc);
959e2612299Spatrick bwfm_chip_set_passive(sc);
96032b2494eSpatrick }
96132b2494eSpatrick
96285d32364Spatrick if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4)) != NULL) {
96385d32364Spatrick bwfm_chip_tcm_ramsize(sc, core);
96485d32364Spatrick bwfm_chip_tcm_rambase(sc);
96585d32364Spatrick } else if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_SYS_MEM)) != NULL) {
96685d32364Spatrick bwfm_chip_sysmem_ramsize(sc, core);
96785d32364Spatrick bwfm_chip_tcm_rambase(sc);
96885d32364Spatrick } else if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM)) != NULL) {
96985d32364Spatrick bwfm_chip_socram_ramsize(sc, core);
97085d32364Spatrick }
97132b2494eSpatrick
97232b2494eSpatrick core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
97332b2494eSpatrick sc->sc_chip.ch_cc_caps = sc->sc_buscore_ops->bc_read(sc,
97432b2494eSpatrick core->co_base + BWFM_CHIP_REG_CAPABILITIES);
97532b2494eSpatrick sc->sc_chip.ch_cc_caps_ext = sc->sc_buscore_ops->bc_read(sc,
97632b2494eSpatrick core->co_base + BWFM_CHIP_REG_CAPABILITIES_EXT);
97732b2494eSpatrick
97832b2494eSpatrick core = bwfm_chip_get_pmu(sc);
97932b2494eSpatrick if (sc->sc_chip.ch_cc_caps & BWFM_CHIP_REG_CAPABILITIES_PMU) {
98032b2494eSpatrick sc->sc_chip.ch_pmucaps = sc->sc_buscore_ops->bc_read(sc,
98132b2494eSpatrick core->co_base + BWFM_CHIP_REG_PMUCAPABILITIES);
98232b2494eSpatrick sc->sc_chip.ch_pmurev = sc->sc_chip.ch_pmucaps &
98332b2494eSpatrick BWFM_CHIP_REG_PMUCAPABILITIES_REV_MASK;
98432b2494eSpatrick }
98532b2494eSpatrick
98632b2494eSpatrick if (sc->sc_buscore_ops->bc_setup)
98732b2494eSpatrick sc->sc_buscore_ops->bc_setup(sc);
98832b2494eSpatrick
989c6a4ab0dSkettenis bwfm_init_board_type(sc);
990c6a4ab0dSkettenis
99132b2494eSpatrick return 0;
99232b2494eSpatrick }
99332b2494eSpatrick
994821b2e31Spatrick void
bwfm_chip_detach(struct bwfm_softc * sc)995821b2e31Spatrick bwfm_chip_detach(struct bwfm_softc *sc)
996821b2e31Spatrick {
997821b2e31Spatrick struct bwfm_core *core, *tmp;
998821b2e31Spatrick
999821b2e31Spatrick LIST_FOREACH_SAFE(core, &sc->sc_chip.ch_list, co_link, tmp) {
1000821b2e31Spatrick LIST_REMOVE(core, co_link);
1001821b2e31Spatrick free(core, M_DEVBUF, sizeof(*core));
1002821b2e31Spatrick }
1003821b2e31Spatrick }
1004821b2e31Spatrick
100532b2494eSpatrick struct bwfm_core *
bwfm_chip_get_core_idx(struct bwfm_softc * sc,int id,int idx)10063c53ddefSpatrick bwfm_chip_get_core_idx(struct bwfm_softc *sc, int id, int idx)
100732b2494eSpatrick {
100832b2494eSpatrick struct bwfm_core *core;
100932b2494eSpatrick
101032b2494eSpatrick LIST_FOREACH(core, &sc->sc_chip.ch_list, co_link) {
10113c53ddefSpatrick if (core->co_id == id && idx-- == 0)
101232b2494eSpatrick return core;
101332b2494eSpatrick }
101432b2494eSpatrick
101532b2494eSpatrick return NULL;
101632b2494eSpatrick }
101732b2494eSpatrick
101832b2494eSpatrick struct bwfm_core *
bwfm_chip_get_core(struct bwfm_softc * sc,int id)10193c53ddefSpatrick bwfm_chip_get_core(struct bwfm_softc *sc, int id)
10203c53ddefSpatrick {
10213c53ddefSpatrick return bwfm_chip_get_core_idx(sc, id, 0);
10223c53ddefSpatrick }
10233c53ddefSpatrick
10243c53ddefSpatrick struct bwfm_core *
bwfm_chip_get_pmu(struct bwfm_softc * sc)102532b2494eSpatrick bwfm_chip_get_pmu(struct bwfm_softc *sc)
102632b2494eSpatrick {
102732b2494eSpatrick struct bwfm_core *cc, *pmu;
102832b2494eSpatrick
102932b2494eSpatrick cc = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
103032b2494eSpatrick if (cc->co_rev >= 35 && sc->sc_chip.ch_cc_caps_ext &
103132b2494eSpatrick BWFM_CHIP_REG_CAPABILITIES_EXT_AOB_PRESENT) {
103232b2494eSpatrick pmu = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_PMU);
103332b2494eSpatrick if (pmu)
103432b2494eSpatrick return pmu;
103532b2494eSpatrick }
103632b2494eSpatrick
103732b2494eSpatrick return cc;
103832b2494eSpatrick }
103932b2494eSpatrick
104032b2494eSpatrick /* Functions for the AI interconnect */
104132b2494eSpatrick int
bwfm_chip_ai_isup(struct bwfm_softc * sc,struct bwfm_core * core)104232b2494eSpatrick bwfm_chip_ai_isup(struct bwfm_softc *sc, struct bwfm_core *core)
104332b2494eSpatrick {
104432b2494eSpatrick uint32_t ioctl, reset;
104532b2494eSpatrick
104632b2494eSpatrick ioctl = sc->sc_buscore_ops->bc_read(sc,
104732b2494eSpatrick core->co_wrapbase + BWFM_AGENT_IOCTL);
104832b2494eSpatrick reset = sc->sc_buscore_ops->bc_read(sc,
104932b2494eSpatrick core->co_wrapbase + BWFM_AGENT_RESET_CTL);
105032b2494eSpatrick
105132b2494eSpatrick if (((ioctl & (BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK)) ==
105232b2494eSpatrick BWFM_AGENT_IOCTL_CLK) &&
105332b2494eSpatrick ((reset & BWFM_AGENT_RESET_CTL_RESET) == 0))
105432b2494eSpatrick return 1;
105532b2494eSpatrick
105632b2494eSpatrick return 0;
105732b2494eSpatrick }
105832b2494eSpatrick
105932b2494eSpatrick void
bwfm_chip_ai_disable(struct bwfm_softc * sc,struct bwfm_core * core,uint32_t prereset,uint32_t reset)106032b2494eSpatrick bwfm_chip_ai_disable(struct bwfm_softc *sc, struct bwfm_core *core,
106132b2494eSpatrick uint32_t prereset, uint32_t reset)
106232b2494eSpatrick {
106332b2494eSpatrick uint32_t val;
106432b2494eSpatrick int i;
106532b2494eSpatrick
106632b2494eSpatrick val = sc->sc_buscore_ops->bc_read(sc,
106732b2494eSpatrick core->co_wrapbase + BWFM_AGENT_RESET_CTL);
106832b2494eSpatrick if ((val & BWFM_AGENT_RESET_CTL_RESET) == 0) {
106932b2494eSpatrick
107032b2494eSpatrick sc->sc_buscore_ops->bc_write(sc,
107132b2494eSpatrick core->co_wrapbase + BWFM_AGENT_IOCTL,
107232b2494eSpatrick prereset | BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK);
107332b2494eSpatrick sc->sc_buscore_ops->bc_read(sc,
107432b2494eSpatrick core->co_wrapbase + BWFM_AGENT_IOCTL);
107532b2494eSpatrick
107632b2494eSpatrick sc->sc_buscore_ops->bc_write(sc,
107732b2494eSpatrick core->co_wrapbase + BWFM_AGENT_RESET_CTL,
107832b2494eSpatrick BWFM_AGENT_RESET_CTL_RESET);
107932b2494eSpatrick delay(20);
108032b2494eSpatrick
108132b2494eSpatrick for (i = 300; i > 0; i--) {
108232b2494eSpatrick if (sc->sc_buscore_ops->bc_read(sc,
108332b2494eSpatrick core->co_wrapbase + BWFM_AGENT_RESET_CTL) ==
108432b2494eSpatrick BWFM_AGENT_RESET_CTL_RESET)
108532b2494eSpatrick break;
108632b2494eSpatrick }
108732b2494eSpatrick if (i == 0)
108832b2494eSpatrick printf("%s: timeout on core reset\n", DEVNAME(sc));
108932b2494eSpatrick }
109032b2494eSpatrick
109132b2494eSpatrick sc->sc_buscore_ops->bc_write(sc,
109232b2494eSpatrick core->co_wrapbase + BWFM_AGENT_IOCTL,
109332b2494eSpatrick reset | BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK);
109432b2494eSpatrick sc->sc_buscore_ops->bc_read(sc,
109532b2494eSpatrick core->co_wrapbase + BWFM_AGENT_IOCTL);
109632b2494eSpatrick }
109732b2494eSpatrick
109832b2494eSpatrick void
bwfm_chip_ai_reset(struct bwfm_softc * sc,struct bwfm_core * core,uint32_t prereset,uint32_t reset,uint32_t postreset)109932b2494eSpatrick bwfm_chip_ai_reset(struct bwfm_softc *sc, struct bwfm_core *core,
110032b2494eSpatrick uint32_t prereset, uint32_t reset, uint32_t postreset)
110132b2494eSpatrick {
110232b2494eSpatrick int i;
110332b2494eSpatrick
110432b2494eSpatrick bwfm_chip_ai_disable(sc, core, prereset, reset);
110532b2494eSpatrick
110632b2494eSpatrick for (i = 50; i > 0; i--) {
110732b2494eSpatrick if ((sc->sc_buscore_ops->bc_read(sc,
110832b2494eSpatrick core->co_wrapbase + BWFM_AGENT_RESET_CTL) &
110932b2494eSpatrick BWFM_AGENT_RESET_CTL_RESET) == 0)
111032b2494eSpatrick break;
111132b2494eSpatrick sc->sc_buscore_ops->bc_write(sc,
111232b2494eSpatrick core->co_wrapbase + BWFM_AGENT_RESET_CTL, 0);
111332b2494eSpatrick delay(60);
111432b2494eSpatrick }
111532b2494eSpatrick if (i == 0)
111632b2494eSpatrick printf("%s: timeout on core reset\n", DEVNAME(sc));
111732b2494eSpatrick
111832b2494eSpatrick sc->sc_buscore_ops->bc_write(sc,
111932b2494eSpatrick core->co_wrapbase + BWFM_AGENT_IOCTL,
112032b2494eSpatrick postreset | BWFM_AGENT_IOCTL_CLK);
112132b2494eSpatrick sc->sc_buscore_ops->bc_read(sc,
112232b2494eSpatrick core->co_wrapbase + BWFM_AGENT_IOCTL);
112332b2494eSpatrick }
112432b2494eSpatrick
112532b2494eSpatrick void
bwfm_chip_dmp_erom_scan(struct bwfm_softc * sc)112632b2494eSpatrick bwfm_chip_dmp_erom_scan(struct bwfm_softc *sc)
112732b2494eSpatrick {
112832b2494eSpatrick uint32_t erom, val, base, wrap;
112932b2494eSpatrick uint8_t type = 0;
113032b2494eSpatrick uint16_t id;
113132b2494eSpatrick uint8_t nmw, nsw, rev;
113232b2494eSpatrick struct bwfm_core *core;
113332b2494eSpatrick
113432b2494eSpatrick erom = sc->sc_buscore_ops->bc_read(sc,
113532b2494eSpatrick BWFM_CHIP_BASE + BWFM_CHIP_REG_EROMPTR);
113632b2494eSpatrick while (type != BWFM_DMP_DESC_EOT) {
113732b2494eSpatrick val = sc->sc_buscore_ops->bc_read(sc, erom);
113832b2494eSpatrick type = val & BWFM_DMP_DESC_MASK;
113932b2494eSpatrick erom += 4;
114032b2494eSpatrick
114132b2494eSpatrick if (type != BWFM_DMP_DESC_COMPONENT)
114232b2494eSpatrick continue;
114332b2494eSpatrick
114432b2494eSpatrick id = (val & BWFM_DMP_COMP_PARTNUM)
114532b2494eSpatrick >> BWFM_DMP_COMP_PARTNUM_S;
114632b2494eSpatrick
114732b2494eSpatrick val = sc->sc_buscore_ops->bc_read(sc, erom);
114832b2494eSpatrick type = val & BWFM_DMP_DESC_MASK;
114932b2494eSpatrick erom += 4;
115032b2494eSpatrick
115132b2494eSpatrick if (type != BWFM_DMP_DESC_COMPONENT) {
115232b2494eSpatrick printf("%s: not component descriptor\n", DEVNAME(sc));
115332b2494eSpatrick return;
115432b2494eSpatrick }
115532b2494eSpatrick
115632b2494eSpatrick nmw = (val & BWFM_DMP_COMP_NUM_MWRAP)
115732b2494eSpatrick >> BWFM_DMP_COMP_NUM_MWRAP_S;
115832b2494eSpatrick nsw = (val & BWFM_DMP_COMP_NUM_SWRAP)
115932b2494eSpatrick >> BWFM_DMP_COMP_NUM_SWRAP_S;
116032b2494eSpatrick rev = (val & BWFM_DMP_COMP_REVISION)
116132b2494eSpatrick >> BWFM_DMP_COMP_REVISION_S;
116232b2494eSpatrick
11639d277f82Spatrick if (nmw + nsw == 0 && id != BWFM_AGENT_CORE_PMU &&
11649d277f82Spatrick id != BWFM_AGENT_CORE_GCI)
116532b2494eSpatrick continue;
116632b2494eSpatrick
116732b2494eSpatrick if (bwfm_chip_dmp_get_regaddr(sc, &erom, &base, &wrap))
116832b2494eSpatrick continue;
116932b2494eSpatrick
117032b2494eSpatrick core = malloc(sizeof(*core), M_DEVBUF, M_WAITOK);
117132b2494eSpatrick core->co_id = id;
117232b2494eSpatrick core->co_base = base;
117332b2494eSpatrick core->co_wrapbase = wrap;
117432b2494eSpatrick core->co_rev = rev;
117532b2494eSpatrick LIST_INSERT_HEAD(&sc->sc_chip.ch_list, core, co_link);
117632b2494eSpatrick }
117732b2494eSpatrick }
117832b2494eSpatrick
117932b2494eSpatrick int
bwfm_chip_dmp_get_regaddr(struct bwfm_softc * sc,uint32_t * erom,uint32_t * base,uint32_t * wrap)118032b2494eSpatrick bwfm_chip_dmp_get_regaddr(struct bwfm_softc *sc, uint32_t *erom,
118132b2494eSpatrick uint32_t *base, uint32_t *wrap)
118232b2494eSpatrick {
118332b2494eSpatrick uint8_t type = 0, mpnum = 0;
118432b2494eSpatrick uint8_t stype, sztype, wraptype;
118532b2494eSpatrick uint32_t val;
118632b2494eSpatrick
118732b2494eSpatrick *base = 0;
118832b2494eSpatrick *wrap = 0;
118932b2494eSpatrick
119032b2494eSpatrick val = sc->sc_buscore_ops->bc_read(sc, *erom);
119132b2494eSpatrick type = val & BWFM_DMP_DESC_MASK;
119232b2494eSpatrick if (type == BWFM_DMP_DESC_MASTER_PORT) {
119332b2494eSpatrick mpnum = (val & BWFM_DMP_MASTER_PORT_NUM)
119432b2494eSpatrick >> BWFM_DMP_MASTER_PORT_NUM_S;
119532b2494eSpatrick wraptype = BWFM_DMP_SLAVE_TYPE_MWRAP;
119632b2494eSpatrick *erom += 4;
119732b2494eSpatrick } else if ((type & ~BWFM_DMP_DESC_ADDRSIZE_GT32) ==
119832b2494eSpatrick BWFM_DMP_DESC_ADDRESS)
119932b2494eSpatrick wraptype = BWFM_DMP_SLAVE_TYPE_SWRAP;
120032b2494eSpatrick else
120132b2494eSpatrick return 1;
120232b2494eSpatrick
120332b2494eSpatrick do {
120432b2494eSpatrick do {
120532b2494eSpatrick val = sc->sc_buscore_ops->bc_read(sc, *erom);
120632b2494eSpatrick type = val & BWFM_DMP_DESC_MASK;
120732b2494eSpatrick if (type == BWFM_DMP_DESC_COMPONENT)
120832b2494eSpatrick return 0;
120932b2494eSpatrick if (type == BWFM_DMP_DESC_EOT)
121032b2494eSpatrick return 1;
121132b2494eSpatrick *erom += 4;
121232b2494eSpatrick } while ((type & ~BWFM_DMP_DESC_ADDRSIZE_GT32) !=
121332b2494eSpatrick BWFM_DMP_DESC_ADDRESS);
121432b2494eSpatrick
121532b2494eSpatrick if (type & BWFM_DMP_DESC_ADDRSIZE_GT32)
121632b2494eSpatrick *erom += 4;
121732b2494eSpatrick
121832b2494eSpatrick sztype = (val & BWFM_DMP_SLAVE_SIZE_TYPE)
121932b2494eSpatrick >> BWFM_DMP_SLAVE_SIZE_TYPE_S;
122032b2494eSpatrick if (sztype == BWFM_DMP_SLAVE_SIZE_DESC) {
122132b2494eSpatrick val = sc->sc_buscore_ops->bc_read(sc, *erom);
122232b2494eSpatrick type = val & BWFM_DMP_DESC_MASK;
122332b2494eSpatrick if (type & BWFM_DMP_DESC_ADDRSIZE_GT32)
122432b2494eSpatrick *erom += 8;
122532b2494eSpatrick else
122632b2494eSpatrick *erom += 4;
122732b2494eSpatrick }
12289d277f82Spatrick if (sztype != BWFM_DMP_SLAVE_SIZE_4K &&
12299d277f82Spatrick sztype != BWFM_DMP_SLAVE_SIZE_8K)
123032b2494eSpatrick continue;
123132b2494eSpatrick
123232b2494eSpatrick stype = (val & BWFM_DMP_SLAVE_TYPE) >> BWFM_DMP_SLAVE_TYPE_S;
123332b2494eSpatrick if (*base == 0 && stype == BWFM_DMP_SLAVE_TYPE_SLAVE)
123432b2494eSpatrick *base = val & BWFM_DMP_SLAVE_ADDR_BASE;
123532b2494eSpatrick if (*wrap == 0 && stype == wraptype)
123632b2494eSpatrick *wrap = val & BWFM_DMP_SLAVE_ADDR_BASE;
123732b2494eSpatrick } while (*base == 0 || *wrap == 0);
123832b2494eSpatrick
123932b2494eSpatrick return 0;
124032b2494eSpatrick }
124132b2494eSpatrick
124232b2494eSpatrick /* Core configuration */
1243e2612299Spatrick int
bwfm_chip_set_active(struct bwfm_softc * sc,uint32_t rstvec)1244e2612299Spatrick bwfm_chip_set_active(struct bwfm_softc *sc, uint32_t rstvec)
1245e2612299Spatrick {
1246e2612299Spatrick if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL)
1247e2612299Spatrick return bwfm_chip_cr4_set_active(sc, rstvec);
1248e2612299Spatrick if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL)
1249e2612299Spatrick return bwfm_chip_ca7_set_active(sc, rstvec);
1250e2612299Spatrick if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL)
1251e2612299Spatrick return bwfm_chip_cm3_set_active(sc);
1252e2612299Spatrick return 1;
1253e2612299Spatrick }
1254e2612299Spatrick
1255e2612299Spatrick void
bwfm_chip_set_passive(struct bwfm_softc * sc)1256e2612299Spatrick bwfm_chip_set_passive(struct bwfm_softc *sc)
1257e2612299Spatrick {
1258e2612299Spatrick if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL) {
1259e2612299Spatrick bwfm_chip_cr4_set_passive(sc);
1260e2612299Spatrick return;
1261e2612299Spatrick }
1262e2612299Spatrick if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL) {
1263e2612299Spatrick bwfm_chip_ca7_set_passive(sc);
1264e2612299Spatrick return;
1265e2612299Spatrick }
1266e2612299Spatrick if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL) {
1267e2612299Spatrick bwfm_chip_cm3_set_passive(sc);
1268e2612299Spatrick return;
1269e2612299Spatrick }
1270e2612299Spatrick }
1271e2612299Spatrick
1272e2612299Spatrick int
bwfm_chip_cr4_set_active(struct bwfm_softc * sc,uint32_t rstvec)1273e2612299Spatrick bwfm_chip_cr4_set_active(struct bwfm_softc *sc, uint32_t rstvec)
1274e2612299Spatrick {
1275e2612299Spatrick struct bwfm_core *core;
1276e2612299Spatrick
1277608f8b14Spatrick sc->sc_buscore_ops->bc_activate(sc, rstvec);
1278e2612299Spatrick core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4);
1279e2612299Spatrick sc->sc_chip.ch_core_reset(sc, core,
1280e2612299Spatrick BWFM_AGENT_IOCTL_ARMCR4_CPUHALT, 0, 0);
1281e2612299Spatrick
1282e2612299Spatrick return 0;
1283e2612299Spatrick }
1284e2612299Spatrick
128532b2494eSpatrick void
bwfm_chip_cr4_set_passive(struct bwfm_softc * sc)128632b2494eSpatrick bwfm_chip_cr4_set_passive(struct bwfm_softc *sc)
128732b2494eSpatrick {
1288c6962397Spatrick struct bwfm_core *core;
12893e129b89Spatrick uint32_t val;
129064be38baSpatrick int i = 0;
1291c6962397Spatrick
1292c6962397Spatrick core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4);
12933e129b89Spatrick val = sc->sc_buscore_ops->bc_read(sc,
12943e129b89Spatrick core->co_wrapbase + BWFM_AGENT_IOCTL);
12953e129b89Spatrick sc->sc_chip.ch_core_reset(sc, core,
12963e129b89Spatrick val & BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
12973e129b89Spatrick BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
12983e129b89Spatrick BWFM_AGENT_IOCTL_ARMCR4_CPUHALT);
12993e129b89Spatrick
130064be38baSpatrick while ((core = bwfm_chip_get_core_idx(sc, BWFM_AGENT_CORE_80211, i++)))
130164be38baSpatrick sc->sc_chip.ch_core_disable(sc, core,
130264be38baSpatrick BWFM_AGENT_D11_IOCTL_PHYRESET |
130364be38baSpatrick BWFM_AGENT_D11_IOCTL_PHYCLOCKEN,
1304c6962397Spatrick BWFM_AGENT_D11_IOCTL_PHYCLOCKEN);
130532b2494eSpatrick }
130632b2494eSpatrick
1307e2612299Spatrick int
bwfm_chip_ca7_set_active(struct bwfm_softc * sc,uint32_t rstvec)1308e2612299Spatrick bwfm_chip_ca7_set_active(struct bwfm_softc *sc, uint32_t rstvec)
1309e2612299Spatrick {
13103e129b89Spatrick struct bwfm_core *core;
13113e129b89Spatrick
13123e129b89Spatrick sc->sc_buscore_ops->bc_activate(sc, rstvec);
13133e129b89Spatrick core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7);
13143e129b89Spatrick sc->sc_chip.ch_core_reset(sc, core,
13153e129b89Spatrick BWFM_AGENT_IOCTL_ARMCR4_CPUHALT, 0, 0);
13163e129b89Spatrick
13173e129b89Spatrick return 0;
1318e2612299Spatrick }
1319e2612299Spatrick
132032b2494eSpatrick void
bwfm_chip_ca7_set_passive(struct bwfm_softc * sc)132132b2494eSpatrick bwfm_chip_ca7_set_passive(struct bwfm_softc *sc)
132232b2494eSpatrick {
13233e129b89Spatrick struct bwfm_core *core;
13243e129b89Spatrick uint32_t val;
1325edb884eaSkettenis int i = 0;
13263e129b89Spatrick
13273e129b89Spatrick core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7);
13283e129b89Spatrick val = sc->sc_buscore_ops->bc_read(sc,
13293e129b89Spatrick core->co_wrapbase + BWFM_AGENT_IOCTL);
13303e129b89Spatrick sc->sc_chip.ch_core_reset(sc, core,
13313e129b89Spatrick val & BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
13323e129b89Spatrick BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
13333e129b89Spatrick BWFM_AGENT_IOCTL_ARMCR4_CPUHALT);
13343e129b89Spatrick
1335edb884eaSkettenis while ((core = bwfm_chip_get_core_idx(sc, BWFM_AGENT_CORE_80211, i++)))
1336edb884eaSkettenis sc->sc_chip.ch_core_disable(sc, core,
1337edb884eaSkettenis BWFM_AGENT_D11_IOCTL_PHYRESET |
1338edb884eaSkettenis BWFM_AGENT_D11_IOCTL_PHYCLOCKEN,
13393e129b89Spatrick BWFM_AGENT_D11_IOCTL_PHYCLOCKEN);
134032b2494eSpatrick }
134132b2494eSpatrick
1342e2612299Spatrick int
bwfm_chip_cm3_set_active(struct bwfm_softc * sc)1343e2612299Spatrick bwfm_chip_cm3_set_active(struct bwfm_softc *sc)
1344e2612299Spatrick {
1345b5537148Spatrick struct bwfm_core *core;
1346b5537148Spatrick
1347b5537148Spatrick core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM);
1348b5537148Spatrick if (!sc->sc_chip.ch_core_isup(sc, core))
1349b5537148Spatrick return 1;
1350b5537148Spatrick
1351b5537148Spatrick sc->sc_buscore_ops->bc_activate(sc, 0);
1352b5537148Spatrick
1353b5537148Spatrick core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3);
1354b5537148Spatrick sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
1355b5537148Spatrick
1356b5537148Spatrick return 0;
1357e2612299Spatrick }
1358e2612299Spatrick
135932b2494eSpatrick void
bwfm_chip_cm3_set_passive(struct bwfm_softc * sc)136032b2494eSpatrick bwfm_chip_cm3_set_passive(struct bwfm_softc *sc)
136132b2494eSpatrick {
136232b2494eSpatrick struct bwfm_core *core;
136332b2494eSpatrick
136432b2494eSpatrick core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3);
136532b2494eSpatrick sc->sc_chip.ch_core_disable(sc, core, 0, 0);
136632b2494eSpatrick core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_80211);
136732b2494eSpatrick sc->sc_chip.ch_core_reset(sc, core, BWFM_AGENT_D11_IOCTL_PHYRESET |
136832b2494eSpatrick BWFM_AGENT_D11_IOCTL_PHYCLOCKEN, BWFM_AGENT_D11_IOCTL_PHYCLOCKEN,
136932b2494eSpatrick BWFM_AGENT_D11_IOCTL_PHYCLOCKEN);
137032b2494eSpatrick core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM);
137132b2494eSpatrick sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
137232b2494eSpatrick
137332b2494eSpatrick if (sc->sc_chip.ch_chip == BRCM_CC_43430_CHIP_ID) {
137432b2494eSpatrick sc->sc_buscore_ops->bc_write(sc,
137532b2494eSpatrick core->co_base + BWFM_SOCRAM_BANKIDX, 3);
137632b2494eSpatrick sc->sc_buscore_ops->bc_write(sc,
137732b2494eSpatrick core->co_base + BWFM_SOCRAM_BANKPDA, 0);
137832b2494eSpatrick }
137932b2494eSpatrick }
138032b2494eSpatrick
1381a4c8e2b6Spatrick int
bwfm_chip_sr_capable(struct bwfm_softc * sc)1382a4c8e2b6Spatrick bwfm_chip_sr_capable(struct bwfm_softc *sc)
1383a4c8e2b6Spatrick {
1384a4c8e2b6Spatrick struct bwfm_core *core;
1385a4c8e2b6Spatrick uint32_t reg;
1386a4c8e2b6Spatrick
1387a4c8e2b6Spatrick if (sc->sc_chip.ch_pmurev < 17)
1388a4c8e2b6Spatrick return 0;
1389a4c8e2b6Spatrick
1390a4c8e2b6Spatrick switch (sc->sc_chip.ch_chip) {
1391315a0156Spatrick case BRCM_CC_4345_CHIP_ID:
1392a4c8e2b6Spatrick case BRCM_CC_4354_CHIP_ID:
1393a4c8e2b6Spatrick case BRCM_CC_4356_CHIP_ID:
13945152d48cSpatrick core = bwfm_chip_get_pmu(sc);
1395a4c8e2b6Spatrick sc->sc_buscore_ops->bc_write(sc, core->co_base +
1396a4c8e2b6Spatrick BWFM_CHIP_REG_CHIPCONTROL_ADDR, 3);
1397a4c8e2b6Spatrick reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
1398a4c8e2b6Spatrick BWFM_CHIP_REG_CHIPCONTROL_DATA);
1399a4c8e2b6Spatrick return (reg & (1 << 2)) != 0;
1400a4c8e2b6Spatrick case BRCM_CC_43241_CHIP_ID:
1401a4c8e2b6Spatrick case BRCM_CC_4335_CHIP_ID:
1402a4c8e2b6Spatrick case BRCM_CC_4339_CHIP_ID:
14035152d48cSpatrick core = bwfm_chip_get_pmu(sc);
1404a4c8e2b6Spatrick sc->sc_buscore_ops->bc_write(sc, core->co_base +
1405a4c8e2b6Spatrick BWFM_CHIP_REG_CHIPCONTROL_ADDR, 3);
1406a4c8e2b6Spatrick reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
1407a4c8e2b6Spatrick BWFM_CHIP_REG_CHIPCONTROL_DATA);
1408a4c8e2b6Spatrick return reg != 0;
1409a4c8e2b6Spatrick case BRCM_CC_43430_CHIP_ID:
14105152d48cSpatrick core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
1411a4c8e2b6Spatrick reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
1412a4c8e2b6Spatrick BWFM_CHIP_REG_SR_CONTROL1);
1413a4c8e2b6Spatrick return reg != 0;
14148db7132fSpatrick case CY_CC_4373_CHIP_ID:
14158db7132fSpatrick core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
14168db7132fSpatrick reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
14178db7132fSpatrick BWFM_CHIP_REG_SR_CONTROL0);
14188db7132fSpatrick return (reg & BWFM_CHIP_REG_SR_CONTROL0_ENABLE) != 0;
14198db7132fSpatrick case BRCM_CC_4359_CHIP_ID:
14208db7132fSpatrick case CY_CC_43752_CHIP_ID:
14218db7132fSpatrick case CY_CC_43012_CHIP_ID:
14228db7132fSpatrick core = bwfm_chip_get_pmu(sc);
14238db7132fSpatrick reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
14248db7132fSpatrick BWFM_CHIP_REG_RETENTION_CTL);
14258db7132fSpatrick return (reg & (BWFM_CHIP_REG_RETENTION_CTL_MACPHY_DIS |
14268db7132fSpatrick BWFM_CHIP_REG_RETENTION_CTL_LOGIC_DIS)) == 0;
1427c38a9bc9Spatrick case BRCM_CC_4378_CHIP_ID:
1428c38a9bc9Spatrick return 0;
1429a4c8e2b6Spatrick default:
14305152d48cSpatrick core = bwfm_chip_get_pmu(sc);
1431a4c8e2b6Spatrick reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
1432a4c8e2b6Spatrick BWFM_CHIP_REG_PMUCAPABILITIES_EXT);
1433a4c8e2b6Spatrick if ((reg & BWFM_CHIP_REG_PMUCAPABILITIES_SR_SUPP) == 0)
1434a4c8e2b6Spatrick return 0;
1435a4c8e2b6Spatrick
1436a4c8e2b6Spatrick reg = sc->sc_buscore_ops->bc_read(sc, core->co_base +
1437a4c8e2b6Spatrick BWFM_CHIP_REG_RETENTION_CTL);
1438a4c8e2b6Spatrick return (reg & (BWFM_CHIP_REG_RETENTION_CTL_MACPHY_DIS |
1439a4c8e2b6Spatrick BWFM_CHIP_REG_RETENTION_CTL_LOGIC_DIS)) == 0;
1440a4c8e2b6Spatrick }
1441a4c8e2b6Spatrick }
1442a4c8e2b6Spatrick
144385d32364Spatrick /* RAM size helpers */
144485d32364Spatrick void
bwfm_chip_socram_ramsize(struct bwfm_softc * sc,struct bwfm_core * core)144585d32364Spatrick bwfm_chip_socram_ramsize(struct bwfm_softc *sc, struct bwfm_core *core)
144685d32364Spatrick {
14475212e703Spatrick uint32_t coreinfo, nb, lss, banksize, bankinfo;
14485212e703Spatrick uint32_t ramsize = 0, srsize = 0;
14495212e703Spatrick int i;
14505212e703Spatrick
14515212e703Spatrick if (!sc->sc_chip.ch_core_isup(sc, core))
14525212e703Spatrick sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
14535212e703Spatrick
14545212e703Spatrick coreinfo = sc->sc_buscore_ops->bc_read(sc,
14555212e703Spatrick core->co_base + BWFM_SOCRAM_COREINFO);
14565212e703Spatrick nb = (coreinfo & BWFM_SOCRAM_COREINFO_SRNB_MASK)
14575212e703Spatrick >> BWFM_SOCRAM_COREINFO_SRNB_SHIFT;
14585212e703Spatrick
14595212e703Spatrick if (core->co_rev <= 7 || core->co_rev == 12) {
14605212e703Spatrick banksize = coreinfo & BWFM_SOCRAM_COREINFO_SRBSZ_MASK;
14615212e703Spatrick lss = (coreinfo & BWFM_SOCRAM_COREINFO_LSS_MASK)
14625212e703Spatrick >> BWFM_SOCRAM_COREINFO_LSS_SHIFT;
14635212e703Spatrick if (lss != 0)
14645212e703Spatrick nb--;
14655212e703Spatrick ramsize = nb * (1 << (banksize + BWFM_SOCRAM_COREINFO_SRBSZ_BASE));
14665212e703Spatrick if (lss != 0)
14675212e703Spatrick ramsize += (1 << ((lss - 1) + BWFM_SOCRAM_COREINFO_SRBSZ_BASE));
14685212e703Spatrick } else {
14695212e703Spatrick for (i = 0; i < nb; i++) {
14705212e703Spatrick sc->sc_buscore_ops->bc_write(sc,
14715212e703Spatrick core->co_base + BWFM_SOCRAM_BANKIDX,
14725212e703Spatrick (BWFM_SOCRAM_BANKIDX_MEMTYPE_RAM <<
14735212e703Spatrick BWFM_SOCRAM_BANKIDX_MEMTYPE_SHIFT) | i);
14745212e703Spatrick bankinfo = sc->sc_buscore_ops->bc_read(sc,
14755212e703Spatrick core->co_base + BWFM_SOCRAM_BANKINFO);
14765212e703Spatrick banksize = ((bankinfo & BWFM_SOCRAM_BANKINFO_SZMASK) + 1)
14775212e703Spatrick * BWFM_SOCRAM_BANKINFO_SZBASE;
14785212e703Spatrick ramsize += banksize;
14795212e703Spatrick if (bankinfo & BWFM_SOCRAM_BANKINFO_RETNTRAM_MASK)
14805212e703Spatrick srsize += banksize;
14815212e703Spatrick }
14825212e703Spatrick }
14835212e703Spatrick
14845212e703Spatrick switch (sc->sc_chip.ch_chip) {
14855212e703Spatrick case BRCM_CC_4334_CHIP_ID:
14865212e703Spatrick if (sc->sc_chip.ch_chiprev < 2)
14875212e703Spatrick srsize = 32 * 1024;
14885212e703Spatrick break;
14895212e703Spatrick case BRCM_CC_43430_CHIP_ID:
14905212e703Spatrick srsize = 64 * 1024;
14915212e703Spatrick break;
14925212e703Spatrick default:
14935212e703Spatrick break;
14945212e703Spatrick }
14955212e703Spatrick
14965212e703Spatrick sc->sc_chip.ch_ramsize = ramsize;
14975212e703Spatrick sc->sc_chip.ch_srsize = srsize;
149885d32364Spatrick }
149985d32364Spatrick
150085d32364Spatrick void
bwfm_chip_sysmem_ramsize(struct bwfm_softc * sc,struct bwfm_core * core)150185d32364Spatrick bwfm_chip_sysmem_ramsize(struct bwfm_softc *sc, struct bwfm_core *core)
150285d32364Spatrick {
1503c9aa06c9Spatrick uint32_t coreinfo, nb, banksize, bankinfo;
1504c9aa06c9Spatrick uint32_t ramsize = 0;
1505c9aa06c9Spatrick int i;
1506c9aa06c9Spatrick
1507c9aa06c9Spatrick if (!sc->sc_chip.ch_core_isup(sc, core))
1508c9aa06c9Spatrick sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
1509c9aa06c9Spatrick
1510c9aa06c9Spatrick coreinfo = sc->sc_buscore_ops->bc_read(sc,
1511c9aa06c9Spatrick core->co_base + BWFM_SOCRAM_COREINFO);
1512c9aa06c9Spatrick nb = (coreinfo & BWFM_SOCRAM_COREINFO_SRNB_MASK)
1513c9aa06c9Spatrick >> BWFM_SOCRAM_COREINFO_SRNB_SHIFT;
1514c9aa06c9Spatrick
1515c9aa06c9Spatrick for (i = 0; i < nb; i++) {
1516c9aa06c9Spatrick sc->sc_buscore_ops->bc_write(sc,
1517c9aa06c9Spatrick core->co_base + BWFM_SOCRAM_BANKIDX,
1518c9aa06c9Spatrick (BWFM_SOCRAM_BANKIDX_MEMTYPE_RAM <<
1519c9aa06c9Spatrick BWFM_SOCRAM_BANKIDX_MEMTYPE_SHIFT) | i);
1520c9aa06c9Spatrick bankinfo = sc->sc_buscore_ops->bc_read(sc,
1521c9aa06c9Spatrick core->co_base + BWFM_SOCRAM_BANKINFO);
1522c9aa06c9Spatrick banksize = ((bankinfo & BWFM_SOCRAM_BANKINFO_SZMASK) + 1)
1523c9aa06c9Spatrick * BWFM_SOCRAM_BANKINFO_SZBASE;
1524c9aa06c9Spatrick ramsize += banksize;
1525c9aa06c9Spatrick }
1526c9aa06c9Spatrick
1527c9aa06c9Spatrick sc->sc_chip.ch_ramsize = ramsize;
152885d32364Spatrick }
152985d32364Spatrick
153085d32364Spatrick void
bwfm_chip_tcm_ramsize(struct bwfm_softc * sc,struct bwfm_core * core)153185d32364Spatrick bwfm_chip_tcm_ramsize(struct bwfm_softc *sc, struct bwfm_core *core)
153285d32364Spatrick {
15335ea0cc51Spatrick uint32_t cap, nab, nbb, totb, bxinfo, blksize, ramsize = 0;
153485d32364Spatrick int i;
153585d32364Spatrick
153685d32364Spatrick cap = sc->sc_buscore_ops->bc_read(sc, core->co_base + BWFM_ARMCR4_CAP);
153785d32364Spatrick nab = (cap & BWFM_ARMCR4_CAP_TCBANB_MASK) >> BWFM_ARMCR4_CAP_TCBANB_SHIFT;
153885d32364Spatrick nbb = (cap & BWFM_ARMCR4_CAP_TCBBNB_MASK) >> BWFM_ARMCR4_CAP_TCBBNB_SHIFT;
153985d32364Spatrick totb = nab + nbb;
154085d32364Spatrick
154185d32364Spatrick for (i = 0; i < totb; i++) {
154285d32364Spatrick sc->sc_buscore_ops->bc_write(sc,
154385d32364Spatrick core->co_base + BWFM_ARMCR4_BANKIDX, i);
154485d32364Spatrick bxinfo = sc->sc_buscore_ops->bc_read(sc,
154585d32364Spatrick core->co_base + BWFM_ARMCR4_BANKINFO);
15465ea0cc51Spatrick if (bxinfo & BWFM_ARMCR4_BANKINFO_BLK_1K_MASK)
15475ea0cc51Spatrick blksize = 1024;
15485ea0cc51Spatrick else
15495ea0cc51Spatrick blksize = 8192;
15505212e703Spatrick ramsize += ((bxinfo & BWFM_ARMCR4_BANKINFO_BSZ_MASK) + 1) *
15515ea0cc51Spatrick blksize;
155285d32364Spatrick }
155385d32364Spatrick
15545212e703Spatrick sc->sc_chip.ch_ramsize = ramsize;
155585d32364Spatrick }
155685d32364Spatrick
155785d32364Spatrick void
bwfm_chip_tcm_rambase(struct bwfm_softc * sc)155885d32364Spatrick bwfm_chip_tcm_rambase(struct bwfm_softc *sc)
155985d32364Spatrick {
156085d32364Spatrick switch (sc->sc_chip.ch_chip) {
156185d32364Spatrick case BRCM_CC_4345_CHIP_ID:
156285d32364Spatrick sc->sc_chip.ch_rambase = 0x198000;
156385d32364Spatrick break;
156485d32364Spatrick case BRCM_CC_4335_CHIP_ID:
156585d32364Spatrick case BRCM_CC_4339_CHIP_ID:
156685d32364Spatrick case BRCM_CC_4350_CHIP_ID:
156785d32364Spatrick case BRCM_CC_4354_CHIP_ID:
156885d32364Spatrick case BRCM_CC_4356_CHIP_ID:
156985d32364Spatrick case BRCM_CC_43567_CHIP_ID:
157085d32364Spatrick case BRCM_CC_43569_CHIP_ID:
157185d32364Spatrick case BRCM_CC_43570_CHIP_ID:
157285d32364Spatrick case BRCM_CC_4358_CHIP_ID:
157385d32364Spatrick case BRCM_CC_43602_CHIP_ID:
157485d32364Spatrick case BRCM_CC_4371_CHIP_ID:
157585d32364Spatrick sc->sc_chip.ch_rambase = 0x180000;
157685d32364Spatrick break;
15778db7132fSpatrick case BRCM_CC_43465_CHIP_ID:
15788db7132fSpatrick case BRCM_CC_43525_CHIP_ID:
15798db7132fSpatrick case BRCM_CC_4365_CHIP_ID:
15808db7132fSpatrick case BRCM_CC_4366_CHIP_ID:
15818db7132fSpatrick case BRCM_CC_43664_CHIP_ID:
15828db7132fSpatrick case BRCM_CC_43666_CHIP_ID:
15838db7132fSpatrick sc->sc_chip.ch_rambase = 0x200000;
15848db7132fSpatrick break;
15850d580645Skettenis case BRCM_CC_4359_CHIP_ID:
15860d580645Skettenis if (sc->sc_chip.ch_chiprev < 9)
15870d580645Skettenis sc->sc_chip.ch_rambase = 0x180000;
15880d580645Skettenis else
15890d580645Skettenis sc->sc_chip.ch_rambase = 0x160000;
15900d580645Skettenis break;
15918db7132fSpatrick case BRCM_CC_4355_CHIP_ID:
15928db7132fSpatrick case BRCM_CC_4364_CHIP_ID:
159385d32364Spatrick case CY_CC_4373_CHIP_ID:
159485d32364Spatrick sc->sc_chip.ch_rambase = 0x160000;
159585d32364Spatrick break;
15968db7132fSpatrick case BRCM_CC_4377_CHIP_ID:
15978db7132fSpatrick case CY_CC_43752_CHIP_ID:
15988db7132fSpatrick sc->sc_chip.ch_rambase = 0x170000;
15998db7132fSpatrick break;
1600c38a9bc9Spatrick case BRCM_CC_4378_CHIP_ID:
1601c38a9bc9Spatrick sc->sc_chip.ch_rambase = 0x352000;
1602c38a9bc9Spatrick break;
16038db7132fSpatrick case BRCM_CC_4387_CHIP_ID:
16048db7132fSpatrick sc->sc_chip.ch_rambase = 0x740000;
16058db7132fSpatrick break;
160685d32364Spatrick default:
160785d32364Spatrick printf("%s: unknown chip: %d\n", DEVNAME(sc),
160885d32364Spatrick sc->sc_chip.ch_chip);
160985d32364Spatrick break;
161085d32364Spatrick }
161185d32364Spatrick }
161285d32364Spatrick
161332b2494eSpatrick /* BCDC protocol implementation */
161432b2494eSpatrick int
bwfm_proto_bcdc_query_dcmd(struct bwfm_softc * sc,int ifidx,int cmd,char * buf,size_t * len)161532b2494eSpatrick bwfm_proto_bcdc_query_dcmd(struct bwfm_softc *sc, int ifidx,
161632b2494eSpatrick int cmd, char *buf, size_t *len)
161732b2494eSpatrick {
161832b2494eSpatrick struct bwfm_proto_bcdc_dcmd *dcmd;
161932b2494eSpatrick size_t size = sizeof(dcmd->hdr) + *len;
1620029d6dd5Spatrick int ret = 1, reqid;
162132b2494eSpatrick
1622029d6dd5Spatrick reqid = sc->sc_bcdc_reqid++;
162332b2494eSpatrick
162432b2494eSpatrick if (*len > sizeof(dcmd->buf))
1625029d6dd5Spatrick return ret;
162632b2494eSpatrick
1627029d6dd5Spatrick dcmd = malloc(size, M_TEMP, M_WAITOK | M_ZERO);
162832b2494eSpatrick dcmd->hdr.cmd = htole32(cmd);
162932b2494eSpatrick dcmd->hdr.len = htole32(*len);
163032b2494eSpatrick dcmd->hdr.flags |= BWFM_BCDC_DCMD_GET;
163132b2494eSpatrick dcmd->hdr.flags |= BWFM_BCDC_DCMD_ID_SET(reqid);
163232b2494eSpatrick dcmd->hdr.flags |= BWFM_BCDC_DCMD_IF_SET(ifidx);
163332b2494eSpatrick dcmd->hdr.flags = htole32(dcmd->hdr.flags);
163432b2494eSpatrick memcpy(&dcmd->buf, buf, *len);
163532b2494eSpatrick
1636029d6dd5Spatrick if (bwfm_proto_bcdc_txctl(sc, reqid, (char *)dcmd, &size)) {
163732b2494eSpatrick DPRINTF(("%s: tx failed\n", DEVNAME(sc)));
1638029d6dd5Spatrick return ret;
163932b2494eSpatrick }
164032b2494eSpatrick
164132b2494eSpatrick if (buf) {
1642029d6dd5Spatrick *len = min(*len, size);
164332b2494eSpatrick memcpy(buf, dcmd->buf, *len);
164432b2494eSpatrick }
164532b2494eSpatrick
164632b2494eSpatrick if (dcmd->hdr.flags & BWFM_BCDC_DCMD_ERROR)
164732b2494eSpatrick ret = dcmd->hdr.status;
164832b2494eSpatrick else
164932b2494eSpatrick ret = 0;
1650029d6dd5Spatrick free(dcmd, M_TEMP, size);
165132b2494eSpatrick return ret;
165232b2494eSpatrick }
165332b2494eSpatrick
165432b2494eSpatrick int
bwfm_proto_bcdc_set_dcmd(struct bwfm_softc * sc,int ifidx,int cmd,char * buf,size_t len)165532b2494eSpatrick bwfm_proto_bcdc_set_dcmd(struct bwfm_softc *sc, int ifidx,
165632b2494eSpatrick int cmd, char *buf, size_t len)
165732b2494eSpatrick {
165832b2494eSpatrick struct bwfm_proto_bcdc_dcmd *dcmd;
165932b2494eSpatrick size_t size = sizeof(dcmd->hdr) + len;
1660029d6dd5Spatrick int ret = 1, reqid;
166132b2494eSpatrick
1662029d6dd5Spatrick reqid = sc->sc_bcdc_reqid++;
166332b2494eSpatrick
166432b2494eSpatrick if (len > sizeof(dcmd->buf))
1665029d6dd5Spatrick return ret;
166632b2494eSpatrick
166777b542adSpatrick dcmd = malloc(size, M_TEMP, M_WAITOK | M_ZERO);
166832b2494eSpatrick dcmd->hdr.cmd = htole32(cmd);
166932b2494eSpatrick dcmd->hdr.len = htole32(len);
167032b2494eSpatrick dcmd->hdr.flags |= BWFM_BCDC_DCMD_SET;
167132b2494eSpatrick dcmd->hdr.flags |= BWFM_BCDC_DCMD_ID_SET(reqid);
167232b2494eSpatrick dcmd->hdr.flags |= BWFM_BCDC_DCMD_IF_SET(ifidx);
167332b2494eSpatrick dcmd->hdr.flags = htole32(dcmd->hdr.flags);
167432b2494eSpatrick memcpy(&dcmd->buf, buf, len);
167532b2494eSpatrick
1676029d6dd5Spatrick if (bwfm_proto_bcdc_txctl(sc, reqid, (char *)dcmd, &size)) {
1677029d6dd5Spatrick DPRINTF(("%s: txctl failed\n", DEVNAME(sc)));
1678029d6dd5Spatrick return ret;
167932b2494eSpatrick }
168032b2494eSpatrick
1681029d6dd5Spatrick if (dcmd->hdr.flags & BWFM_BCDC_DCMD_ERROR)
1682029d6dd5Spatrick ret = dcmd->hdr.status;
1683029d6dd5Spatrick else
1684029d6dd5Spatrick ret = 0;
1685029d6dd5Spatrick free(dcmd, M_TEMP, size);
1686029d6dd5Spatrick return ret;
168732b2494eSpatrick }
1688029d6dd5Spatrick
1689029d6dd5Spatrick int
bwfm_proto_bcdc_txctl(struct bwfm_softc * sc,int reqid,char * buf,size_t * len)1690029d6dd5Spatrick bwfm_proto_bcdc_txctl(struct bwfm_softc *sc, int reqid, char *buf, size_t *len)
1691029d6dd5Spatrick {
1692029d6dd5Spatrick struct bwfm_proto_bcdc_ctl *ctl, *tmp;
1693029d6dd5Spatrick int timeout = 0;
1694029d6dd5Spatrick
1695029d6dd5Spatrick ctl = malloc(sizeof(*ctl), M_TEMP, M_WAITOK|M_ZERO);
1696029d6dd5Spatrick ctl->reqid = reqid;
1697029d6dd5Spatrick ctl->buf = buf;
1698029d6dd5Spatrick ctl->len = *len;
1699029d6dd5Spatrick
17002802c178Spatrick if (sc->sc_bus_ops->bs_txctl(sc, ctl)) {
1701029d6dd5Spatrick DPRINTF(("%s: tx failed\n", DEVNAME(sc)));
1702029d6dd5Spatrick return 1;
1703029d6dd5Spatrick }
1704029d6dd5Spatrick
17058544fed6Smpi if (tsleep_nsec(ctl, PWAIT, "bwfm", SEC_TO_NSEC(1)))
1706029d6dd5Spatrick timeout = 1;
1707029d6dd5Spatrick
1708029d6dd5Spatrick TAILQ_FOREACH_SAFE(ctl, &sc->sc_bcdc_rxctlq, next, tmp) {
1709029d6dd5Spatrick if (ctl->reqid != reqid)
1710029d6dd5Spatrick continue;
1711029d6dd5Spatrick if (ctl->done) {
1712029d6dd5Spatrick TAILQ_REMOVE(&sc->sc_bcdc_rxctlq, ctl, next);
1713029d6dd5Spatrick *len = ctl->len;
1714029d6dd5Spatrick free(ctl, M_TEMP, sizeof(*ctl));
1715029d6dd5Spatrick return 0;
1716029d6dd5Spatrick }
1717029d6dd5Spatrick if (timeout) {
1718029d6dd5Spatrick TAILQ_REMOVE(&sc->sc_bcdc_rxctlq, ctl, next);
1719029d6dd5Spatrick DPRINTF(("%s: timeout waiting for txctl response\n",
1720029d6dd5Spatrick DEVNAME(sc)));
1721029d6dd5Spatrick free(ctl->buf, M_TEMP, ctl->len);
1722029d6dd5Spatrick free(ctl, M_TEMP, sizeof(*ctl));
1723029d6dd5Spatrick return 1;
1724029d6dd5Spatrick }
1725029d6dd5Spatrick break;
1726029d6dd5Spatrick }
1727029d6dd5Spatrick
1728029d6dd5Spatrick DPRINTF(("%s: did%s find txctl metadata (timeout %d)\n",
1729029d6dd5Spatrick DEVNAME(sc), ctl == NULL ? " not": "", timeout));
1730029d6dd5Spatrick return 1;
1731029d6dd5Spatrick }
1732029d6dd5Spatrick
1733029d6dd5Spatrick void
bwfm_proto_bcdc_rxctl(struct bwfm_softc * sc,char * buf,size_t len)1734029d6dd5Spatrick bwfm_proto_bcdc_rxctl(struct bwfm_softc *sc, char *buf, size_t len)
1735029d6dd5Spatrick {
1736029d6dd5Spatrick struct bwfm_proto_bcdc_dcmd *dcmd;
1737029d6dd5Spatrick struct bwfm_proto_bcdc_ctl *ctl, *tmp;
1738029d6dd5Spatrick
1739029d6dd5Spatrick if (len < sizeof(dcmd->hdr))
1740029d6dd5Spatrick return;
1741029d6dd5Spatrick
1742029d6dd5Spatrick dcmd = (struct bwfm_proto_bcdc_dcmd *)buf;
174332b2494eSpatrick dcmd->hdr.cmd = letoh32(dcmd->hdr.cmd);
174432b2494eSpatrick dcmd->hdr.len = letoh32(dcmd->hdr.len);
174532b2494eSpatrick dcmd->hdr.flags = letoh32(dcmd->hdr.flags);
174632b2494eSpatrick dcmd->hdr.status = letoh32(dcmd->hdr.status);
174732b2494eSpatrick
1748029d6dd5Spatrick TAILQ_FOREACH_SAFE(ctl, &sc->sc_bcdc_rxctlq, next, tmp) {
1749029d6dd5Spatrick if (ctl->reqid != BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags))
1750029d6dd5Spatrick continue;
1751029d6dd5Spatrick if (ctl->len != len) {
1752029d6dd5Spatrick free(ctl->buf, M_TEMP, ctl->len);
1753029d6dd5Spatrick free(ctl, M_TEMP, sizeof(*ctl));
1754029d6dd5Spatrick return;
175532b2494eSpatrick }
1756029d6dd5Spatrick memcpy(ctl->buf, buf, len);
1757029d6dd5Spatrick ctl->done = 1;
1758029d6dd5Spatrick wakeup(ctl);
1759029d6dd5Spatrick return;
1760029d6dd5Spatrick }
176132b2494eSpatrick }
176232b2494eSpatrick
17637dfc0b73Spatrick void
bwfm_proto_bcdc_rx(struct bwfm_softc * sc,struct mbuf * m,struct mbuf_list * ml)17646f241297Spatrick bwfm_proto_bcdc_rx(struct bwfm_softc *sc, struct mbuf *m, struct mbuf_list *ml)
17657dfc0b73Spatrick {
17667dfc0b73Spatrick struct bwfm_proto_bcdc_hdr *hdr;
17677dfc0b73Spatrick
17687dfc0b73Spatrick hdr = mtod(m, struct bwfm_proto_bcdc_hdr *);
17697dfc0b73Spatrick if (m->m_len < sizeof(*hdr)) {
17707dfc0b73Spatrick m_freem(m);
17717dfc0b73Spatrick return;
17727dfc0b73Spatrick }
17737dfc0b73Spatrick if (m->m_len < sizeof(*hdr) + (hdr->data_offset << 2)) {
17747dfc0b73Spatrick m_freem(m);
17757dfc0b73Spatrick return;
17767dfc0b73Spatrick }
17777dfc0b73Spatrick m_adj(m, sizeof(*hdr) + (hdr->data_offset << 2));
17788878a15eSpatrick
17796f241297Spatrick bwfm_rx(sc, m, ml);
17807dfc0b73Spatrick }
17817dfc0b73Spatrick
178232b2494eSpatrick /* FW Variable code */
178332b2494eSpatrick int
bwfm_fwvar_cmd_get_data(struct bwfm_softc * sc,int cmd,void * data,size_t len)178432b2494eSpatrick bwfm_fwvar_cmd_get_data(struct bwfm_softc *sc, int cmd, void *data, size_t len)
178532b2494eSpatrick {
178632b2494eSpatrick return sc->sc_proto_ops->proto_query_dcmd(sc, 0, cmd, data, &len);
178732b2494eSpatrick }
178832b2494eSpatrick
178932b2494eSpatrick int
bwfm_fwvar_cmd_set_data(struct bwfm_softc * sc,int cmd,void * data,size_t len)179032b2494eSpatrick bwfm_fwvar_cmd_set_data(struct bwfm_softc *sc, int cmd, void *data, size_t len)
179132b2494eSpatrick {
179232b2494eSpatrick return sc->sc_proto_ops->proto_set_dcmd(sc, 0, cmd, data, len);
179332b2494eSpatrick }
179432b2494eSpatrick
179532b2494eSpatrick int
bwfm_fwvar_cmd_get_int(struct bwfm_softc * sc,int cmd,uint32_t * data)179632b2494eSpatrick bwfm_fwvar_cmd_get_int(struct bwfm_softc *sc, int cmd, uint32_t *data)
179732b2494eSpatrick {
179832b2494eSpatrick int ret;
179932b2494eSpatrick ret = bwfm_fwvar_cmd_get_data(sc, cmd, data, sizeof(*data));
180032b2494eSpatrick *data = letoh32(*data);
180132b2494eSpatrick return ret;
180232b2494eSpatrick }
180332b2494eSpatrick
180432b2494eSpatrick int
bwfm_fwvar_cmd_set_int(struct bwfm_softc * sc,int cmd,uint32_t data)180532b2494eSpatrick bwfm_fwvar_cmd_set_int(struct bwfm_softc *sc, int cmd, uint32_t data)
180632b2494eSpatrick {
180732b2494eSpatrick data = htole32(data);
180832b2494eSpatrick return bwfm_fwvar_cmd_set_data(sc, cmd, &data, sizeof(data));
180932b2494eSpatrick }
181032b2494eSpatrick
181132b2494eSpatrick int
bwfm_fwvar_var_get_data(struct bwfm_softc * sc,char * name,void * data,size_t len)181232b2494eSpatrick bwfm_fwvar_var_get_data(struct bwfm_softc *sc, char *name, void *data, size_t len)
181332b2494eSpatrick {
181432b2494eSpatrick char *buf;
181532b2494eSpatrick int ret;
181632b2494eSpatrick
181732b2494eSpatrick buf = malloc(strlen(name) + 1 + len, M_TEMP, M_WAITOK);
181832b2494eSpatrick memcpy(buf, name, strlen(name) + 1);
181932b2494eSpatrick memcpy(buf + strlen(name) + 1, data, len);
182032b2494eSpatrick ret = bwfm_fwvar_cmd_get_data(sc, BWFM_C_GET_VAR,
182132b2494eSpatrick buf, strlen(name) + 1 + len);
182232b2494eSpatrick memcpy(data, buf, len);
182332b2494eSpatrick free(buf, M_TEMP, strlen(name) + 1 + len);
182432b2494eSpatrick return ret;
182532b2494eSpatrick }
182632b2494eSpatrick
182732b2494eSpatrick int
bwfm_fwvar_var_set_data(struct bwfm_softc * sc,char * name,void * data,size_t len)182832b2494eSpatrick bwfm_fwvar_var_set_data(struct bwfm_softc *sc, char *name, void *data, size_t len)
182932b2494eSpatrick {
183032b2494eSpatrick char *buf;
183132b2494eSpatrick int ret;
183232b2494eSpatrick
183332b2494eSpatrick buf = malloc(strlen(name) + 1 + len, M_TEMP, M_WAITOK);
183432b2494eSpatrick memcpy(buf, name, strlen(name) + 1);
183532b2494eSpatrick memcpy(buf + strlen(name) + 1, data, len);
183632b2494eSpatrick ret = bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_VAR,
183732b2494eSpatrick buf, strlen(name) + 1 + len);
183832b2494eSpatrick free(buf, M_TEMP, strlen(name) + 1 + len);
183932b2494eSpatrick return ret;
184032b2494eSpatrick }
184132b2494eSpatrick
184232b2494eSpatrick int
bwfm_fwvar_var_get_int(struct bwfm_softc * sc,char * name,uint32_t * data)184332b2494eSpatrick bwfm_fwvar_var_get_int(struct bwfm_softc *sc, char *name, uint32_t *data)
184432b2494eSpatrick {
184532b2494eSpatrick int ret;
184632b2494eSpatrick ret = bwfm_fwvar_var_get_data(sc, name, data, sizeof(*data));
184732b2494eSpatrick *data = letoh32(*data);
184832b2494eSpatrick return ret;
184932b2494eSpatrick }
185032b2494eSpatrick
185132b2494eSpatrick int
bwfm_fwvar_var_set_int(struct bwfm_softc * sc,char * name,uint32_t data)185232b2494eSpatrick bwfm_fwvar_var_set_int(struct bwfm_softc *sc, char *name, uint32_t data)
185332b2494eSpatrick {
185432b2494eSpatrick data = htole32(data);
185532b2494eSpatrick return bwfm_fwvar_var_set_data(sc, name, &data, sizeof(data));
185632b2494eSpatrick }
185732b2494eSpatrick
18588b2458cfSpatrick /* Channel parameters */
18598b2458cfSpatrick uint32_t
bwfm_chan2spec(struct bwfm_softc * sc,struct ieee80211_channel * c)186073b11a26Spatrick bwfm_chan2spec(struct bwfm_softc *sc, struct ieee80211_channel *c)
186173b11a26Spatrick {
186273b11a26Spatrick if (sc->sc_io_type == BWFM_IO_TYPE_D11N)
186373b11a26Spatrick return bwfm_chan2spec_d11n(sc, c);
186473b11a26Spatrick else
186573b11a26Spatrick return bwfm_chan2spec_d11ac(sc, c);
186673b11a26Spatrick }
186773b11a26Spatrick
186873b11a26Spatrick uint32_t
bwfm_chan2spec_d11n(struct bwfm_softc * sc,struct ieee80211_channel * c)186973b11a26Spatrick bwfm_chan2spec_d11n(struct bwfm_softc *sc, struct ieee80211_channel *c)
187073b11a26Spatrick {
187173b11a26Spatrick uint32_t chanspec;
187273b11a26Spatrick
187373b11a26Spatrick chanspec = ieee80211_mhz2ieee(c->ic_freq, 0) & BWFM_CHANSPEC_CHAN_MASK;
187473b11a26Spatrick chanspec |= BWFM_CHANSPEC_D11N_SB_N;
187573b11a26Spatrick chanspec |= BWFM_CHANSPEC_D11N_BW_20;
187673b11a26Spatrick if (IEEE80211_IS_CHAN_2GHZ(c))
187773b11a26Spatrick chanspec |= BWFM_CHANSPEC_D11N_BND_2G;
187873b11a26Spatrick if (IEEE80211_IS_CHAN_5GHZ(c))
187973b11a26Spatrick chanspec |= BWFM_CHANSPEC_D11N_BND_5G;
188073b11a26Spatrick
188173b11a26Spatrick return chanspec;
188273b11a26Spatrick }
188373b11a26Spatrick
188473b11a26Spatrick uint32_t
bwfm_chan2spec_d11ac(struct bwfm_softc * sc,struct ieee80211_channel * c)188573b11a26Spatrick bwfm_chan2spec_d11ac(struct bwfm_softc *sc, struct ieee80211_channel *c)
188673b11a26Spatrick {
188773b11a26Spatrick uint32_t chanspec;
188873b11a26Spatrick
188973b11a26Spatrick chanspec = ieee80211_mhz2ieee(c->ic_freq, 0) & BWFM_CHANSPEC_CHAN_MASK;
189073b11a26Spatrick chanspec |= BWFM_CHANSPEC_D11AC_SB_LLL;
189173b11a26Spatrick chanspec |= BWFM_CHANSPEC_D11AC_BW_20;
189273b11a26Spatrick if (IEEE80211_IS_CHAN_2GHZ(c))
189373b11a26Spatrick chanspec |= BWFM_CHANSPEC_D11AC_BND_2G;
189473b11a26Spatrick if (IEEE80211_IS_CHAN_5GHZ(c))
189573b11a26Spatrick chanspec |= BWFM_CHANSPEC_D11AC_BND_5G;
189673b11a26Spatrick
189773b11a26Spatrick return chanspec;
189873b11a26Spatrick }
189973b11a26Spatrick
190073b11a26Spatrick uint32_t
bwfm_spec2chan(struct bwfm_softc * sc,uint32_t chanspec)19018b2458cfSpatrick bwfm_spec2chan(struct bwfm_softc *sc, uint32_t chanspec)
19028b2458cfSpatrick {
19038b2458cfSpatrick if (sc->sc_io_type == BWFM_IO_TYPE_D11N)
19048b2458cfSpatrick return bwfm_spec2chan_d11n(sc, chanspec);
19058b2458cfSpatrick else
19068b2458cfSpatrick return bwfm_spec2chan_d11ac(sc, chanspec);
19078b2458cfSpatrick }
19088b2458cfSpatrick
19098b2458cfSpatrick uint32_t
bwfm_spec2chan_d11n(struct bwfm_softc * sc,uint32_t chanspec)19108b2458cfSpatrick bwfm_spec2chan_d11n(struct bwfm_softc *sc, uint32_t chanspec)
19118b2458cfSpatrick {
19128b2458cfSpatrick uint32_t chanidx;
19138b2458cfSpatrick
19148b2458cfSpatrick chanidx = chanspec & BWFM_CHANSPEC_CHAN_MASK;
19158b2458cfSpatrick
19168b2458cfSpatrick switch (chanspec & BWFM_CHANSPEC_D11N_BW_MASK) {
19178b2458cfSpatrick case BWFM_CHANSPEC_D11N_BW_40:
19188b2458cfSpatrick switch (chanspec & BWFM_CHANSPEC_D11N_SB_MASK) {
19198b2458cfSpatrick case BWFM_CHANSPEC_D11N_SB_L:
19208b2458cfSpatrick chanidx -= 2;
19218b2458cfSpatrick break;
19228b2458cfSpatrick case BWFM_CHANSPEC_D11N_SB_U:
19238b2458cfSpatrick chanidx += 2;
19248b2458cfSpatrick break;
19258b2458cfSpatrick default:
19268b2458cfSpatrick break;
19278b2458cfSpatrick }
19288b2458cfSpatrick break;
19298b2458cfSpatrick default:
19308b2458cfSpatrick break;
19318b2458cfSpatrick }
19328b2458cfSpatrick
19338b2458cfSpatrick return chanidx;
19348b2458cfSpatrick }
19358b2458cfSpatrick
19368b2458cfSpatrick uint32_t
bwfm_spec2chan_d11ac(struct bwfm_softc * sc,uint32_t chanspec)19378b2458cfSpatrick bwfm_spec2chan_d11ac(struct bwfm_softc *sc, uint32_t chanspec)
19388b2458cfSpatrick {
19398b2458cfSpatrick uint32_t chanidx;
19408b2458cfSpatrick
19418b2458cfSpatrick chanidx = chanspec & BWFM_CHANSPEC_CHAN_MASK;
19428b2458cfSpatrick
19438b2458cfSpatrick switch (chanspec & BWFM_CHANSPEC_D11AC_BW_MASK) {
19448b2458cfSpatrick case BWFM_CHANSPEC_D11AC_BW_40:
19458b2458cfSpatrick switch (chanspec & BWFM_CHANSPEC_D11AC_SB_MASK) {
19468b2458cfSpatrick case BWFM_CHANSPEC_D11AC_SB_LLL:
19478b2458cfSpatrick chanidx -= 2;
19488b2458cfSpatrick break;
19498b2458cfSpatrick case BWFM_CHANSPEC_D11AC_SB_LLU:
19508b2458cfSpatrick chanidx += 2;
19518b2458cfSpatrick break;
19528b2458cfSpatrick default:
19538b2458cfSpatrick break;
19548b2458cfSpatrick }
19558b2458cfSpatrick break;
19568b2458cfSpatrick case BWFM_CHANSPEC_D11AC_BW_80:
19578b2458cfSpatrick switch (chanspec & BWFM_CHANSPEC_D11AC_SB_MASK) {
19588b2458cfSpatrick case BWFM_CHANSPEC_D11AC_SB_LLL:
19598b2458cfSpatrick chanidx -= 6;
19608b2458cfSpatrick break;
19618b2458cfSpatrick case BWFM_CHANSPEC_D11AC_SB_LLU:
19628b2458cfSpatrick chanidx -= 2;
19638b2458cfSpatrick break;
19648b2458cfSpatrick case BWFM_CHANSPEC_D11AC_SB_LUL:
19658b2458cfSpatrick chanidx += 2;
19668b2458cfSpatrick break;
19678b2458cfSpatrick case BWFM_CHANSPEC_D11AC_SB_LUU:
19688b2458cfSpatrick chanidx += 6;
19698b2458cfSpatrick break;
19708b2458cfSpatrick default:
19718b2458cfSpatrick break;
19728b2458cfSpatrick }
19738b2458cfSpatrick break;
19748b2458cfSpatrick default:
19758b2458cfSpatrick break;
19768b2458cfSpatrick }
19778b2458cfSpatrick
19788b2458cfSpatrick return chanidx;
19798b2458cfSpatrick }
19808b2458cfSpatrick
198132b2494eSpatrick /* 802.11 code */
198232b2494eSpatrick void
bwfm_connect(struct bwfm_softc * sc)198363498aa8Spatrick bwfm_connect(struct bwfm_softc *sc)
198432b2494eSpatrick {
198532b2494eSpatrick struct ieee80211com *ic = &sc->sc_ic;
198663498aa8Spatrick struct bwfm_ext_join_params *params;
198763498aa8Spatrick uint8_t buf[64]; /* XXX max WPA/RSN/WMM IE length */
198863498aa8Spatrick uint8_t *frm;
198963498aa8Spatrick
199063498aa8Spatrick /*
1991c8e9388cSstsp * OPEN: Open or WEP or WPA/WPA2 on newer Chips/Firmware.
199263498aa8Spatrick * AUTO: Automatic, probably for older Chips/Firmware.
199363498aa8Spatrick */
199463498aa8Spatrick if (ic->ic_flags & IEEE80211_F_RSNON) {
199563498aa8Spatrick uint32_t wsec = 0;
199663498aa8Spatrick uint32_t wpa = 0;
199763498aa8Spatrick
199863498aa8Spatrick /* tell firmware to add WPA/RSN IE to (re)assoc request */
199963498aa8Spatrick if (ic->ic_bss->ni_rsnprotos == IEEE80211_PROTO_RSN)
200063498aa8Spatrick frm = ieee80211_add_rsn(buf, ic, ic->ic_bss);
200163498aa8Spatrick else
200263498aa8Spatrick frm = ieee80211_add_wpa(buf, ic, ic->ic_bss);
200363498aa8Spatrick bwfm_fwvar_var_set_data(sc, "wpaie", buf, frm - buf);
200463498aa8Spatrick
200563498aa8Spatrick if (ic->ic_rsnprotos & IEEE80211_PROTO_WPA) {
200663498aa8Spatrick if (ic->ic_rsnakms & IEEE80211_AKM_PSK)
200763498aa8Spatrick wpa |= BWFM_WPA_AUTH_WPA_PSK;
200863498aa8Spatrick if (ic->ic_rsnakms & IEEE80211_AKM_8021X)
200963498aa8Spatrick wpa |= BWFM_WPA_AUTH_WPA_UNSPECIFIED;
201063498aa8Spatrick }
201163498aa8Spatrick if (ic->ic_rsnprotos & IEEE80211_PROTO_RSN) {
201263498aa8Spatrick if (ic->ic_rsnakms & IEEE80211_AKM_PSK)
201363498aa8Spatrick wpa |= BWFM_WPA_AUTH_WPA2_PSK;
201463498aa8Spatrick if (ic->ic_rsnakms & IEEE80211_AKM_SHA256_PSK)
201563498aa8Spatrick wpa |= BWFM_WPA_AUTH_WPA2_PSK_SHA256;
201663498aa8Spatrick if (ic->ic_rsnakms & IEEE80211_AKM_8021X)
201763498aa8Spatrick wpa |= BWFM_WPA_AUTH_WPA2_UNSPECIFIED;
201863498aa8Spatrick if (ic->ic_rsnakms & IEEE80211_AKM_SHA256_8021X)
201963498aa8Spatrick wpa |= BWFM_WPA_AUTH_WPA2_1X_SHA256;
202063498aa8Spatrick }
202163498aa8Spatrick if (ic->ic_rsnciphers & IEEE80211_WPA_CIPHER_TKIP ||
202263498aa8Spatrick ic->ic_rsngroupcipher & IEEE80211_WPA_CIPHER_TKIP)
202363498aa8Spatrick wsec |= BWFM_WSEC_TKIP;
202463498aa8Spatrick if (ic->ic_rsnciphers & IEEE80211_WPA_CIPHER_CCMP ||
202563498aa8Spatrick ic->ic_rsngroupcipher & IEEE80211_WPA_CIPHER_CCMP)
202663498aa8Spatrick wsec |= BWFM_WSEC_AES;
202763498aa8Spatrick
202863498aa8Spatrick bwfm_fwvar_var_set_int(sc, "wpa_auth", wpa);
202963498aa8Spatrick bwfm_fwvar_var_set_int(sc, "wsec", wsec);
2030c8e9388cSstsp } else if (ic->ic_flags & IEEE80211_F_WEPON) {
2031c8e9388cSstsp bwfm_fwvar_var_set_int(sc, "wpa_auth", BWFM_WPA_AUTH_DISABLED);
2032c8e9388cSstsp bwfm_fwvar_var_set_int(sc, "wsec", BWFM_WSEC_WEP);
203363498aa8Spatrick } else {
203463498aa8Spatrick bwfm_fwvar_var_set_int(sc, "wpa_auth", BWFM_WPA_AUTH_DISABLED);
203563498aa8Spatrick bwfm_fwvar_var_set_int(sc, "wsec", BWFM_WSEC_NONE);
203663498aa8Spatrick }
203763498aa8Spatrick bwfm_fwvar_var_set_int(sc, "auth", BWFM_AUTH_OPEN);
203863498aa8Spatrick bwfm_fwvar_var_set_int(sc, "mfp", BWFM_MFP_NONE);
203963498aa8Spatrick
20404ba67829Sstsp if (ic->ic_des_esslen && ic->ic_des_esslen <= BWFM_MAX_SSID_LEN) {
204163498aa8Spatrick params = malloc(sizeof(*params), M_TEMP, M_WAITOK | M_ZERO);
204263498aa8Spatrick memcpy(params->ssid.ssid, ic->ic_des_essid, ic->ic_des_esslen);
204363498aa8Spatrick params->ssid.len = htole32(ic->ic_des_esslen);
204402662318Spatrick memcpy(params->assoc.bssid, ic->ic_bss->ni_bssid,
204502662318Spatrick sizeof(params->assoc.bssid));
204663498aa8Spatrick params->scan.scan_type = -1;
204763498aa8Spatrick params->scan.nprobes = htole32(-1);
204863498aa8Spatrick params->scan.active_time = htole32(-1);
204963498aa8Spatrick params->scan.passive_time = htole32(-1);
205063498aa8Spatrick params->scan.home_time = htole32(-1);
205163498aa8Spatrick if (bwfm_fwvar_var_set_data(sc, "join", params, sizeof(*params))) {
205263498aa8Spatrick struct bwfm_join_params join;
205363498aa8Spatrick memset(&join, 0, sizeof(join));
205402662318Spatrick memcpy(join.ssid.ssid, ic->ic_des_essid,
205502662318Spatrick ic->ic_des_esslen);
205663498aa8Spatrick join.ssid.len = htole32(ic->ic_des_esslen);
205702662318Spatrick memcpy(join.assoc.bssid, ic->ic_bss->ni_bssid,
205802662318Spatrick sizeof(join.assoc.bssid));
205963498aa8Spatrick bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_SSID, &join,
206063498aa8Spatrick sizeof(join));
206163498aa8Spatrick }
206263498aa8Spatrick free(params, M_TEMP, sizeof(*params));
206363498aa8Spatrick }
206463498aa8Spatrick }
206563498aa8Spatrick
2066c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
2067c11618f6Spatrick void
bwfm_hostap(struct bwfm_softc * sc)2068c11618f6Spatrick bwfm_hostap(struct bwfm_softc *sc)
2069c11618f6Spatrick {
2070c11618f6Spatrick struct ieee80211com *ic = &sc->sc_ic;
2071c11618f6Spatrick struct ieee80211_node *ni = ic->ic_bss;
2072c11618f6Spatrick struct bwfm_join_params join;
2073c11618f6Spatrick
2074c11618f6Spatrick /*
2075c8e9388cSstsp * OPEN: Open or WEP or WPA/WPA2 on newer Chips/Firmware.
2076c11618f6Spatrick * AUTO: Automatic, probably for older Chips/Firmware.
2077c11618f6Spatrick */
2078c11618f6Spatrick if (ic->ic_flags & IEEE80211_F_RSNON) {
2079c11618f6Spatrick uint32_t wsec = 0;
2080c11618f6Spatrick uint32_t wpa = 0;
2081c11618f6Spatrick
2082c11618f6Spatrick /* TODO: Turn off if replay counter set */
2083c11618f6Spatrick if (ni->ni_rsnprotos & IEEE80211_PROTO_RSN)
2084c11618f6Spatrick bwfm_fwvar_var_set_int(sc, "wme_bss_disable", 1);
2085c11618f6Spatrick
2086c11618f6Spatrick if (ni->ni_rsnprotos & IEEE80211_PROTO_WPA) {
2087c11618f6Spatrick if (ni->ni_rsnakms & IEEE80211_AKM_PSK)
2088c11618f6Spatrick wpa |= BWFM_WPA_AUTH_WPA_PSK;
2089c11618f6Spatrick if (ni->ni_rsnakms & IEEE80211_AKM_8021X)
2090c11618f6Spatrick wpa |= BWFM_WPA_AUTH_WPA_UNSPECIFIED;
2091c11618f6Spatrick }
2092c11618f6Spatrick if (ni->ni_rsnprotos & IEEE80211_PROTO_RSN) {
2093c11618f6Spatrick if (ni->ni_rsnakms & IEEE80211_AKM_PSK)
2094c11618f6Spatrick wpa |= BWFM_WPA_AUTH_WPA2_PSK;
2095c11618f6Spatrick if (ni->ni_rsnakms & IEEE80211_AKM_SHA256_PSK)
2096c11618f6Spatrick wpa |= BWFM_WPA_AUTH_WPA2_PSK_SHA256;
2097c11618f6Spatrick if (ni->ni_rsnakms & IEEE80211_AKM_8021X)
2098c11618f6Spatrick wpa |= BWFM_WPA_AUTH_WPA2_UNSPECIFIED;
2099c11618f6Spatrick if (ni->ni_rsnakms & IEEE80211_AKM_SHA256_8021X)
2100c11618f6Spatrick wpa |= BWFM_WPA_AUTH_WPA2_1X_SHA256;
2101c11618f6Spatrick }
2102c11618f6Spatrick if (ni->ni_rsnciphers & IEEE80211_WPA_CIPHER_TKIP ||
2103c11618f6Spatrick ni->ni_rsngroupcipher & IEEE80211_WPA_CIPHER_TKIP)
2104c11618f6Spatrick wsec |= BWFM_WSEC_TKIP;
2105c11618f6Spatrick if (ni->ni_rsnciphers & IEEE80211_WPA_CIPHER_CCMP ||
2106c11618f6Spatrick ni->ni_rsngroupcipher & IEEE80211_WPA_CIPHER_CCMP)
2107c11618f6Spatrick wsec |= BWFM_WSEC_AES;
2108c11618f6Spatrick
2109c11618f6Spatrick bwfm_fwvar_var_set_int(sc, "wpa_auth", wpa);
2110c11618f6Spatrick bwfm_fwvar_var_set_int(sc, "wsec", wsec);
2111c11618f6Spatrick } else {
2112c11618f6Spatrick bwfm_fwvar_var_set_int(sc, "wpa_auth", BWFM_WPA_AUTH_DISABLED);
2113c11618f6Spatrick bwfm_fwvar_var_set_int(sc, "wsec", BWFM_WSEC_NONE);
2114c11618f6Spatrick }
2115c11618f6Spatrick bwfm_fwvar_var_set_int(sc, "auth", BWFM_AUTH_OPEN);
2116c11618f6Spatrick bwfm_fwvar_var_set_int(sc, "mfp", BWFM_MFP_NONE);
2117c11618f6Spatrick
21181b8f8c22Stobhe bwfm_fwvar_var_set_int(sc, "mpc", 0);
2119c11618f6Spatrick bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_INFRA, 1);
2120c11618f6Spatrick bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_AP, 1);
212173b11a26Spatrick bwfm_fwvar_var_set_int(sc, "chanspec",
212273b11a26Spatrick bwfm_chan2spec(sc, ic->ic_bss->ni_chan));
2123c11618f6Spatrick bwfm_fwvar_cmd_set_int(sc, BWFM_C_UP, 1);
2124c11618f6Spatrick
2125c11618f6Spatrick memset(&join, 0, sizeof(join));
2126c11618f6Spatrick memcpy(join.ssid.ssid, ic->ic_des_essid, ic->ic_des_esslen);
2127c11618f6Spatrick join.ssid.len = htole32(ic->ic_des_esslen);
2128c11618f6Spatrick memset(join.assoc.bssid, 0xff, sizeof(join.assoc.bssid));
2129c11618f6Spatrick bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_SSID, &join, sizeof(join));
2130c11618f6Spatrick bwfm_fwvar_var_set_int(sc, "closednet",
2131eb9f82c0Sstsp (ic->ic_userflags & IEEE80211_F_HIDENWID) != 0);
2132c11618f6Spatrick }
2133c11618f6Spatrick #endif
2134c11618f6Spatrick
213563498aa8Spatrick void
bwfm_scan_v0(struct bwfm_softc * sc)21369b183917Skettenis bwfm_scan_v0(struct bwfm_softc *sc)
213763498aa8Spatrick {
213836cbf554Spatrick struct ieee80211com *ic = &sc->sc_ic;
21399b183917Skettenis struct bwfm_escan_params_v0 *params;
214036cbf554Spatrick uint32_t nssid = 0, nchan = 0;
214136cbf554Spatrick size_t params_size, chan_size, ssid_size;
214236cbf554Spatrick struct bwfm_ssid *ssid;
214332b2494eSpatrick
214436cbf554Spatrick if (ic->ic_flags & IEEE80211_F_ASCAN &&
21454ba67829Sstsp ic->ic_des_esslen && ic->ic_des_esslen <= BWFM_MAX_SSID_LEN)
214636cbf554Spatrick nssid = 1;
214736cbf554Spatrick
214836cbf554Spatrick chan_size = roundup(nchan * sizeof(uint16_t), sizeof(uint32_t));
214936cbf554Spatrick ssid_size = sizeof(struct bwfm_ssid) * nssid;
215036cbf554Spatrick params_size = sizeof(*params) + chan_size + ssid_size;
215132b2494eSpatrick
215232b2494eSpatrick params = malloc(params_size, M_TEMP, M_WAITOK | M_ZERO);
215336cbf554Spatrick ssid = (struct bwfm_ssid *)
215436cbf554Spatrick (((uint8_t *)params) + sizeof(*params) + chan_size);
215536cbf554Spatrick
215632b2494eSpatrick memset(params->scan_params.bssid, 0xff,
215732b2494eSpatrick sizeof(params->scan_params.bssid));
215832b2494eSpatrick params->scan_params.bss_type = 2;
21591d29b516Spatrick params->scan_params.scan_type = BWFM_SCANTYPE_PASSIVE;
216032b2494eSpatrick params->scan_params.nprobes = htole32(-1);
216132b2494eSpatrick params->scan_params.active_time = htole32(-1);
216232b2494eSpatrick params->scan_params.passive_time = htole32(-1);
216332b2494eSpatrick params->scan_params.home_time = htole32(-1);
216432b2494eSpatrick params->version = htole32(BWFM_ESCAN_REQ_VERSION);
216532b2494eSpatrick params->action = htole16(WL_ESCAN_ACTION_START);
216632b2494eSpatrick params->sync_id = htole16(0x1234);
216732b2494eSpatrick
216836cbf554Spatrick if (ic->ic_flags & IEEE80211_F_ASCAN &&
21694ba67829Sstsp ic->ic_des_esslen && ic->ic_des_esslen <= BWFM_MAX_SSID_LEN) {
217036cbf554Spatrick params->scan_params.scan_type = BWFM_SCANTYPE_ACTIVE;
217136cbf554Spatrick ssid->len = htole32(ic->ic_des_esslen);
217236cbf554Spatrick memcpy(ssid->ssid, ic->ic_des_essid, ic->ic_des_esslen);
217336cbf554Spatrick }
217436cbf554Spatrick
217536cbf554Spatrick params->scan_params.channel_num = htole32(
217636cbf554Spatrick nssid << BWFM_CHANNUM_NSSID_SHIFT |
217736cbf554Spatrick nchan << BWFM_CHANNUM_NCHAN_SHIFT);
217836cbf554Spatrick
217932b2494eSpatrick #if 0
218032b2494eSpatrick /* Scan a specific channel */
218132b2494eSpatrick params->scan_params.channel_list[0] = htole16(
218232b2494eSpatrick (1 & 0xff) << 0 |
218332b2494eSpatrick (3 & 0x3) << 8 |
218432b2494eSpatrick (2 & 0x3) << 10 |
218532b2494eSpatrick (2 & 0x3) << 12
218632b2494eSpatrick );
218732b2494eSpatrick #endif
218832b2494eSpatrick
218932b2494eSpatrick bwfm_fwvar_var_set_data(sc, "escan", params, params_size);
219032b2494eSpatrick free(params, M_TEMP, params_size);
219132b2494eSpatrick }
219232b2494eSpatrick
219325440f89Spatrick void
bwfm_scan_v2(struct bwfm_softc * sc)21949b183917Skettenis bwfm_scan_v2(struct bwfm_softc *sc)
219525440f89Spatrick {
21969b183917Skettenis struct ieee80211com *ic = &sc->sc_ic;
21979b183917Skettenis struct bwfm_escan_params_v2 *params;
21989b183917Skettenis uint32_t nssid = 0, nchan = 0;
21999b183917Skettenis size_t params_size, chan_size, ssid_size;
22009b183917Skettenis struct bwfm_ssid *ssid;
22019b183917Skettenis
22029b183917Skettenis if (ic->ic_flags & IEEE80211_F_ASCAN &&
22039b183917Skettenis ic->ic_des_esslen && ic->ic_des_esslen <= BWFM_MAX_SSID_LEN)
22049b183917Skettenis nssid = 1;
22059b183917Skettenis
22069b183917Skettenis chan_size = roundup(nchan * sizeof(uint16_t), sizeof(uint32_t));
22079b183917Skettenis ssid_size = sizeof(struct bwfm_ssid) * nssid;
22089b183917Skettenis params_size = sizeof(*params) + chan_size + ssid_size;
22099b183917Skettenis
22109b183917Skettenis params = malloc(params_size, M_TEMP, M_WAITOK | M_ZERO);
22119b183917Skettenis ssid = (struct bwfm_ssid *)
22129b183917Skettenis (((uint8_t *)params) + sizeof(*params) + chan_size);
22139b183917Skettenis
22149b183917Skettenis params->scan_params.version = 2;
22159b183917Skettenis params->scan_params.length = params_size;
22169b183917Skettenis memset(params->scan_params.bssid, 0xff,
22179b183917Skettenis sizeof(params->scan_params.bssid));
22189b183917Skettenis params->scan_params.bss_type = 2;
22199b183917Skettenis params->scan_params.scan_type = BWFM_SCANTYPE_PASSIVE;
22209b183917Skettenis params->scan_params.nprobes = htole32(-1);
22219b183917Skettenis params->scan_params.active_time = htole32(-1);
22229b183917Skettenis params->scan_params.passive_time = htole32(-1);
22239b183917Skettenis params->scan_params.home_time = htole32(-1);
22249b183917Skettenis params->version = htole32(BWFM_ESCAN_REQ_VERSION_V2);
22259b183917Skettenis params->action = htole16(WL_ESCAN_ACTION_START);
22269b183917Skettenis params->sync_id = htole16(0x1234);
22279b183917Skettenis
22289b183917Skettenis if (ic->ic_flags & IEEE80211_F_ASCAN &&
22299b183917Skettenis ic->ic_des_esslen && ic->ic_des_esslen <= BWFM_MAX_SSID_LEN) {
22309b183917Skettenis params->scan_params.scan_type = BWFM_SCANTYPE_ACTIVE;
22319b183917Skettenis ssid->len = htole32(ic->ic_des_esslen);
22329b183917Skettenis memcpy(ssid->ssid, ic->ic_des_essid, ic->ic_des_esslen);
22339b183917Skettenis }
22349b183917Skettenis
22359b183917Skettenis params->scan_params.channel_num = htole32(
22369b183917Skettenis nssid << BWFM_CHANNUM_NSSID_SHIFT |
22379b183917Skettenis nchan << BWFM_CHANNUM_NCHAN_SHIFT);
22389b183917Skettenis
22399b183917Skettenis #if 0
22409b183917Skettenis /* Scan a specific channel */
22419b183917Skettenis params->scan_params.channel_list[0] = htole16(
22429b183917Skettenis (1 & 0xff) << 0 |
22439b183917Skettenis (3 & 0x3) << 8 |
22449b183917Skettenis (2 & 0x3) << 10 |
22459b183917Skettenis (2 & 0x3) << 12
22469b183917Skettenis );
22479b183917Skettenis #endif
22489b183917Skettenis
22499b183917Skettenis bwfm_fwvar_var_set_data(sc, "escan", params, params_size);
22509b183917Skettenis free(params, M_TEMP, params_size);
22519b183917Skettenis }
22529b183917Skettenis
22539b183917Skettenis void
bwfm_scan(struct bwfm_softc * sc)22549b183917Skettenis bwfm_scan(struct bwfm_softc *sc)
22559b183917Skettenis {
22569b183917Skettenis if (sc->sc_scan_ver == 0)
22579b183917Skettenis bwfm_scan_v0(sc);
22589b183917Skettenis else
22599b183917Skettenis bwfm_scan_v2(sc);
22609b183917Skettenis }
22619b183917Skettenis
22629b183917Skettenis void
bwfm_scan_abort_v0(struct bwfm_softc * sc)22639b183917Skettenis bwfm_scan_abort_v0(struct bwfm_softc *sc)
22649b183917Skettenis {
22659b183917Skettenis struct bwfm_escan_params_v0 *params;
226625440f89Spatrick size_t params_size;
226725440f89Spatrick
226825440f89Spatrick params_size = sizeof(*params) + sizeof(uint16_t);
226925440f89Spatrick params = malloc(params_size, M_TEMP, M_WAITOK | M_ZERO);
227025440f89Spatrick memset(params->scan_params.bssid, 0xff,
227125440f89Spatrick sizeof(params->scan_params.bssid));
227225440f89Spatrick params->scan_params.bss_type = 2;
227325440f89Spatrick params->scan_params.scan_type = BWFM_SCANTYPE_PASSIVE;
227425440f89Spatrick params->scan_params.nprobes = htole32(-1);
227525440f89Spatrick params->scan_params.active_time = htole32(-1);
227625440f89Spatrick params->scan_params.passive_time = htole32(-1);
227725440f89Spatrick params->scan_params.home_time = htole32(-1);
227825440f89Spatrick params->version = htole32(BWFM_ESCAN_REQ_VERSION);
227925440f89Spatrick params->action = htole16(WL_ESCAN_ACTION_START);
228025440f89Spatrick params->sync_id = htole16(0x1234);
228125440f89Spatrick params->scan_params.channel_num = htole32(1);
228225440f89Spatrick params->scan_params.channel_list[0] = htole16(-1);
228325440f89Spatrick bwfm_fwvar_var_set_data(sc, "escan", params, params_size);
228425440f89Spatrick free(params, M_TEMP, params_size);
228525440f89Spatrick }
228625440f89Spatrick
22879b183917Skettenis void
bwfm_scan_abort_v2(struct bwfm_softc * sc)22889b183917Skettenis bwfm_scan_abort_v2(struct bwfm_softc *sc)
22899b183917Skettenis {
22909b183917Skettenis struct bwfm_escan_params_v2 *params;
22919b183917Skettenis size_t params_size;
22929b183917Skettenis
22939b183917Skettenis params_size = sizeof(*params) + sizeof(uint16_t);
22949b183917Skettenis params = malloc(params_size, M_TEMP, M_WAITOK | M_ZERO);
22959b183917Skettenis params->scan_params.version = 2;
22969b183917Skettenis params->scan_params.length = params_size;
22979b183917Skettenis memset(params->scan_params.bssid, 0xff,
22989b183917Skettenis sizeof(params->scan_params.bssid));
22999b183917Skettenis params->scan_params.bss_type = 2;
23009b183917Skettenis params->scan_params.scan_type = BWFM_SCANTYPE_PASSIVE;
23019b183917Skettenis params->scan_params.nprobes = htole32(-1);
23029b183917Skettenis params->scan_params.active_time = htole32(-1);
23039b183917Skettenis params->scan_params.passive_time = htole32(-1);
23049b183917Skettenis params->scan_params.home_time = htole32(-1);
23059b183917Skettenis params->version = htole32(BWFM_ESCAN_REQ_VERSION_V2);
23069b183917Skettenis params->action = htole16(WL_ESCAN_ACTION_START);
23079b183917Skettenis params->sync_id = htole16(0x1234);
23089b183917Skettenis params->scan_params.channel_num = htole32(1);
23099b183917Skettenis params->scan_params.channel_list[0] = htole16(-1);
23109b183917Skettenis bwfm_fwvar_var_set_data(sc, "escan", params, params_size);
23119b183917Skettenis free(params, M_TEMP, params_size);
23129b183917Skettenis }
23139b183917Skettenis
23149b183917Skettenis void
bwfm_scan_abort(struct bwfm_softc * sc)23159b183917Skettenis bwfm_scan_abort(struct bwfm_softc *sc)
23169b183917Skettenis {
23179b183917Skettenis if (sc->sc_scan_ver == 0)
23189b183917Skettenis bwfm_scan_abort_v0(sc);
23199b183917Skettenis else
23209b183917Skettenis bwfm_scan_abort_v2(sc);
23219b183917Skettenis }
23229b183917Skettenis
2323c11618f6Spatrick struct mbuf *
bwfm_newbuf(void)2324c11618f6Spatrick bwfm_newbuf(void)
2325c11618f6Spatrick {
2326c11618f6Spatrick struct mbuf *m;
2327c11618f6Spatrick
2328c11618f6Spatrick MGETHDR(m, M_DONTWAIT, MT_DATA);
2329c11618f6Spatrick if (m == NULL)
2330c11618f6Spatrick return (NULL);
2331c11618f6Spatrick
2332c11618f6Spatrick MCLGET(m, M_DONTWAIT);
2333c11618f6Spatrick if (!(m->m_flags & M_EXT)) {
2334c11618f6Spatrick m_freem(m);
2335c11618f6Spatrick return (NULL);
2336c11618f6Spatrick }
2337c11618f6Spatrick
2338c11618f6Spatrick m->m_len = m->m_pkthdr.len = MCLBYTES;
2339c11618f6Spatrick
2340c11618f6Spatrick return (m);
2341c11618f6Spatrick }
2342c11618f6Spatrick
234332b2494eSpatrick void
bwfm_rx(struct bwfm_softc * sc,struct mbuf * m,struct mbuf_list * ml)23446f241297Spatrick bwfm_rx(struct bwfm_softc *sc, struct mbuf *m, struct mbuf_list *ml)
234532b2494eSpatrick {
234632b2494eSpatrick struct ieee80211com *ic = &sc->sc_ic;
234732b2494eSpatrick struct ifnet *ifp = &ic->ic_if;
2348c11618f6Spatrick struct ieee80211_node *ni;
2349b0079d05Spatrick struct bwfm_event *e;
235032b2494eSpatrick
23519614a7fdSpatrick #ifdef __STRICT_ALIGNMENT
23529614a7fdSpatrick /* Remaining data is an ethernet packet, so align. */
23539614a7fdSpatrick if ((mtod(m, paddr_t) & 0x3) != ETHER_ALIGN) {
23549614a7fdSpatrick struct mbuf *m0;
23559614a7fdSpatrick m0 = m_dup_pkt(m, ETHER_ALIGN, M_WAITOK);
23569614a7fdSpatrick m_freem(m);
23579614a7fdSpatrick if (m0 == NULL) {
23589614a7fdSpatrick ifp->if_ierrors++;
23599614a7fdSpatrick return;
23609614a7fdSpatrick }
23619614a7fdSpatrick m = m0;
23629614a7fdSpatrick }
23639614a7fdSpatrick #endif
23649614a7fdSpatrick
2365b0079d05Spatrick e = mtod(m, struct bwfm_event *);
2366f37fc236Spatrick if (m->m_len >= sizeof(e->ehdr) &&
236732b2494eSpatrick ntohs(e->ehdr.ether_type) == BWFM_ETHERTYPE_LINK_CTL &&
236832b2494eSpatrick memcmp(BWFM_BRCM_OUI, e->hdr.oui, sizeof(e->hdr.oui)) == 0 &&
2369010fb2f1Spatrick ntohs(e->hdr.usr_subtype) == BWFM_BRCM_SUBTYPE_EVENT) {
2370f4e8af02Spatrick bwfm_rx_event(sc, m);
2371010fb2f1Spatrick return;
2372010fb2f1Spatrick }
2373010fb2f1Spatrick
2374010fb2f1Spatrick /* Drop network packets if we are not in RUN state. */
2375010fb2f1Spatrick if (ic->ic_state != IEEE80211_S_RUN) {
2376010fb2f1Spatrick m_freem(m);
2377010fb2f1Spatrick return;
2378010fb2f1Spatrick }
237932b2494eSpatrick
238063498aa8Spatrick if ((ic->ic_flags & IEEE80211_F_RSNON) &&
2381f37fc236Spatrick m->m_len >= sizeof(e->ehdr) &&
238283aa0ba6Sdlg ntohs(e->ehdr.ether_type) == ETHERTYPE_EAPOL) {
238363498aa8Spatrick ifp->if_ipackets++;
238463498aa8Spatrick #if NBPFILTER > 0
238563498aa8Spatrick if (ifp->if_bpf)
238663498aa8Spatrick bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
238763498aa8Spatrick #endif
2388c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
2389c11618f6Spatrick if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
2390c11618f6Spatrick ni = ieee80211_find_node(ic,
2391c11618f6Spatrick (void *)&e->ehdr.ether_shost);
2392c11618f6Spatrick if (ni == NULL) {
2393cb2afc74Spatrick m_freem(m);
2394c11618f6Spatrick return;
2395c11618f6Spatrick }
2396c11618f6Spatrick } else
2397c11618f6Spatrick #endif
2398c11618f6Spatrick ni = ic->ic_bss;
2399c11618f6Spatrick ieee80211_eapol_key_input(ic, m, ni);
24006f241297Spatrick } else
24016f241297Spatrick ml_enqueue(ml, m);
240263498aa8Spatrick }
240332b2494eSpatrick
2404c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
2405c11618f6Spatrick void
bwfm_rx_auth_ind(struct bwfm_softc * sc,struct bwfm_event * e,size_t len)2406c11618f6Spatrick bwfm_rx_auth_ind(struct bwfm_softc *sc, struct bwfm_event *e, size_t len)
2407c11618f6Spatrick {
2408c11618f6Spatrick struct ieee80211com *ic = &sc->sc_ic;
2409c11618f6Spatrick struct ifnet *ifp = &ic->ic_if;
2410c11618f6Spatrick struct ieee80211_rxinfo rxi;
2411c11618f6Spatrick struct ieee80211_frame *wh;
2412c11618f6Spatrick struct mbuf *m;
2413c11618f6Spatrick uint32_t pktlen, ieslen;
2414c11618f6Spatrick
2415c11618f6Spatrick /* Build a fake beacon frame to let net80211 do all the parsing. */
2416c11618f6Spatrick ieslen = betoh32(e->msg.datalen);
2417c11618f6Spatrick pktlen = sizeof(*wh) + ieslen + 6;
2418c11618f6Spatrick if (pktlen > MCLBYTES)
2419c11618f6Spatrick return;
2420c11618f6Spatrick m = bwfm_newbuf();
2421c11618f6Spatrick if (m == NULL)
2422c11618f6Spatrick return;
2423c11618f6Spatrick wh = mtod(m, struct ieee80211_frame *);
2424c11618f6Spatrick wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
2425c11618f6Spatrick IEEE80211_FC0_SUBTYPE_AUTH;
2426c11618f6Spatrick wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
2427c11618f6Spatrick *(uint16_t *)wh->i_dur = 0;
2428c11618f6Spatrick IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
2429c11618f6Spatrick IEEE80211_ADDR_COPY(wh->i_addr2, &e->msg.addr);
2430c11618f6Spatrick IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid);
2431c11618f6Spatrick *(uint16_t *)wh->i_seq = 0;
2432c11618f6Spatrick ((uint16_t *)(&wh[1]))[0] = IEEE80211_AUTH_ALG_OPEN;
2433c11618f6Spatrick ((uint16_t *)(&wh[1]))[1] = IEEE80211_AUTH_OPEN_REQUEST;
2434c11618f6Spatrick ((uint16_t *)(&wh[1]))[2] = 0;
2435c11618f6Spatrick
2436c11618f6Spatrick /* Finalize mbuf. */
2437c11618f6Spatrick m->m_pkthdr.len = m->m_len = pktlen;
243852a13037Sstsp memset(&rxi, 0, sizeof(rxi));
24396605539cSpatrick ieee80211_input(ifp, m, ic->ic_bss, &rxi);
2440c11618f6Spatrick }
2441c11618f6Spatrick
2442c11618f6Spatrick void
bwfm_rx_assoc_ind(struct bwfm_softc * sc,struct bwfm_event * e,size_t len,int reassoc)2443c11618f6Spatrick bwfm_rx_assoc_ind(struct bwfm_softc *sc, struct bwfm_event *e, size_t len,
2444c11618f6Spatrick int reassoc)
2445c11618f6Spatrick {
2446c11618f6Spatrick struct ieee80211com *ic = &sc->sc_ic;
2447c11618f6Spatrick struct ifnet *ifp = &ic->ic_if;
2448c11618f6Spatrick struct ieee80211_rxinfo rxi;
2449c11618f6Spatrick struct ieee80211_frame *wh;
2450c11618f6Spatrick struct ieee80211_node *ni;
2451c11618f6Spatrick struct mbuf *m;
2452c11618f6Spatrick uint32_t pktlen, ieslen;
2453c11618f6Spatrick
2454c11618f6Spatrick /* Build a fake beacon frame to let net80211 do all the parsing. */
2455c11618f6Spatrick ieslen = betoh32(e->msg.datalen);
2456c11618f6Spatrick pktlen = sizeof(*wh) + ieslen + 4;
2457c11618f6Spatrick if (reassoc)
2458c11618f6Spatrick pktlen += IEEE80211_ADDR_LEN;
2459c11618f6Spatrick if (pktlen > MCLBYTES)
2460c11618f6Spatrick return;
2461c11618f6Spatrick m = bwfm_newbuf();
2462c11618f6Spatrick if (m == NULL)
2463c11618f6Spatrick return;
2464c11618f6Spatrick wh = mtod(m, struct ieee80211_frame *);
2465c11618f6Spatrick wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT;
2466c11618f6Spatrick if (reassoc)
2467c11618f6Spatrick wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_REASSOC_REQ;
2468c11618f6Spatrick else
2469c11618f6Spatrick wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_ASSOC_REQ;
2470c11618f6Spatrick wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
2471c11618f6Spatrick *(uint16_t *)wh->i_dur = 0;
2472c11618f6Spatrick IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
2473c11618f6Spatrick IEEE80211_ADDR_COPY(wh->i_addr2, &e->msg.addr);
2474c11618f6Spatrick IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid);
2475c11618f6Spatrick *(uint16_t *)wh->i_seq = 0;
2476c11618f6Spatrick ((uint16_t *)(&wh[1]))[0] = IEEE80211_CAPINFO_ESS; /* XXX */
2477c11618f6Spatrick ((uint16_t *)(&wh[1]))[1] = 100; /* XXX */
2478c11618f6Spatrick if (reassoc) {
247977b542adSpatrick memset(((uint8_t *)&wh[1]) + 4, 0, IEEE80211_ADDR_LEN);
2480c11618f6Spatrick memcpy(((uint8_t *)&wh[1]) + 4 + IEEE80211_ADDR_LEN,
2481c11618f6Spatrick &e[1], ieslen);
2482c11618f6Spatrick } else
2483c11618f6Spatrick memcpy(((uint8_t *)&wh[1]) + 4, &e[1], ieslen);
2484c11618f6Spatrick
2485c11618f6Spatrick /* Finalize mbuf. */
2486c11618f6Spatrick m->m_pkthdr.len = m->m_len = pktlen;
2487c11618f6Spatrick ni = ieee80211_find_node(ic, wh->i_addr2);
2488c11618f6Spatrick if (ni == NULL) {
2489cb2afc74Spatrick m_freem(m);
2490c11618f6Spatrick return;
2491c11618f6Spatrick }
249252a13037Sstsp memset(&rxi, 0, sizeof(rxi));
2493c11618f6Spatrick ieee80211_input(ifp, m, ni, &rxi);
2494c11618f6Spatrick }
2495c11618f6Spatrick
2496c11618f6Spatrick void
bwfm_rx_deauth_ind(struct bwfm_softc * sc,struct bwfm_event * e,size_t len)2497c11618f6Spatrick bwfm_rx_deauth_ind(struct bwfm_softc *sc, struct bwfm_event *e, size_t len)
2498c11618f6Spatrick {
2499c11618f6Spatrick bwfm_rx_leave_ind(sc, e, len, IEEE80211_FC0_SUBTYPE_DEAUTH);
2500c11618f6Spatrick }
2501c11618f6Spatrick
2502c11618f6Spatrick void
bwfm_rx_disassoc_ind(struct bwfm_softc * sc,struct bwfm_event * e,size_t len)2503c11618f6Spatrick bwfm_rx_disassoc_ind(struct bwfm_softc *sc, struct bwfm_event *e, size_t len)
2504c11618f6Spatrick {
2505c11618f6Spatrick bwfm_rx_leave_ind(sc, e, len, IEEE80211_FC0_SUBTYPE_DISASSOC);
2506c11618f6Spatrick }
2507c11618f6Spatrick
2508c11618f6Spatrick void
bwfm_rx_leave_ind(struct bwfm_softc * sc,struct bwfm_event * e,size_t len,int subtype)2509c11618f6Spatrick bwfm_rx_leave_ind(struct bwfm_softc *sc, struct bwfm_event *e, size_t len,
2510c11618f6Spatrick int subtype)
2511c11618f6Spatrick {
2512c11618f6Spatrick struct ieee80211com *ic = &sc->sc_ic;
2513c11618f6Spatrick struct ifnet *ifp = &ic->ic_if;
2514c11618f6Spatrick struct ieee80211_rxinfo rxi;
2515c11618f6Spatrick struct ieee80211_frame *wh;
2516c11618f6Spatrick struct ieee80211_node *ni;
2517c11618f6Spatrick struct mbuf *m;
2518c11618f6Spatrick uint32_t pktlen;
2519c11618f6Spatrick
2520c11618f6Spatrick /* Build a fake beacon frame to let net80211 do all the parsing. */
2521c11618f6Spatrick pktlen = sizeof(*wh) + 2;
2522c11618f6Spatrick if (pktlen > MCLBYTES)
2523c11618f6Spatrick return;
2524c11618f6Spatrick m = bwfm_newbuf();
2525c11618f6Spatrick if (m == NULL)
2526c11618f6Spatrick return;
2527c11618f6Spatrick wh = mtod(m, struct ieee80211_frame *);
2528c11618f6Spatrick wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
2529c11618f6Spatrick subtype;
2530c11618f6Spatrick wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
2531c11618f6Spatrick *(uint16_t *)wh->i_dur = 0;
2532c11618f6Spatrick IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
2533c11618f6Spatrick IEEE80211_ADDR_COPY(wh->i_addr2, &e->msg.addr);
2534c11618f6Spatrick IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid);
2535c11618f6Spatrick *(uint16_t *)wh->i_seq = 0;
253677b542adSpatrick memset((uint8_t *)&wh[1], 0, 2);
2537c11618f6Spatrick
2538c11618f6Spatrick /* Finalize mbuf. */
2539c11618f6Spatrick m->m_pkthdr.len = m->m_len = pktlen;
2540c11618f6Spatrick ni = ieee80211_find_node(ic, wh->i_addr2);
2541c11618f6Spatrick if (ni == NULL) {
2542cb2afc74Spatrick m_freem(m);
2543c11618f6Spatrick return;
2544c11618f6Spatrick }
254552a13037Sstsp memset(&rxi, 0, sizeof(rxi));
2546c11618f6Spatrick ieee80211_input(ifp, m, ni, &rxi);
2547c11618f6Spatrick }
2548c11618f6Spatrick #endif
2549c11618f6Spatrick
255032b2494eSpatrick void
bwfm_rx_event(struct bwfm_softc * sc,struct mbuf * m)2551f4e8af02Spatrick bwfm_rx_event(struct bwfm_softc *sc, struct mbuf *m)
2552f4e8af02Spatrick {
2553625a64d2Spatrick int s;
2554f4e8af02Spatrick
2555560a2bb3Sclaudio s = splnet();
2556625a64d2Spatrick ml_enqueue(&sc->sc_evml, m);
2557625a64d2Spatrick splx(s);
2558625a64d2Spatrick
2559625a64d2Spatrick task_add(sc->sc_taskq, &sc->sc_task);
2560f4e8af02Spatrick }
2561f4e8af02Spatrick
2562f4e8af02Spatrick void
bwfm_rx_event_cb(struct bwfm_softc * sc,struct mbuf * m)2563625a64d2Spatrick bwfm_rx_event_cb(struct bwfm_softc *sc, struct mbuf *m)
256432b2494eSpatrick {
256532b2494eSpatrick struct ieee80211com *ic = &sc->sc_ic;
256663498aa8Spatrick struct ifnet *ifp = &ic->ic_if;
2567f4e8af02Spatrick struct bwfm_event *e = mtod(m, void *);
2568f4e8af02Spatrick size_t len = m->m_len;
256932b2494eSpatrick
2570f4e8af02Spatrick if (ntohl(e->msg.event_type) >= BWFM_E_LAST) {
2571f4e8af02Spatrick m_freem(m);
257232b2494eSpatrick return;
2573f4e8af02Spatrick }
257432b2494eSpatrick
257532b2494eSpatrick switch (ntohl(e->msg.event_type)) {
257632b2494eSpatrick case BWFM_E_ESCAN_RESULT: {
2577cc283629Spatrick struct bwfm_escan_results *res;
257832b2494eSpatrick struct bwfm_bss_info *bss;
2579cc283629Spatrick size_t reslen;
258032b2494eSpatrick int i;
258125440f89Spatrick /* Abort event triggered by SCAN -> INIT */
258225440f89Spatrick if (ic->ic_state == IEEE80211_S_INIT &&
258325440f89Spatrick ntohl(e->msg.status) == BWFM_E_STATUS_ABORT)
258425440f89Spatrick break;
258525440f89Spatrick if (ic->ic_state != IEEE80211_S_SCAN) {
258625440f89Spatrick DPRINTF(("%s: scan result (%u) while not in SCAN\n",
258725440f89Spatrick DEVNAME(sc), ntohl(e->msg.status)));
258825440f89Spatrick break;
258925440f89Spatrick }
259025440f89Spatrick if (ntohl(e->msg.status) != BWFM_E_STATUS_SUCCESS &&
259125440f89Spatrick ntohl(e->msg.status) != BWFM_E_STATUS_PARTIAL) {
259225440f89Spatrick DPRINTF(("%s: unexpected scan result (%u)\n",
259325440f89Spatrick DEVNAME(sc), ntohl(e->msg.status)));
259425440f89Spatrick break;
259525440f89Spatrick }
259625440f89Spatrick if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS) {
259763498aa8Spatrick ieee80211_end_scan(ifp);
259832b2494eSpatrick break;
259932b2494eSpatrick }
260032b2494eSpatrick len -= sizeof(*e);
2601cc283629Spatrick if (len < sizeof(*res)) {
2602029d6dd5Spatrick DPRINTF(("%s: results too small\n", DEVNAME(sc)));
2603f4e8af02Spatrick m_freem(m);
260432b2494eSpatrick return;
260532b2494eSpatrick }
2606cc283629Spatrick reslen = len;
2607cc283629Spatrick res = malloc(len, M_TEMP, M_WAITOK);
2608cc283629Spatrick memcpy(res, (void *)&e[1], len);
2609cc283629Spatrick if (len < letoh32(res->buflen)) {
2610029d6dd5Spatrick DPRINTF(("%s: results too small\n", DEVNAME(sc)));
2611cc283629Spatrick free(res, M_TEMP, reslen);
2612cc283629Spatrick m_freem(m);
2613cc283629Spatrick return;
2614cc283629Spatrick }
261532b2494eSpatrick len -= sizeof(*res);
261632b2494eSpatrick if (len < letoh16(res->bss_count) * sizeof(struct bwfm_bss_info)) {
2617029d6dd5Spatrick DPRINTF(("%s: results too small\n", DEVNAME(sc)));
2618cc283629Spatrick free(res, M_TEMP, reslen);
2619f4e8af02Spatrick m_freem(m);
262032b2494eSpatrick return;
262132b2494eSpatrick }
262232b2494eSpatrick bss = &res->bss_info[0];
262332b2494eSpatrick for (i = 0; i < letoh16(res->bss_count); i++) {
262432b2494eSpatrick bwfm_scan_node(sc, &res->bss_info[i], len);
262532b2494eSpatrick len -= sizeof(*bss) + letoh32(bss->length);
262632b2494eSpatrick bss = (void *)((char *)bss) + letoh32(bss->length);
262732b2494eSpatrick if (len <= 0)
262832b2494eSpatrick break;
262932b2494eSpatrick }
2630cc283629Spatrick free(res, M_TEMP, reslen);
263132b2494eSpatrick break;
263232b2494eSpatrick }
26337a818b26Spatrick case BWFM_E_AUTH:
2634750cbd98Spatrick if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS &&
2635750cbd98Spatrick ic->ic_state == IEEE80211_S_AUTH)
26367a818b26Spatrick ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
263763498aa8Spatrick else
263825440f89Spatrick ieee80211_begin_scan(ifp);
263963498aa8Spatrick break;
264063498aa8Spatrick case BWFM_E_ASSOC:
2641750cbd98Spatrick if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS &&
2642750cbd98Spatrick ic->ic_state == IEEE80211_S_ASSOC)
26437a818b26Spatrick ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
2644e5148d43Spatrick else if (ntohl(e->msg.status) != BWFM_E_STATUS_UNSOLICITED)
264525440f89Spatrick ieee80211_begin_scan(ifp);
264663498aa8Spatrick break;
2647750cbd98Spatrick case BWFM_E_DEAUTH:
2648750cbd98Spatrick case BWFM_E_DISASSOC:
26491f820e28Spatrick if (ic->ic_state > IEEE80211_S_SCAN)
26501f820e28Spatrick ieee80211_begin_scan(ifp);
2651750cbd98Spatrick break;
265263498aa8Spatrick case BWFM_E_LINK:
265363498aa8Spatrick if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS &&
265463498aa8Spatrick ntohl(e->msg.reason) == 0)
265563498aa8Spatrick break;
265663498aa8Spatrick /* Link status has changed */
26571f820e28Spatrick if (ic->ic_state > IEEE80211_S_SCAN)
26581f820e28Spatrick ieee80211_begin_scan(ifp);
265963498aa8Spatrick break;
2660c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
2661c11618f6Spatrick case BWFM_E_AUTH_IND:
2662c11618f6Spatrick bwfm_rx_auth_ind(sc, e, len);
2663c11618f6Spatrick break;
2664c11618f6Spatrick case BWFM_E_ASSOC_IND:
2665c11618f6Spatrick bwfm_rx_assoc_ind(sc, e, len, 0);
2666c11618f6Spatrick break;
2667c11618f6Spatrick case BWFM_E_REASSOC_IND:
2668c11618f6Spatrick bwfm_rx_assoc_ind(sc, e, len, 1);
2669c11618f6Spatrick break;
2670c11618f6Spatrick case BWFM_E_DEAUTH_IND:
2671c11618f6Spatrick bwfm_rx_deauth_ind(sc, e, len);
2672c11618f6Spatrick break;
2673c11618f6Spatrick case BWFM_E_DISASSOC_IND:
2674c11618f6Spatrick bwfm_rx_disassoc_ind(sc, e, len);
2675c11618f6Spatrick break;
2676c11618f6Spatrick #endif
267732b2494eSpatrick default:
2678cd99bb20Spatrick DPRINTF(("%s: len %lu datalen %u code %u status %u"
2679cd99bb20Spatrick " reason %u\n", __func__, len, ntohl(e->msg.datalen),
268032b2494eSpatrick ntohl(e->msg.event_type), ntohl(e->msg.status),
2681cd99bb20Spatrick ntohl(e->msg.reason)));
268232b2494eSpatrick break;
268332b2494eSpatrick }
2684f4e8af02Spatrick
2685f4e8af02Spatrick m_freem(m);
268632b2494eSpatrick }
268732b2494eSpatrick
268832b2494eSpatrick void
bwfm_scan_node(struct bwfm_softc * sc,struct bwfm_bss_info * bss,size_t len)268932b2494eSpatrick bwfm_scan_node(struct bwfm_softc *sc, struct bwfm_bss_info *bss, size_t len)
269032b2494eSpatrick {
269132b2494eSpatrick struct ieee80211com *ic = &sc->sc_ic;
269263498aa8Spatrick struct ifnet *ifp = &ic->ic_if;
269363498aa8Spatrick struct ieee80211_frame *wh;
269432b2494eSpatrick struct ieee80211_node *ni;
26958b2458cfSpatrick struct ieee80211_rxinfo rxi;
269663498aa8Spatrick struct mbuf *m;
269763498aa8Spatrick uint32_t pktlen, ieslen;
269863498aa8Spatrick uint16_t iesoff;
26998b2458cfSpatrick int chanidx;
270032b2494eSpatrick
270163498aa8Spatrick iesoff = letoh16(bss->ie_offset);
270263498aa8Spatrick ieslen = letoh32(bss->ie_length);
270363498aa8Spatrick if (ieslen > len - iesoff)
270463498aa8Spatrick return;
270563498aa8Spatrick
270663498aa8Spatrick /* Build a fake beacon frame to let net80211 do all the parsing. */
270763498aa8Spatrick pktlen = sizeof(*wh) + ieslen + 12;
2708c11618f6Spatrick if (pktlen > MCLBYTES)
270963498aa8Spatrick return;
2710c11618f6Spatrick m = bwfm_newbuf();
2711c11618f6Spatrick if (m == NULL)
271263498aa8Spatrick return;
271363498aa8Spatrick wh = mtod(m, struct ieee80211_frame *);
271463498aa8Spatrick wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
271563498aa8Spatrick IEEE80211_FC0_SUBTYPE_BEACON;
271663498aa8Spatrick wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
271763498aa8Spatrick *(uint16_t *)wh->i_dur = 0;
271863498aa8Spatrick IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
271963498aa8Spatrick IEEE80211_ADDR_COPY(wh->i_addr2, bss->bssid);
272063498aa8Spatrick IEEE80211_ADDR_COPY(wh->i_addr3, bss->bssid);
272163498aa8Spatrick *(uint16_t *)wh->i_seq = 0;
272263498aa8Spatrick memset(&wh[1], 0, 12);
272363498aa8Spatrick ((uint16_t *)(&wh[1]))[4] = bss->beacon_period;
272463498aa8Spatrick ((uint16_t *)(&wh[1]))[5] = bss->capability;
272563498aa8Spatrick memcpy(((uint8_t *)&wh[1]) + 12, ((uint8_t *)bss) + iesoff, ieslen);
272663498aa8Spatrick
272763498aa8Spatrick /* Finalize mbuf. */
272863498aa8Spatrick m->m_pkthdr.len = m->m_len = pktlen;
272963498aa8Spatrick ni = ieee80211_find_rxnode(ic, wh);
27308b2458cfSpatrick /* Channel mask equals IEEE80211_CHAN_MAX */
27318b2458cfSpatrick chanidx = bwfm_spec2chan(sc, letoh32(bss->chanspec));
27328b2458cfSpatrick /* Supply RSSI */
273352a13037Sstsp memset(&rxi, 0, sizeof(rxi));
27346e91f165Spatrick rxi.rxi_rssi = (int16_t)letoh16(bss->rssi);
27357307575aSstsp rxi.rxi_chan = chanidx;
273663498aa8Spatrick ieee80211_input(ifp, m, ni, &rxi);
273763498aa8Spatrick /* Node is no longer needed. */
273863498aa8Spatrick ieee80211_release_node(ic, ni);
273963498aa8Spatrick }
274063498aa8Spatrick
274163498aa8Spatrick void
bwfm_task(void * arg)274263498aa8Spatrick bwfm_task(void *arg)
274363498aa8Spatrick {
274463498aa8Spatrick struct bwfm_softc *sc = arg;
274563498aa8Spatrick struct bwfm_host_cmd_ring *ring = &sc->sc_cmdq;
274663498aa8Spatrick struct bwfm_host_cmd *cmd;
2747625a64d2Spatrick struct mbuf *m;
274863498aa8Spatrick int s;
274963498aa8Spatrick
2750560a2bb3Sclaudio s = splnet();
275163498aa8Spatrick while (ring->next != ring->cur) {
275263498aa8Spatrick cmd = &ring->cmd[ring->next];
275363498aa8Spatrick splx(s);
275463498aa8Spatrick cmd->cb(sc, cmd->data);
2755560a2bb3Sclaudio s = splnet();
275663498aa8Spatrick ring->queued--;
275763498aa8Spatrick ring->next = (ring->next + 1) % BWFM_HOST_CMD_RING_COUNT;
275863498aa8Spatrick }
275963498aa8Spatrick splx(s);
2760625a64d2Spatrick
2761560a2bb3Sclaudio s = splnet();
2762625a64d2Spatrick while ((m = ml_dequeue(&sc->sc_evml)) != NULL) {
2763625a64d2Spatrick splx(s);
2764625a64d2Spatrick bwfm_rx_event_cb(sc, m);
2765560a2bb3Sclaudio s = splnet();
2766625a64d2Spatrick }
2767625a64d2Spatrick splx(s);
276863498aa8Spatrick }
276963498aa8Spatrick
277063498aa8Spatrick void
bwfm_do_async(struct bwfm_softc * sc,void (* cb)(struct bwfm_softc *,void *),void * arg,int len)277163498aa8Spatrick bwfm_do_async(struct bwfm_softc *sc,
277263498aa8Spatrick void (*cb)(struct bwfm_softc *, void *), void *arg, int len)
277363498aa8Spatrick {
277463498aa8Spatrick struct bwfm_host_cmd_ring *ring = &sc->sc_cmdq;
277563498aa8Spatrick struct bwfm_host_cmd *cmd;
277663498aa8Spatrick int s;
277763498aa8Spatrick
2778560a2bb3Sclaudio s = splnet();
2779b72aacb1Spatrick KASSERT(ring->queued < BWFM_HOST_CMD_RING_COUNT);
2780f4e8af02Spatrick if (ring->queued >= BWFM_HOST_CMD_RING_COUNT) {
2781f4e8af02Spatrick splx(s);
2782f4e8af02Spatrick return;
2783f4e8af02Spatrick }
278463498aa8Spatrick cmd = &ring->cmd[ring->cur];
278563498aa8Spatrick cmd->cb = cb;
278663498aa8Spatrick KASSERT(len <= sizeof(cmd->data));
278763498aa8Spatrick memcpy(cmd->data, arg, len);
278863498aa8Spatrick ring->cur = (ring->cur + 1) % BWFM_HOST_CMD_RING_COUNT;
2789f4e8af02Spatrick ring->queued++;
279063498aa8Spatrick task_add(sc->sc_taskq, &sc->sc_task);
279163498aa8Spatrick splx(s);
279263498aa8Spatrick }
279363498aa8Spatrick
279463498aa8Spatrick int
bwfm_send_mgmt(struct ieee80211com * ic,struct ieee80211_node * ni,int type,int arg1,int arg2)279563498aa8Spatrick bwfm_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
279663498aa8Spatrick int type, int arg1, int arg2)
279763498aa8Spatrick {
279863498aa8Spatrick #ifdef BWFM_DEBUG
279963498aa8Spatrick struct bwfm_softc *sc = ic->ic_softc;
280063498aa8Spatrick DPRINTF(("%s: %s\n", DEVNAME(sc), __func__));
280163498aa8Spatrick #endif
280263498aa8Spatrick return 0;
280363498aa8Spatrick }
280463498aa8Spatrick
280563498aa8Spatrick int
bwfm_set_key(struct ieee80211com * ic,struct ieee80211_node * ni,struct ieee80211_key * k)280663498aa8Spatrick bwfm_set_key(struct ieee80211com *ic, struct ieee80211_node *ni,
280763498aa8Spatrick struct ieee80211_key *k)
280863498aa8Spatrick {
280963498aa8Spatrick struct bwfm_softc *sc = ic->ic_softc;
281063498aa8Spatrick struct bwfm_cmd_key cmd;
281163498aa8Spatrick
281263498aa8Spatrick cmd.ni = ni;
281363498aa8Spatrick cmd.k = k;
281463498aa8Spatrick bwfm_do_async(sc, bwfm_set_key_cb, &cmd, sizeof(cmd));
2815cbb32f9cSkrw sc->sc_key_tasks++;
2816cbb32f9cSkrw return EBUSY;
281763498aa8Spatrick }
281863498aa8Spatrick
281963498aa8Spatrick void
bwfm_set_key_cb(struct bwfm_softc * sc,void * arg)282063498aa8Spatrick bwfm_set_key_cb(struct bwfm_softc *sc, void *arg)
282163498aa8Spatrick {
282263498aa8Spatrick struct bwfm_cmd_key *cmd = arg;
282363498aa8Spatrick struct ieee80211_key *k = cmd->k;
282463498aa8Spatrick struct ieee80211_node *ni = cmd->ni;
2825cbb32f9cSkrw struct ieee80211com *ic = &sc->sc_ic;
282663498aa8Spatrick struct bwfm_wsec_key key;
282763498aa8Spatrick uint32_t wsec, wsec_enable;
282863498aa8Spatrick int ext_key = 0;
282963498aa8Spatrick
2830cbb32f9cSkrw sc->sc_key_tasks--;
2831cbb32f9cSkrw
283263498aa8Spatrick if ((k->k_flags & IEEE80211_KEY_GROUP) == 0 &&
283363498aa8Spatrick k->k_cipher != IEEE80211_CIPHER_WEP40 &&
283463498aa8Spatrick k->k_cipher != IEEE80211_CIPHER_WEP104)
283563498aa8Spatrick ext_key = 1;
283663498aa8Spatrick
283763498aa8Spatrick memset(&key, 0, sizeof(key));
2838c11618f6Spatrick if (ext_key && !IEEE80211_IS_MULTICAST(ni->ni_macaddr))
2839c11618f6Spatrick memcpy(key.ea, ni->ni_macaddr, sizeof(key.ea));
284063498aa8Spatrick key.index = htole32(k->k_id);
284163498aa8Spatrick key.len = htole32(k->k_len);
284263498aa8Spatrick memcpy(key.data, k->k_key, sizeof(key.data));
284363498aa8Spatrick if (!ext_key)
284463498aa8Spatrick key.flags = htole32(BWFM_WSEC_PRIMARY_KEY);
284563498aa8Spatrick
284663498aa8Spatrick switch (k->k_cipher) {
284763498aa8Spatrick case IEEE80211_CIPHER_WEP40:
284863498aa8Spatrick key.algo = htole32(BWFM_CRYPTO_ALGO_WEP1);
284963498aa8Spatrick wsec_enable = BWFM_WSEC_WEP;
285063498aa8Spatrick break;
285163498aa8Spatrick case IEEE80211_CIPHER_WEP104:
285263498aa8Spatrick key.algo = htole32(BWFM_CRYPTO_ALGO_WEP128);
285363498aa8Spatrick wsec_enable = BWFM_WSEC_WEP;
285463498aa8Spatrick break;
285563498aa8Spatrick case IEEE80211_CIPHER_TKIP:
285663498aa8Spatrick key.algo = htole32(BWFM_CRYPTO_ALGO_TKIP);
285763498aa8Spatrick wsec_enable = BWFM_WSEC_TKIP;
285863498aa8Spatrick break;
285963498aa8Spatrick case IEEE80211_CIPHER_CCMP:
286063498aa8Spatrick key.algo = htole32(BWFM_CRYPTO_ALGO_AES_CCM);
286163498aa8Spatrick wsec_enable = BWFM_WSEC_AES;
286263498aa8Spatrick break;
286363498aa8Spatrick default:
286463498aa8Spatrick printf("%s: cipher %x not supported\n", DEVNAME(sc),
286563498aa8Spatrick k->k_cipher);
2866cbb32f9cSkrw ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
286763498aa8Spatrick return;
286863498aa8Spatrick }
286963498aa8Spatrick
287018460ee7Sjcs delay(100);
287118460ee7Sjcs
287263498aa8Spatrick bwfm_fwvar_var_set_data(sc, "wsec_key", &key, sizeof(key));
287363498aa8Spatrick bwfm_fwvar_var_get_int(sc, "wsec", &wsec);
2874c8e9388cSstsp wsec &= ~(BWFM_WSEC_WEP | BWFM_WSEC_TKIP | BWFM_WSEC_AES);
287563498aa8Spatrick wsec |= wsec_enable;
287663498aa8Spatrick bwfm_fwvar_var_set_int(sc, "wsec", wsec);
2877cbb32f9cSkrw
2878c8e9388cSstsp if (wsec_enable != BWFM_WSEC_WEP && cmd->ni != NULL &&
2879c8e9388cSstsp sc->sc_key_tasks == 0) {
2880cbb32f9cSkrw DPRINTF(("%s: marking port %s valid\n", DEVNAME(sc),
2881cbb32f9cSkrw ether_sprintf(cmd->ni->ni_macaddr)));
2882cbb32f9cSkrw cmd->ni->ni_port_valid = 1;
2883cbb32f9cSkrw ieee80211_set_link_state(ic, LINK_STATE_UP);
2884cbb32f9cSkrw }
288563498aa8Spatrick }
288663498aa8Spatrick
288763498aa8Spatrick void
bwfm_delete_key(struct ieee80211com * ic,struct ieee80211_node * ni,struct ieee80211_key * k)288863498aa8Spatrick bwfm_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni,
288963498aa8Spatrick struct ieee80211_key *k)
289063498aa8Spatrick {
289163498aa8Spatrick struct bwfm_softc *sc = ic->ic_softc;
289263498aa8Spatrick struct bwfm_cmd_key cmd;
289363498aa8Spatrick
289463498aa8Spatrick cmd.ni = ni;
289563498aa8Spatrick cmd.k = k;
289663498aa8Spatrick bwfm_do_async(sc, bwfm_delete_key_cb, &cmd, sizeof(cmd));
289763498aa8Spatrick }
289863498aa8Spatrick
289963498aa8Spatrick void
bwfm_delete_key_cb(struct bwfm_softc * sc,void * arg)290063498aa8Spatrick bwfm_delete_key_cb(struct bwfm_softc *sc, void *arg)
290163498aa8Spatrick {
290263498aa8Spatrick struct bwfm_cmd_key *cmd = arg;
290363498aa8Spatrick struct ieee80211_key *k = cmd->k;
290463498aa8Spatrick struct bwfm_wsec_key key;
290563498aa8Spatrick
290663498aa8Spatrick memset(&key, 0, sizeof(key));
290763498aa8Spatrick key.index = htole32(k->k_id);
290863498aa8Spatrick key.flags = htole32(BWFM_WSEC_PRIMARY_KEY);
290963498aa8Spatrick bwfm_fwvar_var_set_data(sc, "wsec_key", &key, sizeof(key));
291063498aa8Spatrick }
291163498aa8Spatrick
291263498aa8Spatrick int
bwfm_newstate(struct ieee80211com * ic,enum ieee80211_state nstate,int arg)291363498aa8Spatrick bwfm_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
291463498aa8Spatrick {
291563498aa8Spatrick struct bwfm_softc *sc = ic->ic_softc;
29165d1a01feSstsp struct ifnet *ifp = &ic->ic_if;
291763498aa8Spatrick int s;
291863498aa8Spatrick
291963498aa8Spatrick s = splnet();
292063498aa8Spatrick
292163498aa8Spatrick switch (nstate) {
292225440f89Spatrick case IEEE80211_S_INIT:
292325440f89Spatrick if (ic->ic_state == IEEE80211_S_SCAN)
292425440f89Spatrick bwfm_scan_abort(sc);
292525440f89Spatrick break;
292663498aa8Spatrick case IEEE80211_S_SCAN:
292773b11a26Spatrick #ifndef IEEE80211_STA_ONLY
292873b11a26Spatrick /* Don't start a scan if we already have a channel. */
292973b11a26Spatrick if (ic->ic_state == IEEE80211_S_INIT &&
293073b11a26Spatrick ic->ic_opmode == IEEE80211_M_HOSTAP &&
293173b11a26Spatrick ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
293273b11a26Spatrick break;
293373b11a26Spatrick }
293473b11a26Spatrick #endif
293525440f89Spatrick /* If we tried to connect, abort. */
293625440f89Spatrick if (ic->ic_state > IEEE80211_S_SCAN)
293725440f89Spatrick bwfm_fwvar_cmd_set_data(sc, BWFM_C_DISASSOC, NULL, 0);
293825440f89Spatrick /* Initiate scan. */
293963498aa8Spatrick bwfm_scan(sc);
29405d1a01feSstsp if (ifp->if_flags & IFF_DEBUG)
29415d1a01feSstsp printf("%s: %s -> %s\n", DEVNAME(sc),
29425d1a01feSstsp ieee80211_state_name[ic->ic_state],
29435d1a01feSstsp ieee80211_state_name[nstate]);
2944341abe6fSpatrick /* No need to do this again. */
2945341abe6fSpatrick if (ic->ic_state == IEEE80211_S_SCAN) {
2946341abe6fSpatrick splx(s);
2947341abe6fSpatrick return 0;
2948341abe6fSpatrick }
29492346aae2Sphessler ieee80211_set_link_state(ic, LINK_STATE_DOWN);
2950732f0a59Spatrick ieee80211_free_allnodes(ic, 1);
295163498aa8Spatrick ic->ic_state = nstate;
295263498aa8Spatrick splx(s);
2953f4e8af02Spatrick return 0;
295463498aa8Spatrick case IEEE80211_S_AUTH:
295563498aa8Spatrick ic->ic_bss->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE;
295663498aa8Spatrick bwfm_connect(sc);
29575d1a01feSstsp if (ifp->if_flags & IFF_DEBUG)
29585d1a01feSstsp printf("%s: %s -> %s\n", DEVNAME(sc),
29595d1a01feSstsp ieee80211_state_name[ic->ic_state],
29605d1a01feSstsp ieee80211_state_name[nstate]);
29615d1a01feSstsp ic->ic_state = nstate;
296263498aa8Spatrick if (ic->ic_flags & IEEE80211_F_RSNON)
296363498aa8Spatrick ic->ic_bss->ni_rsn_supp_state = RSNA_SUPP_PTKSTART;
296463498aa8Spatrick splx(s);
2965f4e8af02Spatrick return 0;
2966c11618f6Spatrick #ifndef IEEE80211_STA_ONLY
2967c11618f6Spatrick case IEEE80211_S_RUN:
2968c11618f6Spatrick if (ic->ic_opmode == IEEE80211_M_HOSTAP)
2969c11618f6Spatrick bwfm_hostap(sc);
2970c11618f6Spatrick break;
2971c11618f6Spatrick #endif
297263498aa8Spatrick default:
297363498aa8Spatrick break;
297463498aa8Spatrick }
2975f4e8af02Spatrick sc->sc_newstate(ic, nstate, arg);
297663498aa8Spatrick splx(s);
2977f4e8af02Spatrick return 0;
297832b2494eSpatrick }
2979632f7369Spatrick
2980632f7369Spatrick int
bwfm_nvram_convert(int node,u_char ** bufp,size_t * sizep,size_t * newlenp)2981db31205aSkettenis bwfm_nvram_convert(int node, u_char **bufp, size_t *sizep, size_t *newlenp)
2982632f7369Spatrick {
2983db31205aSkettenis u_char *src, *dst, *end = *bufp + *sizep, *newbuf;
2984db31205aSkettenis size_t count = 0, newsize, pad;
2985632f7369Spatrick uint32_t token;
2986632f7369Spatrick int skip = 0;
2987632f7369Spatrick
2988db31205aSkettenis /*
2989db31205aSkettenis * Allocate a new buffer with enough space for the MAC
2990db31205aSkettenis * address, padding and final token.
2991db31205aSkettenis */
2992db31205aSkettenis newsize = *sizep + 64;
2993db31205aSkettenis newbuf = malloc(newsize, M_DEVBUF, M_NOWAIT);
2994db31205aSkettenis if (newbuf == NULL)
2995db31205aSkettenis return 1;
2996db31205aSkettenis
2997db31205aSkettenis for (src = *bufp, dst = newbuf; src != end; ++src) {
2998632f7369Spatrick if (*src == '\n') {
2999632f7369Spatrick if (count > 0)
3000632f7369Spatrick *dst++ = '\0';
3001632f7369Spatrick count = 0;
3002632f7369Spatrick skip = 0;
3003632f7369Spatrick continue;
3004632f7369Spatrick }
3005632f7369Spatrick if (skip)
3006632f7369Spatrick continue;
3007632f7369Spatrick if (*src == '#' && count == 0) {
3008632f7369Spatrick skip = 1;
3009632f7369Spatrick continue;
3010632f7369Spatrick }
3011632f7369Spatrick if (*src == '\r')
3012632f7369Spatrick continue;
3013632f7369Spatrick *dst++ = *src;
3014632f7369Spatrick ++count;
3015632f7369Spatrick }
3016632f7369Spatrick
3017db31205aSkettenis #if defined(__HAVE_FDT)
3018db31205aSkettenis /*
3019db31205aSkettenis * Append MAC address if one is provided in the device tree.
3020db31205aSkettenis * This is needed on Apple Silicon Macs.
3021db31205aSkettenis */
3022db31205aSkettenis if (node) {
3023db31205aSkettenis u_char enaddr[ETHER_ADDR_LEN];
3024db31205aSkettenis char macaddr[32];
3025632f7369Spatrick
3026db31205aSkettenis if (OF_getprop(node, "local-mac-address",
3027db31205aSkettenis enaddr, sizeof(enaddr))) {
3028db31205aSkettenis snprintf(macaddr, sizeof(macaddr),
3029db31205aSkettenis "macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
3030db31205aSkettenis enaddr[0], enaddr[1], enaddr[2], enaddr[3],
3031db31205aSkettenis enaddr[4], enaddr[5]);
3032db31205aSkettenis if (*dst)
3033db31205aSkettenis *dst++ = '\0';
3034db31205aSkettenis memcpy(dst, macaddr, strlen(macaddr));
3035db31205aSkettenis dst += strlen(macaddr);
3036db31205aSkettenis }
3037db31205aSkettenis }
3038db31205aSkettenis #endif
3039db31205aSkettenis
3040db31205aSkettenis count = dst - newbuf;
3041db31205aSkettenis pad = roundup(count + 1, 4) - count;
3042632f7369Spatrick
3043632f7369Spatrick memset(dst, 0, pad);
3044632f7369Spatrick count += pad;
3045632f7369Spatrick dst += pad;
3046632f7369Spatrick
3047632f7369Spatrick token = (count / 4) & 0xffff;
3048632f7369Spatrick token |= ~token << 16;
3049632f7369Spatrick token = htole32(token);
3050632f7369Spatrick
3051632f7369Spatrick memcpy(dst, &token, sizeof(token));
3052632f7369Spatrick count += sizeof(token);
3053632f7369Spatrick
3054db31205aSkettenis free(*bufp, M_DEVBUF, *sizep);
3055db31205aSkettenis *bufp = newbuf;
3056db31205aSkettenis *sizep = newsize;
3057632f7369Spatrick *newlenp = count;
3058632f7369Spatrick return 0;
3059632f7369Spatrick }
30606c2f85e4Spatrick
30616c2f85e4Spatrick void
bwfm_process_blob(struct bwfm_softc * sc,char * var,u_char ** blob,size_t * blobsize)3062da5a4af2Spatrick bwfm_process_blob(struct bwfm_softc *sc, char *var, u_char **blob,
3063da5a4af2Spatrick size_t *blobsize)
30646c2f85e4Spatrick {
30656c2f85e4Spatrick struct bwfm_dload_data *data;
30666c2f85e4Spatrick size_t off, remain, len;
30676c2f85e4Spatrick
3068da5a4af2Spatrick if (*blob == NULL || *blobsize == 0)
30696c2f85e4Spatrick return;
30706c2f85e4Spatrick
30716c2f85e4Spatrick off = 0;
3072da5a4af2Spatrick remain = *blobsize;
30736c2f85e4Spatrick data = malloc(sizeof(*data) + BWFM_DLOAD_MAX_LEN, M_TEMP, M_WAITOK);
30746c2f85e4Spatrick
30756c2f85e4Spatrick while (remain) {
30766c2f85e4Spatrick len = min(remain, BWFM_DLOAD_MAX_LEN);
30776c2f85e4Spatrick
30786c2f85e4Spatrick data->flag = htole16(BWFM_DLOAD_FLAG_HANDLER_VER_1);
30796c2f85e4Spatrick if (off == 0)
30806c2f85e4Spatrick data->flag |= htole16(BWFM_DLOAD_FLAG_BEGIN);
30810813c1e9Spatrick if (remain <= BWFM_DLOAD_MAX_LEN)
30826c2f85e4Spatrick data->flag |= htole16(BWFM_DLOAD_FLAG_END);
30836c2f85e4Spatrick data->type = htole16(BWFM_DLOAD_TYPE_CLM);
30846c2f85e4Spatrick data->len = htole32(len);
30856c2f85e4Spatrick data->crc = 0;
3086da5a4af2Spatrick memcpy(data->data, *blob + off, len);
30876c2f85e4Spatrick
3088da5a4af2Spatrick if (bwfm_fwvar_var_set_data(sc, var, data,
30896c2f85e4Spatrick sizeof(*data) + len)) {
3090da5a4af2Spatrick printf("%s: could not load blob (%s)\n", DEVNAME(sc),
3091da5a4af2Spatrick var);
30926c2f85e4Spatrick goto out;
30936c2f85e4Spatrick }
30946c2f85e4Spatrick
30956c2f85e4Spatrick off += len;
30966c2f85e4Spatrick remain -= len;
30976c2f85e4Spatrick }
30986c2f85e4Spatrick
30996c2f85e4Spatrick out:
31006c2f85e4Spatrick free(data, M_TEMP, sizeof(*data) + BWFM_DLOAD_MAX_LEN);
3101da5a4af2Spatrick free(*blob, M_DEVBUF, *blobsize);
3102da5a4af2Spatrick *blob = NULL;
3103da5a4af2Spatrick *blobsize = 0;
31046c2f85e4Spatrick }
310594e4747cSpatrick
3106c6a4ab0dSkettenis void
bwfm_init_board_type(struct bwfm_softc * sc)3107c6a4ab0dSkettenis bwfm_init_board_type(struct bwfm_softc *sc)
310894e4747cSpatrick {
3109c6a4ab0dSkettenis #if defined(__HAVE_FDT)
3110c6a4ab0dSkettenis char compat[128];
311194e4747cSpatrick int len;
311294e4747cSpatrick char *p;
311394e4747cSpatrick
3114c6a4ab0dSkettenis len = OF_getprop(OF_peer(0), "compatible", compat, sizeof(compat));
3115c6a4ab0dSkettenis if (len > 0 && len < sizeof(compat)) {
3116c6a4ab0dSkettenis compat[len] = '\0';
3117c6a4ab0dSkettenis if ((p = strchr(compat, '/')) != NULL)
311894e4747cSpatrick *p = '\0';
3119c6a4ab0dSkettenis strlcpy(sc->sc_board_type, compat, sizeof(sc->sc_board_type));
312094e4747cSpatrick }
312194e4747cSpatrick #endif
3122c6a4ab0dSkettenis }
312394e4747cSpatrick
312494e4747cSpatrick int
bwfm_loadfirmware(struct bwfm_softc * sc,const char * chip,const char * bus,u_char ** ucode,size_t * size,u_char ** nvram,size_t * nvsize,size_t * nvlen)312594e4747cSpatrick bwfm_loadfirmware(struct bwfm_softc *sc, const char *chip, const char *bus,
312694e4747cSpatrick u_char **ucode, size_t *size, u_char **nvram, size_t *nvsize, size_t *nvlen)
312794e4747cSpatrick {
3128c6a4ab0dSkettenis const char *board_type = NULL;
312994e4747cSpatrick char name[128];
313094e4747cSpatrick int r;
313194e4747cSpatrick
313294e4747cSpatrick *ucode = *nvram = NULL;
313394e4747cSpatrick *size = *nvsize = *nvlen = 0;
313494e4747cSpatrick
3135c6a4ab0dSkettenis if (strlen(sc->sc_board_type) > 0)
3136c6a4ab0dSkettenis board_type = sc->sc_board_type;
313794e4747cSpatrick
3138c6a4ab0dSkettenis if (board_type != NULL) {
3139ec9994a1Skettenis r = snprintf(name, sizeof(name), "%sbrcmfmac%s%s.%s.bin",
3140ec9994a1Skettenis sc->sc_fwdir, chip, bus, board_type);
314194e4747cSpatrick if ((r > 0 && r < sizeof(name)) &&
314294e4747cSpatrick loadfirmware(name, ucode, size) != 0)
314394e4747cSpatrick *size = 0;
314494e4747cSpatrick }
314594e4747cSpatrick if (*size == 0) {
3146ec9994a1Skettenis snprintf(name, sizeof(name), "%sbrcmfmac%s%s.bin",
3147ec9994a1Skettenis sc->sc_fwdir, chip, bus);
314894e4747cSpatrick if (loadfirmware(name, ucode, size) != 0) {
3149ec9994a1Skettenis snprintf(name, sizeof(name), "%sbrcmfmac%s%s%s%s.bin",
3150ec9994a1Skettenis sc->sc_fwdir, chip, bus, board_type ? "." : "",
3151c6a4ab0dSkettenis board_type ? board_type : "");
315294e4747cSpatrick printf("%s: failed loadfirmware of file %s\n",
315394e4747cSpatrick DEVNAME(sc), name);
315494e4747cSpatrick return 1;
315594e4747cSpatrick }
315694e4747cSpatrick }
315794e4747cSpatrick
315894e4747cSpatrick /* .txt needs to be processed first */
3159c6a4ab0dSkettenis if (strlen(sc->sc_modrev) > 0) {
3160c6a4ab0dSkettenis r = snprintf(name, sizeof(name),
3161ec9994a1Skettenis "%sbrcmfmac%s%s.%s-%s-%s-%s.txt", sc->sc_fwdir, chip, bus,
3162ec9994a1Skettenis board_type, sc->sc_module, sc->sc_vendor, sc->sc_modrev);
3163c6a4ab0dSkettenis if (r > 0 && r < sizeof(name))
3164c6a4ab0dSkettenis loadfirmware(name, nvram, nvsize);
3165c6a4ab0dSkettenis }
3166c6a4ab0dSkettenis if (*nvsize == 0 && strlen(sc->sc_vendor) > 0) {
3167c6a4ab0dSkettenis r = snprintf(name, sizeof(name),
3168ec9994a1Skettenis "%sbrcmfmac%s%s.%s-%s-%s.txt", sc->sc_fwdir, chip, bus,
3169ec9994a1Skettenis board_type, sc->sc_module, sc->sc_vendor);
3170c6a4ab0dSkettenis if (r > 0 && r < sizeof(name))
3171c6a4ab0dSkettenis loadfirmware(name, nvram, nvsize);
3172c6a4ab0dSkettenis }
3173c6a4ab0dSkettenis
3174c6a4ab0dSkettenis if (*nvsize == 0 && board_type != NULL) {
3175ec9994a1Skettenis r = snprintf(name, sizeof(name), "%sbrcmfmac%s%s.%s.txt",
3176ec9994a1Skettenis sc->sc_fwdir, chip, bus, board_type);
3177db31205aSkettenis if (r > 0 && r < sizeof(name))
3178db31205aSkettenis loadfirmware(name, nvram, nvsize);
317994e4747cSpatrick }
318094e4747cSpatrick
3181db31205aSkettenis if (*nvsize == 0) {
3182ec9994a1Skettenis snprintf(name, sizeof(name), "%sbrcmfmac%s%s.txt",
3183ec9994a1Skettenis sc->sc_fwdir, chip, bus);
3184db31205aSkettenis loadfirmware(name, nvram, nvsize);
3185db31205aSkettenis }
3186db31205aSkettenis
3187db31205aSkettenis if (*nvsize != 0) {
3188db31205aSkettenis if (bwfm_nvram_convert(sc->sc_node, nvram, nvsize, nvlen)) {
318994e4747cSpatrick printf("%s: failed to process file %s\n",
319094e4747cSpatrick DEVNAME(sc), name);
319194e4747cSpatrick free(*ucode, M_DEVBUF, *size);
319294e4747cSpatrick free(*nvram, M_DEVBUF, *nvsize);
319394e4747cSpatrick return 1;
319494e4747cSpatrick }
319594e4747cSpatrick }
319694e4747cSpatrick
319794e4747cSpatrick /* .nvram is the pre-processed version */
319894e4747cSpatrick if (*nvlen == 0) {
3199ec9994a1Skettenis snprintf(name, sizeof(name), "%sbrcmfmac%s%s.nvram",
3200ec9994a1Skettenis sc->sc_fwdir, chip, bus);
320194e4747cSpatrick if (loadfirmware(name, nvram, nvsize) == 0)
320294e4747cSpatrick *nvlen = *nvsize;
320394e4747cSpatrick }
320494e4747cSpatrick
320594e4747cSpatrick if (*nvlen == 0 && strcmp(bus, "-sdio") == 0) {
3206ec9994a1Skettenis snprintf(name, sizeof(name), "%sbrcmfmac%s%s%s%s.txt",
3207ec9994a1Skettenis sc->sc_fwdir, chip, bus, board_type ? "." : "",
3208ec9994a1Skettenis board_type ? board_type : "");
320994e4747cSpatrick printf("%s: failed loadfirmware of file %s\n",
321094e4747cSpatrick DEVNAME(sc), name);
321194e4747cSpatrick free(*ucode, M_DEVBUF, *size);
321294e4747cSpatrick return 1;
321394e4747cSpatrick }
321494e4747cSpatrick
3215c6a4ab0dSkettenis if (board_type != NULL) {
3216ec9994a1Skettenis r = snprintf(name, sizeof(name), "%sbrcmfmac%s%s.%s.clm_blob",
3217ec9994a1Skettenis sc->sc_fwdir, chip, bus, board_type);
321894e4747cSpatrick if (r > 0 && r < sizeof(name))
321994e4747cSpatrick loadfirmware(name, &sc->sc_clm, &sc->sc_clmsize);
322094e4747cSpatrick }
322194e4747cSpatrick if (sc->sc_clmsize == 0) {
3222ec9994a1Skettenis snprintf(name, sizeof(name), "%sbrcmfmac%s%s.clm_blob",
3223ec9994a1Skettenis sc->sc_fwdir, chip, bus);
322494e4747cSpatrick loadfirmware(name, &sc->sc_clm, &sc->sc_clmsize);
322594e4747cSpatrick }
322694e4747cSpatrick
3227c6a4ab0dSkettenis if (board_type != NULL) {
3228ec9994a1Skettenis r = snprintf(name, sizeof(name),
3229ec9994a1Skettenis "%sbrcmfmac%s%s.%s.txcap_blob", sc->sc_fwdir,
3230c6a4ab0dSkettenis chip, bus, board_type);
3231da5a4af2Spatrick if (r > 0 && r < sizeof(name))
3232da5a4af2Spatrick loadfirmware(name, &sc->sc_txcap, &sc->sc_txcapsize);
3233da5a4af2Spatrick }
3234da5a4af2Spatrick if (sc->sc_txcapsize == 0) {
3235ec9994a1Skettenis snprintf(name, sizeof(name), "%sbrcmfmac%s%s.txcap_blob",
3236ec9994a1Skettenis sc->sc_fwdir, chip, bus);
3237da5a4af2Spatrick loadfirmware(name, &sc->sc_txcap, &sc->sc_txcapsize);
3238da5a4af2Spatrick }
3239da5a4af2Spatrick
324094e4747cSpatrick return 0;
324194e4747cSpatrick }
3242