Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion embassy-stm32-wpan/src/wba/ble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@

use core::sync::atomic::{AtomicBool, Ordering};

use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::zerocopy_channel::Receiver;

use crate::linklayer_plat::RNG_CHANNEL;
use crate::wba::error::BleError;
use crate::wba::gap::Advertiser;
use crate::wba::hci::command::CommandSender;
Expand Down Expand Up @@ -72,11 +76,14 @@ impl Ble {
///
/// - `Ok(())` if initialization succeeded
/// - `Err(BleError)` if any initialization step failed
pub fn init(&mut self) -> Result<(), BleError> {
pub fn init(&mut self, rng_channel: Receiver<'static, CriticalSectionRawMutex, [u8; 8]>) -> Result<(), BleError> {
if self.initialized.load(Ordering::Acquire) {
return Ok(());
}

// Setup the rng channel
unsafe { RNG_CHANNEL.replace(rng_channel) };

// 1. Reset BLE controller
self.cmd_sender.reset()?;

Expand Down
107 changes: 28 additions & 79 deletions embassy-stm32-wpan/src/wba/linklayer_plat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
#![cfg(feature = "wba")]
#![allow(clippy::missing_safety_doc)]

use core::cell::RefCell;
use core::hint::spin_loop;
use core::ptr;
use core::sync::atomic::{AtomicBool, AtomicI32, AtomicPtr, AtomicU32, Ordering};
Expand All @@ -84,9 +83,9 @@ use cortex_m::asm::{dsb, isb};
use cortex_m::interrupt::InterruptNumber;
use cortex_m::peripheral::NVIC;
use cortex_m::register::basepri;
use critical_section::{self, Mutex};
use critical_section::{self};
#[cfg(feature = "defmt")]
use defmt::{error, trace};
use defmt::trace;
#[cfg(not(feature = "defmt"))]
macro_rules! trace {
($($arg:tt)*) => {{}};
Expand All @@ -95,7 +94,10 @@ macro_rules! trace {
macro_rules! error {
($($arg:tt)*) => {{}};
}

use embassy_stm32::NVIC_PRIO_BITS;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::zerocopy_channel;
use embassy_time::{Duration, block_for};

use super::bindings::{link_layer, mac};
Expand Down Expand Up @@ -147,55 +149,10 @@ static RADIO_SLEEP_TIMER_VAL: AtomicU32 = AtomicU32::new(0);
// Only written when the IRQ disable counter transitions 0->1, and consumed when it transitions 1->0.
static mut CS_RESTORE_STATE: Option<critical_section::RestoreState> = None;

// Wrapper for the RNG pointer that implements Send
// Safety: The pointer is only ever accessed from interrupt-disabled critical sections,
// ensuring single-threaded access despite the raw pointer.
struct SendPtr(*mut ());
unsafe impl Send for SendPtr {}

// Optional hardware RNG instance for true random number generation.
// The RNG peripheral pointer is stored here to be used by LINKLAYER_PLAT_GetRNG.
// This must be set by the application using `set_rng_instance` before the link layer requests random numbers.
static HARDWARE_RNG: Mutex<RefCell<Option<SendPtr>>> = Mutex::new(RefCell::new(None));

/// Set the hardware RNG instance to be used by the link layer.
///
/// This function allows the application to provide a hardware RNG peripheral
/// that will be used for generating random numbers instead of the software PRNG.
///
/// # Safety
///
/// The caller must ensure that:
/// - The pointer remains valid for the lifetime of the link layer usage
/// - The RNG peripheral is properly initialized and configured
/// - The pointer points to a valid `embassy_stm32::rng::Rng` instance
///
/// # Example
///
/// ```no_run
/// use embassy_stm32::rng::Rng;
/// use embassy_stm32::peripherals;
/// use embassy_stm32::bind_interrupts;
///
/// bind_interrupts!(struct Irqs {
/// RNG => embassy_stm32::rng::InterruptHandler<peripherals::RNG>;
/// });
///
/// let mut rng = Rng::new(p.RNG, Irqs);
/// embassy_stm32_wpan::wba::linklayer_plat::set_rng_instance(&mut rng as *mut _ as *mut ());
/// ```
pub fn set_rng_instance(rng: *mut ()) {
critical_section::with(|cs| {
HARDWARE_RNG.borrow(cs).replace(Some(SendPtr(rng)));
});
}

/// Clear the hardware RNG instance, falling back to software PRNG.
pub fn clear_rng_instance() {
critical_section::with(|cs| {
HARDWARE_RNG.borrow(cs).replace(None);
});
}
pub(crate) static mut RNG_CHANNEL: Option<zerocopy_channel::Receiver<CriticalSectionRawMutex, [u8; 8]>> = None;

fn store_callback(slot: &AtomicPtr<()>, cb: Option<Callback>) {
let ptr = cb.map_or(ptr::null_mut(), |f| f as *mut ());
Expand Down Expand Up @@ -504,21 +461,27 @@ pub unsafe extern "C" fn LINKLAYER_PLAT_GetRNG(ptr_rnd: *mut u8, len: u32) {
return;
}

// Get the hardware RNG instance
let rng_ptr = critical_section::with(|cs| HARDWARE_RNG.borrow(cs).borrow().as_ref().map(|p| p.0));
// Create a slice from the raw pointer
let mut dst = core::slice::from_raw_parts_mut(ptr_rnd, len as usize);

let rng_ptr = rng_ptr.expect(
"Hardware RNG not initialized! Call embassy_stm32_wpan::wba::set_rng_instance() before using the BLE stack.",
);
static mut RNG_BYTES: [u8; 8] = [0u8; 8];
static mut RNG_PTR: usize = 8;

// Cast the void pointer back to an Rng instance
let rng = &mut *(rng_ptr as *mut embassy_stm32::rng::Rng<'static, embassy_stm32::peripherals::RNG>);
while !dst.is_empty() {
if RNG_PTR == 8 {
RNG_BYTES.copy_from_slice(RNG_CHANNEL.as_mut().unwrap().try_receive().unwrap());
RNG_CHANNEL.as_mut().unwrap().receive_done();
RNG_PTR = 0;
}

// Create a slice from the raw pointer
let dest = core::slice::from_raw_parts_mut(ptr_rnd, len as usize);
let src = &RNG_BYTES[RNG_PTR..];
dst.copy_from_slice(src);

// Fill the buffer with hardware random bytes
rng.fill_bytes(dest);
// advance the ptr by the number of bytes actually copied.
let copied = src.len().min(dst.len());
RNG_PTR += copied;
dst = &mut dst[copied..];
}

trace!("LINKLAYER_PLAT_GetRNG: generated {} random bytes", len);
}
Expand Down Expand Up @@ -940,19 +903,19 @@ pub unsafe extern "C" fn LINKLAYER_PLAT_GetUDN() -> u32 {
#[unsafe(no_mangle)]
pub unsafe extern "C" fn LINKLAYER_DEBUG_SIGNAL_SET() {
trace!("LINKLAYER_DEBUG_SIGNAL_SET");
todo!()
// todo!()
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn LINKLAYER_DEBUG_SIGNAL_RESET() {
trace!("LINKLAYER_DEBUG_SIGNAL_RESET");
todo!()
// todo!()
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn LINKLAYER_DEBUG_SIGNAL_TOGGLE() {
trace!("LINKLAYER_DEBUG_SIGNAL_TOGGLE");
todo!()
// todo!()
}

// BLE Platform functions required by BLE stack
Expand All @@ -974,23 +937,9 @@ pub unsafe extern "C" fn BLEPLAT_Init() {
pub unsafe extern "C" fn BLEPLAT_RngGet(n: u8, val: *mut u32) {
trace!("BLEPLAT_RngGet: n={}", n);

if val.is_null() || n == 0 || n > 4 {
if val.is_null() {
return;
}

// Get RNG instance from HARDWARE_RNG
critical_section::with(|cs| {
let rng_cell = HARDWARE_RNG.borrow(cs).borrow();
if let Some(rng_ptr) = rng_cell.as_ref() {
let rng = &mut *(rng_ptr.0 as *mut embassy_stm32::rng::Rng<'static, embassy_stm32::peripherals::RNG>);

// Generate n random 32-bit values
for i in 0..n {
let dest = core::slice::from_raw_parts_mut(val.add(i as usize) as *mut u8, 4);
rng.fill_bytes(dest);
}
} else {
error!("BLEPLAT_RngGet: RNG not initialized");
}
});
LINKLAYER_PLAT_GetRNG(val as *mut u8, n as u32 * 4);
}
14 changes: 13 additions & 1 deletion embassy-stm32-wpan/src/wba/ll_sys_if.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![cfg(feature = "wba")]
use defmt::trace;

// /* USER CODE BEGIN Header */
// /**
// ******************************************************************************
Expand Down Expand Up @@ -345,6 +346,7 @@ const TASK_PRIO_LINK_LAYER: u32 = mac::CFG_SEQ_PRIO_ID_T_CFG_SEQ_PRIO_0 as u32;
*/
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ll_sys_bg_process_init() {
trace!("ll_sys_bg_process_init");
util_seq::UTIL_SEQ_RegTask(TASK_LINK_LAYER_MASK, UTIL_SEQ_RFU, Some(link_layer::ll_sys_bg_process));
}

Expand All @@ -355,6 +357,7 @@ pub unsafe extern "C" fn ll_sys_bg_process_init() {
*/
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ll_sys_schedule_bg_process() {
trace!("ll_sys_schedule_bg_process");
util_seq::UTIL_SEQ_SetTask(TASK_LINK_LAYER_MASK, TASK_PRIO_LINK_LAYER);
}

Expand All @@ -365,6 +368,7 @@ pub unsafe extern "C" fn ll_sys_schedule_bg_process() {
*/
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ll_sys_schedule_bg_process_isr() {
trace!("ll_sys_schedule_bg_process_isr");
util_seq::UTIL_SEQ_SetTask(TASK_LINK_LAYER_MASK, TASK_PRIO_LINK_LAYER);
}

Expand All @@ -375,6 +379,8 @@ pub unsafe extern "C" fn ll_sys_schedule_bg_process_isr() {
*/
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ll_sys_config_params() {
trace!("ll_sys_config_params");

let allow_low_isr = mac::USE_RADIO_LOW_ISR as u8;
let run_from_isr = mac::NEXT_EVENT_SCHEDULING_FROM_ISR as u8;
let _ = link_layer::ll_intf_cmn_config_ll_ctx_params(allow_low_isr, run_from_isr);
Expand All @@ -390,6 +396,8 @@ pub unsafe extern "C" fn ll_sys_config_params() {
*/
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ll_sys_reset() {
trace!("ll_sys_reset");

ll_sys_sleep_clock_source_selection();

let sleep_accuracy = ll_sys_BLE_sleep_clock_accuracy_selection();
Expand All @@ -400,6 +408,8 @@ pub unsafe extern "C" fn ll_sys_reset() {
/// Defaults to the crystal oscillator when no explicit configuration is available.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ll_sys_sleep_clock_source_selection() {
trace!("ll_sys_sleep_clock_source_selection");

let mut frequency: u16 = 0;
let _ = link_layer::ll_intf_cmn_le_select_slp_clk_src(
link_layer::_SLPTMR_SRC_TYPE_E_CRYSTAL_OSCILLATOR_SLPTMR as u8,
Expand All @@ -411,6 +421,8 @@ pub unsafe extern "C" fn ll_sys_sleep_clock_source_selection() {
/// Returns zero when board-specific calibration data is unavailable.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn ll_sys_BLE_sleep_clock_accuracy_selection() -> u8 {
trace!("ll_sys_BLE_sleep_clock_accuracy_selection");

// TODO: derive the board-specific sleep clock accuracy once calibration data is available.
0
}
3 changes: 2 additions & 1 deletion embassy-stm32-wpan/src/wba/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ pub mod linklayer_plat;
pub mod ll_sys;
pub mod ll_sys_if;
pub mod mac_sys_if;
pub mod rng;
pub mod util_seq;

// Re-export main types
pub use ble::{Ble, VersionInfo};
pub use error::BleError;
pub use linklayer_plat::{clear_rng_instance, run_radio_high_isr, run_radio_sw_low_isr, set_rng_instance};
pub use linklayer_plat::{run_radio_high_isr, run_radio_sw_low_isr};
56 changes: 56 additions & 0 deletions embassy-stm32-wpan/src/wba/rng.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use core::mem::MaybeUninit;

use embassy_stm32::rng::{Instance, Rng};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::zerocopy_channel::{self, Channel, Receiver, Sender};

pub struct RngState {
buf: [[u8; 8]; 8],
inner: MaybeUninit<RngStateInner<'static>>,
}

impl RngState {
pub const fn new() -> Self {
Self {
buf: [[0u8; 8]; 8],
inner: MaybeUninit::uninit(),
}
}
}

struct RngStateInner<'d> {
ch: Channel<'d, CriticalSectionRawMutex, [u8; 8]>,
}

pub struct RngService<'d, T: Instance> {
rng: Rng<'d, T>,
sender: Sender<'d, CriticalSectionRawMutex, [u8; 8]>,
}

impl<'d, T: Instance> RngService<'d, T> {
pub fn new(
rng: Rng<'d, T>,
state: &'static mut RngState,
) -> (Self, Receiver<'d, CriticalSectionRawMutex, [u8; 8]>) {
// 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<RngStateInner<'d>> =
(&mut state.inner as *mut MaybeUninit<RngStateInner<'static>>).cast();
let state = unsafe { &mut *state_uninit }.write(RngStateInner {
ch: zerocopy_channel::Channel::new(&mut state.buf[..]),
});

let (sender, receiver) = state.ch.split();

(Self { rng, sender }, receiver)
}

pub async fn run(&mut self) -> ! {
loop {
let buf = self.sender.send().await;
self.rng.async_fill_bytes(&mut buf[..]).await.unwrap();
self.sender.send_done();
}
}
}
25 changes: 20 additions & 5 deletions examples/stm32wba/src/bin/ble_advertiser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,43 @@ use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::bind_interrupts;
use embassy_stm32::rng::{self, Rng};
use embassy_stm32_wpan::Ble;
use embassy_stm32_wpan::gap::{AdvData, AdvParams, AdvType};
use embassy_stm32_wpan::gatt::{CharProperties, GattEventMask, GattServer, SecurityPermissions, ServiceType, Uuid};
use embassy_stm32_wpan::{Ble, set_rng_instance};
use embassy_stm32_wpan::rng::{RngService, RngState};
use embassy_time::{Duration, Timer};
use static_cell::StaticCell;
use {defmt_rtt as _, panic_probe as _};

bind_interrupts!(struct Irqs {
RNG => rng::InterruptHandler<embassy_stm32::peripherals::RNG>;
});

#[embassy_executor::task]
async fn run_rng(mut rng_service: RngService<'static, embassy_stm32::peripherals::RNG>) {
rng_service.run().await;
}

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
async fn main(spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
info!("Embassy STM32WBA BLE Advertiser Example");

// Initialize RNG (required by BLE stack)
let mut rng = Rng::new(p.RNG, Irqs);
set_rng_instance(&mut rng as *mut _ as *mut ());
static RNG_STATE: StaticCell<RngState> = StaticCell::new();

let rng = Rng::new(p.RNG, Irqs);
let (rng_service, receiver) = RngService::new(rng, RNG_STATE.init(RngState::new()));

spawner.spawn(run_rng(rng_service).unwrap());
info!("RNG initialized");

// Wait for the rng to fill the channel
Timer::after(Duration::from_millis(500)).await;

// Initialize BLE stack
let mut ble = Ble::new();
ble.init().expect("BLE initialization failed");
ble.init(receiver).expect("BLE initialization failed");
info!("BLE stack initialized");

// Initialize GATT server
Expand Down