Hi folks.

I'm new to network driver code. I've managed to get a working bwfm with
the mentioned patches, though I haven't done a full release build yet.
I've only tested an open network due to not having other choices, but I
will soon test WPA.
It seems to work for fetching files & pinging stuff.

I'm omitting the actual firmware from the diff because it's big and
binary.

Let me know what you think. I will commit this in a few days if nobody
objects.
>From 4df83365c365371a15d2f6d837bd906add23bee0 Mon Sep 17 00:00:00 2001
From: coypu <[email protected]>
Date: Fri, 4 May 2018 02:41:00 +0300
Subject: [PATCH 1/4] sync with openbsd bwfm

adds a txcheck.
set chip active/passive for more kinds of chips and add wrapper around
detect chip RAM
make bwfm_rx take an mbuf
---
 sys/dev/ic/bwfm.c            | 333 ++++++++++++++++++++++++++++++-----
 sys/dev/ic/bwfmreg.h         | 107 ++++++++++-
 sys/dev/ic/bwfmvar.h         |  17 +-
 sys/dev/sdmmc/if_bwfm_sdio.c |  11 ++
 sys/dev/usb/if_bwfm_usb.c    |  50 +++++-
 5 files changed, 466 insertions(+), 52 deletions(-)

diff --git a/sys/dev/ic/bwfm.c b/sys/dev/ic/bwfm.c
index 82bac1d2a4..8da9214833 100644
--- a/sys/dev/ic/bwfm.c
+++ b/sys/dev/ic/bwfm.c
@@ -21,7 +21,6 @@
 #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>
@@ -85,9 +84,16 @@ void  bwfm_chip_ai_reset(struct bwfm_softc *, struct 
bwfm_core *,
 void    bwfm_chip_dmp_erom_scan(struct bwfm_softc *);
 int     bwfm_chip_dmp_get_regaddr(struct bwfm_softc *, uint32_t *,
             uint32_t *, uint32_t *);
+int     bwfm_chip_cr4_set_active(struct bwfm_softc *, const uint32_t);
 void    bwfm_chip_cr4_set_passive(struct bwfm_softc *);
+int     bwfm_chip_ca7_set_active(struct bwfm_softc *, const uint32_t);
 void    bwfm_chip_ca7_set_passive(struct bwfm_softc *);
+int     bwfm_chip_cm3_set_active(struct bwfm_softc *);
 void    bwfm_chip_cm3_set_passive(struct bwfm_softc *);
+void    bwfm_chip_socram_ramsize(struct bwfm_softc *, struct bwfm_core *);
+void    bwfm_chip_sysmem_ramsize(struct bwfm_softc *, struct bwfm_core *);
+void    bwfm_chip_tcm_ramsize(struct bwfm_softc *, struct bwfm_core *);
+void    bwfm_chip_tcm_rambase(struct bwfm_softc *);
 
 int     bwfm_proto_bcdc_query_dcmd(struct bwfm_softc *, int,
             int, char *, size_t *);
@@ -107,7 +113,7 @@ struct ieee80211_channel *bwfm_bss2chan(struct bwfm_softc 
*, struct bwfm_bss_inf
 void    bwfm_scan(struct bwfm_softc *);
 void    bwfm_connect(struct bwfm_softc *);
 
-void    bwfm_rx(struct bwfm_softc *, char *, size_t);
+void    bwfm_rx(struct bwfm_softc *, struct mbuf *);
 void    bwfm_rx_event(struct bwfm_softc *, char *, size_t);
 void    bwfm_scan_node(struct bwfm_softc *, struct bwfm_bss_info *, size_t);
 
@@ -133,7 +139,7 @@ bwfm_attach(struct bwfm_softc *sc)
        char fw_version[BWFM_DCMD_SMLEN];
        uint32_t bandlist[3];
        uint32_t tmp;
-       int i, error;
+       int i, j, error;
 
        error = workqueue_create(&sc->sc_taskq, DEVNAME(sc),
            bwfm_task, sc, PRI_NONE, IPL_NET, 0);
@@ -203,8 +209,8 @@ bwfm_attach(struct bwfm_softc *sc)
                        ic->ic_sup_rates[IEEE80211_MODE_11B] = 
ieee80211_std_rateset_11b;
                        ic->ic_sup_rates[IEEE80211_MODE_11G] = 
ieee80211_std_rateset_11g;
 
-                       for (i = 0; i < __arraycount(bwfm_2ghz_channels); i++) {
-                               uint8_t chan = bwfm_2ghz_channels[i];
+                       for (j = 0; j < __arraycount(bwfm_2ghz_channels); j++) {
+                               uint8_t chan = bwfm_2ghz_channels[j];
                                ic->ic_channels[chan].ic_freq =
                                    ieee80211_ieee2mhz(chan, 
IEEE80211_CHAN_2GHZ);
                                ic->ic_channels[chan].ic_flags =
@@ -215,8 +221,8 @@ bwfm_attach(struct bwfm_softc *sc)
                case BWFM_BAND_5G:
                        ic->ic_sup_rates[IEEE80211_MODE_11A] = 
ieee80211_std_rateset_11a;
 
-                       for (i = 0; i < __arraycount(bwfm_5ghz_channels); i++) {
-                               uint8_t chan = bwfm_5ghz_channels[i];
+                       for (j = 0; j < __arraycount(bwfm_5ghz_channels); j++) {
+                               uint8_t chan = bwfm_5ghz_channels[j];
                                ic->ic_channels[chan].ic_freq =
                                    ieee80211_ieee2mhz(chan, 
IEEE80211_CHAN_5GHZ);
                                ic->ic_channels[chan].ic_flags =
@@ -307,6 +313,12 @@ bwfm_start(struct ifnet *ifp)
                        continue;
                }
 
+               if (sc->sc_bus_ops->bs_txcheck(sc)) {
+                       IF_PREPEND(&ifp->if_snd, m);
+                       ifp->if_flags |= IFF_OACTIVE;
+                       break;
+               }
+
                IFQ_DEQUEUE(&ifp->if_snd, m);
                if (m == NULL)
                        break;
@@ -848,24 +860,22 @@ bwfm_chip_attach(struct bwfm_softc *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);
+       bwfm_chip_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);
+               bwfm_chip_set_passive(sc);
        }
 
-       /* TODO: get raminfo */
+       if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4)) != NULL) {
+               bwfm_chip_tcm_ramsize(sc, core);
+               bwfm_chip_tcm_rambase(sc);
+       } else if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_SYS_MEM)) != NULL) 
{
+               bwfm_chip_sysmem_ramsize(sc, core);
+               bwfm_chip_tcm_rambase(sc);
+       } else if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM)) != 
NULL) {
+               bwfm_chip_socram_ramsize(sc, core);
+       }
 
        core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
        sc->sc_chip.ch_cc_caps = sc->sc_buscore_ops->bc_read(sc,
@@ -1117,16 +1127,116 @@ bwfm_chip_dmp_get_regaddr(struct bwfm_softc *sc, 
uint32_t *erom,
 }
 
 /* Core configuration */
+int
+bwfm_chip_set_active(struct bwfm_softc *sc, const uint32_t rstvec)
+{
+       if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL)
+               return bwfm_chip_cr4_set_active(sc, rstvec);
+       if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL)
+               return bwfm_chip_ca7_set_active(sc, rstvec);
+       if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL)
+               return bwfm_chip_cm3_set_active(sc);
+       return 1;
+}
+
+void
+bwfm_chip_set_passive(struct bwfm_softc *sc)
+{
+       if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL) {
+               bwfm_chip_cr4_set_passive(sc);
+               return;
+       }
+       if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL) {
+               bwfm_chip_ca7_set_passive(sc);
+               return;
+       }
+       if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL) {
+               bwfm_chip_cm3_set_passive(sc);
+               return;
+       }
+}
+
+int
+bwfm_chip_cr4_set_active(struct bwfm_softc *sc, const uint32_t rstvec)
+{
+       struct bwfm_core *core;
+
+       sc->sc_buscore_ops->bc_activate(sc, rstvec);
+       core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4);
+       sc->sc_chip.ch_core_reset(sc, core,
+           BWFM_AGENT_IOCTL_ARMCR4_CPUHALT, 0, 0);
+
+       return 0;
+}
+
 void
 bwfm_chip_cr4_set_passive(struct bwfm_softc *sc)
 {
-       panic("%s: CR4 not supported", DEVNAME(sc));
+       struct bwfm_core *core;
+       uint32_t val;
+
+       core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4);
+       val = sc->sc_buscore_ops->bc_read(sc,
+           core->co_wrapbase + BWFM_AGENT_IOCTL);
+       sc->sc_chip.ch_core_reset(sc, core,
+           val & BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
+           BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
+           BWFM_AGENT_IOCTL_ARMCR4_CPUHALT);
+
+       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);
+}
+
+int
+bwfm_chip_ca7_set_active(struct bwfm_softc *sc, const uint32_t rstvec)
+{
+       struct bwfm_core *core;
+
+       sc->sc_buscore_ops->bc_activate(sc, rstvec);
+       core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7);
+       sc->sc_chip.ch_core_reset(sc, core,
+           BWFM_AGENT_IOCTL_ARMCR4_CPUHALT, 0, 0);
+
+       return 0;
 }
 
 void
 bwfm_chip_ca7_set_passive(struct bwfm_softc *sc)
 {
-       panic("%s: CA7 not supported", DEVNAME(sc));
+       struct bwfm_core *core;
+       uint32_t val;
+
+       core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7);
+       val = sc->sc_buscore_ops->bc_read(sc,
+           core->co_wrapbase + BWFM_AGENT_IOCTL);
+       sc->sc_chip.ch_core_reset(sc, core,
+           val & BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
+           BWFM_AGENT_IOCTL_ARMCR4_CPUHALT,
+           BWFM_AGENT_IOCTL_ARMCR4_CPUHALT);
+
+       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);
+}
+
+int
+bwfm_chip_cm3_set_active(struct bwfm_softc *sc)
+{
+       struct bwfm_core *core;
+
+       core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM);
+       if (!sc->sc_chip.ch_core_isup(sc, core))
+               return 1;
+
+       sc->sc_buscore_ops->bc_activate(sc, 0);
+
+       core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3);
+       sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
+
+       return 0;
 }
 
 void
@@ -1151,6 +1261,153 @@ bwfm_chip_cm3_set_passive(struct bwfm_softc *sc)
        }
 }
 
+/* RAM size helpers */
+void
+bwfm_chip_socram_ramsize(struct bwfm_softc *sc, struct bwfm_core *core)
+{
+       uint32_t coreinfo, nb, lss, banksize, bankinfo;
+       uint32_t ramsize = 0, srsize = 0;
+       int i;
+
+       if (!sc->sc_chip.ch_core_isup(sc, core))
+               sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
+
+       coreinfo = sc->sc_buscore_ops->bc_read(sc,
+           core->co_base + BWFM_SOCRAM_COREINFO);
+       nb = (coreinfo & BWFM_SOCRAM_COREINFO_SRNB_MASK)
+           >> BWFM_SOCRAM_COREINFO_SRNB_SHIFT;
+
+       if (core->co_rev <= 7 || core->co_rev == 12) {
+               banksize = coreinfo & BWFM_SOCRAM_COREINFO_SRBSZ_MASK;
+               lss = (coreinfo & BWFM_SOCRAM_COREINFO_LSS_MASK)
+                   >> BWFM_SOCRAM_COREINFO_LSS_SHIFT;
+               if (lss != 0)
+                       nb--;
+               ramsize = nb * (1 << (banksize + 
BWFM_SOCRAM_COREINFO_SRBSZ_BASE));
+               if (lss != 0)
+                       ramsize += (1 << ((lss - 1) + 
BWFM_SOCRAM_COREINFO_SRBSZ_BASE));
+       } else {
+               for (i = 0; i < nb; i++) {
+                       sc->sc_buscore_ops->bc_write(sc,
+                           core->co_base + BWFM_SOCRAM_BANKIDX,
+                           (BWFM_SOCRAM_BANKIDX_MEMTYPE_RAM <<
+                           BWFM_SOCRAM_BANKIDX_MEMTYPE_SHIFT) | i);
+                       bankinfo = sc->sc_buscore_ops->bc_read(sc,
+                           core->co_base + BWFM_SOCRAM_BANKINFO);
+                       banksize = ((bankinfo & BWFM_SOCRAM_BANKINFO_SZMASK) + 
1)
+                           * BWFM_SOCRAM_BANKINFO_SZBASE;
+                       ramsize += banksize;
+                       if (bankinfo & BWFM_SOCRAM_BANKINFO_RETNTRAM_MASK)
+                               srsize += banksize;
+               }
+       }
+
+       switch (sc->sc_chip.ch_chip) {
+       case BRCM_CC_4334_CHIP_ID:
+               if (sc->sc_chip.ch_chiprev < 2)
+                       srsize = 32 * 1024;
+               break;
+       case BRCM_CC_43430_CHIP_ID:
+               srsize = 64 * 1024;
+               break;
+       default:
+               break;
+       }
+
+       sc->sc_chip.ch_ramsize = ramsize;
+       sc->sc_chip.ch_srsize = srsize;
+}
+
+void
+bwfm_chip_sysmem_ramsize(struct bwfm_softc *sc, struct bwfm_core *core)
+{
+       uint32_t coreinfo, nb, banksize, bankinfo;
+       uint32_t ramsize = 0;
+       int i;
+
+       if (!sc->sc_chip.ch_core_isup(sc, core))
+               sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0);
+
+       coreinfo = sc->sc_buscore_ops->bc_read(sc,
+           core->co_base + BWFM_SOCRAM_COREINFO);
+       nb = (coreinfo & BWFM_SOCRAM_COREINFO_SRNB_MASK)
+           >> BWFM_SOCRAM_COREINFO_SRNB_SHIFT;
+
+       for (i = 0; i < nb; i++) {
+               sc->sc_buscore_ops->bc_write(sc,
+                   core->co_base + BWFM_SOCRAM_BANKIDX,
+                   (BWFM_SOCRAM_BANKIDX_MEMTYPE_RAM <<
+                   BWFM_SOCRAM_BANKIDX_MEMTYPE_SHIFT) | i);
+               bankinfo = sc->sc_buscore_ops->bc_read(sc,
+                   core->co_base + BWFM_SOCRAM_BANKINFO);
+               banksize = ((bankinfo & BWFM_SOCRAM_BANKINFO_SZMASK) + 1)
+                   * BWFM_SOCRAM_BANKINFO_SZBASE;
+               ramsize += banksize;
+       }
+
+       sc->sc_chip.ch_ramsize = ramsize;
+}
+
+void
+bwfm_chip_tcm_ramsize(struct bwfm_softc *sc, struct bwfm_core *core)
+{
+       uint32_t cap, nab, nbb, totb, bxinfo, ramsize = 0;
+       int i;
+
+       cap = sc->sc_buscore_ops->bc_read(sc, core->co_base + BWFM_ARMCR4_CAP);
+       nab = (cap & BWFM_ARMCR4_CAP_TCBANB_MASK) >> 
BWFM_ARMCR4_CAP_TCBANB_SHIFT;
+       nbb = (cap & BWFM_ARMCR4_CAP_TCBBNB_MASK) >> 
BWFM_ARMCR4_CAP_TCBBNB_SHIFT;
+       totb = nab + nbb;
+
+       for (i = 0; i < totb; i++) {
+               sc->sc_buscore_ops->bc_write(sc,
+                   core->co_base + BWFM_ARMCR4_BANKIDX, i);
+               bxinfo = sc->sc_buscore_ops->bc_read(sc,
+                   core->co_base + BWFM_ARMCR4_BANKINFO);
+               ramsize += ((bxinfo & BWFM_ARMCR4_BANKINFO_BSZ_MASK) + 1) *
+                   BWFM_ARMCR4_BANKINFO_BSZ_MULT;
+       }
+
+       sc->sc_chip.ch_ramsize = ramsize;
+}
+
+void
+bwfm_chip_tcm_rambase(struct bwfm_softc *sc)
+{
+       switch (sc->sc_chip.ch_chip) {
+       case BRCM_CC_4345_CHIP_ID:
+               sc->sc_chip.ch_rambase = 0x198000;
+               break;
+       case BRCM_CC_4335_CHIP_ID:
+       case BRCM_CC_4339_CHIP_ID:
+       case BRCM_CC_4350_CHIP_ID:
+       case BRCM_CC_4354_CHIP_ID:
+       case BRCM_CC_4356_CHIP_ID:
+       case BRCM_CC_43567_CHIP_ID:
+       case BRCM_CC_43569_CHIP_ID:
+       case BRCM_CC_43570_CHIP_ID:
+       case BRCM_CC_4358_CHIP_ID:
+       case BRCM_CC_4359_CHIP_ID:
+       case BRCM_CC_43602_CHIP_ID:
+       case BRCM_CC_4371_CHIP_ID:
+               sc->sc_chip.ch_rambase = 0x180000;
+               break;
+       case BRCM_CC_43465_CHIP_ID:
+       case BRCM_CC_43525_CHIP_ID:
+       case BRCM_CC_4365_CHIP_ID:
+       case BRCM_CC_4366_CHIP_ID:
+               sc->sc_chip.ch_rambase = 0x200000;
+               break;
+       case CY_CC_4373_CHIP_ID:
+               sc->sc_chip.ch_rambase = 0x160000;
+               break;
+       default:
+               printf("%s: unknown chip: %d\n", DEVNAME(sc),
+                   sc->sc_chip.ch_chip);
+               break;
+       }
+}
+
 /* BCDC protocol implementation */
 int
 bwfm_proto_bcdc_query_dcmd(struct bwfm_softc *sc, int ifidx,
@@ -1504,44 +1761,26 @@ bwfm_connect(struct bwfm_softc *sc)
 }
 
 void
-bwfm_rx(struct bwfm_softc *sc, char *buf, size_t len)
+bwfm_rx(struct bwfm_softc *sc, struct mbuf *m)
 {
        struct ieee80211com *ic = &sc->sc_ic;
        struct ifnet *ifp = ic->ic_ifp;
-       struct bwfm_event *e = (void *)buf;
-       struct mbuf *m;
-       char *mb;
+       struct bwfm_event *e = mtod(m, struct bwfm_event *);
        int s;
 
-       DPRINTF(("%s: buf %p len %lu\n", __func__, buf, len));
-
-       if (len >= sizeof(e->ehdr) &&
+       if (m->m_len >= sizeof(e->ehdr) &&
            ntohs(e->ehdr.ether_type) == BWFM_ETHERTYPE_LINK_CTL &&
            memcmp(BWFM_BRCM_OUI, e->hdr.oui, sizeof(e->hdr.oui)) == 0 &&
-           ntohs(e->hdr.usr_subtype) == BWFM_BRCM_SUBTYPE_EVENT)
-               bwfm_rx_event(sc, buf, len);
-
-       if (__predict_false(len > MCLBYTES || len == 0))
+           ntohs(e->hdr.usr_subtype) == BWFM_BRCM_SUBTYPE_EVENT) {
+               bwfm_rx_event(sc, mtod(m, char *), m->m_len);
+               m_freem(m);
                return;
-       MGETHDR(m, M_DONTWAIT, MT_DATA);
-       if (__predict_false(m == NULL))
-               return;
-       if (len > MHLEN) {
-               MCLGET(m, M_DONTWAIT);
-               if (!(m->m_flags & M_EXT)) {
-                       m_free(m);
-                       return;
-               }
        }
 
        s = splnet();
 
        if ((ifp->if_flags & IFF_RUNNING) != 0) {
-               mb = mtod(m, char *);
-               memcpy(mb, buf, len);
-               m->m_pkthdr.len = m->m_len = len;
                m_set_rcvif(m, ifp);
-
                if_percpuq_enqueue(ifp->if_percpuq, m);
        }
 
@@ -1627,7 +1866,7 @@ bwfm_rx_event(struct bwfm_softc *sc, char *buf, size_t 
len)
                if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS &&
                    ntohl(e->msg.reason) == 0)
                        break;
-               
+
                /* Link status has changed */
                ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
                break;
diff --git a/sys/dev/ic/bwfmreg.h b/sys/dev/ic/bwfmreg.h
index 22871ae06b..9ad133035f 100644
--- a/sys/dev/ic/bwfmreg.h
+++ b/sys/dev/ic/bwfmreg.h
@@ -1,5 +1,5 @@
 /* $NetBSD: bwfmreg.h,v 1.2 2017/10/20 23:38:56 jmcneill Exp $ */
-/* $OpenBSD: bwfmreg.h,v 1.4 2017/10/16 22:27:16 patrick Exp $ */
+/* $OpenBSD: bwfmreg.h,v 1.16 2018/02/07 21:44:09 patrick Exp $ */
 /*
  * Copyright (c) 2010-2016 Broadcom Corporation
  * Copyright (c) 2016,2017 Patrick Wildt <[email protected]>
@@ -50,13 +50,24 @@
 #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_WATCHDOG                 0x00000080
 #define BWFM_CHIP_REG_EROMPTR                  0x000000FC
+#define BWFM_CHIP_REG_SR_CAPABILITY            0x00000500
+#define BWFM_CHIP_REG_SR_CONTROL0              0x00000504
+#define BWFM_CHIP_REG_SR_CONTROL1              0x00000508
 #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
+#define BWFM_CHIP_REG_PMUCAPABILITIES_EXT      0x0000064C
+#define  BWFM_CHIP_REG_PMUCAPABILITIES_SR_SUPP         (1 << 1)
+#define BWFM_CHIP_REG_CHIPCONTROL_ADDR         0x00000650
+#define BWFM_CHIP_REG_CHIPCONTROL_DATA         0x00000654
+#define BWFM_CHIP_REG_RETENTION_CTL            0x00000670
+#define  BWFM_CHIP_REG_RETENTION_CTL_MACPHY_DIS                (1 << 26)
+#define  BWFM_CHIP_REG_RETENTION_CTL_LOGIC_DIS         (1 << 27)
 
 /* Agent registers */
 #define BWFM_AGENT_IOCTL                       0x0408
@@ -65,6 +76,7 @@
 #define  BWFM_AGENT_IOCTL_CORE_BITS                    0x3FFC
 #define  BWFM_AGENT_IOCTL_PME_EN                       0x4000
 #define  BWFM_AGENT_IOCTL_BIST_EN                      0x8000
+#define  BWFM_AGENT_IOCTL_ARMCR4_CPUHALT               0x0020
 #define BWFM_AGENT_RESET_CTL                   0x0800
 #define  BWFM_AGENT_RESET_CTL_RESET                    0x0001
 
@@ -75,16 +87,46 @@
 #define BWFM_AGENT_CORE_PMU                    0x827
 #define BWFM_AGENT_CORE_SDIO_DEV               0x829
 #define BWFM_AGENT_CORE_ARM_CM3                        0x82A
+#define BWFM_AGENT_CORE_PCIE2                  0x83C
 #define BWFM_AGENT_CORE_ARM_CR4                        0x83E
 #define BWFM_AGENT_CORE_ARM_CA7                        0x847
+#define BWFM_AGENT_SYS_MEM                     0x849
 
 /* Specific Core Bits */
 #define BWFM_AGENT_ARMCR4_IOCTL_CPUHALT                0x0020
 #define BWFM_AGENT_D11_IOCTL_PHYCLOCKEN                0x0004
 #define BWFM_AGENT_D11_IOCTL_PHYRESET          0x0008
 
+/* CR4 registers */
+#define BWFM_ARMCR4_CAP                                0x0004
+#define  BWFM_ARMCR4_CAP_TCBANB_MASK                   0xf
+#define  BWFM_ARMCR4_CAP_TCBANB_SHIFT                  0
+#define  BWFM_ARMCR4_CAP_TCBBNB_MASK                   0xf0
+#define  BWFM_ARMCR4_CAP_TCBBNB_SHIFT                  4
+#define BWFM_ARMCR4_BANKIDX                    0x0040
+#define BWFM_ARMCR4_BANKINFO                   0x0044
+#define  BWFM_ARMCR4_BANKINFO_BSZ_MASK                 0x3f
+#define  BWFM_ARMCR4_BANKINFO_BSZ_MULT                 8192
+#define BWFM_ARMCR4_BANKPDA                    0x004C
+
 /* SOCRAM registers */
+#define BWFM_SOCRAM_COREINFO                   0x0000
+#define  BWFM_SOCRAM_COREINFO_SRBSZ_BASE               14
+#define  BWFM_SOCRAM_COREINFO_SRBSZ_MASK               0xf
+#define  BWFM_SOCRAM_COREINFO_SRBSZ_SHIFT              0
+#define  BWFM_SOCRAM_COREINFO_SRNB_MASK                        0xf0
+#define  BWFM_SOCRAM_COREINFO_SRNB_SHIFT               4
+#define  BWFM_SOCRAM_COREINFO_LSS_MASK                 0xf00000
+#define  BWFM_SOCRAM_COREINFO_LSS_SHIFT                        20
 #define BWFM_SOCRAM_BANKIDX                    0x0010
+#define  BWFM_SOCRAM_BANKIDX_MEMTYPE_RAM               0
+#define  BWFM_SOCRAM_BANKIDX_MEMTYPE_ROM               1
+#define  BWFM_SOCRAM_BANKIDX_MEMTYPE_DEVRAM            2
+#define  BWFM_SOCRAM_BANKIDX_MEMTYPE_SHIFT             8
+#define BWFM_SOCRAM_BANKINFO                   0x0040
+#define  BWFM_SOCRAM_BANKINFO_SZBASE                   8192
+#define  BWFM_SOCRAM_BANKINFO_SZMASK                   0x7f
+#define  BWFM_SOCRAM_BANKINFO_RETNTRAM_MASK            0x10000
 #define BWFM_SOCRAM_BANKPDA                    0x0044
 
 /* SDPCMD registers */
@@ -140,6 +182,13 @@
 #define BWFM_AUTH_OPEN                         0
 #define BWFM_AUTH_SHARED_KEY                   1
 #define BWFM_AUTH_AUTO                         2
+#define BWFM_CRYPTO_ALGO_OFF                   0
+#define BWFM_CRYPTO_ALGO_WEP1                  1
+#define BWFM_CRYPTO_ALGO_TKIP                  2
+#define BWFM_CRYPTO_ALGO_WEP128                        3
+#define BWFM_CRYPTO_ALGO_AES_CCM               4
+#define BWFM_CRYPTO_ALGO_AES_RESERVED1         5
+#define BWFM_CRYPTO_ALGO_AES_RESERVED2         6
 #define BWFM_MFP_NONE                          0
 #define BWFM_MFP_CAPABLE                       1
 #define BWFM_MFP_REQUIRED                      2
@@ -156,6 +205,59 @@
 #define BWFM_WSEC_TKIP                         (1 << 1)
 #define BWFM_WSEC_AES                          (1 << 2)
 
+/* Channel Parameters */
+#define BWFM_CHANSPEC_CHAN_MASK                        0xff
+#define BWFM_CHANSPEC_CHAN_SHIFT               0
+#define BWFM_CHANSPEC_D11N_SB_L                        (0x1 << 8) /* control 
lower */
+#define BWFM_CHANSPEC_D11N_SB_U                        (0x2 << 8) /* control 
lower */
+#define BWFM_CHANSPEC_D11N_SB_N                        (0x3 << 8) /* none */
+#define BWFM_CHANSPEC_D11N_SB_MASK             (0x3 << 8)
+#define BWFM_CHANSPEC_D11N_SB_SHIFT            8
+#define BWFM_CHANSPEC_D11N_BW_10               (0x1 << 10)
+#define BWFM_CHANSPEC_D11N_BW_20               (0x2 << 10)
+#define BWFM_CHANSPEC_D11N_BW_40               (0x3 << 10)
+#define BWFM_CHANSPEC_D11N_BW_MASK             (0x3 << 10)
+#define BWFM_CHANSPEC_D11N_BW_SHIFT            10
+#define BWFM_CHANSPEC_D11N_BND_5G              (0x1 << 12)
+#define BWFM_CHANSPEC_D11N_BND_2G              (0x2 << 12)
+#define BWFM_CHANSPEC_D11N_BND_MASK            (0x3 << 12)
+#define BWFM_CHANSPEC_D11N_BND_SHIFT           12
+#define BWFM_CHANSPEC_D11AC_SB_LLL             (0x0 << 8)
+#define BWFM_CHANSPEC_D11AC_SB_LLU             (0x1 << 8)
+#define BWFM_CHANSPEC_D11AC_SB_LUL             (0x2 << 8)
+#define BWFM_CHANSPEC_D11AC_SB_LUU             (0x3 << 8)
+#define BWFM_CHANSPEC_D11AC_SB_ULL             (0x4 << 8)
+#define BWFM_CHANSPEC_D11AC_SB_ULU             (0x5 << 8)
+#define BWFM_CHANSPEC_D11AC_SB_UUL             (0x6 << 8)
+#define BWFM_CHANSPEC_D11AC_SB_UUU             (0x7 << 8)
+#define BWFM_CHANSPEC_D11AC_SB_MASK            (0x7 << 8)
+#define BWFM_CHANSPEC_D11AC_SB_SHIFT           8
+#define BWFM_CHANSPEC_D11AC_BW_5               (0x0 << 11)
+#define BWFM_CHANSPEC_D11AC_BW_10              (0x1 << 11)
+#define BWFM_CHANSPEC_D11AC_BW_20              (0x2 << 11)
+#define BWFM_CHANSPEC_D11AC_BW_40              (0x3 << 11)
+#define BWFM_CHANSPEC_D11AC_BW_80              (0x4 << 11)
+#define BWFM_CHANSPEC_D11AC_BW_160             (0x5 << 11)
+#define BWFM_CHANSPEC_D11AC_BW_8080            (0x6 << 11)
+#define BWFM_CHANSPEC_D11AC_BW_MASK            (0x7 << 11)
+#define BWFM_CHANSPEC_D11AC_BW_SHIFT           11
+#define BWFM_CHANSPEC_D11AC_BND_2G             (0x0 << 14)
+#define BWFM_CHANSPEC_D11AC_BND_3G             (0x1 << 14)
+#define BWFM_CHANSPEC_D11AC_BND_4G             (0x2 << 14)
+#define BWFM_CHANSPEC_D11AC_BND_5G             (0x3 << 14)
+#define BWFM_CHANSPEC_D11AC_BND_MASK           (0x3 << 14)
+#define BWFM_CHANSPEC_D11AC_BND_SHIFT          14
+
+#define BWFM_BAND_AUTO                         0
+#define BWFM_BAND_5G                           1
+#define BWFM_BAND_2G                           2
+#define BWFM_BAND_ALL                          3
+
+/* Power Modes */
+#define BWFM_PM_CAM                            0
+#define BWFM_PM_PS                             1
+#define BWFM_PM_FAST_PS                                2
+
 /* DCMD commands */
 #define BWFM_C_GET_VERSION                     1
 #define BWFM_C_UP                              2
@@ -300,6 +402,9 @@ struct bwfm_scan_params {
        uint8_t bss_type;
 #define DOT11_BSSTYPE_ANY              2
        uint8_t scan_type;
+#define BWFM_SCANTYPE_ACTIVE           0
+#define BWFM_SCANTYPE_PASSIVE          1
+#define BWFM_SCANTYPE_DEFAULT          0xff
        uint32_t nprobes;
        uint32_t active_time;
        uint32_t passive_time;
diff --git a/sys/dev/ic/bwfmvar.h b/sys/dev/ic/bwfmvar.h
index 938337b2ff..939bd55c07 100644
--- a/sys/dev/ic/bwfmvar.h
+++ b/sys/dev/ic/bwfmvar.h
@@ -17,6 +17,8 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <sys/pcq.h>
+
 /* Chipcommon Core Chip IDs */
 #define BRCM_CC_43143_CHIP_ID          43143
 #define BRCM_CC_43235_CHIP_ID          43235
@@ -33,7 +35,9 @@
 #define BRCM_CC_4339_CHIP_ID           0x4339
 #define BRCM_CC_43430_CHIP_ID          43430
 #define BRCM_CC_4345_CHIP_ID           0x4345
+#define BRCM_CC_43465_CHIP_ID          43465
 #define BRCM_CC_4350_CHIP_ID           0x4350
+#define BRCM_CC_43525_CHIP_ID          43525
 #define BRCM_CC_4354_CHIP_ID           0x4354
 #define BRCM_CC_4356_CHIP_ID           0x4356
 #define BRCM_CC_43566_CHIP_ID          43566
@@ -46,6 +50,7 @@
 #define BRCM_CC_4365_CHIP_ID           0x4365
 #define BRCM_CC_4366_CHIP_ID           0x4366
 #define BRCM_CC_4371_CHIP_ID           0x4371
+#define CY_CC_4373_CHIP_ID             0x4373
 
 /* Defaults */
 #define BWFM_DEFAULT_SCAN_CHANNEL_TIME 40
@@ -87,6 +92,7 @@ struct bwfm_chip {
 struct bwfm_bus_ops {
        void (*bs_init)(struct bwfm_softc *);
        void (*bs_stop)(struct bwfm_softc *);
+       int (*bs_txcheck)(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 *);
@@ -98,7 +104,7 @@ struct bwfm_buscore_ops {
        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);
+       void (*bc_activate)(struct bwfm_softc *, const uint32_t);
 };
 
 struct bwfm_proto_ops {
@@ -163,8 +169,15 @@ struct bwfm_softc {
 };
 
 void bwfm_attach(struct bwfm_softc *);
+void bwfm_chip_socram_ramsize(struct bwfm_softc *, struct bwfm_core *);
+void bwfm_chip_sysmem_ramsize(struct bwfm_softc *, struct bwfm_core *);
+void bwfm_chip_tcm_ramsize(struct bwfm_softc *, struct bwfm_core *);
+void bwfm_chip_tcm_rambase(struct bwfm_softc *);
+void bwfm_start(struct ifnet *);
 int bwfm_detach(struct bwfm_softc *, int);
 int bwfm_chip_attach(struct bwfm_softc *);
+int bwfm_chip_set_active(struct bwfm_softc *, uint32_t);
+void bwfm_chip_set_passive(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);
+void bwfm_rx(struct bwfm_softc *, struct mbuf *m);
diff --git a/sys/dev/sdmmc/if_bwfm_sdio.c b/sys/dev/sdmmc/if_bwfm_sdio.c
index f31d1afc96..639ee98982 100644
--- a/sys/dev/sdmmc/if_bwfm_sdio.c
+++ b/sys/dev/sdmmc/if_bwfm_sdio.c
@@ -87,6 +87,7 @@ void           bwfm_sdio_buscore_write(struct bwfm_softc *, 
uint32_t,
 int             bwfm_sdio_buscore_prepare(struct bwfm_softc *);
 void            bwfm_sdio_buscore_activate(struct bwfm_softc *, uint32_t);
 
+int             bwfm_sdio_txcheck(struct bwfm_softc *);
 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 *);
@@ -94,6 +95,7 @@ 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_txcheck = bwfm_sdio_txcheck,
        .bs_txdata = bwfm_sdio_txdata,
        .bs_txctl = bwfm_sdio_txctl,
        .bs_rxctl = bwfm_sdio_rxctl,
@@ -404,6 +406,15 @@ bwfm_sdio_buscore_activate(struct bwfm_softc *bwfm, 
uint32_t rstvec)
 #endif
 }
 
+int
+bwfm_sdio_txcheck(struct bwfm_softc *bwfm, struct mbuf *m)
+{
+       DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__));
+
+       return 0;
+}
+
+
 int
 bwfm_sdio_txdata(struct bwfm_softc *bwfm, struct mbuf *m)
 {
diff --git a/sys/dev/usb/if_bwfm_usb.c b/sys/dev/usb/if_bwfm_usb.c
index 060e948513..10d4e24ed8 100644
--- a/sys/dev/usb/if_bwfm_usb.c
+++ b/sys/dev/usb/if_bwfm_usb.c
@@ -199,16 +199,19 @@ void               bwfm_usb_free_rx_list(struct 
bwfm_usb_softc *);
 int             bwfm_usb_alloc_tx_list(struct bwfm_usb_softc *);
 void            bwfm_usb_free_tx_list(struct bwfm_usb_softc *);
 
+int             bwfm_usb_txcheck(struct bwfm_softc *);
 int             bwfm_usb_txdata(struct bwfm_softc *, struct mbuf *);
 int             bwfm_usb_txctl(struct bwfm_softc *, char *, size_t);
 int             bwfm_usb_rxctl(struct bwfm_softc *, char *, size_t *);
 
+struct mbuf *   bwfm_usb_newbuf(void);
 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_txcheck = bwfm_usb_txcheck,
        .bs_txdata = bwfm_usb_txdata,
        .bs_txctl = bwfm_usb_txctl,
        .bs_rxctl = bwfm_usb_rxctl,
@@ -435,6 +438,26 @@ bwfm_usb_attachhook(device_t self)
        }
 }
 
+struct mbuf *
+bwfm_usb_newbuf(void)
+{
+       struct mbuf *m;
+
+       MGETHDR(m, M_DONTWAIT, MT_DATA);
+       if (m == NULL)
+               return (NULL);
+
+       MCLGET(m, M_DONTWAIT);
+       if (!(m->m_flags & M_EXT)) {
+               m_freem(m);
+               return (NULL);
+       }
+
+       m->m_len = m->m_pkthdr.len = MCLBYTES;
+
+       return (m);
+}
+
 void
 bwfm_usb_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
 {
@@ -443,6 +466,7 @@ bwfm_usb_rxeof(struct usbd_xfer *xfer, void *priv, 
usbd_status status)
        struct bwfm_proto_bcdc_hdr *hdr;
        usbd_status error;
        uint32_t len, off;
+       struct mbuf *m;
 
        DPRINTFN(2, ("%s: %s status %s\n", DEVNAME(sc), __func__,
            usbd_errstr(status)));
@@ -466,8 +490,14 @@ bwfm_usb_rxeof(struct usbd_xfer *xfer, void *priv, 
usbd_status status)
        len -= hdr->data_offset << 2;
        off += hdr->data_offset << 2;
 
-       mutex_enter(&sc->sc_rx_lock);
-       bwfm_rx(&sc->sc_sc, &data->buf[off], len);
+       m = bwfm_usb_newbuf();
+       if (m == NULL)
+               goto resubmit;
+
+       memcpy(mtod(m, char *), data->buf + off, len);
+       m->m_len = m->m_pkthdr.len = len;
+       mutex_enter(&sc->sc_rx_lock); /* XXX */
+       bwfm_rx(&sc->sc_sc, m);
        mutex_exit(&sc->sc_rx_lock);
 
 resubmit:
@@ -734,6 +764,22 @@ err:
        return 1;
 }
 
+int
+bwfm_usb_txcheck(struct bwfm_softc *bwfm)
+{
+       struct bwfm_usb_softc *sc = (void *)bwfm;
+
+       mutex_enter(&sc->sc_tx_lock);
+
+       if (TAILQ_EMPTY(&sc->sc_tx_free_list)) {
+               mutex_exit(&sc->sc_tx_lock);
+               return ENOBUFS;
+       }
+
+       return 0;
+}
+
+
 int
 bwfm_usb_txdata(struct bwfm_softc *bwfm, struct mbuf *m)
 {
-- 
2.17.0

>From 0a2e6220eb196a5bc76baa4f3f739c20368c3c17 Mon Sep 17 00:00:00 2001
From: coypu <[email protected]>
Date: Fri, 4 May 2018 04:35:21 +0300
Subject: [PATCH 2/4] add bwfm pci support. tested on BCM43602.

XXX is testing workqueue before destroy like so valid
if (sc->flowring_wq != NULL)
        workqueue_destroy(sc->flowring_wq);
---
 sys/dev/pci/files.pci     |    4 +
 sys/dev/pci/if_bwfm_pci.c | 2062 +++++++++++++++++++++++++++++++++++++
 sys/dev/pci/if_bwfm_pci.h |  279 +++++
 3 files changed, 2345 insertions(+)
 create mode 100644 sys/dev/pci/if_bwfm_pci.c
 create mode 100644 sys/dev/pci/if_bwfm_pci.h

diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci
index 930a42c78a..fda5bd36bb 100644
--- a/sys/dev/pci/files.pci
+++ b/sys/dev/pci/files.pci
@@ -1073,6 +1073,10 @@ include "dev/pci/n8/files.n8"
 attach bwi at pci with bwi_pci
 file   dev/pci/if_bwi_pci.c            bwi_pci
 
+# Broadcom FullMAC USB wireless adapter
+attach bwfm at pci with bwfm_pci: firmload
+file   dev/pci/if_bwfm_pci.c           bwfm_pci
+
 # Marvell Serial-ATA Host Controller
 attach mvsata at pci with mvsata_pci
 file   dev/pci/mvsata_pci.c            mvsata_pci
diff --git a/sys/dev/pci/if_bwfm_pci.c b/sys/dev/pci/if_bwfm_pci.c
new file mode 100644
index 0000000000..edefd9f7c2
--- /dev/null
+++ b/sys/dev/pci/if_bwfm_pci.c
@@ -0,0 +1,2062 @@
+/*     $NetBSD$        */
+/*     $OpenBSD: if_bwfm_pci.c,v 1.18 2018/02/08 05:00:38 patrick Exp $        
*/
+/*
+ * Copyright (c) 2010-2016 Broadcom Corporation
+ * Copyright (c) 2017 Patrick Wildt <[email protected]>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/kernel.h>
+#include <sys/kmem.h>
+#include <sys/device.h>
+#include <sys/pool.h>
+#include <sys/workqueue.h>
+#include <sys/socket.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_ether.h>
+#include <net/if_media.h>
+
+#include <netinet/in.h>
+
+#include <net80211/ieee80211_var.h>
+
+#include <dev/firmload.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+
+#include <dev/ic/bwfmvar.h>
+#include <dev/ic/bwfmreg.h>
+#include <dev/pci/if_bwfm_pci.h>
+
+#define BWFM_DMA_D2H_SCRATCH_BUF_LEN           8
+#define BWFM_DMA_D2H_RINGUPD_BUF_LEN           1024
+#define BWFM_DMA_H2D_IOCTL_BUF_LEN             ETHER_MAX_LEN
+
+#define BWFM_NUM_TX_MSGRINGS                   2
+#define BWFM_NUM_RX_MSGRINGS                   3
+
+#define BWFM_NUM_TX_PKTIDS                     2048
+#define BWFM_NUM_RX_PKTIDS                     1024
+
+#define BWFM_NUM_TX_DESCS                      1
+#define BWFM_NUM_RX_DESCS                      1
+
+#ifdef BWFM_DEBUG
+#define DPRINTF(x)     do { if (bwfm_debug > 0) printf x; } while (0)
+#define DPRINTFN(n, x) do { if (bwfm_debug >= (n)) printf x; } while (0)
+static int bwfm_debug = 2;
+#else
+#define DPRINTF(x)     do { ; } while (0)
+#define DPRINTFN(n, x) do { ; } while (0)
+#endif
+
+#define DEVNAME(sc)    device_xname((sc)->sc_sc.sc_dev)
+#define letoh16                htole16
+#define letoh32                htole32
+#define nitems(x)      __arraycount(x)
+
+enum ring_status {
+       RING_CLOSED,
+       RING_CLOSING,
+       RING_OPEN,
+       RING_OPENING,
+};
+
+struct bwfm_pci_msgring {
+       uint32_t                 w_idx_addr;
+       uint32_t                 r_idx_addr;
+       uint32_t                 w_ptr;
+       uint32_t                 r_ptr;
+       int                      nitem;
+       int                      itemsz;
+       enum ring_status         status;
+       struct bwfm_pci_dmamem  *ring;
+       struct mbuf             *m;
+
+       int                      fifo;
+       uint8_t                  mac[ETHER_ADDR_LEN];
+};
+
+struct bwfm_pci_buf {
+       bus_dmamap_t     bb_map;
+       struct mbuf     *bb_m;
+};
+
+struct bwfm_pci_pkts {
+       struct bwfm_pci_buf     *pkts;
+       uint32_t                 npkt;
+       int                      last;
+};
+
+struct if_rxring {
+       u_int   rxr_total;
+       u_int   rxr_inuse;
+};
+
+struct bwfm_cmd_flowring_create {
+       struct work              wq_cookie;
+       struct bwfm_pci_softc   *sc;
+       struct mbuf             *m;
+       int                      flowid;
+       int                      prio;
+};
+
+struct bwfm_pci_softc {
+       struct bwfm_softc        sc_sc;
+       pci_chipset_tag_t        sc_pc;
+       pcitag_t                 sc_tag;
+       pcireg_t                 sc_id;
+       void                    *sc_ih;
+       pci_intr_handle_t       *sc_pihp;
+
+       bus_space_tag_t          sc_reg_iot;
+       bus_space_handle_t       sc_reg_ioh;
+       bus_size_t               sc_reg_ios;
+
+       bus_space_tag_t          sc_tcm_iot;
+       bus_space_handle_t       sc_tcm_ioh;
+       bus_size_t               sc_tcm_ios;
+
+       bus_dma_tag_t            sc_dmat;
+
+       uint32_t                 sc_shared_address;
+       uint32_t                 sc_shared_flags;
+       uint8_t                  sc_shared_version;
+
+       uint8_t                  sc_dma_idx_sz;
+       struct bwfm_pci_dmamem  *sc_dma_idx_buf;
+       size_t                   sc_dma_idx_bufsz;
+
+       uint16_t                 sc_max_rxbufpost;
+       uint32_t                 sc_rx_dataoffset;
+       uint32_t                 sc_htod_mb_data_addr;
+       uint32_t                 sc_dtoh_mb_data_addr;
+       uint32_t                 sc_ring_info_addr;
+
+       uint32_t                 sc_console_base_addr;
+       uint32_t                 sc_console_buf_addr;
+       uint32_t                 sc_console_buf_size;
+       uint32_t                 sc_console_readidx;
+
+       struct pool              sc_flowring_pool;
+       struct workqueue        *flowring_wq;
+
+       uint16_t                 sc_max_flowrings;
+       uint16_t                 sc_max_submissionrings;
+       uint16_t                 sc_max_completionrings;
+
+       struct bwfm_pci_msgring  sc_ctrl_submit;
+       struct bwfm_pci_msgring  sc_rxpost_submit;
+       struct bwfm_pci_msgring  sc_ctrl_complete;
+       struct bwfm_pci_msgring  sc_tx_complete;
+       struct bwfm_pci_msgring  sc_rx_complete;
+       struct bwfm_pci_msgring *sc_flowrings;
+
+       struct bwfm_pci_dmamem  *sc_scratch_buf;
+       struct bwfm_pci_dmamem  *sc_ringupd_buf;
+
+       struct bwfm_pci_dmamem  *sc_ioctl_buf;
+       int                      sc_ioctl_reqid;
+       uint32_t                 sc_ioctl_resp_pktid;
+       uint32_t                 sc_ioctl_resp_ret_len;
+       uint32_t                 sc_ioctl_resp_status;
+       int                      sc_ioctl_poll;
+
+       struct if_rxring         sc_ioctl_ring;
+       struct if_rxring         sc_event_ring;
+       struct if_rxring         sc_rxbuf_ring;
+
+       struct bwfm_pci_pkts     sc_rx_pkts;
+       struct bwfm_pci_pkts     sc_tx_pkts;
+       int                      sc_tx_pkts_full;
+};
+
+struct bwfm_pci_dmamem {
+       bus_dmamap_t            bdm_map;
+       bus_dma_segment_t       bdm_seg;
+       size_t                  bdm_size;
+       char *                  bdm_kva;
+};
+
+#define BWFM_PCI_DMA_MAP(_bdm) ((_bdm)->bdm_map)
+#define BWFM_PCI_DMA_LEN(_bdm) ((_bdm)->bdm_size)
+#define BWFM_PCI_DMA_DVA(_bdm) ((_bdm)->bdm_map->dm_segs[0].ds_addr)
+#define BWFM_PCI_DMA_KVA(_bdm) ((_bdm)->bdm_kva)
+
+static u_int    if_rxr_get(struct if_rxring *rxr, unsigned int max);
+static void     if_rxr_put(struct if_rxring *rxr, unsigned int n);
+static void     if_rxr_init(struct if_rxring *rxr, unsigned int lwm, unsigned 
int hwm);
+
+int             bwfm_pci_match(device_t parent, cfdata_t match, void *aux);
+void            bwfm_pci_attachhook(device_t);
+void            bwfm_pci_attach(device_t, device_t, void *);
+int             bwfm_pci_detach(device_t, int);
+
+int             bwfm_pci_intr(void *);
+void            bwfm_pci_intr_enable(struct bwfm_pci_softc *);
+void            bwfm_pci_intr_disable(struct bwfm_pci_softc *);
+int             bwfm_pci_load_microcode(struct bwfm_pci_softc *, const u_char 
*,
+                   size_t);
+void            bwfm_pci_select_core(struct bwfm_pci_softc *, int );
+
+struct bwfm_pci_dmamem *
+                bwfm_pci_dmamem_alloc(struct bwfm_pci_softc *, bus_size_t,
+                   bus_size_t);
+void            bwfm_pci_dmamem_free(struct bwfm_pci_softc *, struct 
bwfm_pci_dmamem *);
+int             bwfm_pci_pktid_avail(struct bwfm_pci_softc *,
+                   struct bwfm_pci_pkts *);
+int             bwfm_pci_pktid_new(struct bwfm_pci_softc *,
+                   struct bwfm_pci_pkts *, struct mbuf *,
+                   uint32_t *, paddr_t *);
+struct mbuf *   bwfm_pci_pktid_free(struct bwfm_pci_softc *,
+                   struct bwfm_pci_pkts *, uint32_t);
+void            bwfm_pci_fill_rx_ioctl_ring(struct bwfm_pci_softc *,
+                   struct if_rxring *, uint32_t);
+void            bwfm_pci_fill_rx_buf_ring(struct bwfm_pci_softc *);
+void            bwfm_pci_fill_rx_rings(struct bwfm_pci_softc *);
+int             bwfm_pci_setup_ring(struct bwfm_pci_softc *, struct 
bwfm_pci_msgring *,
+                   int, size_t, uint32_t, uint32_t, int, uint32_t, uint32_t *);
+int             bwfm_pci_setup_flowring(struct bwfm_pci_softc *, struct 
bwfm_pci_msgring *,
+                   int, size_t);
+
+void            bwfm_pci_ring_bell(struct bwfm_pci_softc *,
+                   struct bwfm_pci_msgring *);
+void            bwfm_pci_ring_update_rptr(struct bwfm_pci_softc *,
+                   struct bwfm_pci_msgring *);
+void            bwfm_pci_ring_update_wptr(struct bwfm_pci_softc *,
+                   struct bwfm_pci_msgring *);
+void            bwfm_pci_ring_write_rptr(struct bwfm_pci_softc *,
+                   struct bwfm_pci_msgring *);
+void            bwfm_pci_ring_write_wptr(struct bwfm_pci_softc *,
+                   struct bwfm_pci_msgring *);
+void *          bwfm_pci_ring_write_reserve(struct bwfm_pci_softc *,
+                   struct bwfm_pci_msgring *);
+void *          bwfm_pci_ring_write_reserve_multi(struct bwfm_pci_softc *,
+                   struct bwfm_pci_msgring *, int, int *);
+void *          bwfm_pci_ring_read_avail(struct bwfm_pci_softc *,
+                   struct bwfm_pci_msgring *, int *);
+void            bwfm_pci_ring_read_commit(struct bwfm_pci_softc *,
+                   struct bwfm_pci_msgring *, int);
+void            bwfm_pci_ring_write_commit(struct bwfm_pci_softc *,
+                   struct bwfm_pci_msgring *);
+void            bwfm_pci_ring_write_cancel(struct bwfm_pci_softc *,
+                   struct bwfm_pci_msgring *, int);
+
+void            bwfm_pci_ring_rx(struct bwfm_pci_softc *,
+                   struct bwfm_pci_msgring *);
+void            bwfm_pci_msg_rx(struct bwfm_pci_softc *, void *);
+
+uint32_t        bwfm_pci_buscore_read(struct bwfm_softc *, uint32_t);
+void            bwfm_pci_buscore_write(struct bwfm_softc *, uint32_t,
+                   uint32_t);
+int             bwfm_pci_buscore_prepare(struct bwfm_softc *);
+int             bwfm_pci_buscore_reset(struct bwfm_softc *);
+void            bwfm_pci_buscore_activate(struct bwfm_softc *, const uint32_t);
+
+int             bwfm_pci_flowring_lookup(struct bwfm_pci_softc *,
+                    struct mbuf *);
+void            bwfm_pci_flowring_create(struct bwfm_pci_softc *,
+                    struct mbuf *);
+void            bwfm_pci_flowring_create_cb(struct work *, void *);
+void            bwfm_pci_flowring_delete(struct bwfm_pci_softc *, int);
+
+void            bwfm_pci_stop(struct bwfm_softc *);
+int             bwfm_pci_txcheck(struct bwfm_softc *);
+int             bwfm_pci_txdata(struct bwfm_softc *, struct mbuf *);
+
+#ifdef BWFM_DEBUG
+void            bwfm_pci_debug_console(struct bwfm_pci_softc *);
+#endif
+
+int             bwfm_pci_msgbuf_query_dcmd(struct bwfm_softc *, int,
+                   int, char *, size_t *);
+int             bwfm_pci_msgbuf_set_dcmd(struct bwfm_softc *, int,
+                   int, char *, size_t);
+
+struct bwfm_buscore_ops bwfm_pci_buscore_ops = {
+       .bc_read = bwfm_pci_buscore_read,
+       .bc_write = bwfm_pci_buscore_write,
+       .bc_prepare = bwfm_pci_buscore_prepare,
+       .bc_reset = bwfm_pci_buscore_reset,
+       .bc_setup = NULL,
+       .bc_activate = bwfm_pci_buscore_activate,
+};
+
+struct bwfm_bus_ops bwfm_pci_bus_ops = {
+       .bs_init = NULL,
+       .bs_stop = bwfm_pci_stop,
+       .bs_txcheck = bwfm_pci_txcheck,
+       .bs_txdata = bwfm_pci_txdata,
+       .bs_txctl = NULL,
+       .bs_rxctl = NULL,
+};
+
+struct bwfm_proto_ops bwfm_pci_msgbuf_ops = {
+       .proto_query_dcmd = bwfm_pci_msgbuf_query_dcmd,
+       .proto_set_dcmd = bwfm_pci_msgbuf_set_dcmd,
+};
+
+
+CFATTACH_DECL_NEW(bwfm_pci, sizeof(struct bwfm_pci_softc),
+    bwfm_pci_match, bwfm_pci_attach, bwfm_pci_detach, NULL);
+
+static const struct bwfm_pci_matchid {
+       pci_vendor_id_t         bwfm_vendor;
+       pci_product_id_t        bwfm_product;
+} bwfm_pci_devices[] = {
+       { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM43602 },
+       { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM4350 },
+};
+
+static struct mbuf *
+MCLGETI(struct bwfm_pci_softc *sc __unused, int how,
+    struct ifnet *ifp __unused, u_int size)
+{
+       struct mbuf *m;
+
+       MGETHDR(m, how, MT_DATA);
+       if (m == NULL)
+               return NULL;
+
+       MEXTMALLOC(m, size, how);
+       if ((m->m_flags & M_EXT) == 0) {
+               m_freem(m);
+               return NULL;
+       }
+       return m;
+}
+
+int
+bwfm_pci_match(device_t parent, cfdata_t match, void *aux)
+{
+       struct pci_attach_args *pa = aux;
+
+       if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_BROADCOM)
+               return 0;
+
+       for (size_t i = 0; i < __arraycount(bwfm_pci_devices); i++)
+               if (PCI_PRODUCT(pa->pa_id) == bwfm_pci_devices[i].bwfm_product)
+                       return 1;
+
+       return 0;
+}
+
+void
+bwfm_pci_attach(device_t parent, device_t self, void *aux)
+{
+       struct bwfm_pci_softc *sc = device_private(self);
+       struct pci_attach_args *pa = (struct pci_attach_args *)aux;
+       const char *intrstr;
+       char intrbuf[PCI_INTRSTR_LEN];
+
+       sc->sc_sc.sc_dev = self;
+
+       if (pci_mapreg_map(pa, PCI_MAPREG_START + 0x00,
+           PCI_MAPREG_MEM_TYPE_64BIT, 0, &sc->sc_reg_iot, &sc->sc_reg_ioh,
+           NULL, &sc->sc_reg_ios)) {
+               printf(": can't map bar0\n");
+               return;
+       }
+
+       if (pci_mapreg_map(pa, PCI_MAPREG_START + 0x08,
+           PCI_MAPREG_MEM_TYPE_64BIT, 0, &sc->sc_tcm_iot, &sc->sc_tcm_ioh,
+           NULL, &sc->sc_tcm_ios)) {
+               printf(": can't map bar1\n");
+               goto bar0;
+       }
+
+       sc->sc_pc = pa->pa_pc;
+       sc->sc_tag = pa->pa_tag;
+       sc->sc_id = pa->pa_id;
+
+       if (pci_dma64_available(pa))
+               sc->sc_dmat = pa->pa_dmat64;
+       else
+               sc->sc_dmat = pa->pa_dmat;
+
+       /* Map and establish the interrupt. */
+       if (pci_intr_alloc(pa, &sc->sc_pihp, NULL, 0) != 0) {
+               printf(": couldn't map interrupt\n");
+               goto bar1;
+       }
+       intrstr = pci_intr_string(pa->pa_pc, sc->sc_pihp[0], intrbuf, 
sizeof(intrbuf));
+
+       sc->sc_ih = pci_intr_establish(pa->pa_pc, sc->sc_pihp[0], IPL_NET,
+           bwfm_pci_intr, sc);
+       if (sc->sc_ih == NULL) {
+               printf(": couldn't establish interrupt");
+               if (intrstr != NULL)
+                       printf(" at %s", intrstr);
+               printf("\n");
+               goto bar1;
+       }
+       printf(": %s\n", intrstr);
+
+       config_mountroot(self, bwfm_pci_attachhook);
+       return;
+
+bar1:
+       bus_space_unmap(sc->sc_tcm_iot, sc->sc_tcm_ioh, sc->sc_tcm_ios);
+bar0:
+       bus_space_unmap(sc->sc_reg_iot, sc->sc_reg_ioh, sc->sc_reg_ios);
+}
+
+void
+bwfm_pci_attachhook(device_t self)
+{
+       struct bwfm_pci_softc *sc = device_private(self);
+       struct bwfm_softc *bwfm = (void *)sc;
+       struct bwfm_pci_ringinfo ringinfo;
+       const char *name = NULL;
+       firmware_handle_t fwh;
+       u_char *ucode; size_t size;
+       uint32_t d2h_w_idx_ptr, d2h_r_idx_ptr;
+       uint32_t h2d_w_idx_ptr, h2d_r_idx_ptr;
+       uint32_t idx_offset, reg;
+       int i;
+       int error;
+
+       sc->sc_sc.sc_buscore_ops = &bwfm_pci_buscore_ops;
+       if (bwfm_chip_attach(&sc->sc_sc) != 0) {
+               printf("%s: cannot attach chip\n", DEVNAME(sc));
+               return;
+       }
+
+       bwfm_pci_select_core(sc, BWFM_AGENT_CORE_PCIE2);
+       bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+           BWFM_PCI_PCIE2REG_CONFIGADDR, 0x4e0);
+       reg = bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+           BWFM_PCI_PCIE2REG_CONFIGDATA);
+       bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+           BWFM_PCI_PCIE2REG_CONFIGDATA, reg);
+
+       switch (bwfm->sc_chip.ch_chip)
+       {
+       case BRCM_CC_4350_CHIP_ID:
+               if (bwfm->sc_chip.ch_chiprev > 7)
+                       name = "brcmfmac4350-pcie.bin";
+               else
+                       name = "brcmfmac4350c2-pcie.bin";
+               break;
+       case BRCM_CC_43602_CHIP_ID:
+               name = "brcmfmac43602-pcie.bin";
+               break;
+       default:
+               printf("%s: unknown firmware for chip %s\n",
+                   DEVNAME(sc), bwfm->sc_chip.ch_name);
+               return;
+       }
+
+       if (firmware_open("if_bwfm", name, &fwh) != 0) {
+               printf("%s: failed firmware_open of file %s\n",
+                   DEVNAME(sc), name);
+               return;
+       }
+       size = firmware_get_size(fwh);
+       ucode = firmware_malloc(size);
+       if (ucode == NULL) {
+               printf("%s: failed to allocate firmware memory\n",
+                   DEVNAME(sc));
+               firmware_close(fwh);
+               return;
+       }
+       error = firmware_read(fwh, 0, ucode, size);
+       firmware_close(fwh);
+       if (error != 0) {
+               printf("%s: failed to read firmware (error %d)\n",
+                   DEVNAME(sc), error);
+               firmware_free(ucode, size);
+               return;
+       }
+
+       /* Retrieve RAM size from firmware. */
+       if (size >= BWFM_RAMSIZE + 8) {
+               uint32_t *ramsize = (uint32_t *)&ucode[BWFM_RAMSIZE];
+               if (letoh32(ramsize[0]) == BWFM_RAMSIZE_MAGIC)
+                       bwfm->sc_chip.ch_ramsize = letoh32(ramsize[1]);
+       }
+
+       if (bwfm_pci_load_microcode(sc, ucode, size) != 0) {
+               printf("%s: could not load microcode\n",
+                   DEVNAME(sc));
+               kmem_free(ucode, size);
+               return;
+       }
+
+       firmware_free(ucode, size);
+
+       sc->sc_shared_flags = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           sc->sc_shared_address + BWFM_SHARED_INFO);
+       sc->sc_shared_version = sc->sc_shared_flags;
+       if (sc->sc_shared_version > BWFM_SHARED_INFO_MAX_VERSION ||
+           sc->sc_shared_version < BWFM_SHARED_INFO_MIN_VERSION) {
+               printf("%s: PCIe version %d unsupported\n",
+                   DEVNAME(sc), sc->sc_shared_version);
+               return;
+       }
+
+       if (sc->sc_shared_flags & BWFM_SHARED_INFO_DMA_INDEX) {
+               if (sc->sc_shared_flags & BWFM_SHARED_INFO_DMA_2B_IDX)
+                       sc->sc_dma_idx_sz = sizeof(uint16_t);
+               else
+                       sc->sc_dma_idx_sz = sizeof(uint32_t);
+       }
+
+       /* Maximum RX data buffers in the ring. */
+       sc->sc_max_rxbufpost = bus_space_read_2(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           sc->sc_shared_address + BWFM_SHARED_MAX_RXBUFPOST);
+       if (sc->sc_max_rxbufpost == 0)
+               sc->sc_max_rxbufpost = BWFM_SHARED_MAX_RXBUFPOST_DEFAULT;
+
+       /* Alternative offset of data in a packet */
+       sc->sc_rx_dataoffset = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           sc->sc_shared_address + BWFM_SHARED_RX_DATAOFFSET);
+
+       /* For Power Management */
+       sc->sc_htod_mb_data_addr = bus_space_read_4(sc->sc_tcm_iot, 
sc->sc_tcm_ioh,
+           sc->sc_shared_address + BWFM_SHARED_HTOD_MB_DATA_ADDR);
+       sc->sc_dtoh_mb_data_addr = bus_space_read_4(sc->sc_tcm_iot, 
sc->sc_tcm_ioh,
+           sc->sc_shared_address + BWFM_SHARED_DTOH_MB_DATA_ADDR);
+
+       /* Ring information */
+       sc->sc_ring_info_addr = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           sc->sc_shared_address + BWFM_SHARED_RING_INFO_ADDR);
+
+       /* Firmware's "dmesg" */
+       sc->sc_console_base_addr = bus_space_read_4(sc->sc_tcm_iot, 
sc->sc_tcm_ioh,
+           sc->sc_shared_address + BWFM_SHARED_CONSOLE_ADDR);
+       sc->sc_console_buf_addr = bus_space_read_4(sc->sc_tcm_iot, 
sc->sc_tcm_ioh,
+           sc->sc_console_base_addr + BWFM_CONSOLE_BUFADDR);
+       sc->sc_console_buf_size = bus_space_read_4(sc->sc_tcm_iot, 
sc->sc_tcm_ioh,
+           sc->sc_console_base_addr + BWFM_CONSOLE_BUFSIZE);
+
+       /* Read ring information. */
+       bus_space_read_region_1(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           sc->sc_ring_info_addr, (void *)&ringinfo, sizeof(ringinfo));
+
+       if (sc->sc_shared_version >= 6) {
+               sc->sc_max_submissionrings = 
le16toh(ringinfo.max_submissionrings);
+               sc->sc_max_flowrings = le16toh(ringinfo.max_flowrings);
+               sc->sc_max_completionrings = 
le16toh(ringinfo.max_completionrings);
+       } else {
+               sc->sc_max_submissionrings = le16toh(ringinfo.max_flowrings);
+               sc->sc_max_flowrings = sc->sc_max_submissionrings -
+                   BWFM_NUM_TX_MSGRINGS;
+               sc->sc_max_completionrings = BWFM_NUM_RX_MSGRINGS;
+       }
+
+       if (sc->sc_dma_idx_sz == 0) {
+               d2h_w_idx_ptr = letoh32(ringinfo.d2h_w_idx_ptr);
+               d2h_r_idx_ptr = letoh32(ringinfo.d2h_r_idx_ptr);
+               h2d_w_idx_ptr = letoh32(ringinfo.h2d_w_idx_ptr);
+               h2d_r_idx_ptr = letoh32(ringinfo.h2d_r_idx_ptr);
+               idx_offset = sizeof(uint32_t);
+       } else {
+               uint64_t address;
+
+               /* Each TX/RX Ring has a Read and Write Ptr */
+               sc->sc_dma_idx_bufsz = (sc->sc_max_submissionrings +
+                   sc->sc_max_completionrings) * sc->sc_dma_idx_sz * 2;
+               sc->sc_dma_idx_buf = bwfm_pci_dmamem_alloc(sc,
+                   sc->sc_dma_idx_bufsz, 8);
+               if (sc->sc_dma_idx_buf == NULL) {
+                       /* XXX: Fallback to TCM? */
+                       printf("%s: cannot allocate idx buf\n",
+                           DEVNAME(sc));
+                       return;
+               }
+
+               idx_offset = sc->sc_dma_idx_sz;
+               h2d_w_idx_ptr = 0;
+               address = BWFM_PCI_DMA_DVA(sc->sc_dma_idx_buf);
+               ringinfo.h2d_w_idx_hostaddr_low =
+                   htole32(address & 0xffffffff);
+               ringinfo.h2d_w_idx_hostaddr_high =
+                   htole32(address >> 32);
+
+               h2d_r_idx_ptr = h2d_w_idx_ptr +
+                   sc->sc_max_submissionrings * idx_offset;
+               address += sc->sc_max_submissionrings * idx_offset;
+               ringinfo.h2d_r_idx_hostaddr_low =
+                   htole32(address & 0xffffffff);
+               ringinfo.h2d_r_idx_hostaddr_high =
+                   htole32(address >> 32);
+
+               d2h_w_idx_ptr = h2d_r_idx_ptr +
+                   sc->sc_max_submissionrings * idx_offset;
+               address += sc->sc_max_submissionrings * idx_offset;
+               ringinfo.d2h_w_idx_hostaddr_low =
+                   htole32(address & 0xffffffff);
+               ringinfo.d2h_w_idx_hostaddr_high =
+                   htole32(address >> 32);
+
+               d2h_r_idx_ptr = d2h_w_idx_ptr +
+                   sc->sc_max_completionrings * idx_offset;
+               address += sc->sc_max_completionrings * idx_offset;
+               ringinfo.d2h_r_idx_hostaddr_low =
+                   htole32(address & 0xffffffff);
+               ringinfo.d2h_r_idx_hostaddr_high =
+                   htole32(address >> 32);
+
+               bus_space_write_region_1(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+                   sc->sc_ring_info_addr, (void *)&ringinfo, sizeof(ringinfo));
+       }
+
+       uint32_t ring_mem_ptr = letoh32(ringinfo.ringmem);
+       /* TX ctrl ring: Send ctrl buffers, send IOCTLs */
+       if (bwfm_pci_setup_ring(sc, &sc->sc_ctrl_submit, 64, 40,
+           h2d_w_idx_ptr, h2d_r_idx_ptr, 0, idx_offset,
+           &ring_mem_ptr))
+               goto cleanup;
+       /* TX rxpost ring: Send clean data mbufs for RX */
+       if (bwfm_pci_setup_ring(sc, &sc->sc_rxpost_submit, 512, 32,
+           h2d_w_idx_ptr, h2d_r_idx_ptr, 1, idx_offset,
+           &ring_mem_ptr))
+               goto cleanup;
+       /* RX completion rings: recv our filled buffers back */
+       if (bwfm_pci_setup_ring(sc, &sc->sc_ctrl_complete, 64, 24,
+           d2h_w_idx_ptr, d2h_r_idx_ptr, 0, idx_offset,
+           &ring_mem_ptr))
+               goto cleanup;
+       if (bwfm_pci_setup_ring(sc, &sc->sc_tx_complete, 1024, 16,
+           d2h_w_idx_ptr, d2h_r_idx_ptr, 1, idx_offset,
+           &ring_mem_ptr))
+               goto cleanup;
+       if (bwfm_pci_setup_ring(sc, &sc->sc_rx_complete, 512, 32,
+           d2h_w_idx_ptr, d2h_r_idx_ptr, 2, idx_offset,
+           &ring_mem_ptr))
+               goto cleanup;
+
+       /* Dynamic TX rings for actual data */
+       sc->sc_flowrings = kmem_zalloc(sc->sc_max_flowrings *
+           sizeof(struct bwfm_pci_msgring), KM_SLEEP);
+       for (i = 0; i < sc->sc_max_flowrings; i++) {
+               struct bwfm_pci_msgring *ring = &sc->sc_flowrings[i];
+               ring->w_idx_addr = h2d_w_idx_ptr + (i + 2) * idx_offset;
+               ring->r_idx_addr = h2d_r_idx_ptr + (i + 2) * idx_offset;
+       }
+
+       pool_init(&sc->sc_flowring_pool, sizeof(struct 
bwfm_cmd_flowring_create),
+           0, 0, 0, "bwfmpl", NULL, IPL_NET);
+
+       /* Scratch and ring update buffers for firmware */
+       if ((sc->sc_scratch_buf = bwfm_pci_dmamem_alloc(sc,
+           BWFM_DMA_D2H_SCRATCH_BUF_LEN, 8)) == NULL)
+               goto cleanup;
+       bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           sc->sc_shared_address + BWFM_SHARED_DMA_SCRATCH_ADDR_LOW,
+           BWFM_PCI_DMA_DVA(sc->sc_scratch_buf) & 0xffffffff);
+       bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           sc->sc_shared_address + BWFM_SHARED_DMA_SCRATCH_ADDR_HIGH,
+           BWFM_PCI_DMA_DVA(sc->sc_scratch_buf) >> 32);
+       bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           sc->sc_shared_address + BWFM_SHARED_DMA_SCRATCH_LEN,
+           BWFM_DMA_D2H_SCRATCH_BUF_LEN);
+
+       if ((sc->sc_ringupd_buf = bwfm_pci_dmamem_alloc(sc,
+           BWFM_DMA_D2H_RINGUPD_BUF_LEN, 8)) == NULL)
+               goto cleanup;
+       bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           sc->sc_shared_address + BWFM_SHARED_DMA_RINGUPD_ADDR_LOW,
+           BWFM_PCI_DMA_DVA(sc->sc_ringupd_buf) & 0xffffffff);
+       bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           sc->sc_shared_address + BWFM_SHARED_DMA_RINGUPD_ADDR_HIGH,
+           BWFM_PCI_DMA_DVA(sc->sc_ringupd_buf) >> 32);
+       bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           sc->sc_shared_address + BWFM_SHARED_DMA_RINGUPD_LEN,
+           BWFM_DMA_D2H_RINGUPD_BUF_LEN);
+
+       if ((sc->sc_ioctl_buf = bwfm_pci_dmamem_alloc(sc,
+           BWFM_DMA_H2D_IOCTL_BUF_LEN, 8)) == NULL)
+               goto cleanup;
+
+       if (workqueue_create(&sc->flowring_wq, "bwfmflow",
+           bwfm_pci_flowring_create_cb, sc, PRI_SOFTNET, IPL_NET, 0))
+               goto cleanup;
+
+       bwfm_pci_select_core(sc, BWFM_AGENT_CORE_PCIE2);
+       bwfm_pci_intr_enable(sc);
+
+       /* Maps RX mbufs to a packet id and back. */
+       sc->sc_rx_pkts.npkt = BWFM_NUM_RX_PKTIDS;
+       sc->sc_rx_pkts.pkts = kmem_zalloc(BWFM_NUM_RX_PKTIDS *
+           sizeof(struct bwfm_pci_buf), KM_SLEEP);
+       for (i = 0; i < BWFM_NUM_RX_PKTIDS; i++)
+               bus_dmamap_create(sc->sc_dmat, MSGBUF_MAX_PKT_SIZE,
+                   BWFM_NUM_RX_DESCS, MSGBUF_MAX_PKT_SIZE, 0, BUS_DMA_WAITOK,
+                   &sc->sc_rx_pkts.pkts[i].bb_map);
+
+       /* Maps TX mbufs to a packet id and back. */
+       sc->sc_tx_pkts.npkt = BWFM_NUM_TX_PKTIDS;
+       sc->sc_tx_pkts.pkts = kmem_zalloc(BWFM_NUM_TX_PKTIDS
+           * sizeof(struct bwfm_pci_buf), KM_SLEEP);
+       for (i = 0; i < BWFM_NUM_TX_PKTIDS; i++)
+               bus_dmamap_create(sc->sc_dmat, MSGBUF_MAX_PKT_SIZE,
+                   BWFM_NUM_TX_DESCS, MSGBUF_MAX_PKT_SIZE, 0, BUS_DMA_WAITOK,
+                   &sc->sc_tx_pkts.pkts[i].bb_map);
+
+       /*
+        * For whatever reason, could also be a bug somewhere in this
+        * driver, the firmware needs a bunch of RX buffers otherwise
+        * it won't send any RX complete messages.  64 buffers don't
+        * suffice, but 128 buffers are enough.
+        */
+       if_rxr_init(&sc->sc_rxbuf_ring, 128, sc->sc_max_rxbufpost);
+       if_rxr_init(&sc->sc_ioctl_ring, 8, 8);
+       if_rxr_init(&sc->sc_event_ring, 8, 8);
+       bwfm_pci_fill_rx_rings(sc);
+
+
+#ifdef BWFM_DEBUG
+       sc->sc_console_readidx = 0;
+       bwfm_pci_debug_console(sc);
+#endif
+
+       sc->sc_ioctl_poll = 1;
+       sc->sc_sc.sc_bus_ops = &bwfm_pci_bus_ops;
+       sc->sc_sc.sc_proto_ops = &bwfm_pci_msgbuf_ops;
+       bwfm_attach(&sc->sc_sc);
+       sc->sc_ioctl_poll = 0;
+       return;
+
+cleanup:
+       if (sc->flowring_wq != NULL)
+               workqueue_destroy(sc->flowring_wq);
+       if (sc->sc_ih != NULL) {
+               pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
+               pci_intr_release(sc->sc_pc, sc->sc_pihp, 1);
+       }
+       if (sc->sc_ioctl_buf)
+               bwfm_pci_dmamem_free(sc, sc->sc_ioctl_buf);
+       if (sc->sc_ringupd_buf)
+               bwfm_pci_dmamem_free(sc, sc->sc_ringupd_buf);
+       if (sc->sc_scratch_buf)
+               bwfm_pci_dmamem_free(sc, sc->sc_scratch_buf);
+       if (sc->sc_rx_complete.ring)
+               bwfm_pci_dmamem_free(sc, sc->sc_rx_complete.ring);
+       if (sc->sc_tx_complete.ring)
+               bwfm_pci_dmamem_free(sc, sc->sc_tx_complete.ring);
+       if (sc->sc_ctrl_complete.ring)
+               bwfm_pci_dmamem_free(sc, sc->sc_ctrl_complete.ring);
+       if (sc->sc_rxpost_submit.ring)
+               bwfm_pci_dmamem_free(sc, sc->sc_rxpost_submit.ring);
+       if (sc->sc_ctrl_submit.ring)
+               bwfm_pci_dmamem_free(sc, sc->sc_ctrl_submit.ring);
+       if (sc->sc_dma_idx_buf)
+               bwfm_pci_dmamem_free(sc, sc->sc_dma_idx_buf);
+}
+
+int
+bwfm_pci_load_microcode(struct bwfm_pci_softc *sc, const u_char *ucode, size_t 
size)
+{
+       struct bwfm_softc *bwfm = (void *)sc;
+       struct bwfm_core *core;
+       uint32_t shared;
+       int i;
+
+       if (bwfm->sc_chip.ch_chip == BRCM_CC_43602_CHIP_ID) {
+               bwfm_pci_select_core(sc, BWFM_AGENT_CORE_ARM_CR4);
+               bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+                   BWFM_PCI_ARMCR4REG_BANKIDX, 5);
+               bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+                   BWFM_PCI_ARMCR4REG_BANKPDA, 0);
+               bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+                   BWFM_PCI_ARMCR4REG_BANKIDX, 7);
+               bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+                   BWFM_PCI_ARMCR4REG_BANKPDA, 0);
+       }
+
+       for (i = 0; i < size; i++)
+               bus_space_write_1(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+                   bwfm->sc_chip.ch_rambase + i, ucode[i]);
+
+       /* Firmware replaces this with a pointer once up. */
+       bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           bwfm->sc_chip.ch_rambase + bwfm->sc_chip.ch_ramsize - 4, 0);
+
+       /* TODO: restore NVRAM */
+
+       /* Load reset vector from firmware and kickstart core. */
+       if (bwfm->sc_chip.ch_chip == BRCM_CC_43602_CHIP_ID) {
+               core = bwfm_chip_get_core(bwfm, BWFM_AGENT_INTERNAL_MEM);
+               bwfm->sc_chip.ch_core_reset(bwfm, core, 0, 0, 0);
+       }
+       bwfm_chip_set_active(bwfm, *(const uint32_t *)ucode);
+
+       for (i = 0; i < 40; i++) {
+               delay(50 * 1000);
+               shared = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+                   bwfm->sc_chip.ch_rambase + bwfm->sc_chip.ch_ramsize - 4);
+               if (shared)
+                       break;
+       }
+       if (!shared) {
+               printf("%s: firmware did not come up\n", DEVNAME(sc));
+               return 1;
+       }
+
+       sc->sc_shared_address = shared;
+       return 0;
+}
+
+int
+bwfm_pci_detach(device_t self, int flags)
+{
+       struct bwfm_pci_softc *sc = device_private(self);
+
+       bwfm_detach(&sc->sc_sc, flags);
+
+       /* FIXME: free RX buffers */
+       /* FIXME: free TX buffers */
+       /* FIXME: free more memory */
+
+       kmem_free(sc->sc_flowrings, sc->sc_max_flowrings
+           * sizeof(struct bwfm_pci_msgring));
+       pool_destroy(&sc->sc_flowring_pool);
+
+       workqueue_destroy(sc->flowring_wq);
+       pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
+       pci_intr_release(sc->sc_pc, sc->sc_pihp, 1);
+       bwfm_pci_dmamem_free(sc, sc->sc_ioctl_buf);
+       bwfm_pci_dmamem_free(sc, sc->sc_ringupd_buf);
+       bwfm_pci_dmamem_free(sc, sc->sc_scratch_buf);
+       bwfm_pci_dmamem_free(sc, sc->sc_rx_complete.ring);
+       bwfm_pci_dmamem_free(sc, sc->sc_tx_complete.ring);
+       bwfm_pci_dmamem_free(sc, sc->sc_ctrl_complete.ring);
+       bwfm_pci_dmamem_free(sc, sc->sc_rxpost_submit.ring);
+       bwfm_pci_dmamem_free(sc, sc->sc_ctrl_submit.ring);
+       bwfm_pci_dmamem_free(sc, sc->sc_dma_idx_buf);
+       return 0;
+}
+
+/* DMA code */
+struct bwfm_pci_dmamem *
+bwfm_pci_dmamem_alloc(struct bwfm_pci_softc *sc, bus_size_t size, bus_size_t 
align)
+{
+       struct bwfm_pci_dmamem *bdm;
+       int nsegs;
+
+       bdm = kmem_zalloc(sizeof(*bdm), KM_SLEEP);
+       bdm->bdm_size = size;
+
+       if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
+           BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &bdm->bdm_map) != 0)
+               goto bdmfree;
+
+       if (bus_dmamem_alloc(sc->sc_dmat, size, align, 0, &bdm->bdm_seg, 1,
+           &nsegs, BUS_DMA_WAITOK) != 0)
+               goto destroy;
+
+       if (bus_dmamem_map(sc->sc_dmat, &bdm->bdm_seg, nsegs, size,
+           (void **) &bdm->bdm_kva, BUS_DMA_WAITOK | BUS_DMA_COHERENT) != 0)
+               goto free;
+
+       if (bus_dmamap_load(sc->sc_dmat, bdm->bdm_map, bdm->bdm_kva, size,
+           NULL, BUS_DMA_WAITOK) != 0)
+               goto unmap;
+
+       bzero(bdm->bdm_kva, size);
+
+       return (bdm);
+
+unmap:
+       bus_dmamem_unmap(sc->sc_dmat, bdm->bdm_kva, size);
+free:
+       bus_dmamem_free(sc->sc_dmat, &bdm->bdm_seg, 1);
+destroy:
+       bus_dmamap_destroy(sc->sc_dmat, bdm->bdm_map);
+bdmfree:
+       kmem_free(bdm, sizeof(*bdm));
+
+       return (NULL);
+}
+
+void
+bwfm_pci_dmamem_free(struct bwfm_pci_softc *sc, struct bwfm_pci_dmamem *bdm)
+{
+       bus_dmamem_unmap(sc->sc_dmat, bdm->bdm_kva, bdm->bdm_size);
+       bus_dmamem_free(sc->sc_dmat, &bdm->bdm_seg, 1);
+       bus_dmamap_destroy(sc->sc_dmat, bdm->bdm_map);
+       kmem_free(bdm, sizeof(*bdm));
+}
+
+/*
+ * We need a simple mapping from a packet ID to mbufs, because when
+ * a transfer completed, we only know the ID so we have to look up
+ * the memory for the ID.  This simply looks for an empty slot.
+ */
+int
+bwfm_pci_pktid_avail(struct bwfm_pci_softc *sc, struct bwfm_pci_pkts *pkts)
+{
+       int i, idx;
+
+       idx = pkts->last + 1;
+       for (i = 0; i < pkts->npkt; i++) {
+               if (idx == pkts->npkt)
+                       idx = 0;
+               if (pkts->pkts[idx].bb_m == NULL)
+                       return 0;
+               idx++;
+       }
+       return ENOBUFS;
+}
+
+int
+bwfm_pci_pktid_new(struct bwfm_pci_softc *sc, struct bwfm_pci_pkts *pkts,
+    struct mbuf *m, uint32_t *pktid, paddr_t *paddr)
+{
+       int i, idx;
+
+       idx = pkts->last + 1;
+       for (i = 0; i < pkts->npkt; i++) {
+               if (idx == pkts->npkt)
+                       idx = 0;
+               if (pkts->pkts[idx].bb_m == NULL) {
+                       if (bus_dmamap_load_mbuf(sc->sc_dmat,
+                           pkts->pkts[idx].bb_map, m, BUS_DMA_NOWAIT) != 0) {
+                               if (m_defrag(m, M_DONTWAIT))
+                                       return EFBIG;
+                               if (bus_dmamap_load_mbuf(sc->sc_dmat,
+                                   pkts->pkts[idx].bb_map, m, BUS_DMA_NOWAIT) 
!= 0)
+                                       return EFBIG;
+                       }
+                       pkts->last = idx;
+                       pkts->pkts[idx].bb_m = m;
+                       *pktid = idx;
+                       *paddr = pkts->pkts[idx].bb_map->dm_segs[0].ds_addr;
+                       return 0;
+               }
+               idx++;
+       }
+       return ENOBUFS;
+}
+
+struct mbuf *
+bwfm_pci_pktid_free(struct bwfm_pci_softc *sc, struct bwfm_pci_pkts *pkts,
+    uint32_t pktid)
+{
+       struct mbuf *m;
+
+       if (pktid >= pkts->npkt || pkts->pkts[pktid].bb_m == NULL)
+               return NULL;
+       bus_dmamap_unload(sc->sc_dmat, pkts->pkts[pktid].bb_map);
+       m = pkts->pkts[pktid].bb_m;
+       pkts->pkts[pktid].bb_m = NULL;
+       return m;
+}
+
+void
+bwfm_pci_fill_rx_rings(struct bwfm_pci_softc *sc)
+{
+       bwfm_pci_fill_rx_buf_ring(sc);
+       bwfm_pci_fill_rx_ioctl_ring(sc, &sc->sc_ioctl_ring,
+           MSGBUF_TYPE_IOCTLRESP_BUF_POST);
+       bwfm_pci_fill_rx_ioctl_ring(sc, &sc->sc_event_ring,
+           MSGBUF_TYPE_EVENT_BUF_POST);
+}
+
+void
+bwfm_pci_fill_rx_ioctl_ring(struct bwfm_pci_softc *sc, struct if_rxring 
*rxring,
+    uint32_t msgtype)
+{
+       struct msgbuf_rx_ioctl_resp_or_event *req;
+       struct mbuf *m;
+       uint32_t pktid;
+       paddr_t paddr;
+       int s, slots;
+
+       s = splnet();
+       for (slots = if_rxr_get(rxring, 8); slots > 0; slots--) {
+               if (bwfm_pci_pktid_avail(sc, &sc->sc_rx_pkts))
+                       break;
+               req = bwfm_pci_ring_write_reserve(sc, &sc->sc_ctrl_submit);
+               if (req == NULL)
+                       break;
+               m = MCLGETI(NULL, M_DONTWAIT, NULL, MSGBUF_MAX_PKT_SIZE);
+               if (m == NULL) {
+                       bwfm_pci_ring_write_cancel(sc, &sc->sc_ctrl_submit, 1);
+                       break;
+               }
+               m->m_len = m->m_pkthdr.len = MSGBUF_MAX_PKT_SIZE;
+               if (bwfm_pci_pktid_new(sc, &sc->sc_rx_pkts, m, &pktid, &paddr)) 
{
+                       bwfm_pci_ring_write_cancel(sc, &sc->sc_ctrl_submit, 1);
+                       m_freem(m);
+                       break;
+               }
+               memset(req, 0, sizeof(*req));
+               req->msg.msgtype = msgtype;
+               req->msg.request_id = htole32(pktid);
+               req->host_buf_len = htole16(MSGBUF_MAX_PKT_SIZE);
+               req->host_buf_addr.high_addr = htole32(paddr >> 32);
+               req->host_buf_addr.low_addr = htole32(paddr & 0xffffffff);
+               bwfm_pci_ring_write_commit(sc, &sc->sc_ctrl_submit);
+       }
+       if_rxr_put(rxring, slots);
+       splx(s);
+}
+
+void
+bwfm_pci_fill_rx_buf_ring(struct bwfm_pci_softc *sc)
+{
+       struct msgbuf_rx_bufpost *req;
+       struct mbuf *m;
+       uint32_t pktid;
+       paddr_t paddr;
+       int s, slots;
+
+       s = splnet();
+       for (slots = if_rxr_get(&sc->sc_rxbuf_ring, sc->sc_max_rxbufpost);
+           slots > 0; slots--) {
+               if (bwfm_pci_pktid_avail(sc, &sc->sc_rx_pkts))
+                       break;
+               req = bwfm_pci_ring_write_reserve(sc, &sc->sc_rxpost_submit);
+               if (req == NULL)
+                       break;
+               m = MCLGETI(NULL, M_DONTWAIT, NULL, MSGBUF_MAX_PKT_SIZE);
+               if (m == NULL) {
+                       bwfm_pci_ring_write_cancel(sc, &sc->sc_rxpost_submit, 
1);
+                       break;
+               }
+               m->m_len = m->m_pkthdr.len = MSGBUF_MAX_PKT_SIZE;
+               if (bwfm_pci_pktid_new(sc, &sc->sc_rx_pkts, m, &pktid, &paddr)) 
{
+                       bwfm_pci_ring_write_cancel(sc, &sc->sc_rxpost_submit, 
1);
+                       m_freem(m);
+                       break;
+               }
+               memset(req, 0, sizeof(*req));
+               req->msg.msgtype = MSGBUF_TYPE_RXBUF_POST;
+               req->msg.request_id = htole32(pktid);
+               req->data_buf_len = htole16(MSGBUF_MAX_PKT_SIZE);
+               req->data_buf_addr.high_addr = htole32(paddr >> 32);
+               req->data_buf_addr.low_addr = htole32(paddr & 0xffffffff);
+               bwfm_pci_ring_write_commit(sc, &sc->sc_rxpost_submit);
+       }
+       if_rxr_put(&sc->sc_rxbuf_ring, slots);
+       splx(s);
+}
+
+int
+bwfm_pci_setup_ring(struct bwfm_pci_softc *sc, struct bwfm_pci_msgring *ring,
+    int nitem, size_t itemsz, uint32_t w_idx, uint32_t r_idx,
+    int idx, uint32_t idx_off, uint32_t *ring_mem)
+{
+       ring->w_idx_addr = w_idx + idx * idx_off;
+       ring->r_idx_addr = r_idx + idx * idx_off;
+       ring->nitem = nitem;
+       ring->itemsz = itemsz;
+       bwfm_pci_ring_write_rptr(sc, ring);
+       bwfm_pci_ring_write_wptr(sc, ring);
+
+       ring->ring = bwfm_pci_dmamem_alloc(sc, nitem * itemsz, 8);
+       if (ring->ring == NULL)
+               return ENOMEM;
+       bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           *ring_mem + BWFM_RING_MEM_BASE_ADDR_LOW,
+           BWFM_PCI_DMA_DVA(ring->ring) & 0xffffffff);
+       bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           *ring_mem + BWFM_RING_MEM_BASE_ADDR_HIGH,
+           BWFM_PCI_DMA_DVA(ring->ring) >> 32);
+       bus_space_write_2(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           *ring_mem + BWFM_RING_MAX_ITEM, nitem);
+       bus_space_write_2(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           *ring_mem + BWFM_RING_LEN_ITEMS, itemsz);
+       *ring_mem = *ring_mem + BWFM_RING_MEM_SZ;
+       return 0;
+}
+
+int
+bwfm_pci_setup_flowring(struct bwfm_pci_softc *sc, struct bwfm_pci_msgring 
*ring,
+    int nitem, size_t itemsz)
+{
+       ring->w_ptr = 0;
+       ring->r_ptr = 0;
+       ring->nitem = nitem;
+       ring->itemsz = itemsz;
+       bwfm_pci_ring_write_rptr(sc, ring);
+       bwfm_pci_ring_write_wptr(sc, ring);
+
+       ring->ring = bwfm_pci_dmamem_alloc(sc, nitem * itemsz, 8);
+       if (ring->ring == NULL)
+               return ENOMEM;
+       return 0;
+}
+
+/* Ring helpers */
+void
+bwfm_pci_ring_bell(struct bwfm_pci_softc *sc,
+    struct bwfm_pci_msgring *ring)
+{
+       bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+           BWFM_PCI_PCIE2REG_H2D_MAILBOX, 1);
+}
+
+void
+bwfm_pci_ring_update_rptr(struct bwfm_pci_softc *sc,
+    struct bwfm_pci_msgring *ring)
+{
+       if (sc->sc_dma_idx_sz == 0) {
+               ring->r_ptr = bus_space_read_2(sc->sc_tcm_iot,
+                   sc->sc_tcm_ioh, ring->r_idx_addr);
+       } else {
+               ring->r_ptr = *(uint16_t *)(BWFM_PCI_DMA_KVA(sc->sc_dma_idx_buf)
+                   + ring->r_idx_addr);
+       }
+}
+
+static u_int
+if_rxr_get(struct if_rxring *rxr, unsigned int max)
+{
+       u_int taken = MIN(max, (rxr->rxr_total - rxr->rxr_inuse));
+
+       KASSERTMSG(rxr->rxr_inuse + taken <= rxr->rxr_total,
+                       "rxr->rxr_inuse: %d\n"
+                       "taken: %d\n"
+                       "rxr->rxr_total: %d\n",
+                       rxr->rxr_inuse, taken, rxr->rxr_total);
+       rxr->rxr_inuse += taken;
+
+       return taken;
+}
+
+static void
+if_rxr_put(struct if_rxring *rxr, unsigned int n)
+{
+       KASSERTMSG(rxr->rxr_inuse >= n,
+                       "rxr->rxr_inuse: %d\n"
+                       "n: %d\n"
+                       "rxr->rxr_total: %d\n",
+                       rxr->rxr_inuse, n, rxr->rxr_total);
+
+       rxr->rxr_inuse -= n;
+}
+
+static void
+if_rxr_init(struct if_rxring *rxr, unsigned int lwm __unused, unsigned int hwm)
+{
+       (void) lwm;
+
+       rxr->rxr_total = hwm;
+       rxr->rxr_inuse = 0;
+}
+
+void
+bwfm_pci_ring_update_wptr(struct bwfm_pci_softc *sc,
+    struct bwfm_pci_msgring *ring)
+{
+       if (sc->sc_dma_idx_sz == 0) {
+               ring->w_ptr = bus_space_read_2(sc->sc_tcm_iot,
+                   sc->sc_tcm_ioh, ring->w_idx_addr);
+       } else {
+               ring->w_ptr = *(uint16_t *)(BWFM_PCI_DMA_KVA(sc->sc_dma_idx_buf)
+                   + ring->w_idx_addr);
+       }
+}
+
+void
+bwfm_pci_ring_write_rptr(struct bwfm_pci_softc *sc,
+    struct bwfm_pci_msgring *ring)
+{
+       if (sc->sc_dma_idx_sz == 0) {
+               bus_space_write_2(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+                   ring->r_idx_addr, ring->r_ptr);
+       } else {
+               *(uint16_t *)(BWFM_PCI_DMA_KVA(sc->sc_dma_idx_buf)
+                   + ring->r_idx_addr) = ring->r_ptr;
+       }
+}
+
+void
+bwfm_pci_ring_write_wptr(struct bwfm_pci_softc *sc,
+    struct bwfm_pci_msgring *ring)
+{
+       if (sc->sc_dma_idx_sz == 0) {
+               bus_space_write_2(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+                   ring->w_idx_addr, ring->w_ptr);
+       } else {
+               *(uint16_t *)(BWFM_PCI_DMA_KVA(sc->sc_dma_idx_buf)
+                   + ring->w_idx_addr) = ring->w_ptr;
+       }
+}
+
+/*
+ * Retrieve a free descriptor to put new stuff in, but don't commit
+ * to it yet so we can rollback later if any error occurs.
+ */
+void *
+bwfm_pci_ring_write_reserve(struct bwfm_pci_softc *sc,
+    struct bwfm_pci_msgring *ring)
+{
+       int available;
+       char *ret;
+
+       bwfm_pci_ring_update_rptr(sc, ring);
+
+       if (ring->r_ptr > ring->w_ptr)
+               available = ring->r_ptr - ring->w_ptr;
+       else
+               available = ring->r_ptr + (ring->nitem - ring->w_ptr);
+
+       if (available < 1)
+               return NULL;
+
+       ret = BWFM_PCI_DMA_KVA(ring->ring) + (ring->w_ptr * ring->itemsz);
+       ring->w_ptr += 1;
+       if (ring->w_ptr == ring->nitem)
+               ring->w_ptr = 0;
+       return ret;
+}
+
+void *
+bwfm_pci_ring_write_reserve_multi(struct bwfm_pci_softc *sc,
+    struct bwfm_pci_msgring *ring, int count, int *avail)
+{
+       int available;
+       char *ret;
+
+       bwfm_pci_ring_update_rptr(sc, ring);
+
+       if (ring->r_ptr > ring->w_ptr)
+               available = ring->r_ptr - ring->w_ptr;
+       else
+               available = ring->r_ptr + (ring->nitem - ring->w_ptr);
+
+       if (available < 1)
+               return NULL;
+
+       ret = BWFM_PCI_DMA_KVA(ring->ring) + (ring->w_ptr * ring->itemsz);
+       *avail = min(count, available - 1);
+       if (*avail + ring->w_ptr > ring->nitem)
+               *avail = ring->nitem - ring->w_ptr;
+       ring->w_ptr += *avail;
+       if (ring->w_ptr == ring->nitem)
+               ring->w_ptr = 0;
+       return ret;
+}
+
+/*
+ * Read number of descriptors available (submitted by the firmware)
+ * and retrieve pointer to first descriptor.
+ */
+void *
+bwfm_pci_ring_read_avail(struct bwfm_pci_softc *sc,
+    struct bwfm_pci_msgring *ring, int *avail)
+{
+       bwfm_pci_ring_update_wptr(sc, ring);
+
+       if (ring->w_ptr >= ring->r_ptr)
+               *avail = ring->w_ptr - ring->r_ptr;
+       else
+               *avail = ring->nitem - ring->r_ptr;
+
+       if (*avail == 0)
+               return NULL;
+
+       return BWFM_PCI_DMA_KVA(ring->ring) + (ring->r_ptr * ring->itemsz);
+}
+
+/*
+ * Let firmware know we read N descriptors.
+ */
+void
+bwfm_pci_ring_read_commit(struct bwfm_pci_softc *sc,
+    struct bwfm_pci_msgring *ring, int nitem)
+{
+       ring->r_ptr += nitem;
+       if (ring->r_ptr == ring->nitem)
+               ring->r_ptr = 0;
+       bwfm_pci_ring_write_rptr(sc, ring);
+}
+
+/*
+ * Let firmware know that we submitted some descriptors.
+ */
+void
+bwfm_pci_ring_write_commit(struct bwfm_pci_softc *sc,
+    struct bwfm_pci_msgring *ring)
+{
+       bwfm_pci_ring_write_wptr(sc, ring);
+       bwfm_pci_ring_bell(sc, ring);
+}
+
+/*
+ * Rollback N descriptors in case we don't actually want
+ * to commit to it.
+ */
+void
+bwfm_pci_ring_write_cancel(struct bwfm_pci_softc *sc,
+    struct bwfm_pci_msgring *ring, int nitem)
+{
+       if (ring->w_ptr == 0)
+               ring->w_ptr = ring->nitem - nitem;
+       else
+               ring->w_ptr -= nitem;
+}
+
+/*
+ * Foreach written descriptor on the ring, pass the descriptor to
+ * a message handler and let the firmware know we handled it.
+ */
+void
+bwfm_pci_ring_rx(struct bwfm_pci_softc *sc, struct bwfm_pci_msgring *ring)
+{
+       char *buf;
+       int avail, processed;
+
+again:
+       buf = bwfm_pci_ring_read_avail(sc, ring, &avail);
+       if (buf == NULL)
+               return;
+
+       processed = 0;
+       while (avail) {
+               bwfm_pci_msg_rx(sc, buf + sc->sc_rx_dataoffset);
+               buf += ring->itemsz;
+               processed++;
+               if (processed == 48) {
+                       bwfm_pci_ring_read_commit(sc, ring, processed);
+                       processed = 0;
+               }
+               avail--;
+       }
+       if (processed)
+               bwfm_pci_ring_read_commit(sc, ring, processed);
+       if (ring->r_ptr == 0)
+               goto again;
+}
+
+void
+bwfm_pci_msg_rx(struct bwfm_pci_softc *sc, void *buf)
+{
+       struct ifnet *ifp = sc->sc_sc.sc_ic.ic_ifp;
+       struct msgbuf_ioctl_resp_hdr *resp;
+       struct msgbuf_tx_status *tx;
+       struct msgbuf_rx_complete *rx;
+       struct msgbuf_rx_event *event;
+       struct msgbuf_common_hdr *msg;
+       struct msgbuf_flowring_create_resp *fcr;
+       struct msgbuf_flowring_delete_resp *fdr;
+       struct bwfm_pci_msgring *ring;
+       struct mbuf *m;
+       int flowid;
+
+       msg = (struct msgbuf_common_hdr *)buf;
+       switch (msg->msgtype)
+       {
+       case MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT:
+               fcr = (struct msgbuf_flowring_create_resp *)buf;
+               flowid = letoh16(fcr->compl_hdr.flow_ring_id);
+               if (flowid < 2)
+                       break;
+               flowid -= 2;
+               if (flowid >= sc->sc_max_flowrings)
+                       break;
+               ring = &sc->sc_flowrings[flowid];
+               if (ring->status != RING_OPENING)
+                       break;
+               if (fcr->compl_hdr.status) {
+                       printf("%s: failed to open flowring %d\n",
+                           DEVNAME(sc), flowid);
+                       ring->status = RING_CLOSED;
+                       if (ring->m) {
+                               m_freem(ring->m);
+                               ring->m = NULL;
+                       }
+                       ifp->if_flags &= ~IFF_OACTIVE;
+                       ifp->if_start(ifp);
+                       break;
+               }
+               ring->status = RING_OPEN;
+               if (ring->m != NULL) {
+                       m = ring->m;
+                       ring->m = NULL;
+                       if (bwfm_pci_txdata(&sc->sc_sc, m))
+                               m_freem(ring->m);
+               }
+               ifp->if_flags &= ~IFF_OACTIVE;
+               ifp->if_start(ifp);
+               break;
+       case MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT:
+               fdr = (struct msgbuf_flowring_delete_resp *)buf;
+               flowid = letoh16(fdr->compl_hdr.flow_ring_id);
+               if (flowid < 2)
+                       break;
+               flowid -= 2;
+               if (flowid >= sc->sc_max_flowrings)
+                       break;
+               ring = &sc->sc_flowrings[flowid];
+               if (ring->status != RING_CLOSING)
+                       break;
+               if (fdr->compl_hdr.status) {
+                       printf("%s: failed to delete flowring %d\n",
+                           DEVNAME(sc), flowid);
+                       break;
+               }
+               bwfm_pci_dmamem_free(sc, ring->ring);
+               ring->status = RING_CLOSED;
+               break;
+       case MSGBUF_TYPE_IOCTLPTR_REQ_ACK:
+               break;
+       case MSGBUF_TYPE_IOCTL_CMPLT:
+               resp = (struct msgbuf_ioctl_resp_hdr *)buf;
+               sc->sc_ioctl_resp_pktid = letoh32(resp->msg.request_id);
+               sc->sc_ioctl_resp_ret_len = letoh16(resp->resp_len);
+               sc->sc_ioctl_resp_status = letoh16(resp->compl_hdr.status);
+               if_rxr_put(&sc->sc_ioctl_ring, 1);
+               bwfm_pci_fill_rx_rings(sc);
+               wakeup(&sc->sc_ioctl_buf);
+               break;
+       case MSGBUF_TYPE_WL_EVENT:
+               event = (struct msgbuf_rx_event *)buf;
+               m = bwfm_pci_pktid_free(sc, &sc->sc_rx_pkts,
+                   letoh32(event->msg.request_id));
+               if (m == NULL)
+                       break;
+               m_adj(m, sc->sc_rx_dataoffset);
+               m->m_len = m->m_pkthdr.len = letoh16(event->event_data_len);
+               bwfm_rx(&sc->sc_sc, m);
+               if_rxr_put(&sc->sc_event_ring, 1);
+               bwfm_pci_fill_rx_rings(sc);
+               break;
+       case MSGBUF_TYPE_TX_STATUS:
+               tx = (struct msgbuf_tx_status *)buf;
+               m = bwfm_pci_pktid_free(sc, &sc->sc_tx_pkts,
+                   letoh32(tx->msg.request_id));
+               if (m == NULL)
+                       break;
+               m_freem(m);
+               if (sc->sc_tx_pkts_full) {
+                       sc->sc_tx_pkts_full = 0;
+                       ifp->if_flags &= ~IFF_OACTIVE;
+                       ifp->if_start(ifp);
+               }
+               break;
+       case MSGBUF_TYPE_RX_CMPLT:
+               rx = (struct msgbuf_rx_complete *)buf;
+               m = bwfm_pci_pktid_free(sc, &sc->sc_rx_pkts,
+                   letoh32(rx->msg.request_id));
+               if (m == NULL)
+                       break;
+               if (letoh16(rx->data_offset))
+                       m_adj(m, letoh16(rx->data_offset));
+               else if (sc->sc_rx_dataoffset)
+                       m_adj(m, sc->sc_rx_dataoffset);
+               m->m_len = m->m_pkthdr.len = letoh16(rx->data_len);
+               bwfm_rx(&sc->sc_sc, m);
+               if_rxr_put(&sc->sc_rxbuf_ring, 1);
+               bwfm_pci_fill_rx_rings(sc);
+               break;
+       default:
+               printf("%s: msgtype 0x%08x\n", __func__, msg->msgtype);
+               break;
+       }
+}
+
+/* Bus core helpers */
+void
+bwfm_pci_select_core(struct bwfm_pci_softc *sc, int id)
+{
+       struct bwfm_softc *bwfm = (void *)sc;
+       struct bwfm_core *core;
+
+       core = bwfm_chip_get_core(bwfm, id);
+       if (core == NULL) {
+               printf("%s: could not find core to select", DEVNAME(sc));
+               return;
+       }
+
+       pci_conf_write(sc->sc_pc, sc->sc_tag,
+           BWFM_PCI_BAR0_WINDOW, core->co_base);
+       if (pci_conf_read(sc->sc_pc, sc->sc_tag,
+           BWFM_PCI_BAR0_WINDOW) != core->co_base)
+               pci_conf_write(sc->sc_pc, sc->sc_tag,
+                   BWFM_PCI_BAR0_WINDOW, core->co_base);
+}
+
+uint32_t
+bwfm_pci_buscore_read(struct bwfm_softc *bwfm, uint32_t reg)
+{
+       struct bwfm_pci_softc *sc = (void *)bwfm;
+       uint32_t page, offset;
+
+       page = reg & ~(BWFM_PCI_BAR0_REG_SIZE - 1);
+       offset = reg & (BWFM_PCI_BAR0_REG_SIZE - 1);
+       pci_conf_write(sc->sc_pc, sc->sc_tag, BWFM_PCI_BAR0_WINDOW, page);
+       return bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh, offset);
+}
+
+void
+bwfm_pci_buscore_write(struct bwfm_softc *bwfm, uint32_t reg, uint32_t val)
+{
+       struct bwfm_pci_softc *sc = (void *)bwfm;
+       uint32_t page, offset;
+
+       page = reg & ~(BWFM_PCI_BAR0_REG_SIZE - 1);
+       offset = reg & (BWFM_PCI_BAR0_REG_SIZE - 1);
+       pci_conf_write(sc->sc_pc, sc->sc_tag, BWFM_PCI_BAR0_WINDOW, page);
+       bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, offset, val);
+}
+
+int
+bwfm_pci_buscore_prepare(struct bwfm_softc *bwfm)
+{
+       return 0;
+}
+
+int
+bwfm_pci_buscore_reset(struct bwfm_softc *bwfm)
+{
+       struct bwfm_pci_softc *sc = (void *)bwfm;
+       struct bwfm_core *core;
+       uint32_t reg;
+       int i;
+
+       bwfm_pci_select_core(sc, BWFM_AGENT_CORE_PCIE2);
+       reg = pci_conf_read(sc->sc_pc, sc->sc_tag,
+           BWFM_PCI_CFGREG_LINK_STATUS_CTRL);
+       pci_conf_write(sc->sc_pc, sc->sc_tag, BWFM_PCI_CFGREG_LINK_STATUS_CTRL,
+           reg & ~BWFM_PCI_CFGREG_LINK_STATUS_CTRL_ASPM_ENAB);
+
+       bwfm_pci_select_core(sc, BWFM_AGENT_CORE_CHIPCOMMON);
+       bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+           BWFM_CHIP_REG_WATCHDOG, 4);
+       delay(100 * 1000);
+
+       bwfm_pci_select_core(sc, BWFM_AGENT_CORE_PCIE2);
+       pci_conf_write(sc->sc_pc, sc->sc_tag,
+           BWFM_PCI_CFGREG_LINK_STATUS_CTRL, reg);
+
+       core = bwfm_chip_get_core(bwfm, BWFM_AGENT_CORE_PCIE2);
+       if (core->co_rev <= 13) {
+               uint16_t cfg_offset[] = {
+                   BWFM_PCI_CFGREG_STATUS_CMD,
+                   BWFM_PCI_CFGREG_PM_CSR,
+                   BWFM_PCI_CFGREG_MSI_CAP,
+                   BWFM_PCI_CFGREG_MSI_ADDR_L,
+                   BWFM_PCI_CFGREG_MSI_ADDR_H,
+                   BWFM_PCI_CFGREG_MSI_DATA,
+                   BWFM_PCI_CFGREG_LINK_STATUS_CTRL2,
+                   BWFM_PCI_CFGREG_RBAR_CTRL,
+                   BWFM_PCI_CFGREG_PML1_SUB_CTRL1,
+                   BWFM_PCI_CFGREG_REG_BAR2_CONFIG,
+                   BWFM_PCI_CFGREG_REG_BAR3_CONFIG,
+               };
+
+               for (i = 0; i < nitems(cfg_offset); i++) {
+                       bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+                           BWFM_PCI_PCIE2REG_CONFIGADDR, cfg_offset[i]);
+                       reg = bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+                           BWFM_PCI_PCIE2REG_CONFIGDATA);
+                       DPRINTFN(3, ("%s: config offset 0x%04x, value 0x%04x\n",
+                           DEVNAME(sc), cfg_offset[i], reg));
+                       bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+                           BWFM_PCI_PCIE2REG_CONFIGDATA, reg);
+               }
+       }
+
+       reg = bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+           BWFM_PCI_PCIE2REG_MAILBOXINT);
+       if (reg != 0xffffffff)
+               bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+                   BWFM_PCI_PCIE2REG_MAILBOXINT, reg);
+
+       return 0;
+}
+
+void
+bwfm_pci_buscore_activate(struct bwfm_softc *bwfm, const uint32_t rstvec)
+{
+       struct bwfm_pci_softc *sc = (void *)bwfm;
+       bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, 0, rstvec);
+}
+
+static int bwfm_pci_prio2fifo[8] = {
+       1, /* best effort */
+       0, /* IPTOS_PREC_IMMEDIATE */
+       0, /* IPTOS_PREC_PRIORITY */
+       1, /* IPTOS_PREC_FLASH */
+       2, /* IPTOS_PREC_FLASHOVERRIDE */
+       2, /* IPTOS_PREC_CRITIC_ECP */
+       3, /* IPTOS_PREC_INTERNETCONTROL */
+       3, /* IPTOS_PREC_NETCONTROL */
+};
+
+int
+bwfm_pci_flowring_lookup(struct bwfm_pci_softc *sc, struct mbuf *m)
+{
+       struct ieee80211com *ic = &sc->sc_sc.sc_ic;
+       uint8_t *da = mtod(m, uint8_t *);
+       struct ether_header *eh;
+       int flowid, prio, fifo;
+       int i, found, ac;
+
+       /* No QoS for EAPOL frames. */
+       eh = mtod(m, struct ether_header *);
+       ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ?
+           M_WME_GETAC(m) : WME_AC_BE;
+
+       prio = ac;
+       fifo = bwfm_pci_prio2fifo[prio];
+
+       switch (ic->ic_opmode)
+       {
+       case IEEE80211_M_STA:
+               flowid = fifo;
+               break;
+#ifndef IEEE80211_STA_ONLY
+       case IEEE80211_M_HOSTAP:
+               if (ETHER_IS_MULTICAST(da))
+                       da = __UNCONST(etherbroadcastaddr);
+               flowid = da[5] * 2 + fifo;
+               break;
+#endif
+       default:
+               printf("%s: state not supported\n", DEVNAME(sc));
+               return ENOBUFS;
+       }
+
+       found = 0;
+       flowid = flowid % sc->sc_max_flowrings;
+       for (i = 0; i < sc->sc_max_flowrings; i++) {
+               if (ic->ic_opmode == IEEE80211_M_STA &&
+                   sc->sc_flowrings[flowid].status >= RING_OPEN &&
+                   sc->sc_flowrings[flowid].fifo == fifo) {
+                       found = 1;
+                       break;
+               }
+#ifndef IEEE80211_STA_ONLY
+               if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+                   sc->sc_flowrings[flowid].status >= RING_OPEN &&
+                   sc->sc_flowrings[flowid].fifo == fifo &&
+                   !memcmp(sc->sc_flowrings[flowid].mac, da, ETHER_ADDR_LEN)) {
+                       found = 1;
+                       break;
+               }
+#endif
+               flowid = (flowid + 1) % sc->sc_max_flowrings;
+       }
+
+       if (found)
+               return flowid;
+
+       return -1;
+}
+
+void
+bwfm_pci_flowring_create(struct bwfm_pci_softc *sc, struct mbuf *m)
+{
+       struct ieee80211com *ic = &sc->sc_sc.sc_ic;
+       struct bwfm_cmd_flowring_create * cmd;
+       uint8_t *da = mtod(m, uint8_t *);
+       struct ether_header *eh;
+       struct bwfm_pci_msgring *ring;
+       int flowid, prio, fifo;
+       int i, found, ac;
+
+       cmd = pool_get(&sc->sc_flowring_pool, PR_NOWAIT);
+       if (__predict_false(cmd == NULL))
+               return;
+
+       /* No QoS for EAPOL frames. */
+       eh = mtod(m, struct ether_header *);
+       ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ?
+           M_WME_GETAC(m) : WME_AC_BE;
+
+       prio = ac;
+       fifo = bwfm_pci_prio2fifo[prio];
+
+       switch (ic->ic_opmode)
+       {
+       case IEEE80211_M_STA:
+               flowid = fifo;
+               break;
+#ifndef IEEE80211_STA_ONLY
+       case IEEE80211_M_HOSTAP:
+               if (ETHER_IS_MULTICAST(da))
+                       da = __UNCONST(etherbroadcastaddr);
+               flowid = da[5] * 2 + fifo;
+               break;
+#endif
+       default:
+               printf("%s: state not supported\n", DEVNAME(sc));
+               return;
+       }
+
+       found = 0;
+       flowid = flowid % sc->sc_max_flowrings;
+       for (i = 0; i < sc->sc_max_flowrings; i++) {
+               ring = &sc->sc_flowrings[flowid];
+               if (ring->status == RING_CLOSED) {
+                       ring->status = RING_OPENING;
+                       found = 1;
+                       break;
+               }
+               flowid = (flowid + 1) % sc->sc_max_flowrings;
+       }
+
+       /*
+        * We cannot recover from that so far.  Only a stop/init
+        * cycle can revive this if it ever happens at all.
+        */
+       if (!found) {
+               printf("%s: no flowring available\n", DEVNAME(sc));
+               return;
+       }
+
+       cmd->sc = sc;
+       cmd->m = m;
+       cmd->prio = prio;
+       cmd->flowid = flowid;
+       workqueue_enqueue(sc->flowring_wq, &cmd->wq_cookie, NULL);
+}
+
+void
+bwfm_pci_flowring_create_cb(struct work *wk, void *arg) //(struct bwfm_softc 
*bwfm, void *arg)
+{
+       struct bwfm_cmd_flowring_create *cmd = container_of(wk, struct 
bwfm_cmd_flowring_create, wq_cookie);
+       struct bwfm_pci_softc *sc = cmd->sc; // (void *)bwfm;
+       struct ieee80211com *ic = &sc->sc_sc.sc_ic;
+       struct msgbuf_tx_flowring_create_req *req;
+       struct bwfm_pci_msgring *ring;
+       uint8_t *da, *sa;
+
+       da = mtod(cmd->m, char *) + 0 * ETHER_ADDR_LEN;
+       sa = mtod(cmd->m, char *) + 1 * ETHER_ADDR_LEN;
+
+       ring = &sc->sc_flowrings[cmd->flowid];
+       if (ring->status != RING_OPENING) {
+               printf("%s: flowring not opening\n", DEVNAME(sc));
+               return;
+       }
+
+       if (bwfm_pci_setup_flowring(sc, ring, 512, 48)) {
+               printf("%s: cannot setup flowring\n", DEVNAME(sc));
+               return;
+       }
+
+       req = bwfm_pci_ring_write_reserve(sc, &sc->sc_ctrl_submit);
+       if (req == NULL) {
+               printf("%s: cannot reserve for flowring\n", DEVNAME(sc));
+               return;
+       }
+
+       ring->status = RING_OPENING;
+       ring->fifo = bwfm_pci_prio2fifo[cmd->prio];
+       ring->m = cmd->m;
+       memcpy(ring->mac, da, ETHER_ADDR_LEN);
+#ifndef IEEE80211_STA_ONLY
+       if (ic->ic_opmode == IEEE80211_M_HOSTAP && ETHER_IS_MULTICAST(da))
+               memcpy(ring->mac, etherbroadcastaddr, ETHER_ADDR_LEN);
+#endif
+
+       req->msg.msgtype = MSGBUF_TYPE_FLOW_RING_CREATE;
+       req->msg.ifidx = 0;
+       req->msg.request_id = 0;
+       req->tid = bwfm_pci_prio2fifo[cmd->prio];
+       req->flow_ring_id = letoh16(cmd->flowid + 2);
+       memcpy(req->da, da, ETHER_ADDR_LEN);
+       memcpy(req->sa, sa, ETHER_ADDR_LEN);
+       req->flow_ring_addr.high_addr =
+           letoh32(BWFM_PCI_DMA_DVA(ring->ring) >> 32);
+       req->flow_ring_addr.low_addr =
+           letoh32(BWFM_PCI_DMA_DVA(ring->ring) & 0xffffffff);
+       req->max_items = letoh16(512);
+       req->len_item = letoh16(48);
+
+       bwfm_pci_ring_write_commit(sc, &sc->sc_ctrl_submit);
+       pool_put(&sc->sc_flowring_pool, cmd);
+}
+
+void
+bwfm_pci_flowring_delete(struct bwfm_pci_softc *sc, int flowid)
+{
+       struct msgbuf_tx_flowring_delete_req *req;
+       struct bwfm_pci_msgring *ring;
+
+       ring = &sc->sc_flowrings[flowid];
+       if (ring->status != RING_OPEN) {
+               printf("%s: flowring not open\n", DEVNAME(sc));
+               return;
+       }
+
+       req = bwfm_pci_ring_write_reserve(sc, &sc->sc_ctrl_submit);
+       if (req == NULL) {
+               printf("%s: cannot reserve for flowring\n", DEVNAME(sc));
+               return;
+       }
+
+       ring->status = RING_CLOSING;
+
+       req->msg.msgtype = MSGBUF_TYPE_FLOW_RING_DELETE;
+       req->msg.ifidx = 0;
+       req->msg.request_id = 0;
+       req->flow_ring_id = letoh16(flowid + 2);
+       req->reason = 0;
+
+       bwfm_pci_ring_write_commit(sc, &sc->sc_ctrl_submit);
+}
+
+void
+bwfm_pci_stop(struct bwfm_softc *bwfm)
+{
+       struct bwfm_pci_softc *sc = (void *)bwfm;
+       struct bwfm_pci_msgring *ring;
+       int i;
+
+       for (i = 0; i < sc->sc_max_flowrings; i++) {
+               ring = &sc->sc_flowrings[i];
+               if (ring->status == RING_OPEN)
+                       bwfm_pci_flowring_delete(sc, i);
+       }
+}
+
+int
+bwfm_pci_txcheck(struct bwfm_softc *bwfm)
+{
+       struct bwfm_pci_softc *sc = (void *)bwfm;
+       struct bwfm_pci_msgring *ring;
+       int i;
+
+       /* If we are transitioning, we cannot send. */
+       for (i = 0; i < sc->sc_max_flowrings; i++) {
+               ring = &sc->sc_flowrings[i];
+               if (ring->status == RING_OPENING)
+                       return ENOBUFS;
+       }
+
+       if (bwfm_pci_pktid_avail(sc, &sc->sc_tx_pkts)) {
+               sc->sc_tx_pkts_full = 1;
+               return ENOBUFS;
+       }
+
+       return 0;
+}
+
+int
+bwfm_pci_txdata(struct bwfm_softc *bwfm, struct mbuf *m)
+{
+       struct bwfm_pci_softc *sc = (void *)bwfm;
+       struct bwfm_pci_msgring *ring;
+       struct msgbuf_tx_msghdr *tx;
+       uint32_t pktid;
+       paddr_t paddr;
+       struct ether_header *eh;
+       int flowid, ret, ac;
+
+       flowid = bwfm_pci_flowring_lookup(sc, m);
+       if (flowid < 0) {
+               /*
+                * We cannot send the packet right now as there is
+                * no flowring yet.  The flowring will be created
+                * asynchronously.  While the ring is transitioning
+                * the TX check will tell the upper layers that we
+                * cannot send packets right now.  When the flowring
+                * is created the queue will be restarted and this
+                * mbuf will be transmitted.
+                */
+               bwfm_pci_flowring_create(sc, m);
+               return 0;
+       }
+
+       ring = &sc->sc_flowrings[flowid];
+       if (ring->status == RING_OPENING ||
+           ring->status == RING_CLOSING) {
+               printf("%s: tried to use a flow that was "
+                   "transitioning in status %d\n",
+                   DEVNAME(sc), ring->status);
+               return ENOBUFS;
+       }
+
+       tx = bwfm_pci_ring_write_reserve(sc, ring);
+       if (tx == NULL)
+               return ENOBUFS;
+
+       /* No QoS for EAPOL frames. */
+       eh = mtod(m, struct ether_header *);
+       ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ?
+           M_WME_GETAC(m) : WME_AC_BE;
+
+       memset(tx, 0, sizeof(*tx));
+       tx->msg.msgtype = MSGBUF_TYPE_TX_POST;
+       tx->msg.ifidx = 0;
+       tx->flags = BWFM_MSGBUF_PKT_FLAGS_FRAME_802_3;
+       tx->flags |= ac << BWFM_MSGBUF_PKT_FLAGS_PRIO_SHIFT;
+       tx->seg_cnt = 1;
+       memcpy(tx->txhdr, mtod(m, char *), ETHER_HDR_LEN);
+
+       ret = bwfm_pci_pktid_new(sc, &sc->sc_tx_pkts, m, &pktid, &paddr);
+       if (ret) {
+               if (ret == ENOBUFS) {
+                       printf("%s: no pktid available for TX\n",
+                           DEVNAME(sc));
+                       sc->sc_tx_pkts_full = 1;
+               }
+               bwfm_pci_ring_write_cancel(sc, ring, 1);
+               return ret;
+       }
+       paddr += ETHER_HDR_LEN;
+
+       tx->msg.request_id = htole32(pktid);
+       tx->data_len = htole16(m->m_len - ETHER_HDR_LEN);
+       tx->data_buf_addr.high_addr = htole32(paddr >> 32);
+       tx->data_buf_addr.low_addr = htole32(paddr & 0xffffffff);
+
+       bwfm_pci_ring_write_commit(sc, ring);
+       return 0;
+}
+
+#ifdef BWFM_DEBUG
+void
+bwfm_pci_debug_console(struct bwfm_pci_softc *sc)
+{
+       uint32_t newidx = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+           sc->sc_console_base_addr + BWFM_CONSOLE_WRITEIDX);
+
+       if (newidx != sc->sc_console_readidx)
+               DPRINTFN(3, ("BWFM CONSOLE: "));
+       while (newidx != sc->sc_console_readidx) {
+               uint8_t ch = bus_space_read_1(sc->sc_tcm_iot, sc->sc_tcm_ioh,
+                   sc->sc_console_buf_addr + sc->sc_console_readidx);
+               sc->sc_console_readidx++;
+               if (sc->sc_console_readidx == sc->sc_console_buf_size)
+                       sc->sc_console_readidx = 0;
+               if (ch == '\r')
+                       continue;
+               DPRINTFN(3, ("%c", ch));
+       }
+}
+#endif
+
+int
+bwfm_pci_intr(void *v)
+{
+       struct bwfm_pci_softc *sc = (void *)v;
+       uint32_t status;
+
+       if ((status = bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+           BWFM_PCI_PCIE2REG_MAILBOXINT)) == 0)
+               return 0;
+
+       bwfm_pci_intr_disable(sc);
+       bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+           BWFM_PCI_PCIE2REG_MAILBOXINT, status);
+
+       if (status & (BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_0 |
+           BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_1))
+               printf("%s: handle MB data\n", __func__);
+
+       if (status & BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H_DB) {
+               bwfm_pci_ring_rx(sc, &sc->sc_rx_complete);
+               bwfm_pci_ring_rx(sc, &sc->sc_tx_complete);
+               bwfm_pci_ring_rx(sc, &sc->sc_ctrl_complete);
+       }
+
+#ifdef BWFM_DEBUG
+       bwfm_pci_debug_console(sc);
+#endif
+
+       bwfm_pci_intr_enable(sc);
+       return 1;
+}
+
+void
+bwfm_pci_intr_enable(struct bwfm_pci_softc *sc)
+{
+       bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+           BWFM_PCI_PCIE2REG_MAILBOXMASK,
+           BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_0 |
+           BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_1 |
+           BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H_DB);
+}
+
+void
+bwfm_pci_intr_disable(struct bwfm_pci_softc *sc)
+{
+       bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh,
+           BWFM_PCI_PCIE2REG_MAILBOXMASK, 0);
+}
+
+/* Msgbuf protocol implementation */
+int
+bwfm_pci_msgbuf_query_dcmd(struct bwfm_softc *bwfm, int ifidx,
+    int cmd, char *buf, size_t *len)
+{
+       struct bwfm_pci_softc *sc = (void *)bwfm;
+       struct msgbuf_ioctl_req_hdr *req;
+       struct mbuf *m;
+       size_t buflen;
+       int s;
+
+       s = splnet();
+       sc->sc_ioctl_resp_pktid = -1;
+       req = bwfm_pci_ring_write_reserve(sc, &sc->sc_ctrl_submit);
+       if (req == NULL) {
+               printf("%s: cannot reserve for write\n", DEVNAME(sc));
+               splx(s);
+               return 1;
+       }
+       req->msg.msgtype = MSGBUF_TYPE_IOCTLPTR_REQ;
+       req->msg.ifidx = 0;
+       req->msg.flags = 0;
+       req->msg.request_id = htole32(MSGBUF_IOCTL_REQ_PKTID);
+       req->cmd = htole32(cmd);
+       req->output_buf_len = htole16(*len);
+       req->trans_id = htole16(sc->sc_ioctl_reqid++);
+
+       buflen = min(*len, BWFM_DMA_H2D_IOCTL_BUF_LEN);
+       req->input_buf_len = htole16(buflen);
+       req->req_buf_addr.high_addr =
+           htole32((uint64_t)BWFM_PCI_DMA_DVA(sc->sc_ioctl_buf) >> 32);
+       req->req_buf_addr.low_addr =
+           htole32((uint64_t)BWFM_PCI_DMA_DVA(sc->sc_ioctl_buf) & 0xffffffff);
+       if (buf)
+               memcpy(BWFM_PCI_DMA_KVA(sc->sc_ioctl_buf), buf, buflen);
+       else
+               memset(BWFM_PCI_DMA_KVA(sc->sc_ioctl_buf), 0, buflen);
+
+       bwfm_pci_ring_write_commit(sc, &sc->sc_ctrl_submit);
+       splx(s);
+
+       if (tsleep(&sc->sc_ioctl_buf, PCATCH, "bwfm", hz)) {
+               printf("%s: timeout waiting for ioctl response\n",
+                   DEVNAME(sc));
+               return 1;
+       }
+
+       m = bwfm_pci_pktid_free(sc, &sc->sc_rx_pkts, sc->sc_ioctl_resp_pktid);
+       if (m == NULL)
+               return 1;
+
+       *len = min(buflen, sc->sc_ioctl_resp_ret_len);
+       if (buf)
+               memcpy(buf, mtod(m, char *), *len);
+       m_freem(m);
+       splx(s);
+
+       return 0;
+}
+
+int
+bwfm_pci_msgbuf_set_dcmd(struct bwfm_softc *bwfm, int ifidx,
+    int cmd, char *buf, size_t len)
+{
+       return bwfm_pci_msgbuf_query_dcmd(bwfm, ifidx, cmd, buf, &len);
+}
diff --git a/sys/dev/pci/if_bwfm_pci.h b/sys/dev/pci/if_bwfm_pci.h
new file mode 100644
index 0000000000..4c5da6fb2e
--- /dev/null
+++ b/sys/dev/pci/if_bwfm_pci.h
@@ -0,0 +1,279 @@
+/*     $OpenBSD: if_bwfm_pci.h,v 1.2 2018/01/05 23:30:16 patrick Exp $ */
+/*
+ * Copyright (c) 2010-2016 Broadcom Corporation
+ * Copyright (c) 2017 Patrick Wildt <[email protected]>
+ *
+ * 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.
+ */
+
+/* Registers */
+#define BWFM_PCI_BAR0_WINDOW                   0x80
+#define BWFM_PCI_BAR0_REG_SIZE                 0x1000
+
+#define BWFM_PCI_ARMCR4REG_BANKIDX             0x40
+#define BWFM_PCI_ARMCR4REG_BANKPDA             0x4C
+
+#define BWFM_PCI_PCIE2REG_INTMASK              0x24
+#define BWFM_PCI_PCIE2REG_MAILBOXINT           0x48
+#define BWFM_PCI_PCIE2REG_MAILBOXMASK          0x4C
+#define  BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_0       0x0100
+#define  BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_1       0x0200
+#define  BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H0_DB0    0x10000
+#define  BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H0_DB1    0x20000
+#define  BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H1_DB0    0x40000
+#define  BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H1_DB1    0x80000
+#define  BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H2_DB0    0x100000
+#define  BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H2_DB1    0x200000
+#define  BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H3_DB0    0x400000
+#define  BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H3_DB1    0x800000
+#define  BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H_DB              \
+               (BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H0_DB0 |   \
+                BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H0_DB1 |   \
+                BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H1_DB0 |   \
+                BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H1_DB1 |   \
+                BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H2_DB0 |   \
+                BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H2_DB1 |   \
+                BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H3_DB0 |   \
+                BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H3_DB1)
+
+#define BWFM_PCI_PCIE2REG_CONFIGADDR           0x120
+#define BWFM_PCI_PCIE2REG_CONFIGDATA           0x124
+#define BWFM_PCI_PCIE2REG_H2D_MAILBOX          0x140
+
+#define BWFM_PCI_CFGREG_STATUS_CMD             0x004
+#define BWFM_PCI_CFGREG_PM_CSR                 0x04C
+#define BWFM_PCI_CFGREG_MSI_CAP                        0x058
+#define BWFM_PCI_CFGREG_MSI_ADDR_L             0x05C
+#define BWFM_PCI_CFGREG_MSI_ADDR_H             0x060
+#define BWFM_PCI_CFGREG_MSI_DATA               0x064
+#define BWFM_PCI_CFGREG_LINK_STATUS_CTRL       0x0BC
+#define  BWFM_PCI_CFGREG_LINK_STATUS_CTRL_ASPM_ENAB    0x3
+#define BWFM_PCI_CFGREG_LINK_STATUS_CTRL2      0x0DC
+#define BWFM_PCI_CFGREG_RBAR_CTRL              0x228
+#define BWFM_PCI_CFGREG_PML1_SUB_CTRL1         0x248
+#define BWFM_PCI_CFGREG_REG_BAR2_CONFIG                0x4E0
+#define BWFM_PCI_CFGREG_REG_BAR3_CONFIG                0x4F4
+
+#define BWFM_RAMSIZE                           0x6c
+#define  BWFM_RAMSIZE_MAGIC                    0x534d4152      /* SMAR */
+
+#define BWFM_SHARED_INFO                       0x000
+#define  BWFM_SHARED_INFO_MIN_VERSION                  5
+#define  BWFM_SHARED_INFO_MAX_VERSION                  6
+#define  BWFM_SHARED_INFO_VERSION_MASK                 0x00FF
+#define  BWFM_SHARED_INFO_DMA_INDEX                    0x10000
+#define  BWFM_SHARED_INFO_DMA_2B_IDX                   0x100000
+#define BWFM_SHARED_CONSOLE_ADDR               0x14
+#define BWFM_SHARED_MAX_RXBUFPOST              0x22
+#define  BWFM_SHARED_MAX_RXBUFPOST_DEFAULT             255
+#define BWFM_SHARED_RX_DATAOFFSET              0x24
+#define BWFM_SHARED_HTOD_MB_DATA_ADDR          0x28
+#define BWFM_SHARED_DTOH_MB_DATA_ADDR          0x2c
+#define BWFM_SHARED_RING_INFO_ADDR             0x30
+#define BWFM_SHARED_DMA_SCRATCH_LEN            0x34
+#define BWFM_SHARED_DMA_SCRATCH_ADDR_LOW       0x38
+#define BWFM_SHARED_DMA_SCRATCH_ADDR_HIGH      0x3c
+#define BWFM_SHARED_DMA_RINGUPD_LEN            0x40
+#define BWFM_SHARED_DMA_RINGUPD_ADDR_LOW       0x44
+#define BWFM_SHARED_DMA_RINGUPD_ADDR_HIGH      0x48
+
+#define BWFM_RING_MAX_ITEM                     0x04
+#define BWFM_RING_LEN_ITEMS                    0x06
+#define BWFM_RING_MEM_BASE_ADDR_LOW            0x08
+#define BWFM_RING_MEM_BASE_ADDR_HIGH           0x0c
+#define BWFM_RING_MEM_SZ                       16
+
+#define BWFM_CONSOLE_BUFADDR                   0x08
+#define BWFM_CONSOLE_BUFSIZE                   0x0c
+#define BWFM_CONSOLE_WRITEIDX                  0x10
+
+struct bwfm_pci_ringinfo {
+       uint32_t                ringmem;
+       uint32_t                h2d_w_idx_ptr;
+       uint32_t                h2d_r_idx_ptr;
+       uint32_t                d2h_w_idx_ptr;
+       uint32_t                d2h_r_idx_ptr;
+       uint32_t                h2d_w_idx_hostaddr_low;
+       uint32_t                h2d_w_idx_hostaddr_high;
+       uint32_t                h2d_r_idx_hostaddr_low;
+       uint32_t                h2d_r_idx_hostaddr_high;
+       uint32_t                d2h_w_idx_hostaddr_low;
+       uint32_t                d2h_w_idx_hostaddr_high;
+       uint32_t                d2h_r_idx_hostaddr_low;
+       uint32_t                d2h_r_idx_hostaddr_high;
+       uint16_t                max_flowrings;
+       uint16_t                max_submissionrings;
+       uint16_t                max_completionrings;
+};
+
+/* Msgbuf defines */
+#define MSGBUF_IOCTL_RESP_TIMEOUT              2000 /* msecs */
+#define MSGBUF_IOCTL_REQ_PKTID                 0xFFFE
+#define MSGBUF_MAX_PKT_SIZE                    2048
+
+#define MSGBUF_TYPE_GEN_STATUS                 0x1
+#define MSGBUF_TYPE_RING_STATUS                        0x2
+#define MSGBUF_TYPE_FLOW_RING_CREATE           0x3
+#define MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT     0x4
+#define MSGBUF_TYPE_FLOW_RING_DELETE           0x5
+#define MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT     0x6
+#define MSGBUF_TYPE_FLOW_RING_FLUSH            0x7
+#define MSGBUF_TYPE_FLOW_RING_FLUSH_CMPLT      0x8
+#define MSGBUF_TYPE_IOCTLPTR_REQ               0x9
+#define MSGBUF_TYPE_IOCTLPTR_REQ_ACK           0xA
+#define MSGBUF_TYPE_IOCTLRESP_BUF_POST         0xB
+#define MSGBUF_TYPE_IOCTL_CMPLT                        0xC
+#define MSGBUF_TYPE_EVENT_BUF_POST             0xD
+#define MSGBUF_TYPE_WL_EVENT                   0xE
+#define MSGBUF_TYPE_TX_POST                    0xF
+#define MSGBUF_TYPE_TX_STATUS                  0x10
+#define MSGBUF_TYPE_RXBUF_POST                 0x11
+#define MSGBUF_TYPE_RX_CMPLT                   0x12
+#define MSGBUF_TYPE_LPBK_DMAXFER               0x13
+#define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT         0x14
+
+struct msgbuf_common_hdr {
+       uint8_t                 msgtype;
+       uint8_t                 ifidx;
+       uint8_t                 flags;
+       uint8_t                 rsvd0;
+       uint32_t                request_id;
+};
+
+struct msgbuf_buf_addr {
+       uint32_t                low_addr;
+       uint32_t                high_addr;
+};
+
+struct msgbuf_ioctl_req_hdr {
+       struct msgbuf_common_hdr        msg;
+       uint32_t                        cmd;
+       uint16_t                        trans_id;
+       uint16_t                        input_buf_len;
+       uint16_t                        output_buf_len;
+       uint16_t                        rsvd0[3];
+       struct msgbuf_buf_addr          req_buf_addr;
+       uint32_t                        rsvd1[2];
+};
+
+struct msgbuf_tx_msghdr {
+       struct msgbuf_common_hdr        msg;
+       uint8_t                         txhdr[ETHER_HDR_LEN];
+       uint8_t                         flags;
+#define BWFM_MSGBUF_PKT_FLAGS_FRAME_802_3      (1 << 0)
+#define BWFM_MSGBUF_PKT_FLAGS_PRIO_SHIFT       5
+       uint8_t                         seg_cnt;
+       struct msgbuf_buf_addr          metadata_buf_addr;
+       struct msgbuf_buf_addr          data_buf_addr;
+       uint16_t                        metadata_buf_len;
+       uint16_t                        data_len;
+       uint32_t                        rsvd0;
+};
+
+struct msgbuf_rx_bufpost {
+       struct msgbuf_common_hdr        msg;
+       uint16_t                        metadata_buf_len;
+       uint16_t                        data_buf_len;
+       uint32_t                        rsvd0;
+       struct msgbuf_buf_addr          metadata_buf_addr;
+       struct msgbuf_buf_addr          data_buf_addr;
+};
+
+struct msgbuf_rx_ioctl_resp_or_event {
+       struct msgbuf_common_hdr        msg;
+       uint16_t                        host_buf_len;
+       uint16_t                        rsvd0[3];
+       struct msgbuf_buf_addr          host_buf_addr;
+       uint32_t                        rsvd1[4];
+};
+
+struct msgbuf_completion_hdr {
+       uint16_t                        status;
+       uint16_t                        flow_ring_id;
+};
+
+struct msgbuf_rx_event {
+       struct msgbuf_common_hdr        msg;
+       struct msgbuf_completion_hdr    compl_hdr;
+       uint16_t                        event_data_len;
+       uint16_t                        seqnum;
+       uint16_t                        rsvd0[4];
+};
+
+struct msgbuf_ioctl_resp_hdr {
+       struct msgbuf_common_hdr        msg;
+       struct msgbuf_completion_hdr    compl_hdr;
+       uint16_t                        resp_len;
+       uint16_t                        trans_id;
+       uint32_t                        cmd;
+       uint32_t                        rsvd0;
+};
+
+struct msgbuf_tx_status {
+       struct msgbuf_common_hdr        msg;
+       struct msgbuf_completion_hdr    compl_hdr;
+       uint16_t                        metadata_len;
+       uint16_t                        tx_status;
+};
+
+struct msgbuf_rx_complete {
+       struct msgbuf_common_hdr        msg;
+       struct msgbuf_completion_hdr    compl_hdr;
+       uint16_t                        metadata_len;
+       uint16_t                        data_len;
+       uint16_t                        data_offset;
+       uint16_t                        flags;
+       uint32_t                        rx_status_0;
+       uint32_t                        rx_status_1;
+       uint32_t                        rsvd0;
+};
+
+struct msgbuf_tx_flowring_create_req {
+       struct msgbuf_common_hdr        msg;
+       uint8_t                         da[ETHER_ADDR_LEN];
+       uint8_t                         sa[ETHER_ADDR_LEN];
+       uint8_t                         tid;
+       uint8_t                         if_flags;
+       uint16_t                        flow_ring_id;
+       uint8_t                         tc;
+       uint8_t                         priority;
+       uint16_t                        int_vector;
+       uint16_t                        max_items;
+       uint16_t                        len_item;
+       struct msgbuf_buf_addr          flow_ring_addr;
+};
+
+struct msgbuf_tx_flowring_delete_req {
+       struct msgbuf_common_hdr        msg;
+       uint16_t                        flow_ring_id;
+       uint16_t                        reason;
+       uint32_t                        rsvd0[7];
+};
+
+struct msgbuf_flowring_create_resp {
+       struct msgbuf_common_hdr        msg;
+       struct msgbuf_completion_hdr    compl_hdr;
+       uint32_t                        rsvd0[3];
+};
+
+struct msgbuf_flowring_delete_resp {
+       struct msgbuf_common_hdr        msg;
+       struct msgbuf_completion_hdr    compl_hdr;
+       uint32_t                        rsvd0[3];
+};
+
+struct msgbuf_flowring_flush_resp {
+       struct msgbuf_common_hdr        msg;
+       struct msgbuf_completion_hdr    compl_hdr;
+       uint32_t                        rsvd0[3];
+};
-- 
2.17.0

>From e3f220ed224933820d98c7a5bc296532f353565c Mon Sep 17 00:00:00 2001
From: coypu <[email protected]>
Date: Fri, 4 May 2018 04:49:32 +0300
Subject: [PATCH 4/4] Update bwfm(4) to note PCI support and mention jmcneill.

---
 share/man/man4/bwfm.4 | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/share/man/man4/bwfm.4 b/share/man/man4/bwfm.4
index c2ce635e20..6877ece7da 100644
--- a/share/man/man4/bwfm.4
+++ b/share/man/man4/bwfm.4
@@ -24,7 +24,7 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd October 26, 2017
+.Dd May 4, 2018
 .Dt BWFM 4
 .Os
 .Sh NAME
@@ -32,6 +32,7 @@
 .Nd Broadcom and Cypress wireless network driver
 .Sh SYNOPSIS
 .Cd "bwfm* at uhub? port ?"
+.Cd "bwfm* at pci? dev ? function ?"
 .Sh DESCRIPTION
 The
 .Nm
@@ -49,7 +50,10 @@ The
 .Nm
 driver was written by
 .An Patrick Wildt
-.Aq Mt [email protected] .
+.Aq Mt [email protected]
+And
+.An Jared D. Mcneill
+.Aq Mt [email protected] .
 .Sh BUGS
 The firmware is outdated and contains known vulnerabilities.
 SDIO and PCI attachments are not supported yet.
-- 
2.17.0

Index: distrib/sets/lists/base/mi
===================================================================
RCS file: /cvsroot/src/distrib/sets/lists/base/mi,v
retrieving revision 1.1173
diff -u -r1.1173 mi
--- distrib/sets/lists/base/mi  29 Mar 2018 04:19:54 -0000      1.1173
+++ distrib/sets/lists/base/mi  3 May 2018 15:53:20 -0000
@@ -145,6 +145,9 @@
 ./libdata/firmware/if_bwfm/brcmfmac43236b.bin  base-firmware-root
 ./libdata/firmware/if_bwfm/brcmfmac43242a.bin  base-firmware-root
 ./libdata/firmware/if_bwfm/brcmfmac43569.bin   base-firmware-root
+./libdata/firmware/if_bwfm/brcmfmac4350-pcie.bin       base-firmware-root
+./libdata/firmware/if_bwfm/brcmfmac4350c2-pcie.bin     base-firmware-root
+./libdata/firmware/if_bwfm/brcmfmac43602-pcie.bin      base-firmware-root
 ./libdata/firmware/if_ipw                      base-firmware-root
 ./libdata/firmware/if_ipw/LICENSE              base-firmware-root
 ./libdata/firmware/if_ipw/ipw2100-1.2-i.fw     base-firmware-root
Index: external/broadcom/bwfm/Makefile
===================================================================
RCS file: /cvsroot/src/external/broadcom/bwfm/Makefile,v
retrieving revision 1.1
diff -u -r1.1 Makefile
--- external/broadcom/bwfm/Makefile     21 Oct 2017 02:06:34 -0000      1.1
+++ external/broadcom/bwfm/Makefile     3 May 2018 15:53:21 -0000
@@ -7,6 +7,9 @@
 FILES+=        dist/brcmfmac43236b.bin
 FILES+=        dist/brcmfmac43242a.bin
 FILES+=        dist/brcmfmac43569.bin
+FILES+=        dist/brcmfmac4350-pcie.bin
+FILES+=        dist/brcmfmac4350c2-pcie.bin
+FILES+=        dist/brcmfmac43602-pcie.bin
 
 FILESDIR=      /libdata/firmware/if_bwfm
 

Reply via email to