aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32-wpan/src/wba/linklayer_plat.rs258
1 files changed, 258 insertions, 0 deletions
diff --git a/embassy-stm32-wpan/src/wba/linklayer_plat.rs b/embassy-stm32-wpan/src/wba/linklayer_plat.rs
index f53783666..be9c49ab3 100644
--- a/embassy-stm32-wpan/src/wba/linklayer_plat.rs
+++ b/embassy-stm32-wpan/src/wba/linklayer_plat.rs
@@ -1,6 +1,84 @@
1// /* USER CODE BEGIN Header */
2// /**
3// ******************************************************************************
4// * @file linklayer_plat.c
5// * @author MCD Application Team
6// * @brief Source file for the linklayer plateform adaptation layer
7// ******************************************************************************
8// * @attention
9// *
10// * Copyright (c) 2024 STMicroelectronics.
11// * All rights reserved.
12// *
13// * This software is licensed under terms that can be found in the LICENSE file
14// * in the root directory of this software component.
15// * If no LICENSE file comes with this software, it is provided AS-IS.
16// *
17// ******************************************************************************
18// */
19// /* USER CODE END Header */
20//
21// #include "stm32wbaxx_hal.h"
22// #include "stm32wbaxx_hal_conf.h"
23// #include "stm32wbaxx_ll_rcc.h"
24//
25// #include "app_common.h"
26// #include "app_conf.h"
27// #include "linklayer_plat.h"
28// #include "scm.h"
29// #include "log_module.h"
30// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)
31// #include "adc_ctrl.h"
32// #endif /* (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1) */
33//
34// #if (CFG_LPM_LEVEL != 0)
35// #include "stm32_lpm.h"
36// #include "stm32_lpm_if.h"
37// #endif /* (CFG_LPM_LEVEL != 0) */
38//
39// /* USER CODE BEGIN Includes */
40//
41// /* USER CODE END Includes */
42//
43// #define max(a,b) ((a) > (b) ? a : b)
44//
45// /* 2.4GHz RADIO ISR callbacks */
46// void (*radio_callback)(void) = NULL;
47// void (*low_isr_callback)(void) = NULL;
48//
49// /* RNG handle */
50// extern RNG_HandleTypeDef hrng;
51//
52// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)
53// /* Link Layer temperature request from background */
54// extern void ll_sys_bg_temperature_measurement(void);
55// #endif /* (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1) */
56//
57// /* Radio critical sections */
58// static uint32_t primask_bit = 0;
59// volatile int32_t prio_high_isr_counter = 0;
60// volatile int32_t prio_low_isr_counter = 0;
61// volatile int32_t prio_sys_isr_counter = 0;
62// volatile int32_t irq_counter = 0;
63// volatile uint32_t local_basepri_value = 0;
64//
65// /* Radio SW low ISR global variable */
66// volatile uint8_t radio_sw_low_isr_is_running_high_prio = 0;
67//
68// /* Radio bus clock control variables */
69// uint8_t AHB5_SwitchedOff = 0;
70// uint32_t radio_sleep_timer_val = 0;
71//
72// /* USER CODE BEGIN LINKLAYER_PLAT 0 */
73//
74// /* USER CODE END LINKLAYER_PLAT 0 */
1#![cfg(feature = "wba")] 75#![cfg(feature = "wba")]
2#![allow(clippy::missing_safety_doc)] 76#![allow(clippy::missing_safety_doc)]
3 77
78//! STM32WBA Link Layer platform adaptation layer.
79//!
80//! Based on STMicroelectronics original C source `linklayer_plat.c` (2024).
81
4use core::hint::spin_loop; 82use core::hint::spin_loop;
5use core::ptr; 83use core::ptr;
6use core::sync::atomic::{AtomicBool, AtomicI32, AtomicPtr, AtomicU32, Ordering}; 84use core::sync::atomic::{AtomicBool, AtomicI32, AtomicPtr, AtomicU32, Ordering};
@@ -200,16 +278,36 @@ pub unsafe fn run_radio_sw_low_isr() {
200 } 278 }
201} 279}
202 280
281/// Initialize radio-related clock prerequisites.
282///
283/// Currently this touches the sleep timer to ensure the Link Layer common
284/// interface is initialized. It does not actively reconfigure clocks.
285///
286/// # Safety
287/// Called from the vendor Link Layer. Must run in a context where accessing
288/// the LL common interface is safe.
203#[unsafe(no_mangle)] 289#[unsafe(no_mangle)]
204pub unsafe extern "C" fn LINKLAYER_PLAT_ClockInit() { 290pub unsafe extern "C" fn LINKLAYER_PLAT_ClockInit() {
205 let _ = link_layer::ll_intf_cmn_get_slptmr_value(); 291 let _ = link_layer::ll_intf_cmn_get_slptmr_value();
206} 292}
207 293
294/// Busy-wait for the requested duration in microseconds.
295///
296/// Blocks the current context until `delay` microseconds have elapsed.
297///
298/// # Safety
299/// Must be called only in contexts where busy-waiting is acceptable (e.g. no
300/// hard real-time deadlines are violated).
208#[unsafe(no_mangle)] 301#[unsafe(no_mangle)]
209pub unsafe extern "C" fn LINKLAYER_PLAT_DelayUs(delay: u32) { 302pub unsafe extern "C" fn LINKLAYER_PLAT_DelayUs(delay: u32) {
210 block_for(Duration::from_micros(u64::from(delay))); 303 block_for(Duration::from_micros(u64::from(delay)));
211} 304}
212 305
306/// Assert a condition and panic if it is false.
307///
308/// # Safety
309/// None beyond general panic considerations; will abort/panic the program if
310/// `condition == 0`.
213#[unsafe(no_mangle)] 311#[unsafe(no_mangle)]
214pub unsafe extern "C" fn LINKLAYER_PLAT_Assert(condition: u8) { 312pub unsafe extern "C" fn LINKLAYER_PLAT_Assert(condition: u8) {
215 if condition == 0 { 313 if condition == 0 {
@@ -217,6 +315,13 @@ pub unsafe extern "C" fn LINKLAYER_PLAT_Assert(condition: u8) {
217 } 315 }
218} 316}
219 317
318/// Wait for the AHB5 clock domain to be ready after low-power entry.
319///
320/// If the platform flagged AHB5 as switched off before WFI, this waits until
321/// the sleep timer ticks, indicating the bus has resumed.
322///
323/// # Safety
324/// Spins while waiting; must be safe to busy-wait in the calling context.
220#[unsafe(no_mangle)] 325#[unsafe(no_mangle)]
221pub unsafe extern "C" fn LINKLAYER_PLAT_WaitHclkRdy() { 326pub unsafe extern "C" fn LINKLAYER_PLAT_WaitHclkRdy() {
222 if AHB5_SWITCHED_OFF.swap(false, Ordering::AcqRel) { 327 if AHB5_SWITCHED_OFF.swap(false, Ordering::AcqRel) {
@@ -227,11 +332,23 @@ pub unsafe extern "C" fn LINKLAYER_PLAT_WaitHclkRdy() {
227 } 332 }
228} 333}
229 334
335/// Notify that the system is entering WFI and AHB5 may be turned off depending
336/// on radio state.
337///
338/// # Safety
339/// None; this only updates internal state used to resynchronize after WFI.
230#[unsafe(no_mangle)] 340#[unsafe(no_mangle)]
231pub unsafe extern "C" fn LINKLAYER_PLAT_NotifyWFIEnter() { 341pub unsafe extern "C" fn LINKLAYER_PLAT_NotifyWFIEnter() {
232 AHB5_SWITCHED_OFF.store(true, Ordering::Release); 342 AHB5_SWITCHED_OFF.store(true, Ordering::Release);
233} 343}
234 344
345/// Notify that the system exited WFI and capture a reference sleep timer value.
346///
347/// If AHB5 was flagged as switched off on entry, records the current sleep
348/// timer value for later synchronization in [`LINKLAYER_PLAT_WaitHclkRdy`].
349///
350/// # Safety
351/// None; reads a monotonic timer from the LL common interface.
235#[unsafe(no_mangle)] 352#[unsafe(no_mangle)]
236pub unsafe extern "C" fn LINKLAYER_PLAT_NotifyWFIExit() { 353pub unsafe extern "C" fn LINKLAYER_PLAT_NotifyWFIExit() {
237 if AHB5_SWITCHED_OFF.load(Ordering::Acquire) { 354 if AHB5_SWITCHED_OFF.load(Ordering::Acquire) {
@@ -240,9 +357,24 @@ pub unsafe extern "C" fn LINKLAYER_PLAT_NotifyWFIExit() {
240 } 357 }
241} 358}
242 359
360/// Control the active clock (placeholder).
361///
362/// Currently a no-op. Present for API compatibility with vendor code.
363///
364/// # Safety
365/// None; function does nothing.
243#[unsafe(no_mangle)] 366#[unsafe(no_mangle)]
244pub unsafe extern "C" fn LINKLAYER_PLAT_AclkCtrl(_enable: u8) {} 367pub unsafe extern "C" fn LINKLAYER_PLAT_AclkCtrl(_enable: u8) {}
245 368
369/// Fill a buffer with pseudo-random bytes.
370///
371/// This uses a xorshift32 PRNG seeded from the sleep timer and core clock.
372/// It is not cryptographically secure and is intended only for non-security
373/// purposes.
374///
375/// # Safety
376/// - `ptr_rnd` must be valid for writes of `len` bytes.
377/// - The memory region must not alias mutable references elsewhere.
246#[unsafe(no_mangle)] 378#[unsafe(no_mangle)]
247pub unsafe extern "C" fn LINKLAYER_PLAT_GetRNG(ptr_rnd: *mut u8, len: u32) { 379pub unsafe extern "C" fn LINKLAYER_PLAT_GetRNG(ptr_rnd: *mut u8, len: u32) {
248 if ptr_rnd.is_null() || len == 0 { 380 if ptr_rnd.is_null() || len == 0 {
@@ -255,6 +387,14 @@ pub unsafe extern "C" fn LINKLAYER_PLAT_GetRNG(ptr_rnd: *mut u8, len: u32) {
255 } 387 }
256} 388}
257 389
390/// Configure the radio high-priority interrupt callback and NVIC state.
391///
392/// When `intr_cb` is `Some`, sets the NVIC priority to
393/// `RADIO_INTR_PRIO_HIGH` and unmasks the interrupt. Passing `None` disables
394/// the interrupt.
395///
396/// # Safety
397/// `intr_cb` must be an ISR-safe function. Alters NVIC state globally.
258#[unsafe(no_mangle)] 398#[unsafe(no_mangle)]
259pub unsafe extern "C" fn LINKLAYER_PLAT_SetupRadioIT(intr_cb: Option<Callback>) { 399pub unsafe extern "C" fn LINKLAYER_PLAT_SetupRadioIT(intr_cb: Option<Callback>) {
260 store_callback(&RADIO_CALLBACK, intr_cb); 400 store_callback(&RADIO_CALLBACK, intr_cb);
@@ -267,6 +407,14 @@ pub unsafe extern "C" fn LINKLAYER_PLAT_SetupRadioIT(intr_cb: Option<Callback>)
267 } 407 }
268} 408}
269 409
410/// Configure the software low-priority radio interrupt callback and NVIC state.
411///
412/// When `intr_cb` is `Some`, sets the NVIC priority to
413/// `RADIO_SW_LOW_INTR_PRIO` and unmasks the interrupt. Passing `None`
414/// disables the interrupt.
415///
416/// # Safety
417/// `intr_cb` must be ISR-safe. Alters NVIC state globally.
270#[unsafe(no_mangle)] 418#[unsafe(no_mangle)]
271pub unsafe extern "C" fn LINKLAYER_PLAT_SetupSwLowIT(intr_cb: Option<Callback>) { 419pub unsafe extern "C" fn LINKLAYER_PLAT_SetupSwLowIT(intr_cb: Option<Callback>) {
272 store_callback(&LOW_ISR_CALLBACK, intr_cb); 420 store_callback(&LOW_ISR_CALLBACK, intr_cb);
@@ -279,6 +427,13 @@ pub unsafe extern "C" fn LINKLAYER_PLAT_SetupSwLowIT(intr_cb: Option<Callback>)
279 } 427 }
280} 428}
281 429
430/// Trigger the software low-priority radio interrupt.
431///
432/// If `priority` is non-zero, elevates the interrupt to the low radio priority
433/// for this trigger or the next run when already active.
434///
435/// # Safety
436/// Alters NVIC pending and priority state; must be safe for the system.
282#[unsafe(no_mangle)] 437#[unsafe(no_mangle)]
283pub unsafe extern "C" fn LINKLAYER_PLAT_TriggerSwLowIT(priority: u8) { 438pub unsafe extern "C" fn LINKLAYER_PLAT_TriggerSwLowIT(priority: u8) {
284 let active = nvic_get_active(RADIO_SW_LOW_INTR_NUM); 439 let active = nvic_get_active(RADIO_SW_LOW_INTR_NUM);
@@ -297,6 +452,13 @@ pub unsafe extern "C" fn LINKLAYER_PLAT_TriggerSwLowIT(priority: u8) {
297 nvic_set_pending(RADIO_SW_LOW_INTR_NUM); 452 nvic_set_pending(RADIO_SW_LOW_INTR_NUM);
298} 453}
299 454
455/// Enable interrupts using a reference-counted scheme.
456///
457/// When the internal counter reaches zero, restores the previous PRIMASK
458/// snapshot and enables or keeps interrupts disabled accordingly.
459///
460/// # Safety
461/// Must be paired with prior calls to [`LINKLAYER_PLAT_DisableIRQ`].
300#[unsafe(no_mangle)] 462#[unsafe(no_mangle)]
301pub unsafe extern "C" fn LINKLAYER_PLAT_EnableIRQ() { 463pub unsafe extern "C" fn LINKLAYER_PLAT_EnableIRQ() {
302 if counter_release(&IRQ_COUNTER) { 464 if counter_release(&IRQ_COUNTER) {
@@ -309,6 +471,13 @@ pub unsafe extern "C" fn LINKLAYER_PLAT_EnableIRQ() {
309 } 471 }
310} 472}
311 473
474/// Disable interrupts using a reference-counted scheme.
475///
476/// Captures the current PRIMASK state on the first disable and then disables
477/// interrupts. Must be balanced with [`LINKLAYER_PLAT_EnableIRQ`].
478///
479/// # Safety
480/// Affects global interrupt state; may impact system timing and ISRs.
312#[unsafe(no_mangle)] 481#[unsafe(no_mangle)]
313pub unsafe extern "C" fn LINKLAYER_PLAT_DisableIRQ() { 482pub unsafe extern "C" fn LINKLAYER_PLAT_DisableIRQ() {
314 if counter_acquire(&IRQ_COUNTER) { 483 if counter_acquire(&IRQ_COUNTER) {
@@ -318,6 +487,16 @@ pub unsafe extern "C" fn LINKLAYER_PLAT_DisableIRQ() {
318 cortex_m::interrupt::disable(); 487 cortex_m::interrupt::disable();
319} 488}
320 489
490/// Enable specific Link Layer interrupt groups.
491///
492/// - `LL_HIGH_ISR_ONLY`: Unmask high-priority radio ISR.
493/// - `LL_LOW_ISR_ONLY`: Unmask software low-priority radio ISR.
494/// - `SYS_LOW_ISR`: Lower BASEPRI mask to re-enable lower-priority system ISRs.
495///
496/// Uses internal reference counters so multiple disables/enables can be nested.
497///
498/// # Safety
499/// Alters NVIC and BASEPRI state globally.
321#[unsafe(no_mangle)] 500#[unsafe(no_mangle)]
322pub unsafe extern "C" fn LINKLAYER_PLAT_EnableSpecificIRQ(isr_type: u8) { 501pub unsafe extern "C" fn LINKLAYER_PLAT_EnableSpecificIRQ(isr_type: u8) {
323 if (isr_type & link_layer::LL_HIGH_ISR_ONLY as u8) != 0 { 502 if (isr_type & link_layer::LL_HIGH_ISR_ONLY as u8) != 0 {
@@ -340,6 +519,16 @@ pub unsafe extern "C" fn LINKLAYER_PLAT_EnableSpecificIRQ(isr_type: u8) {
340 } 519 }
341} 520}
342 521
522/// Disable specific Link Layer interrupt groups.
523///
524/// - `LL_HIGH_ISR_ONLY`: Mask high-priority radio ISR.
525/// - `LL_LOW_ISR_ONLY`: Mask software low-priority radio ISR.
526/// - `SYS_LOW_ISR`: Raise BASEPRI to mask system ISRs lower than SW low-priority.
527///
528/// Uses internal reference counters so multiple disables/enables can be nested.
529///
530/// # Safety
531/// Alters NVIC and BASEPRI state globally.
343#[unsafe(no_mangle)] 532#[unsafe(no_mangle)]
344pub unsafe extern "C" fn LINKLAYER_PLAT_DisableSpecificIRQ(isr_type: u8) { 533pub unsafe extern "C" fn LINKLAYER_PLAT_DisableSpecificIRQ(isr_type: u8) {
345 if (isr_type & link_layer::LL_HIGH_ISR_ONLY as u8) != 0 { 534 if (isr_type & link_layer::LL_HIGH_ISR_ONLY as u8) != 0 {
@@ -363,45 +552,107 @@ pub unsafe extern "C" fn LINKLAYER_PLAT_DisableSpecificIRQ(isr_type: u8) {
363 } 552 }
364} 553}
365 554
555/// Unmask the radio high-priority interrupt.
556///
557/// # Safety
558/// Alters NVIC state globally.
366#[unsafe(no_mangle)] 559#[unsafe(no_mangle)]
367pub unsafe extern "C" fn LINKLAYER_PLAT_EnableRadioIT() { 560pub unsafe extern "C" fn LINKLAYER_PLAT_EnableRadioIT() {
368 nvic_enable(mac::RADIO_INTR_NUM); 561 nvic_enable(mac::RADIO_INTR_NUM);
369} 562}
370 563
564/// Mask the radio high-priority interrupt.
565///
566/// # Safety
567/// Alters NVIC state globally.
371#[unsafe(no_mangle)] 568#[unsafe(no_mangle)]
372pub unsafe extern "C" fn LINKLAYER_PLAT_DisableRadioIT() { 569pub unsafe extern "C" fn LINKLAYER_PLAT_DisableRadioIT() {
373 nvic_disable(mac::RADIO_INTR_NUM); 570 nvic_disable(mac::RADIO_INTR_NUM);
374} 571}
375 572
573/// Notify that a radio activity is starting.
574///
575/// Sets the radio interrupt priority to high and unmasks it.
576///
577/// # Safety
578/// Alters NVIC state globally.
376#[unsafe(no_mangle)] 579#[unsafe(no_mangle)]
377pub unsafe extern "C" fn LINKLAYER_PLAT_StartRadioEvt() { 580pub unsafe extern "C" fn LINKLAYER_PLAT_StartRadioEvt() {
378 nvic_set_priority(mac::RADIO_INTR_NUM, pack_priority(mac::RADIO_INTR_PRIO_HIGH)); 581 nvic_set_priority(mac::RADIO_INTR_NUM, pack_priority(mac::RADIO_INTR_PRIO_HIGH));
379 nvic_enable(mac::RADIO_INTR_NUM); 582 nvic_enable(mac::RADIO_INTR_NUM);
380} 583}
381 584
585/// Notify that a radio activity ended.
586///
587/// Lowers the radio interrupt priority to its low setting.
588///
589/// # Safety
590/// Alters NVIC state globally.
382#[unsafe(no_mangle)] 591#[unsafe(no_mangle)]
383pub unsafe extern "C" fn LINKLAYER_PLAT_StopRadioEvt() { 592pub unsafe extern "C" fn LINKLAYER_PLAT_StopRadioEvt() {
384 nvic_set_priority(mac::RADIO_INTR_NUM, pack_priority(mac::RADIO_INTR_PRIO_LOW)); 593 nvic_set_priority(mac::RADIO_INTR_NUM, pack_priority(mac::RADIO_INTR_PRIO_LOW));
385} 594}
386 595
596/// Notify that RCO calibration is starting (placeholder).
597///
598/// Currently a no-op.
599///
600/// # Safety
601/// None.
387#[unsafe(no_mangle)] 602#[unsafe(no_mangle)]
388pub unsafe extern "C" fn LINKLAYER_PLAT_RCOStartClbr() {} 603pub unsafe extern "C" fn LINKLAYER_PLAT_RCOStartClbr() {}
389 604
605/// Notify that RCO calibration ended (placeholder).
606///
607/// Currently a no-op.
608///
609/// # Safety
610/// None.
390#[unsafe(no_mangle)] 611#[unsafe(no_mangle)]
391pub unsafe extern "C" fn LINKLAYER_PLAT_RCOStopClbr() {} 612pub unsafe extern "C" fn LINKLAYER_PLAT_RCOStopClbr() {}
392 613
614/// Request a temperature measurement for radio calibration (placeholder).
615///
616/// Currently a no-op.
617///
618/// # Safety
619/// None.
393#[unsafe(no_mangle)] 620#[unsafe(no_mangle)]
394pub unsafe extern "C" fn LINKLAYER_PLAT_RequestTemperature() {} 621pub unsafe extern "C" fn LINKLAYER_PLAT_RequestTemperature() {}
395 622
623/// Notify that PHY calibration is starting (placeholder).
624///
625/// Currently a no-op.
626///
627/// # Safety
628/// None.
396#[unsafe(no_mangle)] 629#[unsafe(no_mangle)]
397pub unsafe extern "C" fn LINKLAYER_PLAT_PhyStartClbr() {} 630pub unsafe extern "C" fn LINKLAYER_PLAT_PhyStartClbr() {}
398 631
632/// Notify that PHY calibration ended (placeholder).
633///
634/// Currently a no-op.
635///
636/// # Safety
637/// None.
399#[unsafe(no_mangle)] 638#[unsafe(no_mangle)]
400pub unsafe extern "C" fn LINKLAYER_PLAT_PhyStopClbr() {} 639pub unsafe extern "C" fn LINKLAYER_PLAT_PhyStopClbr() {}
401 640
641/// Notify that new Link Layer scheduler timings have been applied (placeholder).
642///
643/// Currently a no-op.
644///
645/// # Safety
646/// None.
402#[unsafe(no_mangle)] 647#[unsafe(no_mangle)]
403pub unsafe extern "C" fn LINKLAYER_PLAT_SCHLDR_TIMING_UPDATE_NOT(_timings: *const link_layer::Evnt_timing_t) {} 648pub unsafe extern "C" fn LINKLAYER_PLAT_SCHLDR_TIMING_UPDATE_NOT(_timings: *const link_layer::Evnt_timing_t) {}
404 649
650/// Return the STMicroelectronics Bluetooth SIG Company Identifier.
651///
652/// Value: `0x0030`.
653///
654/// # Safety
655/// None.
405#[unsafe(no_mangle)] 656#[unsafe(no_mangle)]
406pub unsafe extern "C" fn LINKLAYER_PLAT_GetSTCompanyID() -> u32 { 657pub unsafe extern "C" fn LINKLAYER_PLAT_GetSTCompanyID() -> u32 {
407 // STMicroelectronics Bluetooth SIG Company Identifier 658 // STMicroelectronics Bluetooth SIG Company Identifier
@@ -409,6 +660,13 @@ pub unsafe extern "C" fn LINKLAYER_PLAT_GetSTCompanyID() -> u32 {
409 0x0030 660 0x0030
410} 661}
411 662
663/// Return the lower 32 bits of the STM32 unique 96-bit device identifier.
664///
665/// Note: This may differ from the ST-defined UDN encoding found in some device
666/// registers/documents; it returns the first word of the unique ID.
667///
668/// # Safety
669/// None.
412#[unsafe(no_mangle)] 670#[unsafe(no_mangle)]
413pub unsafe extern "C" fn LINKLAYER_PLAT_GetUDN() -> u32 { 671pub unsafe extern "C" fn LINKLAYER_PLAT_GetUDN() -> u32 {
414 // Read the first 32 bits of the STM32 unique 96-bit ID 672 // Read the first 32 bits of the STM32 unique 96-bit ID