From 1b6799d93f0bbd6154c124d51aa47aeed0acf15d Mon Sep 17 00:00:00 2001
From: Dario Nieuwenhuis <dirbaio@dirbaio.net>
Date: Mon, 26 Dec 2022 23:21:58 +0100
Subject: [PATCH] split bus, consts into separate mods.

---
 src/bus.rs    | 321 ++++++++++++++++++++++++++++++
 src/consts.rs | 105 ++++++++++
 src/lib.rs    | 536 +++++++-------------------------------------------
 3 files changed, 496 insertions(+), 466 deletions(-)
 create mode 100644 src/bus.rs
 create mode 100644 src/consts.rs

diff --git a/src/bus.rs b/src/bus.rs
new file mode 100644
index 000000000..f220cffcd
--- /dev/null
+++ b/src/bus.rs
@@ -0,0 +1,321 @@
+use core::slice;
+
+use embassy_time::{Duration, Timer};
+use embedded_hal_1::digital::OutputPin;
+use embedded_hal_async::spi::{transaction, SpiBusRead, SpiBusWrite, SpiDevice};
+
+use crate::consts::*;
+
+pub(crate) struct Bus<PWR, SPI> {
+    backplane_window: u32,
+    pwr: PWR,
+    spi: SPI,
+}
+
+impl<PWR, SPI> Bus<PWR, SPI>
+where
+    PWR: OutputPin,
+    SPI: SpiDevice,
+    SPI::Bus: SpiBusRead<u32> + SpiBusWrite<u32>,
+{
+    pub(crate) fn new(pwr: PWR, spi: SPI) -> Self {
+        Self {
+            backplane_window: 0xAAAA_AAAA,
+            pwr,
+            spi,
+        }
+    }
+
+    pub async fn init(&mut self) {
+        // Reset
+        self.pwr.set_low().unwrap();
+        Timer::after(Duration::from_millis(20)).await;
+        self.pwr.set_high().unwrap();
+        Timer::after(Duration::from_millis(250)).await;
+
+        while self.read32_swapped(REG_BUS_TEST_RO).await != FEEDBEAD {}
+
+        self.write32_swapped(REG_BUS_TEST_RW, TEST_PATTERN).await;
+        let val = self.read32_swapped(REG_BUS_TEST_RW).await;
+        assert_eq!(val, TEST_PATTERN);
+
+        // 32-bit word length, little endian (which is the default endianess).
+        self.write32_swapped(REG_BUS_CTRL, WORD_LENGTH_32 | HIGH_SPEED).await;
+
+        let val = self.read32(FUNC_BUS, REG_BUS_TEST_RO).await;
+        assert_eq!(val, FEEDBEAD);
+        let val = self.read32(FUNC_BUS, REG_BUS_TEST_RW).await;
+        assert_eq!(val, TEST_PATTERN);
+    }
+
+    pub async fn wlan_read(&mut self, buf: &mut [u32]) {
+        let cmd = cmd_word(READ, INC_ADDR, FUNC_WLAN, 0, buf.len() as u32 * 4);
+        transaction!(&mut self.spi, |bus| async {
+            bus.write(&[cmd]).await?;
+            bus.read(buf).await?;
+            Ok(())
+        })
+        .await
+        .unwrap();
+    }
+
+    pub async fn wlan_write(&mut self, buf: &[u32]) {
+        let cmd = cmd_word(WRITE, INC_ADDR, FUNC_WLAN, 0, buf.len() as u32 * 4);
+        transaction!(&mut self.spi, |bus| async {
+            bus.write(&[cmd]).await?;
+            bus.write(buf).await?;
+            Ok(())
+        })
+        .await
+        .unwrap();
+    }
+
+    #[allow(unused)]
+    pub async fn bp_read(&mut self, mut addr: u32, mut data: &mut [u8]) {
+        // It seems the HW force-aligns the addr
+        // to 2 if data.len() >= 2
+        // to 4 if data.len() >= 4
+        // To simplify, enforce 4-align for now.
+        assert!(addr % 4 == 0);
+
+        let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4];
+
+        while !data.is_empty() {
+            // Ensure transfer doesn't cross a window boundary.
+            let window_offs = addr & BACKPLANE_ADDRESS_MASK;
+            let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize;
+
+            let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining);
+
+            self.backplane_set_window(addr).await;
+
+            let cmd = cmd_word(READ, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32);
+
+            transaction!(&mut self.spi, |bus| async {
+                bus.write(&[cmd]).await?;
+
+                // 4-byte response delay.
+                let mut junk = [0; 1];
+                bus.read(&mut junk).await?;
+
+                // Read data
+                bus.read(&mut buf[..(len + 3) / 4]).await?;
+                Ok(())
+            })
+            .await
+            .unwrap();
+
+            data[..len].copy_from_slice(&slice8_mut(&mut buf)[..len]);
+
+            // Advance ptr.
+            addr += len as u32;
+            data = &mut data[len..];
+        }
+    }
+
+    pub async fn bp_write(&mut self, mut addr: u32, mut data: &[u8]) {
+        // It seems the HW force-aligns the addr
+        // to 2 if data.len() >= 2
+        // to 4 if data.len() >= 4
+        // To simplify, enforce 4-align for now.
+        assert!(addr % 4 == 0);
+
+        let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4];
+
+        while !data.is_empty() {
+            // Ensure transfer doesn't cross a window boundary.
+            let window_offs = addr & BACKPLANE_ADDRESS_MASK;
+            let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize;
+
+            let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining);
+            slice8_mut(&mut buf)[..len].copy_from_slice(&data[..len]);
+
+            self.backplane_set_window(addr).await;
+
+            let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32);
+
+            transaction!(&mut self.spi, |bus| async {
+                bus.write(&[cmd]).await?;
+                bus.write(&buf[..(len + 3) / 4]).await?;
+                Ok(())
+            })
+            .await
+            .unwrap();
+
+            // Advance ptr.
+            addr += len as u32;
+            data = &data[len..];
+        }
+    }
+
+    pub async fn bp_read8(&mut self, addr: u32) -> u8 {
+        self.backplane_readn(addr, 1).await as u8
+    }
+
+    pub async fn bp_write8(&mut self, addr: u32, val: u8) {
+        self.backplane_writen(addr, val as u32, 1).await
+    }
+
+    pub async fn bp_read16(&mut self, addr: u32) -> u16 {
+        self.backplane_readn(addr, 2).await as u16
+    }
+
+    #[allow(unused)]
+    pub async fn bp_write16(&mut self, addr: u32, val: u16) {
+        self.backplane_writen(addr, val as u32, 2).await
+    }
+
+    #[allow(unused)]
+    pub async fn bp_read32(&mut self, addr: u32) -> u32 {
+        self.backplane_readn(addr, 4).await
+    }
+
+    pub async fn bp_write32(&mut self, addr: u32, val: u32) {
+        self.backplane_writen(addr, val, 4).await
+    }
+
+    async fn backplane_readn(&mut self, addr: u32, len: u32) -> u32 {
+        self.backplane_set_window(addr).await;
+
+        let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK;
+        if len == 4 {
+            bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG
+        }
+        self.readn(FUNC_BACKPLANE, bus_addr, len).await
+    }
+
+    async fn backplane_writen(&mut self, addr: u32, val: u32, len: u32) {
+        self.backplane_set_window(addr).await;
+
+        let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK;
+        if len == 4 {
+            bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG
+        }
+        self.writen(FUNC_BACKPLANE, bus_addr, val, len).await
+    }
+
+    async fn backplane_set_window(&mut self, addr: u32) {
+        let new_window = addr & !BACKPLANE_ADDRESS_MASK;
+
+        if (new_window >> 24) as u8 != (self.backplane_window >> 24) as u8 {
+            self.write8(
+                FUNC_BACKPLANE,
+                REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH,
+                (new_window >> 24) as u8,
+            )
+            .await;
+        }
+        if (new_window >> 16) as u8 != (self.backplane_window >> 16) as u8 {
+            self.write8(
+                FUNC_BACKPLANE,
+                REG_BACKPLANE_BACKPLANE_ADDRESS_MID,
+                (new_window >> 16) as u8,
+            )
+            .await;
+        }
+        if (new_window >> 8) as u8 != (self.backplane_window >> 8) as u8 {
+            self.write8(
+                FUNC_BACKPLANE,
+                REG_BACKPLANE_BACKPLANE_ADDRESS_LOW,
+                (new_window >> 8) as u8,
+            )
+            .await;
+        }
+        self.backplane_window = new_window;
+    }
+
+    pub async fn read8(&mut self, func: u32, addr: u32) -> u8 {
+        self.readn(func, addr, 1).await as u8
+    }
+
+    pub async fn write8(&mut self, func: u32, addr: u32, val: u8) {
+        self.writen(func, addr, val as u32, 1).await
+    }
+
+    pub async fn read16(&mut self, func: u32, addr: u32) -> u16 {
+        self.readn(func, addr, 2).await as u16
+    }
+
+    #[allow(unused)]
+    pub async fn write16(&mut self, func: u32, addr: u32, val: u16) {
+        self.writen(func, addr, val as u32, 2).await
+    }
+
+    pub async fn read32(&mut self, func: u32, addr: u32) -> u32 {
+        self.readn(func, addr, 4).await
+    }
+
+    #[allow(unused)]
+    pub async fn write32(&mut self, func: u32, addr: u32, val: u32) {
+        self.writen(func, addr, val, 4).await
+    }
+
+    async fn readn(&mut self, func: u32, addr: u32, len: u32) -> u32 {
+        let cmd = cmd_word(READ, INC_ADDR, func, addr, len);
+        let mut buf = [0; 1];
+
+        transaction!(&mut self.spi, |bus| async {
+            bus.write(&[cmd]).await?;
+            if func == FUNC_BACKPLANE {
+                // 4-byte response delay.
+                bus.read(&mut buf).await?;
+            }
+            bus.read(&mut buf).await?;
+            Ok(())
+        })
+        .await
+        .unwrap();
+
+        buf[0]
+    }
+
+    async fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) {
+        let cmd = cmd_word(WRITE, INC_ADDR, func, addr, len);
+
+        transaction!(&mut self.spi, |bus| async {
+            bus.write(&[cmd, val]).await?;
+            Ok(())
+        })
+        .await
+        .unwrap();
+    }
+
+    async fn read32_swapped(&mut self, addr: u32) -> u32 {
+        let cmd = cmd_word(READ, INC_ADDR, FUNC_BUS, addr, 4);
+        let mut buf = [0; 1];
+
+        transaction!(&mut self.spi, |bus| async {
+            bus.write(&[swap16(cmd)]).await?;
+            bus.read(&mut buf).await?;
+            Ok(())
+        })
+        .await
+        .unwrap();
+
+        swap16(buf[0])
+    }
+
+    async fn write32_swapped(&mut self, addr: u32, val: u32) {
+        let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BUS, addr, 4);
+
+        transaction!(&mut self.spi, |bus| async {
+            bus.write(&[swap16(cmd), swap16(val)]).await?;
+            Ok(())
+        })
+        .await
+        .unwrap();
+    }
+}
+
+fn swap16(x: u32) -> u32 {
+    x.rotate_left(16)
+}
+
+fn cmd_word(write: bool, incr: bool, func: u32, addr: u32, len: u32) -> u32 {
+    (write as u32) << 31 | (incr as u32) << 30 | (func & 0b11) << 28 | (addr & 0x1FFFF) << 11 | (len & 0x7FF)
+}
+
+fn slice8_mut(x: &mut [u32]) -> &mut [u8] {
+    let len = x.len() * 4;
+    unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) }
+}
diff --git a/src/consts.rs b/src/consts.rs
new file mode 100644
index 000000000..bee706600
--- /dev/null
+++ b/src/consts.rs
@@ -0,0 +1,105 @@
+#![allow(unused)]
+pub(crate) const FUNC_BUS: u32 = 0;
+pub(crate) const FUNC_BACKPLANE: u32 = 1;
+pub(crate) const FUNC_WLAN: u32 = 2;
+pub(crate) const FUNC_BT: u32 = 3;
+
+pub(crate) const REG_BUS_CTRL: u32 = 0x0;
+pub(crate) const REG_BUS_INTERRUPT: u32 = 0x04; // 16 bits - Interrupt status
+pub(crate) const REG_BUS_INTERRUPT_ENABLE: u32 = 0x06; // 16 bits - Interrupt mask
+pub(crate) const REG_BUS_STATUS: u32 = 0x8;
+pub(crate) const REG_BUS_TEST_RO: u32 = 0x14;
+pub(crate) const REG_BUS_TEST_RW: u32 = 0x18;
+pub(crate) const REG_BUS_RESP_DELAY: u32 = 0x1c;
+pub(crate) const WORD_LENGTH_32: u32 = 0x1;
+pub(crate) const HIGH_SPEED: u32 = 0x10;
+
+// SPI_STATUS_REGISTER bits
+pub(crate) const STATUS_DATA_NOT_AVAILABLE: u32 = 0x00000001;
+pub(crate) const STATUS_UNDERFLOW: u32 = 0x00000002;
+pub(crate) const STATUS_OVERFLOW: u32 = 0x00000004;
+pub(crate) const STATUS_F2_INTR: u32 = 0x00000008;
+pub(crate) const STATUS_F3_INTR: u32 = 0x00000010;
+pub(crate) const STATUS_F2_RX_READY: u32 = 0x00000020;
+pub(crate) const STATUS_F3_RX_READY: u32 = 0x00000040;
+pub(crate) const STATUS_HOST_CMD_DATA_ERR: u32 = 0x00000080;
+pub(crate) const STATUS_F2_PKT_AVAILABLE: u32 = 0x00000100;
+pub(crate) const STATUS_F2_PKT_LEN_MASK: u32 = 0x000FFE00;
+pub(crate) const STATUS_F2_PKT_LEN_SHIFT: u32 = 9;
+pub(crate) const STATUS_F3_PKT_AVAILABLE: u32 = 0x00100000;
+pub(crate) const STATUS_F3_PKT_LEN_MASK: u32 = 0xFFE00000;
+pub(crate) const STATUS_F3_PKT_LEN_SHIFT: u32 = 21;
+
+pub(crate) const REG_BACKPLANE_GPIO_SELECT: u32 = 0x10005;
+pub(crate) const REG_BACKPLANE_GPIO_OUTPUT: u32 = 0x10006;
+pub(crate) const REG_BACKPLANE_GPIO_ENABLE: u32 = 0x10007;
+pub(crate) const REG_BACKPLANE_FUNCTION2_WATERMARK: u32 = 0x10008;
+pub(crate) const REG_BACKPLANE_DEVICE_CONTROL: u32 = 0x10009;
+pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_LOW: u32 = 0x1000A;
+pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_MID: u32 = 0x1000B;
+pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH: u32 = 0x1000C;
+pub(crate) const REG_BACKPLANE_FRAME_CONTROL: u32 = 0x1000D;
+pub(crate) const REG_BACKPLANE_CHIP_CLOCK_CSR: u32 = 0x1000E;
+pub(crate) const REG_BACKPLANE_PULL_UP: u32 = 0x1000F;
+pub(crate) const REG_BACKPLANE_READ_FRAME_BC_LOW: u32 = 0x1001B;
+pub(crate) const REG_BACKPLANE_READ_FRAME_BC_HIGH: u32 = 0x1001C;
+pub(crate) const REG_BACKPLANE_WAKEUP_CTRL: u32 = 0x1001E;
+pub(crate) const REG_BACKPLANE_SLEEP_CSR: u32 = 0x1001F;
+
+pub(crate) const BACKPLANE_WINDOW_SIZE: usize = 0x8000;
+pub(crate) const BACKPLANE_ADDRESS_MASK: u32 = 0x7FFF;
+pub(crate) const BACKPLANE_ADDRESS_32BIT_FLAG: u32 = 0x08000;
+pub(crate) const BACKPLANE_MAX_TRANSFER_SIZE: usize = 64;
+// Active Low Power (ALP) clock constants
+pub(crate) const BACKPLANE_ALP_AVAIL_REQ: u8 = 0x08;
+pub(crate) const BACKPLANE_ALP_AVAIL: u8 = 0x40;
+
+// Broadcom AMBA (Advanced Microcontroller Bus Architecture) Interconnect
+// (AI) pub (crate) constants
+pub(crate) const AI_IOCTRL_OFFSET: u32 = 0x408;
+pub(crate) const AI_IOCTRL_BIT_FGC: u8 = 0x0002;
+pub(crate) const AI_IOCTRL_BIT_CLOCK_EN: u8 = 0x0001;
+pub(crate) const AI_IOCTRL_BIT_CPUHALT: u8 = 0x0020;
+
+pub(crate) const AI_RESETCTRL_OFFSET: u32 = 0x800;
+pub(crate) const AI_RESETCTRL_BIT_RESET: u8 = 1;
+
+pub(crate) const AI_RESETSTATUS_OFFSET: u32 = 0x804;
+
+pub(crate) const TEST_PATTERN: u32 = 0x12345678;
+pub(crate) const FEEDBEAD: u32 = 0xFEEDBEAD;
+
+// SPI_INTERRUPT_REGISTER and SPI_INTERRUPT_ENABLE_REGISTER Bits
+pub(crate) const IRQ_DATA_UNAVAILABLE: u16 = 0x0001; // Requested data not available; Clear by writing a "1"
+pub(crate) const IRQ_F2_F3_FIFO_RD_UNDERFLOW: u16 = 0x0002;
+pub(crate) const IRQ_F2_F3_FIFO_WR_OVERFLOW: u16 = 0x0004;
+pub(crate) const IRQ_COMMAND_ERROR: u16 = 0x0008; // Cleared by writing 1
+pub(crate) const IRQ_DATA_ERROR: u16 = 0x0010; // Cleared by writing 1
+pub(crate) const IRQ_F2_PACKET_AVAILABLE: u16 = 0x0020;
+pub(crate) const IRQ_F3_PACKET_AVAILABLE: u16 = 0x0040;
+pub(crate) const IRQ_F1_OVERFLOW: u16 = 0x0080; // Due to last write. Bkplane has pending write requests
+pub(crate) const IRQ_MISC_INTR0: u16 = 0x0100;
+pub(crate) const IRQ_MISC_INTR1: u16 = 0x0200;
+pub(crate) const IRQ_MISC_INTR2: u16 = 0x0400;
+pub(crate) const IRQ_MISC_INTR3: u16 = 0x0800;
+pub(crate) const IRQ_MISC_INTR4: u16 = 0x1000;
+pub(crate) const IRQ_F1_INTR: u16 = 0x2000;
+pub(crate) const IRQ_F2_INTR: u16 = 0x4000;
+pub(crate) const IRQ_F3_INTR: u16 = 0x8000;
+
+pub(crate) const IOCTL_CMD_UP: u32 = 2;
+pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26;
+pub(crate) const IOCTL_CMD_ANTDIV: u32 = 64;
+pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263;
+pub(crate) const IOCTL_CMD_GET_VAR: u32 = 262;
+pub(crate) const IOCTL_CMD_SET_PASSPHRASE: u32 = 268;
+
+pub(crate) const CHANNEL_TYPE_CONTROL: u8 = 0;
+pub(crate) const CHANNEL_TYPE_EVENT: u8 = 1;
+pub(crate) const CHANNEL_TYPE_DATA: u8 = 2;
+
+// CYW_SPID command structure constants.
+pub(crate) const WRITE: bool = true;
+pub(crate) const READ: bool = false;
+pub(crate) const INC_ADDR: bool = true;
+pub(crate) const FIXED_ADDR: bool = false;
diff --git a/src/lib.rs b/src/lib.rs
index 883e669de..fa73b32e0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,6 +6,8 @@
 // This mod MUST go first, so that the others see its macros.
 pub(crate) mod fmt;
 
+mod bus;
+mod consts;
 mod countries;
 mod events;
 mod structs;
@@ -23,132 +25,12 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex;
 use embassy_sync::channel::Channel;
 use embassy_time::{block_for, Duration, Timer};
 use embedded_hal_1::digital::OutputPin;
-use embedded_hal_async::spi::{transaction, SpiBusRead, SpiBusWrite, SpiDevice};
+use embedded_hal_async::spi::{SpiBusRead, SpiBusWrite, SpiDevice};
 
-use self::structs::*;
+use crate::bus::Bus;
+use crate::consts::*;
 use crate::events::Event;
-
-fn swap16(x: u32) -> u32 {
-    x.rotate_left(16)
-}
-
-fn cmd_word(write: bool, incr: bool, func: u32, addr: u32, len: u32) -> u32 {
-    (write as u32) << 31 | (incr as u32) << 30 | (func & 0b11) << 28 | (addr & 0x1FFFF) << 11 | (len & 0x7FF)
-}
-
-fn slice8_mut(x: &mut [u32]) -> &mut [u8] {
-    let len = x.len() * 4;
-    unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) }
-}
-
-mod constants {
-    #![allow(unused)]
-    pub(crate) const FUNC_BUS: u32 = 0;
-    pub(crate) const FUNC_BACKPLANE: u32 = 1;
-    pub(crate) const FUNC_WLAN: u32 = 2;
-    pub(crate) const FUNC_BT: u32 = 3;
-
-    pub(crate) const REG_BUS_CTRL: u32 = 0x0;
-    pub(crate) const REG_BUS_INTERRUPT: u32 = 0x04; // 16 bits - Interrupt status
-    pub(crate) const REG_BUS_INTERRUPT_ENABLE: u32 = 0x06; // 16 bits - Interrupt mask
-    pub(crate) const REG_BUS_STATUS: u32 = 0x8;
-    pub(crate) const REG_BUS_TEST_RO: u32 = 0x14;
-    pub(crate) const REG_BUS_TEST_RW: u32 = 0x18;
-    pub(crate) const REG_BUS_RESP_DELAY: u32 = 0x1c;
-    pub(crate) const WORD_LENGTH_32: u32 = 0x1;
-    pub(crate) const HIGH_SPEED: u32 = 0x10;
-
-    // SPI_STATUS_REGISTER bits
-    pub(crate) const STATUS_DATA_NOT_AVAILABLE: u32 = 0x00000001;
-    pub(crate) const STATUS_UNDERFLOW: u32 = 0x00000002;
-    pub(crate) const STATUS_OVERFLOW: u32 = 0x00000004;
-    pub(crate) const STATUS_F2_INTR: u32 = 0x00000008;
-    pub(crate) const STATUS_F3_INTR: u32 = 0x00000010;
-    pub(crate) const STATUS_F2_RX_READY: u32 = 0x00000020;
-    pub(crate) const STATUS_F3_RX_READY: u32 = 0x00000040;
-    pub(crate) const STATUS_HOST_CMD_DATA_ERR: u32 = 0x00000080;
-    pub(crate) const STATUS_F2_PKT_AVAILABLE: u32 = 0x00000100;
-    pub(crate) const STATUS_F2_PKT_LEN_MASK: u32 = 0x000FFE00;
-    pub(crate) const STATUS_F2_PKT_LEN_SHIFT: u32 = 9;
-    pub(crate) const STATUS_F3_PKT_AVAILABLE: u32 = 0x00100000;
-    pub(crate) const STATUS_F3_PKT_LEN_MASK: u32 = 0xFFE00000;
-    pub(crate) const STATUS_F3_PKT_LEN_SHIFT: u32 = 21;
-
-    pub(crate) const REG_BACKPLANE_GPIO_SELECT: u32 = 0x10005;
-    pub(crate) const REG_BACKPLANE_GPIO_OUTPUT: u32 = 0x10006;
-    pub(crate) const REG_BACKPLANE_GPIO_ENABLE: u32 = 0x10007;
-    pub(crate) const REG_BACKPLANE_FUNCTION2_WATERMARK: u32 = 0x10008;
-    pub(crate) const REG_BACKPLANE_DEVICE_CONTROL: u32 = 0x10009;
-    pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_LOW: u32 = 0x1000A;
-    pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_MID: u32 = 0x1000B;
-    pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH: u32 = 0x1000C;
-    pub(crate) const REG_BACKPLANE_FRAME_CONTROL: u32 = 0x1000D;
-    pub(crate) const REG_BACKPLANE_CHIP_CLOCK_CSR: u32 = 0x1000E;
-    pub(crate) const REG_BACKPLANE_PULL_UP: u32 = 0x1000F;
-    pub(crate) const REG_BACKPLANE_READ_FRAME_BC_LOW: u32 = 0x1001B;
-    pub(crate) const REG_BACKPLANE_READ_FRAME_BC_HIGH: u32 = 0x1001C;
-    pub(crate) const REG_BACKPLANE_WAKEUP_CTRL: u32 = 0x1001E;
-    pub(crate) const REG_BACKPLANE_SLEEP_CSR: u32 = 0x1001F;
-
-    pub(crate) const BACKPLANE_WINDOW_SIZE: usize = 0x8000;
-    pub(crate) const BACKPLANE_ADDRESS_MASK: u32 = 0x7FFF;
-    pub(crate) const BACKPLANE_ADDRESS_32BIT_FLAG: u32 = 0x08000;
-    pub(crate) const BACKPLANE_MAX_TRANSFER_SIZE: usize = 64;
-    // Active Low Power (ALP) clock constants
-    pub(crate) const BACKPLANE_ALP_AVAIL_REQ: u8 = 0x08;
-    pub(crate) const BACKPLANE_ALP_AVAIL: u8 = 0x40;
-
-    // Broadcom AMBA (Advanced Microcontroller Bus Architecture) Interconnect
-    // (AI) pub (crate) constants
-    pub(crate) const AI_IOCTRL_OFFSET: u32 = 0x408;
-    pub(crate) const AI_IOCTRL_BIT_FGC: u8 = 0x0002;
-    pub(crate) const AI_IOCTRL_BIT_CLOCK_EN: u8 = 0x0001;
-    pub(crate) const AI_IOCTRL_BIT_CPUHALT: u8 = 0x0020;
-
-    pub(crate) const AI_RESETCTRL_OFFSET: u32 = 0x800;
-    pub(crate) const AI_RESETCTRL_BIT_RESET: u8 = 1;
-
-    pub(crate) const AI_RESETSTATUS_OFFSET: u32 = 0x804;
-
-    pub(crate) const TEST_PATTERN: u32 = 0x12345678;
-    pub(crate) const FEEDBEAD: u32 = 0xFEEDBEAD;
-
-    // SPI_INTERRUPT_REGISTER and SPI_INTERRUPT_ENABLE_REGISTER Bits
-    pub(crate) const IRQ_DATA_UNAVAILABLE: u16 = 0x0001; // Requested data not available; Clear by writing a "1"
-    pub(crate) const IRQ_F2_F3_FIFO_RD_UNDERFLOW: u16 = 0x0002;
-    pub(crate) const IRQ_F2_F3_FIFO_WR_OVERFLOW: u16 = 0x0004;
-    pub(crate) const IRQ_COMMAND_ERROR: u16 = 0x0008; // Cleared by writing 1
-    pub(crate) const IRQ_DATA_ERROR: u16 = 0x0010; // Cleared by writing 1
-    pub(crate) const IRQ_F2_PACKET_AVAILABLE: u16 = 0x0020;
-    pub(crate) const IRQ_F3_PACKET_AVAILABLE: u16 = 0x0040;
-    pub(crate) const IRQ_F1_OVERFLOW: u16 = 0x0080; // Due to last write. Bkplane has pending write requests
-    pub(crate) const IRQ_MISC_INTR0: u16 = 0x0100;
-    pub(crate) const IRQ_MISC_INTR1: u16 = 0x0200;
-    pub(crate) const IRQ_MISC_INTR2: u16 = 0x0400;
-    pub(crate) const IRQ_MISC_INTR3: u16 = 0x0800;
-    pub(crate) const IRQ_MISC_INTR4: u16 = 0x1000;
-    pub(crate) const IRQ_F1_INTR: u16 = 0x2000;
-    pub(crate) const IRQ_F2_INTR: u16 = 0x4000;
-    pub(crate) const IRQ_F3_INTR: u16 = 0x8000;
-
-    pub(crate) const IOCTL_CMD_UP: u32 = 2;
-    pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26;
-    pub(crate) const IOCTL_CMD_ANTDIV: u32 = 64;
-    pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263;
-    pub(crate) const IOCTL_CMD_GET_VAR: u32 = 262;
-    pub(crate) const IOCTL_CMD_SET_PASSPHRASE: u32 = 268;
-
-    pub(crate) const CHANNEL_TYPE_CONTROL: u8 = 0;
-    pub(crate) const CHANNEL_TYPE_EVENT: u8 = 1;
-    pub(crate) const CHANNEL_TYPE_DATA: u8 = 2;
-
-    // CYW_SPID command structure constants.
-    pub(crate) const WRITE: bool = true;
-    pub(crate) const READ: bool = false;
-    pub(crate) const INC_ADDR: bool = true;
-    pub(crate) const FIXED_ADDR: bool = false;
-}
-use crate::constants::*;
+use crate::structs::*;
 
 #[derive(Clone, Copy)]
 pub enum IoctlType {
@@ -565,15 +447,11 @@ impl<'a> embassy_net::Device for NetDevice<'a> {
 }
 
 pub struct Runner<'a, PWR, SPI> {
+    bus: Bus<PWR, SPI>,
+
     state: &'a State,
-
-    pwr: PWR,
-    spi: SPI,
-
     ioctl_id: u16,
     sdpcm_seq: u8,
-    backplane_window: u32,
-
     sdpcm_seq_max: u8,
 
     #[cfg(feature = "firmware-logs")]
@@ -600,14 +478,11 @@ where
     SPI::Bus: SpiBusRead<u32> + SpiBusWrite<u32>,
 {
     let mut runner = Runner {
-        state,
-        pwr,
-        spi,
+        bus: Bus::new(pwr, spi),
 
+        state,
         ioctl_id: 0,
         sdpcm_seq: 0,
-        backplane_window: 0xAAAA_AAAA,
-
         sdpcm_seq_max: 1,
 
         #[cfg(feature = "firmware-logs")]
@@ -631,62 +506,41 @@ where
     SPI::Bus: SpiBusRead<u32> + SpiBusWrite<u32>,
 {
     async fn init(&mut self, firmware: &[u8]) {
-        // Reset
-        self.pwr.set_low().unwrap();
-        Timer::after(Duration::from_millis(20)).await;
-        self.pwr.set_high().unwrap();
-        Timer::after(Duration::from_millis(250)).await;
-
-        info!("waiting for ping...");
-        while self.read32_swapped(REG_BUS_TEST_RO).await != FEEDBEAD {}
-        info!("ping ok");
-
-        self.write32_swapped(REG_BUS_TEST_RW, TEST_PATTERN).await;
-        let val = self.read32_swapped(REG_BUS_TEST_RW).await;
-        assert_eq!(val, TEST_PATTERN);
-
-        // 32-bit word length, little endian (which is the default endianess).
-        self.write32_swapped(REG_BUS_CTRL, WORD_LENGTH_32 | HIGH_SPEED).await;
-
-        let val = self.read32(FUNC_BUS, REG_BUS_TEST_RO).await;
-        assert_eq!(val, FEEDBEAD);
-        let val = self.read32(FUNC_BUS, REG_BUS_TEST_RW).await;
-        assert_eq!(val, TEST_PATTERN);
-
-        // No response delay in any of the funcs.
-        // seems to break backplane??? eat the 4-byte delay instead, that's what the vendor drivers do...
-        //self.write32(FUNC_BUS, REG_BUS_RESP_DELAY, 0).await;
+        self.bus.init().await;
 
         // Init ALP (Active Low Power) clock
-        self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, BACKPLANE_ALP_AVAIL_REQ)
+        self.bus
+            .write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, BACKPLANE_ALP_AVAIL_REQ)
             .await;
         info!("waiting for clock...");
-        while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & BACKPLANE_ALP_AVAIL == 0 {}
+        while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & BACKPLANE_ALP_AVAIL == 0 {}
         info!("clock ok");
 
-        let chip_id = self.bp_read16(0x1800_0000).await;
+        let chip_id = self.bus.bp_read16(0x1800_0000).await;
         info!("chip ID: {}", chip_id);
 
         // Upload firmware.
         self.core_disable(Core::WLAN).await;
         self.core_reset(Core::SOCSRAM).await;
-        self.bp_write32(CHIP.socsram_base_address + 0x10, 3).await;
-        self.bp_write32(CHIP.socsram_base_address + 0x44, 0).await;
+        self.bus.bp_write32(CHIP.socsram_base_address + 0x10, 3).await;
+        self.bus.bp_write32(CHIP.socsram_base_address + 0x44, 0).await;
 
         let ram_addr = CHIP.atcm_ram_base_address;
 
         info!("loading fw");
-        self.bp_write(ram_addr, firmware).await;
+        self.bus.bp_write(ram_addr, firmware).await;
 
         info!("loading nvram");
         // Round up to 4 bytes.
         let nvram_len = (NVRAM.len() + 3) / 4 * 4;
-        self.bp_write(ram_addr + CHIP.chip_ram_size - 4 - nvram_len as u32, NVRAM)
+        self.bus
+            .bp_write(ram_addr + CHIP.chip_ram_size - 4 - nvram_len as u32, NVRAM)
             .await;
 
         let nvram_len_words = nvram_len as u32 / 4;
         let nvram_len_magic = (!nvram_len_words << 16) | nvram_len_words;
-        self.bp_write32(ram_addr + CHIP.chip_ram_size - 4, nvram_len_magic)
+        self.bus
+            .bp_write32(ram_addr + CHIP.chip_ram_size - 4, nvram_len_magic)
             .await;
 
         // Start core!
@@ -694,18 +548,20 @@ where
         self.core_reset(Core::WLAN).await;
         assert!(self.core_is_up(Core::WLAN).await);
 
-        while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {}
+        while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {}
 
         // "Set up the interrupt mask and enable interrupts"
-        self.bp_write32(CHIP.sdiod_core_base_address + 0x24, 0xF0).await;
+        self.bus.bp_write32(CHIP.sdiod_core_base_address + 0x24, 0xF0).await;
 
         // "Lower F2 Watermark to avoid DMA Hang in F2 when SD Clock is stopped."
         // Sounds scary...
-        self.write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 32).await;
+        self.bus
+            .write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 32)
+            .await;
 
         // wait for wifi startup
         info!("waiting for wifi init...");
-        while self.read32(FUNC_BUS, REG_BUS_STATUS).await & STATUS_F2_RX_READY == 0 {}
+        while self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await & STATUS_F2_RX_READY == 0 {}
 
         // Some random configs related to sleep.
         // These aren't needed if we don't want to sleep the bus.
@@ -713,25 +569,25 @@ where
         // being on the same pin as MOSI/MISO?
 
         /*
-        let mut val = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL).await;
+        let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL).await;
         val |= 0x02; // WAKE_TILL_HT_AVAIL
-        self.write8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL, val).await;
-        self.write8(FUNC_BUS, 0xF0, 0x08).await; // SDIOD_CCCR_BRCM_CARDCAP.CMD_NODEC = 1
-        self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x02).await; // SBSDIO_FORCE_HT
+        self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL, val).await;
+        self.bus.write8(FUNC_BUS, 0xF0, 0x08).await; // SDIOD_CCCR_BRCM_CARDCAP.CMD_NODEC = 1
+        self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x02).await; // SBSDIO_FORCE_HT
 
-        let mut val = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR).await;
+        let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR).await;
         val |= 0x01; // SBSDIO_SLPCSR_KEEP_SDIO_ON
-        self.write8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR, val).await;
+        self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR, val).await;
          */
 
         // clear pulls
-        self.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0).await;
-        let _ = self.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP).await;
+        self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0).await;
+        let _ = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP).await;
 
         // start HT clock
-        //self.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10).await;
+        //self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10).await;
         //info!("waiting for HT clock...");
-        //while self.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {}
+        //while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {}
         //info!("clock ok");
 
         #[cfg(feature = "firmware-logs")]
@@ -744,13 +600,12 @@ where
     async fn log_init(&mut self) {
         // Initialize shared memory for logging.
 
-        let shared_addr = self
-            .bp_read32(CHIP.atcm_ram_base_address + CHIP.chip_ram_size - 4 - CHIP.socram_srmem_size)
-            .await;
+        let addr = CHIP.atcm_ram_base_address + CHIP.chip_ram_size - 4 - CHIP.socram_srmem_size;
+        let shared_addr = self.bus.bp_read32(addr).await;
         info!("shared_addr {:08x}", shared_addr);
 
         let mut shared = [0; SharedMemData::SIZE];
-        self.bp_read(shared_addr, &mut shared).await;
+        self.bus.bp_read(shared_addr, &mut shared).await;
         let shared = SharedMemData::from_bytes(&shared);
         info!("shared: {:08x}", shared);
 
@@ -761,7 +616,7 @@ where
     async fn log_read(&mut self) {
         // Read log struct
         let mut log = [0; SharedMemLog::SIZE];
-        self.bp_read(self.log.addr, &mut log).await;
+        self.bus.bp_read(self.log.addr, &mut log).await;
         let log = SharedMemLog::from_bytes(&log);
 
         let idx = log.idx as usize;
@@ -774,7 +629,7 @@ where
         // Read entire buf for now. We could read only what we need, but then we
         // run into annoying alignment issues in `bp_read`.
         let mut buf = [0; 0x400];
-        self.bp_read(log.buf, &mut buf).await;
+        self.bus.bp_read(log.buf, &mut buf).await;
 
         while self.log.last_idx != idx as usize {
             let b = buf[self.log.last_idx];
@@ -821,29 +676,19 @@ where
             }
 
             // Receive stuff
-            let irq = self.read16(FUNC_BUS, REG_BUS_INTERRUPT).await;
+            let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await;
 
             if irq & IRQ_F2_PACKET_AVAILABLE != 0 {
                 let mut status = 0xFFFF_FFFF;
                 while status == 0xFFFF_FFFF {
-                    status = self.read32(FUNC_BUS, REG_BUS_STATUS).await;
+                    status = self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await;
                 }
 
                 if status & STATUS_F2_PKT_AVAILABLE != 0 {
                     let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT;
 
-                    let cmd = cmd_word(READ, INC_ADDR, FUNC_WLAN, 0, len);
-
-                    transaction!(&mut self.spi, |bus| async {
-                        bus.write(&[cmd]).await?;
-                        bus.read(&mut buf[..(len as usize + 3) / 4]).await?;
-                        Ok(())
-                    })
-                    .await
-                    .unwrap();
-
+                    self.bus.wlan_read(&mut buf[..(len as usize + 3) / 4]).await;
                     trace!("rx {:02x}", &slice8_mut(&mut buf)[..(len as usize).min(48)]);
-
                     self.rx(&slice8_mut(&mut buf)[..len as usize]);
                 }
             }
@@ -893,14 +738,7 @@ where
 
         trace!("    {:02x}", &buf8[..total_len.min(48)]);
 
-        let cmd = cmd_word(WRITE, INC_ADDR, FUNC_WLAN, 0, total_len as _);
-        transaction!(&mut self.spi, |bus| async {
-            bus.write(&[cmd]).await?;
-            bus.write(&buf[..(total_len / 4)]).await?;
-            Ok(())
-        })
-        .await
-        .unwrap();
+        self.bus.wlan_write(&buf[..(total_len / 4)]).await;
     }
 
     fn rx(&mut self, packet: &[u8]) {
@@ -1086,52 +924,49 @@ where
 
         trace!("    {:02x}", &buf8[..total_len.min(48)]);
 
-        let cmd = cmd_word(WRITE, INC_ADDR, FUNC_WLAN, 0, total_len as _);
-
-        transaction!(&mut self.spi, |bus| async {
-            bus.write(&[cmd]).await?;
-            bus.write(&buf[..total_len / 4]).await?;
-            Ok(())
-        })
-        .await
-        .unwrap();
+        self.bus.wlan_write(&buf[..total_len / 4]).await;
     }
 
     async fn core_disable(&mut self, core: Core) {
         let base = core.base_addr();
 
         // Dummy read?
-        let _ = self.bp_read8(base + AI_RESETCTRL_OFFSET).await;
+        let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await;
 
         // Check it isn't already reset
-        let r = self.bp_read8(base + AI_RESETCTRL_OFFSET).await;
+        let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await;
         if r & AI_RESETCTRL_BIT_RESET != 0 {
             return;
         }
 
-        self.bp_write8(base + AI_IOCTRL_OFFSET, 0).await;
-        let _ = self.bp_read8(base + AI_IOCTRL_OFFSET).await;
+        self.bus.bp_write8(base + AI_IOCTRL_OFFSET, 0).await;
+        let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;
 
         block_for(Duration::from_millis(1));
 
-        self.bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET).await;
-        let _ = self.bp_read8(base + AI_RESETCTRL_OFFSET).await;
+        self.bus
+            .bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET)
+            .await;
+        let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await;
     }
 
     async fn core_reset(&mut self, core: Core) {
         self.core_disable(core).await;
 
         let base = core.base_addr();
-        self.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN)
+        self.bus
+            .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN)
             .await;
-        let _ = self.bp_read8(base + AI_IOCTRL_OFFSET).await;
+        let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;
 
-        self.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await;
+        self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await;
 
         Timer::after(Duration::from_millis(1)).await;
 
-        self.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN).await;
-        let _ = self.bp_read8(base + AI_IOCTRL_OFFSET).await;
+        self.bus
+            .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN)
+            .await;
+        let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;
 
         Timer::after(Duration::from_millis(1)).await;
     }
@@ -1139,13 +974,13 @@ where
     async fn core_is_up(&mut self, core: Core) -> bool {
         let base = core.base_addr();
 
-        let io = self.bp_read8(base + AI_IOCTRL_OFFSET).await;
+        let io = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;
         if io & (AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) != AI_IOCTRL_BIT_CLOCK_EN {
             debug!("core_is_up: returning false due to bad ioctrl {:02x}", io);
             return false;
         }
 
-        let r = self.bp_read8(base + AI_RESETCTRL_OFFSET).await;
+        let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await;
         if r & (AI_RESETCTRL_BIT_RESET) != 0 {
             debug!("core_is_up: returning false due to bad resetctrl {:02x}", r);
             return false;
@@ -1153,242 +988,11 @@ where
 
         true
     }
+}
 
-    #[allow(unused)]
-    async fn bp_read(&mut self, mut addr: u32, mut data: &mut [u8]) {
-        // It seems the HW force-aligns the addr
-        // to 2 if data.len() >= 2
-        // to 4 if data.len() >= 4
-        // To simplify, enforce 4-align for now.
-        assert!(addr % 4 == 0);
-
-        let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4];
-
-        while !data.is_empty() {
-            // Ensure transfer doesn't cross a window boundary.
-            let window_offs = addr & BACKPLANE_ADDRESS_MASK;
-            let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize;
-
-            let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining);
-
-            self.backplane_set_window(addr).await;
-
-            let cmd = cmd_word(READ, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32);
-
-            transaction!(&mut self.spi, |bus| async {
-                bus.write(&[cmd]).await?;
-
-                // 4-byte response delay.
-                let mut junk = [0; 1];
-                bus.read(&mut junk).await?;
-
-                // Read data
-                bus.read(&mut buf[..(len + 3) / 4]).await?;
-                Ok(())
-            })
-            .await
-            .unwrap();
-
-            data[..len].copy_from_slice(&slice8_mut(&mut buf)[..len]);
-
-            // Advance ptr.
-            addr += len as u32;
-            data = &mut data[len..];
-        }
-    }
-
-    async fn bp_write(&mut self, mut addr: u32, mut data: &[u8]) {
-        // It seems the HW force-aligns the addr
-        // to 2 if data.len() >= 2
-        // to 4 if data.len() >= 4
-        // To simplify, enforce 4-align for now.
-        assert!(addr % 4 == 0);
-
-        let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4];
-
-        while !data.is_empty() {
-            // Ensure transfer doesn't cross a window boundary.
-            let window_offs = addr & BACKPLANE_ADDRESS_MASK;
-            let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize;
-
-            let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining);
-            slice8_mut(&mut buf)[..len].copy_from_slice(&data[..len]);
-
-            self.backplane_set_window(addr).await;
-
-            let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32);
-
-            transaction!(&mut self.spi, |bus| async {
-                bus.write(&[cmd]).await?;
-                bus.write(&buf[..(len + 3) / 4]).await?;
-                Ok(())
-            })
-            .await
-            .unwrap();
-
-            // Advance ptr.
-            addr += len as u32;
-            data = &data[len..];
-        }
-    }
-
-    async fn bp_read8(&mut self, addr: u32) -> u8 {
-        self.backplane_readn(addr, 1).await as u8
-    }
-
-    async fn bp_write8(&mut self, addr: u32, val: u8) {
-        self.backplane_writen(addr, val as u32, 1).await
-    }
-
-    async fn bp_read16(&mut self, addr: u32) -> u16 {
-        self.backplane_readn(addr, 2).await as u16
-    }
-
-    #[allow(unused)]
-    async fn bp_write16(&mut self, addr: u32, val: u16) {
-        self.backplane_writen(addr, val as u32, 2).await
-    }
-
-    #[allow(unused)]
-    async fn bp_read32(&mut self, addr: u32) -> u32 {
-        self.backplane_readn(addr, 4).await
-    }
-
-    async fn bp_write32(&mut self, addr: u32, val: u32) {
-        self.backplane_writen(addr, val, 4).await
-    }
-
-    async fn backplane_readn(&mut self, addr: u32, len: u32) -> u32 {
-        self.backplane_set_window(addr).await;
-
-        let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK;
-        if len == 4 {
-            bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG
-        }
-        self.readn(FUNC_BACKPLANE, bus_addr, len).await
-    }
-
-    async fn backplane_writen(&mut self, addr: u32, val: u32, len: u32) {
-        self.backplane_set_window(addr).await;
-
-        let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK;
-        if len == 4 {
-            bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG
-        }
-        self.writen(FUNC_BACKPLANE, bus_addr, val, len).await
-    }
-
-    async fn backplane_set_window(&mut self, addr: u32) {
-        let new_window = addr & !BACKPLANE_ADDRESS_MASK;
-
-        if (new_window >> 24) as u8 != (self.backplane_window >> 24) as u8 {
-            self.write8(
-                FUNC_BACKPLANE,
-                REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH,
-                (new_window >> 24) as u8,
-            )
-            .await;
-        }
-        if (new_window >> 16) as u8 != (self.backplane_window >> 16) as u8 {
-            self.write8(
-                FUNC_BACKPLANE,
-                REG_BACKPLANE_BACKPLANE_ADDRESS_MID,
-                (new_window >> 16) as u8,
-            )
-            .await;
-        }
-        if (new_window >> 8) as u8 != (self.backplane_window >> 8) as u8 {
-            self.write8(
-                FUNC_BACKPLANE,
-                REG_BACKPLANE_BACKPLANE_ADDRESS_LOW,
-                (new_window >> 8) as u8,
-            )
-            .await;
-        }
-        self.backplane_window = new_window;
-    }
-
-    async fn read8(&mut self, func: u32, addr: u32) -> u8 {
-        self.readn(func, addr, 1).await as u8
-    }
-
-    async fn write8(&mut self, func: u32, addr: u32, val: u8) {
-        self.writen(func, addr, val as u32, 1).await
-    }
-
-    async fn read16(&mut self, func: u32, addr: u32) -> u16 {
-        self.readn(func, addr, 2).await as u16
-    }
-
-    #[allow(unused)]
-    async fn write16(&mut self, func: u32, addr: u32, val: u16) {
-        self.writen(func, addr, val as u32, 2).await
-    }
-
-    async fn read32(&mut self, func: u32, addr: u32) -> u32 {
-        self.readn(func, addr, 4).await
-    }
-
-    #[allow(unused)]
-    async fn write32(&mut self, func: u32, addr: u32, val: u32) {
-        self.writen(func, addr, val, 4).await
-    }
-
-    async fn readn(&mut self, func: u32, addr: u32, len: u32) -> u32 {
-        let cmd = cmd_word(READ, INC_ADDR, func, addr, len);
-        let mut buf = [0; 1];
-
-        transaction!(&mut self.spi, |bus| async {
-            bus.write(&[cmd]).await?;
-            if func == FUNC_BACKPLANE {
-                // 4-byte response delay.
-                bus.read(&mut buf).await?;
-            }
-            bus.read(&mut buf).await?;
-            Ok(())
-        })
-        .await
-        .unwrap();
-
-        buf[0]
-    }
-
-    async fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) {
-        let cmd = cmd_word(WRITE, INC_ADDR, func, addr, len);
-
-        transaction!(&mut self.spi, |bus| async {
-            bus.write(&[cmd, val]).await?;
-            Ok(())
-        })
-        .await
-        .unwrap();
-    }
-
-    async fn read32_swapped(&mut self, addr: u32) -> u32 {
-        let cmd = cmd_word(READ, INC_ADDR, FUNC_BUS, addr, 4);
-        let mut buf = [0; 1];
-
-        transaction!(&mut self.spi, |bus| async {
-            bus.write(&[swap16(cmd)]).await?;
-            bus.read(&mut buf).await?;
-            Ok(())
-        })
-        .await
-        .unwrap();
-
-        swap16(buf[0])
-    }
-
-    async fn write32_swapped(&mut self, addr: u32, val: u32) {
-        let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BUS, addr, 4);
-
-        transaction!(&mut self.spi, |bus| async {
-            bus.write(&[swap16(cmd), swap16(val)]).await?;
-            Ok(())
-        })
-        .await
-        .unwrap();
-    }
+fn slice8_mut(x: &mut [u32]) -> &mut [u8] {
+    let len = x.len() * 4;
+    unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) }
 }
 
 macro_rules! nvram {