summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/amd64/conf/GENERIC4
-rw-r--r--sys/arch/armv7/conf/GENERIC4
-rw-r--r--sys/conf/files6
-rw-r--r--sys/dev/ic/bwfm.c1346
-rw-r--r--sys/dev/ic/bwfmreg.h469
-rw-r--r--sys/dev/ic/bwfmvar.h129
-rw-r--r--sys/dev/sdmmc/files.sdmmc5
-rw-r--r--sys/dev/sdmmc/if_bwfm_sdio.c451
-rw-r--r--sys/dev/usb/files.usb5
-rw-r--r--sys/dev/usb/if_bwfm_usb.c698
-rw-r--r--sys/dev/usb/usbdevs7
11 files changed, 3118 insertions, 6 deletions
diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
index df7a5247e3c..1e0da5b9561 100644
--- a/sys/arch/amd64/conf/GENERIC
+++ b/sys/arch/amd64/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.445 2017/08/28 19:32:53 jasper Exp $
+# $OpenBSD: GENERIC,v 1.446 2017/10/11 17:19:50 patrick Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -308,6 +308,7 @@ video* at utvfu?
audio* at utvfu?
udl* at uhub? # DisplayLink USB displays
wsdisplay* at udl?
+#bwfm* at uhub? # Broadcom FullMAC
puc* at pci? # PCI "universal" communication device
com* at cardbus?
@@ -316,6 +317,7 @@ sdhc* at pci? # SD Host Controller
sdmmc* at sdhc? # SD/MMC bus
rtsx* at pci? # Realtek SD Card Reader
sdmmc* at rtsx? # SD/MMC bus
+#bwfm* at sdmmc? # Broadcom FullMAC
isadma0 at isa?
diff --git a/sys/arch/armv7/conf/GENERIC b/sys/arch/armv7/conf/GENERIC
index da04e636a23..897b8cba811 100644
--- a/sys/arch/armv7/conf/GENERIC
+++ b/sys/arch/armv7/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.96 2017/10/08 07:58:23 kettenis Exp $
+# $OpenBSD: GENERIC,v 1.97 2017/10/11 17:19:50 patrick Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -114,6 +114,7 @@ virtio* at fdt?
sdhc* at fdt?
sdmmc* at sdhc?
+#bwfm* at sdmmc? # Broadcom FullMAC
psci* at fdt?
syscon* at fdt?
@@ -294,6 +295,7 @@ video* at utvfu?
audio* at utvfu?
udl* at uhub? # DisplayLink USB displays
wsdisplay* at udl?
+#bwfm* at uhub? # Broadcom FullMAC
atphy* at mii? # Attansic F1 PHYs
rgephy* at mii? # Realtek 8169S/8110S PHY
diff --git a/sys/conf/files b/sys/conf/files
index db3fc01fa00..03b4a0e24cc 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.652 2017/09/12 08:23:42 mpi Exp $
+# $OpenBSD: files,v 1.653 2017/10/11 17:19:50 patrick Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -387,6 +387,10 @@ file dev/ic/ar9287.c athn
file dev/ic/ar9003.c athn
file dev/ic/ar9380.c athn
+# Broadcom FullMAC 802.11 driver
+device bwfm: ether, ifnet, ifmedia, wlan
+file dev/ic/bwfm.c bwfm
+
# ADMtek ADM8211 802.11
device atw: ether, ifnet, ifmedia, smc93cx6, wlan
file dev/ic/atw.c atw
diff --git a/sys/dev/ic/bwfm.c b/sys/dev/ic/bwfm.c
new file mode 100644
index 00000000000..69f2cc7d2dc
--- /dev/null
+++ b/sys/dev/ic/bwfm.c
@@ -0,0 +1,1346 @@
+/* $OpenBSD: bwfm.c,v 1.1 2017/10/11 17:19:50 patrick Exp $ */
+/*
+ * Copyright (c) 2010-2016 Broadcom Corporation
+ * Copyright (c) 2016,2017 Patrick Wildt <patrick@blueri.se>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <dev/ic/bwfmvar.h>
+#include <dev/ic/bwfmreg.h>
+
+/* #define BWFM_DEBUG */
+#ifdef BWFM_DEBUG
+#define DPRINTF(x) do { if (bwfm_debug > 0) printf x; } while (0)
+#define DPRINTFN(n, x) do { if (bwfm_debug >= (n)) printf x; } while (0)
+static int bwfm_debug = 1;
+#else
+#define DPRINTF(x) do { ; } while (0)
+#define DPRINTFN(n, x) do { ; } while (0)
+#endif
+
+#define DEVNAME(sc) ((sc)->sc_dev.dv_xname)
+
+void bwfm_start(struct ifnet *);
+void bwfm_init(struct ifnet *);
+void bwfm_stop(struct ifnet *);
+void bwfm_watchdog(struct ifnet *);
+int bwfm_ioctl(struct ifnet *, u_long, caddr_t);
+int bwfm_media_change(struct ifnet *);
+void bwfm_media_status(struct ifnet *, struct ifmediareq *);
+
+int bwfm_chip_attach(struct bwfm_softc *);
+int bwfm_chip_detach(struct bwfm_softc *, int);
+struct bwfm_core *bwfm_chip_get_core(struct bwfm_softc *, int);
+struct bwfm_core *bwfm_chip_get_pmu(struct bwfm_softc *);
+int bwfm_chip_ai_isup(struct bwfm_softc *, struct bwfm_core *);
+void bwfm_chip_ai_disable(struct bwfm_softc *, struct bwfm_core *,
+ uint32_t, uint32_t);
+void bwfm_chip_ai_reset(struct bwfm_softc *, struct bwfm_core *,
+ uint32_t, uint32_t, uint32_t);
+void bwfm_chip_dmp_erom_scan(struct bwfm_softc *);
+int bwfm_chip_dmp_get_regaddr(struct bwfm_softc *, uint32_t *,
+ uint32_t *, uint32_t *);
+void bwfm_chip_cr4_set_passive(struct bwfm_softc *);
+void bwfm_chip_ca7_set_passive(struct bwfm_softc *);
+void bwfm_chip_cm3_set_passive(struct bwfm_softc *);
+
+int bwfm_proto_bcdc_query_dcmd(struct bwfm_softc *, int,
+ int, char *, size_t *);
+int bwfm_proto_bcdc_set_dcmd(struct bwfm_softc *, int,
+ int, char *, size_t);
+
+int bwfm_fwvar_cmd_get_data(struct bwfm_softc *, int, void *, size_t);
+int bwfm_fwvar_cmd_set_data(struct bwfm_softc *, int, void *, size_t);
+int bwfm_fwvar_cmd_get_int(struct bwfm_softc *, int, uint32_t *);
+int bwfm_fwvar_cmd_set_int(struct bwfm_softc *, int, uint32_t);
+int bwfm_fwvar_var_get_data(struct bwfm_softc *, char *, void *, size_t);
+int bwfm_fwvar_var_set_data(struct bwfm_softc *, char *, void *, size_t);
+int bwfm_fwvar_var_get_int(struct bwfm_softc *, char *, uint32_t *);
+int bwfm_fwvar_var_set_int(struct bwfm_softc *, char *, uint32_t);
+
+void bwfm_scan(struct bwfm_softc *);
+
+void bwfm_rx(struct bwfm_softc *, char *, size_t);
+void bwfm_rx_event(struct bwfm_softc *, char *, size_t);
+void bwfm_scan_node(struct bwfm_softc *, struct bwfm_bss_info *, size_t);
+
+extern void ieee80211_node2req(struct ieee80211com *,
+ const struct ieee80211_node *, struct ieee80211_nodereq *);
+extern void ieee80211_req2node(struct ieee80211com *,
+ const struct ieee80211_nodereq *, struct ieee80211_node *);
+
+uint8_t bwfm_2ghz_channels[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+};
+uint8_t bwfm_5ghz_channels[] = {
+ 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64, 100, 104, 108, 112,
+ 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165,
+};
+
+struct bwfm_proto_ops bwfm_proto_bcdc_ops = {
+ .proto_query_dcmd = bwfm_proto_bcdc_query_dcmd,
+ .proto_set_dcmd = bwfm_proto_bcdc_set_dcmd,
+};
+
+struct cfdriver bwfm_cd = {
+ NULL, "bwfm", DV_IFNET
+};
+
+void
+bwfm_attach(struct bwfm_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+ uint32_t tmp;
+ int i;
+
+ if (bwfm_fwvar_cmd_get_int(sc, BWFM_C_GET_VERSION, &tmp)) {
+ printf("%s: could not read io type\n", DEVNAME(sc));
+ return;
+ } else
+ sc->sc_io_type = tmp;
+ if (bwfm_fwvar_var_get_data(sc, "cur_etheraddr", ic->ic_myaddr,
+ sizeof(ic->ic_myaddr))) {
+ printf("%s: could not read mac address\n", DEVNAME(sc));
+ return;
+ }
+ printf("%s: address %s\n", DEVNAME(sc), ether_sprintf(ic->ic_myaddr));
+
+ /*
+ * This hardware is supposed to be run with wpa_supplicant, but since
+ * we want to handle basic authentication in our stack, we _need_ to
+ * rely on the firmware's handshake algorithm. If it doesn't exist we
+ * will have to think about something new.
+ */
+ if (bwfm_fwvar_var_get_int(sc, "sup_wpa", &tmp)) {
+ printf("%s: no supplicant in firmware, bailing\n", DEVNAME(sc));
+ return;
+ }
+
+ ic->ic_sup_rates[IEEE80211_MODE_11A] = ieee80211_std_rateset_11a;
+ ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
+ ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g;
+
+ /* IBSS channel undefined for now. */
+ ic->ic_ibss_chan = &ic->ic_channels[0];
+
+ for (i = 0; i < nitems(bwfm_2ghz_channels); i++) {
+ uint8_t chan = bwfm_2ghz_channels[i];
+ ic->ic_channels[chan].ic_freq =
+ ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ);
+ ic->ic_channels[chan].ic_flags =
+ IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
+ IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
+ }
+ for (i = 0; i < nitems(bwfm_5ghz_channels); i++) {
+ uint8_t chan = bwfm_5ghz_channels[i];
+ ic->ic_channels[chan].ic_freq =
+ ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ);
+ ic->ic_channels[chan].ic_flags =
+ IEEE80211_CHAN_A;
+ }
+
+ /* Init some net80211 stuff we abuse. */
+ ieee80211_node_attach(ifp);
+
+ ifp->if_softc = sc;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = bwfm_ioctl;
+ ifp->if_start = bwfm_start;
+ ifp->if_watchdog = bwfm_watchdog;
+ memcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ);
+
+ ifmedia_init(&sc->sc_media, 0, bwfm_media_change, bwfm_media_status);
+ ifmedia_set(&sc->sc_media,
+ IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, 0));
+ if_attach(ifp);
+ memcpy(((struct arpcom *)ifp)->ac_enaddr, ic->ic_myaddr,
+ ETHER_ADDR_LEN);
+ ether_ifattach(ifp);
+}
+
+int
+bwfm_detach(struct bwfm_softc *sc, int flags)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+ ether_ifdetach(ifp);
+ if_detach(ifp);
+ return 0;
+}
+
+void
+bwfm_start(struct ifnet *ifp)
+{
+ struct bwfm_softc *sc = ifp->if_softc;
+ struct mbuf *m;
+ int error;
+
+ if (!(ifp->if_flags & IFF_RUNNING))
+ return;
+ if (ifq_is_oactive(&ifp->if_snd))
+ return;
+ if (IFQ_IS_EMPTY(&ifp->if_snd))
+ return;
+
+ /* TODO: return if no link? */
+
+ m = ifq_deq_begin(&ifp->if_snd);
+ while (m != NULL) {
+ error = sc->sc_bus_ops->bs_txdata(sc, m);
+ if (error == ENOBUFS) {
+ ifq_deq_rollback(&ifp->if_snd, m);
+ ifq_set_oactive(&ifp->if_snd);
+ break;
+ }
+ if (error == EFBIG) {
+ ifq_deq_commit(&ifp->if_snd, m);
+ m_freem(m); /* give up: drop it */
+ ifp->if_oerrors++;
+ continue;
+ }
+
+ /* Now we are committed to transmit the packet. */
+ ifq_deq_commit(&ifp->if_snd, m);
+
+#if NBPFILTER > 0
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
+#endif
+
+ m = ifq_deq_begin(&ifp->if_snd);
+ }
+}
+
+void
+bwfm_init(struct ifnet *ifp)
+{
+ struct bwfm_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint8_t evmask[BWFM_EVENT_MASK_LEN];
+ struct bwfm_ext_join_params *params;
+
+ if (bwfm_fwvar_var_set_int(sc, "mpc", 1)) {
+ printf("%s: could not set mpc\n", DEVNAME(sc));
+ return;
+ }
+
+ /* TODO: joinpref defaults */
+
+ if (bwfm_fwvar_var_get_data(sc, "event_msgs", evmask, sizeof(evmask))) {
+ printf("%s: could not get event mask\n", DEVNAME(sc));
+ return;
+ }
+ evmask[BWFM_E_IF / 8] |= 1 << (BWFM_E_IF % 8);
+ evmask[BWFM_E_ESCAN_RESULT / 8] |= 1 << (BWFM_E_ESCAN_RESULT % 8);
+ evmask[BWFM_E_SET_SSID / 8] |= 1 << (BWFM_E_SET_SSID % 8);
+ if (bwfm_fwvar_var_set_data(sc, "event_msgs", evmask, sizeof(evmask))) {
+ printf("%s: could not set event mask\n", DEVNAME(sc));
+ return;
+ }
+
+ if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_CHANNEL_TIME,
+ BWFM_DEFAULT_SCAN_CHANNEL_TIME)) {
+ printf("%s: could not set scan channel time\n", DEVNAME(sc));
+ return;
+ }
+ if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_UNASSOC_TIME,
+ BWFM_DEFAULT_SCAN_UNASSOC_TIME)) {
+ printf("%s: could not set scan unassoc time\n", DEVNAME(sc));
+ return;
+ }
+ if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_SCAN_PASSIVE_TIME,
+ BWFM_DEFAULT_SCAN_PASSIVE_TIME)) {
+ printf("%s: could not set scan passive time\n", DEVNAME(sc));
+ return;
+ }
+
+ if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, 2)) {
+ printf("%s: could not set power\n", DEVNAME(sc));
+ return;
+ }
+
+ bwfm_fwvar_var_set_int(sc, "txbf", 1);
+ bwfm_fwvar_cmd_set_int(sc, BWFM_C_UP, 0);
+ bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_INFRA, 1);
+ bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_AP, 0);
+
+ /* Disable all offloading (ARP, NDP, TCP/UDP cksum). */
+ bwfm_fwvar_var_set_int(sc, "arp_ol", 0);
+ bwfm_fwvar_var_set_int(sc, "arpoe", 0);
+ bwfm_fwvar_var_set_int(sc, "ndoe", 0);
+ bwfm_fwvar_var_set_int(sc, "toe", 0);
+
+ bwfm_fwvar_var_set_int(sc, "mfp", 0); /* NONE */
+ bwfm_fwvar_var_set_int(sc, "sup_wpa", 1);
+ if (ic->ic_flags & IEEE80211_F_PSK) {
+ struct bwfm_wsec_pmk pmk;
+ int i;
+
+ pmk.key_len = htole16(sizeof(ic->ic_psk) << 1);
+ pmk.flags = htole16(BWFM_WSEC_PASSPHRASE);
+ for (i = 0; i < 32; i++)
+ snprintf(&pmk.key[2 * i], 3, "%02x",
+ ic->ic_psk[i]);
+ bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_WSEC_PMK, &pmk,
+ sizeof(pmk));
+
+ bwfm_fwvar_var_set_int(sc, "wpa_auth", 0x80|0x40); /* WPA2|UNSPEC */
+ //bwfm_fwvar_var_set_int(sc, "wpa_auth", 0x4|0x2); /* WPA|UNSPEC */
+ bwfm_fwvar_var_set_int(sc, "wsec", 0x4|0x2); /* AES|TKIP */
+ //bwfm_fwvar_var_set_int(sc, "auth", 0); /* OPEN */
+ //bwfm_fwvar_var_set_int(sc, "auth", 1); /* SHARED KEY */
+ bwfm_fwvar_var_set_int(sc, "auth", 2); /* AUTO */
+ } else {
+ bwfm_fwvar_var_set_int(sc, "wpa_auth", 0); /* WPA DISABLED */
+ bwfm_fwvar_var_set_int(sc, "wsec", 0); /* NO CIPHERS */
+ bwfm_fwvar_var_set_int(sc, "auth", 0); /* OPEN */
+ }
+
+ if (ic->ic_des_esslen && ic->ic_des_esslen < BWFM_MAX_SSID_LEN) {
+ params = malloc(sizeof(*params), M_TEMP, M_WAITOK | M_ZERO);
+ memcpy(params->ssid.ssid, ic->ic_des_essid, ic->ic_des_esslen);
+ params->ssid.len = htole32(ic->ic_des_esslen);
+ memset(params->assoc.bssid, 0xff, sizeof(params->assoc.bssid));
+ params->scan.scan_type = -1;
+ params->scan.nprobes = htole32(-1);
+ params->scan.active_time = htole32(-1);
+ params->scan.passive_time = htole32(-1);
+ params->scan.home_time = htole32(-1);
+ if (bwfm_fwvar_var_set_data(sc, "join", params, sizeof(*params))) {
+ struct bwfm_join_params join;
+ memset(&join, 0, sizeof(join));
+ memcpy(join.ssid.ssid, ic->ic_des_essid, ic->ic_des_esslen);
+ join.ssid.len = htole32(ic->ic_des_esslen);
+ memset(join.assoc.bssid, 0xff, sizeof(join.assoc.bssid));
+ bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_SSID, &join,
+ sizeof(join));
+ }
+ free(params, M_TEMP, sizeof(*params));
+ }
+
+ /* XXX: added for testing only, remove */
+ bwfm_fwvar_var_set_int(sc, "allmulti", 1);
+ bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PROMISC, 1);
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifq_clr_oactive(&ifp->if_snd);
+
+ if (ic->ic_scan_lock == IEEE80211_SCAN_UNLOCKED) {
+ ieee80211_clean_cached(ic);
+ bwfm_scan(sc);
+ }
+}
+
+void
+bwfm_stop(struct ifnet *ifp)
+{
+ struct bwfm_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ sc->sc_tx_timer = 0;
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~IFF_RUNNING;
+ ifq_clr_oactive(&ifp->if_snd);
+
+ /* In case we were scanning, release the scan "lock". */
+ ic->ic_scan_lock = IEEE80211_SCAN_UNLOCKED;
+
+ bwfm_fwvar_cmd_set_int(sc, BWFM_C_DOWN, 1);
+ bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, 0);
+}
+
+void
+bwfm_watchdog(struct ifnet *ifp)
+{
+ struct bwfm_softc *sc = ifp->if_softc;
+
+ ifp->if_timer = 0;
+
+ if (sc->sc_tx_timer > 0) {
+ if (--sc->sc_tx_timer == 0) {
+ printf("%s: device timeout\n", DEVNAME(sc));
+ ifp->if_oerrors++;
+ return;
+ }
+ ifp->if_timer = 1;
+ }
+ ieee80211_watchdog(ifp);
+}
+
+int
+bwfm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct bwfm_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_nwid nwid;
+ struct ieee80211_wpapsk *psk;
+ struct ieee80211_nodereq *nr, nrbuf;
+ struct ieee80211_nodereq_all *na;
+ struct ieee80211_node *ni;
+ int s, i, error = 0;
+ uint32_t flags;
+
+ s = splnet();
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ bwfm_init(ifp);
+ break;
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (!(ifp->if_flags & IFF_RUNNING))
+ bwfm_init(ifp);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ bwfm_stop(ifp);
+ }
+ break;
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
+ break;
+ case SIOCSIFMTU:
+ ifr = (struct ifreq *)data;
+ if (!(IEEE80211_MTU_MIN <= ifr->ifr_mtu &&
+ ifr->ifr_mtu <= IEEE80211_MTU_MAX))
+ error = EINVAL;
+ else
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+ case SIOCG80211ALLNODES:
+ na = (struct ieee80211_nodereq_all *)data;
+ na->na_nodes = i = 0;
+ ni = RBT_MIN(ieee80211_tree, &ic->ic_tree);
+ while (ni && na->na_size >=
+ i + sizeof(struct ieee80211_nodereq)) {
+ ieee80211_node2req(ic, ni, &nrbuf);
+ error = copyout(&nrbuf, (caddr_t)na->na_node + i,
+ sizeof(struct ieee80211_nodereq));
+ if (error)
+ break;
+ i += sizeof(struct ieee80211_nodereq);
+ na->na_nodes++;
+ ni = RBT_NEXT(ieee80211_tree, ni);
+ }
+ break;
+ case SIOCG80211FLAGS:
+ flags = ic->ic_flags;
+#ifndef IEEE80211_STA_ONLY
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+#endif
+ flags &= ~IEEE80211_F_HOSTAPMASK;
+ ifr->ifr_flags = flags >> IEEE80211_F_USERSHIFT;
+ break;
+ case SIOCS80211FLAGS:
+ if ((error = suser(curproc, 0)) != 0)
+ break;
+ flags = (u_int32_t)ifr->ifr_flags << IEEE80211_F_USERSHIFT;
+ if (
+#ifndef IEEE80211_STA_ONLY
+ ic->ic_opmode != IEEE80211_M_HOSTAP &&
+#endif
+ (flags & IEEE80211_F_HOSTAPMASK)) {
+ error = EINVAL;
+ break;
+ }
+ ic->ic_flags = (ic->ic_flags & ~IEEE80211_F_USERMASK) | flags;
+ error = ENETRESET;
+ break;
+ case SIOCG80211NODE:
+ nr = (struct ieee80211_nodereq *)data;
+ ni = ieee80211_find_node(ic, nr->nr_macaddr);
+ if (ni == NULL) {
+ error = ENOENT;
+ break;
+ }
+ ieee80211_node2req(ic, ni, nr);
+ break;
+ case SIOCS80211NODE:
+ if ((error = suser(curproc, 0)) != 0)
+ break;
+#ifndef IEEE80211_STA_ONLY
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ error = EINVAL;
+ break;
+ }
+#endif
+ nr = (struct ieee80211_nodereq *)data;
+
+ ni = ieee80211_find_node(ic, nr->nr_macaddr);
+ if (ni == NULL)
+ ni = ieee80211_alloc_node(ic, nr->nr_macaddr);
+ if (ni == NULL) {
+ error = ENOENT;
+ break;
+ }
+
+ if (nr->nr_flags & IEEE80211_NODEREQ_COPY)
+ ieee80211_req2node(ic, nr, ni);
+ break;
+ case SIOCS80211NWID:
+ if ((error = suser(curproc, 0)) != 0)
+ break;
+ if ((error = copyin(ifr->ifr_data, &nwid, sizeof(nwid))) != 0)
+ break;
+ if (nwid.i_len > IEEE80211_NWID_LEN) {
+ error = EINVAL;
+ break;
+ }
+ memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN);
+ ic->ic_des_esslen = nwid.i_len;
+ memcpy(ic->ic_des_essid, nwid.i_nwid, nwid.i_len);
+ error = ENETRESET;
+ break;
+ case SIOCG80211NWID:
+ memset(&nwid, 0, sizeof(nwid));
+ nwid.i_len = ic->ic_des_esslen;
+ memcpy(nwid.i_nwid, ic->ic_des_essid, nwid.i_len);
+ error = copyout(&nwid, ifr->ifr_data, sizeof(nwid));
+ break;
+ case SIOCS80211SCAN:
+ if ((error = suser(curproc, 0)) != 0)
+ break;
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+ break;
+ if (ic->ic_scan_lock == IEEE80211_SCAN_UNLOCKED) {
+ ieee80211_clean_cached(ic);
+ bwfm_scan(sc);
+ }
+ /* Let the userspace process wait for completion */
+ error = tsleep(&ic->ic_scan_lock, PCATCH, "80211scan",
+ hz * IEEE80211_SCAN_TIMEOUT);
+ break;
+ case SIOCS80211WPAPARMS:
+ case SIOCG80211WPAPARMS:
+ break;
+ case SIOCS80211WPAPSK:
+ if ((error = suser(curproc, 0)) != 0)
+ break;
+ psk = (struct ieee80211_wpapsk *)data;
+ if (psk->i_enabled) {
+ ic->ic_flags |= IEEE80211_F_PSK;
+ memcpy(ic->ic_psk, psk->i_psk, sizeof(ic->ic_psk));
+ } else {
+ ic->ic_flags &= ~IEEE80211_F_PSK;
+ memset(ic->ic_psk, 0, sizeof(ic->ic_psk));
+ }
+ error = ENETRESET;
+ break;
+ case SIOCG80211WPAPSK:
+ psk = (struct ieee80211_wpapsk *)data;
+ if (ic->ic_flags & IEEE80211_F_PSK) {
+ psk->i_enabled = 1;
+ /* do not show any keys to non-root user */
+ if (suser(curproc, 0) != 0) {
+ psk->i_enabled = 2;
+ memset(psk->i_psk, 0, sizeof(psk->i_psk));
+ break; /* return ok but w/o key */
+ }
+ memcpy(psk->i_psk, ic->ic_psk, sizeof(psk->i_psk));
+ } else
+ psk->i_enabled = 0;
+ break;
+ case SIOCS80211NWKEY:
+ case SIOCG80211NWKEY:
+ case SIOCS80211WMMPARMS:
+ case SIOCG80211WMMPARMS:
+ case SIOCS80211KEYAVAIL:
+ case SIOCS80211KEYRUN:
+ case SIOCS80211POWER:
+ case SIOCG80211POWER:
+ case SIOCS80211BSSID:
+ case SIOCG80211BSSID:
+ case SIOCS80211CHANNEL:
+ case SIOCG80211CHANNEL:
+ case SIOCG80211ALLCHANS:
+ case SIOCG80211STATS:
+ case SIOCS80211TXPOWER:
+ case SIOCG80211TXPOWER:
+ case SIOCS80211DELNODE:
+ default:
+ error = ether_ioctl(ifp, &sc->sc_ic.ic_ac, cmd, data);
+ }
+ if (error == ENETRESET) {
+ if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
+ (IFF_UP | IFF_RUNNING)) {
+ bwfm_stop(ifp);
+ bwfm_init(ifp);
+ }
+ error = 0;
+ }
+ splx(s);
+ return error;
+}
+
+int
+bwfm_media_change(struct ifnet *ifp)
+{
+ return 0;
+}
+
+void
+bwfm_media_status(struct ifnet *ifp, struct ifmediareq *imr)
+{
+}
+
+/* Chip initialization (SDIO, PCIe) */
+int
+bwfm_chip_attach(struct bwfm_softc *sc)
+{
+ struct bwfm_core *core;
+ int need_socram = 0;
+ int has_socram = 0;
+ int cpu_found = 0;
+ uint32_t val;
+
+ LIST_INIT(&sc->sc_chip.ch_list);
+
+ if (sc->sc_buscore_ops->bc_prepare(sc) != 0) {
+ printf("%s: failed buscore prepare\n", DEVNAME(sc));
+ return 1;
+ }
+
+ val = sc->sc_buscore_ops->bc_read(sc,
+ BWFM_CHIP_BASE + BWFM_CHIP_REG_CHIPID);
+ sc->sc_chip.ch_chip = BWFM_CHIP_CHIPID_ID(val);
+ sc->sc_chip.ch_chiprev = BWFM_CHIP_CHIPID_REV(val);
+
+ if ((sc->sc_chip.ch_chip > 0xa000) || (sc->sc_chip.ch_chip < 0x4000))
+ snprintf(sc->sc_chip.ch_name, sizeof(sc->sc_chip.ch_name),
+ "%d", sc->sc_chip.ch_chip);
+ else
+ snprintf(sc->sc_chip.ch_name, sizeof(sc->sc_chip.ch_name),
+ "%x", sc->sc_chip.ch_chip);
+
+ switch (BWFM_CHIP_CHIPID_TYPE(val))
+ {
+ case BWFM_CHIP_CHIPID_TYPE_SOCI_SB:
+ printf("%s: SoC interconnect SB not implemented\n",
+ DEVNAME(sc));
+ return 1;
+ case BWFM_CHIP_CHIPID_TYPE_SOCI_AI:
+ sc->sc_chip.ch_core_isup = bwfm_chip_ai_isup;
+ sc->sc_chip.ch_core_disable = bwfm_chip_ai_disable;
+ sc->sc_chip.ch_core_reset = bwfm_chip_ai_reset;
+ bwfm_chip_dmp_erom_scan(sc);
+ break;
+ default:
+ printf("%s: SoC interconnect %d unknown\n",
+ DEVNAME(sc), BWFM_CHIP_CHIPID_TYPE(val));
+ return 1;
+ }
+
+ LIST_FOREACH(core, &sc->sc_chip.ch_list, co_link) {
+ DPRINTF(("%s: 0x%x:%-2d base 0x%08x wrap 0x%08x\n",
+ DEVNAME(sc), core->co_id, core->co_rev,
+ core->co_base, core->co_wrapbase));
+
+ switch (core->co_id) {
+ case BWFM_AGENT_CORE_ARM_CM3:
+ need_socram = true;
+ /* FALLTHROUGH */
+ case BWFM_AGENT_CORE_ARM_CR4:
+ case BWFM_AGENT_CORE_ARM_CA7:
+ cpu_found = true;
+ break;
+ case BWFM_AGENT_INTERNAL_MEM:
+ has_socram = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!cpu_found) {
+ printf("%s: CPU core not detected\n", DEVNAME(sc));
+ return 1;
+ }
+ if (need_socram && !has_socram) {
+ printf("%s: RAM core not provided\n", DEVNAME(sc));
+ return 1;
+ }
+
+ if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL)
+ bwfm_chip_cr4_set_passive(sc);
+ if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL)
+ bwfm_chip_ca7_set_passive(sc);
+ if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL)
+ bwfm_chip_cm3_set_passive(sc);
+
+ if (sc->sc_buscore_ops->bc_reset) {
+ sc->sc_buscore_ops->bc_reset(sc);
+ if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL)
+ bwfm_chip_cr4_set_passive(sc);
+ if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL)
+ bwfm_chip_ca7_set_passive(sc);
+ if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL)
+ bwfm_chip_cm3_set_passive(sc);
+ }
+
+ /* TODO: get raminfo */
+
+ core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
+ sc->sc_chip.ch_cc_caps = sc->sc_buscore_ops->bc_read(sc,
+ core->co_base + BWFM_CHIP_REG_CAPABILITIES);
+ sc->sc_chip.ch_cc_caps_ext = sc->sc_buscore_ops->bc_read(sc,
+ core->co_base + BWFM_CHIP_REG_CAPABILITIES_EXT);
+
+ core = bwfm_chip_get_pmu(sc);
+ if (sc->sc_chip.ch_cc_caps & BWFM_CHIP_REG_CAPABILITIES_PMU) {
+ sc->sc_chip.ch_pmucaps = sc->sc_buscore_ops->bc_read(sc,
+ core->co_base + BWFM_CHIP_REG_PMUCAPABILITIES);
+ sc->sc_chip.ch_pmurev = sc->sc_chip.ch_pmucaps &
+ BWFM_CHIP_REG_PMUCAPABILITIES_REV_MASK;
+ }
+
+ if (sc->sc_buscore_ops->bc_setup)
+ sc->sc_buscore_ops->bc_setup(sc);
+
+ return 0;
+}
+
+struct bwfm_core *
+bwfm_chip_get_core(struct bwfm_softc *sc, int id)
+{
+ struct bwfm_core *core;
+
+ LIST_FOREACH(core, &sc->sc_chip.ch_list, co_link) {
+ if (core->co_id == id)
+ return core;
+ }
+
+ return NULL;
+}
+
+struct bwfm_core *
+bwfm_chip_get_pmu(struct bwfm_softc *sc)
+{
+ struct bwfm_core *cc, *pmu;
+
+ cc = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
+ if (cc->co_rev >= 35 && sc->sc_chip.ch_cc_caps_ext &
+ BWFM_CHIP_REG_CAPABILITIES_EXT_AOB_PRESENT) {
+ pmu = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_PMU);
+ if (pmu)
+ return pmu;
+ }
+
+ return cc;
+}
+
+/* Functions for the AI interconnect */
+int
+bwfm_chip_ai_isup(struct bwfm_softc *sc, struct bwfm_core *core)
+{
+ uint32_t ioctl, reset;
+
+ ioctl = sc->sc_buscore_ops->bc_read(sc,
+ core->co_wrapbase + BWFM_AGENT_IOCTL);
+ reset = sc->sc_buscore_ops->bc_read(sc,
+ core->co_wrapbase + BWFM_AGENT_RESET_CTL);
+
+ if (((ioctl & (BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK)) ==
+ BWFM_AGENT_IOCTL_CLK) &&
+ ((reset & BWFM_AGENT_RESET_CTL_RESET) == 0))
+ return 1;
+
+ return 0;
+}
+
+void
+bwfm_chip_ai_disable(struct bwfm_softc *sc, struct bwfm_core *core,
+ uint32_t prereset, uint32_t reset)
+{
+ uint32_t val;
+ int i;
+
+ val = sc->sc_buscore_ops->bc_read(sc,
+ core->co_wrapbase + BWFM_AGENT_RESET_CTL);
+ if ((val & BWFM_AGENT_RESET_CTL_RESET) == 0) {
+
+ sc->sc_buscore_ops->bc_write(sc,
+ core->co_wrapbase + BWFM_AGENT_IOCTL,
+ prereset | BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK);
+ sc->sc_buscore_ops->bc_read(sc,
+ core->co_wrapbase + BWFM_AGENT_IOCTL);
+
+ sc->sc_buscore_ops->bc_write(sc,
+ core->co_wrapbase + BWFM_AGENT_RESET_CTL,
+ BWFM_AGENT_RESET_CTL_RESET);
+ delay(20);
+
+ for (i = 300; i > 0; i--) {
+ if (sc->sc_buscore_ops->bc_read(sc,
+ core->co_wrapbase + BWFM_AGENT_RESET_CTL) ==
+ BWFM_AGENT_RESET_CTL_RESET)
+ break;
+ }
+ if (i == 0)
+ printf("%s: timeout on core reset\n", DEVNAME(sc));
+ }
+
+ sc->sc_buscore_ops->bc_write(sc,
+ core->co_wrapbase + BWFM_AGENT_IOCTL,
+ reset | BWFM_AGENT_IOCTL_FGC | BWFM_AGENT_IOCTL_CLK);
+ sc->sc_buscore_ops->bc_read(sc,
+ core->co_wrapbase + BWFM_AGENT_IOCTL);
+}
+
+void
+bwfm_chip_ai_reset(struct bwfm_softc *sc, struct bwfm_core *core,
+ uint32_t prereset, uint32_t reset, uint32_t postreset)
+{
+ int i;
+
+ bwfm_chip_ai_disable(sc, core, prereset, reset);
+
+ for (i = 50; i > 0; i--) {
+ if ((sc->sc_buscore_ops->bc_read(sc,
+ core->co_wrapbase + BWFM_AGENT_RESET_CTL) &
+ BWFM_AGENT_RESET_CTL_RESET) == 0)
+ break;
+ sc->sc_buscore_ops->bc_write(sc,
+ core->co_wrapbase + BWFM_AGENT_RESET_CTL, 0);
+ delay(60);
+ }
+ if (i == 0)
+ printf("%s: timeout on core reset\n", DEVNAME(sc));
+
+ sc->sc_buscore_ops->bc_write(sc,
+ core->co_wrapbase + BWFM_AGENT_IOCTL,
+ postreset | BWFM_AGENT_IOCTL_CLK);
+ sc->sc_buscore_ops->bc_read(sc,
+ core->co_wrapbase + BWFM_AGENT_IOCTL);
+}
+
+void
+bwfm_chip_dmp_erom_scan(struct bwfm_softc *sc)
+{
+ uint32_t erom, val, base, wrap;
+ uint8_t type = 0;
+ uint16_t id;
+ uint8_t nmw, nsw, rev;
+ struct bwfm_core *core;
+
+ erom = sc->sc_buscore_ops->bc_read(sc,
+ BWFM_CHIP_BASE + BWFM_CHIP_REG_EROMPTR);
+ while (type != BWFM_DMP_DESC_EOT) {
+ val = sc->sc_buscore_ops->bc_read(sc, erom);
+ type = val & BWFM_DMP_DESC_MASK;
+ erom += 4;
+
+ if (type != BWFM_DMP_DESC_COMPONENT)
+ continue;
+
+ id = (val & BWFM_DMP_COMP_PARTNUM)
+ >> BWFM_DMP_COMP_PARTNUM_S;
+
+ val = sc->sc_buscore_ops->bc_read(sc, erom);
+ type = val & BWFM_DMP_DESC_MASK;
+ erom += 4;
+
+ if (type != BWFM_DMP_DESC_COMPONENT) {
+ printf("%s: not component descriptor\n", DEVNAME(sc));
+ return;
+ }
+
+ nmw = (val & BWFM_DMP_COMP_NUM_MWRAP)
+ >> BWFM_DMP_COMP_NUM_MWRAP_S;
+ nsw = (val & BWFM_DMP_COMP_NUM_SWRAP)
+ >> BWFM_DMP_COMP_NUM_SWRAP_S;
+ rev = (val & BWFM_DMP_COMP_REVISION)
+ >> BWFM_DMP_COMP_REVISION_S;
+
+ if (nmw + nsw == 0 && id != BWFM_AGENT_CORE_PMU)
+ continue;
+
+ if (bwfm_chip_dmp_get_regaddr(sc, &erom, &base, &wrap))
+ continue;
+
+ core = malloc(sizeof(*core), M_DEVBUF, M_WAITOK);
+ core->co_id = id;
+ core->co_base = base;
+ core->co_wrapbase = wrap;
+ core->co_rev = rev;
+ LIST_INSERT_HEAD(&sc->sc_chip.ch_list, core, co_link);
+ }
+}
+
+int
+bwfm_chip_dmp_get_regaddr(struct bwfm_softc *sc, uint32_t *erom,
+ uint32_t *base, uint32_t *wrap)
+{
+ uint8_t type = 0, mpnum = 0;
+ uint8_t stype, sztype, wraptype;
+ uint32_t val;
+
+ *base = 0;
+ *wrap = 0;
+
+ val = sc->sc_buscore_ops->bc_read(sc, *erom);
+ type = val & BWFM_DMP_DESC_MASK;
+ if (type == BWFM_DMP_DESC_MASTER_PORT) {
+ mpnum = (val & BWFM_DMP_MASTER_PORT_NUM)
+ >> BWFM_DMP_MASTER_PORT_NUM_S;
+ wraptype = BWFM_DMP_SLAVE_TYPE_MWRAP;
+ *erom += 4;
+ } else if ((type & ~BWFM_DMP_DESC_ADDRSIZE_GT32) ==
+ BWFM_DMP_DESC_ADDRESS)
+ wraptype = BWFM_DMP_SLAVE_TYPE_SWRAP;
+ else
+ return 1;
+
+ do {
+ do {
+ val = sc->sc_buscore_ops->bc_read(sc, *erom);
+ type = val & BWFM_DMP_DESC_MASK;
+ if (type == BWFM_DMP_DESC_COMPONENT)
+ return 0;
+ if (type == BWFM_DMP_DESC_EOT)
+ return 1;
+ *erom += 4;
+ } while ((type & ~BWFM_DMP_DESC_ADDRSIZE_GT32) !=
+ BWFM_DMP_DESC_ADDRESS);
+
+ if (type & BWFM_DMP_DESC_ADDRSIZE_GT32)
+ *erom += 4;
+
+ sztype = (val & BWFM_DMP_SLAVE_SIZE_TYPE)
+ >> BWFM_DMP_SLAVE_SIZE_TYPE_S;
+ if (sztype == BWFM_DMP_SLAVE_SIZE_DESC) {
+ val = sc->sc_buscore_ops->bc_read(sc, *erom);
+ type = val & BWFM_DMP_DESC_MASK;
+ if (type & BWFM_DMP_DESC_ADDRSIZE_GT32)
+ *erom += 8;
+ else
+ *erom += 4;
+ }
+ if (sztype != BWFM_DMP_SLAVE_SIZE_4K)
+ continue;
+
+ stype = (val & BWFM_DMP_SLAVE_TYPE) >> BWFM_DMP_SLAVE_TYPE_S;
+ if (*base == 0 && stype == BWFM_DMP_SLAVE_TYPE_SLAVE)
+ *base = val & BWFM_DMP_SLAVE_ADDR_BASE;
+ if (*wrap == 0 && stype == wraptype)
+ *wrap = val & BWFM_DMP_SLAVE_ADDR_BASE;
+ } while (*base == 0 || *wrap == 0);
+
+ return 0;
+}
+
+/* Core configuration */
+void
+bwfm_chip_cr4_set_passive(struct bwfm_softc *sc)
+{
+ panic("%s: CR4 not supported", DEVNAME(sc));
+}
+
+void
+bwfm_chip_ca7_set_passive(struct bwfm_softc *sc)
+{
+ panic("%s: CA7 not supported", DEVNAME(sc));
+}
+
+void
+bwfm_chip_cm3_set_passive(struct bwfm_softc *sc)
+{
+ struct bwfm_core *core;
+
+ core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3);
+ sc->sc_chip.ch_core_disable(sc, core, 0, 0);
+ core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_80211);
+ sc->sc_chip.ch_core_reset(sc, core, BWFM_AGENT_D11_IOCTL_PHYRESET |
+ BWFM_AGENT_D11_IOCTL_PHYCLOCKEN, BWFM_AGENT_D11_IOCTL_PHYCLOCKEN,
+ BWFM_AGENT_D11_IOCTL_PHYCLOCKEN);
+ core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM);
+ sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
+
+ if (sc->sc_chip.ch_chip == BRCM_CC_43430_CHIP_ID) {
+ sc->sc_buscore_ops->bc_write(sc,
+ core->co_base + BWFM_SOCRAM_BANKIDX, 3);
+ sc->sc_buscore_ops->bc_write(sc,
+ core->co_base + BWFM_SOCRAM_BANKPDA, 0);
+ }
+}
+
+/* BCDC protocol implementation */
+int
+bwfm_proto_bcdc_query_dcmd(struct bwfm_softc *sc, int ifidx,
+ int cmd, char *buf, size_t *len)
+{
+ struct bwfm_proto_bcdc_dcmd *dcmd;
+ size_t size = sizeof(dcmd->hdr) + *len;
+ static int reqid = 0;
+ int ret = 1;
+
+ reqid++;
+
+ dcmd = malloc(sizeof(*dcmd), M_TEMP, M_WAITOK | M_ZERO);
+ if (*len > sizeof(dcmd->buf))
+ goto err;
+
+ dcmd->hdr.cmd = htole32(cmd);
+ dcmd->hdr.len = htole32(*len);
+ dcmd->hdr.flags |= BWFM_BCDC_DCMD_GET;
+ dcmd->hdr.flags |= BWFM_BCDC_DCMD_ID_SET(reqid);
+ dcmd->hdr.flags |= BWFM_BCDC_DCMD_IF_SET(ifidx);
+ dcmd->hdr.flags = htole32(dcmd->hdr.flags);
+ memcpy(&dcmd->buf, buf, *len);
+
+ if (sc->sc_bus_ops->bs_txctl(sc, (void *)dcmd,
+ sizeof(dcmd->hdr) + *len)) {
+ DPRINTF(("%s: tx failed\n", DEVNAME(sc)));
+ goto err;
+ }
+
+ do {
+ if (sc->sc_bus_ops->bs_rxctl(sc, (void *)dcmd, &size)) {
+ DPRINTF(("%s: rx failed\n", DEVNAME(sc)));
+ goto err;
+ }
+ dcmd->hdr.cmd = letoh32(dcmd->hdr.cmd);
+ dcmd->hdr.len = letoh32(dcmd->hdr.len);
+ dcmd->hdr.flags = letoh32(dcmd->hdr.flags);
+ dcmd->hdr.status = letoh32(dcmd->hdr.status);
+ } while (BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags) != reqid);
+
+ if (BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags) != reqid) {
+ printf("%s: unexpected request id\n", DEVNAME(sc));
+ goto err;
+ }
+
+ if (buf) {
+ if (size > *len)
+ size = *len;
+ if (size < *len)
+ *len = size;
+ memcpy(buf, dcmd->buf, *len);
+ }
+
+ if (dcmd->hdr.flags & BWFM_BCDC_DCMD_ERROR)
+ ret = dcmd->hdr.status;
+ else
+ ret = 0;
+err:
+ free(dcmd, M_TEMP, sizeof(*dcmd));
+ return ret;
+}
+
+int
+bwfm_proto_bcdc_set_dcmd(struct bwfm_softc *sc, int ifidx,
+ int cmd, char *buf, size_t len)
+{
+ struct bwfm_proto_bcdc_dcmd *dcmd;
+ size_t size = sizeof(dcmd->hdr) + len;
+ int reqid = 0;
+ int ret = 1;
+
+ reqid++;
+
+ dcmd = malloc(sizeof(*dcmd), M_TEMP, M_WAITOK | M_ZERO);
+ if (len > sizeof(dcmd->buf))
+ goto err;
+
+ dcmd->hdr.cmd = htole32(cmd);
+ dcmd->hdr.len = htole32(len);
+ dcmd->hdr.flags |= BWFM_BCDC_DCMD_SET;
+ dcmd->hdr.flags |= BWFM_BCDC_DCMD_ID_SET(reqid);
+ dcmd->hdr.flags |= BWFM_BCDC_DCMD_IF_SET(ifidx);
+ dcmd->hdr.flags = htole32(dcmd->hdr.flags);
+ memcpy(&dcmd->buf, buf, len);
+
+ if (sc->sc_bus_ops->bs_txctl(sc, (void *)dcmd, size)) {
+ DPRINTF(("%s: tx failed\n", DEVNAME(sc)));
+ goto err;
+ }
+
+ do {
+ if (sc->sc_bus_ops->bs_rxctl(sc, (void *)dcmd, &size)) {
+ DPRINTF(("%s: rx failed\n", DEVNAME(sc)));
+ goto err;
+ }
+ dcmd->hdr.cmd = letoh32(dcmd->hdr.cmd);
+ dcmd->hdr.len = letoh32(dcmd->hdr.len);
+ dcmd->hdr.flags = letoh32(dcmd->hdr.flags);
+ dcmd->hdr.status = letoh32(dcmd->hdr.status);
+ } while (BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags) != reqid);
+
+ if (BWFM_BCDC_DCMD_ID_GET(dcmd->hdr.flags) != reqid) {
+ printf("%s: unexpected request id\n", DEVNAME(sc));
+ goto err;
+ }
+
+ if (dcmd->hdr.flags & BWFM_BCDC_DCMD_ERROR)
+ return dcmd->hdr.status;
+
+ ret = 0;
+err:
+ free(dcmd, M_TEMP, sizeof(*dcmd));
+ return ret;
+}
+
+/* FW Variable code */
+int
+bwfm_fwvar_cmd_get_data(struct bwfm_softc *sc, int cmd, void *data, size_t len)
+{
+ return sc->sc_proto_ops->proto_query_dcmd(sc, 0, cmd, data, &len);
+}
+
+int
+bwfm_fwvar_cmd_set_data(struct bwfm_softc *sc, int cmd, void *data, size_t len)
+{
+ return sc->sc_proto_ops->proto_set_dcmd(sc, 0, cmd, data, len);
+}
+
+int
+bwfm_fwvar_cmd_get_int(struct bwfm_softc *sc, int cmd, uint32_t *data)
+{
+ int ret;
+ ret = bwfm_fwvar_cmd_get_data(sc, cmd, data, sizeof(*data));
+ *data = letoh32(*data);
+ return ret;
+}
+
+int
+bwfm_fwvar_cmd_set_int(struct bwfm_softc *sc, int cmd, uint32_t data)
+{
+ data = htole32(data);
+ return bwfm_fwvar_cmd_set_data(sc, cmd, &data, sizeof(data));
+}
+
+int
+bwfm_fwvar_var_get_data(struct bwfm_softc *sc, char *name, void *data, size_t len)
+{
+ char *buf;
+ int ret;
+
+ buf = malloc(strlen(name) + 1 + len, M_TEMP, M_WAITOK);
+ memcpy(buf, name, strlen(name) + 1);
+ memcpy(buf + strlen(name) + 1, data, len);
+ ret = bwfm_fwvar_cmd_get_data(sc, BWFM_C_GET_VAR,
+ buf, strlen(name) + 1 + len);
+ memcpy(data, buf, len);
+ free(buf, M_TEMP, strlen(name) + 1 + len);
+ return ret;
+}
+
+int
+bwfm_fwvar_var_set_data(struct bwfm_softc *sc, char *name, void *data, size_t len)
+{
+ char *buf;
+ int ret;
+
+ buf = malloc(strlen(name) + 1 + len, M_TEMP, M_WAITOK);
+ memcpy(buf, name, strlen(name) + 1);
+ memcpy(buf + strlen(name) + 1, data, len);
+ ret = bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_VAR,
+ buf, strlen(name) + 1 + len);
+ free(buf, M_TEMP, strlen(name) + 1 + len);
+ return ret;
+}
+
+int
+bwfm_fwvar_var_get_int(struct bwfm_softc *sc, char *name, uint32_t *data)
+{
+ int ret;
+ ret = bwfm_fwvar_var_get_data(sc, name, data, sizeof(*data));
+ *data = letoh32(*data);
+ return ret;
+}
+
+int
+bwfm_fwvar_var_set_int(struct bwfm_softc *sc, char *name, uint32_t data)
+{
+ data = htole32(data);
+ return bwfm_fwvar_var_set_data(sc, name, &data, sizeof(data));
+}
+
+/* 802.11 code */
+void
+bwfm_scan(struct bwfm_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct bwfm_escan_params *params;
+ uint32_t nssid = 0, nchannel = 0;
+ size_t params_size;
+
+ ic->ic_scan_lock = IEEE80211_SCAN_LOCKED;
+
+#if 0
+ /* Active scan is used for scanning for an SSID */
+ bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PASSIVE_SCAN, 0);
+#endif
+ bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PASSIVE_SCAN, 1);
+
+ params_size = sizeof(*params);
+ params_size += sizeof(uint32_t) * ((nchannel + 1) / 2);
+ params_size += sizeof(struct bwfm_ssid) * nssid;
+
+ params = malloc(params_size, M_TEMP, M_WAITOK | M_ZERO);
+ memset(params->scan_params.bssid, 0xff,
+ sizeof(params->scan_params.bssid));
+ params->scan_params.bss_type = 2;
+ params->scan_params.nprobes = htole32(-1);
+ params->scan_params.active_time = htole32(-1);
+ params->scan_params.passive_time = htole32(-1);
+ params->scan_params.home_time = htole32(-1);
+ params->version = htole32(BWFM_ESCAN_REQ_VERSION);
+ params->action = htole16(WL_ESCAN_ACTION_START);
+ params->sync_id = htole16(0x1234);
+
+#if 0
+ /* Scan a specific channel */
+ params->scan_params.channel_list[0] = htole16(
+ (1 & 0xff) << 0 |
+ (3 & 0x3) << 8 |
+ (2 & 0x3) << 10 |
+ (2 & 0x3) << 12
+ );
+ params->scan_params.channel_num = htole32(
+ (1 & 0xffff) << 0
+ );
+#endif
+
+ bwfm_fwvar_var_set_data(sc, "escan", params, params_size);
+ free(params, M_TEMP, params_size);
+}
+
+void
+bwfm_rx(struct bwfm_softc *sc, char *buf, size_t len)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+ struct bwfm_event *e = (void *)buf;
+ struct mbuf_list ml = MBUF_LIST_INITIALIZER();
+ struct mbuf *m;
+ char *mb;
+
+ printf("%s: buf %p len %lu\n", __func__, buf, len);
+
+ if (len >= sizeof(e->ehdr) &&
+ ntohs(e->ehdr.ether_type) == BWFM_ETHERTYPE_LINK_CTL &&
+ memcmp(BWFM_BRCM_OUI, e->hdr.oui, sizeof(e->hdr.oui)) == 0 &&
+ ntohs(e->hdr.usr_subtype) == BWFM_BRCM_SUBTYPE_EVENT)
+ bwfm_rx_event(sc, buf, len);
+
+ if (__predict_false(len > MCLBYTES))
+ return;
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (__predict_false(m == NULL))
+ return;
+ if (len > MHLEN) {
+ MCLGET(m, M_DONTWAIT);
+ if (!(m->m_flags & M_EXT)) {
+ m_free(m);
+ return;
+ }
+ }
+ mb = mtod(m, char *);
+ memcpy(mb, buf, len);
+ m->m_pkthdr.len = m->m_len = len;
+
+ ml_enqueue(&ml, m);
+ if_input(ifp, &ml);
+}
+
+void
+bwfm_rx_event(struct bwfm_softc *sc, char *buf, size_t len)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ //struct ifnet *ifp = &ic->ic_if;
+ struct bwfm_event *e = (void *)buf;
+
+ if (ntohl(e->msg.event_type) >= BWFM_E_LAST)
+ return;
+
+ switch (ntohl(e->msg.event_type)) {
+ case BWFM_E_ESCAN_RESULT: {
+ struct bwfm_escan_results *res = (void *)(buf + sizeof(*e));
+ struct bwfm_bss_info *bss;
+ int i;
+ if (ntohl(e->msg.status) != BWFM_E_STATUS_PARTIAL) {
+ ic->ic_scan_lock = IEEE80211_SCAN_UNLOCKED;
+ wakeup(&ic->ic_scan_lock);
+ break;
+ }
+ len -= sizeof(*e);
+ if (len < sizeof(*res) || len < letoh32(res->buflen)) {
+ printf("%s: results too small\n", DEVNAME(sc));
+ return;
+ }
+ len -= sizeof(*res);
+ if (len < letoh16(res->bss_count) * sizeof(struct bwfm_bss_info)) {
+ printf("%s: results too small\n", DEVNAME(sc));
+ return;
+ }
+ bss = &res->bss_info[0];
+ for (i = 0; i < letoh16(res->bss_count); i++) {
+ bwfm_scan_node(sc, &res->bss_info[i], len);
+ len -= sizeof(*bss) + letoh32(bss->length);
+ bss = (void *)((char *)bss) + letoh32(bss->length);
+ if (len <= 0)
+ break;
+ }
+ break;
+ }
+ default:
+ printf("%s: buf %p len %lu datalen %u code %u status %u"
+ " reason %u\n", __func__, buf, len, ntohl(e->msg.datalen),
+ ntohl(e->msg.event_type), ntohl(e->msg.status),
+ ntohl(e->msg.reason));
+ break;
+ }
+}
+
+void
+bwfm_scan_node(struct bwfm_softc *sc, struct bwfm_bss_info *bss, size_t len)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_node *ni;
+
+ ni = ieee80211_alloc_node(ic, bss->bssid);
+ IEEE80211_ADDR_COPY(ni->ni_macaddr, bss->bssid);
+ IEEE80211_ADDR_COPY(ni->ni_bssid, bss->bssid);
+ ni->ni_esslen = min(bss->ssid_len, sizeof(bss->ssid));
+ ni->ni_esslen = min(ni->ni_esslen, IEEE80211_NWID_LEN);
+ memcpy(ni->ni_essid, bss->ssid, ni->ni_esslen);
+ ni->ni_chan = ic->ic_ibss_chan;
+ ni->ni_rssi = letoh32(bss->rssi);
+}
diff --git a/sys/dev/ic/bwfmreg.h b/sys/dev/ic/bwfmreg.h
new file mode 100644
index 00000000000..6ff9b745ce2
--- /dev/null
+++ b/sys/dev/ic/bwfmreg.h
@@ -0,0 +1,469 @@
+/* $OpenBSD: bwfmreg.h,v 1.1 2017/10/11 17:19:50 patrick Exp $ */
+/*
+ * Copyright (c) 2010-2016 Broadcom Corporation
+ * Copyright (c) 2016,2017 Patrick Wildt <patrick@blueri.se>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* SDIO registers */
+#define BWFM_SDIO_FUNC1_SBADDRLOW 0x1000A
+#define BWFM_SDIO_FUNC1_SBADDRMID 0x1000B
+#define BWFM_SDIO_FUNC1_SBADDRHIGH 0x1000C
+#define BWFM_SDIO_FUNC1_CHIPCLKCSR 0x1000E
+#define BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_ALP 0x01
+#define BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HT 0x02
+#define BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_ILP 0x04
+#define BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ 0x08
+#define BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL_REQ 0x10
+#define BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF 0x20
+#define BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL 0x40
+#define BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL 0x80
+#define BWFM_SDIO_FUNC1_CHIPCLKCSR_CSR_MASK 0x1F
+#define BWFM_SDIO_FUNC1_SDIOPULLUP 0x1000F
+
+#define BWFM_SDIO_SB_OFT_ADDR_MASK 0x07FFF
+#define BWFM_SDIO_SB_ACCESS_2_4B_FLAG 0x08000
+
+/* Chip registers */
+#define BWFM_CHIP_BASE 0x18000000
+#define BWFM_CHIP_REG_CHIPID 0x00000000
+#define BWFM_CHIP_CHIPID_ID(x) (((x) >> 0) & 0xffff)
+#define BWFM_CHIP_CHIPID_REV(x) (((x) >> 16) & 0xf)
+#define BWFM_CHIP_CHIPID_PKG(x) (((x) >> 20) & 0xf)
+#define BWFM_CHIP_CHIPID_CC(x) (((x) >> 24) & 0xf)
+#define BWFM_CHIP_CHIPID_TYPE(x) (((x) >> 28) & 0xf)
+#define BWFM_CHIP_CHIPID_TYPE_SOCI_SB 0
+#define BWFM_CHIP_CHIPID_TYPE_SOCI_AI 1
+#define BWFM_CHIP_REG_CAPABILITIES 0x00000004
+#define BWFM_CHIP_REG_CAPABILITIES_PMU 0x10000000
+#define BWFM_CHIP_REG_CAPABILITIES_EXT 0x000000AC
+#define BWFM_CHIP_REG_CAPABILITIES_EXT_AOB_PRESENT 0x00000040
+#define BWFM_CHIP_REG_EROMPTR 0x000000FC
+#define BWFM_CHIP_REG_PMUCONTROL 0x00000600
+#define BWFM_CHIP_REG_PMUCONTROL_RES_MASK 0x00006000
+#define BWFM_CHIP_REG_PMUCONTROL_RES_SHIFT 13
+#define BWFM_CHIP_REG_PMUCONTROL_RES_RELOAD 0x2
+#define BWFM_CHIP_REG_PMUCAPABILITIES 0x00000604
+#define BWFM_CHIP_REG_PMUCAPABILITIES_REV_MASK 0x000000ff
+
+/* Agent registers */
+#define BWFM_AGENT_IOCTL 0x0408
+#define BWFM_AGENT_IOCTL_CLK 0x0001
+#define BWFM_AGENT_IOCTL_FGC 0x0002
+#define BWFM_AGENT_IOCTL_CORE_BITS 0x3FFC
+#define BWFM_AGENT_IOCTL_PME_EN 0x4000
+#define BWFM_AGENT_IOCTL_BIST_EN 0x8000
+#define BWFM_AGENT_RESET_CTL 0x0800
+#define BWFM_AGENT_RESET_CTL_RESET 0x0001
+
+/* Agent Core-IDs */
+#define BWFM_AGENT_CORE_CHIPCOMMON 0x800
+#define BWFM_AGENT_INTERNAL_MEM 0x80E
+#define BWFM_AGENT_CORE_80211 0x812
+#define BWFM_AGENT_CORE_PMU 0x827
+#define BWFM_AGENT_CORE_SDIO_DEV 0x829
+#define BWFM_AGENT_CORE_ARM_CM3 0x82A
+#define BWFM_AGENT_CORE_ARM_CR4 0x83E
+#define BWFM_AGENT_CORE_ARM_CA7 0x847
+
+/* Specific Core Bits */
+#define BWFM_AGENT_ARMCR4_IOCTL_CPUHALT 0x0020
+#define BWFM_AGENT_D11_IOCTL_PHYCLOCKEN 0x0004
+#define BWFM_AGENT_D11_IOCTL_PHYRESET 0x0008
+
+/* SOCRAM registers */
+#define BWFM_SOCRAM_BANKIDX 0x0010
+#define BWFM_SOCRAM_BANKPDA 0x0044
+
+/* SDPCMD registers */
+#define BWFM_SDPCMD_INTSTATUS 0x0020
+
+/* DMP descriptor */
+#define BWFM_DMP_DESC_MASK 0x0000000F
+#define BWFM_DMP_DESC_EMPTY 0x00000000
+#define BWFM_DMP_DESC_VALID 0x00000001
+#define BWFM_DMP_DESC_COMPONENT 0x00000001
+#define BWFM_DMP_DESC_MASTER_PORT 0x00000003
+#define BWFM_DMP_DESC_ADDRESS 0x00000005
+#define BWFM_DMP_DESC_ADDRSIZE_GT32 0x00000008
+#define BWFM_DMP_DESC_EOT 0x0000000F
+#define BWFM_DMP_COMP_DESIGNER 0xFFF00000
+#define BWFM_DMP_COMP_DESIGNER_S 20
+#define BWFM_DMP_COMP_PARTNUM 0x000FFF00
+#define BWFM_DMP_COMP_PARTNUM_S 8
+#define BWFM_DMP_COMP_CLASS 0x000000F0
+#define BWFM_DMP_COMP_CLASS_S 4
+#define BWFM_DMP_COMP_REVISION 0xFF000000
+#define BWFM_DMP_COMP_REVISION_S 24
+#define BWFM_DMP_COMP_NUM_SWRAP 0x00F80000
+#define BWFM_DMP_COMP_NUM_SWRAP_S 19
+#define BWFM_DMP_COMP_NUM_MWRAP 0x0007C000
+#define BWFM_DMP_COMP_NUM_MWRAP_S 14
+#define BWFM_DMP_COMP_NUM_SPORT 0x00003E00
+#define BWFM_DMP_COMP_NUM_SPORT_S 9
+#define BWFM_DMP_COMP_NUM_MPORT 0x000001F0
+#define BWFM_DMP_COMP_NUM_MPORT_S 4
+#define BWFM_DMP_MASTER_PORT_UID 0x0000FF00
+#define BWFM_DMP_MASTER_PORT_UID_S 8
+#define BWFM_DMP_MASTER_PORT_NUM 0x000000F0
+#define BWFM_DMP_MASTER_PORT_NUM_S 4
+#define BWFM_DMP_SLAVE_ADDR_BASE 0xFFFFF000
+#define BWFM_DMP_SLAVE_ADDR_BASE_S 12
+#define BWFM_DMP_SLAVE_PORT_NUM 0x00000F00
+#define BWFM_DMP_SLAVE_PORT_NUM_S 8
+#define BWFM_DMP_SLAVE_TYPE 0x000000C0
+#define BWFM_DMP_SLAVE_TYPE_S 6
+#define BWFM_DMP_SLAVE_TYPE_SLAVE 0
+#define BWFM_DMP_SLAVE_TYPE_BRIDGE 1
+#define BWFM_DMP_SLAVE_TYPE_SWRAP 2
+#define BWFM_DMP_SLAVE_TYPE_MWRAP 3
+#define BWFM_DMP_SLAVE_SIZE_TYPE 0x00000030
+#define BWFM_DMP_SLAVE_SIZE_TYPE_S 4
+#define BWFM_DMP_SLAVE_SIZE_4K 0
+#define BWFM_DMP_SLAVE_SIZE_8K 1
+#define BWFM_DMP_SLAVE_SIZE_16K 2
+#define BWFM_DMP_SLAVE_SIZE_DESC 3
+
+/* DCMD commands */
+#define BWFM_C_GET_VERSION 1
+#define BWFM_C_UP 2
+#define BWFM_C_DOWN 3
+#define BWFM_C_SET_PROMISC 10
+#define BWFM_C_GET_RATE 12
+#define BWFM_C_GET_INFRA 19
+#define BWFM_C_SET_INFRA 20
+#define BWFM_C_GET_AUTH 21
+#define BWFM_C_SET_AUTH 22
+#define BWFM_C_GET_BSSID 23
+#define BWFM_C_GET_SSID 25
+#define BWFM_C_SET_SSID 26
+#define BWFM_C_TERMINATED 28
+#define BWFM_C_GET_CHANNEL 29
+#define BWFM_C_SET_CHANNEL 30
+#define BWFM_C_GET_SRL 31
+#define BWFM_C_SET_SRL 32
+#define BWFM_C_GET_LRL 33
+#define BWFM_C_SET_LRL 34
+#define BWFM_C_GET_RADIO 37
+#define BWFM_C_SET_RADIO 38
+#define BWFM_C_GET_PHYTYPE 39
+#define BWFM_C_SET_KEY 45
+#define BWFM_C_GET_REGULATORY 46
+#define BWFM_C_SET_REGULATORY 47
+#define BWFM_C_SET_PASSIVE_SCAN 49
+#define BWFM_C_SCAN 50
+#define BWFM_C_SCAN_RESULTS 51
+#define BWFM_C_DISASSOC 52
+#define BWFM_C_REASSOC 53
+#define BWFM_C_SET_ROAM_TRIGGER 55
+#define BWFM_C_SET_ROAM_DELTA 57
+#define BWFM_C_GET_BCNPRD 75
+#define BWFM_C_SET_BCNPRD 76
+#define BWFM_C_GET_DTIMPRD 77
+#define BWFM_C_SET_DTIMPRD 78
+#define BWFM_C_SET_COUNTRY 84
+#define BWFM_C_GET_PM 85
+#define BWFM_C_SET_PM 86
+#define BWFM_C_GET_REVINFO 98
+#define BWFM_C_GET_CURR_RATESET 114
+#define BWFM_C_GET_AP 117
+#define BWFM_C_SET_AP 118
+#define BWFM_C_SET_SCB_AUTHORIZE 121
+#define BWFM_C_SET_SCB_DEAUTHORIZE 122
+#define BWFM_C_GET_RSSI 127
+#define BWFM_C_GET_WSEC 133
+#define BWFM_C_SET_WSEC 134
+#define BWFM_C_GET_PHY_NOISE 135
+#define BWFM_C_GET_BSS_INFO 136
+#define BWFM_C_GET_GET_PKTCNTS 137
+#define BWFM_C_GET_BANDLIST 140
+#define BWFM_C_SET_SCB_TIMEOUT 158
+#define BWFM_C_GET_ASSOCLIST 159
+#define BWFM_C_GET_PHYLIST 180
+#define BWFM_C_SET_SCAN_CHANNEL_TIME 185
+#define BWFM_C_SET_SCAN_UNASSOC_TIME 187
+#define BWFM_C_SCB_DEAUTHENTICATE_FOR_REASON 201
+#define BWFM_C_SET_ASSOC_PREFER 205
+#define BWFM_C_GET_VALID_CHANNELS 217
+#define BWFM_C_GET_KEY_PRIMARY 235
+#define BWFM_C_SET_KEY_PRIMARY 236
+#define BWFM_C_SET_SCAN_PASSIVE_TIME 258
+#define BWFM_C_GET_VAR 262
+#define BWFM_C_SET_VAR 263
+#define BWFM_C_SET_WSEC_PMK 268
+
+struct bwfm_proto_bcdc_dcmd {
+ struct {
+ uint32_t cmd;
+ uint32_t len;
+ uint32_t flags;
+#define BWFM_BCDC_DCMD_ERROR (1 << 0)
+#define BWFM_BCDC_DCMD_GET (0 << 1)
+#define BWFM_BCDC_DCMD_SET (1 << 1)
+#define BWFM_BCDC_DCMD_IF_GET(x) (((x) >> 12) & 0xf)
+#define BWFM_BCDC_DCMD_IF_SET(x) (((x) & 0xf) << 12)
+#define BWFM_BCDC_DCMD_ID_GET(x) (((x) >> 16) & 0xffff)
+#define BWFM_BCDC_DCMD_ID_SET(x) (((x) & 0xffff) << 16)
+ uint32_t status;
+ } hdr;
+ char buf[8192];
+} __packed;
+
+struct bwfm_proto_bcdc_hdr {
+ uint8_t flags;
+#define BWFM_BCDC_FLAG_PROTO_VER 2
+#define BWFM_BCDC_FLAG_VER(x) (((x) & 0xf) << 4)
+#define BWFM_BCDC_FLAG_SUM_GOOD (1 << 2) /* rx */
+#define BWFM_BCDC_FLAG_SUM_NEEDED (1 << 3) /* tx */
+ uint8_t priority;
+#define BWFM_BCDC_PRIORITY_MASK 0x7
+ uint8_t flags2;
+#define BWFM_BCDC_FLAG2_IF_MASK 0xf
+ uint8_t data_offset;
+} __packed;
+
+#define BWFM_MCSSET_LEN 16
+#define BWFM_MAX_SSID_LEN 32
+struct bwfm_bss_info {
+ uint32_t version;
+ uint32_t length;
+ uint8_t bssid[ETHER_ADDR_LEN];
+ uint16_t beacon_period;
+ uint16_t capability;
+ uint8_t ssid_len;
+ uint8_t ssid[BWFM_MAX_SSID_LEN];
+ uint32_t nrates;
+ uint8_t rates[16];
+ uint16_t chanspec;
+ uint16_t atim_window;
+ uint8_t dtim_period;
+ uint16_t rssi;
+ uint8_t phy_noise;
+ uint8_t n_cap;
+ uint32_t nbss_cap;
+ uint8_t ctl_ch;
+ uint32_t reserved32[1];
+ uint8_t flags;
+ uint8_t reserved[3];
+ uint8_t basic_mcs[BWFM_MCSSET_LEN];
+ uint16_t ie_offset;
+ uint32_t ie_length;
+ uint16_t snr;
+} __packed;
+
+struct bwfm_ssid {
+ uint32_t len;
+ uint8_t ssid[BWFM_MAX_SSID_LEN];
+} __packed;
+
+struct bwfm_scan_params {
+ struct bwfm_ssid ssid;
+ uint8_t bssid[ETHER_ADDR_LEN];
+ uint8_t bss_type;
+#define DOT11_BSSTYPE_ANY 2
+ uint8_t scan_type;
+ uint32_t nprobes;
+ uint32_t active_time;
+ uint32_t passive_time;
+ uint32_t home_time;
+ uint32_t channel_num;
+ uint16_t channel_list[];
+} __packed;
+
+struct bwfm_scan_results {
+ uint32_t buflen;
+ uint32_t version;
+ uint32_t count;
+ struct bwfm_bss_info bss_info[];
+} __packed;
+
+struct bwfm_escan_params {
+ uint32_t version;
+#define BWFM_ESCAN_REQ_VERSION 1
+ uint16_t action;
+#define WL_ESCAN_ACTION_START 1
+#define WL_ESCAN_ACTION_CONTINUE 2
+#define WL_ESCAN_ACTION_ABORT 3
+ uint16_t sync_id;
+ struct bwfm_scan_params scan_params;
+} __packed;
+
+struct bwfm_escan_results {
+ uint32_t buflen;
+ uint32_t version;
+ uint16_t sync_id;
+ uint16_t bss_count;
+ struct bwfm_bss_info bss_info[];
+} __packed;
+
+struct bwfm_assoc_params {
+ uint8_t bssid[ETHER_ADDR_LEN];
+ uint32_t chanspec_num;
+ uint16_t chanspec_list[];
+} __packed;
+
+struct bwfm_join_params {
+ struct bwfm_ssid ssid;
+ struct bwfm_assoc_params assoc;
+} __packed;
+
+struct bwfm_join_scan_params {
+ uint8_t scan_type;
+ uint32_t nprobes;
+ uint32_t active_time;
+ uint32_t passive_time;
+ uint32_t home_time;
+} __packed;
+
+struct bwfm_ext_join_params {
+ struct bwfm_ssid ssid;
+ struct bwfm_join_scan_params scan;
+ struct bwfm_assoc_params assoc;
+} __packed;
+
+struct bwfm_wsec_pmk {
+ uint16_t key_len;
+#define BWFM_WSEC_MAX_PSK_LEN 32
+ uint16_t flags;
+#define BWFM_WSEC_PASSPHRASE (1 << 0)
+ uint8_t key[2 * BWFM_WSEC_MAX_PSK_LEN + 1];
+} __packed;
+
+/* Event handling */
+enum bwfm_fweh_event_code {
+ BWFM_E_SET_SSID = 0,
+ BWFM_E_JOIN = 1,
+ BWFM_E_START = 2,
+ BWFM_E_AUTH = 3,
+ BWFM_E_AUTH_IND = 4,
+ BWFM_E_DEAUTH = 5,
+ BWFM_E_DEAUTH_IND = 6,
+ BWFM_E_ASSOC = 7,
+ BWFM_E_ASSOC_IND = 8,
+ BWFM_E_REASSOC = 9,
+ BWFM_E_REASSOC_IND = 10,
+ BWFM_E_DISASSOC = 11,
+ BWFM_E_DISASSOC_IND = 12,
+ BWFM_E_QUIET_START = 13,
+ BWFM_E_QUIET_END = 14,
+ BWFM_E_BEACON_RX = 15,
+ BWFM_E_LINK = 16,
+ BWFM_E_MIC_ERROR = 17,
+ BWFM_E_NDIS_LINK = 18,
+ BWFM_E_ROAM = 19,
+ BWFM_E_TXFAIL = 20,
+ BWFM_E_PMKID_CACHE = 21,
+ BWFM_E_RETROGRADE_TSF = 22,
+ BWFM_E_PRUNE = 23,
+ BWFM_E_AUTOAUTH = 24,
+ BWFM_E_EAPOL_MSG = 25,
+ BWFM_E_SCAN_COMPLETE = 26,
+ BWFM_E_ADDTS_IND = 27,
+ BWFM_E_DELTS_IND = 28,
+ BWFM_E_BCNSENT_IND = 29,
+ BWFM_E_BCNRX_MSG = 30,
+ BWFM_E_BCNLOST_MSG = 31,
+ BWFM_E_ROAM_PREP = 32,
+ BWFM_E_PFN_NET_FOUND = 33,
+ BWFM_E_PFN_NET_LOST = 34,
+ BWFM_E_RESET_COMPLETE = 35,
+ BWFM_E_JOIN_START = 36,
+ BWFM_E_ROAM_START = 37,
+ BWFM_E_ASSOC_START = 38,
+ BWFM_E_IBSS_ASSOC = 39,
+ BWFM_E_RADIO = 40,
+ BWFM_E_PSM_WATCHDOG = 41,
+ BWFM_E_PROBREQ_MSG = 44,
+ BWFM_E_SCAN_CONFIRM_IND = 45,
+ BWFM_E_PSK_SUP = 46,
+ BWFM_E_COUNTRY_CODE_CHANGED = 47,
+ BWFM_E_EXCEEDED_MEDIUM_TIME = 48,
+ BWFM_E_ICV_ERROR = 49,
+ BWFM_E_UNICAST_DECODE_ERROR = 50,
+ BWFM_E_MULTICAST_DECODE_ERROR = 51,
+ BWFM_E_TRACE = 52,
+ BWFM_E_IF = 54,
+ BWFM_E_P2P_DISC_LISTEN_COMPLETE = 55,
+ BWFM_E_RSSI = 56,
+ BWFM_E_EXTLOG_MSG = 58,
+ BWFM_E_ACTION_FRAME = 59,
+ BWFM_E_ACTION_FRAME_COMPLETE = 60,
+ BWFM_E_PRE_ASSOC_IND = 61,
+ BWFM_E_PRE_REASSOC_IND = 62,
+ BWFM_E_CHANNEL_ADOPTED = 63,
+ BWFM_E_AP_STARTED = 64,
+ BWFM_E_DFS_AP_STOP = 65,
+ BWFM_E_DFS_AP_RESUME = 66,
+ BWFM_E_ESCAN_RESULT = 69,
+ BWFM_E_ACTION_FRAME_OFF_CHAN_COMPLETE = 70,
+ BWFM_E_PROBERESP_MSG = 71,
+ BWFM_E_P2P_PROBEREQ_MSG = 72,
+ BWFM_E_DCS_REQUEST = 73,
+ BWFM_E_FIFO_CREDIT_MAP = 74,
+ BWFM_E_ACTION_FRAME_RX = 75,
+ BWFM_E_TDLS_PEER_EVENT = 92,
+ BWFM_E_BCMC_CREDIT_SUPPORT = 127,
+ BWFM_E_LAST = 139
+};
+#define BWFM_EVENT_MASK_LEN (roundup(BWFM_E_LAST, 8) / 8)
+
+enum bwfm_fweh_event_status {
+ BWFM_E_STATUS_SUCCESS = 0,
+ BWFM_E_STATUS_FAIL = 1,
+ BWFM_E_STATUS_TIMEOUT = 2,
+ BWFM_E_STATUS_NO_NETWORKS = 3,
+ BWFM_E_STATUS_ABORT = 4,
+ BWFM_E_STATUS_NO_ACK = 5,
+ BWFM_E_STATUS_UNSOLICITED = 6,
+ BWFM_E_STATUS_ATTEMPT = 7,
+ BWFM_E_STATUS_PARTIAL = 8,
+ BWFM_E_STATUS_NEWSCAN = 9,
+ BWFM_E_STATUS_NEWASSOC = 10,
+ BWFM_E_STATUS_11HQUIET = 11,
+ BWFM_E_STATUS_SUPPRESS = 12,
+ BWFM_E_STATUS_NOCHANS = 13,
+ BWFM_E_STATUS_CS_ABORT = 15,
+ BWFM_E_STATUS_ERROR = 16,
+};
+
+struct bwfm_ethhdr {
+ uint16_t subtype;
+ uint16_t length;
+ uint8_t version;
+ uint8_t oui[3];
+#define BWFM_BRCM_OUI "\x00\x10\x18"
+ uint16_t usr_subtype;
+#define BWFM_BRCM_SUBTYPE_EVENT 1
+} __packed;
+
+struct bwfm_event_msg {
+ uint16_t version;
+ uint16_t flags;
+ uint32_t event_type;
+ uint32_t status;
+ uint32_t reason;
+ uint32_t auth_type;
+ uint32_t datalen;
+ struct ether_addr addr;
+ char ifname[IFNAMSIZ];
+ uint8_t ifidx;
+ uint8_t bsscfgidx;
+} __packed;
+
+struct bwfm_event {
+ struct ether_header ehdr;
+#define BWFM_ETHERTYPE_LINK_CTL 0x886c
+ struct bwfm_ethhdr hdr;
+ struct bwfm_event_msg msg;
+} __packed;
diff --git a/sys/dev/ic/bwfmvar.h b/sys/dev/ic/bwfmvar.h
new file mode 100644
index 00000000000..88b608fb7d1
--- /dev/null
+++ b/sys/dev/ic/bwfmvar.h
@@ -0,0 +1,129 @@
+/* $OpenBSD: bwfmvar.h,v 1.1 2017/10/11 17:19:50 patrick Exp $ */
+/*
+ * Copyright (c) 2010-2016 Broadcom Corporation
+ * Copyright (c) 2016,2017 Patrick Wildt <patrick@blueri.se>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Chipcommon Core Chip IDs */
+#define BRCM_CC_43143_CHIP_ID 43143
+#define BRCM_CC_43235_CHIP_ID 43235
+#define BRCM_CC_43236_CHIP_ID 43236
+#define BRCM_CC_43238_CHIP_ID 43238
+#define BRCM_CC_43241_CHIP_ID 0x4324
+#define BRCM_CC_43242_CHIP_ID 43242
+#define BRCM_CC_4329_CHIP_ID 0x4329
+#define BRCM_CC_4330_CHIP_ID 0x4330
+#define BRCM_CC_4334_CHIP_ID 0x4334
+#define BRCM_CC_43340_CHIP_ID 43340
+#define BRCM_CC_43362_CHIP_ID 43362
+#define BRCM_CC_4335_CHIP_ID 0x4335
+#define BRCM_CC_4339_CHIP_ID 0x4339
+#define BRCM_CC_43430_CHIP_ID 43430
+#define BRCM_CC_4345_CHIP_ID 0x4345
+#define BRCM_CC_4350_CHIP_ID 0x4350
+#define BRCM_CC_4354_CHIP_ID 0x4354
+#define BRCM_CC_4356_CHIP_ID 0x4356
+#define BRCM_CC_43566_CHIP_ID 43566
+#define BRCM_CC_43567_CHIP_ID 43567
+#define BRCM_CC_43569_CHIP_ID 43569
+#define BRCM_CC_43570_CHIP_ID 43570
+#define BRCM_CC_4358_CHIP_ID 0x4358
+#define BRCM_CC_4359_CHIP_ID 0x4359
+#define BRCM_CC_43602_CHIP_ID 43602
+#define BRCM_CC_4365_CHIP_ID 0x4365
+#define BRCM_CC_4366_CHIP_ID 0x4366
+#define BRCM_CC_4371_CHIP_ID 0x4371
+
+/* Defaults */
+#define BWFM_DEFAULT_SCAN_CHANNEL_TIME 40
+#define BWFM_DEFAULT_SCAN_UNASSOC_TIME 40
+#define BWFM_DEFAULT_SCAN_PASSIVE_TIME 120
+
+
+struct bwfm_softc;
+
+struct bwfm_core {
+ uint16_t co_id;
+ uint16_t co_rev;
+ uint32_t co_base;
+ uint32_t co_wrapbase;
+ LIST_ENTRY(bwfm_core) co_link;
+};
+
+struct bwfm_chip {
+ uint32_t ch_chip;
+ uint32_t ch_chiprev;
+ uint32_t ch_cc_caps;
+ uint32_t ch_cc_caps_ext;
+ uint32_t ch_pmucaps;
+ uint32_t ch_pmurev;
+ uint32_t ch_rambase;
+ uint32_t ch_ramsize;
+ uint32_t ch_srsize;
+ char ch_name[8];
+ LIST_HEAD(,bwfm_core) ch_list;
+ int (*ch_core_isup)(struct bwfm_softc *, struct bwfm_core *);
+ void (*ch_core_disable)(struct bwfm_softc *, struct bwfm_core *,
+ uint32_t prereset, uint32_t reset);
+ void (*ch_core_reset)(struct bwfm_softc *, struct bwfm_core *,
+ uint32_t prereset, uint32_t reset, uint32_t postreset);
+};
+
+struct bwfm_bus_ops {
+ void (*bs_init)(struct bwfm_softc *);
+ void (*bs_stop)(struct bwfm_softc *);
+ int (*bs_txdata)(struct bwfm_softc *, struct mbuf *);
+ int (*bs_txctl)(struct bwfm_softc *, char *, size_t);
+ int (*bs_rxctl)(struct bwfm_softc *, char *, size_t *);
+};
+
+struct bwfm_buscore_ops {
+ uint32_t (*bc_read)(struct bwfm_softc *, uint32_t);
+ void (*bc_write)(struct bwfm_softc *, uint32_t, uint32_t);
+ int (*bc_prepare)(struct bwfm_softc *);
+ int (*bc_reset)(struct bwfm_softc *);
+ int (*bc_setup)(struct bwfm_softc *);
+ void (*bc_activate)(struct bwfm_softc *, uint32_t);
+};
+
+struct bwfm_proto_ops {
+ int (*proto_query_dcmd)(struct bwfm_softc *, int, int,
+ char *, size_t *);
+ int (*proto_set_dcmd)(struct bwfm_softc *, int, int,
+ char *, size_t);
+};
+extern struct bwfm_proto_ops bwfm_proto_bcdc_ops;
+
+struct bwfm_softc {
+ struct device sc_dev;
+ struct ieee80211com sc_ic;
+ struct ifmedia sc_media;
+ struct bwfm_bus_ops *sc_bus_ops;
+ struct bwfm_buscore_ops *sc_buscore_ops;
+ struct bwfm_proto_ops *sc_proto_ops;
+ struct bwfm_chip sc_chip;
+ uint8_t sc_io_type;
+#define BWFM_IO_TYPE_D11N 1
+#define BWFM_IO_TYPE_D11AC 2
+
+ int sc_tx_timer;
+};
+
+void bwfm_attach(struct bwfm_softc *);
+int bwfm_detach(struct bwfm_softc *, int);
+int bwfm_chip_attach(struct bwfm_softc *);
+struct bwfm_core *bwfm_chip_get_core(struct bwfm_softc *, int);
+struct bwfm_core *bwfm_chip_get_pmu(struct bwfm_softc *);
+void bwfm_rx(struct bwfm_softc *, char *, size_t);
diff --git a/sys/dev/sdmmc/files.sdmmc b/sys/dev/sdmmc/files.sdmmc
index b90aaff855c..1b70aa8672f 100644
--- a/sys/dev/sdmmc/files.sdmmc
+++ b/sys/dev/sdmmc/files.sdmmc
@@ -1,4 +1,4 @@
-# $OpenBSD: files.sdmmc,v 1.4 2014/07/11 21:54:38 tedu Exp $
+# $OpenBSD: files.sdmmc,v 1.5 2017/10/11 17:19:50 patrick Exp $
#
# Config file and device description for machine-independent SD/MMC code.
# Included by ports that need it.
@@ -11,3 +11,6 @@ file dev/sdmmc/sdmmc_cis.c sdmmc
file dev/sdmmc/sdmmc_io.c sdmmc
file dev/sdmmc/sdmmc_mem.c sdmmc
file dev/sdmmc/sdmmc_scsi.c sdmmc
+
+attach bwfm at sdmmc with bwfm_sdio
+file dev/sdmmc/if_bwfm_sdio.c bwfm_sdio
diff --git a/sys/dev/sdmmc/if_bwfm_sdio.c b/sys/dev/sdmmc/if_bwfm_sdio.c
new file mode 100644
index 00000000000..d96285bdb0a
--- /dev/null
+++ b/sys/dev/sdmmc/if_bwfm_sdio.c
@@ -0,0 +1,451 @@
+/* $OpenBSD: if_bwfm_sdio.c,v 1.1 2017/10/11 17:19:50 patrick Exp $ */
+/*
+ * Copyright (c) 2010-2016 Broadcom Corporation
+ * Copyright (c) 2016,2017 Patrick Wildt <patrick@blueri.se>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <dev/sdmmc/sdmmcvar.h>
+
+#include <dev/ic/bwfmvar.h>
+#include <dev/ic/bwfmreg.h>
+
+#define BWFM_SDIO_CCCR_BRCM_CARDCAP 0xf0
+#define BWFM_SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT 0x02
+#define BWFM_SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT 0x04
+#define BWFM_SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC 0x08
+#define BWFM_SDIO_CCCR_BRCM_CARDCTRL 0xf1
+#define BWFM_SDIO_CCCR_BRCM_CARDCTRL_WLANRESET 0x02
+#define BWFM_SDIO_CCCR_BRCM_SEPINT 0xf2
+
+#ifdef BWFM_DEBUG
+#define DPRINTF(x) do { if (bwfm_debug > 0) printf x; } while (0)
+#define DPRINTFN(n, x) do { if (bwfm_debug >= (n)) printf x; } while (0)
+static int bwfm_debug = 1;
+#else
+#define DPRINTF(x) do { ; } while (0)
+#define DPRINTFN(n, x) do { ; } while (0)
+#endif
+
+#undef DEVNAME
+#define DEVNAME(sc) ((sc)->sc_sc.sc_dev.dv_xname)
+
+struct bwfm_sdio_softc {
+ struct bwfm_softc sc_sc;
+ struct sdmmc_function **sc_sf;
+ uint32_t sc_bar0;
+};
+
+int bwfm_sdio_match(struct device *, void *, void *);
+void bwfm_sdio_attach(struct device *, struct device *, void *);
+int bwfm_sdio_detach(struct device *, int);
+
+uint8_t bwfm_sdio_read_1(struct bwfm_sdio_softc *, uint32_t);
+uint32_t bwfm_sdio_read_4(struct bwfm_sdio_softc *, uint32_t);
+void bwfm_sdio_write_1(struct bwfm_sdio_softc *, uint32_t,
+ uint8_t);
+void bwfm_sdio_write_4(struct bwfm_sdio_softc *, uint32_t,
+ uint32_t);
+
+uint32_t bwfm_sdio_buscore_read(struct bwfm_softc *, uint32_t);
+void bwfm_sdio_buscore_write(struct bwfm_softc *, uint32_t,
+ uint32_t);
+int bwfm_sdio_buscore_prepare(struct bwfm_softc *);
+void bwfm_sdio_buscore_activate(struct bwfm_softc *, uint32_t);
+
+int bwfm_sdio_txdata(struct bwfm_softc *, struct mbuf *);
+int bwfm_sdio_txctl(struct bwfm_softc *, char *, size_t);
+int bwfm_sdio_rxctl(struct bwfm_softc *, char *, size_t *);
+
+struct bwfm_bus_ops bwfm_sdio_bus_ops = {
+ .bs_init = NULL,
+ .bs_stop = NULL,
+ .bs_txdata = bwfm_sdio_txdata,
+ .bs_txctl = bwfm_sdio_txctl,
+ .bs_rxctl = bwfm_sdio_rxctl,
+};
+
+struct bwfm_buscore_ops bwfm_sdio_buscore_ops = {
+ .bc_read = bwfm_sdio_buscore_read,
+ .bc_write = bwfm_sdio_buscore_write,
+ .bc_prepare = bwfm_sdio_buscore_prepare,
+ .bc_reset = NULL,
+ .bc_setup = NULL,
+ .bc_activate = bwfm_sdio_buscore_activate,
+};
+
+struct cfattach bwfm_sdio_ca = {
+ sizeof(struct bwfm_sdio_softc),
+ bwfm_sdio_match,
+ bwfm_sdio_attach,
+ bwfm_sdio_detach,
+};
+
+int
+bwfm_sdio_match(struct device *parent, void *match, void *aux)
+{
+ struct sdmmc_attach_args *saa = aux;
+ struct sdmmc_function *sf = saa->sf;
+ struct sdmmc_cis *cis;
+
+ /* Not SDIO. */
+ if (sf == NULL)
+ return 0;
+
+ /* Look for Broadcom 433[04]. */
+ cis = &sf->sc->sc_fn0->cis;
+ if (cis->manufacturer != 0x02d0 || (cis->product != 0x4330 &&
+ cis->product != 0x4334))
+ return 0;
+
+ /* We need both functions, but ... */
+ if (sf->sc->sc_function_count <= 1)
+ return 0;
+
+ /* ... only attach for one. */
+ if (sf->number != 1)
+ return 0;
+
+ return 1;
+}
+
+void
+bwfm_sdio_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct bwfm_sdio_softc *sc = (struct bwfm_sdio_softc *)self;
+ struct sdmmc_attach_args *saa = aux;
+ struct sdmmc_function *sf = saa->sf;
+ struct bwfm_core *core;
+
+ printf("\n");
+
+ rw_assert_wrlock(&sf->sc->sc_lock);
+
+ sc->sc_sf = mallocarray(sf->sc->sc_function_count + 1,
+ sizeof(struct sdmmc_function *), M_DEVBUF, M_WAITOK);
+
+ /* Copy all function pointers. */
+ SIMPLEQ_FOREACH(sf, &saa->sf->sc->sf_head, sf_list) {
+ sc->sc_sf[sf->number] = sf;
+ }
+ sf = saa->sf;
+
+ /*
+ * TODO: set block size to 64 for func 1, 512 for func 2.
+ * We might need to work on the SDMMC stack to be able to set
+ * a block size per function. Currently the IO code uses the
+ * SDHC controller's maximum block length.
+ */
+
+ /* Enable Function 1. */
+ if (sdmmc_io_function_enable(sc->sc_sf[1]) != 0) {
+ printf("%s: cannot enable function 1\n", DEVNAME(sc));
+ goto err;
+ }
+
+ DPRINTF(("%s: F1 signature read @0x18000000=%x\n", DEVNAME(sc),
+ bwfm_sdio_read_4(sc, 0x18000000)));
+
+ /* Force PLL off */
+ bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR,
+ BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF |
+ BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ);
+
+ sc->sc_sc.sc_buscore_ops = &bwfm_sdio_buscore_ops;
+ if (bwfm_chip_attach(&sc->sc_sc) != 0) {
+ printf("%s: cannot attach chip\n", DEVNAME(sc));
+ goto err;
+ }
+
+ /* TODO: drive strength */
+
+ bwfm_sdio_write_1(sc, BWFM_SDIO_CCCR_BRCM_CARDCTRL,
+ bwfm_sdio_read_1(sc, BWFM_SDIO_CCCR_BRCM_CARDCTRL) |
+ BWFM_SDIO_CCCR_BRCM_CARDCTRL_WLANRESET);
+
+ core = bwfm_chip_get_pmu(&sc->sc_sc);
+ bwfm_sdio_write_4(sc, core->co_base + BWFM_CHIP_REG_PMUCONTROL,
+ bwfm_sdio_read_4(sc, core->co_base + BWFM_CHIP_REG_PMUCONTROL) |
+ (BWFM_CHIP_REG_PMUCONTROL_RES_RELOAD <<
+ BWFM_CHIP_REG_PMUCONTROL_RES_SHIFT));
+
+ sc->sc_sc.sc_bus_ops = &bwfm_sdio_bus_ops;
+ sc->sc_sc.sc_proto_ops = &bwfm_proto_bcdc_ops;
+ bwfm_attach(&sc->sc_sc);
+
+ return;
+
+err:
+ free(sc->sc_sf, M_DEVBUF, 0);
+}
+
+int
+bwfm_sdio_detach(struct device *self, int flags)
+{
+ struct bwfm_sdio_softc *sc = (struct bwfm_sdio_softc *)self;
+
+ bwfm_detach(&sc->sc_sc, flags);
+
+ free(sc->sc_sf, M_DEVBUF, 0);
+
+ return 0;
+}
+
+uint8_t
+bwfm_sdio_read_1(struct bwfm_sdio_softc *sc, uint32_t addr)
+{
+ struct sdmmc_function *sf;
+ uint8_t rv;
+
+ /*
+ * figure out how to read the register based on address range
+ * 0x00 ~ 0x7FF: function 0 CCCR and FBR
+ * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
+ * The rest: function 1 silicon backplane core registers
+ */
+ if ((addr & ~0x7ff) == 0)
+ sf = sc->sc_sf[0];
+ else
+ sf = sc->sc_sf[1];
+
+ rv = sdmmc_io_read_1(sf, addr);
+ return rv;
+}
+
+uint32_t
+bwfm_sdio_read_4(struct bwfm_sdio_softc *sc, uint32_t addr)
+{
+ struct sdmmc_function *sf;
+ uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK;
+ uint32_t rv;
+
+ if (sc->sc_bar0 != bar0) {
+ bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRLOW,
+ (bar0 >> 8) & 0x80);
+ bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRMID,
+ (bar0 >> 16) & 0xff);
+ bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRHIGH,
+ (bar0 >> 24) & 0xff);
+ sc->sc_bar0 = bar0;
+ }
+
+ addr &= BWFM_SDIO_SB_OFT_ADDR_MASK;
+ addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG;
+
+ /*
+ * figure out how to read the register based on address range
+ * 0x00 ~ 0x7FF: function 0 CCCR and FBR
+ * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
+ * The rest: function 1 silicon backplane core registers
+ */
+ if ((addr & ~0x7ff) == 0)
+ sf = sc->sc_sf[0];
+ else
+ sf = sc->sc_sf[1];
+
+ rv = sdmmc_io_read_4(sf, addr);
+ return rv;
+}
+
+void
+bwfm_sdio_write_1(struct bwfm_sdio_softc *sc, uint32_t addr, uint8_t data)
+{
+ struct sdmmc_function *sf;
+
+ /*
+ * figure out how to read the register based on address range
+ * 0x00 ~ 0x7FF: function 0 CCCR and FBR
+ * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
+ * The rest: function 1 silicon backplane core registers
+ */
+ if ((addr & ~0x7ff) == 0)
+ sf = sc->sc_sf[0];
+ else
+ sf = sc->sc_sf[1];
+
+ sdmmc_io_write_1(sf, addr, data);
+}
+
+void
+bwfm_sdio_write_4(struct bwfm_sdio_softc *sc, uint32_t addr, uint32_t data)
+{
+ struct sdmmc_function *sf;
+ uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK;
+
+ if (sc->sc_bar0 != bar0) {
+ bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRLOW,
+ (bar0 >> 8) & 0x80);
+ bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRMID,
+ (bar0 >> 16) & 0xff);
+ bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRHIGH,
+ (bar0 >> 24) & 0xff);
+ sc->sc_bar0 = bar0;
+ }
+
+ addr &= BWFM_SDIO_SB_OFT_ADDR_MASK;
+ addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG;
+
+ /*
+ * figure out how to read the register based on address range
+ * 0x00 ~ 0x7FF: function 0 CCCR and FBR
+ * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
+ * The rest: function 1 silicon backplane core registers
+ */
+ if ((addr & ~0x7ff) == 0)
+ sf = sc->sc_sf[0];
+ else
+ sf = sc->sc_sf[1];
+
+ sdmmc_io_write_4(sf, addr, data);
+}
+
+uint32_t
+bwfm_sdio_buscore_read(struct bwfm_softc *bwfm, uint32_t reg)
+{
+ struct bwfm_sdio_softc *sc = (void *)bwfm;
+ uint32_t val;
+
+ val = bwfm_sdio_read_4(sc, reg);
+ /* TODO: Workaround for 4335/4339 */
+
+ return val;
+}
+
+void
+bwfm_sdio_buscore_write(struct bwfm_softc *bwfm, uint32_t reg, uint32_t val)
+{
+ struct bwfm_sdio_softc *sc = (void *)bwfm;
+ bwfm_sdio_write_4(sc, reg, val);
+}
+
+int
+bwfm_sdio_buscore_prepare(struct bwfm_softc *bwfm)
+{
+ struct bwfm_sdio_softc *sc = (void *)bwfm;
+ uint8_t clkval, clkset, clkmask;
+ int i;
+
+ clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ |
+ BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF;
+ bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clkset);
+
+ clkmask = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL |
+ BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL;
+ clkval = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR);
+
+ if ((clkval & ~clkmask) != clkset) {
+ printf("%s: wrote 0x%02x read 0x%02x\n", DEVNAME(sc),
+ clkset, clkval);
+ return 1;
+ }
+
+ for (i = 1000; i > 0; i--) {
+ clkval = bwfm_sdio_read_1(sc,
+ BWFM_SDIO_FUNC1_CHIPCLKCSR);
+ if (clkval & clkmask)
+ break;
+ }
+ if (i == 0) {
+ printf("%s: timeout on ALPAV wait, clkval 0x%02x\n",
+ DEVNAME(sc), clkval);
+ return 1;
+ }
+
+ clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF |
+ BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_ALP;
+ bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clkset);
+ delay(65);
+
+ bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SDIOPULLUP, 0);
+
+ return 0;
+}
+
+void
+bwfm_sdio_buscore_activate(struct bwfm_softc *bwfm, uint32_t rstvec)
+{
+ struct bwfm_sdio_softc *sc = (void *)bwfm;
+ struct bwfm_core *core;
+
+ core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV);
+ bwfm_sdio_buscore_write(&sc->sc_sc,
+ core->co_base + BWFM_SDPCMD_INTSTATUS, 0xFFFFFFFF);
+
+#if notyet
+ if (rstvec)
+ bwfm_sdio_ram_write(&sc->sc_sc, 0, &rstvec, sizeof(rstvec));
+#endif
+}
+
+int
+bwfm_sdio_txdata(struct bwfm_softc *bwfm, struct mbuf *m)
+{
+#ifdef BWFM_DEBUG
+ struct bwfm_sdio_softc *sc = (void *)bwfm;
+#endif
+ int ret = 1;
+
+ DPRINTF(("%s: %s\n", DEVNAME(sc), __func__));
+
+ return ret;
+}
+
+int
+bwfm_sdio_txctl(struct bwfm_softc *bwfm, char *buf, size_t len)
+{
+#ifdef BWFM_DEBUG
+ struct bwfm_sdio_softc *sc = (void *)bwfm;
+#endif
+ int ret = 1;
+
+ DPRINTF(("%s: %s\n", DEVNAME(sc), __func__));
+
+ return ret;
+}
+
+int
+bwfm_sdio_rxctl(struct bwfm_softc *bwfm, char *buf, size_t *len)
+{
+#ifdef BWFM_DEBUG
+ struct bwfm_sdio_softc *sc = (void *)bwfm;
+#endif
+ int ret = 1;
+
+ DPRINTF(("%s: %s\n", DEVNAME(sc), __func__));
+
+ return ret;
+}
diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb
index 34cd81b8c4c..551fd9dd356 100644
--- a/sys/dev/usb/files.usb
+++ b/sys/dev/usb/files.usb
@@ -1,4 +1,4 @@
-# $OpenBSD: files.usb,v 1.133 2017/08/29 03:35:11 deraadt Exp $
+# $OpenBSD: files.usb,v 1.134 2017/10/11 17:19:50 patrick Exp $
# $NetBSD: files.usb,v 1.16 2000/02/14 20:29:54 augustss Exp $
#
# Config file and device description for machine-independent USB code.
@@ -440,3 +440,6 @@ file dev/usb/upd.c upd
device uwacom: hid, hidms, wsmousedev
attach uwacom at uhidbus
file dev/usb/uwacom.c uwacom
+
+attach bwfm at uhub with bwfm_usb: firmload
+file dev/usb/if_bwfm_usb.c bwfm_usb
diff --git a/sys/dev/usb/if_bwfm_usb.c b/sys/dev/usb/if_bwfm_usb.c
new file mode 100644
index 00000000000..a90f9634f6d
--- /dev/null
+++ b/sys/dev/usb/if_bwfm_usb.c
@@ -0,0 +1,698 @@
+/* $OpenBSD: if_bwfm_usb.c,v 1.1 2017/10/11 17:19:50 patrick Exp $ */
+/*
+ * Copyright (c) 2010-2016 Broadcom Corporation
+ * Copyright (c) 2016,2017 Patrick Wildt <patrick@blueri.se>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdevs.h>
+
+#include <dev/ic/bwfmvar.h>
+#include <dev/ic/bwfmreg.h>
+
+/*
+ * Various supported device vendors/products.
+ */
+static const struct usb_devno bwfm_usbdevs[] = {
+ { USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM43143 },
+ { USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM43236 },
+ { USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM43242 },
+ { USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM43569 },
+ { USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCMFW },
+};
+
+#ifdef BWFM_DEBUG
+#define DPRINTF(x) do { if (bwfm_debug > 0) printf x; } while (0)
+#define DPRINTFN(n, x) do { if (bwfm_debug >= (n)) printf x; } while (0)
+static int bwfm_debug = 2;
+#else
+#define DPRINTF(x) do { ; } while (0)
+#define DPRINTFN(n, x) do { ; } while (0)
+#endif
+
+#define DEVNAME(sc) ((sc)->sc_sc.sc_dev.dv_xname)
+
+#define BRCMF_POSTBOOT_ID 0xA123 /* ID to detect if dongle
+ * has boot up
+ */
+
+#define TRX_MAGIC 0x30524448 /* "HDR0" */
+#define TRX_MAX_OFFSET 3 /* Max number of file offsets */
+#define TRX_UNCOMP_IMAGE 0x20 /* Trx holds uncompressed img */
+#define TRX_RDL_CHUNK 1500 /* size of each dl transfer */
+#define TRX_OFFSETS_DLFWLEN_IDX 0
+
+/* Control messages: bRequest values */
+#define DL_GETSTATE 0 /* returns the rdl_state_t struct */
+#define DL_CHECK_CRC 1 /* currently unused */
+#define DL_GO 2 /* execute downloaded image */
+#define DL_START 3 /* initialize dl state */
+#define DL_REBOOT 4 /* reboot the device in 2 seconds */
+#define DL_GETVER 5 /* returns the bootrom_id_t struct */
+#define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset
+ * event to occur in 2 seconds. It is the
+ * responsibility of the downloaded code to
+ * clear this event
+ */
+#define DL_EXEC 7 /* jump to a supplied address */
+#define DL_RESETCFG 8 /* To support single enum on dongle
+ * - Not used by bootloader
+ */
+#define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup
+ * if resp unavailable
+ */
+
+/* states */
+#define DL_WAITING 0 /* waiting to rx first pkt */
+#define DL_READY 1 /* hdr was good, waiting for more of the
+ * compressed image
+ */
+#define DL_BAD_HDR 2 /* hdr was corrupted */
+#define DL_BAD_CRC 3 /* compressed image was corrupted */
+#define DL_RUNNABLE 4 /* download was successful,waiting for go cmd */
+#define DL_START_FAIL 5 /* failed to initialize correctly */
+#define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM
+ * value
+ */
+#define DL_IMAGE_TOOBIG 7 /* firmware image too big */
+
+
+struct trx_header {
+ uint32_t magic; /* "HDR0" */
+ uint32_t len; /* Length of file including header */
+ uint32_t crc32; /* CRC from flag_version to end of file */
+ uint32_t flag_version; /* 0:15 flags, 16:31 version */
+ uint32_t offsets[TRX_MAX_OFFSET];/* Offsets of partitions from start of
+ * header
+ */
+};
+
+struct rdl_state {
+ uint32_t state;
+ uint32_t bytes;
+};
+
+struct bootrom_id {
+ uint32_t chip; /* Chip id */
+ uint32_t chiprev; /* Chip rev */
+ uint32_t ramsize; /* Size of RAM */
+ uint32_t remapbase; /* Current remap base address */
+ uint32_t boardtype; /* Type of board */
+ uint32_t boardrev; /* Board revision */
+};
+
+#define BWFM_RXBUFSZ 1600
+#define BWFM_TXBUFSZ 1600
+struct bwfm_usb_softc {
+ struct bwfm_softc sc_sc;
+ struct usbd_device *sc_udev;
+ struct usbd_interface *sc_iface;
+ uint8_t sc_ifaceno;
+
+ uint16_t sc_vendor;
+ uint16_t sc_product;
+
+ uint32_t sc_chip;
+ uint32_t sc_chiprev;
+
+ int sc_rx_no;
+ int sc_tx_no;
+
+ struct usbd_pipe *sc_rx_pipeh;
+ struct usbd_pipe *sc_tx_pipeh;
+
+ struct usbd_xfer *sc_rx_xfer;
+ char *sc_rx_buf;
+
+ struct usbd_xfer *sc_tx_xfer;
+ char *sc_tx_buf;
+};
+
+int bwfm_usb_match(struct device *, void *, void *);
+void bwfm_usb_attachhook(struct device *);
+void bwfm_usb_attach(struct device *, struct device *, void *);
+int bwfm_usb_detach(struct device *, int);
+
+int bwfm_usb_dl_cmd(struct bwfm_usb_softc *, uint8_t, void *, int);
+int bwfm_usb_load_microcode(struct bwfm_usb_softc *, const u_char *,
+ size_t);
+
+int bwfm_usb_txdata(struct bwfm_softc *, struct mbuf *);
+int bwfm_usb_txctl(struct bwfm_softc *, char *, size_t);
+int bwfm_usb_rxctl(struct bwfm_softc *, char *, size_t *);
+
+void bwfm_usb_rxeof(struct usbd_xfer *, void *, usbd_status);
+void bwfm_usb_txeof(struct usbd_xfer *, void *, usbd_status);
+
+struct bwfm_bus_ops bwfm_usb_bus_ops = {
+ .bs_init = NULL,
+ .bs_stop = NULL,
+ .bs_txdata = bwfm_usb_txdata,
+ .bs_txctl = bwfm_usb_txctl,
+ .bs_rxctl = bwfm_usb_rxctl,
+};
+
+struct cfattach bwfm_usb_ca = {
+ sizeof(struct bwfm_usb_softc),
+ bwfm_usb_match,
+ bwfm_usb_attach,
+ bwfm_usb_detach,
+};
+
+int
+bwfm_usb_match(struct device *parent, void *match, void *aux)
+{
+ struct usb_attach_arg *uaa = aux;
+
+ if (uaa->iface == NULL || uaa->configno != 1)
+ return UMATCH_NONE;
+
+ return (usb_lookup(bwfm_usbdevs, uaa->vendor, uaa->product) != NULL) ?
+ UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE;
+}
+
+void
+bwfm_usb_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct bwfm_usb_softc *sc = (struct bwfm_usb_softc *)self;
+ struct usb_attach_arg *uaa = aux;
+ usb_device_descriptor_t *dd;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ int i;
+
+ sc->sc_udev = uaa->device;
+ sc->sc_iface = uaa->iface;
+ sc->sc_ifaceno = uaa->ifaceno;
+ sc->sc_vendor = uaa->vendor;
+ sc->sc_product = uaa->product;
+ sc->sc_sc.sc_bus_ops = &bwfm_usb_bus_ops;
+ sc->sc_sc.sc_proto_ops = &bwfm_proto_bcdc_ops;
+
+ /* Check number of configurations. */
+ dd = usbd_get_device_descriptor(sc->sc_udev);
+ if (dd->bNumConfigurations != 1) {
+ printf("%s: number of configurations not supported\n",
+ DEVNAME(sc));
+ return;
+ }
+
+ /* Get endpoints. */
+ id = usbd_get_interface_descriptor(sc->sc_iface);
+
+ sc->sc_rx_no = sc->sc_tx_no = -1;
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
+ if (ed == NULL) {
+ printf("%s: no endpoint descriptor for iface %d\n",
+ DEVNAME(sc), i);
+ return;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK &&
+ sc->sc_rx_no == -1)
+ sc->sc_rx_no = ed->bEndpointAddress;
+ else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK &&
+ sc->sc_tx_no == -1)
+ sc->sc_tx_no = ed->bEndpointAddress;
+ }
+ if (sc->sc_rx_no == -1 || sc->sc_tx_no == -1) {
+ printf("%s: missing endpoint\n", DEVNAME(sc));
+ return;
+ }
+
+ config_mountroot(self, bwfm_usb_attachhook);
+}
+
+void
+bwfm_usb_attachhook(struct device *self)
+{
+ struct bwfm_usb_softc *sc = (struct bwfm_usb_softc *)self;
+ const char *name = NULL;
+ struct bootrom_id brom;
+ usbd_status error;
+ u_char *ucode;
+ size_t size;
+ int i;
+
+ /* Read chip id and chip rev to check the firmware. */
+ memset(&brom, 0, sizeof(brom));
+ bwfm_usb_dl_cmd(sc, DL_GETVER, &brom, sizeof(brom));
+ sc->sc_chip = letoh32(brom.chip);
+ sc->sc_chiprev = letoh32(brom.chiprev);
+
+ /* Setup data pipes */
+ error = usbd_open_pipe(sc->sc_iface, sc->sc_rx_no, USBD_EXCLUSIVE_USE,
+ &sc->sc_rx_pipeh);
+ if (error != 0) {
+ printf("%s: could not open rx pipe: %s\n",
+ DEVNAME(sc), usbd_errstr(error));
+ return;
+ }
+ error = usbd_open_pipe(sc->sc_iface, sc->sc_tx_no, USBD_EXCLUSIVE_USE,
+ &sc->sc_tx_pipeh);
+ if (error != 0) {
+ printf("%s: could not open tx pipe: %s\n",
+ DEVNAME(sc), usbd_errstr(error));
+ return;
+ }
+
+ /* Firmware not yet loaded? */
+ if (sc->sc_chip != BRCMF_POSTBOOT_ID) {
+ switch (sc->sc_chip)
+ {
+ case BRCM_CC_43143_CHIP_ID:
+ name = "brcmfmac43143.bin";
+ break;
+ case BRCM_CC_43235_CHIP_ID:
+ case BRCM_CC_43236_CHIP_ID:
+ case BRCM_CC_43238_CHIP_ID:
+ if (sc->sc_chiprev == 3)
+ name = "brcmfmac43236b.bin";
+ break;
+ case BRCM_CC_43242_CHIP_ID:
+ name = "brcmfmac43242a.bin";
+ break;
+ case BRCM_CC_43566_CHIP_ID:
+ case BRCM_CC_43569_CHIP_ID:
+ name = "brcmfmac43569.bin";
+ break;
+ default:
+ break;
+ }
+
+ if (name == NULL) {
+ printf("%s: unknown firmware\n", DEVNAME(sc));
+ return;
+ }
+
+ if (loadfirmware(name, &ucode, &size) != 0) {
+ printf("%s: failed loadfirmware of file %s\n",
+ DEVNAME(sc), name);
+ return;
+ }
+
+ if (bwfm_usb_load_microcode(sc, ucode, size) != 0) {
+ printf("%s: could not load microcode\n",
+ DEVNAME(sc));
+ return;
+ }
+
+ free(ucode, M_DEVBUF, 0);
+
+ for (i = 0; i < 10; i++) {
+ delay(100 * 1000);
+ memset(&brom, 0, sizeof(brom));
+ bwfm_usb_dl_cmd(sc, DL_GETVER, &brom, sizeof(brom));
+ if (letoh32(brom.chip) == BRCMF_POSTBOOT_ID)
+ break;
+ }
+
+ if (letoh32(brom.chip) != BRCMF_POSTBOOT_ID) {
+ printf("%s: firmware did not start up\n",
+ DEVNAME(sc));
+ return;
+ }
+
+ sc->sc_chip = letoh32(brom.chip);
+ sc->sc_chiprev = letoh32(brom.chiprev);
+ printf("%s: firmware loaded\n", DEVNAME(sc));
+ }
+
+ bwfm_usb_dl_cmd(sc, DL_RESETCFG, &brom, sizeof(brom));
+
+ sc->sc_rx_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_rx_xfer == NULL) {
+ printf("%s: cannot alloc xfer\n", DEVNAME(sc));
+ return;
+ }
+
+ sc->sc_rx_buf = usbd_alloc_buffer(sc->sc_rx_xfer, BWFM_RXBUFSZ);
+ if (sc->sc_rx_buf == NULL) {
+ printf("%s: cannot alloc buf\n", DEVNAME(sc));
+ return;
+ }
+ usbd_setup_xfer(sc->sc_rx_xfer, sc->sc_rx_pipeh, sc, sc->sc_rx_buf,
+ BWFM_RXBUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT,
+ bwfm_usb_rxeof);
+ error = usbd_transfer(sc->sc_rx_xfer);
+ if (error != 0 && error != USBD_IN_PROGRESS)
+ printf("%s: could not set up new transfer: %s\n",
+ DEVNAME(sc), usbd_errstr(error));
+
+ sc->sc_tx_xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (sc->sc_tx_xfer == NULL) {
+ printf("%s: cannot alloc xfer\n", DEVNAME(sc));
+ return;
+ }
+
+ sc->sc_tx_buf = usbd_alloc_buffer(sc->sc_tx_xfer, BWFM_TXBUFSZ);
+ if (sc->sc_tx_buf == NULL) {
+ printf("%s: cannot alloc buf\n", DEVNAME(sc));
+ return;
+ }
+
+ bwfm_attach(&sc->sc_sc);
+}
+
+void
+bwfm_usb_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
+{
+ struct bwfm_usb_softc *sc = priv;
+ struct bwfm_proto_bcdc_hdr *hdr;
+ usbd_status error;
+ uint32_t len, off;
+
+ DPRINTFN(2, ("%s: %s status %s\n", DEVNAME(sc), __func__,
+ usbd_errstr(status)));
+
+ if (usbd_is_dying(sc->sc_udev))
+ return;
+
+ if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
+ usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh);
+ if (status != USBD_CANCELLED)
+ goto resubmit;
+ return;
+ }
+ usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
+
+ hdr = (void *)sc->sc_rx_buf;
+ if (len < sizeof(*hdr))
+ return;
+ len -= sizeof(*hdr);
+ off += sizeof(*hdr);
+ if (len < hdr->data_offset << 2)
+ return;
+ len -= hdr->data_offset << 2;
+ off += hdr->data_offset << 2;
+
+ bwfm_rx(&sc->sc_sc, &sc->sc_rx_buf[off], len);
+
+ usbd_setup_xfer(sc->sc_rx_xfer, sc->sc_rx_pipeh, sc, sc->sc_rx_buf,
+ BWFM_RXBUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT,
+ bwfm_usb_rxeof);
+
+resubmit:
+ error = usbd_transfer(sc->sc_rx_xfer);
+ if (error != 0 && error != USBD_IN_PROGRESS)
+ printf("%s: could not set up new transfer: %s\n",
+ DEVNAME(sc), usbd_errstr(error));
+}
+
+void
+bwfm_usb_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
+{
+ struct bwfm_usb_softc *sc = priv;
+ usbd_status error;
+
+ DPRINTFN(2, ("%s: %s status %s\n", DEVNAME(sc), __func__,
+ usbd_errstr(status)));
+
+ if (usbd_is_dying(sc->sc_udev))
+ return;
+
+ if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
+ usbd_clear_endpoint_stall_async(sc->sc_tx_pipeh);
+ if (status != USBD_CANCELLED)
+ goto resubmit;
+ return;
+ }
+
+ // FIXME: leak
+ //m_freem(m);
+ return;
+
+resubmit:
+ error = usbd_transfer(sc->sc_tx_xfer);
+ if (error != 0 && error != USBD_IN_PROGRESS)
+ printf("%s: could not set up new transfer: %s\n",
+ DEVNAME(sc), usbd_errstr(error));
+}
+
+int
+bwfm_usb_detach(struct device *self, int flags)
+{
+ struct bwfm_usb_softc *sc = (struct bwfm_usb_softc *)self;
+
+ bwfm_detach(&sc->sc_sc, flags);
+
+ if (sc->sc_rx_pipeh != NULL) {
+ usbd_abort_pipe(sc->sc_rx_pipeh);
+ usbd_close_pipe(sc->sc_rx_pipeh);
+ }
+ if (sc->sc_tx_pipeh != NULL) {
+ usbd_abort_pipe(sc->sc_tx_pipeh);
+ usbd_close_pipe(sc->sc_tx_pipeh);
+ }
+
+ return 0;
+}
+
+int
+bwfm_usb_dl_cmd(struct bwfm_usb_softc *sc, uByte cmd, void *buf, int len)
+{
+ usb_device_request_t req;
+ usbd_status error;
+
+ req.bmRequestType = UT_READ_VENDOR_INTERFACE;
+ req.bRequest = cmd;
+
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_ifaceno);
+ USETW(req.wLength, len);
+
+ error = usbd_do_request(sc->sc_udev, &req, buf);
+ if (error != 0) {
+ printf("%s: could not read register: %s\n",
+ DEVNAME(sc), usbd_errstr(error));
+ }
+ return error;
+}
+
+int
+bwfm_usb_load_microcode(struct bwfm_usb_softc *sc, const u_char *ucode, size_t size)
+{
+ struct trx_header *trx = (struct trx_header *)ucode;
+ struct rdl_state state;
+ uint32_t rdlstate, rdlbytes, sent = 0, sendlen = 0;
+ struct usbd_xfer *xfer;
+ usbd_status error;
+ char *buf;
+
+ if (letoh32(trx->magic) != TRX_MAGIC ||
+ (letoh32(trx->flag_version) & TRX_UNCOMP_IMAGE) == 0) {
+ printf("%s: invalid firmware\n", DEVNAME(sc));
+ return 1;
+ }
+
+ bwfm_usb_dl_cmd(sc, DL_START, &state, sizeof(state));
+ rdlstate = letoh32(state.state);
+ rdlbytes = letoh32(state.bytes);
+
+ if (rdlstate != DL_WAITING) {
+ printf("%s: cannot start fw download\n", DEVNAME(sc));
+ return 1;
+ }
+
+ xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (xfer == NULL) {
+ printf("%s: cannot alloc xfer\n", DEVNAME(sc));
+ goto err;
+ }
+
+ buf = usbd_alloc_buffer(xfer, TRX_RDL_CHUNK);
+ if (buf == NULL) {
+ printf("%s: cannot alloc buf\n", DEVNAME(sc));
+ goto err;
+ }
+
+ while (rdlbytes != size) {
+ sendlen = MIN(size - sent, TRX_RDL_CHUNK);
+ memcpy(buf, ucode + sent, sendlen);
+
+ usbd_setup_xfer(xfer, sc->sc_tx_pipeh, NULL, buf, sendlen,
+ USBD_SYNCHRONOUS | USBD_NO_COPY, USBD_NO_TIMEOUT, NULL);
+ error = usbd_transfer(xfer);
+ if (error != 0 && error != USBD_IN_PROGRESS) {
+ printf("%s: transfer error\n", DEVNAME(sc));
+ goto err;
+ }
+ sent += sendlen;
+
+ bwfm_usb_dl_cmd(sc, DL_GETSTATE, &state, sizeof(state));
+ rdlstate = letoh32(state.state);
+ rdlbytes = letoh32(state.bytes);
+
+ if (rdlbytes != sent) {
+ printf("%s: device reported different size\n",
+ DEVNAME(sc));
+ goto err;
+ }
+
+ if (rdlstate == DL_BAD_HDR || rdlstate == DL_BAD_CRC) {
+ printf("%s: device reported bad hdr/crc\n",
+ DEVNAME(sc));
+ goto err;
+ }
+ }
+
+ bwfm_usb_dl_cmd(sc, DL_GETSTATE, &state, sizeof(state));
+ rdlstate = letoh32(state.state);
+ rdlbytes = letoh32(state.bytes);
+
+ if (rdlstate != DL_RUNNABLE) {
+ printf("%s: dongle not runnable\n", DEVNAME(sc));
+ goto err;
+ }
+
+ bwfm_usb_dl_cmd(sc, DL_GO, &state, sizeof(state));
+
+ return 0;
+err:
+ if (sc->sc_tx_pipeh != NULL) {
+ usbd_abort_pipe(sc->sc_tx_pipeh);
+ usbd_close_pipe(sc->sc_tx_pipeh);
+ sc->sc_tx_pipeh = NULL;
+ }
+ if (xfer != NULL)
+ usbd_free_xfer(xfer);
+ return 1;
+}
+
+int
+bwfm_usb_txdata(struct bwfm_softc *bwfm, struct mbuf *m)
+{
+ struct bwfm_usb_softc *sc = (void *)bwfm;
+ struct bwfm_proto_bcdc_hdr *hdr;
+ uint32_t len = 0;
+ int error;
+
+ DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__));
+
+ hdr = (void *)&sc->sc_tx_buf[len];
+ hdr->data_offset = 0;
+ hdr->flags = BWFM_BCDC_FLAG_VER(BWFM_BCDC_FLAG_PROTO_VER);
+ len += sizeof(*hdr);
+
+ m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)&sc->sc_tx_buf[len]);
+ len += m->m_pkthdr.len;
+ //m_freem(m);
+
+ usbd_setup_xfer(sc->sc_tx_xfer, sc->sc_tx_pipeh, sc, sc->sc_tx_buf,
+ len, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, USBD_NO_TIMEOUT,
+ bwfm_usb_txeof);
+ error = usbd_transfer(sc->sc_tx_xfer);
+ if (error != 0 && error != USBD_IN_PROGRESS)
+ printf("%s: could not set up new transfer: %s\n",
+ DEVNAME(sc), usbd_errstr(error));
+ return 0;
+}
+
+int
+bwfm_usb_txctl(struct bwfm_softc *bwfm, char *buf, size_t len)
+{
+ struct bwfm_usb_softc *sc = (void *)bwfm;
+ usb_device_request_t req;
+ usbd_status error;
+ int ret = 1;
+
+ DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__));
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = 0;
+
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_ifaceno);
+ USETW(req.wLength, len);
+
+ error = usbd_do_request(sc->sc_udev, &req, buf);
+ if (error != 0) {
+ printf("%s: could not read ctl packet: %s\n",
+ DEVNAME(sc), usbd_errstr(error));
+ goto err;
+ }
+
+ ret = 0;
+err:
+ return ret;
+}
+
+int
+bwfm_usb_rxctl(struct bwfm_softc *bwfm, char *buf, size_t *len)
+{
+ struct bwfm_usb_softc *sc = (void *)bwfm;
+ usb_device_request_t req;
+ usbd_status error;
+ uint32_t len32;
+ int ret = 1;
+
+ DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__));
+
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = 1;
+
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_ifaceno);
+ USETW(req.wLength, *len);
+
+ error = usbd_do_request_flags(sc->sc_udev, &req, buf, 0,
+ &len32, USBD_DEFAULT_TIMEOUT);
+ if (error != 0) {
+ printf("%s: could not read ctl packet: %s\n",
+ DEVNAME(sc), usbd_errstr(error));
+ goto err;
+ }
+
+ if (len32 > *len) {
+ printf("%s: broken length\n", DEVNAME(sc));
+ goto err;
+ }
+
+ *len = len32;
+ ret = 0;
+err:
+ return ret;
+}
diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs
index 46811894b25..8a8a4706c4d 100644
--- a/sys/dev/usb/usbdevs
+++ b/sys/dev/usb/usbdevs
@@ -1,4 +1,4 @@
-$OpenBSD: usbdevs,v 1.677 2017/08/26 12:45:16 abieber Exp $
+$OpenBSD: usbdevs,v 1.678 2017/10/11 17:19:50 patrick Exp $
/* $NetBSD: usbdevs,v 1.322 2003/05/10 17:47:14 hamajima Exp $ */
/*
@@ -1239,8 +1239,13 @@ product BILLIONTON USBE100 0x8511 USBE100
product BILLIONTON USB2AR 0x90ff USB2AR Ethernet
/* Broadcom products */
+product BROADCOM BCMFW 0x0bdc BCMFW
product BROADCOM BCM2033 0x2000 BCM2033
product BROADCOM BCM2033NF 0x2033 BCM2033 (no fw)
+product BROADCOM BCM43236 0xbd17 BCM43236
+product BROADCOM BCM43143 0xbd1e BCM43143
+product BROADCOM BCM43242 0xbd1f BCM43242
+product BROADCOM BCM43569 0xbd27 BCM43569
/* Brother Industries products */
product BROTHER HL1050 0x0002 HL-1050 laser printer