From a9ba5145e2f8a39c8b2a14c2322b6fe8daff9049 Mon Sep 17 00:00:00 2001 From: Brandon Ros Date: Thu, 24 Aug 2023 14:23:57 -0400 Subject: [PATCH] blend firmware + HCI PRs together to call out design problem --- cyw43/Cargo.toml | 1 + cyw43/src/bluetooth.rs | 197 +------------ cyw43/src/bus.rs | 34 +-- cyw43/src/consts.rs | 9 + cyw43/src/control.rs | 111 ++++---- cyw43/src/hci_connector.rs | 36 +++ cyw43/src/lib.rs | 16 +- cyw43/src/runner.rs | 319 ++++++++++++++++++++-- cyw43/src/utilities.rs | 18 ++ examples/rp/src/bin/bluetooth_blinky.rs | 2 +- examples/rp/src/bin/wifi_ap_tcp_server.rs | 2 +- examples/rp/src/bin/wifi_blinky.rs | 2 +- examples/rp/src/bin/wifi_scan.rs | 2 +- examples/rp/src/bin/wifi_tcp_server.rs | 2 +- 14 files changed, 455 insertions(+), 296 deletions(-) create mode 100644 cyw43/src/hci_connector.rs create mode 100644 cyw43/src/utilities.rs diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index 855d54b1..f0cbafeb 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml @@ -25,6 +25,7 @@ cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.1" } +embedded-io-async = { version = "0.5.0", features = ["defmt-03"] } num_enum = { version = "0.5.7", default-features = false } [package.metadata.embassy_docs] diff --git a/cyw43/src/bluetooth.rs b/cyw43/src/bluetooth.rs index 8914f5ea..3091b699 100644 --- a/cyw43/src/bluetooth.rs +++ b/cyw43/src/bluetooth.rs @@ -1,30 +1,17 @@ -use embassy_time::{Duration, Timer}; -use embedded_hal_1::digital::OutputPin; - -use crate::bus::Bus; use crate::consts::*; -use crate::{SpiBusCyw43, CHIP}; -struct CybtFwCb<'a> { - p_next_line_start: &'a [u8], +pub(crate) struct CybtFwCb<'a> { + pub p_next_line_start: &'a [u8], } -struct HexFileData<'a> { - addr_mode: i32, - hi_addr: u16, - dest_addr: u32, - p_ds: &'a mut [u8], +pub(crate) struct HexFileData<'a> { + pub addr_mode: i32, + pub hi_addr: u16, + pub dest_addr: u32, + pub p_ds: &'a mut [u8], } -fn is_aligned(a: u32, x: u32) -> bool { - (a & (x - 1)) == 0 -} - -fn round_down(x: u32, a: u32) -> u32 { - x & !(a - 1) -} - -fn read_firmware_patch_line(p_btfw_cb: &mut CybtFwCb, hfd: &mut HexFileData) -> u32 { +pub(crate) fn read_firmware_patch_line(p_btfw_cb: &mut CybtFwCb, hfd: &mut HexFileData) -> u32 { let mut abs_base_addr32 = 0; loop { @@ -75,171 +62,3 @@ fn read_firmware_patch_line(p_btfw_cb: &mut CybtFwCb, hfd: &mut HexFileData) -> } 0 } - -pub(crate) async fn upload_bluetooth_firmware( - bus: &mut Bus, - firmware: &[u8], -) { - // read version - let version_length = firmware[0]; - let _version = &firmware[1..=version_length as usize]; - // skip version + 1 extra byte as per cybt_shared_bus_driver.c - let firmware = &firmware[version_length as usize + 2..]; - // buffers - let mut data_buffer: [u8; 0x100] = [0; 0x100]; - let mut aligned_data_buffer: [u8; 0x100] = [0; 0x100]; - // structs - let mut btfw_cb = CybtFwCb { - p_next_line_start: firmware, - }; - let mut hfd = HexFileData { - addr_mode: BTFW_ADDR_MODE_EXTENDED, - hi_addr: 0, - dest_addr: 0, - p_ds: &mut data_buffer, - }; - loop { - let num_fw_bytes = read_firmware_patch_line(&mut btfw_cb, &mut hfd); - if num_fw_bytes == 0 { - break; - } - let fw_bytes = &hfd.p_ds[0..num_fw_bytes as usize]; - let mut dest_start_addr = hfd.dest_addr + CHIP.bluetooth_base_address; - let mut aligned_data_buffer_index: usize = 0; - // pad start - if !is_aligned(dest_start_addr, 4) { - let num_pad_bytes = dest_start_addr % 4; - let padded_dest_start_addr = round_down(dest_start_addr, 4); - let memory_value = bus.bp_read32(padded_dest_start_addr).await; - let memory_value_bytes = memory_value.to_le_bytes(); // TODO: le or be - // Copy the previous memory value's bytes to the start - for i in 0..num_pad_bytes as usize { - aligned_data_buffer[aligned_data_buffer_index] = memory_value_bytes[i]; - aligned_data_buffer_index += 1; - } - // Copy the firmware bytes after the padding bytes - for i in 0..num_fw_bytes as usize { - aligned_data_buffer[aligned_data_buffer_index] = fw_bytes[i]; - aligned_data_buffer_index += 1; - } - dest_start_addr = padded_dest_start_addr; - } else { - // Directly copy fw_bytes into aligned_data_buffer if no start padding is required - for i in 0..num_fw_bytes as usize { - aligned_data_buffer[aligned_data_buffer_index] = fw_bytes[i]; - aligned_data_buffer_index += 1; - } - } - // pad end - let mut dest_end_addr = dest_start_addr + aligned_data_buffer_index as u32; - if !is_aligned(dest_end_addr, 4) { - let offset = dest_end_addr % 4; - let num_pad_bytes_end = 4 - offset; - let padded_dest_end_addr = round_down(dest_end_addr, 4); - let memory_value = bus.bp_read32(padded_dest_end_addr).await; - let memory_value_bytes = memory_value.to_le_bytes(); // TODO: le or be - // Append the necessary memory bytes to pad the end of aligned_data_buffer - for i in offset..4 { - aligned_data_buffer[aligned_data_buffer_index] = memory_value_bytes[i as usize]; - aligned_data_buffer_index += 1; - } - dest_end_addr += num_pad_bytes_end; - } else { - // pad end alignment not needed - } - let buffer_to_write = &aligned_data_buffer[0..aligned_data_buffer_index as usize]; - assert!(dest_start_addr % 4 == 0); - assert!(dest_end_addr % 4 == 0); - assert!(aligned_data_buffer_index % 4 == 0); - // write in 0x40 chunks TODO: is this needed or can we write straight away - let chunk_size = 0x40; - for (i, chunk) in buffer_to_write.chunks(chunk_size).enumerate() { - let offset = i * chunk_size; - bus.bp_write(dest_start_addr + (offset as u32), chunk).await; - } - // sleep TODO: is this needed - Timer::after(Duration::from_millis(1)).await; - } -} - -pub(crate) async fn wait_bt_ready(bus: &mut Bus) { - debug!("wait_bt_ready"); - let mut success = false; - for _ in 0..300 { - let val = bus.bp_read32(BT_CTRL_REG_ADDR).await; - // TODO: do we need to swap endianness on this read? - debug!("BT_CTRL_REG_ADDR = {:08x}", val); - if val & BTSDIO_REG_FW_RDY_BITMASK != 0 { - success = true; - break; - } - Timer::after(Duration::from_millis(1)).await; - } - assert!(success == true); -} - -pub(crate) async fn wait_bt_awake(bus: &mut Bus) { - debug!("wait_bt_awake"); - let mut success = false; - for _ in 0..300 { - let val = bus.bp_read32(BT_CTRL_REG_ADDR).await; - // TODO: do we need to swap endianness on this read? - debug!("BT_CTRL_REG_ADDR = {:08x}", val); - if val & BTSDIO_REG_BT_AWAKE_BITMASK != 0 { - success = true; - break; - } - Timer::after(Duration::from_millis(1)).await; - } - assert!(success == true); -} - -pub(crate) async fn bt_set_host_ready(bus: &mut Bus) { - debug!("bt_set_host_ready"); - let old_val = bus.bp_read32(HOST_CTRL_REG_ADDR).await; - // TODO: do we need to swap endianness on this read? - let new_val = old_val | BTSDIO_REG_SW_RDY_BITMASK; - bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await; -} - -// TODO: use this -#[allow(dead_code)] -pub(crate) async fn bt_set_awake(bus: &mut Bus) { - debug!("bt_set_awake"); - let old_val = bus.bp_read32(HOST_CTRL_REG_ADDR).await; - // TODO: do we need to swap endianness on this read? - let new_val = old_val | BTSDIO_REG_WAKE_BT_BITMASK; - bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await; -} - -pub(crate) async fn bt_toggle_intr(bus: &mut Bus) { - debug!("bt_toggle_intr"); - let old_val = bus.bp_read32(HOST_CTRL_REG_ADDR).await; - // TODO: do we need to swap endianness on this read? - let new_val = old_val ^ BTSDIO_REG_DATA_VALID_BITMASK; - bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await; -} - -// TODO: use this -#[allow(dead_code)] -pub(crate) async fn bt_set_intr(bus: &mut Bus) { - debug!("bt_set_intr"); - let old_val = bus.bp_read32(HOST_CTRL_REG_ADDR).await; - let new_val = old_val | BTSDIO_REG_DATA_VALID_BITMASK; - bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await; -} - -pub(crate) async fn init_bluetooth(bus: &mut Bus, firmware: &[u8]) { - Timer::after(Duration::from_millis(100)).await; - debug!("init_bluetooth"); - Timer::after(Duration::from_millis(100)).await; - bus.bp_write32(CHIP.bluetooth_base_address + BT2WLAN_PWRUP_ADDR, BT2WLAN_PWRUP_WAKE) - .await; - Timer::after(Duration::from_millis(2)).await; - upload_bluetooth_firmware(bus, firmware).await; - wait_bt_ready(bus).await; - // TODO: cybt_init_buffer(); - wait_bt_awake(bus).await; - bt_set_host_ready(bus).await; - bt_toggle_intr(bus).await; -} diff --git a/cyw43/src/bus.rs b/cyw43/src/bus.rs index 5749fa8e..fc50135e 100644 --- a/cyw43/src/bus.rs +++ b/cyw43/src/bus.rs @@ -4,7 +4,7 @@ use embedded_hal_1::digital::OutputPin; use futures::FutureExt; use crate::consts::*; -use crate::slice8_mut; +use crate::utilities; /// Custom Spi Trait that _only_ supports the bus operation of the cyw43 /// Implementors are expected to hold the CS pin low during an operation. @@ -48,7 +48,7 @@ where } } - pub async fn init(&mut self) { + pub async fn init(&mut self, bluetooth_enabled: bool) { // Reset debug!("WL_REG off/on"); self.pwr.set_low().unwrap(); @@ -122,18 +122,16 @@ where // Enable a selection of interrupts // TODO: why not all of these F2_F3_FIFO_RD_UNDERFLOW | F2_F3_FIFO_WR_OVERFLOW | COMMAND_ERROR | DATA_ERROR | F2_PACKET_AVAILABLE | F1_OVERFLOW | F1_INTR debug!("enable a selection of interrupts"); - self.write16( - FUNC_BUS, - REG_BUS_INTERRUPT_ENABLE, - IRQ_F2_F3_FIFO_RD_UNDERFLOW - | IRQ_F2_F3_FIFO_WR_OVERFLOW - | IRQ_COMMAND_ERROR - | IRQ_DATA_ERROR - | IRQ_F2_PACKET_AVAILABLE - | IRQ_F1_OVERFLOW - | IRQ_F1_INTR, - ) - .await; + let mut val = IRQ_F2_F3_FIFO_RD_UNDERFLOW + | IRQ_F2_F3_FIFO_WR_OVERFLOW + | IRQ_COMMAND_ERROR + | IRQ_DATA_ERROR + | IRQ_F2_PACKET_AVAILABLE + | IRQ_F1_OVERFLOW; + if bluetooth_enabled { + val = val | IRQ_F1_INTR; + } + self.write16(FUNC_BUS, REG_BUS_INTERRUPT_ENABLE, val).await; } pub async fn wlan_read(&mut self, buf: &mut [u32], len_in_u8: u32) { @@ -155,6 +153,8 @@ where #[allow(unused)] pub async fn bp_read(&mut self, mut addr: u32, mut data: &mut [u8]) { + debug!("bp_read addr = {:08x}", addr); + // It seems the HW force-aligns the addr // to 2 if data.len() >= 2 // to 4 if data.len() >= 4 @@ -179,7 +179,7 @@ where self.status = self.spi.cmd_read(cmd, &mut buf[..(len + 3) / 4 + 1]).await; // when writing out the data, we skip the response-delay byte - data[..len].copy_from_slice(&slice8_mut(&mut buf[1..])[..len]); + data[..len].copy_from_slice(&utilities::slice8_mut(&mut buf[1..])[..len]); // Advance ptr. addr += len as u32; @@ -188,6 +188,8 @@ where } pub async fn bp_write(&mut self, mut addr: u32, mut data: &[u8]) { + debug!("bp_write addr = {:08x}", addr); + // It seems the HW force-aligns the addr // to 2 if data.len() >= 2 // to 4 if data.len() >= 4 @@ -202,7 +204,7 @@ where 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[1..])[..len].copy_from_slice(&data[..len]); + utilities::slice8_mut(&mut buf[1..])[..len].copy_from_slice(&data[..len]); self.backplane_set_window(addr).await; diff --git a/cyw43/src/consts.rs b/cyw43/src/consts.rs index f61d8b04..9798538c 100644 --- a/cyw43/src/consts.rs +++ b/cyw43/src/consts.rs @@ -168,6 +168,15 @@ pub(crate) const BTSDIO_REG_WAKE_BT_BITMASK: u32 = 1 << 17; pub(crate) const BTSDIO_REG_SW_RDY_BITMASK: u32 = 1 << 24; pub(crate) const BTSDIO_REG_FW_RDY_BITMASK: u32 = 1 << 24; +pub(crate) const BTSDIO_FWBUF_SIZE: u32 = 0x1000; +pub(crate) const BTSDIO_OFFSET_HOST_WRITE_BUF: u32 = 0; +pub(crate) const BTSDIO_OFFSET_HOST_READ_BUF: u32 = BTSDIO_FWBUF_SIZE; + +pub(crate) const BTSDIO_OFFSET_HOST2BT_IN: u32 = 0x00002000; +pub(crate) const BTSDIO_OFFSET_HOST2BT_OUT: u32 = 0x00002004; +pub(crate) const BTSDIO_OFFSET_BT2HOST_IN: u32 = 0x00002008; +pub(crate) const BTSDIO_OFFSET_BT2HOST_OUT: u32 = 0x0000200C; + // Security type (authentication and encryption types are combined using bit mask) #[allow(non_camel_case_types)] #[derive(Copy, Clone, PartialEq)] diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs index c67614dd..9ffb75b7 100644 --- a/cyw43/src/control.rs +++ b/cyw43/src/control.rs @@ -32,13 +32,11 @@ impl<'a> Control<'a> { } } - pub async fn init(&mut self, clm: &[u8]) { - const CHUNK_SIZE: usize = 1024; - - debug!("Downloading CLM..."); - + async fn load_clm(&mut self, clm: &[u8]) { + debug!("load_clm"); + const CLM_CHUNK_SIZE: usize = 1024; let mut offs = 0; - for chunk in clm.chunks(CHUNK_SIZE) { + for chunk in clm.chunks(CLM_CHUNK_SIZE) { let mut flag = DOWNLOAD_FLAG_HANDLER_VER; if offs == 0 { flag |= DOWNLOAD_FLAG_BEGIN; @@ -54,16 +52,19 @@ impl<'a> Control<'a> { len: chunk.len() as _, crc: 0, }; - let mut buf = [0; 8 + 12 + CHUNK_SIZE]; + let mut buf = [0; 8 + 12 + CLM_CHUNK_SIZE]; buf[0..8].copy_from_slice(b"clmload\x00"); buf[8..20].copy_from_slice(&header.to_bytes()); buf[20..][..chunk.len()].copy_from_slice(&chunk); self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..8 + 12 + chunk.len()]) .await; } - // check clmload ok assert_eq!(self.get_iovar_u32("clmload_status").await, 0); + } + + pub async fn init(&mut self, clm: &[u8], wifi_enabled: bool, bluetooth_enabled: bool) { + self.load_clm(&clm).await; debug!("Configuring misc stuff..."); @@ -78,64 +79,70 @@ impl<'a> Control<'a> { assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); debug!("mac addr: {:02x}", Bytes(&mac_addr)); - let country = countries::WORLD_WIDE_XX; - let country_info = CountryInfo { - country_abbrev: [country.code[0], country.code[1], 0, 0], - country_code: [country.code[0], country.code[1], 0, 0], - rev: if country.rev == 0 { -1 } else { country.rev as _ }, - }; - self.set_iovar("country", &country_info.to_bytes()).await; + if wifi_enabled { + let country = countries::WORLD_WIDE_XX; + let country_info = CountryInfo { + country_abbrev: [country.code[0], country.code[1], 0, 0], + country_code: [country.code[0], country.code[1], 0, 0], + rev: if country.rev == 0 { -1 } else { country.rev as _ }, + }; + self.set_iovar("country", &country_info.to_bytes()).await; - // set country takes some time, next ioctls fail if we don't wait. - Timer::after(Duration::from_millis(100)).await; + // set country takes some time, next ioctls fail if we don't wait. + Timer::after(Duration::from_millis(100)).await; - // Set antenna to chip antenna - self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await; + // Set antenna to chip antenna + self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await; - self.set_iovar_u32("bus:txglom", 0).await; - Timer::after(Duration::from_millis(100)).await; - //self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...?? - //Timer::after(Duration::from_millis(100)).await; - self.set_iovar_u32("ampdu_ba_wsize", 8).await; - Timer::after(Duration::from_millis(100)).await; - self.set_iovar_u32("ampdu_mpdu", 4).await; - Timer::after(Duration::from_millis(100)).await; - //self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes + self.set_iovar_u32("bus:txglom", 0).await; + Timer::after(Duration::from_millis(100)).await; + //self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...?? + //Timer::after(Duration::from_millis(100)).await; + self.set_iovar_u32("ampdu_ba_wsize", 8).await; + Timer::after(Duration::from_millis(100)).await; + self.set_iovar_u32("ampdu_mpdu", 4).await; + Timer::after(Duration::from_millis(100)).await; + //self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes - //Timer::after(Duration::from_millis(100)).await; + //Timer::after(Duration::from_millis(100)).await; - // evts - let mut evts = EventMask { - iface: 0, - events: [0xFF; 24], - }; + // evts + let mut evts = EventMask { + iface: 0, + events: [0xFF; 24], + }; - // Disable spammy uninteresting events. - evts.unset(Event::RADIO); - evts.unset(Event::IF); - evts.unset(Event::PROBREQ_MSG); - evts.unset(Event::PROBREQ_MSG_RX); - evts.unset(Event::PROBRESP_MSG); - evts.unset(Event::PROBRESP_MSG); - evts.unset(Event::ROAM); + // Disable spammy uninteresting events. + evts.unset(Event::RADIO); + evts.unset(Event::IF); + evts.unset(Event::PROBREQ_MSG); + evts.unset(Event::PROBREQ_MSG_RX); + evts.unset(Event::PROBRESP_MSG); + evts.unset(Event::PROBRESP_MSG); + evts.unset(Event::ROAM); - self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await; + self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await; - Timer::after(Duration::from_millis(100)).await; + Timer::after(Duration::from_millis(100)).await; - // set wifi up - self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; + // set wifi up + self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; - Timer::after(Duration::from_millis(100)).await; + Timer::after(Duration::from_millis(100)).await; - self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto - self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any + self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto + self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any - Timer::after(Duration::from_millis(100)).await; + Timer::after(Duration::from_millis(100)).await; - self.state_ch.set_ethernet_address(mac_addr); + self.state_ch.set_ethernet_address(mac_addr); + } - debug!("INIT DONE"); + if bluetooth_enabled { + // TODO: call runner.init_bluetooth somehow and pass it bluetooth_firmware? + } + + debug!("cyw43 control init done"); } pub async fn set_power_management(&mut self, mode: PowerManagementMode) { diff --git a/cyw43/src/hci_connector.rs b/cyw43/src/hci_connector.rs new file mode 100644 index 00000000..1a799512 --- /dev/null +++ b/cyw43/src/hci_connector.rs @@ -0,0 +1,36 @@ +pub struct HciConnector {} + +impl HciConnector { + pub fn new() -> HciConnector { + return HciConnector {}; + } +} + +#[derive(Debug)] +pub enum HciConnectorError { + Unknown, +} + +impl embedded_io_async::Error for HciConnectorError { + fn kind(&self) -> embedded_io_async::ErrorKind { + embedded_io_async::ErrorKind::Other + } +} + +impl embedded_io_async::ErrorType for HciConnector { + type Error = HciConnectorError; +} + +impl embedded_io_async::Read for HciConnector { + async fn read(&mut self, buf: &mut [u8]) -> Result { + // TODO: how to get all the way to runner.hci_read()? + Ok(0) + } +} + +impl embedded_io_async::Write for HciConnector { + async fn write(&mut self, buf: &[u8]) -> Result { + // TODO: how to get all the way to runner.hci_write()? + Ok(0) + } +} diff --git a/cyw43/src/lib.rs b/cyw43/src/lib.rs index 597d1de5..6bd86706 100644 --- a/cyw43/src/lib.rs +++ b/cyw43/src/lib.rs @@ -10,16 +10,15 @@ pub(crate) mod fmt; mod bluetooth; mod bus; mod consts; +mod control; mod countries; mod events; +mod hci_connector; mod ioctl; -mod structs; - -mod control; mod nvram; mod runner; - -use core::slice; +mod structs; +mod utilities; use embassy_net_driver_channel as ch; use embedded_hal_1::digital::OutputPin; @@ -226,6 +225,8 @@ where runner.init(firmware, None).await; + // TODO: build and return something like MPSC channels that can interact as hci_connector with runner/bus? + ( device, Control::new(state_ch, &state.events, &state.ioctl_state), @@ -257,8 +258,3 @@ where runner, ) } - -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/cyw43/src/runner.rs b/cyw43/src/runner.rs index 86c7d7bb..91e11015 100644 --- a/cyw43/src/runner.rs +++ b/cyw43/src/runner.rs @@ -4,6 +4,7 @@ use embassy_sync::pubsub::PubSubBehavior; use embassy_time::{block_for, Duration, Timer}; use embedded_hal_1::digital::OutputPin; +use crate::bluetooth::{CybtFwCb, HexFileData}; use crate::bus::Bus; pub use crate::bus::SpiBusCyw43; use crate::consts::*; @@ -12,7 +13,7 @@ use crate::fmt::Bytes; use crate::ioctl::{IoctlState, IoctlType, PendingIoctl}; use crate::nvram::NVRAM; use crate::structs::*; -use crate::{bluetooth, events, slice8_mut, Core, CHIP, MTU}; +use crate::{bluetooth, events, utilities, Core, CHIP, MTU}; #[cfg(feature = "firmware-logs")] struct LogState { @@ -47,6 +48,12 @@ pub struct Runner<'a, PWR, SPI> { #[cfg(feature = "firmware-logs")] log: LogState, + + // Bluetooth circular buffers + h2b_buf_addr: u32, + h2b_buf_addr_pointer: u32, + b2h_buf_addr: u32, + b2h_buf_addr_pointer: u32, } impl<'a, PWR, SPI> Runner<'a, PWR, SPI> @@ -63,18 +70,26 @@ where Self { ch, bus, + ioctl_state, ioctl_id: 0, sdpcm_seq: 0, sdpcm_seq_max: 1, + events, + #[cfg(feature = "firmware-logs")] log: LogState::default(), + + h2b_buf_addr: 0, + h2b_buf_addr_pointer: 0, + b2h_buf_addr: 0, + b2h_buf_addr_pointer: 0, } } pub(crate) async fn init(&mut self, firmware: &[u8], bluetooth_firmware: Option<&[u8]>) { - self.bus.init().await; + self.bus.init(bluetooth_firmware.is_some()).await; // Init ALP (Active Low Power) clock debug!("init alp"); @@ -83,13 +98,15 @@ where .await; // check we can set the bluetooth watermark - debug!("set bluetooth watermark"); - self.bus - .write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 0x10) - .await; - let watermark = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK).await; - debug!("watermark = {:02x}", watermark); - assert!(watermark == 0x10); + if bluetooth_firmware.is_some() { + debug!("set bluetooth watermark"); + self.bus + .write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 0x10) + .await; + let watermark = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK).await; + debug!("watermark = {:02x}", watermark); + assert!(watermark == 0x10); + } debug!("waiting for clock..."); while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & BACKPLANE_ALP_AVAIL == 0 {} @@ -116,12 +133,6 @@ where debug!("loading fw"); self.bus.bp_write(ram_addr, firmware).await; - // Optionally load Bluetooth fimrware into RAM. - if bluetooth_firmware.is_some() { - debug!("loading bluetooth fw"); - bluetooth::init_bluetooth(&mut self.bus, bluetooth_firmware.unwrap()).await; - } - debug!("loading nvram"); // Round up to 4 bytes. let nvram_len = (NVRAM.len() + 3) / 4 * 4; @@ -151,10 +162,12 @@ where .await; // Set up the interrupt mask and enable interrupts - debug!("bluetooth setup interrupt mask"); - self.bus - .bp_write32(CHIP.sdiod_core_base_address + SDIO_INT_HOST_MASK, I_HMB_FC_CHANGE) - .await; + if bluetooth_firmware.is_some() { + debug!("bluetooth setup interrupt mask"); + self.bus + .bp_write32(CHIP.sdiod_core_base_address + SDIO_INT_HOST_MASK, I_HMB_FC_CHANGE) + .await; + } // TODO: turn interrupts on here or in bus.init()? /*self.bus @@ -204,7 +217,7 @@ where #[cfg(feature = "firmware-logs")] self.log_init().await; - debug!("wifi init done"); + debug!("cyw43 runner init done"); } #[cfg(feature = "firmware-logs")] @@ -261,6 +274,261 @@ where } } + pub(crate) async fn init_bluetooth(&mut self, firmware: &[u8]) { + debug!("init_bluetooth"); + self.bus + .bp_write32(CHIP.bluetooth_base_address + BT2WLAN_PWRUP_ADDR, BT2WLAN_PWRUP_WAKE) + .await; + Timer::after(Duration::from_millis(2)).await; + self.upload_bluetooth_firmware(firmware).await; + self.wait_bt_ready().await; + self.init_bt_buffers().await; + self.wait_bt_awake().await; + self.bt_set_host_ready().await; + self.bt_toggle_intr().await; + } + + pub(crate) async fn upload_bluetooth_firmware(&mut self, firmware: &[u8]) { + // read version + let version_length = firmware[0]; + let _version = &firmware[1..=version_length as usize]; + // skip version + 1 extra byte as per cybt_shared_bus_driver.c + let firmware = &firmware[version_length as usize + 2..]; + // buffers + let mut data_buffer: [u8; 0x100] = [0; 0x100]; + let mut aligned_data_buffer: [u8; 0x100] = [0; 0x100]; + // structs + let mut btfw_cb = CybtFwCb { + p_next_line_start: firmware, + }; + let mut hfd = HexFileData { + addr_mode: BTFW_ADDR_MODE_EXTENDED, + hi_addr: 0, + dest_addr: 0, + p_ds: &mut data_buffer, + }; + loop { + let num_fw_bytes = bluetooth::read_firmware_patch_line(&mut btfw_cb, &mut hfd); + if num_fw_bytes == 0 { + break; + } + let fw_bytes = &hfd.p_ds[0..num_fw_bytes as usize]; + let mut dest_start_addr = hfd.dest_addr + CHIP.bluetooth_base_address; + let mut aligned_data_buffer_index: usize = 0; + // pad start + if utilities::is_aligned(dest_start_addr, 4) { + let num_pad_bytes = dest_start_addr % 4; + let padded_dest_start_addr = utilities::round_down(dest_start_addr, 4); + let memory_value = self.bus.bp_read32(padded_dest_start_addr).await; + let memory_value_bytes = memory_value.to_le_bytes(); // TODO: le or be + // Copy the previous memory value's bytes to the start + for i in 0..num_pad_bytes as usize { + aligned_data_buffer[aligned_data_buffer_index] = memory_value_bytes[i]; + aligned_data_buffer_index += 1; + } + // Copy the firmware bytes after the padding bytes + for i in 0..num_fw_bytes as usize { + aligned_data_buffer[aligned_data_buffer_index] = fw_bytes[i]; + aligned_data_buffer_index += 1; + } + dest_start_addr = padded_dest_start_addr; + } else { + // Directly copy fw_bytes into aligned_data_buffer if no start padding is required + for i in 0..num_fw_bytes as usize { + aligned_data_buffer[aligned_data_buffer_index] = fw_bytes[i]; + aligned_data_buffer_index += 1; + } + } + // pad end + let mut dest_end_addr = dest_start_addr + aligned_data_buffer_index as u32; + if !utilities::is_aligned(dest_end_addr, 4) { + let offset = dest_end_addr % 4; + let num_pad_bytes_end = 4 - offset; + let padded_dest_end_addr = utilities::round_down(dest_end_addr, 4); + let memory_value = self.bus.bp_read32(padded_dest_end_addr).await; + let memory_value_bytes = memory_value.to_le_bytes(); // TODO: le or be + // Append the necessary memory bytes to pad the end of aligned_data_buffer + for i in offset..4 { + aligned_data_buffer[aligned_data_buffer_index] = memory_value_bytes[i as usize]; + aligned_data_buffer_index += 1; + } + dest_end_addr += num_pad_bytes_end; + } else { + // pad end alignment not needed + } + let buffer_to_write = &aligned_data_buffer[0..aligned_data_buffer_index as usize]; + assert!(dest_start_addr % 4 == 0); + assert!(dest_end_addr % 4 == 0); + assert!(aligned_data_buffer_index % 4 == 0); + // write in 0x40 chunks TODO: is this needed or can we write straight away + let chunk_size = 0x40; + for (i, chunk) in buffer_to_write.chunks(chunk_size).enumerate() { + let offset = i * chunk_size; + self.bus.bp_write(dest_start_addr + (offset as u32), chunk).await; + } + // sleep TODO: is this needed + Timer::after(Duration::from_millis(1)).await; + } + } + + pub(crate) async fn wait_bt_ready(&mut self) { + debug!("wait_bt_ready"); + let mut success = false; + for _ in 0..300 { + let val = self.bus.bp_read32(BT_CTRL_REG_ADDR).await; + // TODO: do we need to swap endianness on this read? + debug!("BT_CTRL_REG_ADDR = {:08x}", val); + if val & BTSDIO_REG_FW_RDY_BITMASK != 0 { + success = true; + break; + } + Timer::after(Duration::from_millis(1)).await; + } + assert!(success == true); + } + + pub(crate) async fn wait_bt_awake(&mut self) { + debug!("wait_bt_awake"); + let mut success = false; + for _ in 0..300 { + let val = self.bus.bp_read32(BT_CTRL_REG_ADDR).await; + // TODO: do we need to swap endianness on this read? + debug!("BT_CTRL_REG_ADDR = {:08x}", val); + if val & BTSDIO_REG_BT_AWAKE_BITMASK != 0 { + success = true; + break; + } + Timer::after(Duration::from_millis(1)).await; + } + assert!(success == true); + } + + pub(crate) async fn bt_set_host_ready(&mut self) { + debug!("bt_set_host_ready"); + let old_val = self.bus.bp_read32(HOST_CTRL_REG_ADDR).await; + // TODO: do we need to swap endianness on this read? + let new_val = old_val | BTSDIO_REG_SW_RDY_BITMASK; + self.bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await; + } + + // TODO: use this + #[allow(dead_code)] + pub(crate) async fn bt_set_awake(&mut self, awake: bool) { + debug!("bt_set_awake"); + let old_val = self.bus.bp_read32(HOST_CTRL_REG_ADDR).await; + // TODO: do we need to swap endianness on this read? + let new_val = if awake { + old_val | BTSDIO_REG_WAKE_BT_BITMASK + } else { + old_val & !BTSDIO_REG_WAKE_BT_BITMASK + }; + self.bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await; + } + + pub(crate) async fn bt_toggle_intr(&mut self) { + debug!("bt_toggle_intr"); + let old_val = self.bus.bp_read32(HOST_CTRL_REG_ADDR).await; + // TODO: do we need to swap endianness on this read? + let new_val = old_val ^ BTSDIO_REG_DATA_VALID_BITMASK; + self.bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await; + } + + // TODO: use this + #[allow(dead_code)] + pub(crate) async fn bt_set_intr(&mut self) { + debug!("bt_set_intr"); + let old_val = self.bus.bp_read32(HOST_CTRL_REG_ADDR).await; + let new_val = old_val | BTSDIO_REG_DATA_VALID_BITMASK; + self.bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await; + } + + pub(crate) async fn init_bt_buffers(&mut self) { + debug!("init_bt_buffers"); + let wlan_ram_base_addr = self.bus.bp_read32(WLAN_RAM_BASE_REG_ADDR).await; + assert!(wlan_ram_base_addr != 0); + debug!("wlan_ram_base_addr = {:08x}", wlan_ram_base_addr); + self.h2b_buf_addr = wlan_ram_base_addr + BTSDIO_OFFSET_HOST_WRITE_BUF; + self.b2h_buf_addr = wlan_ram_base_addr + BTSDIO_OFFSET_HOST_READ_BUF; + let h2b_buf_in_addr = wlan_ram_base_addr + BTSDIO_OFFSET_HOST2BT_IN; + let h2b_buf_out_addr = wlan_ram_base_addr + BTSDIO_OFFSET_HOST2BT_OUT; + let b2h_buf_in_addr = wlan_ram_base_addr + BTSDIO_OFFSET_BT2HOST_IN; + let b2h_buf_out_addr = wlan_ram_base_addr + BTSDIO_OFFSET_BT2HOST_OUT; + self.bus.bp_write32(h2b_buf_in_addr, 0).await; + self.bus.bp_write32(h2b_buf_out_addr, 0).await; + self.bus.bp_write32(b2h_buf_in_addr, 0).await; + self.bus.bp_write32(b2h_buf_out_addr, 0).await; + } + + async fn bt_bus_request(&mut self) { + // TODO: CYW43_THREAD_ENTER mutex? + self.bt_set_awake(true).await; + self.wait_bt_awake().await; + } + + async fn bt_bus_release(&mut self) { + // TODO: CYW43_THREAD_EXIT mutex? + } + + #[allow(dead_code)] + pub(crate) async fn hci_read(&mut self, buf: &mut [u8]) -> u32 { + debug!("hci_read buf = {:02x}", buf); + self.bt_bus_request().await; + let mut header = [0 as u8; 4]; + self.bus + .bp_read(self.b2h_buf_addr + self.b2h_buf_addr_pointer, &mut header) + .await; + self.b2h_buf_addr_pointer += header.len() as u32; + debug!("hci_read heaer = {:02x}", header); + // cybt_get_bt_buf_index(&fw_membuf_info); + // fw_b2h_buf_count = CIRC_BUF_CNT(fw_membuf_info.bt2host_in_val, fw_membuf_info.bt2host_out_val); + // cybt_mem_read_idx(B2H_BUF_ADDR_IDX, fw_membuf_info.bt2host_out_val, p_data, read_len); + // cybt_mem_read_idx(B2H_BUF_ADDR_IDX, 0, p_data + first_read_len, second_read_len); + // cybt_reg_write_idx(B2H_BUF_OUT_ADDR_IDX, new_b2h_out_val); + self.bt_toggle_intr().await; + let bytes_read = 0; + self.bt_bus_release().await; + return bytes_read; + } + + #[allow(dead_code)] + pub(crate) async fn hci_write(&mut self, buf: &[u8]) { + let buf_len = buf.len(); + let algined_buf_len = utilities::round_up((buf_len + 3) as u32, 4); + assert!(buf_len <= algined_buf_len as usize); + let cmd_len = buf_len + 3 - 4; // add 3 bytes for SDIO header thingie? + let mut buf_with_cmd = [0 as u8; 0x100]; + buf_with_cmd[0] = (cmd_len & 0xFF) as u8; + buf_with_cmd[1] = ((cmd_len & 0xFF00) >> 8) as u8; + buf_with_cmd[2] = 0x00; + for i in 0..buf_len { + buf_with_cmd[3 + i] = buf[i]; + } + let padded_buf_with_cmd = &buf_with_cmd[0..algined_buf_len as usize]; + debug!("hci_write padded_buf_with_cmd = {:02x}", padded_buf_with_cmd); + self.bt_bus_request().await; + self.bus + .bp_write(self.h2b_buf_addr + self.h2b_buf_addr_pointer, &padded_buf_with_cmd) + .await; + self.h2b_buf_addr_pointer += padded_buf_with_cmd.len() as u32; + // TODO: handle wrapping based on BTSDIO_FWBUF_SIZE + self.bt_toggle_intr().await; + self.bt_bus_release().await; + } + + pub(crate) async fn bt_has_work(&mut self) -> bool { + let int_status = self.bus.bp_read32(CHIP.sdiod_core_base_address + SDIO_INT_STATUS).await; + if int_status & I_HMB_FC_CHANGE != 0 { + self.bus + .bp_write32( + CHIP.sdiod_core_base_address + SDIO_INT_STATUS, + int_status & I_HMB_FC_CHANGE, + ) + .await; + return true; + } + return false; + } + pub async fn run(mut self) -> ! { let mut buf = [0; 512]; loop { @@ -286,7 +554,7 @@ where trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); let mut buf = [0; 512]; - let buf8 = slice8_mut(&mut buf); + let buf8 = utilities::slice8_mut(&mut buf); // There MUST be 2 bytes of padding between the SDPCM and BDC headers. // And ONLY for data packets! @@ -375,8 +643,11 @@ where if status & STATUS_F2_PKT_AVAILABLE != 0 { let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; self.bus.wlan_read(buf, len).await; - trace!("rx {:02x}", Bytes(&slice8_mut(buf)[..(len as usize).min(48)])); - self.rx(&mut slice8_mut(buf)[..len as usize]); + trace!( + "rx {:02x}", + Bytes(&utilities::slice8_mut(buf)[..(len as usize).min(48)]) + ); + self.rx(&mut utilities::slice8_mut(buf)[..len as usize]); } else { break; } @@ -519,7 +790,7 @@ where async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) { let mut buf = [0; 512]; - let buf8 = slice8_mut(&mut buf); + let buf8 = utilities::slice8_mut(&mut buf); let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); diff --git a/cyw43/src/utilities.rs b/cyw43/src/utilities.rs new file mode 100644 index 00000000..39b9d5f1 --- /dev/null +++ b/cyw43/src/utilities.rs @@ -0,0 +1,18 @@ +use core::slice; + +pub(crate) 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) } +} + +pub(crate) fn is_aligned(a: u32, x: u32) -> bool { + (a & (x - 1)) == 0 +} + +pub(crate) fn round_down(x: u32, a: u32) -> u32 { + x & !(a - 1) +} + +pub(crate) fn round_up(x: u32, a: u32) -> u32 { + ((x + a - 1) / a) * a +} diff --git a/examples/rp/src/bin/bluetooth_blinky.rs b/examples/rp/src/bin/bluetooth_blinky.rs index 6a861544..dfa3350e 100644 --- a/examples/rp/src/bin/bluetooth_blinky.rs +++ b/examples/rp/src/bin/bluetooth_blinky.rs @@ -51,7 +51,7 @@ async fn main(spawner: Spawner) { let (_net_device, mut control, runner) = cyw43::new_with_bluetooth(state, pwr, spi, fw, btfw).await; unwrap!(spawner.spawn(cyw43_runner_task(runner))); - control.init(clm).await; + control.init(clm, false, true).await; control .set_power_management(cyw43::PowerManagementMode::PowerSave) .await; diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs index cd61ad78..d3a9b9f2 100644 --- a/examples/rp/src/bin/wifi_ap_tcp_server.rs +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs @@ -64,7 +64,7 @@ async fn main(spawner: Spawner) { let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; unwrap!(spawner.spawn(wifi_task(runner))); - control.init(clm).await; + control.init(clm, true, false).await; control .set_power_management(cyw43::PowerManagementMode::PowerSave) .await; diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs index 33d43788..c76827aa 100644 --- a/examples/rp/src/bin/wifi_blinky.rs +++ b/examples/rp/src/bin/wifi_blinky.rs @@ -50,7 +50,7 @@ async fn main(spawner: Spawner) { let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; unwrap!(spawner.spawn(wifi_task(runner))); - control.init(clm).await; + control.init(clm, true, false).await; control .set_power_management(cyw43::PowerManagementMode::PowerSave) .await; diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs index 743fab61..f74843e6 100644 --- a/examples/rp/src/bin/wifi_scan.rs +++ b/examples/rp/src/bin/wifi_scan.rs @@ -61,7 +61,7 @@ async fn main(spawner: Spawner) { let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; unwrap!(spawner.spawn(wifi_task(runner))); - control.init(clm).await; + control.init(clm, true, false).await; control .set_power_management(cyw43::PowerManagementMode::PowerSave) .await; diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index 55fcb4a6..65119347 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs @@ -67,7 +67,7 @@ async fn main(spawner: Spawner) { let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; unwrap!(spawner.spawn(wifi_task(runner))); - control.init(clm).await; + control.init(clm, true, false).await; control .set_power_management(cyw43::PowerManagementMode::PowerSave) .await;