Module Name:    src
Committed By:   jmcneill
Date:           Thu Oct 19 23:58:41 UTC 2017

Modified Files:
        src/sys/conf: files
        src/sys/dev/usb: files.usb
Added Files:
        src/sys/dev/ic: bwfm.c bwfmreg.h bwfmvar.h
        src/sys/dev/usb: if_bwfm_usb.c

Log Message:
Add driver for Broadcom 802.11a/b/g/n/ac USB wireless devices, based on
the OpenBSD bwfm(4) driver.


To generate a diff of this commit:
cvs rdiff -u -r1.1182 -r1.1183 src/sys/conf/files
cvs rdiff -u -r0 -r1.1 src/sys/dev/ic/bwfm.c src/sys/dev/ic/bwfmreg.h \
    src/sys/dev/ic/bwfmvar.h
cvs rdiff -u -r1.146 -r1.147 src/sys/dev/usb/files.usb
cvs rdiff -u -r0 -r1.1 src/sys/dev/usb/if_bwfm_usb.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/conf/files
diff -u src/sys/conf/files:1.1182 src/sys/conf/files:1.1183
--- src/sys/conf/files:1.1182	Mon Oct  9 10:28:33 2017
+++ src/sys/conf/files	Thu Oct 19 23:58:41 2017
@@ -1,4 +1,4 @@
-#	$NetBSD: files,v 1.1182 2017/10/09 10:28:33 kre Exp $
+#	$NetBSD: files,v 1.1183 2017/10/19 23:58:41 jmcneill Exp $
 #	@(#)files.newconf	7.5 (Berkeley) 5/10/93
 
 version 	20170615
@@ -1415,6 +1415,10 @@ file	dev/ic/nvme.c			nvme
 attach	ld at nvme with ld_nvme
 file	dev/ic/ld_nvme.c		ld_nvme
 
+# Broadcom FullMAC 802.11 driver
+device	bwfm: arp, ifnet, wlan
+file	dev/ic/bwfm.c			bwfm
+
 # legitimate pseudo-devices
 #
 defpseudodev vnd:	disk

Index: src/sys/dev/usb/files.usb
diff -u src/sys/dev/usb/files.usb:1.146 src/sys/dev/usb/files.usb:1.147
--- src/sys/dev/usb/files.usb:1.146	Thu Aug 31 19:55:43 2017
+++ src/sys/dev/usb/files.usb	Thu Oct 19 23:58:41 2017
@@ -1,4 +1,4 @@
-#	$NetBSD: files.usb,v 1.146 2017/08/31 19:55:43 jmcneill Exp $
+#	$NetBSD: files.usb,v 1.147 2017/10/19 23:58:41 jmcneill Exp $
 #
 # Config file and device description for machine-independent USB code.
 # Included by ports that need it.  Ports that use it must provide
@@ -551,3 +551,7 @@ file	dev/usb/udsir.c			udsir
 device	ualea
 attach	ualea at usbifif
 file	dev/usb/ualea.c			ualea
+
+# Broadcom FullMAC USB wireless adapter
+attach	bwfm at usbdevif with bwfm_usb: firmload
+file	dev/usb/if_bwfm_usb.c		bwfm_usb

Added files:

Index: src/sys/dev/ic/bwfm.c
diff -u /dev/null src/sys/dev/ic/bwfm.c:1.1
--- /dev/null	Thu Oct 19 23:58:41 2017
+++ src/sys/dev/ic/bwfm.c	Thu Oct 19 23:58:41 2017
@@ -0,0 +1,1701 @@
+/* $NetBSD: bwfm.c,v 1.1 2017/10/19 23:58:41 jmcneill Exp $ */
+/* $OpenBSD: bwfm.c,v 1.5 2017/10/16 22:27:16 patrick Exp $ */
+/*
+ * Copyright (c) 2010-2016 Broadcom Corporation
+ * Copyright (c) 2016,2017 Patrick Wildt <patr...@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 <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/kmem.h>
+#include <sys/workqueue.h>
+#include <sys/pcq.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_ether.h>
+
+#include <netinet/in.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)	device_xname((sc)->sc_dev)
+
+void	 bwfm_start(struct ifnet *);
+int	 bwfm_init(struct ifnet *);
+void	 bwfm_stop(struct ifnet *, int);
+void	 bwfm_watchdog(struct ifnet *);
+int	 bwfm_ioctl(struct ifnet *, u_long, void *);
+int	 bwfm_media_change(struct ifnet *);
+void	 bwfm_media_status(struct ifnet *, struct ifmediareq *);
+
+int	 bwfm_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
+	     int, int);
+void	 bwfm_recv_mgmt(struct ieee80211com *, struct mbuf *,
+	     struct ieee80211_node *, int, int, uint32_t);
+int	 bwfm_key_set(struct ieee80211com *, const struct ieee80211_key *,
+	     const uint8_t *);
+int	 bwfm_key_delete(struct ieee80211com *, const struct ieee80211_key *);
+int	 bwfm_newstate(struct ieee80211com *, enum ieee80211_state, int);
+void	 bwfm_newstate_cb(struct bwfm_softc *, struct bwfm_cmd_newstate *);
+void	 bwfm_task(struct work *, void *);
+
+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 *, const char *, void *, size_t);
+int	 bwfm_fwvar_var_set_data(struct bwfm_softc *, const char *, void *, size_t);
+int	 bwfm_fwvar_var_get_int(struct bwfm_softc *, const char *, uint32_t *);
+int	 bwfm_fwvar_var_set_int(struct bwfm_softc *, const char *, uint32_t);
+
+struct ieee80211_channel *bwfm_bss2chan(struct bwfm_softc *, struct bwfm_bss_info *);
+void	 bwfm_scan(struct bwfm_softc *);
+void	 bwfm_connect(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);
+
+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,
+};
+
+void
+bwfm_attach(struct bwfm_softc *sc)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ifnet *ifp = &sc->sc_if;
+	struct bwfm_task *t;
+	char fw_version[BWFM_DCMD_SMLEN];
+	uint32_t bandlist[3];
+	uint32_t tmp;
+	int i, error;
+
+	error = workqueue_create(&sc->sc_taskq, DEVNAME(sc),
+	    bwfm_task, sc, PRI_NONE, IPL_NET, 0);
+	if (error != 0) {
+		printf("%s: could not create workqueue\n", DEVNAME(sc));
+		return;
+	}
+	sc->sc_freetask = pcq_create(BWFM_TASK_COUNT, KM_SLEEP);
+	for (i = 0; i < BWFM_TASK_COUNT; i++) {
+		t = &sc->sc_task[i];
+		t->t_sc = sc;
+		pcq_put(sc->sc_freetask, t);
+	}
+
+	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;
+	}
+
+	memset(fw_version, 0, sizeof(fw_version));
+	if (bwfm_fwvar_var_get_data(sc, "ver", fw_version, sizeof(fw_version)) == 0)
+		printf("%s: %s", DEVNAME(sc), fw_version);
+	printf("%s: address %s\n", DEVNAME(sc), ether_sprintf(ic->ic_myaddr));
+
+	ic->ic_ifp = ifp;
+	ic->ic_phytype = IEEE80211_T_OFDM;
+	ic->ic_opmode = IEEE80211_M_STA;
+	ic->ic_state = IEEE80211_S_INIT;
+
+	ic->ic_caps =
+	    IEEE80211_C_WEP |
+	    IEEE80211_C_TKIP |
+	    IEEE80211_C_AES |
+	    IEEE80211_C_AES_CCM |
+#if notyet
+	    IEEE80211_C_MONITOR |		/* monitor mode suported */
+	    IEEE80211_C_IBSS |
+	    IEEE80211_C_TXPMGT |
+	    IEEE80211_C_WME |
+#endif
+	    IEEE80211_C_SHSLOT |		/* short slot time supported */
+	    IEEE80211_C_SHPREAMBLE |		/* short preamble supported */
+	    IEEE80211_C_WPA |			/* 802.11i */
+	    /* IEEE80211_C_WPA_4WAY */0;		/* WPA 4-way handshake in hw */
+
+	/* IBSS channel undefined for now. */
+	ic->ic_ibss_chan = &ic->ic_channels[0];
+
+	if (bwfm_fwvar_cmd_get_data(sc, BWFM_C_GET_BANDLIST, bandlist,
+	    sizeof(bandlist))) {
+		printf("%s: couldn't get supported band list\n", DEVNAME(sc));
+		return;
+	} 
+	const u_int nbands = le32toh(bandlist[0]);
+	for (i = 1; i <= MIN(nbands, __arraycount(bandlist) - 1); i++) {
+		switch (le32toh(bandlist[i])) {
+		case BWFM_BAND_2G:
+			ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
+			ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g;
+
+			for (i = 0; i < __arraycount(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;
+			}
+			break;
+		case BWFM_BAND_5G:
+			ic->ic_sup_rates[IEEE80211_MODE_11A] = ieee80211_std_rateset_11a;
+
+			for (i = 0; i < __arraycount(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;
+			}
+			break;
+		}
+	}
+
+	ifp->if_softc = sc;
+	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+	ifp->if_init = bwfm_init;
+	ifp->if_ioctl = bwfm_ioctl;
+	ifp->if_start = bwfm_start;
+	ifp->if_watchdog = bwfm_watchdog;
+	IFQ_SET_READY(&ifp->if_snd);
+	memcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ);
+
+	if_initialize(ifp);
+	ieee80211_ifattach(ic);
+	ifp->if_percpuq = if_percpuq_create(ifp);
+	if_deferred_start_init(ifp, NULL);
+	if_register(ifp);
+
+	sc->sc_newstate = ic->ic_newstate;
+	ic->ic_newstate = bwfm_newstate;
+	ic->ic_send_mgmt = bwfm_send_mgmt;
+	ic->ic_recv_mgmt = bwfm_recv_mgmt;
+	ic->ic_crypto.cs_key_set = bwfm_key_set;
+	ic->ic_crypto.cs_key_delete = bwfm_key_delete;
+	ieee80211_media_init(ic, bwfm_media_change, bwfm_media_status);
+
+	ieee80211_announce(ic);
+
+	sc->sc_if_attached = true;
+}
+
+int
+bwfm_detach(struct bwfm_softc *sc, int flags)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ifnet *ifp = ic->ic_ifp;
+
+	if (sc->sc_if_attached) {
+		bpf_detach(ifp);
+		ieee80211_ifdetach(ic);
+		if_detach(ifp);
+	}
+
+	if (sc->sc_taskq)
+		workqueue_destroy(sc->sc_taskq);
+	if (sc->sc_freetask)
+		pcq_destroy(sc->sc_freetask);
+
+	return 0;
+}
+
+void
+bwfm_start(struct ifnet *ifp)
+{
+	struct bwfm_softc *sc = ifp->if_softc;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct mbuf *m;
+	int error;
+
+	if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+		return;
+
+	/* TODO: return if no link? */
+
+	for (;;) {
+		struct ieee80211_node *ni;
+		struct ether_header *eh;
+
+		/* Discard management packets (fw handles this for us) */
+		IF_DEQUEUE(&ic->ic_mgtq, m);
+		if (m != NULL) {
+			m_freem(m);
+			continue;
+		}
+
+		IFQ_DEQUEUE(&ifp->if_snd, m);
+		if (m == NULL)
+			break;
+
+		eh = mtod(m, struct ether_header *);
+		ni = ieee80211_find_txnode(ic, eh->ether_dhost);
+		if (ni == NULL) {
+			ifp->if_oerrors++;
+			m_freem(m);
+			continue;
+		}
+
+		if (ieee80211_classify(ic, m, ni) != 0) {
+			ifp->if_oerrors++;
+			m_freem(m);
+			ieee80211_free_node(ni);
+			continue;
+		}
+
+		error = sc->sc_bus_ops->bs_txdata(sc, m);
+		if (error == ENOBUFS) {
+			IF_PREPEND(&ifp->if_snd, m);
+			ifp->if_flags |= IFF_OACTIVE;
+			break;
+		}
+
+		if (error != 0) {
+			ifp->if_oerrors++;
+			m_freem(m);
+			if (ni != NULL)
+				ieee80211_free_node(ni);
+		} else {
+			bpf_mtap3(ic->ic_rawbpf, m);
+		}
+	}
+}
+
+int
+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_join_pref_params join_pref[2];
+
+	if (bwfm_fwvar_var_set_int(sc, "mpc", 1)) {
+		printf("%s: could not set mpc\n", DEVNAME(sc));
+		return EIO;
+	}
+
+	/* Select target by RSSI (boost on 5GHz) */
+	join_pref[0].type = BWFM_JOIN_PREF_RSSI_DELTA;
+	join_pref[0].len = 2;
+	join_pref[0].rssi_gain = BWFM_JOIN_PREF_RSSI_BOOST;
+	join_pref[0].band = BWFM_JOIN_PREF_BAND_5G;
+	join_pref[1].type = BWFM_JOIN_PREF_RSSI;
+	join_pref[1].len = 2;
+	join_pref[1].rssi_gain = 0;
+	join_pref[1].band = 0;
+	if (bwfm_fwvar_var_set_data(sc, "join_pref", join_pref,
+	    sizeof(join_pref))) {
+		printf("%s: could not set join pref\n", DEVNAME(sc));
+		return EIO;
+	}
+
+	memset(evmask, 0, sizeof(evmask));
+
+#define	ENABLE_EVENT(e)		evmask[(e) / 8] |= 1 << ((e) % 8)
+	/* Events used to drive the state machine */
+	ENABLE_EVENT(BWFM_E_ASSOC);
+	ENABLE_EVENT(BWFM_E_ESCAN_RESULT);
+	ENABLE_EVENT(BWFM_E_SET_SSID);
+	ENABLE_EVENT(BWFM_E_LINK);
+#undef	ENABLE_EVENT
+
+#ifdef BWFM_DEBUG
+	memset(evmask, 0xff, sizeof(evmask));
+#endif
+	
+	if (bwfm_fwvar_var_set_data(sc, "event_msgs", evmask, sizeof(evmask))) {
+		printf("%s: could not set event mask\n", DEVNAME(sc));
+		return EIO;
+	}
+
+	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 EIO;
+	}
+	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 EIO;
+	}
+	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 EIO;
+	}
+
+	if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, 2)) {
+		printf("%s: could not set power\n", DEVNAME(sc));
+		return EIO;
+	}
+
+	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);
+
+	/*
+	 * Tell the firmware supplicant that we are going to handle the
+	 * WPA handshake ourselves.
+	 */
+	bwfm_fwvar_var_set_int(sc, "sup_wpa", 0);
+
+	ifp->if_flags |= IFF_RUNNING;
+	ifp->if_flags &= ~IFF_OACTIVE;
+
+	if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+		if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+	} else {
+		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+	}
+
+	return 0;
+}
+
+void
+bwfm_stop(struct ifnet *ifp, int disable)
+{
+	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 | IFF_OACTIVE);
+
+	bwfm_fwvar_cmd_set_int(sc, BWFM_C_DOWN, 1);
+	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, 0);
+
+	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+}
+
+void
+bwfm_watchdog(struct ifnet *ifp)
+{
+	struct bwfm_softc *sc = ifp->if_softc;
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	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(ic);
+}
+
+int
+bwfm_ioctl(struct ifnet *ifp, u_long cmd, void *data)
+{
+	struct bwfm_softc *sc = ifp->if_softc;
+	struct ieee80211com *ic = &sc->sc_ic;
+	int s, error = 0;
+
+	s = splnet();
+
+	switch (cmd) {
+	case SIOCSIFFLAGS:
+		if ((error = ifioctl_common(ifp, cmd, data)) != 0)
+			break;
+		switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
+		case IFF_UP | IFF_RUNNING:
+			break;
+		case IFF_UP:
+			bwfm_init(ifp);
+			break;
+		case IFF_RUNNING:
+			bwfm_stop(ifp, 1);
+			break;
+		case 0:
+			break;
+		}
+		break;
+
+	case SIOCADDMULTI:
+	case SIOCDELMULTI:
+		if ((error = ether_ioctl(ifp, cmd, data)) == ENETRESET) {
+			/* setup multicast filter, etc */
+			error = 0;
+		}
+		break;
+
+	default:
+		error = ieee80211_ioctl(ic, cmd, data);
+	}
+
+	if (error == ENETRESET) {
+		if ((ifp->if_flags & IFF_UP) != 0 &&
+		    (ifp->if_flags & IFF_RUNNING) != 0 &&
+		    ic->ic_roaming != IEEE80211_ROAMING_MANUAL) {
+			bwfm_init(ifp);
+		}
+		error = 0;
+	}
+
+	splx(s);
+
+	return error;
+}
+
+int
+bwfm_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni,
+    int type, int arg)
+{
+	return 0;
+}
+
+void
+bwfm_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
+    struct ieee80211_node *ni, int subtype, int rssi, uint32_t rstamp)
+{
+}
+
+int
+bwfm_key_set(struct ieee80211com *ic, const struct ieee80211_key *wk,
+    const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+	struct bwfm_softc *sc = ic->ic_ifp->if_softc;
+	struct bwfm_task *t;
+
+	t = pcq_get(sc->sc_freetask);
+	if (t == NULL) {
+		printf("%s: no free tasks\n", DEVNAME(sc));
+		return 0;
+	}
+
+	t->t_cmd = BWFM_TASK_KEY_SET;
+	t->t_key.key = wk;
+	memcpy(t->t_key.mac, mac, sizeof(t->t_key.mac));
+	workqueue_enqueue(sc->sc_taskq, (struct work *)t, NULL);
+	return 1;
+}
+
+static void
+bwfm_key_set_cb(struct bwfm_softc *sc, struct bwfm_cmd_key *ck)
+{
+	const struct ieee80211_key *wk = ck->key;
+	const uint8_t *mac = ck->mac;
+	struct bwfm_wsec_key wsec_key;
+	uint32_t wsec_enable, wsec;
+	bool ext_key;
+
+#ifdef BWFM_DEBUG
+	printf("key_set: key cipher %s len %d: ", wk->wk_cipher->ic_name, wk->wk_keylen);
+	for (int j = 0; j < sizeof(wk->wk_key); j++)
+		printf("%02x", wk->wk_key[j]);
+#endif
+
+	if ((wk->wk_flags & IEEE80211_KEY_GROUP) == 0 &&
+	    wk->wk_cipher->ic_cipher != IEEE80211_CIPHER_WEP) {
+		ext_key = true;
+	} else {
+		ext_key = false;
+	}
+
+#ifdef BWFM_DEBUG
+	printf(", ext_key = %d", ext_key);
+	printf(", mac = %02x:%02x:%02x:%02x:%02x:%02x",
+	    mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+	printf("\n");
+#endif
+
+	memset(&wsec_key, 0, sizeof(wsec_key));
+	if (ext_key && !IEEE80211_IS_MULTICAST(mac))
+		memcpy(wsec_key.ea, mac, sizeof(wsec_key.ea));
+	wsec_key.index = htole32(wk->wk_keyix);
+	wsec_key.len = htole32(wk->wk_keylen);
+	memcpy(wsec_key.data, wk->wk_key, sizeof(wsec_key.data));
+	if (!ext_key)
+		wsec_key.flags = htole32(BWFM_PRIMARY_KEY);
+
+	switch (wk->wk_cipher->ic_cipher) {
+	case IEEE80211_CIPHER_WEP:
+		if (wk->wk_keylen == 5)
+			wsec_key.algo = htole32(BWFM_CRYPTO_ALGO_WEP1);
+		else if (wk->wk_keylen == 13)
+			wsec_key.algo = htole32(BWFM_CRYPTO_ALGO_WEP128);
+		else
+			return;
+		wsec_enable = BWFM_WSEC_WEP;
+		break;
+	case IEEE80211_CIPHER_TKIP:
+		wsec_key.algo = htole32(BWFM_CRYPTO_ALGO_TKIP);
+		wsec_enable = BWFM_WSEC_TKIP;
+		break;
+	case IEEE80211_CIPHER_AES_CCM:
+		wsec_key.algo = htole32(BWFM_CRYPTO_ALGO_AES_CCM);
+		wsec_enable = BWFM_WSEC_AES;
+		break;
+	default:
+		printf("%s: %s: cipher %s not supported\n", DEVNAME(sc),
+		    __func__, wk->wk_cipher->ic_name);
+		return;
+	}
+
+	if (bwfm_fwvar_var_set_data(sc, "wsec_key", &wsec_key, sizeof(wsec_key)))
+		return;
+
+	bwfm_fwvar_var_set_int(sc, "wpa_auth", BWFM_WPA_AUTH_WPA2_PSK);
+
+	bwfm_fwvar_var_get_int(sc, "wsec", &wsec);
+	wsec |= wsec_enable;
+	bwfm_fwvar_var_set_int(sc, "wsec", wsec);
+}
+
+int
+bwfm_key_delete(struct ieee80211com *ic, const struct ieee80211_key *wk)
+{
+	struct bwfm_softc *sc = ic->ic_ifp->if_softc;
+	struct bwfm_task *t;
+
+	t = pcq_get(sc->sc_freetask);
+	if (t == NULL) {
+		printf("%s: no free tasks\n", DEVNAME(sc));
+		return 0;
+	}
+
+	t->t_cmd = BWFM_TASK_KEY_DELETE;
+	t->t_key.key = wk;
+	memset(t->t_key.mac, 0, sizeof(t->t_key.mac));
+	workqueue_enqueue(sc->sc_taskq, (struct work *)t, NULL);
+
+	return 1;
+}
+
+static void
+bwfm_key_delete_cb(struct bwfm_softc *sc, struct bwfm_cmd_key *ck)
+{
+	const struct ieee80211_key *wk = ck->key;
+	struct bwfm_wsec_key wsec_key;
+
+	memset(&wsec_key, 0, sizeof(wsec_key));
+	wsec_key.index = htole32(wk->wk_keyix);
+	wsec_key.flags = htole32(BWFM_PRIMARY_KEY);
+
+	if (bwfm_fwvar_var_set_data(sc, "wsec_key", &wsec_key, sizeof(wsec_key)))
+		return;
+}
+
+int
+bwfm_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+{
+	struct bwfm_softc *sc = ic->ic_ifp->if_softc;
+	struct bwfm_task *t;
+
+	t = pcq_get(sc->sc_freetask);
+	if (t == NULL) {
+		printf("%s: no free tasks\n", DEVNAME(sc));
+		return EIO;
+	}
+
+	t->t_cmd = BWFM_TASK_NEWSTATE;
+	t->t_newstate.state = nstate;
+	t->t_newstate.arg = arg;
+	workqueue_enqueue(sc->sc_taskq, (struct work *)t, NULL);
+
+	return 0;
+}
+
+void
+bwfm_newstate_cb(struct bwfm_softc *sc, struct bwfm_cmd_newstate *cmd)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	enum ieee80211_state ostate = ic->ic_state;
+	enum ieee80211_state nstate = cmd->state;
+	int s;
+
+	DPRINTF(("%s: newstate %d -> %d\n", DEVNAME(sc), ostate, nstate));
+
+	s = splnet();
+
+	switch (nstate) {
+	case IEEE80211_S_INIT:
+		break;
+
+	case IEEE80211_S_SCAN:
+		if (ostate != IEEE80211_S_SCAN) {
+			/* Start of scanning */
+			bwfm_scan(sc);
+		}
+		break;
+
+	case IEEE80211_S_AUTH:
+		bwfm_connect(sc);
+		break;
+
+	case IEEE80211_S_ASSOC:
+		break;
+
+	case IEEE80211_S_RUN:
+		break;
+	}
+
+	sc->sc_newstate(ic, nstate, cmd->arg);
+
+	splx(s);
+}
+
+void
+bwfm_task(struct work *wk, void *arg)
+{
+	struct bwfm_task *t = (struct bwfm_task *)wk;
+	struct bwfm_softc *sc = t->t_sc;
+
+	switch (t->t_cmd) {
+	case BWFM_TASK_NEWSTATE:
+		bwfm_newstate_cb(sc, &t->t_newstate);
+		break;
+	case BWFM_TASK_KEY_SET:
+		bwfm_key_set_cb(sc, &t->t_key);
+		break;
+	case BWFM_TASK_KEY_DELETE:
+		bwfm_key_delete_cb(sc, &t->t_key);
+		break;
+	default:
+		panic("bwfm: unknown task command %d", t->t_cmd);
+	}
+
+	pcq_put(sc->sc_freetask, t);
+}
+
+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 = kmem_alloc(sizeof(*core), KM_SLEEP);
+		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 __unused = 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 = kmem_zalloc(sizeof(*dcmd), KM_SLEEP);
+	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 = le32toh(dcmd->hdr.cmd);
+		dcmd->hdr.len = le32toh(dcmd->hdr.len);
+		dcmd->hdr.flags = le32toh(dcmd->hdr.flags);
+		dcmd->hdr.status = le32toh(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:
+	kmem_free(dcmd, 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 = kmem_zalloc(sizeof(*dcmd), KM_SLEEP);
+	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 = le32toh(dcmd->hdr.cmd);
+		dcmd->hdr.len = le32toh(dcmd->hdr.len);
+		dcmd->hdr.flags = le32toh(dcmd->hdr.flags);
+		dcmd->hdr.status = le32toh(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:
+	kmem_free(dcmd, 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 = le32toh(*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, const char *name, void *data, size_t len)
+{
+	char *buf;
+	int ret;
+
+	buf = kmem_alloc(strlen(name) + 1 + len, KM_SLEEP);
+	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);
+	kmem_free(buf, strlen(name) + 1 + len);
+	return ret;
+}
+
+int
+bwfm_fwvar_var_set_data(struct bwfm_softc *sc, const char *name, void *data, size_t len)
+{
+	char *buf;
+	int ret;
+
+	buf = kmem_alloc(strlen(name) + 1 + len, KM_SLEEP);
+	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);
+	kmem_free(buf, strlen(name) + 1 + len);
+	return ret;
+}
+
+int
+bwfm_fwvar_var_get_int(struct bwfm_softc *sc, const char *name, uint32_t *data)
+{
+	int ret;
+	ret = bwfm_fwvar_var_get_data(sc, name, data, sizeof(*data));
+	*data = le32toh(*data);
+	return ret;
+}
+
+int
+bwfm_fwvar_var_set_int(struct bwfm_softc *sc, const 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 bwfm_escan_params *params;
+	uint32_t nssid = 0, nchannel = 0;
+	size_t params_size;
+
+#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 = kmem_zalloc(params_size, KM_SLEEP);
+	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);
+	kmem_free(params, params_size);
+}
+
+static __inline int
+bwfm_iswpaoui(const uint8_t *frm)
+{
+	return frm[1] > 3 && le32dec(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
+}
+
+/*
+ * Derive wireless security settings from WPA/RSN IE.
+ */
+static uint32_t
+bwfm_get_wsec(struct bwfm_softc *sc)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	uint8_t *wpa = ic->ic_opt_ie;
+
+	KASSERT(ic->ic_opt_ie_len > 0);
+
+	if (wpa[0] != IEEE80211_ELEMID_RSN) {
+		if (ic->ic_opt_ie_len < 12)
+			return BWFM_WSEC_NONE;
+
+		/* non-RSN IE, expect that we are doing WPA1 */
+		if ((ic->ic_flags & IEEE80211_F_WPA1) == 0)
+			return BWFM_WSEC_NONE;
+
+		/* Must contain WPA OUI */
+		if (!bwfm_iswpaoui(wpa))
+			return BWFM_WSEC_NONE;
+
+		switch (le32dec(wpa + 8)) {
+		case ((WPA_CSE_TKIP<<24)|WPA_OUI):
+			return BWFM_WSEC_TKIP;
+		case ((WPA_CSE_CCMP<<24)|WPA_OUI):
+			return BWFM_WSEC_AES;
+		default:
+			return BWFM_WSEC_NONE;
+		}
+	} else {
+		if (ic->ic_opt_ie_len < 14)
+			return BWFM_WSEC_NONE;
+
+		/* RSN IE, expect that we are doing WPA2 */
+		if ((ic->ic_flags & IEEE80211_F_WPA2) == 0)
+			return BWFM_WSEC_NONE;
+
+		switch (le32dec(wpa + 10)) {
+		case ((RSN_CSE_TKIP<<24)|RSN_OUI):
+			return BWFM_WSEC_TKIP;
+		case ((RSN_CSE_CCMP<<24)|RSN_OUI):
+			return BWFM_WSEC_AES;
+		default:
+			return BWFM_WSEC_NONE;
+		}
+	}
+}
+
+void
+bwfm_connect(struct bwfm_softc *sc)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ieee80211_node *ni = ic->ic_bss;
+	struct bwfm_ext_join_params *params;
+
+	if (ic->ic_flags & IEEE80211_F_WPA) {
+		uint32_t wsec = 0;
+		uint32_t wpa = 0;
+
+		if (ic->ic_opt_ie_len)
+			bwfm_fwvar_var_set_data(sc, "wpaie", ic->ic_opt_ie, ic->ic_opt_ie_len);
+
+		if (ic->ic_flags & IEEE80211_F_WPA1)
+			wpa |= BWFM_WPA_AUTH_WPA_PSK;
+		if (ic->ic_flags & IEEE80211_F_WPA2)
+			wpa |= BWFM_WPA_AUTH_WPA2_PSK;
+
+		wsec |= bwfm_get_wsec(sc);
+
+		DPRINTF(("%s: WPA enabled, ic_flags = 0x%x, wpa 0x%x, wsec 0x%x\n",
+		    DEVNAME(sc), ic->ic_flags, wpa, wsec));
+
+		bwfm_fwvar_var_set_int(sc, "wpa_auth", wpa);
+		bwfm_fwvar_var_set_int(sc, "wsec", wsec);
+	} else {
+		bwfm_fwvar_var_set_int(sc, "wpa_auth", BWFM_WPA_AUTH_DISABLED);
+		bwfm_fwvar_var_set_int(sc, "wsec", BWFM_WSEC_NONE);
+	}
+
+	bwfm_fwvar_var_set_int(sc, "auth", BWFM_AUTH_OPEN);
+	bwfm_fwvar_var_set_int(sc, "mfp", BWFM_MFP_NONE);
+
+	if (ni->ni_esslen && ni->ni_esslen < BWFM_MAX_SSID_LEN) {
+		params = kmem_zalloc(sizeof(*params), KM_SLEEP);
+		memcpy(params->ssid.ssid, ni->ni_essid, ni->ni_esslen);
+		params->ssid.len = htole32(ni->ni_esslen);
+		memcpy(params->assoc.bssid, ni->ni_bssid, 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, ni->ni_essid, ni->ni_esslen);
+			join.ssid.len = htole32(ni->ni_esslen);
+			memcpy(join.assoc.bssid, ni->ni_bssid, sizeof(join.assoc.bssid));
+			bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_SSID, &join,
+			    sizeof(join));
+		}
+		kmem_free(params, sizeof(*params));
+	}
+
+	/* XXX: added for testing only, remove */
+	bwfm_fwvar_var_set_int(sc, "allmulti", 1);
+#if 0
+	bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PROMISC, 1);
+#endif
+}
+
+void
+bwfm_rx(struct bwfm_softc *sc, char *buf, size_t len)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ifnet *ifp = ic->ic_ifp;
+	struct bwfm_event *e = (void *)buf;
+	struct mbuf *m;
+	char *mb;
+	int s;
+
+	DPRINTF(("%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 || len == 0))
+		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;
+		}
+	}
+
+	s = splnet();
+
+	if ((ifp->if_flags & IFF_RUNNING) != 0) {
+		mb = mtod(m, char *);
+		memcpy(mb, buf, len);
+		m->m_pkthdr.len = m->m_len = len;
+		m_set_rcvif(m, ifp);
+
+		if_percpuq_enqueue(ifp->if_percpuq, m);
+	}
+
+	splx(s);
+}
+
+void
+bwfm_rx_event(struct bwfm_softc *sc, char *buf, size_t len)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct bwfm_event *e = (void *)buf;
+	int s;
+
+	DPRINTF(("%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)));
+
+	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) {
+			/* Scan complete */
+			s = splnet();
+			if (ic->ic_opmode != IEEE80211_M_MONITOR)
+				ieee80211_end_scan(ic);
+			splx(s);
+			break;
+		}
+		len -= sizeof(*e);
+		if (len < sizeof(*res) || len < le32toh(res->buflen)) {
+			printf("%s: results too small\n", DEVNAME(sc));
+			return;
+		}
+		len -= sizeof(*res);
+		if (len < le16toh(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 < le16toh(res->bss_count); i++) {
+			bwfm_scan_node(sc, &res->bss_info[i], len);
+			len -= sizeof(*bss) + le32toh(bss->length);
+			bss = (void *)(((uintptr_t)bss) + le32toh(bss->length));
+			if (len <= 0)
+				break;
+		}
+		break;
+		}
+
+	case BWFM_E_SET_SSID:
+		if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS) {
+			ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+		} else {
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+		}
+		break;
+
+	case BWFM_E_ASSOC:
+		if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS) {
+			ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
+		} else {
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+		}
+		break;
+
+	case BWFM_E_LINK:
+		if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS &&
+		    ntohl(e->msg.reason) == 0)
+			break;
+		
+		/* Link status has changed */
+		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+		break;
+
+	default:
+		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_frame wh;
+	struct ieee80211_scanparams scan;
+	uint8_t rates[sizeof(bss->rates) + 2];
+	uint8_t ssid[sizeof(bss->ssid) + 2];
+	uint8_t *frm, *sfrm, *efrm;
+	uint64_t tsf;
+
+	tsf = 0;
+	sfrm = ((uint8_t *)bss) + le16toh(bss->ie_offset);
+	efrm = sfrm + le32toh(bss->ie_length);
+
+	/* Fake a wireless header with the scan result's BSSID */
+	memset(&wh, 0, sizeof(wh));
+	IEEE80211_ADDR_COPY(wh.i_addr2, bss->bssid);
+	IEEE80211_ADDR_COPY(wh.i_addr3, bss->bssid);
+
+	if (efrm - sfrm < 12) {
+		ic->ic_stats.is_rx_elem_toosmall++;
+		return;
+	}
+
+	rates[0] = 0;
+	rates[1] = le32toh(bss->nrates);
+	memcpy(&rates[2], bss->rates, sizeof(bss->rates));
+
+	ssid[0] = 0;
+	ssid[1] = bss->ssid_len;
+	memcpy(&ssid[2], bss->ssid, sizeof(bss->ssid));
+
+	/* Build scan result */
+	memset(&scan, 0, sizeof(scan));
+	scan.tstamp  = (uint8_t *)&tsf;
+	scan.bintval = le16toh(bss->beacon_period);
+	scan.capinfo = le16toh(bss->capability);
+	scan.bchan   = ieee80211_chan2ieee(ic, ic->ic_curchan);
+	scan.chan    = scan.bchan;
+	scan.rates   = rates;
+	scan.ssid    = ssid;
+
+	for (frm = sfrm; frm < efrm; frm += frm[1] + 2) {
+		switch (frm[0]) {
+		case IEEE80211_ELEMID_COUNTRY:
+			scan.country = frm;
+			break;
+		case IEEE80211_ELEMID_FHPARMS:
+			if (ic->ic_phytype == IEEE80211_T_FH) {
+				scan.fhdwell = le16dec(&frm[2]);
+				scan.chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
+				scan.fhindex = frm[6];
+			}
+			break;
+		case IEEE80211_ELEMID_DSPARMS:
+			if (ic->ic_phytype != IEEE80211_T_FH)
+				scan.chan = frm[2];
+			break;
+		case IEEE80211_ELEMID_TIM:
+			scan.tim = frm;
+			scan.timoff = frm - sfrm;
+			break;
+		case IEEE80211_ELEMID_XRATES:
+			scan.xrates = frm;
+			break;
+		case IEEE80211_ELEMID_ERP:
+			if (frm[1] != 1) {
+				ic->ic_stats.is_rx_elem_toobig++;
+				break;
+			}
+			scan.erp = frm[2];
+			break;
+		case IEEE80211_ELEMID_RSN:
+			scan.wpa = frm;
+			break;
+		case IEEE80211_ELEMID_VENDOR:
+			if (bwfm_iswpaoui(frm))
+				scan.wpa = frm;
+			break;
+		}
+	}
+
+	if (ic->ic_flags & IEEE80211_F_SCAN)
+		ieee80211_add_scan(ic, &scan, &wh, IEEE80211_FC0_SUBTYPE_BEACON,
+		    le32toh(bss->rssi), 0);
+}
Index: src/sys/dev/ic/bwfmreg.h
diff -u /dev/null src/sys/dev/ic/bwfmreg.h:1.1
--- /dev/null	Thu Oct 19 23:58:41 2017
+++ src/sys/dev/ic/bwfmreg.h	Thu Oct 19 23:58:41 2017
@@ -0,0 +1,540 @@
+/* $NetBSD: bwfmreg.h,v 1.1 2017/10/19 23:58:41 jmcneill Exp $ */
+/* $OpenBSD: bwfmreg.h,v 1.4 2017/10/16 22:27:16 patrick Exp $ */
+/*
+ * Copyright (c) 2010-2016 Broadcom Corporation
+ * Copyright (c) 2016,2017 Patrick Wildt <patr...@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
+
+/* Security Parameters */
+#define BWFM_AUTH_OPEN				0
+#define BWFM_AUTH_SHARED_KEY			1
+#define BWFM_AUTH_AUTO				2
+#define BWFM_MFP_NONE				0
+#define BWFM_MFP_CAPABLE			1
+#define BWFM_MFP_REQUIRED			2
+#define BWFM_WPA_AUTH_DISABLED			(0 << 0)
+#define BWFM_WPA_AUTH_NONE			(1 << 0)
+#define BWFM_WPA_AUTH_WPA_UNSPECIFIED		(1 << 1)
+#define BWFM_WPA_AUTH_WPA_PSK			(1 << 2)
+#define BWFM_WPA_AUTH_WPA2_UNSPECIFIED		(1 << 6)
+#define BWFM_WPA_AUTH_WPA2_PSK			(1 << 7)
+#define BWFM_WPA_AUTH_WPA2_1X_SHA256		(1 << 12)
+#define BWFM_WPA_AUTH_WPA2_PSK_SHA256		(1 << 15)
+#define BWFM_WSEC_NONE				(0 << 0)
+#define BWFM_WSEC_WEP				(1 << 0)
+#define BWFM_WSEC_TKIP				(1 << 1)
+#define BWFM_WSEC_AES				(1 << 2)
+
+/* 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
+
+/* Small, medium, and maximum buffer size for dcmd */
+#define	BWFM_DCMD_SMLEN				256
+#define	BWFM_DCMD_MEDLEN			1536
+#define	BWFM_DCMD_MAXLEN			8192
+
+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];
+};
+
+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;
+};
+
+#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;
+};
+
+struct bwfm_ssid {
+	uint32_t len;
+	uint8_t ssid[BWFM_MAX_SSID_LEN];
+};
+
+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[];
+};
+
+struct bwfm_scan_results {
+	uint32_t buflen;
+	uint32_t version;
+	uint32_t count;
+	struct bwfm_bss_info bss_info[];
+};
+
+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;
+};
+
+struct bwfm_escan_results {
+	uint32_t buflen;
+	uint32_t version;
+	uint16_t sync_id;
+	uint16_t bss_count;
+	struct bwfm_bss_info bss_info[];
+};
+
+struct bwfm_assoc_params {
+	uint8_t bssid[ETHER_ADDR_LEN];
+	uint32_t chanspec_num;
+	uint16_t chanspec_list[];
+};
+
+struct bwfm_join_pref_params {
+	uint8_t type;
+#define BWFM_JOIN_PREF_RSSI		1
+#define BWFM_JOIN_PREF_WPA		2
+#define BWFM_JOIN_PREF_BAND		3
+#define BWFM_JOIN_PREF_RSSI_DELTA	4
+	uint8_t len;
+	uint8_t rssi_gain;
+#define BWFM_JOIN_PREF_RSSI_BOOST	8
+	uint8_t band;
+#define BWFM_JOIN_PREF_BAND_AUTO	0
+#define BWFM_JOIN_PREF_BAND_5G		1
+#define BWFM_JOIN_PREF_BAND_2G		2
+#define BWFM_JOIN_PREF_BAND_ALL		3
+};
+
+struct bwfm_join_params {
+	struct bwfm_ssid ssid;
+	struct bwfm_assoc_params assoc;
+};
+
+struct bwfm_join_scan_params {
+	uint8_t scan_type;
+	uint32_t nprobes;
+	uint32_t active_time;
+	uint32_t passive_time;
+	uint32_t home_time;
+};
+
+struct bwfm_ext_join_params {
+	struct bwfm_ssid ssid;
+	struct bwfm_join_scan_params scan;
+	struct bwfm_assoc_params assoc;
+};
+
+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];
+};
+
+struct bwfm_wsec_key {
+	uint32_t index;
+	uint32_t len;
+	uint8_t data[32];
+	uint32_t pad_1[18];
+	uint32_t algo;
+#define	BWFM_CRYPTO_ALGO_OFF		0
+#define	BWFM_CRYPTO_ALGO_WEP1		1
+#define	BWFM_CRYPTO_ALGO_TKIP		2
+#define	BWFM_CRYPTO_ALGO_WEP128		3
+#define	BWFM_CRYPTO_ALGO_AES_CCM	4
+#define	BWFM_CRYPTO_ALGO_AES_RESERVED1	5
+#define	BWFM_CRYPTO_ALGO_AES_RESERVED2	6
+	uint32_t flags;
+#define	BWFM_PRIMARY_KEY		(1 << 1)
+	uint32_t pad_2[3];
+	uint32_t iv_initialized;
+	uint32_t pad_3;
+	struct {
+		uint32_t hi;
+		uint16_t lo;
+	} rxiv;
+	uint32_t pad_4[2];
+	uint8_t ea[IEEE80211_ADDR_LEN];
+};
+
+#define	BWFM_BAND_5G			1
+#define	BWFM_BAND_2G			2
+
+/* 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
+};
+
+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;
+};
+
+struct bwfm_event {
+	struct ether_header ehdr;
+#define BWFM_ETHERTYPE_LINK_CTL			0x886c
+	struct bwfm_ethhdr hdr;
+	struct bwfm_event_msg msg;
+} __packed;
Index: src/sys/dev/ic/bwfmvar.h
diff -u /dev/null src/sys/dev/ic/bwfmvar.h:1.1
--- /dev/null	Thu Oct 19 23:58:41 2017
+++ src/sys/dev/ic/bwfmvar.h	Thu Oct 19 23:58:41 2017
@@ -0,0 +1,170 @@
+/* $NetBSD: bwfmvar.h,v 1.1 2017/10/19 23:58:41 jmcneill Exp $ */
+/* $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 <patr...@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
+
+#define	BWFM_TASK_COUNT			32
+
+
+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;
+
+enum bwfm_task_cmd {
+	BWFM_TASK_NEWSTATE,
+	BWFM_TASK_KEY_SET,
+	BWFM_TASK_KEY_DELETE,
+};
+
+struct bwfm_cmd_newstate {
+	enum ieee80211_state	 state;
+	int			 arg;
+};
+
+struct bwfm_cmd_key {
+	const struct ieee80211_key *key;
+	uint8_t			 mac[IEEE80211_ADDR_LEN];
+};
+
+struct bwfm_task {
+	struct work		 t_work;
+	struct bwfm_softc	*t_sc;
+	enum bwfm_task_cmd	 t_cmd;
+	union {
+		struct bwfm_cmd_newstate	newstate;
+		struct bwfm_cmd_key		key;
+	} t_u;
+#define	t_newstate	t_u.newstate
+#define	t_key		t_u.key
+};
+
+struct bwfm_softc {
+	device_t		 sc_dev;
+	struct ieee80211com	 sc_ic;
+	struct ethercom		 sc_ec;
+#define	sc_if			 sc_ec.ec_if
+
+	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;
+
+	bool			 sc_if_attached;
+	struct bwfm_task	 sc_task[BWFM_TASK_COUNT];
+	pcq_t			*sc_freetask;
+	struct workqueue	*sc_taskq;
+
+	int			(*sc_newstate)(struct ieee80211com *,
+				    enum ieee80211_state, int);
+};
+
+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);

Index: src/sys/dev/usb/if_bwfm_usb.c
diff -u /dev/null src/sys/dev/usb/if_bwfm_usb.c:1.1
--- /dev/null	Thu Oct 19 23:58:41 2017
+++ src/sys/dev/usb/if_bwfm_usb.c	Thu Oct 19 23:58:41 2017
@@ -0,0 +1,845 @@
+/* $NetBSD: if_bwfm_usb.c,v 1.1 2017/10/19 23:58:41 jmcneill Exp $ */
+/* $OpenBSD: if_bwfm_usb.c,v 1.2 2017/10/15 14:55:13 patrick Exp $ */
+/*
+ * Copyright (c) 2010-2016 Broadcom Corporation
+ * Copyright (c) 2016,2017 Patrick Wildt <patr...@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 <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/mutex.h>
+#include <sys/workqueue.h>
+#include <sys/pcq.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_ether.h>
+
+#include <netinet/in.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <dev/firmload.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)	device_xname((sc)->sc_sc.sc_dev)
+
+#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 */
+};
+
+struct bwfm_usb_rx_data {
+	struct bwfm_usb_softc		*sc;
+	struct usbd_xfer		*xfer;
+	uint8_t				*buf;
+};
+
+struct bwfm_usb_tx_data {
+	struct bwfm_usb_softc		*sc;
+	struct usbd_xfer		*xfer;
+	uint8_t				*buf;
+	struct mbuf			*mbuf;
+	TAILQ_ENTRY(bwfm_usb_tx_data)	 next;
+};
+
+#define BWFM_RX_LIST_COUNT		50
+#define BWFM_TX_LIST_COUNT		50
+#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 bwfm_usb_rx_data	 sc_rx_data[BWFM_RX_LIST_COUNT];
+	struct bwfm_usb_tx_data	 sc_tx_data[BWFM_TX_LIST_COUNT];
+	TAILQ_HEAD(, bwfm_usb_tx_data) sc_tx_free_list;
+
+	kmutex_t		 sc_rx_lock;
+	kmutex_t		 sc_tx_lock;
+};
+
+int		 bwfm_usb_match(device_t, cfdata_t, void *);
+void		 bwfm_usb_attachhook(device_t);
+void		 bwfm_usb_attach(device_t, device_t, void *);
+int		 bwfm_usb_detach(device_t, 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_alloc_rx_list(struct bwfm_usb_softc *);
+void		 bwfm_usb_free_rx_list(struct bwfm_usb_softc *);
+int		 bwfm_usb_alloc_tx_list(struct bwfm_usb_softc *);
+void		 bwfm_usb_free_tx_list(struct bwfm_usb_softc *);
+
+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,
+};
+
+CFATTACH_DECL_NEW(bwfm_usb, sizeof(struct bwfm_usb_softc),
+    bwfm_usb_match, bwfm_usb_attach, bwfm_usb_detach, NULL);
+
+int
+bwfm_usb_match(device_t parent, cfdata_t match, void *aux)
+{
+	struct usb_attach_arg *uaa = aux;
+
+	return (usb_lookup(bwfm_usbdevs, uaa->uaa_vendor, uaa->uaa_product) != NULL) ?
+	    UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE;
+}
+
+void
+bwfm_usb_attach(device_t parent, device_t self, void *aux)
+{
+	struct bwfm_usb_softc *sc = device_private(self);
+	struct usb_attach_arg *uaa = aux;
+	usb_device_descriptor_t *dd;
+	usb_interface_descriptor_t *id;
+	usb_endpoint_descriptor_t *ed;
+	char *devinfop;
+	int i;
+
+	sc->sc_sc.sc_dev = self;
+	sc->sc_udev = uaa->uaa_device;
+	mutex_init(&sc->sc_rx_lock, MUTEX_DEFAULT, IPL_NET);
+	mutex_init(&sc->sc_tx_lock, MUTEX_DEFAULT, IPL_NET);
+
+	aprint_naive("\n");
+
+	devinfop = usbd_devinfo_alloc(sc->sc_udev, 0);
+	aprint_normal(": %s\n", devinfop);
+	usbd_devinfo_free(devinfop);
+
+	if (usbd_set_config_no(sc->sc_udev, 1, 1) != 0) {
+		aprint_error_dev(self, "failed to set configuration\n");
+		return;
+	}
+	if (usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface) != 0) {
+		aprint_error_dev(self, "failed to get interface handle\n");
+		return;
+	}
+
+	sc->sc_ifaceno = 0;
+	sc->sc_vendor = uaa->uaa_vendor;
+	sc->sc_product = uaa->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(device_t self)
+{
+	struct bwfm_usb_softc *sc = device_private(self);
+	struct bwfm_usb_rx_data *data;
+	const char *name = NULL;
+	struct bootrom_id brom;
+	firmware_handle_t fwh;
+	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 = le32toh(brom.chip);
+	sc->sc_chiprev = le32toh(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 (firmware_open("if_bwfm", name, &fwh) != 0) {
+			printf("%s: failed firmware_open of file %s\n",
+			    DEVNAME(sc), name);
+			return;
+		}
+		size = firmware_get_size(fwh);
+		ucode = firmware_malloc(size);
+		if (ucode == NULL) {
+			printf("%s: failed to allocate firmware memory\n",
+			    DEVNAME(sc));
+			firmware_close(fwh);
+			return;
+		}
+		error = firmware_read(fwh, 0, ucode, size);
+		firmware_close(fwh);
+		if (error != 0) {
+			printf("%s: failed to read firmware (error %d)\n",
+			    DEVNAME(sc), error);
+			firmware_free(ucode, size);
+			return;
+		}
+
+		if (bwfm_usb_load_microcode(sc, ucode, size) != 0) {
+			printf("%s: could not load microcode\n",
+			    DEVNAME(sc));
+			return;
+		}
+
+		firmware_free(ucode, size);
+
+		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 (le32toh(brom.chip) == BRCMF_POSTBOOT_ID)
+				break;
+		}
+
+		if (le32toh(brom.chip) != BRCMF_POSTBOOT_ID) {
+			printf("%s: firmware did not start up\n",
+			    DEVNAME(sc));
+			return;
+		}
+
+		sc->sc_chip = le32toh(brom.chip);
+		sc->sc_chiprev = le32toh(brom.chiprev);
+		printf("%s: firmware loaded\n", DEVNAME(sc));
+	}
+
+	bwfm_usb_dl_cmd(sc, DL_RESETCFG, &brom, sizeof(brom));
+
+	if (bwfm_usb_alloc_rx_list(sc) || bwfm_usb_alloc_tx_list(sc)) {
+		printf("%s: cannot allocate rx/tx lists\n", DEVNAME(sc));
+		return;
+	}
+
+	bwfm_attach(&sc->sc_sc);
+
+	for (i = 0; i < BWFM_RX_LIST_COUNT; i++) {
+		data = &sc->sc_rx_data[i];
+
+		usbd_setup_xfer(data->xfer, data, data->buf,
+		    BWFM_RXBUFSZ, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT,
+		    bwfm_usb_rxeof);
+		error = usbd_transfer(data->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_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
+{
+	struct bwfm_usb_rx_data *data = priv;
+	struct bwfm_usb_softc *sc = data->sc;
+	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 (__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);
+
+	off = 0;
+	hdr = (void *)data->buf;
+	if (len < sizeof(*hdr))
+		goto resubmit;
+	len -= sizeof(*hdr);
+	off += sizeof(*hdr);
+	if (len < hdr->data_offset << 2)
+		goto resubmit;
+	len -= hdr->data_offset << 2;
+	off += hdr->data_offset << 2;
+
+	mutex_enter(&sc->sc_rx_lock);
+	bwfm_rx(&sc->sc_sc, &data->buf[off], len);
+	mutex_exit(&sc->sc_rx_lock);
+
+resubmit:
+	usbd_setup_xfer(data->xfer, data, data->buf,
+	    BWFM_RXBUFSZ, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT,
+	    bwfm_usb_rxeof);
+	error = usbd_transfer(data->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_alloc_rx_list(struct bwfm_usb_softc *sc)
+{
+	struct bwfm_usb_rx_data *data;
+	int i, error = 0;
+
+	for (i = 0; i < BWFM_RX_LIST_COUNT; i++) {
+		data = &sc->sc_rx_data[i];
+
+		data->sc = sc; /* Backpointer for callbacks. */
+
+		if (usbd_create_xfer(sc->sc_rx_pipeh, BWFM_RXBUFSZ,
+		    USBD_SHORT_XFER_OK, 0, &data->xfer) != 0) {
+			printf("%s: could not create xfer\n",
+			    DEVNAME(sc));
+			error = ENOMEM;
+			break;
+		}
+		data->buf = usbd_get_buffer(data->xfer);
+	}
+	if (error != 0)
+		bwfm_usb_free_rx_list(sc);
+	return (error);
+}
+
+void
+bwfm_usb_free_rx_list(struct bwfm_usb_softc *sc)
+{
+	int i;
+
+	/* NB: Caller must abort pipe first. */
+	for (i = 0; i < BWFM_RX_LIST_COUNT; i++) {
+		if (sc->sc_rx_data[i].xfer != NULL)
+			usbd_destroy_xfer(sc->sc_rx_data[i].xfer);
+		sc->sc_rx_data[i].xfer = NULL;
+	}
+}
+
+int
+bwfm_usb_alloc_tx_list(struct bwfm_usb_softc *sc)
+{
+	struct bwfm_usb_tx_data *data;
+	int i, error = 0;
+
+	TAILQ_INIT(&sc->sc_tx_free_list);
+	for (i = 0; i < BWFM_TX_LIST_COUNT; i++) {
+		data = &sc->sc_tx_data[i];
+
+		data->sc = sc; /* Backpointer for callbacks. */
+
+		if (usbd_create_xfer(sc->sc_tx_pipeh, BWFM_TXBUFSZ,
+		    USBD_FORCE_SHORT_XFER, 0, &data->xfer) != 0) {
+			printf("%s: could not create xfer\n",
+			    DEVNAME(sc));
+			error = ENOMEM;
+			break;
+		}
+		data->buf = usbd_get_buffer(data->xfer);
+
+		/* Append this Tx buffer to our free list. */
+		TAILQ_INSERT_TAIL(&sc->sc_tx_free_list, data, next);
+	}
+	if (error != 0)
+		bwfm_usb_free_tx_list(sc);
+	return (error);
+}
+
+void
+bwfm_usb_free_tx_list(struct bwfm_usb_softc *sc)
+{
+	int i;
+
+	/* NB: Caller must abort pipe first. */
+	for (i = 0; i < BWFM_TX_LIST_COUNT; i++) {
+		if (sc->sc_tx_data[i].xfer != NULL)
+			usbd_destroy_xfer(sc->sc_tx_data[i].xfer);
+		sc->sc_tx_data[i].xfer = NULL;
+	}
+}
+
+void
+bwfm_usb_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
+{
+	struct bwfm_usb_tx_data *data = priv;
+	struct bwfm_usb_softc *sc = data->sc;
+	struct ifnet *ifp = sc->sc_sc.sc_ic.ic_ifp;
+	int s;
+
+	DPRINTFN(2, ("%s: %s status %s\n", DEVNAME(sc), __func__,
+	    usbd_errstr(status)));
+
+	m_freem(data->mbuf);
+	data->mbuf = NULL;
+
+	mutex_enter(&sc->sc_tx_lock);
+	/* Put this Tx buffer back to our free list. */
+	TAILQ_INSERT_TAIL(&sc->sc_tx_free_list, data, next);
+	mutex_exit(&sc->sc_tx_lock);
+
+	s = splnet();
+
+	if (__predict_false(status != USBD_NORMAL_COMPLETION)) {
+		if (status == USBD_CANCELLED)
+			usbd_clear_endpoint_stall_async(sc->sc_tx_pipeh);
+		ifp->if_oerrors++;
+		splx(s);
+		return;
+	}
+
+	ifp->if_opackets++;
+
+	/* We just released a Tx buffer, notify Tx. */
+	if ((ifp->if_flags & IFF_OACTIVE) != 0) {
+		ifp->if_flags &= ~IFF_OACTIVE;
+		if_schedule_deferred_start(ifp);
+	}
+	splx(s);
+}
+
+int
+bwfm_usb_detach(device_t self, int flags)
+{
+	struct bwfm_usb_softc *sc = device_private(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);
+	}
+
+	bwfm_usb_free_rx_list(sc);
+	bwfm_usb_free_tx_list(sc);
+
+	mutex_destroy(&sc->sc_rx_lock);
+	mutex_destroy(&sc->sc_tx_lock);
+
+	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)
+{
+	const struct trx_header *trx = (const 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 (le32toh(trx->magic) != TRX_MAGIC ||
+	    (le32toh(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 = le32toh(state.state);
+	rdlbytes = le32toh(state.bytes);
+
+	if (rdlstate != DL_WAITING) {
+		printf("%s: cannot start fw download\n", DEVNAME(sc));
+		return 1;
+	}
+
+	error = usbd_create_xfer(sc->sc_tx_pipeh, TRX_RDL_CHUNK,
+	    0, 0, &xfer);
+	if (error != 0) {
+		printf("%s: cannot create xfer\n", DEVNAME(sc));
+		goto err;
+	}
+
+	buf = usbd_get_buffer(xfer);
+
+	while (rdlbytes != size) {
+		sendlen = MIN(size - sent, TRX_RDL_CHUNK);
+		memcpy(buf, ucode + sent, sendlen);
+
+		usbd_setup_xfer(xfer, NULL, buf, sendlen,
+		    USBD_SYNCHRONOUS, 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 = le32toh(state.state);
+		rdlbytes = le32toh(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 = le32toh(state.state);
+	rdlbytes = le32toh(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));
+
+	usbd_destroy_xfer(xfer);
+
+	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_destroy_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;
+	struct bwfm_usb_tx_data *data;
+	uint32_t len = 0;
+	int error;
+
+	DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__));
+
+	mutex_enter(&sc->sc_tx_lock);
+
+	if (TAILQ_EMPTY(&sc->sc_tx_free_list)) {
+		mutex_exit(&sc->sc_tx_lock);
+		return ENOBUFS;
+	}
+
+	/* Grab a Tx buffer from our free list. */
+	data = TAILQ_FIRST(&sc->sc_tx_free_list);
+	TAILQ_REMOVE(&sc->sc_tx_free_list, data, next);
+
+	mutex_exit(&sc->sc_tx_lock);
+
+	hdr = (void *)&data->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, &data->buf[len]);
+	len += m->m_pkthdr.len;
+
+	data->mbuf = m;
+
+	usbd_setup_xfer(data->xfer, data, data->buf,
+	    len, USBD_FORCE_SHORT_XFER, USBD_NO_TIMEOUT,
+	    bwfm_usb_txeof);
+	error = usbd_transfer(data->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;
+}

Reply via email to