diff options
-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 |