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