embassy/cyw43/src/bluetooth.rs
Dario Nieuwenhuis 4f7ac1946a cyw43: add Bluetooth support.
Co-Authored-By: Brandon Ros <brandonros1@gmail.com>
2024-08-05 21:07:42 +02:00

509 lines
20 KiB
Rust

use core::cell::RefCell;
use core::future::Future;
use core::mem::MaybeUninit;
use bt_hci::transport::WithIndicator;
use bt_hci::{ControllerToHostPacket, FromHciBytes, HostToControllerPacket, PacketKind, WriteHci};
use embassy_futures::yield_now;
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_sync::zerocopy_channel;
use embassy_time::{Duration, Timer};
use embedded_hal_1::digital::OutputPin;
use crate::bus::Bus;
pub use crate::bus::SpiBusCyw43;
use crate::consts::*;
use crate::util::round_up;
use crate::{util, CHIP};
pub(crate) struct BtState {
rx: [BtPacketBuf; 4],
tx: [BtPacketBuf; 4],
inner: MaybeUninit<BtStateInnre<'static>>,
}
impl BtState {
pub const fn new() -> Self {
Self {
rx: [const { BtPacketBuf::new() }; 4],
tx: [const { BtPacketBuf::new() }; 4],
inner: MaybeUninit::uninit(),
}
}
}
struct BtStateInnre<'d> {
rx: zerocopy_channel::Channel<'d, NoopRawMutex, BtPacketBuf>,
tx: zerocopy_channel::Channel<'d, NoopRawMutex, BtPacketBuf>,
}
/// Bluetooth driver.
pub struct BtDriver<'d> {
rx: RefCell<zerocopy_channel::Receiver<'d, NoopRawMutex, BtPacketBuf>>,
tx: RefCell<zerocopy_channel::Sender<'d, NoopRawMutex, BtPacketBuf>>,
}
pub(crate) struct BtRunner<'d> {
pub(crate) tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, BtPacketBuf>,
rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, BtPacketBuf>,
// Bluetooth circular buffers
addr: u32,
h2b_write_pointer: u32,
b2h_read_pointer: u32,
}
const BT_HCI_MTU: usize = 1024;
/// Represents a packet of size MTU.
pub(crate) struct BtPacketBuf {
pub(crate) len: usize,
pub(crate) buf: [u8; BT_HCI_MTU],
}
impl BtPacketBuf {
/// Create a new packet buffer.
pub const fn new() -> Self {
Self {
len: 0,
buf: [0; BT_HCI_MTU],
}
}
}
pub(crate) fn new<'d>(state: &'d mut BtState) -> (BtRunner<'d>, BtDriver<'d>) {
// safety: this is a self-referential struct, however:
// - it can't move while the `'d` borrow is active.
// - when the borrow ends, the dangling references inside the MaybeUninit will never be used again.
let state_uninit: *mut MaybeUninit<BtStateInnre<'d>> =
(&mut state.inner as *mut MaybeUninit<BtStateInnre<'static>>).cast();
let state = unsafe { &mut *state_uninit }.write(BtStateInnre {
rx: zerocopy_channel::Channel::new(&mut state.rx[..]),
tx: zerocopy_channel::Channel::new(&mut state.tx[..]),
});
let (rx_sender, rx_receiver) = state.rx.split();
let (tx_sender, tx_receiver) = state.tx.split();
(
BtRunner {
tx_chan: tx_receiver,
rx_chan: rx_sender,
addr: 0,
h2b_write_pointer: 0,
b2h_read_pointer: 0,
},
BtDriver {
rx: RefCell::new(rx_receiver),
tx: RefCell::new(tx_sender),
},
)
}
pub(crate) struct CybtFwCb<'a> {
pub p_next_line_start: &'a [u8],
}
pub(crate) struct HexFileData<'a> {
pub addr_mode: i32,
pub hi_addr: u16,
pub dest_addr: u32,
pub p_ds: &'a mut [u8],
}
pub(crate) fn read_firmware_patch_line(p_btfw_cb: &mut CybtFwCb, hfd: &mut HexFileData) -> u32 {
let mut abs_base_addr32 = 0;
loop {
let num_bytes = p_btfw_cb.p_next_line_start[0];
p_btfw_cb.p_next_line_start = &p_btfw_cb.p_next_line_start[1..];
let addr = (p_btfw_cb.p_next_line_start[0] as u16) << 8 | p_btfw_cb.p_next_line_start[1] as u16;
p_btfw_cb.p_next_line_start = &p_btfw_cb.p_next_line_start[2..];
let line_type = p_btfw_cb.p_next_line_start[0];
p_btfw_cb.p_next_line_start = &p_btfw_cb.p_next_line_start[1..];
if num_bytes == 0 {
break;
}
hfd.p_ds[..num_bytes as usize].copy_from_slice(&p_btfw_cb.p_next_line_start[..num_bytes as usize]);
p_btfw_cb.p_next_line_start = &p_btfw_cb.p_next_line_start[num_bytes as usize..];
match line_type {
BTFW_HEX_LINE_TYPE_EXTENDED_ADDRESS => {
hfd.hi_addr = (hfd.p_ds[0] as u16) << 8 | hfd.p_ds[1] as u16;
hfd.addr_mode = BTFW_ADDR_MODE_EXTENDED;
}
BTFW_HEX_LINE_TYPE_EXTENDED_SEGMENT_ADDRESS => {
hfd.hi_addr = (hfd.p_ds[0] as u16) << 8 | hfd.p_ds[1] as u16;
hfd.addr_mode = BTFW_ADDR_MODE_SEGMENT;
}
BTFW_HEX_LINE_TYPE_ABSOLUTE_32BIT_ADDRESS => {
abs_base_addr32 = (hfd.p_ds[0] as u32) << 24
| (hfd.p_ds[1] as u32) << 16
| (hfd.p_ds[2] as u32) << 8
| hfd.p_ds[3] as u32;
hfd.addr_mode = BTFW_ADDR_MODE_LINEAR32;
}
BTFW_HEX_LINE_TYPE_DATA => {
hfd.dest_addr = addr as u32;
match hfd.addr_mode {
BTFW_ADDR_MODE_EXTENDED => hfd.dest_addr += (hfd.hi_addr as u32) << 16,
BTFW_ADDR_MODE_SEGMENT => hfd.dest_addr += (hfd.hi_addr as u32) << 4,
BTFW_ADDR_MODE_LINEAR32 => hfd.dest_addr += abs_base_addr32,
_ => {}
}
return num_bytes as u32;
}
_ => {}
}
}
0
}
impl<'a> BtRunner<'a> {
pub(crate) async fn init_bluetooth(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>, firmware: &[u8]) {
trace!("init_bluetooth");
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(bus, firmware).await;
self.wait_bt_ready(bus).await;
self.init_bt_buffers(bus).await;
self.wait_bt_awake(bus).await;
self.bt_set_host_ready(bus).await;
self.bt_toggle_intr(bus).await;
}
pub(crate) async fn upload_bluetooth_firmware(
&mut self,
bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>,
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 !util::is_aligned(dest_start_addr, 4) {
let num_pad_bytes = dest_start_addr % 4;
let padded_dest_start_addr = util::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();
// 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 !util::is_aligned(dest_end_addr, 4) {
let offset = dest_end_addr % 4;
let num_pad_bytes_end = 4 - offset;
let padded_dest_end_addr = util::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();
// 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);
bus.bp_write(dest_start_addr, buffer_to_write).await;
}
}
pub(crate) async fn wait_bt_ready(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
trace!("wait_bt_ready");
let mut success = false;
for _ in 0..300 {
let val = bus.bp_read32(BT_CTRL_REG_ADDR).await;
trace!("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, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
trace!("wait_bt_awake");
let mut success = false;
for _ in 0..300 {
let val = bus.bp_read32(BT_CTRL_REG_ADDR).await;
trace!("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, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
trace!("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(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>, awake: bool) {
trace!("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 = if awake {
old_val | BTSDIO_REG_WAKE_BT_BITMASK
} else {
old_val & !BTSDIO_REG_WAKE_BT_BITMASK
};
bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await;
}
pub(crate) async fn bt_toggle_intr(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
trace!("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(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
trace!("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_bt_buffers(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
trace!("init_bt_buffers");
self.addr = bus.bp_read32(WLAN_RAM_BASE_REG_ADDR).await;
assert!(self.addr != 0);
trace!("wlan_ram_base_addr = {:08x}", self.addr);
bus.bp_write32(self.addr + BTSDIO_OFFSET_HOST2BT_IN, 0).await;
bus.bp_write32(self.addr + BTSDIO_OFFSET_HOST2BT_OUT, 0).await;
bus.bp_write32(self.addr + BTSDIO_OFFSET_BT2HOST_IN, 0).await;
bus.bp_write32(self.addr + BTSDIO_OFFSET_BT2HOST_OUT, 0).await;
}
async fn bt_bus_request(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
// TODO: CYW43_THREAD_ENTER mutex?
self.bt_set_awake(bus, true).await;
self.wait_bt_awake(bus).await;
}
pub(crate) async fn hci_write(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
self.bt_bus_request(bus).await;
// NOTE(unwrap): we only call this when we do have a packet in the queue.
let buf = self.tx_chan.try_receive().unwrap();
debug!("HCI tx: {:02x}", crate::fmt::Bytes(&buf.buf[..buf.len]));
let len = buf.len as u32 - 1; // len doesn't include hci type byte
let rounded_len = round_up(len, 4);
let total_len = 4 + rounded_len;
let read_pointer = bus.bp_read32(self.addr + BTSDIO_OFFSET_HOST2BT_OUT).await;
let available = read_pointer.wrapping_sub(self.h2b_write_pointer + 4) % BTSDIO_FWBUF_SIZE;
if available < total_len {
warn!(
"bluetooth tx queue full, retrying. len {} available {}",
total_len, available
);
yield_now().await;
return;
}
// Build header
let mut header = [0u8; 4];
header[0] = len as u8;
header[1] = (len >> 8) as u8;
header[2] = (len >> 16) as u8;
header[3] = buf.buf[0]; // HCI type byte
// Write header
let addr = self.addr + BTSDIO_OFFSET_HOST_WRITE_BUF + self.h2b_write_pointer;
bus.bp_write(addr, &header).await;
self.h2b_write_pointer = (self.h2b_write_pointer + 4) % BTSDIO_FWBUF_SIZE;
// Write payload.
let payload = &buf.buf[1..][..rounded_len as usize];
if self.h2b_write_pointer as usize + payload.len() > BTSDIO_FWBUF_SIZE as usize {
// wraparound
let n = BTSDIO_FWBUF_SIZE - self.h2b_write_pointer;
let addr = self.addr + BTSDIO_OFFSET_HOST_WRITE_BUF + self.h2b_write_pointer;
bus.bp_write(addr, &payload[..n as usize]).await;
let addr = self.addr + BTSDIO_OFFSET_HOST_WRITE_BUF;
bus.bp_write(addr, &payload[n as usize..]).await;
} else {
// no wraparound
let addr = self.addr + BTSDIO_OFFSET_HOST_WRITE_BUF + self.h2b_write_pointer;
bus.bp_write(addr, payload).await;
}
self.h2b_write_pointer = (self.h2b_write_pointer + payload.len() as u32) % BTSDIO_FWBUF_SIZE;
// Update pointer.
bus.bp_write32(self.addr + BTSDIO_OFFSET_HOST2BT_IN, self.h2b_write_pointer)
.await;
self.bt_toggle_intr(bus).await;
self.tx_chan.receive_done();
}
async fn bt_has_work(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) -> bool {
let int_status = bus.bp_read32(CHIP.sdiod_core_base_address + SDIO_INT_STATUS).await;
if int_status & I_HMB_FC_CHANGE != 0 {
bus.bp_write32(
CHIP.sdiod_core_base_address + SDIO_INT_STATUS,
int_status & I_HMB_FC_CHANGE,
)
.await;
return true;
}
return false;
}
pub(crate) async fn handle_irq(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
if self.bt_has_work(bus).await {
loop {
// Check if we have data.
let write_pointer = bus.bp_read32(self.addr + BTSDIO_OFFSET_BT2HOST_IN).await;
let available = write_pointer.wrapping_sub(self.b2h_read_pointer) % BTSDIO_FWBUF_SIZE;
if available == 0 {
break;
}
// read header
let mut header = [0u8; 4];
let addr = self.addr + BTSDIO_OFFSET_HOST_READ_BUF + self.b2h_read_pointer;
bus.bp_read(addr, &mut header).await;
// calc length
let len = header[0] as u32 | ((header[1]) as u32) << 8 | ((header[2]) as u32) << 16;
let rounded_len = round_up(len, 4);
if available < 4 + rounded_len {
warn!("ringbuf data not enough for a full packet?");
break;
}
self.b2h_read_pointer = (self.b2h_read_pointer + 4) % BTSDIO_FWBUF_SIZE;
// Obtain a buf from the channel.
let buf = self.rx_chan.send().await;
buf.buf[0] = header[3]; // hci packet type
let payload = &mut buf.buf[1..][..rounded_len as usize];
if self.b2h_read_pointer as usize + payload.len() > BTSDIO_FWBUF_SIZE as usize {
// wraparound
let n = BTSDIO_FWBUF_SIZE - self.b2h_read_pointer;
let addr = self.addr + BTSDIO_OFFSET_HOST_READ_BUF + self.b2h_read_pointer;
bus.bp_read(addr, &mut payload[..n as usize]).await;
let addr = self.addr + BTSDIO_OFFSET_HOST_READ_BUF;
bus.bp_read(addr, &mut payload[n as usize..]).await;
} else {
// no wraparound
let addr = self.addr + BTSDIO_OFFSET_HOST_READ_BUF + self.b2h_read_pointer;
bus.bp_read(addr, payload).await;
}
self.b2h_read_pointer = (self.b2h_read_pointer + payload.len() as u32) % BTSDIO_FWBUF_SIZE;
bus.bp_write32(self.addr + BTSDIO_OFFSET_BT2HOST_OUT, self.b2h_read_pointer)
.await;
buf.len = 1 + len as usize;
debug!("HCI rx: {:02x}", crate::fmt::Bytes(&buf.buf[..buf.len]));
self.rx_chan.send_done();
self.bt_toggle_intr(bus).await;
}
}
}
}
impl<'d> embedded_io_async::ErrorType for BtDriver<'d> {
type Error = core::convert::Infallible;
}
impl<'d> bt_hci::transport::Transport for BtDriver<'d> {
fn read<'a>(&self, rx: &'a mut [u8]) -> impl Future<Output = Result<ControllerToHostPacket<'a>, Self::Error>> {
async {
let ch = &mut *self.rx.borrow_mut();
let buf = ch.receive().await;
let n = buf.len;
assert!(n < rx.len());
rx[..n].copy_from_slice(&buf.buf[..n]);
ch.receive_done();
let kind = PacketKind::from_hci_bytes_complete(&rx[..1]).unwrap();
let (res, _) = ControllerToHostPacket::from_hci_bytes_with_kind(kind, &rx[1..n]).unwrap();
Ok(res)
}
}
/// Write a complete HCI packet from the tx buffer
fn write<T: HostToControllerPacket>(&self, val: &T) -> impl Future<Output = Result<(), Self::Error>> {
async {
let ch = &mut *self.tx.borrow_mut();
let buf = ch.send().await;
let buf_len = buf.buf.len();
let mut slice = &mut buf.buf[..];
WithIndicator::new(val).write_hci(&mut slice).unwrap();
buf.len = buf_len - slice.len();
ch.send_done();
Ok(())
}
}
}