[brcmfmac] Fix ownership in the bus interface

This commit moves the state held in the brcmf_device struct into a
Device class, which proper C++ lifetime semantics.  Most of the state
formerly held in brcmf_device (which is not much) is moved into
brcmf_pub.

This allows us to fix the brcmf_bus_ops interface to take a brcmf_bus
instance as its context, rather than a brcmf_device.

This change also performs some cleanup of static state:
* The async dispatcher is now a member of Device, rather than a static
  instance, and its lifetime is properly tied to its Device.
* The irq_callback_lock is now a brcmf_pub member, and instantiated
  per-device.

Change-Id: I70362e57aa5f5aa269cc1dff919fc99c1333c468
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/BUILD.gn b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/BUILD.gn
index c12bf83..fa4e775 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/BUILD.gn
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/BUILD.gn
@@ -79,47 +79,47 @@
     # "bus.cc" is not included here; see top-of-file comment.
     "bcdc.cc",
     "bits.cc",
+    "bits.h",
     "btcoex.cc",
+    "btcoex.h",
     "cfg80211.cc",
+    "cfg80211.h",
     "chip.cc",
     "common.cc",
     "core.cc",
     "d11.cc",
     "device.cc",
     "feature.cc",
+    "feature.h",
     "fweh.cc",
     "fwil.cc",
     "fwsignal.cc",
-    "of.cc",
+    "fwsignal.h",
     "p2p.cc",
+    "p2p.h",
     "pno.cc",
+    "pno.h",
     "proto.cc",
+    "proto.h",
+    "timer.cc",
     "utils.cc",
   ]
   public = [
     "bcdc.h",
-    "bits.h",
     "brcm_hw_ids.h",
     "brcmu_d11.h",
     "brcmu_utils.h",
     "brcmu_wifi.h",
-    "btcoex.h",
     "bus.h",
-    "cfg80211.h",
     "chip.h",
     "common.h",
     "core.h",
     "device.h",
-    "feature.h",
     "fweh.h",
     "fwil.h",
     "fwil_types.h",
-    "fwsignal.h",
-    "of.h",
-    "p2p.h",
-    "pno.h",
-    "proto.h",
     "soc.h",
+    "timer.h",
   ]
   deps = [
     ":debug",
@@ -134,9 +134,11 @@
     "//zircon/public/banjo/ddk.protocol.wlanphyimpl",
     "//zircon/public/lib/async",
     "//zircon/public/lib/async-loop",
+    "//zircon/public/lib/ddktl",
     "//zircon/public/lib/sync",
     "//zircon/system/public",
   ]
+  friend = [ "test/*" ]
 }
 
 source_set("firmware") {
@@ -204,6 +206,7 @@
     "sim.h",
   ]
   deps = [
+    ":core",
     ":debug",
     "//src/connectivity/wlan/drivers/testing/lib/sim-device",
     "//zircon/public/lib/ddk",
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bcdc.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bcdc.cc
index 9850d9d..bf479ad 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bcdc.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bcdc.cc
@@ -26,7 +26,6 @@
 #include "bus.h"
 #include "core.h"
 #include "debug.h"
-#include "device.h"
 #include "fwil.h"
 #include "fwsignal.h"
 #include "linuxisms.h"
@@ -336,18 +335,13 @@
   return brcmf_bus_txdata(drvr->bus_if, pktbuf);
 }
 
-void brcmf_proto_bcdc_txflowblock(struct brcmf_device* dev, bool state) {
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
-  struct brcmf_pub* drvr = bus_if->drvr.get();
-
+void brcmf_proto_bcdc_txflowblock(brcmf_pub* drvr, bool state) {
   BRCMF_DBG(TRACE, "Enter\n");
-
   brcmf_fws_bus_blocked(drvr, state);
 }
 
-void brcmf_proto_bcdc_txcomplete(struct brcmf_device* dev, struct brcmf_netbuf* txp, bool success) {
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
-  struct brcmf_bcdc* bcdc = static_cast<decltype(bcdc)>(bus_if->drvr->proto->pd);
+void brcmf_proto_bcdc_txcomplete(brcmf_pub* drvr, struct brcmf_netbuf* txp, bool success) {
+  struct brcmf_bcdc* bcdc = static_cast<decltype(bcdc)>(drvr->proto->pd);
   struct brcmf_if* ifp;
 
   /* await txstatus signal for firmware if active */
@@ -356,7 +350,7 @@
       brcmf_fws_bustxfail(bcdc->fws, txp);
     }
   } else {
-    if (brcmf_proto_bcdc_hdrpull(bus_if->drvr.get(), false, txp, &ifp)) {
+    if (brcmf_proto_bcdc_hdrpull(drvr, false, txp, &ifp)) {
       brcmu_pkt_buf_free_netbuf(txp);
     } else {
       brcmf_txfinalize(ifp, txp, success);
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bcdc.h b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bcdc.h
index 4b75d76..bfb6003 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bcdc.h
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bcdc.h
@@ -17,7 +17,6 @@
 #define SRC_CONNECTIVITY_WLAN_DRIVERS_THIRD_PARTY_BROADCOM_BRCMFMAC_BCDC_H_
 
 #include "core.h"
-#include "device.h"
 #include "netbuf.h"
 
 struct brcmf_proto_bcdc_dcmd {
@@ -54,10 +53,10 @@
 
 // clang-format on
 
-zx_status_t brcmf_proto_bcdc_attach(struct brcmf_pub* drvr);
-void brcmf_proto_bcdc_detach(struct brcmf_pub* drvr);
-void brcmf_proto_bcdc_txflowblock(struct brcmf_device* dev, bool state);
-void brcmf_proto_bcdc_txcomplete(struct brcmf_device* dev, struct brcmf_netbuf* txp, bool success);
-struct brcmf_fws_info* drvr_to_fws(struct brcmf_pub* drvr);
+zx_status_t brcmf_proto_bcdc_attach(brcmf_pub* drvr);
+void brcmf_proto_bcdc_detach(brcmf_pub* drvr);
+void brcmf_proto_bcdc_txflowblock(brcmf_pub* drvr, bool state);
+void brcmf_proto_bcdc_txcomplete(brcmf_pub* drvr, brcmf_netbuf* txp, bool success);
+struct brcmf_fws_info* drvr_to_fws(brcmf_pub* drvr);
 
 #endif  // SRC_CONNECTIVITY_WLAN_DRIVERS_THIRD_PARTY_BROADCOM_BRCMFMAC_BCDC_H_
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bcmsdh.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bcmsdh.cc
index c48c793..e064514 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bcmsdh.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bcmsdh.cc
@@ -43,7 +43,6 @@
 #include "common.h"
 #include "debug.h"
 #include "defs.h"
-#include "device.h"
 #include "linuxisms.h"
 #include "netbuf.h"
 #include "sdio.h"
@@ -91,7 +90,7 @@
   uint8_t bootloader_macaddr[8];
   size_t actual_len;
   zx_status_t ret =
-      device_get_metadata(sdiodev->dev->zxdev, DEVICE_METADATA_MAC_ADDRESS, bootloader_macaddr,
+      device_get_metadata(sdiodev->drvr->zxdev, DEVICE_METADATA_MAC_ADDRESS, bootloader_macaddr,
                           sizeof(bootloader_macaddr), &actual_len);
 
   if (ret != ZX_OK || actual_len < ETH_ALEN) {
@@ -113,7 +112,7 @@
   pdata->oob_irq_supported = false;
   wifi_config_t config;
   size_t actual;
-  ret = device_get_metadata(sdiodev->dev->zxdev, DEVICE_METADATA_WIFI_CONFIG, &config,
+  ret = device_get_metadata(sdiodev->drvr->zxdev, DEVICE_METADATA_WIFI_CONFIG, &config,
                             sizeof(wifi_config_t), &actual);
   if ((ret != ZX_OK && ret != ZX_ERR_NOT_FOUND) ||
       (ret == ZX_OK && actual != sizeof(wifi_config_t))) {
@@ -769,7 +768,7 @@
     {/* end: all zeroes */}};
 #endif  // TODO_ADD_SDIO_IDS
 
-zx_status_t brcmf_sdio_register(struct brcmf_device* device) {
+zx_status_t brcmf_sdio_register(brcmf_pub* drvr, std::unique_ptr<brcmf_bus>* out_bus) {
   zx_status_t err;
   zx_status_t status;
 
@@ -781,7 +780,7 @@
   BRCMF_DBG(SDIO, "Enter\n");
 
   composite_protocol_t composite_proto = {};
-  status = device_get_protocol(device->zxdev, ZX_PROTOCOL_COMPOSITE, &composite_proto);
+  status = device_get_protocol(drvr->zxdev, ZX_PROTOCOL_COMPOSITE, &composite_proto);
   if (status != ZX_OK) {
     return status;
   }
@@ -856,7 +855,7 @@
   /* Set MMC_QUIRK_LENIENT_FN0 for this card */
   // func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
 
-  bus_if = std::make_unique<struct brcmf_bus>();
+  bus_if = std::make_unique<brcmf_bus>();
   func1 = static_cast<decltype(func1)>(calloc(1, sizeof(struct sdio_func)));
   if (!func1) {
     err = ZX_ERR_NO_MEMORY;
@@ -898,9 +897,8 @@
   sdiodev->bus_if = bus_if.get();
   sdiodev->func1 = func1;
   sdiodev->func2 = func2;
-  sdiodev->dev = device;
+  sdiodev->drvr = drvr;
   bus_if->bus_priv.sdio = sdiodev;
-  device->bus = std::move(bus_if);
 
   sdiodev->manufacturer_id = devinfo.funcs_hw_info[SDIO_FN_1].manufacturer_id;
   sdiodev->product_id = devinfo.funcs_hw_info[SDIO_FN_1].product_id;
@@ -916,6 +914,8 @@
 
   pthread_mutexattr_destroy(&mutex_attr);
   BRCMF_DBG(SDIO, "F2 init completed...\n");
+
+  *out_bus = std::move(bus_if);
   return ZX_OK;
 
 fail:
@@ -957,18 +957,10 @@
   BRCMF_DBG(SDIO, "Exit\n");
 }
 
-void brcmf_sdio_wowl_config(struct brcmf_device* dev, bool enabled) {
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
-  struct brcmf_sdio_dev* sdiodev = bus_if->bus_priv.sdio;
-
-  BRCMF_DBG(SDIO, "Configuring WOWL, enabled=%d\n", enabled);
-  sdiodev->wowl_enabled = enabled;
-}
-
-void brcmf_sdio_exit(struct brcmf_device* device) {
+void brcmf_sdio_exit(brcmf_bus* bus) {
   BRCMF_DBG(SDIO, "Enter\n");
 
-  brcmf_ops_sdio_remove(device->bus->bus_priv.sdio);
-  delete device->bus->bus_priv.sdio;
-  device->bus.reset();
+  brcmf_ops_sdio_remove(bus->bus_priv.sdio);
+  delete bus->bus_priv.sdio;
+  bus->bus_priv.sdio = nullptr;
 }
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/binding.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/binding.cc
index 1236416..392086f 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/binding.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/binding.cc
@@ -11,130 +11,21 @@
 // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 // OF THIS SOFTWARE.
 
-#include <zircon/types.h>
-
 #include <memory>
 
 #include <ddk/binding.h>
 #include <ddk/device.h>
 #include <ddk/driver.h>
 #include <ddk/platform-defs.h>
-#include <ddktl/device.h>
-#include <ddktl/protocol/wlanphyimpl.h>
 #include <hw/pci.h>
 
 #include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/brcm_hw_ids.h"
-#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bus.h"
-#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/common.h"
-#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/core.h"
 #include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/device.h"
 
-namespace wlan {
-namespace brcmfmac {
-
-// This class uses the DDKTL classes to manage the lifetime of a brcmfmac driver instance.
-class WlanphyImplDevice : public ::ddk::Device<WlanphyImplDevice, ::ddk::Unbindable>,
-                          public ::ddk::WlanphyImplProtocol<WlanphyImplDevice, ddk::base_protocol> {
- public:
-  // Static factory function for WlanphyImplDevice instances. This factory does not return the
-  // instance itself, as on successful invocation the instance will have its lifecycle managed by
-  // the devhost.
-  static zx_status_t Create(zx_device_t* device);
-
-  // DDK interface implementation.
-  void DdkUnbind();
-  void DdkRelease();
-
-  // WlanphyImpl protocol implementation.
-  zx_status_t WlanphyImplQuery(wlanphy_impl_info_t* out_info);
-  zx_status_t WlanphyImplCreateIface(const wlanphy_impl_create_iface_req_t* req,
-                                     uint16_t* out_iface_id);
-  zx_status_t WlanphyImplDestroyIface(uint16_t iface_id);
-  zx_status_t WlanphyImplSetCountry(const wlanphy_country_t* country);
-
- protected:
-  using DeviceType = ::ddk::Device<WlanphyImplDevice, ::ddk::Unbindable>;
-
-  explicit WlanphyImplDevice(zx_device_t* parent);
-  ~WlanphyImplDevice() = default;
-
- private:
-  brcmf_device device_;
-};
-
-// static
-zx_status_t WlanphyImplDevice::Create(zx_device_t* device) {
-  zx_status_t status = ZX_OK;
-
-  const auto ddk_remover = [](WlanphyImplDevice* device) { device->DdkRemove(); };
-  std::unique_ptr<WlanphyImplDevice, decltype(ddk_remover)> wlanphyimpl_device(
-      new WlanphyImplDevice(device), ddk_remover);
-  if ((status = wlanphyimpl_device->DdkAdd("brcmfmac-wlanphy", DEVICE_ADD_INVISIBLE)) != ZX_OK) {
-    delete wlanphyimpl_device.release();
-    return status;
-  }
-  wlanphyimpl_device->device_.zxdev = device;
-  wlanphyimpl_device->device_.phy_zxdev = wlanphyimpl_device->zxdev();
-
-  if ((status = brcmf_core_init(&wlanphyimpl_device->device_)) != ZX_OK) {
-    return status;
-  }
-
-  wlanphyimpl_device.release();  // This now has its lifecycle managed by the devhost.
-  return ZX_OK;
-}
-
-void WlanphyImplDevice::DdkUnbind() {
-  brcmf_core_exit(&device_);
-  DdkRemove();
-}
-
-void WlanphyImplDevice::DdkRelease() { delete this; }
-
-zx_status_t WlanphyImplDevice::WlanphyImplQuery(wlanphy_impl_info_t* out_info) {
-  if (!device_.bus) {
-    return ZX_ERR_BAD_STATE;
-  }
-  brcmf_if* const ifp = device_.bus->drvr->iflist[0];
-  return brcmf_phy_query(ifp, out_info);
-}
-
-zx_status_t WlanphyImplDevice::WlanphyImplCreateIface(const wlanphy_impl_create_iface_req_t* req,
-                                                      uint16_t* out_iface_id) {
-  if (!device_.bus) {
-    return ZX_ERR_BAD_STATE;
-  }
-  brcmf_if* const ifp = device_.bus->drvr->iflist[0];
-  return brcmf_phy_create_iface(ifp, req, out_iface_id);
-}
-
-zx_status_t WlanphyImplDevice::WlanphyImplDestroyIface(uint16_t iface_id) {
-  if (!device_.bus) {
-    return ZX_ERR_BAD_STATE;
-  }
-  brcmf_if* const ifp = device_.bus->drvr->iflist[0];
-  return brcmf_phy_destroy_iface(ifp, iface_id);
-}
-
-zx_status_t WlanphyImplDevice::WlanphyImplSetCountry(const wlanphy_country_t* country) {
-  if (!device_.bus) {
-    return ZX_ERR_BAD_STATE;
-  }
-  brcmf_if* const ifp = device_.bus->drvr->iflist[0];
-  return brcmf_phy_set_country(ifp, country);
-}
-
-WlanphyImplDevice::WlanphyImplDevice(zx_device_t* parent) : DeviceType(parent), device_() {}
-
-}  // namespace brcmfmac
-}  // namespace wlan
-
 static constexpr zx_driver_ops_t brcmfmac_driver_ops = {
     .version = DRIVER_OPS_VERSION,
-    .init = [](void** out_ctx) { return brcmfmac_module_init(); },
     .bind = [](void* ctx,
-               zx_device_t* device) { return ::wlan::brcmfmac::WlanphyImplDevice::Create(device); },
-    .release = [](void* ctx) { return brcmfmac_module_exit(); },
+               zx_device_t* device) { return ::wlan::brcmfmac::Device::Create(device, nullptr); },
 };
 
 // clang-format off
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/btcoex.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/btcoex.cc
index fa1c091..2be9ca6 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/btcoex.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/btcoex.cc
@@ -22,7 +22,6 @@
 #include "core.h"
 #include "debug.h"
 #include "defs.h"
-#include "device.h"
 #include "fwil.h"
 #include "fwil_types.h"
 #include "linuxisms.h"
@@ -264,13 +263,13 @@
  * brcmf_btcoex_timerfunc() - BT coex timer callback
  */
 static void brcmf_btcoex_timerfunc(void* data) {
-  pthread_mutex_lock(&irq_callback_lock);
   struct brcmf_btcoex_info* bt_local = static_cast<decltype(bt_local)>(data);
+  bt_local->cfg->pub->irq_callback_lock.lock();
   BRCMF_DBG(TRACE, "enter\n");
 
   bt_local->timer_on = false;
   workqueue_schedule_default(&bt_local->work);
-  pthread_mutex_unlock(&irq_callback_lock);
+  bt_local->cfg->pub->irq_callback_lock.unlock();
 }
 
 /**
@@ -362,7 +361,7 @@
   /* Set up timer for BT  */
   btci->timer_on = false;
   btci->timeout = BRCMF_BTCOEX_OPPR_WIN_TIME_MSEC;
-  brcmf_timer_init(&btci->timer, brcmf_btcoex_timerfunc, btci);
+  brcmf_timer_init(&btci->timer, cfg->pub->dispatcher, brcmf_btcoex_timerfunc, btci);
   btci->cfg = cfg;
   btci->saved_regs_part1 = false;
   btci->saved_regs_part2 = false;
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bus.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bus.cc
index 5d2edf8..210bde4 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bus.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bus.cc
@@ -11,18 +11,18 @@
 // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 // OF THIS SOFTWARE.
 
-#include "bus.h"
+#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bus.h"
 
-#include "debug.h"
+#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/debug.h"
 
 // Note that this file does not include any headers that define the bus-specific functions it calls,
 // since it cannot depend on them.  Hence we just declare them directly before use.
 
-zx_status_t brcmf_bus_register(struct brcmf_device* device) {
+zx_status_t brcmf_bus_register(brcmf_pub* drvr, std::unique_ptr<brcmf_bus>* out_bus) {
 #if CONFIG_BRCMFMAC_SDIO
   {
-    extern zx_status_t brcmf_sdio_register(struct brcmf_device * device);
-    const zx_status_t result = brcmf_sdio_register(device);
+    extern zx_status_t brcmf_sdio_register(brcmf_pub * drvr, std::unique_ptr<brcmf_bus> * out_bus);
+    const zx_status_t result = brcmf_sdio_register(drvr, out_bus);
     if (result != ZX_OK) {
       BRCMF_DBG(INFO, "SDIO registration failed: %d\n", result);
     } else {
@@ -33,8 +33,8 @@
 
 #if CONFIG_BRCMFMAC_SIM
   {
-    extern zx_status_t brcmf_sim_register(struct brcmf_device * device);
-    const zx_status_t result = brcmf_sim_register(device);
+    extern zx_status_t brcmf_sim_register(brcmf_pub * drvr, std::unique_ptr<brcmf_bus> * out_bus);
+    const zx_status_t result = brcmf_sim_register(drvr, out_bus);
     if (result != ZX_OK) {
       BRCMF_DBG(INFO, "SIM registration failed: %d\n", result);
     } else {
@@ -46,14 +46,14 @@
   return ZX_ERR_NOT_SUPPORTED;
 }
 
-void brcmf_bus_exit(struct brcmf_device* device) {
+void brcmf_bus_exit(brcmf_bus* bus) {
 #if CONFIG_BRCMFMAC_SDIO
-  extern void brcmf_sdio_exit(struct brcmf_device * device);
-  brcmf_sdio_exit(device);
+  extern void brcmf_sdio_exit(brcmf_bus * bus);
+  brcmf_sdio_exit(bus);
 #endif
 
 #if CONFIG_BRCMFMAC_SIM
-  extern void brcmf_sim_exit(struct brcmf_device * device);
-  brcmf_sim_exit(device);
+  extern void brcmf_sim_exit(brcmf_bus * bus);
+  brcmf_sim_exit(bus);
 #endif
 }
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bus.h b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bus.h
index 5d77d1b..e7cd956 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bus.h
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bus.h
@@ -23,8 +23,6 @@
 #include <ddk/device.h>
 #include <ddk/driver.h>
 
-#include "core.h"
-#include "device.h"
 #include "netbuf.h"
 
 // HW/SW bus in use
@@ -36,6 +34,7 @@
   BRCMF_BUS_UP    /* Ready for frame transfers */
 };
 
+struct brcmf_pub;
 struct brcmf_mp_device;
 
 struct brcmf_bus_dcmd {
@@ -72,22 +71,21 @@
  * indicated otherwise these callbacks are mandatory.
  */
 
-#include "device.h"
+struct brcmf_bus;
 
 struct brcmf_bus_ops {
   enum brcmf_bus_type (*get_bus_type)();
-  zx_status_t (*preinit)(struct brcmf_device* dev);
-  void (*stop)(struct brcmf_device* dev);
-  zx_status_t (*txdata)(struct brcmf_device* dev, struct brcmf_netbuf* netbuf);
-  zx_status_t (*txctl)(struct brcmf_device* dev, unsigned char* msg, uint len);
-  zx_status_t (*rxctl)(struct brcmf_device* dev, unsigned char* msg, uint len, int* rxlen_out);
-  struct pktq* (*gettxq)(struct brcmf_device* dev);
-  void (*wowl_config)(struct brcmf_device* dev, bool enabled);
-  size_t (*get_ramsize)(struct brcmf_device* dev);
-  zx_status_t (*get_memdump)(struct brcmf_device* dev, void* data, size_t len);
-  zx_status_t (*get_fwname)(struct brcmf_device* dev, uint chip, uint chiprev,
-                            unsigned char* fw_name);
-  zx_status_t (*get_bootloader_macaddr)(struct brcmf_device* dev, uint8_t* mac_addr);
+  zx_status_t (*preinit)(brcmf_bus* bus);
+  void (*stop)(brcmf_bus* bus);
+  zx_status_t (*txdata)(brcmf_bus* bus, struct brcmf_netbuf* netbuf);
+  zx_status_t (*txctl)(brcmf_bus* bus, unsigned char* msg, uint len);
+  zx_status_t (*rxctl)(brcmf_bus* bus, unsigned char* msg, uint len, int* rxlen_out);
+  struct pktq* (*gettxq)(brcmf_bus* bus);
+  void (*wowl_config)(brcmf_bus* bus, bool enabled);
+  size_t (*get_ramsize)(brcmf_bus* bus);
+  zx_status_t (*get_memdump)(brcmf_bus* bus, void* data, size_t len);
+  zx_status_t (*get_fwname)(brcmf_bus* bus, uint chip, uint chiprev, unsigned char* fw_name);
+  zx_status_t (*get_bootloader_macaddr)(brcmf_bus* bus, uint8_t* mac_addr);
   zx_status_t (*device_add)(zx_device_t* parent, device_add_args_t* args, zx_device_t** out);
 };
 
@@ -123,8 +121,6 @@
     struct brcmf_pciedev* pcie;
     struct brcmf_simdev* sim;
   } bus_priv;
-  struct brcmf_device* dev;
-  std::unique_ptr<struct brcmf_pub> drvr;
   enum brcmf_bus_state state;
   struct brcmf_bus_stats stats;
   uint maxctl;
@@ -143,22 +139,22 @@
   if (!bus->ops->preinit) {
     return ZX_OK;
   }
-  return bus->ops->preinit(bus->dev);
+  return bus->ops->preinit(bus);
 }
 
-static inline void brcmf_bus_stop(struct brcmf_bus* bus) { bus->ops->stop(bus->dev); }
+static inline void brcmf_bus_stop(struct brcmf_bus* bus) { bus->ops->stop(bus); }
 
 static inline int brcmf_bus_txdata(struct brcmf_bus* bus, struct brcmf_netbuf* netbuf) {
-  return bus->ops->txdata(bus->dev, netbuf);
+  return bus->ops->txdata(bus, netbuf);
 }
 
 static inline int brcmf_bus_txctl(struct brcmf_bus* bus, unsigned char* msg, uint len) {
-  return bus->ops->txctl(bus->dev, msg, len);
+  return bus->ops->txctl(bus, msg, len);
 }
 
 static inline int brcmf_bus_rxctl(struct brcmf_bus* bus, unsigned char* msg, uint len,
                                   int* rxlen_out) {
-  return bus->ops->rxctl(bus->dev, msg, len, rxlen_out);
+  return bus->ops->rxctl(bus, msg, len, rxlen_out);
 }
 
 static inline zx_status_t brcmf_bus_gettxq(struct brcmf_bus* bus, struct pktq** txq_out) {
@@ -169,14 +165,14 @@
     return ZX_ERR_NOT_FOUND;
   }
   if (txq_out) {
-    *txq_out = bus->ops->gettxq(bus->dev);
+    *txq_out = bus->ops->gettxq(bus);
   }
   return ZX_OK;
 }
 
 static inline void brcmf_bus_wowl_config(struct brcmf_bus* bus, bool enabled) {
   if (bus->ops->wowl_config) {
-    bus->ops->wowl_config(bus->dev, enabled);
+    bus->ops->wowl_config(bus, enabled);
   }
 }
 
@@ -185,7 +181,7 @@
     return 0;
   }
 
-  return bus->ops->get_ramsize(bus->dev);
+  return bus->ops->get_ramsize(bus);
 }
 
 static inline zx_status_t brcmf_bus_get_memdump(struct brcmf_bus* bus, void* data, size_t len) {
@@ -193,17 +189,17 @@
     return ZX_ERR_NOT_FOUND;
   }
 
-  return bus->ops->get_memdump(bus->dev, data, len);
+  return bus->ops->get_memdump(bus, data, len);
 }
 
 static inline zx_status_t brcmf_bus_get_fwname(struct brcmf_bus* bus, uint chip, uint chiprev,
                                                unsigned char* fw_name) {
-  return bus->ops->get_fwname(bus->dev, chip, chiprev, fw_name);
+  return bus->ops->get_fwname(bus, chip, chiprev, fw_name);
 }
 
 static inline zx_status_t brcmf_bus_get_bootloader_macaddr(struct brcmf_bus* bus,
                                                            uint8_t* mac_addr) {
-  return bus->ops->get_bootloader_macaddr(bus->dev, mac_addr);
+  return bus->ops->get_bootloader_macaddr(bus, mac_addr);
 }
 
 static inline zx_status_t brcmf_bus_device_add(struct brcmf_bus* bus, zx_device_t* parent,
@@ -211,32 +207,8 @@
   return bus->ops->device_add(parent, args, out);
 }
 
-/*
- * interface functions from common layer
- */
-
-/* Receive frame for delivery to OS.  Callee disposes of rxp. */
-void brcmf_rx_frame(struct brcmf_device* dev, struct brcmf_netbuf* rxp, bool handle_event);
-/* Receive async event packet from firmware. Callee disposes of rxp. */
-void brcmf_rx_event(struct brcmf_device* dev, struct brcmf_netbuf* rxp);
-
-/* Indication from bus module regarding presence/insertion of dongle. */
-zx_status_t brcmf_attach(struct brcmf_device* dev, struct brcmf_mp_device* settings);
-/* Indication from bus module regarding removal/absence of dongle */
-void brcmf_detach(struct brcmf_device* dev);
-/* Indication from bus module that dongle should be reset */
-void brcmf_dev_reset(struct brcmf_device* dev);
-
-/* Configure the "global" bus state used by upper layers */
-void brcmf_bus_change_state(struct brcmf_bus* bus, enum brcmf_bus_state state);
-
-zx_status_t brcmf_bus_started(struct brcmf_device* dev);
-zx_status_t brcmf_iovar_data_set(struct brcmf_device* dev, const char* name, void* data,
-                                 uint32_t len, int32_t* fwerr_ptr);
-void brcmf_bus_add_txhdrlen(struct brcmf_device* dev, uint len);
-
 // Interface to the system bus.
-zx_status_t brcmf_bus_register(struct brcmf_device* device);
-void brcmf_bus_exit(struct brcmf_device* device);
+zx_status_t brcmf_bus_register(brcmf_pub* drvr, std::unique_ptr<brcmf_bus>* out_bus);
+void brcmf_bus_exit(brcmf_bus* bus);
 
 #endif  // SRC_CONNECTIVITY_WLAN_DRIVERS_THIRD_PARTY_BROADCOM_BRCMFMAC_BUS_H_
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/cfg80211.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/cfg80211.cc
index aa339d6..425a770 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/cfg80211.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/cfg80211.cc
@@ -35,7 +35,6 @@
 #include "core.h"
 #include "debug.h"
 #include "defs.h"
-#include "device.h"
 #include "feature.h"
 #include "fwil.h"
 #include "fwil_types.h"
@@ -1146,13 +1145,12 @@
 }
 
 static void brcmf_disconnect_timeout(void* data) {
-  pthread_mutex_lock(&irq_callback_lock);
-
   struct brcmf_cfg80211_info* cfg = static_cast<decltype(cfg)>(data);
+  cfg->pub->irq_callback_lock.lock();
   BRCMF_DBG(TRACE, "Enter\n");
   workqueue_schedule_default(&cfg->disconnect_timeout_work);
 
-  pthread_mutex_unlock(&irq_callback_lock);
+  cfg->pub->irq_callback_lock.unlock();
 }
 
 static zx_status_t brcmf_cfg80211_disconnect(struct net_device* ndev,
@@ -1198,7 +1196,7 @@
     goto done;
   }
 
-  brcmf_timer_init(&cfg->disconnect_timeout, brcmf_disconnect_timeout, cfg);
+  brcmf_timer_init(&cfg->disconnect_timeout, ifp->drvr->dispatcher,  brcmf_disconnect_timeout, cfg);
   brcmf_timer_set(&cfg->disconnect_timeout, BRCMF_DISCONNECT_TIMEOUT);
 
 done:
@@ -1572,14 +1570,14 @@
 }
 
 static void brcmf_escan_timeout(void* data) {
-  pthread_mutex_lock(&irq_callback_lock);
   struct brcmf_cfg80211_info* cfg = static_cast<decltype(cfg)>(data);
+  cfg->pub->irq_callback_lock.lock();
 
   if (cfg->int_escan_map || cfg->scan_request) {
     BRCMF_ERR("timer expired\n");
     workqueue_schedule_default(&cfg->escan_timeout_work);
   }
-  pthread_mutex_unlock(&irq_callback_lock);
+  cfg->pub->irq_callback_lock.unlock();
 }
 
 static bool brcmf_compare_update_same_bss(struct brcmf_cfg80211_info* cfg,
@@ -1720,7 +1718,7 @@
   cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
   /* Init scan_timeout timer */
   cfg->escan_timeout.data = cfg;
-  brcmf_timer_init(&cfg->escan_timeout, brcmf_escan_timeout, cfg);
+  brcmf_timer_init(&cfg->escan_timeout, cfg->pub->dispatcher, brcmf_escan_timeout, cfg);
   workqueue_init_work(&cfg->escan_timeout_work, brcmf_cfg80211_escan_timeout_worker);
 }
 
@@ -4491,8 +4489,7 @@
   free(wiphy);
 }
 
-struct brcmf_cfg80211_info* brcmf_cfg80211_attach(struct brcmf_pub* drvr,
-                                                  struct brcmf_device* busdev, bool p2pdev_forced) {
+struct brcmf_cfg80211_info* brcmf_cfg80211_attach(struct brcmf_pub* drvr) {
   struct net_device* ndev = brcmf_get_ifp(drvr, 0)->ndev;
   struct brcmf_cfg80211_info* cfg;
   struct wiphy* wiphy;
@@ -4520,7 +4517,6 @@
     goto wiphy_out;
   }
   memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
-  wiphy->dev = busdev;
 
   cfg = wiphy_to_cfg(wiphy);
   cfg->wiphy = wiphy;
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/cfg80211.h b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/cfg80211.h
index b09a129..adb29f2 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/cfg80211.h
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/cfg80211.h
@@ -27,9 +27,9 @@
 /* for brcmu_d11inf */
 #include "brcmu_d11.h"
 #include "core.h"
-#include "device.h"
 #include "fwil_types.h"
 #include "p2p.h"
+#include "timer.h"
 #include "workqueue.h"
 
 // clang-format off
@@ -418,8 +418,7 @@
   return &cfg->conn_info;
 }
 
-struct brcmf_cfg80211_info* brcmf_cfg80211_attach(struct brcmf_pub* drvr,
-                                                  struct brcmf_device* busdev, bool p2pdev_forced);
+struct brcmf_cfg80211_info* brcmf_cfg80211_attach(struct brcmf_pub* drvr);
 void brcmf_cfg80211_detach(struct brcmf_cfg80211_info* cfg);
 zx_status_t brcmf_cfg80211_up(struct net_device* ndev);
 zx_status_t brcmf_cfg80211_down(struct net_device* ndev);
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/chip.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/chip.cc
index c755b5b..1563397 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/chip.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/chip.cc
@@ -24,7 +24,6 @@
 #include "chipcommon.h"
 #include "debug.h"
 #include "defs.h"
-#include "device.h"
 #include "linuxisms.h"
 #include "soc.h"
 
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/common.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/common.cc
index d0d6635..c5638fe 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/common.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/common.cc
@@ -25,11 +25,9 @@
 #include "brcmu_wifi.h"
 #include "bus.h"
 #include "debug.h"
-#include "device.h"
 #include "fwil.h"
 #include "fwil_types.h"
 #include "linuxisms.h"
-#include "of.h"
 
 MODULE_AUTHOR("Broadcom Corporation")
 MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.")
@@ -49,10 +47,6 @@
 module_param_named(feature_disable, brcmf_feature_disable, int, 0)
     MODULE_PARM_DESC(feature_disable, "Disable features")
 
-        static char brcmf_firmware_path[BRCMF_FW_ALTPATH_LEN] = "brcmfmac/";
-module_param_string(alternative_fw_path, brcmf_firmware_path, BRCMF_FW_ALTPATH_LEN, S_IRUSR)
-    MODULE_PARM_DESC(alternative_fw_path, "Alternative firmware path")
-
         static int brcmf_fcmode;
 module_param_named(fcmode, brcmf_fcmode, int, 0)
     MODULE_PARM_DESC(fcmode, "Mode of firmware signalled flow control")
@@ -67,8 +61,6 @@
     MODULE_PARM_DESC(ignore_probe_fail, "always succeed probe for debugging")
 #endif  // !defined(NDEBUG)
 
-        struct brcmf_mp_global_t brcmf_mp_global;
-
 void brcmf_c_set_joinpref_default(struct brcmf_if* ifp) {
   struct brcmf_join_pref_params join_pref_params[2];
   zx_status_t err;
@@ -338,16 +330,7 @@
   return err;
 }
 
-static void brcmf_mp_attach(void) {
-  /* If module param firmware path is set then this will always be used,
-   * if not set then if available use the platform data version. To make
-   * sure it gets initialized at all, always copy the module param version
-   */
-  strlcpy(brcmf_mp_global.firmware_path, brcmf_firmware_path, BRCMF_FW_ALTPATH_LEN);
-}
-
-struct brcmf_mp_device* brcmf_get_module_param(struct brcmf_device* dev,
-                                               enum brcmf_bus_type bus_type, uint32_t chip,
+struct brcmf_mp_device* brcmf_get_module_param(enum brcmf_bus_type bus_type, uint32_t chip,
                                                uint32_t chiprev) {
   struct brcmf_mp_device* settings;
 
@@ -406,43 +389,8 @@
       }
     }
   }
-  if (!found) {
-    /* No platform data for this device, try OF (Open Firwmare) */
-    brcmf_of_probe(dev, bus_type, settings);
-  }
 #endif /* USE_PLATFORM_DATA */
   return settings;
 }
 
 void brcmf_release_module_param(struct brcmf_mp_device* module_param) { free(module_param); }
-
-zx_status_t brcmfmac_module_init(void) {
-  zx_status_t err = ZX_OK;
-
-  async_loop_t* async_loop;
-  async_loop_config_t async_config;
-  memset(&async_config, 0, sizeof(async_config));
-  err = async_loop_create(&async_config, &async_loop);
-  if (err != ZX_OK) {
-    BRCMF_ERR("Returning err %d %s", err, zx_status_get_string(err));
-    return err;
-  }
-  err = async_loop_start_thread(async_loop, "async_thread", NULL);
-  if (err != ZX_OK) {
-    async_loop_destroy(async_loop);
-    BRCMF_ERR("Returning err %d %s", err, zx_status_get_string(err));
-    return err;
-  }
-  default_dispatcher = async_loop_get_dispatcher(async_loop);
-
-  /* Initialize global module paramaters */
-  brcmf_mp_attach();
-
-  return ZX_OK;
-}
-
-void brcmfmac_module_exit(void) {
-  if (default_dispatcher != NULL) {
-    async_loop_destroy(async_loop_from_dispatcher(default_dispatcher));
-  }
-}
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/common.h b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/common.h
index 0536d35..4c7caaa 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/common.h
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/common.h
@@ -17,32 +17,12 @@
 
 #include "bus.h"
 #include "core.h"
-#include "device.h"
 #include "fwil_types.h"
 #include "linuxisms.h"
 
 #define BRCMF_FW_ALTPATH_LEN 256
 #define BRCMF_FW_NAME_LEN 320
 
-/* Definitions for the module global and device specific settings are defined
- * here. Two structs are used for them. brcmf_mp_global_t and brcmf_mp_device.
- * The mp_global is instantiated once in a global struct and gets initialized
- * by the common_attach function which should be called before any other
- * (module) initiliazation takes place. The device specific settings is part
- * of the drvr struct and should be initialized on every brcmf_attach.
- */
-
-/**
- * struct brcmf_mp_global_t - Global module paramaters.
- *
- * @firmware_path: Alternative firmware path.
- */
-struct brcmf_mp_global_t {
-  char firmware_path[BRCMF_FW_ALTPATH_LEN];
-};
-
-extern struct brcmf_mp_global_t brcmf_mp_global;
-
 /**
  * struct brcmfmac_sdio_pd - SDIO-specific device module parameters
  */
@@ -78,15 +58,11 @@
 
 void brcmf_c_set_joinpref_default(struct brcmf_if* ifp);
 
-struct brcmf_mp_device* brcmf_get_module_param(struct brcmf_device* dev,
-                                               enum brcmf_bus_type bus_type, uint32_t chip,
+struct brcmf_mp_device* brcmf_get_module_param(enum brcmf_bus_type bus_type, uint32_t chip,
                                                uint32_t chiprev);
 void brcmf_release_module_param(struct brcmf_mp_device* module_param);
 
 /* Sets dongle media info (drv_version, mac address). */
 zx_status_t brcmf_c_preinit_dcmds(struct brcmf_if* ifp);
 
-zx_status_t brcmfmac_module_init();
-void brcmfmac_module_exit();
-
 #endif  // SRC_CONNECTIVITY_WLAN_DRIVERS_THIRD_PARTY_BROADCOM_BRCMFMAC_COMMON_H_
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/core.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/core.cc
index 4f4001b..d0ac119 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/core.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/core.cc
@@ -33,7 +33,6 @@
 #include "cfg80211.h"
 #include "common.h"
 #include "debug.h"
-#include "device.h"
 #include "feature.h"
 #include "fwil.h"
 #include "fwil_types.h"
@@ -46,14 +45,12 @@
 
 #define MAX_WAIT_FOR_8021X_TX_MSEC (950)
 
-#define BRCMF_BSSIDX_INVALID -1
-
-static inline struct brcmf_device* if_to_dev(struct brcmf_if* ifp) {
-  return ifp->drvr->bus_if->dev;
+static inline brcmf_pub* if_to_pub(struct brcmf_if* ifp) {
+  return ifp->drvr;
 }
 
-static inline struct brcmf_device* ndev_to_dev(struct net_device* ndev) {
-  return if_to_dev(ndev_to_if(ndev));
+static inline brcmf_pub* ndev_to_pub(struct net_device* ndev) {
+  return if_to_pub(ndev_to_if(ndev));
 }
 
 const char* brcmf_ifname(struct brcmf_if* ifp) {
@@ -301,7 +298,7 @@
             ifp->netif_stop, reason, state);
 
   // spin_lock_irqsave(&ifp->netif_stop_lock, flags);
-  pthread_mutex_lock(&irq_callback_lock);
+  ifp->drvr->irq_callback_lock.lock();
 
   if (state) {
     if (!ifp->netif_stop) {
@@ -315,7 +312,7 @@
     }
   }
   // spin_unlock_irqrestore(&ifp->netif_stop_lock, flags);
-  pthread_mutex_unlock(&irq_callback_lock);
+  ifp->drvr->irq_callback_lock.unlock();
 }
 
 void brcmf_netif_rx(struct brcmf_if* ifp, struct brcmf_netbuf* netbuf) {
@@ -377,12 +374,10 @@
   return ZX_OK;
 }
 
-void brcmf_rx_frame(struct brcmf_device* dev, struct brcmf_netbuf* netbuf, bool handle_event) {
+void brcmf_rx_frame(brcmf_pub* drvr, brcmf_netbuf* netbuf, bool handle_event) {
   struct brcmf_if* ifp;
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
-  struct brcmf_pub* drvr = bus_if->drvr.get();
 
-  BRCMF_DBG(DATA, "Enter: %s: rxp=%p\n", device_get_name(dev->zxdev), netbuf);
+  BRCMF_DBG(DATA, "Enter: %s: rxp=%p\n", device_get_name(drvr->zxdev), netbuf);
 
   if (brcmf_rx_hdrpull(drvr, netbuf, &ifp)) {
     BRCMF_DBG(TEMP, "hdrpull returned nonzero\n");
@@ -401,12 +396,10 @@
   }
 }
 
-void brcmf_rx_event(struct brcmf_device* dev, struct brcmf_netbuf* netbuf) {
+void brcmf_rx_event(brcmf_pub* drvr, brcmf_netbuf* netbuf) {
   struct brcmf_if* ifp;
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
-  struct brcmf_pub* drvr = bus_if->drvr.get();
 
-  BRCMF_DBG(EVENT, "Enter: %s: rxp=%p\n", device_get_name(dev->zxdev), netbuf);
+  BRCMF_DBG(EVENT, "Enter: %s: rxp=%p\n", device_get_name(drvr->zxdev), netbuf);
 
   if (brcmf_rx_hdrpull(drvr, netbuf, &ifp)) {
     return;
@@ -543,11 +536,11 @@
       .proto_ops = &if_impl_proto_ops,
   };
 
-  struct brcmf_device* device = ifp->drvr->bus_if->dev;
-  struct brcmf_bus* bus = device->bus.get();
+  brcmf_pub* const drvr = ifp->drvr;
+  brcmf_bus* const bus = drvr->bus_if;
 
   BRCMF_DBG(TEMP, "About to add if_dev");
-  result = brcmf_bus_device_add(bus, device->phy_zxdev, &args, &device->if_zxdev);
+  result = brcmf_bus_device_add(bus, drvr->phy_zxdev, &args, &drvr->if_zxdev);
   if (result != ZX_OK) {
     BRCMF_ERR("Failed to device_add: %s", zx_status_get_string(result));
     return result;
@@ -591,23 +584,19 @@
 
   ndev->needed_headroom += drvr->hdrlen;
   workqueue_init_work(&ifp->multicast_work, _brcmf_set_multicast_list);
-  device_make_visible(ifp->drvr->bus_if->dev->phy_zxdev);
-
-  BRCMF_DBG(TEMP, "device_make_visible() succeeded. Activated phy hooks.\n");
-
   return ZX_OK;
 }
 
 static void brcmf_net_detach(struct net_device* ndev, bool rtnl_locked) {
-  struct brcmf_device* device = ndev_to_dev(ndev);
+  brcmf_pub* drvr = ndev_to_pub(ndev);
 
   // TODO(cphoenix): Make sure devices are removed and memory is freed properly. This code
   // is probably wrong. See WLAN-1057.
   brcmf_free_net_device_vif(ndev);
   brcmf_free_net_device(ndev);
-  if (device->phy_zxdev != NULL) {
-    device_remove(device->phy_zxdev);
-    device->phy_zxdev = NULL;
+  if (drvr->phy_zxdev != NULL) {
+    device_remove(drvr->phy_zxdev);
+    drvr->phy_zxdev = NULL;
   }
 }
 
@@ -787,52 +776,36 @@
   brcmf_del_if(ifp->drvr, ifp->bsscfgidx, rtnl_locked);
 }
 
-zx_status_t brcmf_attach(struct brcmf_device* dev, struct brcmf_mp_device* settings) {
+zx_status_t brcmf_attach(brcmf_pub* drvr, brcmf_bus* bus_if, brcmf_mp_device* settings) {
   zx_status_t ret = ZX_OK;
-  int i;
 
   BRCMF_DBG(TRACE, "Enter\n");
 
-  /* Allocate primary brcmf_info */
-  auto drvr = std::make_unique<struct brcmf_pub>();
-  if (!drvr) {
-    return ZX_ERR_NO_MEMORY;
-  }
-
-  for (i = 0; i < (int)countof(drvr->if2bss); i++) {
-    drvr->if2bss[i] = BRCMF_BSSIDX_INVALID;
-  }
-
-  mtx_init(&drvr->proto_block, mtx_plain);
-
   /* Link to bus module */
   drvr->hdrlen = 0;
-  drvr->bus_if = dev_to_bus(dev);
+  drvr->bus_if = bus_if;
   drvr->settings = settings;
 
   /* Attach and link in the protocol */
-  ret = brcmf_proto_attach(drvr.get());
+  ret = brcmf_proto_attach(drvr);
   if (ret != ZX_OK) {
     BRCMF_ERR("brcmf_prot_attach failed\n");
     goto fail;
   }
 
   /* attach firmware event handler */
-  brcmf_fweh_attach(drvr.get());
-
-  drvr->bus_if->drvr = std::move(drvr);
+  brcmf_fweh_attach(drvr);
   return ret;
 
 fail:
-  brcmf_detach(dev);
+  brcmf_detach(drvr);
 
   return ret;
 }
 
-zx_status_t brcmf_bus_started(struct brcmf_device* dev) {
+zx_status_t brcmf_bus_started(brcmf_pub* drvr) {
   zx_status_t ret = ZX_ERR_IO;
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
-  struct brcmf_pub* drvr = bus_if->drvr.get();
+  struct brcmf_bus* bus_if = drvr->bus_if;
   struct brcmf_if* ifp;
   struct brcmf_if* p2p_ifp;
   zx_status_t err;
@@ -871,7 +844,7 @@
 
   brcmf_proto_add_if(drvr, ifp);
 
-  drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev, drvr->settings->p2p_enable);
+  drvr->config = brcmf_cfg80211_attach(drvr);
   if (drvr->config == NULL) {
     ret = ZX_ERR_IO;
     goto fail;
@@ -911,19 +884,13 @@
   return ret;
 }
 
-void brcmf_bus_add_txhdrlen(struct brcmf_device* dev, uint len) {
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
-  struct brcmf_pub* drvr = bus_if->drvr.get();
-
+void brcmf_bus_add_txhdrlen(brcmf_pub* drvr, uint len) {
   if (drvr) {
     drvr->hdrlen += len;
   }
 }
 
-void brcmf_dev_reset(struct brcmf_device* dev) {
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
-  struct brcmf_pub* drvr = bus_if->drvr.get();
-
+void brcmf_dev_reset(brcmf_pub* drvr) {
   if (drvr == NULL) {
     return;
   }
@@ -933,11 +900,8 @@
   }
 }
 
-void brcmf_detach(struct brcmf_device* dev) {
+void brcmf_detach(brcmf_pub* drvr) {
   int32_t i;
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
-  struct brcmf_pub* drvr = bus_if->drvr.get();
-
   BRCMF_DBG(TRACE, "Enter\n");
 
   if (drvr == NULL) {
@@ -947,7 +911,7 @@
   /* stop firmware event handling */
   brcmf_fweh_detach(drvr);
 
-  brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN);
+  brcmf_bus_change_state(drvr->bus_if, BRCMF_BUS_DOWN);
 
   /* make sure primary interface removed last */
   for (i = BRCMF_MAX_IFS - 1; i > -1; i--) {
@@ -959,14 +923,11 @@
   brcmf_bus_stop(drvr->bus_if);
 
   brcmf_proto_detach(drvr);
-
-  bus_if->drvr.reset();
 }
 
-zx_status_t brcmf_iovar_data_set(struct brcmf_device* dev, const char* name, void* data,
+zx_status_t brcmf_iovar_data_set(brcmf_pub* drvr, const char* name, void* data,
                                  uint32_t len, int32_t* fwerr_ptr) {
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
-  struct brcmf_if* ifp = bus_if->drvr->iflist[0];
+  struct brcmf_if* ifp = drvr->iflist[0];
 
   return brcmf_fil_iovar_data_set(ifp, name, data, len, fwerr_ptr);
 }
@@ -1012,13 +973,14 @@
 }
 
 void brcmf_bus_change_state(struct brcmf_bus* bus, enum brcmf_bus_state state) {
-  struct brcmf_pub* drvr = bus->drvr.get();
-  struct net_device* ndev;
-  int ifidx;
-
   BRCMF_DBG(TRACE, "%d -> %d\n", bus->state, state);
   bus->state = state;
 
+#if 0
+  struct brcmf_pub* drvr = bus->priv.sdio->drvr.get();
+  struct net_device* ndev;
+  int ifidx;
+
   if (state == BRCMF_BUS_UP) {
     for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) {
       if ((drvr->iflist[ifidx]) && (drvr->iflist[ifidx]->ndev)) {
@@ -1029,23 +991,5 @@
       }
     }
   }
+#endif
 }
-
-zx_status_t brcmf_core_init(struct brcmf_device* device) {
-  pthread_mutexattr_t pmutex_attributes;
-  zx_status_t result;
-
-  BRCMF_DBG(TEMP, "brcmfmac: core_init was called\n");
-
-  pthread_mutexattr_init(&pmutex_attributes);
-  pthread_mutexattr_settype(&pmutex_attributes, PTHREAD_MUTEX_NORMAL | PTHREAD_MUTEX_RECURSIVE);
-  pthread_mutex_init(&irq_callback_lock, &pmutex_attributes);
-
-  result = brcmf_bus_register(device);
-  if (result != ZX_OK) {
-    BRCMF_ERR("Bus registration failed: %s\n", zx_status_get_string(result));
-  }
-  return result;
-}
-
-void brcmf_core_exit(struct brcmf_device* device) { brcmf_bus_exit(device); }
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/core.h b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/core.h
index b4e941c..d44a9ae 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/core.h
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/core.h
@@ -23,14 +23,16 @@
 
 #include <threads.h>
 #include <atomic>
+#include <mutex>
 
 #include <ddk/protocol/ethernet.h>
 #include <ddk/protocol/wlanphyimpl.h>
+#include <lib/async/dispatcher.h>
 #include <netinet/if_ether.h>
 #include <lib/sync/completion.h>
 #include <wlan/protocol/if-impl.h>
 
-#include "device.h"
+#include "bus.h"
 #include "fweh.h"
 #include "linuxisms.h"
 #include "netbuf.h"
@@ -39,6 +41,8 @@
 #define TOE_TX_CSUM_OL 0x00000001
 #define TOE_RX_CSUM_OL 0x00000002
 
+#define BRCMF_BSSIDX_INVALID -1
+
 /* For supporting multiple interfaces */
 #define BRCMF_MAX_IFS 16
 
@@ -125,6 +129,12 @@
 
 /* Common structure for module and instance linkage */
 struct brcmf_pub {
+  zx_device_t* zxdev;
+  zx_device_t* phy_zxdev;
+  zx_device_t* if_zxdev;
+  async_dispatcher_t* dispatcher;
+  std::recursive_mutex irq_callback_lock;
+
   /* Linkage ponters */
   struct brcmf_bus* bus_if;
   struct brcmf_proto* proto;
@@ -142,7 +152,7 @@
   struct brcmf_if* iflist[BRCMF_MAX_IFS];
   int32_t if2bss[BRCMF_MAX_IFS];
 
-  mtx_t proto_block;
+  std::mutex proto_block;
   unsigned char proto_buf[BRCMF_DCMD_MAXLEN];
 
   struct brcmf_fweh_info fweh;
@@ -240,8 +250,6 @@
 void brcmf_txfinalize(struct brcmf_if* ifp, struct brcmf_netbuf* txp, bool success);
 void brcmf_netif_rx(struct brcmf_if* ifp, struct brcmf_netbuf* netbuf);
 void brcmf_net_setcarrier(struct brcmf_if* ifp, bool on);
-zx_status_t brcmf_core_init(struct brcmf_device* dev);
-void brcmf_core_exit(struct brcmf_device* dev);
 
 // Used in net_device.flags to indicate interface is up.
 #define IFF_UP 1
@@ -281,4 +289,28 @@
 zx_status_t brcmf_phy_destroy_iface(void* ctx, uint16_t id);
 zx_status_t brcmf_phy_set_country(void* ctx, const wlanphy_country_t* country);
 
+/*
+ * interface functions from common layer
+ */
+
+/* Receive frame for delivery to OS.  Callee disposes of rxp. */
+void brcmf_rx_frame(brcmf_pub* drvr, brcmf_netbuf* rxp, bool handle_event);
+/* Receive async event packet from firmware. Callee disposes of rxp. */
+void brcmf_rx_event(brcmf_pub* drvr, brcmf_netbuf* rxp);
+
+/* Indication from bus module regarding presence/insertion of dongle. */
+zx_status_t brcmf_attach(brcmf_pub* drvr, brcmf_bus* bus_if, brcmf_mp_device* settings);
+/* Indication from bus module regarding removal/absence of dongle */
+void brcmf_detach(brcmf_pub* drvr);
+/* Indication from bus module that dongle should be reset */
+void brcmf_dev_reset(brcmf_pub* drvr);
+
+/* Configure the "global" bus state used by upper layers */
+void brcmf_bus_change_state(brcmf_bus* bus, enum brcmf_bus_state state);
+
+zx_status_t brcmf_bus_started(brcmf_pub* drvr);
+zx_status_t brcmf_iovar_data_set(brcmf_pub* drvr, const char* name, void* data, uint32_t len,
+                                 int32_t* fwerr_ptr);
+void brcmf_bus_add_txhdrlen(brcmf_pub* drvr, uint len);
+
 #endif  // SRC_CONNECTIVITY_WLAN_DRIVERS_THIRD_PARTY_BROADCOM_BRCMFMAC_CORE_H_
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/device.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/device.cc
index 7d7eef9..9bbeb52 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/device.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/device.cc
@@ -1,71 +1,110 @@
-/*
- * Copyright (c) 2018 The Fuchsia Authors
- *
- * 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.
- */
+// Copyright (c) 2019 The Fuchsia Authors
+//
+// 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 "device.h"
+#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/device.h"
 
-#include <lib/async/time.h>  // for async_now()
+#include <zircon/status.h>
 
-#include "debug.h"
+#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/debug.h"
 
-pthread_mutex_t irq_callback_lock;
+namespace wlan {
+namespace brcmfmac {
 
-async_dispatcher_t* default_dispatcher;
+// static
+zx_status_t Device::Create(zx_device_t* parent_device, Device** device_out) {
+  zx_status_t status = ZX_OK;
 
-static void brcmf_timer_handler(async_dispatcher_t* dispatcher, async_task_t* task,
-                                zx_status_t status) {
-  if (status != ZX_OK) {
-    return;
+  auto dispatcher = std::make_unique<::async::Loop>(&kAsyncLoopConfigNoAttachToThread);
+  if ((status = dispatcher->StartThread("brcmfmac-worker", nullptr)) != ZX_OK) {
+    return status;
   }
-  brcmf_timer_info_t* timer = containerof(task, brcmf_timer_info_t, task);
-  timer->callback_function(timer->data);
-  mtx_lock(&timer->lock);
-  timer->scheduled = false;
-  sync_completion_signal(&timer->finished);
-  mtx_unlock(&timer->lock);
-}
 
-void brcmf_timer_init(brcmf_timer_info_t* timer, brcmf_timer_callback_t* callback, void* data) {
-  memset(&timer->task.state, 0, sizeof(timer->task.state));
-  timer->task.handler = brcmf_timer_handler;
-  timer->data = data;
-  timer->callback_function = callback;
-  timer->finished = {};
-  timer->scheduled = false;
-  mtx_init(&timer->lock, mtx_plain);
-}
-
-void brcmf_timer_set(brcmf_timer_info_t* timer, zx_duration_t delay) {
-  mtx_lock(&timer->lock);
-  async_cancel_task(default_dispatcher, &timer->task);  // Make sure it's not scheduled
-  timer->task.deadline = delay + async_now(default_dispatcher);
-  timer->scheduled = true;
-  sync_completion_reset(&timer->finished);
-  async_post_task(default_dispatcher, &timer->task);
-  mtx_unlock(&timer->lock);
-}
-
-void brcmf_timer_stop(brcmf_timer_info_t* timer) {
-  mtx_lock(&timer->lock);
-  if (!timer->scheduled) {
-    mtx_unlock(&timer->lock);
-    return;
+  const auto ddk_remover = [](Device* device) { device->DdkRemove(); };
+  std::unique_ptr<Device, decltype(ddk_remover)> device(new Device(parent_device), ddk_remover);
+  if ((status = device->DdkAdd("brcmfmac-wlanphy", DEVICE_ADD_INVISIBLE)) != ZX_OK) {
+    delete device.release();
+    return status;
   }
-  zx_status_t result = async_cancel_task(default_dispatcher, &timer->task);
-  mtx_unlock(&timer->lock);
-  if (result != ZX_OK) {
-    sync_completion_wait(&timer->finished, ZX_TIME_INFINITE);
+
+  auto pub = std::make_unique<brcmf_pub>();
+  pub->zxdev = parent_device;
+  pub->phy_zxdev = device->zxdev();
+  pub->dispatcher = dispatcher->dispatcher();
+  for (auto& entry : pub->if2bss) {
+    entry = BRCMF_BSSIDX_INVALID;
+  }
+
+  std::unique_ptr<brcmf_bus> bus;
+  if ((status = brcmf_bus_register(pub.get(), &bus)) != ZX_OK) {
+    BRCMF_ERR("brcmf_bus_register() returned %s\n", zx_status_get_string(status));
+    return status;
+  }
+
+  device->dispatcher_ = std::move(dispatcher);
+  device->brcmf_pub_ = std::move(pub);
+  device->brcmf_bus_ = std::move(bus);
+  device->DdkMakeVisible();
+
+  if (device_out != nullptr) {
+    *device_out = device.get();
+  }
+  device.release();  // This now has its lifecycle managed by the devhost.
+  return ZX_OK;
+}
+
+void Device::SimUnbind() {
+  // Simulate the devhost unbind/remove process.
+  DdkUnbind();
+
+  // device_remove() on this device causes the equivalent of Unbind() to be invoked on its children.
+  brcmf_phy_destroy_iface(brcmf_pub_->iflist[0], 0);
+
+  // Now this device is released.
+  DdkRelease();
+}
+
+void Device::DdkUnbind() { DdkRemove(); }
+
+void Device::DdkRelease() { delete this; }
+
+zx_status_t Device::WlanphyImplQuery(wlanphy_impl_info_t* out_info) {
+  return brcmf_phy_query(brcmf_pub_->iflist[0], out_info);
+}
+
+zx_status_t Device::WlanphyImplCreateIface(const wlanphy_impl_create_iface_req_t* req,
+                                           uint16_t* out_iface_id) {
+  return brcmf_phy_create_iface(brcmf_pub_->iflist[0], req, out_iface_id);
+}
+
+zx_status_t Device::WlanphyImplDestroyIface(uint16_t iface_id) {
+  return brcmf_phy_destroy_iface(brcmf_pub_->iflist[0], iface_id);
+}
+
+zx_status_t Device::WlanphyImplSetCountry(const wlanphy_country_t* country) {
+  return brcmf_phy_set_country(brcmf_pub_->iflist[0], country);
+}
+
+Device::Device(zx_device_t* parent) : ::ddk::Device<Device, ::ddk::Unbindable>(parent) {}
+
+Device::~Device() {
+  // Make sure any asynchronous work is stopped first.
+  if (dispatcher_ != nullptr) {
+    dispatcher_->Shutdown();
+  }
+  if (brcmf_bus_ != nullptr) {
+    brcmf_bus_exit(brcmf_bus_.get());
   }
 }
+
+}  // namespace brcmfmac
+}  // namespace wlan
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/device.h b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/device.h
index 88d772c..c57e3df 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/device.h
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/device.h
@@ -1,69 +1,73 @@
-/*
- * Copyright (c) 2018 The Fuchsia Authors
- *
- * 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.
- */
+// Copyright (c) 2019 The Fuchsia Authors
+//
+// 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.
 
 #ifndef SRC_CONNECTIVITY_WLAN_DRIVERS_THIRD_PARTY_BROADCOM_BRCMFMAC_DEVICE_H_
 #define SRC_CONNECTIVITY_WLAN_DRIVERS_THIRD_PARTY_BROADCOM_BRCMFMAC_DEVICE_H_
 
-#include <lib/async-loop/loop.h>  // to start the worker thread
-#include <lib/async/task.h>       // for async_post_task()
-#include <lib/sync/completion.h>
-#include <pthread.h>
+#include <lib/async-loop/cpp/loop.h>
 #include <zircon/types.h>
 
 #include <memory>
 
 #include <ddk/device.h>
+#include <ddktl/device.h>
+#include <ddktl/protocol/wlanphyimpl.h>
 
-extern async_dispatcher_t* default_dispatcher;
+#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/bus.h"
+#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/core.h"
 
-// This is the function that timer users write to receive callbacks.
-typedef void(brcmf_timer_callback_t)(void* data);
+namespace wlan {
+namespace brcmfmac {
 
-typedef struct brcmf_timer_info {
-  async_task_t task;
-  void* data;
-  brcmf_timer_callback_t* callback_function;
-  bool scheduled;
-  sync_completion_t finished;
-  mtx_t lock;
-} brcmf_timer_info_t;
+// This class uses the DDKTL classes to manage the lifetime of a brcmfmac driver instance.
+class Device : public ::ddk::Device<Device, ::ddk::Unbindable>,
+               public ::ddk::WlanphyImplProtocol<Device, ::ddk::base_protocol> {
+ public:
+  // Static factory function for Device instances. This factory does not return an owned instance,
+  // as on successful invocation the instance will have its lifecycle maanged by the devhost.
+  static zx_status_t Create(zx_device_t* parent_device, Device** device_out);
+  explicit Device(std::unique_ptr<::async::Loop> dispatcher, std::unique_ptr<brcmf_pub> brcmf_pub,
+                  std::unique_ptr<brcmf_bus> brcmf_bus);
 
-void brcmf_timer_init(brcmf_timer_info_t* timer, brcmf_timer_callback_t* callback, void* data);
+  Device(const Device& device) = delete;
+  Device& operator=(const Device& other) = delete;
 
-void brcmf_timer_set(brcmf_timer_info_t* timer, zx_duration_t delay);
+  // For testing: unbind this instance explicitly, when no devhost is available. After this call,
+  // the instance is deleted.
+  void SimUnbind();
 
-void brcmf_timer_stop(brcmf_timer_info_t* timer);
+  // DDK interface implementation.
+  void DdkUnbind();
+  void DdkRelease();
 
-struct brcmf_bus;
+  // WlanphyImpl protocol implementation.
+  zx_status_t WlanphyImplQuery(wlanphy_impl_info_t* out_info);
+  zx_status_t WlanphyImplCreateIface(const wlanphy_impl_create_iface_req_t* req,
+                                     uint16_t* out_iface_id);
+  zx_status_t WlanphyImplDestroyIface(uint16_t iface_id);
+  zx_status_t WlanphyImplSetCountry(const wlanphy_country_t* country);
 
-struct brcmf_device {
-  void* of_node;
-  void* parent;
-  std::unique_ptr<struct brcmf_bus> bus;
-  zx_device_t* zxdev;
-  zx_device_t* phy_zxdev;
-  zx_device_t* if_zxdev;
+ protected:
+  explicit Device(zx_device_t* parent);
+  ~Device();
+
+ private:
+  std::unique_ptr<::async::Loop> dispatcher_;
+  std::unique_ptr<brcmf_pub> brcmf_pub_;
+  std::unique_ptr<brcmf_bus> brcmf_bus_;
 };
 
-static inline struct brcmf_bus* dev_to_bus(struct brcmf_device* dev) { return dev->bus.get(); }
-
-// TODO(cphoenix): Wrap around whatever completion functions exist in PCIE and SDIO.
-// TODO(cphoenix): To improve efficiency, analyze which spinlocks only need to protect small
-// critical subsections of the completion functions. For those, bring back the individual spinlock.
-// Note: This is a pthread_mutex_t instead of mtx_t because mtx_t doesn't implement recursive.
-extern pthread_mutex_t irq_callback_lock;
+}  // namespace brcmfmac
+}  // namespace wlan
 
 #endif  // SRC_CONNECTIVITY_WLAN_DRIVERS_THIRD_PARTY_BROADCOM_BRCMFMAC_DEVICE_H_
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/firmware.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/firmware.cc
index 1088509..f6ac568 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/firmware.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/firmware.cc
@@ -22,7 +22,6 @@
 #include "common.h"
 #include "core.h"
 #include "debug.h"
-#include "device.h"
 #include "linuxisms.h"
 
 #define BRCMF_FW_MAX_NVRAM_SIZE 64000
@@ -30,6 +29,8 @@
 #define BRCMF_FW_NVRAM_PCIEDEV_LEN 10 /* pcie/1/4/ + \0 */
 #define BRCMF_FW_DEFAULT_BOARDREV "boardrev=0xff"
 
+constexpr char kDefaultFirmwarePath[] = "brcmfmac/";
+
 enum nvram_parser_state { IDLE, KEY, VALUE, COMMENT, END };
 
 /**
@@ -425,13 +426,13 @@
 void brcmf_fw_nvram_free(void* nvram) { free(nvram); }
 
 struct brcmf_fw {
-  struct brcmf_device* dev;
+  brcmf_pub* drvr;
   uint16_t flags;
   const struct brcmf_firmware* code;
   const char* nvram_name;
   uint16_t domain_nr;
   uint16_t bus_nr;
-  void (*done)(struct brcmf_device* dev, zx_status_t err, const struct brcmf_firmware* fw,
+  void (*done)(brcmf_pub* drvr, zx_status_t err, const struct brcmf_firmware* fw,
                void* nvram_image, uint32_t nvram_len);
 };
 
@@ -443,7 +444,7 @@
   size_t data_len;
   bool raw_nvram;
 
-  BRCMF_DBG(TRACE, "enter: dev=%s\n", device_get_name(fwctx->dev->zxdev));
+  BRCMF_DBG(TRACE, "enter: dev=%s\n", device_get_name(fwctx->drvr->zxdev));
   if (fw && fw->data) {
     data = (uint8_t*)fw->data;
     data_len = fw->size;
@@ -466,26 +467,24 @@
     goto fail;
   }
 
-  fwctx->done(fwctx->dev, ZX_OK, fwctx->code, nvram, nvram_length);
+  fwctx->done(fwctx->drvr, ZX_OK, fwctx->code, nvram, nvram_length);
   free(fwctx);
   return ZX_OK;
 
 fail:
-  BRCMF_DBG(TRACE, "failed: dev=%s\n", device_get_name(fwctx->dev->zxdev));
-  fwctx->done(fwctx->dev, ZX_ERR_NOT_FOUND, NULL, NULL, 0);
+  BRCMF_DBG(TRACE, "failed: dev=%s\n", device_get_name(fwctx->drvr->zxdev));
+  fwctx->done(fwctx->drvr, ZX_ERR_NOT_FOUND, NULL, NULL, 0);
   free(fwctx);
   return ZX_ERR_NO_RESOURCES;
 }
 
-zx_status_t request_firmware_nowait(bool b, const char* name, struct brcmf_device* dev,
-                                    struct brcmf_fw* ctx,
-                                    zx_status_t (*callback)(const struct brcmf_firmware* fw,
-                                                            void* ctx)) {
+zx_status_t request_firmware_nowait(bool b, const char* name, brcmf_pub* drvr, brcmf_fw* ctx,
+                                    zx_status_t (*callback)(const brcmf_firmware* fw, void* ctx)) {
   zx_status_t result;
   zx_handle_t fw_vmo;
   struct brcmf_firmware fw;
 
-  result = load_firmware(dev->zxdev, name, &fw_vmo, &fw.size);
+  result = load_firmware(drvr->zxdev, name, &fw_vmo, &fw.size);
   BRCMF_DBG(TEMP, "load_firmware of '%s' -> ret %d, size %ld", name, result, fw.size);
   if (result != ZX_OK) {
     return result;
@@ -514,7 +513,7 @@
   struct brcmf_fw* fwctx = static_cast<decltype(fwctx)>(ctx);
   zx_status_t result = ZX_OK;
 
-  BRCMF_DBG(TRACE, "enter: dev=%s\n", device_get_name(fwctx->dev->zxdev));
+  BRCMF_DBG(TRACE, "enter: dev=%s\n", device_get_name(fwctx->drvr->zxdev));
   if (!fw) {
     result = ZX_ERR_INVALID_ARGS;
     goto fail;
@@ -525,7 +524,7 @@
   }
 
   fwctx->code = fw;
-  result = request_firmware_nowait(true, fwctx->nvram_name, fwctx->dev, fwctx,
+  result = request_firmware_nowait(true, fwctx->nvram_name, fwctx->drvr, fwctx,
                                    brcmf_fw_request_nvram_done);
 
   /* pass NULL to nvram callback for bcm47xx fallback */
@@ -535,22 +534,22 @@
   return result;
 
 fail:
-  BRCMF_DBG(TRACE, "failed: dev=%s\n", device_get_name(fwctx->dev->zxdev));
+  BRCMF_DBG(TRACE, "failed: dev=%s\n", device_get_name(fwctx->drvr->zxdev));
 done:
-  fwctx->done(fwctx->dev, result, fw, NULL, 0);
+  fwctx->done(fwctx->drvr, result, fw, NULL, 0);
   free(fwctx);
   return result;
 }
 
-zx_status_t brcmf_fw_get_firmwares_pcie(struct brcmf_device* dev, uint16_t flags, const char* code,
+zx_status_t brcmf_fw_get_firmwares_pcie(brcmf_pub* drvr, uint16_t flags, const char* code,
                                         const char* nvram,
-                                        void (*fw_cb)(struct brcmf_device* dev, zx_status_t err,
-                                                      const struct brcmf_firmware* fw,
-                                                      void* nvram_image, uint32_t nvram_len),
+                                        void (*fw_cb)(brcmf_pub* drvr, zx_status_t err,
+                                                      const brcmf_firmware* fw, void* nvram_image,
+                                                      uint32_t nvram_len),
                                         uint16_t domain_nr, uint16_t bus_nr) {
   struct brcmf_fw* fwctx;
 
-  BRCMF_DBG(TRACE, "enter: dev=%s\n", device_get_name(dev->zxdev));
+  BRCMF_DBG(TRACE, "enter: dev=%s\n", device_get_name(drvr->zxdev));
   if (!fw_cb || !code) {
     return ZX_ERR_INVALID_ARGS;
   }
@@ -564,7 +563,7 @@
     return ZX_ERR_NO_MEMORY;
   }
 
-  fwctx->dev = dev;
+  fwctx->drvr = drvr;
   fwctx->flags = flags;
   fwctx->done = fw_cb;
   if (flags & BRCMF_FW_REQUEST_NVRAM) {
@@ -573,15 +572,15 @@
   fwctx->domain_nr = domain_nr;
   fwctx->bus_nr = bus_nr;
 
-  return request_firmware_nowait(true, code, dev, fwctx, brcmf_fw_request_code_done);
+  return request_firmware_nowait(true, code, drvr, fwctx, brcmf_fw_request_code_done);
 }
 
-zx_status_t brcmf_fw_get_firmwares(struct brcmf_device* dev, uint16_t flags, const char* code,
+zx_status_t brcmf_fw_get_firmwares(brcmf_pub* drvr, uint16_t flags, const char* code,
                                    const char* nvram,
-                                   void (*fw_cb)(struct brcmf_device* dev, zx_status_t err,
-                                                 const struct brcmf_firmware* fw, void* nvram_image,
+                                   void (*fw_cb)(brcmf_pub* drvr, zx_status_t err,
+                                                 const brcmf_firmware* fw, void* nvram_image,
                                                  uint32_t nvram_len)) {
-  return brcmf_fw_get_firmwares_pcie(dev, flags, code, nvram, fw_cb, 0, 0);
+  return brcmf_fw_get_firmwares_pcie(drvr, flags, code, nvram, fw_cb, 0, 0);
 }
 
 zx_status_t brcmf_fw_map_chip_to_name(uint32_t chip, uint32_t chiprev,
@@ -603,13 +602,13 @@
   }
 
   /* check if firmware path is provided by module parameter */
-  if (brcmf_mp_global.firmware_path[0] != '\0') {
-    strlcpy(fw_name, brcmf_mp_global.firmware_path, BRCMF_FW_NAME_LEN);
+  if (kDefaultFirmwarePath[0] != '\0') {
+    strlcpy(fw_name, kDefaultFirmwarePath, BRCMF_FW_NAME_LEN);
     if ((nvram_name) && (mapping_table[i].nvram)) {
-      strlcpy(nvram_name, brcmf_mp_global.firmware_path, BRCMF_FW_NAME_LEN);
+      strlcpy(nvram_name, kDefaultFirmwarePath, BRCMF_FW_NAME_LEN);
     }
 
-    end = brcmf_mp_global.firmware_path[strlen(brcmf_mp_global.firmware_path) - 1];
+    end = kDefaultFirmwarePath[strlen(kDefaultFirmwarePath) - 1];
     if (end != '/') {
       strlcat(fw_name, "/", BRCMF_FW_NAME_LEN);
       if ((nvram_name) && (mapping_table[i].nvram)) {
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/firmware.h b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/firmware.h
index 7ffa723..c83b801 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/firmware.h
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/firmware.h
@@ -17,7 +17,6 @@
 #define SRC_CONNECTIVITY_WLAN_DRIVERS_THIRD_PARTY_BROADCOM_BRCMFMAC_FIRMWARE_H_
 
 #include "common.h"
-#include "device.h"
 #include "linuxisms.h"
 
 // clang-format off
@@ -78,16 +77,16 @@
  * fails it will not use the callback, but call device_release_driver()
  * instead which will call the driver .remove() callback.
  */
-zx_status_t brcmf_fw_get_firmwares_pcie(struct brcmf_device* dev, uint16_t flags, const char* code,
+zx_status_t brcmf_fw_get_firmwares_pcie(brcmf_pub* drvr, uint16_t flags, const char* code,
                                         const char* nvram,
-                                        void (*fw_cb)(struct brcmf_device* dev, zx_status_t err,
-                                                      const struct brcmf_firmware* fw,
-                                                      void* nvram_image, uint32_t nvram_len),
+                                        void (*fw_cb)(brcmf_pub* drvr, zx_status_t err,
+                                                      const brcmf_firmware* fw, void* nvram_image,
+                                                      uint32_t nvram_len),
                                         uint16_t domain_nr, uint16_t bus_nr);
-zx_status_t brcmf_fw_get_firmwares(struct brcmf_device* dev, uint16_t flags, const char* code,
+zx_status_t brcmf_fw_get_firmwares(brcmf_pub* drvr, uint16_t flags, const char* code,
                                    const char* nvram,
-                                   void (*fw_cb)(struct brcmf_device* dev, zx_status_t err,
-                                                 const struct brcmf_firmware* fw, void* nvram_image,
+                                   void (*fw_cb)(brcmf_pub* drvr, zx_status_t err,
+                                                 const brcmf_firmware* fw, void* nvram_image,
                                                  uint32_t nvram_len));
 
 #endif  // SRC_CONNECTIVITY_WLAN_DRIVERS_THIRD_PARTY_BROADCOM_BRCMFMAC_FIRMWARE_H_
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/fweh.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/fweh.cc
index 033c3654..7b5d4f6 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/fweh.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/fweh.cc
@@ -24,7 +24,6 @@
 #include "cfg80211.h"
 #include "core.h"
 #include "debug.h"
-#include "device.h"
 #include "fwil.h"
 #include "linuxisms.h"
 #include "proto.h"
@@ -89,13 +88,14 @@
  * @fweh: firmware event handling info.
  * @event: event queue entry.
  */
-static void brcmf_fweh_queue_event(struct brcmf_fweh_info* fweh,
-                                   struct brcmf_fweh_queue_item* event) {
+static void brcmf_fweh_queue_event(brcmf_pub* drvr,
+                                   brcmf_fweh_info* fweh,
+                                   brcmf_fweh_queue_item* event) {
   // spin_lock_irqsave(&fweh->evt_q_lock, flags);
-  pthread_mutex_lock(&irq_callback_lock);
+  drvr->irq_callback_lock.lock();
   list_add_tail(&fweh->event_q, &event->q);
   // spin_unlock_irqrestore(&fweh->evt_q_lock, flags);
-  pthread_mutex_unlock(&irq_callback_lock);
+  drvr->irq_callback_lock.unlock();
   workqueue_schedule_default(&fweh->event_work);
 }
 
@@ -194,17 +194,18 @@
  *
  * @fweh: firmware event handling info.
  */
-static struct brcmf_fweh_queue_item* brcmf_fweh_dequeue_event(struct brcmf_fweh_info* fweh) {
+static struct brcmf_fweh_queue_item* brcmf_fweh_dequeue_event(brcmf_pub* drvr,
+                                                              brcmf_fweh_info* fweh) {
   struct brcmf_fweh_queue_item* event = NULL;
 
   // spin_lock_irqsave(&fweh->evt_q_lock, flags);
-  pthread_mutex_lock(&irq_callback_lock);
+  drvr->irq_callback_lock.lock();
   if (!list_is_empty(&fweh->event_q)) {
     event = list_peek_head_type(&fweh->event_q, struct brcmf_fweh_queue_item, q);
     list_delete(&event->q);
   }
   // spin_unlock_irqrestore(&fweh->evt_q_lock, flags);
-  pthread_mutex_unlock(&irq_callback_lock);
+  drvr->irq_callback_lock.unlock();
 
   return event;
 }
@@ -226,7 +227,7 @@
   fweh = containerof(work, struct brcmf_fweh_info, event_work);
   drvr = containerof(fweh, struct brcmf_pub, fweh);
 
-  while ((event = brcmf_fweh_dequeue_event(fweh))) {
+  while ((event = brcmf_fweh_dequeue_event(drvr, fweh))) {
     BRCMF_DBG(EVENT, "event %s (%u) ifidx %u bsscfg %u addr %pM\n",
               brcmf_fweh_event_name(event->code), event->code, event->emsg.ifidx,
               event->emsg.bsscfgidx, event->emsg.addr);
@@ -429,5 +430,5 @@
   memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN);
 
   // BRCMF_DBG(TEMP, "Queueing event!");
-  brcmf_fweh_queue_event(fweh, event);
+  brcmf_fweh_queue_event(drvr, fweh, event);
 }
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/fwil.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/fwil.cc
index ead38af..6ed30e6 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/fwil.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/fwil.cc
@@ -142,13 +142,13 @@
                                    int32_t* fwerr_ptr) {
   zx_status_t err;
 
-  mtx_lock(&ifp->drvr->proto_block);
+  ifp->drvr->proto_block.lock();
 
   BRCMF_DBG(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
   BRCMF_DBG_HEX_DUMP(BRCMF_IS_ON(FIL), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 
   err = brcmf_fil_cmd_data(ifp, cmd, data, len, true, fwerr_ptr);
-  mtx_unlock(&ifp->drvr->proto_block);
+  ifp->drvr->proto_block.unlock();
 
   return err;
 }
@@ -157,13 +157,13 @@
                                    int32_t* fwerr_ptr) {
   zx_status_t err;
 
-  mtx_lock(&ifp->drvr->proto_block);
+  ifp->drvr->proto_block.lock();
   err = brcmf_fil_cmd_data(ifp, cmd, data, len, false, fwerr_ptr);
 
   BRCMF_DBG(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
   BRCMF_DBG_HEX_DUMP(BRCMF_IS_ON(FIL), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 
-  mtx_unlock(&ifp->drvr->proto_block);
+  ifp->drvr->proto_block.unlock();
 
   return err;
 }
@@ -173,10 +173,10 @@
   zx_status_t err;
   uint32_t data_le = data;
 
-  mtx_lock(&ifp->drvr->proto_block);
+  ifp->drvr->proto_block.lock();
   BRCMF_DBG(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, data);
   err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true, fwerr_ptr);
-  mtx_unlock(&ifp->drvr->proto_block);
+  ifp->drvr->proto_block.unlock();
 
   return err;
 }
@@ -186,9 +186,9 @@
   zx_status_t err;
   uint32_t data_le = *data;
 
-  mtx_lock(&ifp->drvr->proto_block);
+  ifp->drvr->proto_block.lock();
   err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false, fwerr_ptr);
-  mtx_unlock(&ifp->drvr->proto_block);
+  ifp->drvr->proto_block.unlock();
   *data = data_le;
   BRCMF_DBG(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, *data);
 
@@ -221,7 +221,7 @@
   zx_status_t err;
   uint32_t buflen;
 
-  mtx_lock(&drvr->proto_block);
+  drvr->proto_block.lock();
 
   BRCMF_DBG(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
   BRCMF_DBG_HEX_DUMP(BRCMF_IS_ON(FIL), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
@@ -234,7 +234,7 @@
     BRCMF_ERR("Creating iovar failed\n");
   }
 
-  mtx_unlock(&drvr->proto_block);
+  drvr->proto_block.unlock();
   return err;
 }
 
@@ -244,7 +244,7 @@
   zx_status_t err;
   uint32_t buflen;
 
-  mtx_lock(&drvr->proto_block);
+  drvr->proto_block.lock();
 
   buflen = brcmf_create_iovar(name, data, len, (char*)drvr->proto_buf, sizeof(drvr->proto_buf));
   if (buflen) {
@@ -260,7 +260,7 @@
   BRCMF_DBG(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
   BRCMF_DBG_HEX_DUMP(BRCMF_IS_ON(FIL), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 
-  mtx_unlock(&drvr->proto_block);
+  drvr->proto_block.unlock();
   return err;
 }
 
@@ -334,7 +334,7 @@
   zx_status_t err;
   uint32_t buflen;
 
-  mtx_lock(&drvr->proto_block);
+  drvr->proto_block.lock();
 
   BRCMF_DBG(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx, ifp->bsscfgidx, name,
             len);
@@ -349,7 +349,7 @@
     BRCMF_ERR("Creating bsscfg failed\n");
   }
 
-  mtx_unlock(&drvr->proto_block);
+  drvr->proto_block.unlock();
   return err;
 }
 
@@ -359,7 +359,7 @@
   zx_status_t err;
   uint32_t buflen;
 
-  mtx_lock(&drvr->proto_block);
+  drvr->proto_block.lock();
 
   buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len, (char*)drvr->proto_buf,
                                sizeof(drvr->proto_buf));
@@ -376,7 +376,7 @@
             len);
   BRCMF_DBG_HEX_DUMP(BRCMF_IS_ON(FIL), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 
-  mtx_unlock(&drvr->proto_block);
+  drvr->proto_block.unlock();
   return err;
 }
 
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/fwsignal.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/fwsignal.cc
index 37db696..4e6ac61 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/fwsignal.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/fwsignal.cc
@@ -26,7 +26,6 @@
 #include "common.h"
 #include "core.h"
 #include "debug.h"
-#include "device.h"
 #include "fweh.h"
 #include "fwil.h"
 #include "fwil_types.h"
@@ -545,12 +544,12 @@
 
 static void brcmf_fws_lock(struct brcmf_fws_info* fws) {  // __TA_ACQUIRE(&fws->spinlock) {
   // spin_lock_irqsave(&fws->spinlock, fws->flags);
-  pthread_mutex_lock(&irq_callback_lock);
+  fws->drvr->irq_callback_lock.lock();
 }
 
 static void brcmf_fws_unlock(struct brcmf_fws_info* fws) {  // __TA_RELEASE(&fws->spinlock) {
   // spin_unlock_irqrestore(&fws->spinlock, fws->flags);
-  pthread_mutex_unlock(&irq_callback_lock);
+  fws->drvr->irq_callback_lock.unlock();
 }
 
 static bool brcmf_fws_ifidx_match(struct brcmf_netbuf* netbuf, void* arg) {
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/linuxisms.h b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/linuxisms.h
index 6f758bd..c9ddb8f 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/linuxisms.h
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/linuxisms.h
@@ -364,7 +364,6 @@
     const struct wiphy_vendor_command* vendor_commands;
     uint8_t perm_addr[ETH_ALEN];
     struct brcmf_cfg80211_info* cfg80211_info;
-    struct brcmf_device* dev;
 };
 
 struct vif_params {
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/of.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/of.cc
deleted file mode 100644
index 9f46b67..0000000
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/of.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2014 Broadcom Corporation
- *
- * 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 "of.h"
-
-#include "common.h"
-#include "core.h"
-#include "debug.h"
-#include "defs.h"
-#include "device.h"
-#include "linuxisms.h"
-
-void brcmf_of_probe(struct brcmf_device* dev, enum brcmf_bus_type bus_type,
-                    struct brcmf_mp_device* settings) {
-  struct brcmfmac_sdio_pd* sdio = &settings->bus.sdio;
-  struct device_node* np = static_cast<decltype(np)>(dev->of_node);
-  int irq;
-  uint32_t irqf;
-  uint32_t val;
-
-  if (!np || bus_type != BRCMF_BUS_TYPE_SDIO || !of_device_is_compatible(np, "brcm,bcm4329-fmac")) {
-    return;
-  }
-
-  if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) {
-    sdio->drive_strength = val;
-  }
-
-  /* make sure there are interrupts defined in the node */
-  if (!of_find_property(np, "interrupts", NULL)) {
-    return;
-  }
-
-  irq = irq_of_parse_and_map(np, 0);
-  if (!irq) {
-    BRCMF_ERR("interrupt could not be mapped\n");
-    return;
-  }
-  irqf = irqd_get_trigger_type(irq_get_irq_data(irq));
-
-  sdio->oob_irq_supported = true;
-}
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/of.h b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/of.h
deleted file mode 100644
index cf57bd74..0000000
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/of.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (c) 2014 Broadcom Corporation
- *
- * 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.
- */
-
-#ifndef SRC_CONNECTIVITY_WLAN_DRIVERS_THIRD_PARTY_BROADCOM_BRCMFMAC_OF_H_
-#define SRC_CONNECTIVITY_WLAN_DRIVERS_THIRD_PARTY_BROADCOM_BRCMFMAC_OF_H_
-
-#include "common.h"
-#include "device.h"
-
-void brcmf_of_probe(struct brcmf_device* dev, enum brcmf_bus_type bus_type,
-                    struct brcmf_mp_device* settings);
-
-#endif  // SRC_CONNECTIVITY_WLAN_DRIVERS_THIRD_PARTY_BROADCOM_BRCMFMAC_OF_H_
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sdio.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sdio.cc
index a1dc4b2..5e3651e2 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sdio.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sdio.cc
@@ -39,11 +39,11 @@
 #include "core.h"
 #include "debug.h"
 #include "defs.h"
-#include "device.h"
 #include "firmware.h"
 #include "linuxisms.h"
 #include "netbuf.h"
 #include "soc.h"
+#include "timer.h"
 #include "workqueue.h"
 
 #define DCMD_RESP_TIMEOUT_MSEC (2500)
@@ -100,7 +100,6 @@
 #include "bus.h"
 #include "chipcommon.h"
 #include "debug.h"
-#include "device.h"
 
 #define TXQLEN 2048         /* bulk tx queue length */
 #define TXHI (TXQLEN - 256) /* turn on flow control above TXHI */
@@ -1571,9 +1570,9 @@
                          brcmf_netbuf_list_prev(&bus->glom, pfirst));
       brcmf_netbuf_list_remove(&bus->glom, pfirst);
       if (brcmf_sdio_fromevntchan(&dptr[SDPCM_HWHDR_LEN])) {
-        brcmf_rx_event(bus->sdiodev->dev, pfirst);
+        brcmf_rx_event(bus->sdiodev->drvr, pfirst);
       } else {
-        brcmf_rx_frame(bus->sdiodev->dev, pfirst, false);
+        brcmf_rx_frame(bus->sdiodev->drvr, pfirst, false);
       }
       bus->sdcnt.rxglompkts++;
     }
@@ -1671,11 +1670,11 @@
 
   /* Point to valid data and indicate its length */
   // spin_lock_bh(&bus->rxctl_lock);
-  pthread_mutex_lock(&irq_callback_lock);
+  bus->sdiodev->drvr->irq_callback_lock.lock();
   if (bus->rxctl) {
     BRCMF_ERR("last control frame is being processed.\n");
     // spin_unlock_bh(&bus->rxctl_lock);
-    pthread_mutex_unlock(&irq_callback_lock);
+    bus->sdiodev->drvr->irq_callback_lock.unlock();
     free(buf);
     goto done;
   }
@@ -1683,7 +1682,7 @@
   bus->rxctl_orig = buf;
   bus->rxlen = len - doff;
   // spin_unlock_bh(&bus->rxctl_lock);
-  pthread_mutex_unlock(&irq_callback_lock);
+  bus->sdiodev->drvr->irq_callback_lock.unlock();
 
 done:
   /* Awake any waiters */
@@ -1884,9 +1883,9 @@
     if (pkt->len == 0) {
       brcmu_pkt_buf_free_netbuf(pkt);
     } else if (rd->channel == SDPCM_EVENT_CHANNEL) {
-      brcmf_rx_event(bus->sdiodev->dev, pkt);
+      brcmf_rx_event(bus->sdiodev->drvr, pkt);
     } else {
-      brcmf_rx_frame(bus->sdiodev->dev, pkt, false);
+      brcmf_rx_frame(bus->sdiodev->drvr, pkt, false);
     }
 
     /* prepare the descriptor for the next read */
@@ -2056,7 +2055,7 @@
   }
   brcmf_netbuf_list_for_every_safe(pktq, pkt_next, tmp) {
     brcmf_netbuf_list_remove(pktq, pkt_next);
-    brcmf_proto_bcdc_txcomplete(bus->sdiodev->dev, pkt_next, ret == ZX_OK);
+    brcmf_proto_bcdc_txcomplete(bus->sdiodev->drvr, pkt_next, ret == ZX_OK);
   }
   return ret;
 }
@@ -2081,7 +2080,7 @@
     pkt_num = min_t(uint32_t, pkt_num, brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol));
     brcmf_netbuf_list_init(&pktq);
     // spin_lock_bh(&bus->txq_lock);
-    pthread_mutex_lock(&irq_callback_lock);
+    bus->sdiodev->drvr->irq_callback_lock.lock();
     for (i = 0; i < pkt_num; i++) {
       pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, &prec_out);
       if (pkt == NULL) {
@@ -2090,7 +2089,7 @@
       brcmf_netbuf_list_add_tail(&pktq, pkt);
     }
     // spin_unlock_bh(&bus->txq_lock);
-    pthread_mutex_unlock(&irq_callback_lock);
+    bus->sdiodev->drvr->irq_callback_lock.unlock();
     if (i == 0) {
       break;
     }
@@ -2119,7 +2118,7 @@
   /* Deflow-control stack if needed */
   if ((bus->sdiodev->state == BRCMF_SDIOD_DATA) && bus->txoff && (pktq_len(&bus->txq) < TXLOW)) {
     bus->txoff = false;
-    brcmf_proto_bcdc_txflowblock(bus->sdiodev->dev, false);
+    brcmf_proto_bcdc_txflowblock(bus->sdiodev->drvr, false);
   }
 
   return cnt;
@@ -2184,8 +2183,7 @@
   return ret;
 }
 
-static void brcmf_sdio_bus_stop(struct brcmf_device* dev) {
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
+static void brcmf_sdio_bus_stop(brcmf_bus* bus_if) {
   struct brcmf_sdio_dev* sdiodev = bus_if->bus_priv.sdio;
   struct brcmf_sdio* bus = sdiodev->bus;
   struct brcmf_core* core = bus->sdio_core;
@@ -2244,10 +2242,10 @@
 
   /* Clear rx control and wake any waiters */
   // spin_lock_bh(&bus->rxctl_lock);
-  pthread_mutex_lock(&irq_callback_lock);
+  sdiodev->drvr->irq_callback_lock.lock();
   bus->rxlen = 0;
   // spin_unlock_bh(&bus->rxctl_lock);
-  pthread_mutex_unlock(&irq_callback_lock);
+  sdiodev->drvr->irq_callback_lock.unlock();
   // TODO(cphoenix): I think the original Linux code in brcmf_sdio_dcmd_resp_wait() would have
   // gone right back to sleep, since rxlen is 0. In the current code, it will exit;
   // brcmf_sdio_bus_rxctl() will return ZX_ERR_SHOULD_WAIT; the loop in brcmf_proto_bcdc_cmplt
@@ -2436,14 +2434,20 @@
   }
 }
 
-static struct pktq* brcmf_sdio_bus_gettxq(struct brcmf_device* dev) {
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
+static struct pktq* brcmf_sdio_bus_gettxq(brcmf_bus* bus_if) {
   struct brcmf_sdio_dev* sdiodev = bus_if->bus_priv.sdio;
   struct brcmf_sdio* bus = sdiodev->bus;
 
   return &bus->txq;
 }
 
+static void brcmf_sdio_wowl_config(brcmf_bus* bus_if, bool enabled) {
+  struct brcmf_sdio_dev* sdiodev = bus_if->bus_priv.sdio;
+
+  BRCMF_DBG(SDIO, "Configuring WOWL, enabled=%d\n", enabled);
+  sdiodev->wowl_enabled = enabled;
+}
+
 static bool brcmf_sdio_prec_enq(struct pktq* q, struct brcmf_netbuf* pkt, int prec) {
   struct brcmf_netbuf* p;
   int eprec = -1; /* precedence to evict from */
@@ -2489,10 +2493,9 @@
   return p != NULL;
 }
 
-static zx_status_t brcmf_sdio_bus_txdata(struct brcmf_device* dev, struct brcmf_netbuf* pkt) {
+static zx_status_t brcmf_sdio_bus_txdata(brcmf_bus* bus_if, brcmf_netbuf* pkt) {
   zx_status_t ret;
   uint prec;
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
   struct brcmf_sdio_dev* sdiodev = bus_if->bus_priv.sdio;
   struct brcmf_sdio* bus = sdiodev->bus;
 
@@ -2514,7 +2517,7 @@
 
   /* Priority based enq */
   // spin_lock_bh(&bus->txq_lock);
-  pthread_mutex_lock(&irq_callback_lock);
+  sdiodev->drvr->irq_callback_lock.lock();
   /* reset bus_flags in packet workspace */
   *(uint16_t*)(pkt->workspace) = 0;
   if (!brcmf_sdio_prec_enq(&bus->txq, pkt, prec)) {
@@ -2527,10 +2530,10 @@
 
   if (pktq_len(&bus->txq) >= TXHI) {
     bus->txoff = true;
-    brcmf_proto_bcdc_txflowblock(dev, true);
+    brcmf_proto_bcdc_txflowblock(sdiodev->drvr, true);
   }
   // spin_unlock_bh(&bus->txq_lock);
-  pthread_mutex_unlock(&irq_callback_lock);
+  sdiodev->drvr->irq_callback_lock.unlock();
 
 #if !defined(NDEBUG)
   if (pktq_plen(&bus->txq, prec) > qcount[prec]) {
@@ -2630,8 +2633,7 @@
 }
 #endif /* !defined(NDEBUG) */
 
-static zx_status_t brcmf_sdio_bus_txctl(struct brcmf_device* dev, unsigned char* msg, uint msglen) {
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
+static zx_status_t brcmf_sdio_bus_txctl(brcmf_bus* bus_if, unsigned char* msg, uint msglen) {
   struct brcmf_sdio_dev* sdiodev = bus_if->bus_priv.sdio;
   struct brcmf_sdio* bus = sdiodev->bus;
   zx_status_t ret;
@@ -2723,13 +2725,12 @@
 }
 #endif /* !defined(NDEBUG) */
 
-static zx_status_t brcmf_sdio_bus_rxctl(struct brcmf_device* dev, unsigned char* msg, uint msglen,
+static zx_status_t brcmf_sdio_bus_rxctl(brcmf_bus* bus_if, unsigned char* msg, uint msglen,
                                         int* rxlen_out) {
   bool timeout;
   uint rxlen = 0;
   bool pending;
   uint8_t* buf;
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
   struct brcmf_sdio_dev* sdiodev = bus_if->bus_priv.sdio;
   struct brcmf_sdio* bus = sdiodev->bus;
 
@@ -2742,7 +2743,7 @@
   timeout = (brcmf_sdio_dcmd_resp_wait(bus, &pending) != ZX_OK);
 
   // spin_lock_bh(&bus->rxctl_lock);
-  pthread_mutex_lock(&irq_callback_lock);
+  sdiodev->drvr->irq_callback_lock.lock();
   rxlen = bus->rxlen;
   memcpy(msg, bus->rxctl, std::min(msglen, rxlen));
   bus->rxctl = NULL;
@@ -2750,7 +2751,7 @@
   bus->rxctl_orig = NULL;
   bus->rxlen = 0;
   // spin_unlock_bh(&bus->rxctl_lock);
-  pthread_mutex_unlock(&irq_callback_lock);
+  sdiodev->drvr->irq_callback_lock.unlock();
   free(buf);
 
   if (rxlen) {
@@ -2970,8 +2971,7 @@
   return ZX_OK;
 }
 
-static zx_status_t brcmf_sdio_bus_preinit(struct brcmf_device* dev) {
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
+static zx_status_t brcmf_sdio_bus_preinit(brcmf_bus* bus_if) {
   struct brcmf_sdio_dev* sdiodev = bus_if->bus_priv.sdio;
   struct brcmf_sdio* bus = sdiodev->bus;
   struct brcmf_core* core = bus->sdio_core;
@@ -2985,13 +2985,13 @@
   if (core->rev < 12) {
     /* for sdio core rev < 12, disable txgloming */
     value = 0;
-    err = brcmf_iovar_data_set(dev, "bus:txglom", &value, sizeof(uint32_t), nullptr);
+    err = brcmf_iovar_data_set(sdiodev->drvr, "bus:txglom", &value, sizeof(uint32_t), nullptr);
   } else {
     /* otherwise, set txglomalign */
     value = sdiodev->settings->bus.sdio.sd_sgentry_align;
     /* SDIO ADMA requires at least 32 bit alignment */
     value = std::max<uint32_t>(value, DMA_ALIGNMENT);
-    err = brcmf_iovar_data_set(dev, "bus:txglomalign", &value, sizeof(uint32_t), nullptr);
+    err = brcmf_iovar_data_set(sdiodev->drvr, "bus:txglomalign", &value, sizeof(uint32_t), nullptr);
   }
 
   // No support for txglomming, requires SDIO scatter/gather support (see WLAN-882)
@@ -3001,23 +3001,20 @@
   }
 
   bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
-  brcmf_bus_add_txhdrlen(bus->sdiodev->dev, bus->tx_hdrlen);
+  brcmf_bus_add_txhdrlen(sdiodev->drvr, bus->tx_hdrlen);
 
 done:
   return err;
 }
 
-static size_t brcmf_sdio_bus_get_ramsize(struct brcmf_device* dev) {
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
+static size_t brcmf_sdio_bus_get_ramsize(brcmf_bus* bus_if) {
   struct brcmf_sdio_dev* sdiodev = bus_if->bus_priv.sdio;
   struct brcmf_sdio* bus = sdiodev->bus;
 
   return bus->ci->ramsize - bus->ci->srsize;
 }
 
-static zx_status_t brcmf_sdio_bus_get_memdump(struct brcmf_device* dev, void* data,
-                                              size_t mem_size) {
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
+static zx_status_t brcmf_sdio_bus_get_memdump(brcmf_bus* bus_if, void* data, size_t mem_size) {
   struct brcmf_sdio_dev* sdiodev = bus_if->bus_priv.sdio;
   struct brcmf_sdio* bus = sdiodev->bus;
   zx_status_t err;
@@ -3455,8 +3452,7 @@
     goto fail;
   }
 
-  sdiodev->settings =
-      brcmf_get_module_param(sdiodev->dev, BRCMF_BUS_TYPE_SDIO, bus->ci->chip, bus->ci->chiprev);
+  sdiodev->settings = brcmf_get_module_param(BRCMF_BUS_TYPE_SDIO, bus->ci->chip, bus->ci->chiprev);
   if (!sdiodev->settings) {
     BRCMF_ERR("Failed to get device parameters\n");
     err = ZX_ERR_INTERNAL;
@@ -3573,8 +3569,8 @@
 }
 
 static void brcmf_sdio_watchdog(void* data) {
-  pthread_mutex_lock(&irq_callback_lock);
   struct brcmf_sdio* bus = static_cast<decltype(bus)>(data);
+  bus->sdiodev->drvr->irq_callback_lock.lock();
 
   if (bus->watchdog_tsk) {
     // Currently signaling watchdog_wait does nothing; brcmf_sdio_watchdog_thread() will
@@ -3589,12 +3585,11 @@
       brcmf_timer_set(&bus->timer, ZX_MSEC(BRCMF_WD_POLL_MSEC));
     }
   }
-  pthread_mutex_unlock(&irq_callback_lock);
+  bus->sdiodev->drvr->irq_callback_lock.unlock();
 }
 
-static zx_status_t brcmf_sdio_get_fwname(struct brcmf_device* dev, uint32_t chip, uint32_t chiprev,
+static zx_status_t brcmf_sdio_get_fwname(brcmf_bus* bus_if, uint32_t chip, uint32_t chiprev,
                                          uint8_t* fw_name) {
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
   struct brcmf_sdio_dev* sdiodev = bus_if->bus_priv.sdio;
   int ret = ZX_OK;
 
@@ -3607,8 +3602,7 @@
   return ret;
 }
 
-static zx_status_t brcmf_sdio_get_bootloader_macaddr(struct brcmf_device* dev, uint8_t* mac_addr) {
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
+static zx_status_t brcmf_sdio_get_bootloader_macaddr(brcmf_bus* bus_if, uint8_t* mac_addr) {
   struct brcmf_sdio_dev* sdiodev = bus_if->bus_priv.sdio;
   return brcmf_sdiod_get_bootloader_macaddr(sdiodev, mac_addr);
 }
@@ -3629,27 +3623,22 @@
     .device_add = device_add,
 };
 
-static void brcmf_sdio_firmware_callback(struct brcmf_device* dev, zx_status_t err,
-                                         const struct brcmf_firmware* code, void* nvram,
+static void brcmf_sdio_firmware_callback(brcmf_pub* drvr, zx_status_t err,
+                                         const brcmf_firmware* code, void* nvram,
                                          uint32_t nvram_len) {
-  struct brcmf_bus* bus_if = dev_to_bus(dev);
-  struct brcmf_sdio_dev* sdiodev = bus_if->bus_priv.sdio;
+  struct brcmf_sdio_dev* sdiodev = drvr->bus_if->bus_priv.sdio;
   struct brcmf_sdio* bus = sdiodev->bus;
   struct brcmf_sdio_dev* sdiod = bus->sdiodev;
   struct brcmf_core* core = bus->sdio_core;
   struct sdpcm_shared sh = {};
   uint8_t saveclk = 0;
 
-  BRCMF_DBG(TRACE, "Enter: dev=%s, err=%d\n", device_get_name(dev->zxdev), err);
+  BRCMF_DBG(TRACE, "Enter: dev=%s, err=%d\n", device_get_name(sdiodev->drvr->zxdev), err);
 
   if (err != ZX_OK) {
     goto fail;
   }
 
-  if (!bus_if->drvr) {
-    return;
-  }
-
   /* try to download image and nvram to the dongle */
   bus->alp_only = true;
   err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len);
@@ -3757,7 +3746,7 @@
   sdio_release_host(sdiodev->func1);
   zx_nanosleep(zx_deadline_after(ZX_MSEC(100)));
 
-  err = brcmf_bus_started(dev);
+  err = brcmf_bus_started(sdiodev->drvr);
   if (err != ZX_OK) {
     BRCMF_ERR("dongle is not responding\n");
     goto fail;
@@ -3767,7 +3756,7 @@
 release:
   sdio_release_host(sdiodev->func1);
 fail:
-  BRCMF_DBG(TRACE, "failed: dev=%s, err=%d\n", device_get_name(dev->zxdev), err);
+  BRCMF_DBG(TRACE, "failed: dev=%s, err=%d\n", device_get_name(sdiodev->drvr->zxdev), err);
   BRCMF_ERR("Need to implement driver release logic (WLAN-888)\n");
   // TODO(WLAN-888)
   // device_release_driver(&sdiodev->func2->dev);
@@ -3799,7 +3788,7 @@
   /* single-threaded workqueue */
   char name[WORKQUEUE_NAME_MAXLEN];
   static int queue_uniquify = 0;
-  snprintf(name, WORKQUEUE_NAME_MAXLEN, "brcmf_wq/%s%d", device_get_name(sdiodev->dev->zxdev),
+  snprintf(name, WORKQUEUE_NAME_MAXLEN, "brcmf_wq/%s%d", device_get_name(sdiodev->drvr->zxdev),
            queue_uniquify++);
   wq = workqueue_create(name);
   if (!wq) {
@@ -3822,7 +3811,7 @@
   bus->dcmd_resp_wait = {};
 
   /* Set up the watchdog timer */
-  brcmf_timer_init(&bus->timer, brcmf_sdio_watchdog, bus);
+  brcmf_timer_init(&bus->timer, sdiodev->drvr->dispatcher, brcmf_sdio_watchdog, bus);
   /* Initialize watchdog thread */
   bus->watchdog_wait = {};
   bus->watchdog_should_stop.store(false);
@@ -3836,7 +3825,6 @@
   bus->dpc_running = false;
 
   /* Assign bus interface call back */
-  bus->sdiodev->bus_if->dev = sdiodev->dev;
   bus->sdiodev->bus_if->ops = &brcmf_sdio_bus_ops;
   bus->sdiodev->bus_if->chip = bus->ci->chip;
   bus->sdiodev->bus_if->chiprev = bus->ci->chiprev;
@@ -3845,7 +3833,7 @@
   bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;
 
   /* Attach to the common layer, reserve hdr space */
-  ret = brcmf_attach(bus->sdiodev->dev, bus->sdiodev->settings);
+  ret = brcmf_attach(bus->sdiodev->drvr, bus->sdiodev->bus_if, bus->sdiodev->settings);
   if (ret != ZX_OK) {
     BRCMF_ERR("brcmf_attach failed\n");
     goto fail;
@@ -3896,7 +3884,7 @@
     goto fail;
   }
 
-  ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM, sdiodev->fw_name,
+  ret = brcmf_fw_get_firmwares(sdiodev->drvr, BRCMF_FW_REQUEST_NVRAM, sdiodev->fw_name,
                                sdiodev->nvram_name, brcmf_sdio_firmware_callback);
   if (ret != ZX_OK) {
     BRCMF_ERR("async firmware request failed: %d\n", ret);
@@ -3918,7 +3906,7 @@
     /* De-register interrupt handler */
     brcmf_sdiod_intr_unregister(bus->sdiodev);
 
-    brcmf_detach(bus->sdiodev->dev);
+    brcmf_detach(bus->sdiodev->drvr);
 
     workqueue_cancel_work(&bus->datawork);
     if (bus->brcmf_wq) {
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sdio.h b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sdio.h
index ad770ea..066a71e 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sdio.h
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sdio.h
@@ -22,7 +22,6 @@
 #include <ddk/protocol/sdio.h>
 
 #include "defs.h"
-#include "device.h"
 #include "firmware.h"
 #include "linuxisms.h"
 #include "netbuf.h"
@@ -200,7 +199,7 @@
   bool has_debug_gpio;
   zx_handle_t irq_handle;
   thrd_t isr_thread;
-  struct brcmf_device* dev;
+  struct brcmf_pub* drvr;
   uint32_t sbwad;             /* Save backplane window address */
   struct brcmf_core* cc_core; /* chipcommon core info struct */
   struct brcmf_sdio* bus;
@@ -393,7 +392,6 @@
 void brcmf_sdio_isr(struct brcmf_sdio* bus);
 
 void brcmf_sdio_wd_timer(struct brcmf_sdio* bus, bool active);
-void brcmf_sdio_wowl_config(struct brcmf_device* dev, bool enabled);
 zx_status_t brcmf_sdio_sleep(struct brcmf_sdio* bus, bool sleep);
 void brcmf_sdio_trigger_dpc(struct brcmf_sdio* bus);
 int brcmf_sdio_oob_irqhandler(void* cookie);
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim.cc
index ee3cad5..b86390c 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim.cc
@@ -24,33 +24,30 @@
 #include "chip.h"
 #include "common.h"
 #include "debug.h"
-#include "device.h"
 #include "src/connectivity/wlan/drivers/testing/lib/sim-device/device.h"
 
-#define BUS_OP(dev) dev->bus->bus_priv.sim->sim_fw
+#define BUS_OP(bus) bus->bus_priv.sim->sim_fw
 static const struct brcmf_bus_ops brcmf_sim_bus_ops = {
     .get_bus_type = []() { return BRCMF_BUS_TYPE_SIM; },
-    .preinit = [](struct brcmf_device* dev) { return BUS_OP(dev)->BusPreinit(); },
-    .stop = [](struct brcmf_device* dev) { return BUS_OP(dev)->BusStop(); },
-    .txdata = [](struct brcmf_device* dev,
-                 struct brcmf_netbuf* netbuf) { return BUS_OP(dev)->BusTxData(netbuf); },
-    .txctl = [](struct brcmf_device* dev, unsigned char* msg,
-                uint len) { return BUS_OP(dev)->BusTxCtl(msg, len); },
-    .rxctl = [](struct brcmf_device* dev, unsigned char* msg, uint len,
-                int* rxlen_out) { return BUS_OP(dev)->BusRxCtl(msg, len, rxlen_out); },
-    .gettxq = [](struct brcmf_device* dev) { return BUS_OP(dev)->BusGetTxQueue(); },
-    .wowl_config = [](struct brcmf_device* dev,
-                      bool enabled) { return BUS_OP(dev)->BusWowlConfig(enabled); },
-    .get_ramsize = [](struct brcmf_device* dev) { return BUS_OP(dev)->BusGetRamsize(); },
-    .get_memdump = [](struct brcmf_device* dev, void* data,
-                      size_t len) { return BUS_OP(dev)->BusGetMemdump(data, len); },
+    .preinit = [](brcmf_bus* bus) { return BUS_OP(bus)->BusPreinit(); },
+    .stop = [](brcmf_bus* bus) { return BUS_OP(bus)->BusStop(); },
+    .txdata = [](brcmf_bus* bus, brcmf_netbuf* netbuf) { return BUS_OP(bus)->BusTxData(netbuf); },
+    .txctl = [](brcmf_bus* bus, unsigned char* msg,
+                uint len) { return BUS_OP(bus)->BusTxCtl(msg, len); },
+    .rxctl = [](brcmf_bus* bus, unsigned char* msg, uint len,
+                int* rxlen_out) { return BUS_OP(bus)->BusRxCtl(msg, len, rxlen_out); },
+    .gettxq = [](brcmf_bus* bus) { return BUS_OP(bus)->BusGetTxQueue(); },
+    .wowl_config = [](brcmf_bus* bus, bool enabled) { return BUS_OP(bus)->BusWowlConfig(enabled); },
+    .get_ramsize = [](brcmf_bus* bus) { return BUS_OP(bus)->BusGetRamsize(); },
+    .get_memdump = [](brcmf_bus* bus, void* data,
+                      size_t len) { return BUS_OP(bus)->BusGetMemdump(data, len); },
     .get_fwname =
-        [](struct brcmf_device* dev, uint chip, uint chiprev, unsigned char* fw_name) {
-          return BUS_OP(dev)->BusGetFwName(chip, chiprev, fw_name);
+        [](brcmf_bus* bus, uint chip, uint chiprev, unsigned char* fw_name) {
+          return BUS_OP(bus)->BusGetFwName(chip, chiprev, fw_name);
         },
     .get_bootloader_macaddr =
-        [](struct brcmf_device* dev, uint8_t* mac_addr) {
-          return BUS_OP(dev)->BusGetBootloaderMacAddr(mac_addr);
+        [](brcmf_bus* bus, uint8_t* mac_addr) {
+          return BUS_OP(bus)->BusGetBootloaderMacAddr(mac_addr);
         },
     .device_add = wlan_sim_device_add};
 #undef BUS_OP
@@ -63,7 +60,7 @@
   bus->chip = chip;
   bus->chiprev = chiprev;
 
-  bus->bus_priv.sim->settings = brcmf_get_module_param(bus->dev, BRCMF_BUS_TYPE_SIM, chip, chiprev);
+  bus->bus_priv.sim->settings = brcmf_get_module_param(BRCMF_BUS_TYPE_SIM, chip, chiprev);
   if (bus->bus_priv.sim->settings == nullptr) {
     BRCMF_ERR("Failed to get device parameters\n");
     return ZX_ERR_INTERNAL;
@@ -73,7 +70,7 @@
 }
 
 // Allocate necessary memory and initialize simulator-specific structures
-zx_status_t brcmf_sim_register(struct brcmf_device* device) {
+zx_status_t brcmf_sim_register(brcmf_pub* drvr, std::unique_ptr<brcmf_bus>* out_bus) {
   zx_status_t status = ZX_OK;
   auto simdev = std::make_unique<brcmf_simdev>();
   auto bus_if = std::make_unique<brcmf_bus>();
@@ -81,7 +78,6 @@
   // Initialize inter-structure pointers
   simdev->sim_fw = std::make_unique<SimFirmware>();
   bus_if->bus_priv.sim = simdev.get();
-  bus_if->dev = device;
 
   BRCMF_DBG(SIM, "Registering simulator target\n");
   status = brcmf_sim_probe(bus_if.get());
@@ -91,8 +87,7 @@
   }
 
   bus_if->ops = &brcmf_sim_bus_ops;
-  device->bus = std::move(bus_if);
-  status = brcmf_attach(device, simdev->settings);
+  status = brcmf_attach(drvr, bus_if.get(), simdev->settings);
   if (status != ZX_OK) {
     BRCMF_ERR("brcmf_attach failed\n");
     return status;
@@ -101,19 +96,18 @@
   // Here is where we would likely simulate loading the firmware into the target. For now,
   // we don't try.
 
-  status = brcmf_bus_started(device);
+  status = brcmf_bus_started(drvr);
   if (status != ZX_OK) {
     BRCMF_ERR("Failed to start (simulated) bus\n");
     return status;
   }
 
   simdev.release();
+  *out_bus = std::move(bus_if);
   return ZX_OK;
 }
 
-void brcmf_sim_exit(struct brcmf_device* device) {
-  delete device->bus->bus_priv.sim;
-  device->bus->bus_priv.sim = nullptr;
-
-  device->bus.reset();
+void brcmf_sim_exit(brcmf_bus* bus) {
+  delete bus->bus_priv.sim;
+  bus->bus_priv.sim = nullptr;
 }
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim.h b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim.h
index 7e66a8f..c552c11 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim.h
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim.h
@@ -20,7 +20,6 @@
 #include <memory>
 
 #include "bus.h"
-#include "device.h"
 #include "sim-fw/sim_fw.h"
 
 struct brcmf_simdev {
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/test/lifecycle_test.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/test/lifecycle_test.cc
index dbee855..4251468 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/test/lifecycle_test.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/test/lifecycle_test.cc
@@ -23,19 +23,21 @@
 #include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/core.h"
 #include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/device.h"
 
+namespace wlan {
+namespace brcmfmac {
 namespace {
 
 TEST(LifecycleTest, StartStop) {
   zx_status_t status = ZX_OK;
-  brcmf_device device = {};
+  Device* device = nullptr;
 
-  status = brcmfmac_module_init();
-  EXPECT_EQ(status, ZX_OK);
-  status = brcmf_core_init(&device);
+  status = Device::Create(nullptr, &device);
   EXPECT_EQ(status, ZX_OK);
 
-  brcmf_core_exit(&device);
-  brcmfmac_module_exit();
+  device->SimUnbind();
+  device = nullptr;
 }
 
 }  // namespace
+}  // namespace brcmfmac
+}  // namespace wlan
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/test/sdio_test.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/test/sdio_test.cc
index 092919a..21c6b15 100644
--- a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/test/sdio_test.cc
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/test/sdio_test.cc
@@ -78,7 +78,7 @@
   wifi_config_t config = {ZX_INTERRUPT_MODE_LEVEL_LOW};
   ddk.SetMetadata(&config, sizeof(config));
 
-  brcmf_device dev = {};
+  brcmf_pub drvr = {};
   brcmf_sdio_dev sdio_dev = {};
   sdio_func func1 = {};
   MockSdio sdio1;
@@ -91,7 +91,7 @@
   sdio_dev.gpios[WIFI_OOB_IRQ_GPIO_INDEX] = *gpio.GetProto();
   sdio_dev.sdio_proto_fn1 = *sdio1.GetProto();
   sdio_dev.sdio_proto_fn2 = *sdio2.GetProto();
-  sdio_dev.dev = &dev;
+  sdio_dev.drvr = &drvr;
   sdio_dev.bus_if = &bus_if;
   sdio_dev.settings = &settings;
 
@@ -109,7 +109,7 @@
 }
 
 TEST(Sdio, IntrUnregister) {
-  brcmf_device dev = {};
+  brcmf_pub drvr = {};
   brcmf_sdio_dev sdio_dev = {};
   sdio_func func1 = {};
 
@@ -118,7 +118,7 @@
   sdio_dev.func1 = &func1;
   sdio_dev.sdio_proto_fn1 = *sdio1.GetProto();
   sdio_dev.sdio_proto_fn2 = *sdio2.GetProto();
-  sdio_dev.dev = &dev;
+  sdio_dev.drvr = &drvr;
   sdio_dev.oob_irq_requested = true;
 
   sdio1.ExpectDoVendorControlRwByte(ZX_OK, true, 0xf2, 0, 0).ExpectDisableFnIntr(ZX_OK);
@@ -135,7 +135,7 @@
   sdio_dev.func1 = &func1;
   sdio_dev.sdio_proto_fn1 = *sdio1.GetProto();
   sdio_dev.sdio_proto_fn2 = *sdio2.GetProto();
-  sdio_dev.dev = &dev;
+  sdio_dev.drvr = &drvr;
   sdio_dev.sd_irq_requested = true;
 
   sdio1.ExpectDisableFnIntr(ZX_OK);
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/timer.cc b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/timer.cc
new file mode 100644
index 0000000..c1bef06
--- /dev/null
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/timer.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2019 The Fuchsia Authors
+//
+// 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 "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/timer.h"
+
+#include <lib/async/time.h>
+#include <zircon/listnode.h>
+#include <zircon/types.h>
+
+static void brcmf_timer_handler(async_dispatcher_t* dispatcher, async_task_t* task,
+                                zx_status_t status) {
+  if (status != ZX_OK) {
+    return;
+  }
+  brcmf_timer_info_t* timer = containerof(task, brcmf_timer_info_t, task);
+  timer->callback_function(timer->data);
+  timer->lock.lock();
+  timer->scheduled = false;
+  sync_completion_signal(&timer->finished);
+  timer->lock.unlock();
+}
+
+void brcmf_timer_init(brcmf_timer_info_t* timer, async_dispatcher_t* dispatcher,
+                      brcmf_timer_callback_t* callback, void* data) {
+  memset(&timer->task.state, 0, sizeof(timer->task.state));
+  timer->task.handler = brcmf_timer_handler;
+  timer->dispatcher = dispatcher;
+  timer->data = data;
+  timer->callback_function = callback;
+  timer->finished = {};
+  timer->scheduled = false;
+}
+
+void brcmf_timer_set(brcmf_timer_info_t* timer, zx_duration_t delay) {
+  timer->lock.lock();
+  async_cancel_task(timer->dispatcher, &timer->task);  // Make sure it's not scheduled
+  timer->task.deadline = delay + async_now(timer->dispatcher);
+  timer->scheduled = true;
+  sync_completion_reset(&timer->finished);
+  async_post_task(timer->dispatcher, &timer->task);
+  timer->lock.unlock();
+}
+
+void brcmf_timer_stop(brcmf_timer_info_t* timer) {
+  timer->lock.lock();
+  if (!timer->scheduled) {
+    timer->lock.unlock();
+    return;
+  }
+  zx_status_t result = async_cancel_task(timer->dispatcher, &timer->task);
+  timer->lock.unlock();
+  if (result != ZX_OK) {
+    sync_completion_wait(&timer->finished, ZX_TIME_INFINITE);
+  }
+}
diff --git a/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/timer.h b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/timer.h
new file mode 100644
index 0000000..3d40b8c
--- /dev/null
+++ b/src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/timer.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2019 The Fuchsia Authors
+//
+// 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 <lib/async/dispatcher.h>
+#include <lib/async/task.h>
+#include <lib/sync/completion.h>
+#include <zircon/time.h>
+
+#include <mutex>
+
+// This is the function that timer users write to receive callbacks.
+typedef void(brcmf_timer_callback_t)(void* data);
+
+typedef struct brcmf_timer_info {
+  async_task_t task;
+  async_dispatcher_t* dispatcher;
+  void* data;
+  brcmf_timer_callback_t* callback_function;
+  bool scheduled;
+  sync_completion_t finished;
+  std::mutex lock;
+} brcmf_timer_info_t;
+
+void brcmf_timer_init(brcmf_timer_info_t* timer, async_dispatcher_t* dispatcher,
+                      brcmf_timer_callback_t* callback, void* data);
+
+void brcmf_timer_set(brcmf_timer_info_t* timer, zx_duration_t delay);
+
+void brcmf_timer_stop(brcmf_timer_info_t* timer);