aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32-wpan/src
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-stm32-wpan/src')
-rw-r--r--embassy-stm32-wpan/src/lib.rs186
-rw-r--r--embassy-stm32-wpan/src/mac/control.rs95
-rw-r--r--embassy-stm32-wpan/src/mac/driver.rs120
-rw-r--r--embassy-stm32-wpan/src/mac/runner.rs109
-rw-r--r--embassy-stm32-wpan/src/sub/mac.rs124
-rw-r--r--embassy-stm32-wpan/src/sub/mod.rs6
-rw-r--r--embassy-stm32-wpan/src/sub/sys.rs116
-rw-r--r--embassy-stm32-wpan/src/unsafe_linked_list.rs257
-rw-r--r--embassy-stm32-wpan/src/wb55/channels.rs (renamed from embassy-stm32-wpan/src/channels.rs)15
-rw-r--r--embassy-stm32-wpan/src/wb55/cmd.rs (renamed from embassy-stm32-wpan/src/cmd.rs)23
-rw-r--r--embassy-stm32-wpan/src/wb55/consts.rs (renamed from embassy-stm32-wpan/src/consts.rs)2
-rw-r--r--embassy-stm32-wpan/src/wb55/evt.rs (renamed from embassy-stm32-wpan/src/evt.rs)1
-rw-r--r--embassy-stm32-wpan/src/wb55/fmt.rs (renamed from embassy-stm32-wpan/src/fmt.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/lhci.rs (renamed from embassy-stm32-wpan/src/lhci.rs)4
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/commands.rs (renamed from embassy-stm32-wpan/src/mac/commands.rs)31
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/consts.rs (renamed from embassy-stm32-wpan/src/mac/consts.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/control.rs204
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/driver.rs213
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/event.rs (renamed from embassy-stm32-wpan/src/mac/event.rs)6
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/indications.rs (renamed from embassy-stm32-wpan/src/mac/indications.rs)35
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/macros.rs (renamed from embassy-stm32-wpan/src/mac/macros.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/mod.rs (renamed from embassy-stm32-wpan/src/mac/mod.rs)6
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/opcodes.rs (renamed from embassy-stm32-wpan/src/mac/opcodes.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/responses.rs (renamed from embassy-stm32-wpan/src/mac/responses.rs)0
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/runner.rs151
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/typedefs.rs (renamed from embassy-stm32-wpan/src/mac/typedefs.rs)55
-rw-r--r--embassy-stm32-wpan/src/wb55/mod.rs198
-rw-r--r--embassy-stm32-wpan/src/wb55/shci.rs (renamed from embassy-stm32-wpan/src/shci.rs)152
-rw-r--r--embassy-stm32-wpan/src/wb55/sub/ble.rs (renamed from embassy-stm32-wpan/src/sub/ble.rs)87
-rw-r--r--embassy-stm32-wpan/src/wb55/sub/mac.rs173
-rw-r--r--embassy-stm32-wpan/src/wb55/sub/mm.rs (renamed from embassy-stm32-wpan/src/sub/mm.rs)53
-rw-r--r--embassy-stm32-wpan/src/wb55/sub/mod.rs6
-rw-r--r--embassy-stm32-wpan/src/wb55/sub/sys.rs103
-rw-r--r--embassy-stm32-wpan/src/wb55/tables.rs (renamed from embassy-stm32-wpan/src/tables.rs)116
-rw-r--r--embassy-stm32-wpan/src/wb55/unsafe_linked_list.rs269
-rw-r--r--embassy-stm32-wpan/src/wba/bindings.rs1
-rw-r--r--embassy-stm32-wpan/src/wba/linklayer_plat.rs913
-rw-r--r--embassy-stm32-wpan/src/wba/ll_sys/ll_sys_cs.rs77
-rw-r--r--embassy-stm32-wpan/src/wba/ll_sys/ll_sys_dp_slp.rs163
-rw-r--r--embassy-stm32-wpan/src/wba/ll_sys/ll_sys_intf.rs199
-rw-r--r--embassy-stm32-wpan/src/wba/ll_sys/ll_sys_startup.rs125
-rw-r--r--embassy-stm32-wpan/src/wba/ll_sys/ll_version.rs115
-rw-r--r--embassy-stm32-wpan/src/wba/ll_sys/mod.rs5
-rw-r--r--embassy-stm32-wpan/src/wba/ll_sys_if.rs416
-rw-r--r--embassy-stm32-wpan/src/wba/mac_sys_if.rs188
-rw-r--r--embassy-stm32-wpan/src/wba/mod.rs6
-rw-r--r--embassy-stm32-wpan/src/wba/util_seq.rs243
47 files changed, 4155 insertions, 1212 deletions
diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs
index 40ff14795..3fabe112a 100644
--- a/embassy-stm32-wpan/src/lib.rs
+++ b/embassy-stm32-wpan/src/lib.rs
@@ -13,187 +13,19 @@
13 13
14#![no_std] 14#![no_std]
15#![allow(async_fn_in_trait)] 15#![allow(async_fn_in_trait)]
16#![allow(unsafe_op_in_unsafe_fn)]
16#![doc = include_str!("../README.md")] 17#![doc = include_str!("../README.md")]
17// #![warn(missing_docs)] 18// #![warn(missing_docs)]
18#![allow(static_mut_refs)] // TODO: Fix 19#![allow(static_mut_refs)] // TODO: Fix
19 20
20// This must go FIRST so that all the other modules see its macros. 21#[cfg(feature = "wb55")]
21mod fmt; 22mod wb55;
22 23
23use core::mem::MaybeUninit; 24#[cfg(feature = "wb55")]
24use core::sync::atomic::{compiler_fence, Ordering}; 25pub use wb55::*;
25 26
26use embassy_hal_internal::Peri; 27#[cfg(feature = "wba")]
27use embassy_stm32::interrupt; 28mod wba;
28use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler};
29use embassy_stm32::peripherals::IPCC;
30use sub::mm::MemoryManager;
31use sub::sys::Sys;
32use tables::*;
33use unsafe_linked_list::LinkedListNode;
34 29
35pub mod channels; 30#[cfg(feature = "wba")]
36pub mod cmd; 31pub use wba::*;
37pub mod consts;
38pub mod evt;
39pub mod lhci;
40pub mod shci;
41pub mod sub;
42pub mod tables;
43pub mod unsafe_linked_list;
44
45#[cfg(feature = "mac")]
46pub mod mac;
47
48#[cfg(feature = "ble")]
49pub use crate::sub::ble::hci;
50
51type PacketHeader = LinkedListNode;
52
53/// Transport Layer for the Mailbox interface
54pub struct TlMbox<'d> {
55 _ipcc: Peri<'d, IPCC>,
56
57 pub sys_subsystem: Sys,
58 pub mm_subsystem: MemoryManager,
59 #[cfg(feature = "ble")]
60 pub ble_subsystem: sub::ble::Ble,
61 #[cfg(feature = "mac")]
62 pub mac_subsystem: sub::mac::Mac,
63}
64
65impl<'d> TlMbox<'d> {
66 /// Initialise the Transport Layer, and creates and returns a wrapper around it.
67 ///
68 /// This method performs the initialisation laid out in AN5289 annex 14.1. However, it differs
69 /// from the implementation documented in Figure 64, to avoid needing to reference any C
70 /// function pointers.
71 ///
72 /// Annex 14.1 lays out the following methods that should be called:
73 /// 1. tl_mbox.c/TL_Init, which initialises the reference table that is shared between CPU1
74 /// and CPU2.
75 /// 2. shci_tl.c/shci_init(), which initialises the system transport layer, and in turn
76 /// calls tl_mbox.c/TL_SYS_Init, which initialises SYSTEM_EVT_QUEUE channel.
77 /// 3. tl_mbox.c/TL_MM_Init(), which initialises the channel used for sending memory
78 /// manager commands.
79 /// 4. tl_mbox.c/TL_Enable(), which enables the IPCC, and starts CPU2.
80 /// This implementation initialises all of the shared refernce tables and all IPCC channel that
81 /// would be initialised by this process. The developer should therefore treat this method as
82 /// completing all steps in Figure 64.
83 ///
84 /// Once this method has been called, no system commands may be sent until the CPU2 ready
85 /// signal is received, via [sys_subsystem.read]; this completes the procedure laid out in
86 /// Figure 65.
87 ///
88 /// If the `ble` feature is enabled, at this point, the user should call
89 /// [sys_subsystem.shci_c2_ble_init], before any commands are written to the
90 /// [TlMbox.ble_subsystem] ([sub::ble::Ble::new()] completes the process that would otherwise
91 /// be handled by `TL_BLE_Init`; see Figure 66). This completes the procedure laid out in
92 /// Figure 66.
93 // TODO: document what the user should do after calling init to use the mac_802_15_4 subsystem
94 pub fn init(
95 ipcc: Peri<'d, IPCC>,
96 _irqs: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler>
97 + interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_TX, TransmitInterruptHandler>,
98 config: Config,
99 ) -> Self {
100 // this is an inlined version of TL_Init from the STM32WB firmware as requested by AN5289.
101 // HW_IPCC_Init is not called, and its purpose is (presumably?) covered by this
102 // implementation
103 unsafe {
104 TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable {
105 device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(),
106 ble_table: TL_BLE_TABLE.as_ptr(),
107 thread_table: TL_THREAD_TABLE.as_ptr(),
108 sys_table: TL_SYS_TABLE.as_ptr(),
109 mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(),
110 traces_table: TL_TRACES_TABLE.as_ptr(),
111 mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(),
112 zigbee_table: TL_ZIGBEE_TABLE.as_ptr(),
113 lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(),
114 ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(),
115 });
116
117 TL_SYS_TABLE
118 .as_mut_ptr()
119 .write_volatile(MaybeUninit::zeroed().assume_init());
120 TL_DEVICE_INFO_TABLE
121 .as_mut_ptr()
122 .write_volatile(MaybeUninit::zeroed().assume_init());
123 TL_BLE_TABLE
124 .as_mut_ptr()
125 .write_volatile(MaybeUninit::zeroed().assume_init());
126 TL_THREAD_TABLE
127 .as_mut_ptr()
128 .write_volatile(MaybeUninit::zeroed().assume_init());
129 TL_MEM_MANAGER_TABLE
130 .as_mut_ptr()
131 .write_volatile(MaybeUninit::zeroed().assume_init());
132
133 TL_TRACES_TABLE
134 .as_mut_ptr()
135 .write_volatile(MaybeUninit::zeroed().assume_init());
136 TL_MAC_802_15_4_TABLE
137 .as_mut_ptr()
138 .write_volatile(MaybeUninit::zeroed().assume_init());
139 TL_ZIGBEE_TABLE
140 .as_mut_ptr()
141 .write_volatile(MaybeUninit::zeroed().assume_init());
142 TL_LLD_TESTS_TABLE
143 .as_mut_ptr()
144 .write_volatile(MaybeUninit::zeroed().assume_init());
145 TL_BLE_LLD_TABLE
146 .as_mut_ptr()
147 .write_volatile(MaybeUninit::zeroed().assume_init());
148
149 EVT_POOL
150 .as_mut_ptr()
151 .write_volatile(MaybeUninit::zeroed().assume_init());
152 SYS_SPARE_EVT_BUF
153 .as_mut_ptr()
154 .write_volatile(MaybeUninit::zeroed().assume_init());
155 CS_BUFFER
156 .as_mut_ptr()
157 .write_volatile(MaybeUninit::zeroed().assume_init());
158
159 #[cfg(feature = "ble")]
160 {
161 BLE_SPARE_EVT_BUF
162 .as_mut_ptr()
163 .write_volatile(MaybeUninit::zeroed().assume_init());
164
165 BLE_CMD_BUFFER
166 .as_mut_ptr()
167 .write_volatile(MaybeUninit::zeroed().assume_init());
168 HCI_ACL_DATA_BUFFER
169 .as_mut_ptr()
170 .write_volatile(MaybeUninit::zeroed().assume_init());
171 }
172
173 #[cfg(feature = "mac")]
174 {
175 MAC_802_15_4_CMD_BUFFER
176 .as_mut_ptr()
177 .write_volatile(MaybeUninit::zeroed().assume_init());
178 MAC_802_15_4_NOTIF_RSP_EVT_BUFFER
179 .as_mut_ptr()
180 .write_volatile(MaybeUninit::zeroed().assume_init());
181 }
182 }
183
184 compiler_fence(Ordering::SeqCst);
185
186 // this is equivalent to `HW_IPCC_Enable`, which is called by `TL_Enable`
187 Ipcc::enable(config);
188
189 Self {
190 _ipcc: ipcc,
191 sys_subsystem: sub::sys::Sys::new(),
192 #[cfg(feature = "ble")]
193 ble_subsystem: sub::ble::Ble::new(),
194 #[cfg(feature = "mac")]
195 mac_subsystem: sub::mac::Mac::new(),
196 mm_subsystem: sub::mm::MemoryManager::new(),
197 }
198 }
199}
diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs
deleted file mode 100644
index e8d2f9f7b..000000000
--- a/embassy-stm32-wpan/src/mac/control.rs
+++ /dev/null
@@ -1,95 +0,0 @@
1use core::future::Future;
2use core::task;
3use core::task::Poll;
4
5use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
6use embassy_sync::mutex::MutexGuard;
7use embassy_sync::signal::Signal;
8use futures_util::FutureExt;
9
10use super::commands::MacCommand;
11use super::event::MacEvent;
12use super::typedefs::MacError;
13use crate::mac::runner::Runner;
14
15pub struct Control<'a> {
16 runner: &'a Runner<'a>,
17}
18
19impl<'a> Control<'a> {
20 pub(crate) fn new(runner: &'a Runner<'a>) -> Self {
21 Self { runner: runner }
22 }
23
24 pub async fn send_command<T>(&self, cmd: &T) -> Result<(), MacError>
25 where
26 T: MacCommand,
27 {
28 let _wm = self.runner.write_mutex.lock().await;
29
30 self.runner.mac_subsystem.send_command(cmd).await
31 }
32
33 pub async fn send_command_and_get_response<T>(&self, cmd: &T) -> Result<EventToken<'a>, MacError>
34 where
35 T: MacCommand,
36 {
37 let rm = self.runner.read_mutex.lock().await;
38 let _wm = self.runner.write_mutex.lock().await;
39 let token = EventToken::new(self.runner, rm);
40
41 self.runner.mac_subsystem.send_command(cmd).await?;
42
43 Ok(token)
44 }
45}
46
47pub struct EventToken<'a> {
48 runner: &'a Runner<'a>,
49 _mutex_guard: MutexGuard<'a, CriticalSectionRawMutex, ()>,
50}
51
52impl<'a> EventToken<'a> {
53 pub(crate) fn new(runner: &'a Runner<'a>, mutex_guard: MutexGuard<'a, CriticalSectionRawMutex, ()>) -> Self {
54 // Enable event receiving
55 runner.rx_event_channel.lock(|s| {
56 *s.borrow_mut() = Some(Signal::new());
57 });
58
59 Self {
60 runner: runner,
61 _mutex_guard: mutex_guard,
62 }
63 }
64}
65
66impl<'a> Future for EventToken<'a> {
67 type Output = MacEvent<'a>;
68
69 fn poll(self: core::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
70 self.get_mut().runner.rx_event_channel.lock(|s| {
71 let signal = s.borrow_mut();
72 let signal = match &*signal {
73 Some(s) => s,
74 _ => unreachable!(),
75 };
76
77 let result = match signal.wait().poll_unpin(cx) {
78 Poll::Ready(mac_event) => Poll::Ready(mac_event),
79 Poll::Pending => Poll::Pending,
80 };
81
82 result
83 })
84 }
85}
86
87impl<'a> Drop for EventToken<'a> {
88 fn drop(&mut self) {
89 // Disable event receiving
90 // This will also drop the contained event, if it exists, and will free up receiving the next event
91 self.runner.rx_event_channel.lock(|s| {
92 *s.borrow_mut() = None;
93 });
94 }
95}
diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs
deleted file mode 100644
index 41cca09e3..000000000
--- a/embassy-stm32-wpan/src/mac/driver.rs
+++ /dev/null
@@ -1,120 +0,0 @@
1#![deny(unused_must_use)]
2
3use core::task::Context;
4
5use embassy_net_driver::{Capabilities, HardwareAddress, LinkState};
6use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
7use embassy_sync::channel::Channel;
8
9use crate::mac::event::MacEvent;
10use crate::mac::runner::Runner;
11use crate::mac::MTU;
12
13pub struct Driver<'d> {
14 runner: &'d Runner<'d>,
15}
16
17impl<'d> Driver<'d> {
18 pub(crate) fn new(runner: &'d Runner<'d>) -> Self {
19 Self { runner: runner }
20 }
21}
22
23impl<'d> embassy_net_driver::Driver for Driver<'d> {
24 // type RxToken<'a> = RxToken<'a, 'd> where Self: 'a;
25 // type TxToken<'a> = TxToken<'a, 'd> where Self: 'a;
26 type RxToken<'a>
27 = RxToken<'d>
28 where
29 Self: 'a;
30 type TxToken<'a>
31 = TxToken<'d>
32 where
33 Self: 'a;
34
35 fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
36 if self.runner.rx_channel.poll_ready_to_receive(cx).is_ready()
37 && self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready()
38 {
39 Some((
40 RxToken {
41 rx: &self.runner.rx_channel,
42 },
43 TxToken {
44 tx: &self.runner.tx_channel,
45 tx_buf: &self.runner.tx_buf_channel,
46 },
47 ))
48 } else {
49 None
50 }
51 }
52
53 fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
54 if self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready() {
55 Some(TxToken {
56 tx: &self.runner.tx_channel,
57 tx_buf: &self.runner.tx_buf_channel,
58 })
59 } else {
60 None
61 }
62 }
63
64 fn capabilities(&self) -> Capabilities {
65 let mut caps = Capabilities::default();
66 caps.max_transmission_unit = MTU;
67 // caps.max_burst_size = Some(self.tx.len());
68 caps
69 }
70
71 fn link_state(&mut self, _cx: &mut Context) -> LinkState {
72 LinkState::Down
73 }
74
75 fn hardware_address(&self) -> HardwareAddress {
76 // self.mac_addr
77 HardwareAddress::Ieee802154([0; 8])
78 }
79}
80
81pub struct RxToken<'d> {
82 rx: &'d Channel<CriticalSectionRawMutex, MacEvent<'d>, 1>,
83}
84
85impl<'d> embassy_net_driver::RxToken for RxToken<'d> {
86 fn consume<R, F>(self, f: F) -> R
87 where
88 F: FnOnce(&mut [u8]) -> R,
89 {
90 // Only valid data events should be put into the queue
91
92 let data_event = match self.rx.try_receive().unwrap() {
93 MacEvent::McpsDataInd(data_event) => data_event,
94 _ => unreachable!(),
95 };
96
97 f(&mut data_event.payload())
98 }
99}
100
101pub struct TxToken<'d> {
102 tx: &'d Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), 5>,
103 tx_buf: &'d Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], 5>,
104}
105
106impl<'d> embassy_net_driver::TxToken for TxToken<'d> {
107 fn consume<R, F>(self, len: usize, f: F) -> R
108 where
109 F: FnOnce(&mut [u8]) -> R,
110 {
111 // Only valid tx buffers should be put into the queue
112 let buf = self.tx_buf.try_receive().unwrap();
113 let r = f(&mut buf[..len]);
114
115 // The tx channel should always be of equal capacity to the tx_buf channel
116 self.tx.try_send((buf, len)).unwrap();
117
118 r
119 }
120}
diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs
deleted file mode 100644
index d3099b6b7..000000000
--- a/embassy-stm32-wpan/src/mac/runner.rs
+++ /dev/null
@@ -1,109 +0,0 @@
1use core::cell::RefCell;
2
3use embassy_futures::join;
4use embassy_sync::blocking_mutex;
5use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
6use embassy_sync::channel::Channel;
7use embassy_sync::mutex::Mutex;
8use embassy_sync::signal::Signal;
9
10use crate::mac::commands::DataRequest;
11use crate::mac::event::MacEvent;
12use crate::mac::typedefs::{AddressMode, MacAddress, PanId, SecurityLevel};
13use crate::mac::MTU;
14use crate::sub::mac::Mac;
15
16type ZeroCopyPubSub<M, T> = blocking_mutex::Mutex<M, RefCell<Option<Signal<NoopRawMutex, T>>>>;
17
18pub struct Runner<'a> {
19 pub(crate) mac_subsystem: Mac,
20 // rx event backpressure is already provided through the MacEvent drop mechanism
21 // therefore, we don't need to worry about overwriting events
22 pub(crate) rx_event_channel: ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,
23 pub(crate) read_mutex: Mutex<CriticalSectionRawMutex, ()>,
24 pub(crate) write_mutex: Mutex<CriticalSectionRawMutex, ()>,
25 pub(crate) rx_channel: Channel<CriticalSectionRawMutex, MacEvent<'a>, 1>,
26 pub(crate) tx_channel: Channel<CriticalSectionRawMutex, (&'a mut [u8; MTU], usize), 5>,
27 pub(crate) tx_buf_channel: Channel<CriticalSectionRawMutex, &'a mut [u8; MTU], 5>,
28}
29
30impl<'a> Runner<'a> {
31 pub fn new(mac: Mac, tx_buf_queue: [&'a mut [u8; MTU]; 5]) -> Self {
32 let this = Self {
33 mac_subsystem: mac,
34 rx_event_channel: blocking_mutex::Mutex::new(RefCell::new(None)),
35 read_mutex: Mutex::new(()),
36 write_mutex: Mutex::new(()),
37 rx_channel: Channel::new(),
38 tx_channel: Channel::new(),
39 tx_buf_channel: Channel::new(),
40 };
41
42 for buf in tx_buf_queue {
43 this.tx_buf_channel.try_send(buf).unwrap();
44 }
45
46 this
47 }
48
49 pub async fn run(&'a self) -> ! {
50 join::join(
51 async {
52 loop {
53 if let Ok(mac_event) = self.mac_subsystem.read().await {
54 match mac_event {
55 MacEvent::McpsDataInd(_) => {
56 self.rx_channel.send(mac_event).await;
57 }
58 _ => {
59 self.rx_event_channel.lock(|s| {
60 match &*s.borrow() {
61 Some(signal) => {
62 signal.signal(mac_event);
63 }
64 None => {}
65 };
66 });
67 }
68 }
69 }
70 }
71 },
72 async {
73 let mut msdu_handle = 0x02;
74
75 loop {
76 let (buf, len) = self.tx_channel.receive().await;
77 let _wm = self.write_mutex.lock().await;
78
79 // The mutex should be dropped on the next loop iteration
80 self.mac_subsystem
81 .send_command(
82 DataRequest {
83 src_addr_mode: AddressMode::Short,
84 dst_addr_mode: AddressMode::Short,
85 dst_pan_id: PanId([0x1A, 0xAA]),
86 dst_address: MacAddress::BROADCAST,
87 msdu_handle: msdu_handle,
88 ack_tx: 0x00,
89 gts_tx: false,
90 security_level: SecurityLevel::Unsecure,
91 ..Default::default()
92 }
93 .set_buffer(&buf[..len]),
94 )
95 .await
96 .unwrap();
97
98 msdu_handle = msdu_handle.wrapping_add(1);
99
100 // The tx channel should always be of equal capacity to the tx_buf channel
101 self.tx_buf_channel.try_send(buf).unwrap();
102 }
103 },
104 )
105 .await;
106
107 loop {}
108 }
109}
diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs
deleted file mode 100644
index baf4da979..000000000
--- a/embassy-stm32-wpan/src/sub/mac.rs
+++ /dev/null
@@ -1,124 +0,0 @@
1use core::future::poll_fn;
2use core::ptr;
3use core::sync::atomic::{AtomicBool, Ordering};
4use core::task::Poll;
5
6use embassy_futures::poll_once;
7use embassy_stm32::ipcc::Ipcc;
8use embassy_sync::waitqueue::AtomicWaker;
9
10use crate::cmd::CmdPacket;
11use crate::consts::TlPacketType;
12use crate::evt::{EvtBox, EvtPacket};
13use crate::mac::commands::MacCommand;
14use crate::mac::event::MacEvent;
15use crate::mac::typedefs::MacError;
16use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER};
17use crate::{channels, evt};
18
19static MAC_WAKER: AtomicWaker = AtomicWaker::new();
20static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false);
21
22pub struct Mac {
23 _private: (),
24}
25
26impl Mac {
27 pub(crate) fn new() -> Self {
28 Self { _private: () }
29 }
30
31 /// `HW_IPCC_MAC_802_15_4_EvtNot`
32 ///
33 /// This function will stall if the previous `EvtBox` has not been dropped
34 pub async fn tl_read(&self) -> EvtBox<Self> {
35 // Wait for the last event box to be dropped
36 poll_fn(|cx| {
37 MAC_WAKER.register(cx.waker());
38 if MAC_EVT_OUT.load(Ordering::SeqCst) {
39 Poll::Pending
40 } else {
41 Poll::Ready(())
42 }
43 })
44 .await;
45
46 // Return a new event box
47 Ipcc::receive(channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL, || unsafe {
48 // The closure is not async, therefore the closure must execute to completion (cannot be dropped)
49 // Therefore, the event box is guaranteed to be cleaned up if it's not leaked
50 MAC_EVT_OUT.store(true, Ordering::SeqCst);
51
52 Some(EvtBox::new(MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _))
53 })
54 .await
55 }
56
57 /// `HW_IPCC_MAC_802_15_4_CmdEvtNot`
58 pub async fn tl_write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 {
59 self.tl_write(opcode, payload).await;
60 Ipcc::flush(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL).await;
61
62 unsafe {
63 let p_event_packet = MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket;
64 let p_mac_rsp_evt = &((*p_event_packet).evt_serial.evt.payload) as *const u8;
65
66 ptr::read_volatile(p_mac_rsp_evt)
67 }
68 }
69
70 /// `TL_MAC_802_15_4_SendCmd`
71 pub async fn tl_write(&self, opcode: u16, payload: &[u8]) {
72 Ipcc::send(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL, || unsafe {
73 CmdPacket::write_into(
74 MAC_802_15_4_CMD_BUFFER.as_mut_ptr(),
75 TlPacketType::MacCmd,
76 opcode,
77 payload,
78 );
79 })
80 .await;
81 }
82
83 pub async fn send_command<T>(&self, cmd: &T) -> Result<(), MacError>
84 where
85 T: MacCommand,
86 {
87 let response = self.tl_write_and_get_response(T::OPCODE as u16, cmd.payload()).await;
88
89 if response == 0x00 {
90 Ok(())
91 } else {
92 Err(MacError::from(response))
93 }
94 }
95
96 pub async fn read(&self) -> Result<MacEvent<'_>, ()> {
97 MacEvent::new(self.tl_read().await)
98 }
99}
100
101impl evt::MemoryManager for Mac {
102 /// SAFETY: passing a pointer to something other than a managed event packet is UB
103 unsafe fn drop_event_packet(_: *mut EvtPacket) {
104 trace!("mac drop event");
105
106 // Write the ack
107 CmdPacket::write_into(
108 MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _,
109 TlPacketType::OtAck,
110 0,
111 &[],
112 );
113
114 // Clear the rx flag
115 let _ = poll_once(Ipcc::receive::<()>(
116 channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL,
117 || None,
118 ));
119
120 // Allow a new read call
121 MAC_EVT_OUT.store(false, Ordering::SeqCst);
122 MAC_WAKER.wake();
123 }
124}
diff --git a/embassy-stm32-wpan/src/sub/mod.rs b/embassy-stm32-wpan/src/sub/mod.rs
deleted file mode 100644
index bee3dbdf1..000000000
--- a/embassy-stm32-wpan/src/sub/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
1#[cfg(feature = "ble")]
2pub mod ble;
3#[cfg(feature = "mac")]
4pub mod mac;
5pub mod mm;
6pub mod sys;
diff --git a/embassy-stm32-wpan/src/sub/sys.rs b/embassy-stm32-wpan/src/sub/sys.rs
deleted file mode 100644
index cf6df58bf..000000000
--- a/embassy-stm32-wpan/src/sub/sys.rs
+++ /dev/null
@@ -1,116 +0,0 @@
1use core::ptr;
2
3use crate::cmd::CmdPacket;
4use crate::consts::TlPacketType;
5use crate::evt::{CcEvt, EvtBox, EvtPacket};
6#[allow(unused_imports)]
7use crate::shci::{SchiCommandStatus, ShciBleInitCmdParam, ShciOpcode};
8use crate::sub::mm;
9use crate::tables::{SysTable, WirelessFwInfoTable};
10use crate::unsafe_linked_list::LinkedListNode;
11use crate::{channels, Ipcc, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE};
12
13/// A guard that, once constructed, allows for sys commands to be sent to CPU2.
14pub struct Sys {
15 _private: (),
16}
17
18impl Sys {
19 /// TL_Sys_Init
20 pub(crate) fn new() -> Self {
21 unsafe {
22 LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr());
23
24 TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable {
25 pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(),
26 sys_queue: SYSTEM_EVT_QUEUE.as_ptr(),
27 });
28 }
29
30 Self { _private: () }
31 }
32
33 /// Returns CPU2 wireless firmware information (if present).
34 pub fn wireless_fw_info(&self) -> Option<WirelessFwInfoTable> {
35 let info = unsafe { TL_DEVICE_INFO_TABLE.as_mut_ptr().read_volatile().wireless_fw_info_table };
36
37 // Zero version indicates that CPU2 wasn't active and didn't fill the information table
38 if info.version != 0 {
39 Some(info)
40 } else {
41 None
42 }
43 }
44
45 pub async fn write(&self, opcode: ShciOpcode, payload: &[u8]) {
46 Ipcc::send(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, || unsafe {
47 CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode as u16, payload);
48 })
49 .await;
50 }
51
52 /// `HW_IPCC_SYS_CmdEvtNot`
53 pub async fn write_and_get_response(&self, opcode: ShciOpcode, payload: &[u8]) -> Result<SchiCommandStatus, ()> {
54 self.write(opcode, payload).await;
55 Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await;
56
57 unsafe {
58 let p_event_packet = SYS_CMD_BUF.as_ptr() as *const EvtPacket;
59 let p_command_event = &((*p_event_packet).evt_serial.evt.payload) as *const _ as *const CcEvt;
60 let p_payload = &((*p_command_event).payload) as *const u8;
61
62 ptr::read_volatile(p_payload).try_into()
63 }
64 }
65
66 #[cfg(feature = "mac")]
67 pub async fn shci_c2_mac_802_15_4_init(&self) -> Result<SchiCommandStatus, ()> {
68 use crate::tables::{
69 Mac802_15_4Table, TracesTable, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER,
70 TL_MAC_802_15_4_TABLE, TL_TRACES_TABLE, TRACES_EVT_QUEUE,
71 };
72
73 unsafe {
74 LinkedListNode::init_head(TRACES_EVT_QUEUE.as_mut_ptr() as *mut _);
75
76 TL_TRACES_TABLE.as_mut_ptr().write_volatile(TracesTable {
77 traces_queue: TRACES_EVT_QUEUE.as_ptr() as *const _,
78 });
79
80 TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table {
81 p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(),
82 p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(),
83 evt_queue: core::ptr::null_mut(),
84 });
85 };
86
87 self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await
88 }
89
90 /// Send a request to CPU2 to initialise the BLE stack.
91 ///
92 /// This must be called before any BLE commands are sent via the BLE channel (according to
93 /// AN5289, Figures 65 and 66). It should only be called after CPU2 sends a system event, via
94 /// `HW_IPCC_SYS_EvtNot`, aka `IoBusCallBackUserEvt` (as detailed in Figure 65), aka
95 /// [crate::sub::ble::hci::host::uart::UartHci::read].
96 #[cfg(feature = "ble")]
97 pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> Result<SchiCommandStatus, ()> {
98 self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await
99 }
100
101 /// `HW_IPCC_SYS_EvtNot`
102 ///
103 /// This method takes the place of the `HW_IPCC_SYS_EvtNot`/`SysUserEvtRx`/`APPE_SysUserEvtRx`,
104 /// as the embassy implementation avoids the need to call C public bindings, and instead
105 /// handles the event channels directly.
106 pub async fn read(&self) -> EvtBox<mm::MemoryManager> {
107 Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe {
108 if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) {
109 Some(EvtBox::new(node_ptr.cast()))
110 } else {
111 None
112 }
113 })
114 .await
115 }
116}
diff --git a/embassy-stm32-wpan/src/unsafe_linked_list.rs b/embassy-stm32-wpan/src/unsafe_linked_list.rs
deleted file mode 100644
index d8bc29763..000000000
--- a/embassy-stm32-wpan/src/unsafe_linked_list.rs
+++ /dev/null
@@ -1,257 +0,0 @@
1//! Unsafe linked list.
2//! Translated from ST's C by `c2rust` tool.
3
4#![allow(
5 dead_code,
6 mutable_transmutes,
7 non_camel_case_types,
8 non_snake_case,
9 non_upper_case_globals,
10 unused_assignments,
11 unused_mut
12)]
13
14use core::ptr;
15
16use cortex_m::interrupt;
17
18#[derive(Copy, Clone)]
19#[repr(C, packed(4))]
20pub struct LinkedListNode {
21 pub next: *mut LinkedListNode,
22 pub prev: *mut LinkedListNode,
23}
24
25impl Default for LinkedListNode {
26 fn default() -> Self {
27 LinkedListNode {
28 next: core::ptr::null_mut(),
29 prev: core::ptr::null_mut(),
30 }
31 }
32}
33
34impl LinkedListNode {
35 pub unsafe fn init_head(mut p_list_head: *mut LinkedListNode) {
36 ptr::write_volatile(
37 p_list_head,
38 LinkedListNode {
39 next: p_list_head,
40 prev: p_list_head,
41 },
42 );
43 }
44
45 pub unsafe fn is_empty(mut p_list_head: *mut LinkedListNode) -> bool {
46 interrupt::free(|_| ptr::read_volatile(p_list_head).next == p_list_head)
47 }
48
49 /// Insert `node` after `list_head` and before the next node
50 pub unsafe fn insert_head(mut p_list_head: *mut LinkedListNode, mut p_node: *mut LinkedListNode) {
51 interrupt::free(|_| {
52 let mut list_head = ptr::read_volatile(p_list_head);
53 if p_list_head != list_head.next {
54 let mut node_next = ptr::read_volatile(list_head.next);
55 let node = LinkedListNode {
56 next: list_head.next,
57 prev: p_list_head,
58 };
59
60 list_head.next = p_node;
61 node_next.prev = p_node;
62
63 // All nodes must be written because they will all be seen by another core
64 ptr::write_volatile(p_node, node);
65 ptr::write_volatile(node.next, node_next);
66 ptr::write_volatile(p_list_head, list_head);
67 } else {
68 let node = LinkedListNode {
69 next: list_head.next,
70 prev: p_list_head,
71 };
72
73 list_head.next = p_node;
74 list_head.prev = p_node;
75
76 // All nodes must be written because they will all be seen by another core
77 ptr::write_volatile(p_node, node);
78 ptr::write_volatile(p_list_head, list_head);
79 }
80 });
81 }
82
83 /// Insert `node` before `list_tail` and after the second-to-last node
84 pub unsafe fn insert_tail(mut p_list_tail: *mut LinkedListNode, mut p_node: *mut LinkedListNode) {
85 interrupt::free(|_| {
86 let mut list_tail = ptr::read_volatile(p_list_tail);
87 if p_list_tail != list_tail.prev {
88 let mut node_prev = ptr::read_volatile(list_tail.prev);
89 let node = LinkedListNode {
90 next: p_list_tail,
91 prev: list_tail.prev,
92 };
93
94 list_tail.prev = p_node;
95 node_prev.next = p_node;
96
97 // All nodes must be written because they will all be seen by another core
98 ptr::write_volatile(p_node, node);
99 ptr::write_volatile(node.prev, node_prev);
100 ptr::write_volatile(p_list_tail, list_tail);
101 } else {
102 let node = LinkedListNode {
103 next: p_list_tail,
104 prev: list_tail.prev,
105 };
106
107 list_tail.prev = p_node;
108 list_tail.next = p_node;
109
110 // All nodes must be written because they will all be seen by another core
111 ptr::write_volatile(p_node, node);
112 ptr::write_volatile(p_list_tail, list_tail);
113 }
114 });
115 }
116
117 /// Remove `node` from the linked list
118 pub unsafe fn remove_node(mut p_node: *mut LinkedListNode) {
119 interrupt::free(|_| {
120 // trace!("remove node: {:x}", p_node);
121 // apparently linked list nodes are not always aligned.
122 // if more hardfaults occur, more of these may need to be converted to unaligned.
123 let node = ptr::read_unaligned(p_node);
124 // trace!("remove node: prev/next {:x}/{:x}", node.prev, node.next);
125
126 if node.next != node.prev {
127 let mut node_next = ptr::read_volatile(node.next);
128 let mut node_prev = ptr::read_volatile(node.prev);
129
130 node_prev.next = node.next;
131 node_next.prev = node.prev;
132
133 ptr::write_volatile(node.next, node_next);
134 ptr::write_volatile(node.prev, node_prev);
135 } else {
136 let mut node_next = ptr::read_volatile(node.next);
137
138 node_next.next = node.next;
139 node_next.prev = node.prev;
140
141 ptr::write_volatile(node.next, node_next);
142 }
143 });
144 }
145
146 /// Remove `list_head` and return a pointer to the `node`.
147 pub unsafe fn remove_head(mut p_list_head: *mut LinkedListNode) -> Option<*mut LinkedListNode> {
148 interrupt::free(|_| {
149 let list_head = ptr::read_volatile(p_list_head);
150
151 if list_head.next == p_list_head {
152 None
153 } else {
154 // Allowed because a removed node is not seen by another core
155 let p_node = list_head.next;
156 Self::remove_node(p_node);
157
158 Some(p_node)
159 }
160 })
161 }
162
163 /// Remove `list_tail` and return a pointer to the `node`.
164 pub unsafe fn remove_tail(mut p_list_tail: *mut LinkedListNode) -> Option<*mut LinkedListNode> {
165 interrupt::free(|_| {
166 let list_tail = ptr::read_volatile(p_list_tail);
167
168 if list_tail.prev == p_list_tail {
169 None
170 } else {
171 // Allowed because a removed node is not seen by another core
172 let p_node = list_tail.prev;
173 Self::remove_node(p_node);
174
175 Some(p_node)
176 }
177 })
178 }
179
180 pub unsafe fn insert_node_after(mut node: *mut LinkedListNode, mut ref_node: *mut LinkedListNode) {
181 interrupt::free(|_| {
182 (*node).next = (*ref_node).next;
183 (*node).prev = ref_node;
184 (*ref_node).next = node;
185 (*(*node).next).prev = node;
186 });
187
188 todo!("this function has not been converted to volatile semantics");
189 }
190
191 pub unsafe fn insert_node_before(mut node: *mut LinkedListNode, mut ref_node: *mut LinkedListNode) {
192 interrupt::free(|_| {
193 (*node).next = ref_node;
194 (*node).prev = (*ref_node).prev;
195 (*ref_node).prev = node;
196 (*(*node).prev).next = node;
197 });
198
199 todo!("this function has not been converted to volatile semantics");
200 }
201
202 pub unsafe fn get_size(mut list_head: *mut LinkedListNode) -> usize {
203 interrupt::free(|_| {
204 let mut size = 0;
205 let mut temp: *mut LinkedListNode = core::ptr::null_mut::<LinkedListNode>();
206
207 temp = (*list_head).next;
208 while temp != list_head {
209 size += 1;
210 temp = (*temp).next
211 }
212
213 size
214 });
215
216 todo!("this function has not been converted to volatile semantics");
217 }
218
219 pub unsafe fn get_next_node(mut p_ref_node: *mut LinkedListNode) -> *mut LinkedListNode {
220 interrupt::free(|_| {
221 let ref_node = ptr::read_volatile(p_ref_node);
222
223 // Allowed because a removed node is not seen by another core
224 ref_node.next
225 })
226 }
227
228 pub unsafe fn get_prev_node(mut p_ref_node: *mut LinkedListNode) -> *mut LinkedListNode {
229 interrupt::free(|_| {
230 let ref_node = ptr::read_volatile(p_ref_node);
231
232 // Allowed because a removed node is not seen by another core
233 ref_node.prev
234 })
235 }
236}
237
238#[allow(dead_code)]
239unsafe fn debug_linked_list(mut p_node: *mut LinkedListNode) {
240 info!("iterating list from node: {:x}", p_node);
241 let mut p_current_node = p_node;
242 let mut i = 0;
243 loop {
244 let current_node = ptr::read_volatile(p_current_node);
245 info!(
246 "node (prev, current, next): {:x}, {:x}, {:x}",
247 current_node.prev, p_current_node, current_node.next
248 );
249
250 i += 1;
251 if i > 10 || current_node.next == p_node {
252 break;
253 }
254
255 p_current_node = current_node.next;
256 }
257}
diff --git a/embassy-stm32-wpan/src/channels.rs b/embassy-stm32-wpan/src/wb55/channels.rs
index 9a2be1cfa..58f857136 100644
--- a/embassy-stm32-wpan/src/channels.rs
+++ b/embassy-stm32-wpan/src/wb55/channels.rs
@@ -48,9 +48,20 @@
48//! |<----HW_IPCC_TRACES_CHANNEL----------------------| 48//! |<----HW_IPCC_TRACES_CHANNEL----------------------|
49//! | | 49//! | |
50//! 50//!
51//!
52
53#[repr(u8)]
54pub enum IpccChannel {
55 Channel1 = 1,
56 Channel2 = 2,
57 Channel3 = 3,
58 Channel4 = 4,
59 Channel5 = 5,
60 Channel6 = 6,
61}
51 62
52pub mod cpu1 { 63pub mod cpu1 {
53 use embassy_stm32::ipcc::IpccChannel; 64 use super::IpccChannel;
54 65
55 pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1; 66 pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1;
56 pub const IPCC_SYSTEM_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel2; 67 pub const IPCC_SYSTEM_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel2;
@@ -70,7 +81,7 @@ pub mod cpu1 {
70} 81}
71 82
72pub mod cpu2 { 83pub mod cpu2 {
73 use embassy_stm32::ipcc::IpccChannel; 84 use super::IpccChannel;
74 85
75 pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1; 86 pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1;
76 pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2; 87 pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2;
diff --git a/embassy-stm32-wpan/src/cmd.rs b/embassy-stm32-wpan/src/wb55/cmd.rs
index 928357384..34f02d6e7 100644
--- a/embassy-stm32-wpan/src/cmd.rs
+++ b/embassy-stm32-wpan/src/wb55/cmd.rs
@@ -1,7 +1,8 @@
1use core::ptr; 1use core::ptr;
2use core::sync::atomic::{Ordering, compiler_fence};
2 3
3use crate::consts::TlPacketType; 4use crate::consts::TlPacketType;
4use crate::PacketHeader; 5use crate::wb55::PacketHeader;
5 6
6#[derive(Copy, Clone)] 7#[derive(Copy, Clone)]
7#[repr(C, packed)] 8#[repr(C, packed)]
@@ -45,11 +46,11 @@ pub struct CmdPacket {
45 46
46impl CmdPacket { 47impl CmdPacket {
47 pub unsafe fn write_into(cmd_buf: *mut CmdPacket, packet_type: TlPacketType, cmd_code: u16, payload: &[u8]) { 48 pub unsafe fn write_into(cmd_buf: *mut CmdPacket, packet_type: TlPacketType, cmd_code: u16, payload: &[u8]) {
48 let p_cmd_serial = &mut (*cmd_buf).cmdserial as *mut _ as *mut CmdSerialStub; 49 let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::<PacketHeader>());
49 let p_payload = &mut (*cmd_buf).cmdserial.cmd.payload as *mut _; 50 let p_payload = p_cmd_serial.add(size_of::<CmdSerialStub>());
50 51
51 ptr::write_volatile( 52 ptr::write_unaligned(
52 p_cmd_serial, 53 p_cmd_serial as *mut _,
53 CmdSerialStub { 54 CmdSerialStub {
54 ty: packet_type as u8, 55 ty: packet_type as u8,
55 cmd_code, 56 cmd_code,
@@ -58,6 +59,8 @@ impl CmdPacket {
58 ); 59 );
59 60
60 ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len()); 61 ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len());
62
63 compiler_fence(Ordering::Release);
61 } 64 }
62} 65}
63 66
@@ -87,11 +90,11 @@ pub struct AclDataPacket {
87 90
88impl AclDataPacket { 91impl AclDataPacket {
89 pub unsafe fn write_into(cmd_buf: *mut AclDataPacket, packet_type: TlPacketType, handle: u16, payload: &[u8]) { 92 pub unsafe fn write_into(cmd_buf: *mut AclDataPacket, packet_type: TlPacketType, handle: u16, payload: &[u8]) {
90 let p_cmd_serial = &mut (*cmd_buf).acl_data_serial as *mut _ as *mut AclDataSerialStub; 93 let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::<PacketHeader>());
91 let p_payload = &mut (*cmd_buf).acl_data_serial.acl_data as *mut _; 94 let p_payload = p_cmd_serial.add(size_of::<AclDataSerialStub>());
92 95
93 ptr::write_volatile( 96 ptr::write_unaligned(
94 p_cmd_serial, 97 p_cmd_serial as *mut _,
95 AclDataSerialStub { 98 AclDataSerialStub {
96 ty: packet_type as u8, 99 ty: packet_type as u8,
97 handle: handle, 100 handle: handle,
@@ -100,5 +103,7 @@ impl AclDataPacket {
100 ); 103 );
101 104
102 ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len()); 105 ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len());
106
107 compiler_fence(Ordering::Release);
103 } 108 }
104} 109}
diff --git a/embassy-stm32-wpan/src/consts.rs b/embassy-stm32-wpan/src/wb55/consts.rs
index e2ae6ca86..659e74e69 100644
--- a/embassy-stm32-wpan/src/consts.rs
+++ b/embassy-stm32-wpan/src/wb55/consts.rs
@@ -1,5 +1,5 @@
1use crate::evt::CsEvt; 1use crate::evt::CsEvt;
2use crate::PacketHeader; 2use crate::wb55::PacketHeader;
3 3
4#[derive(Debug)] 4#[derive(Debug)]
5#[repr(C)] 5#[repr(C)]
diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/wb55/evt.rs
index c6528413d..f32821269 100644
--- a/embassy-stm32-wpan/src/evt.rs
+++ b/embassy-stm32-wpan/src/wb55/evt.rs
@@ -67,6 +67,7 @@ pub struct EvtSerial {
67pub struct EvtStub { 67pub struct EvtStub {
68 pub kind: u8, 68 pub kind: u8,
69 pub evt_code: u8, 69 pub evt_code: u8,
70 pub payload_len: u8,
70} 71}
71 72
72/// This format shall be used for all events (asynchronous and command response) reported 73/// This format shall be used for all events (asynchronous and command response) reported
diff --git a/embassy-stm32-wpan/src/fmt.rs b/embassy-stm32-wpan/src/wb55/fmt.rs
index 8ca61bc39..8ca61bc39 100644
--- a/embassy-stm32-wpan/src/fmt.rs
+++ b/embassy-stm32-wpan/src/wb55/fmt.rs
diff --git a/embassy-stm32-wpan/src/lhci.rs b/embassy-stm32-wpan/src/wb55/lhci.rs
index 89f204f99..59c8bfb5d 100644
--- a/embassy-stm32-wpan/src/lhci.rs
+++ b/embassy-stm32-wpan/src/wb55/lhci.rs
@@ -1,9 +1,9 @@
1use core::ptr; 1use core::ptr;
2 2
3use crate::cmd::CmdPacket; 3use crate::cmd::CmdPacket;
4use crate::consts::{TlPacketType, TL_EVT_HEADER_SIZE}; 4use crate::consts::{TL_EVT_HEADER_SIZE, TlPacketType};
5use crate::evt::{CcEvt, EvtPacket, EvtSerial}; 5use crate::evt::{CcEvt, EvtPacket, EvtSerial};
6use crate::tables::{DeviceInfoTable, RssInfoTable, SafeBootInfoTable, WirelessFwInfoTable, TL_DEVICE_INFO_TABLE}; 6use crate::tables::{DeviceInfoTable, RssInfoTable, SafeBootInfoTable, TL_DEVICE_INFO_TABLE, WirelessFwInfoTable};
7 7
8const TL_BLEEVT_CC_OPCODE: u8 = 0x0e; 8const TL_BLEEVT_CC_OPCODE: u8 = 0x0e;
9const LHCI_OPCODE_C1_DEVICE_INF: u16 = 0xfd62; 9const LHCI_OPCODE_C1_DEVICE_INF: u16 = 0xfd62;
diff --git a/embassy-stm32-wpan/src/mac/commands.rs b/embassy-stm32-wpan/src/wb55/mac/commands.rs
index 82b9d2772..d96f0094a 100644
--- a/embassy-stm32-wpan/src/mac/commands.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/commands.rs
@@ -2,6 +2,8 @@
2 2
3use core::{mem, slice}; 3use core::{mem, slice};
4 4
5use smoltcp::wire::ieee802154::Frame;
6
5use super::opcodes::OpcodeM4ToM0; 7use super::opcodes::OpcodeM4ToM0;
6use super::typedefs::{ 8use super::typedefs::{
7 AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, MacStatus, 9 AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, MacStatus,
@@ -379,6 +381,35 @@ impl DataRequest {
379 } 381 }
380} 382}
381 383
384impl<'a, T: AsRef<[u8]>> TryFrom<Frame<&'a T>> for DataRequest {
385 type Error = ();
386
387 fn try_from(frame: Frame<&'a T>) -> Result<Self, Self::Error> {
388 // TODO: map the rest of these
389
390 let mut request = DataRequest {
391 src_addr_mode: frame.src_addressing_mode().try_into()?,
392 dst_addr_mode: frame.dst_addressing_mode().try_into()?,
393 dst_pan_id: frame.dst_pan_id().ok_or(())?.into(),
394 dst_address: frame.dst_addr().ok_or(())?.into(),
395 msdu_handle: frame.sequence_number().ok_or(())?,
396 key_source: frame.key_source().unwrap_or_default().try_into().unwrap_or_default(),
397 ack_tx: frame.ack_request() as u8,
398 gts_tx: false,
399 security_level: if frame.security_enabled() {
400 SecurityLevel::Secured
401 } else {
402 SecurityLevel::Unsecure
403 },
404 ..Default::default()
405 };
406
407 request.set_buffer(frame.payload().ok_or(())?);
408
409 Ok(request)
410 }
411}
412
382impl Default for DataRequest { 413impl Default for DataRequest {
383 fn default() -> Self { 414 fn default() -> Self {
384 Self { 415 Self {
diff --git a/embassy-stm32-wpan/src/mac/consts.rs b/embassy-stm32-wpan/src/wb55/mac/consts.rs
index 56903d980..56903d980 100644
--- a/embassy-stm32-wpan/src/mac/consts.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/consts.rs
diff --git a/embassy-stm32-wpan/src/wb55/mac/control.rs b/embassy-stm32-wpan/src/wb55/mac/control.rs
new file mode 100644
index 000000000..14c6fdd2b
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/mac/control.rs
@@ -0,0 +1,204 @@
1use core::cell::RefCell;
2use core::future::Future;
3use core::sync::atomic::{Ordering, compiler_fence};
4use core::task;
5use core::task::Poll;
6
7use embassy_net_driver::LinkState;
8use embassy_sync::blocking_mutex;
9use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
10use embassy_sync::mutex::Mutex;
11use embassy_sync::signal::Signal;
12use futures_util::FutureExt;
13
14use crate::mac::commands::*;
15use crate::mac::driver::NetworkState;
16use crate::mac::event::MacEvent;
17use crate::mac::runner::ZeroCopyPubSub;
18use crate::mac::typedefs::*;
19use crate::sub::mac::MacTx;
20
21pub struct Control<'a> {
22 rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,
23 mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx<'a>>,
24 #[allow(unused)]
25 network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,
26}
27
28impl<'a> Control<'a> {
29 pub(crate) fn new(
30 rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,
31 mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx<'a>>,
32 network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,
33 ) -> Self {
34 Self {
35 rx_event_channel,
36 mac_tx,
37 network_state,
38 }
39 }
40
41 pub async fn init_link(&mut self, pan_id: [u8; 2]) {
42 debug!("resetting");
43
44 debug!(
45 "{:#x}",
46 self.send_command_and_get_response(&ResetRequest {
47 set_default_pib: true,
48 ..Default::default()
49 })
50 .await
51 .unwrap()
52 .await
53 );
54
55 let (short_address, mac_address) = critical_section::with(|cs| {
56 let mut network_state = self.network_state.borrow(cs).borrow_mut();
57
58 network_state.pan_id = pan_id;
59
60 (network_state.short_addr, network_state.mac_addr)
61 });
62
63 debug!("setting extended address");
64 debug!(
65 "{:#x}",
66 self.send_command_and_get_response(&SetRequest {
67 pib_attribute_ptr: &u64::from_be_bytes(mac_address) as *const _ as *const u8,
68 pib_attribute: PibId::ExtendedAddress,
69 })
70 .await
71 .unwrap()
72 .await
73 );
74
75 debug!("setting short address");
76 debug!(
77 "{:#x}",
78 self.send_command_and_get_response(&SetRequest {
79 pib_attribute_ptr: &u16::from_be_bytes(short_address) as *const _ as *const u8,
80 pib_attribute: PibId::ShortAddress,
81 })
82 .await
83 .unwrap()
84 .await
85 );
86
87 debug!("setting association permit");
88 let association_permit: bool = true;
89 debug!(
90 "{:#x}",
91 self.send_command_and_get_response(&SetRequest {
92 pib_attribute_ptr: &association_permit as *const _ as *const u8,
93 pib_attribute: PibId::AssociationPermit,
94 })
95 .await
96 .unwrap()
97 .await
98 );
99
100 debug!("setting TX power");
101 let transmit_power: i8 = 2;
102 debug!(
103 "{:#x}",
104 self.send_command_and_get_response(&SetRequest {
105 pib_attribute_ptr: &transmit_power as *const _ as *const u8,
106 pib_attribute: PibId::TransmitPower,
107 })
108 .await
109 .unwrap()
110 .await
111 );
112
113 debug!("starting FFD device");
114 debug!(
115 "{:#x}",
116 self.send_command_and_get_response(&StartRequest {
117 pan_id: PanId(pan_id),
118 channel_number: MacChannel::Channel16,
119 beacon_order: 0x0F,
120 superframe_order: 0x0F,
121 pan_coordinator: true,
122 battery_life_extension: false,
123 ..Default::default()
124 })
125 .await
126 .unwrap()
127 .await
128 );
129
130 debug!("setting RX on when idle");
131 let rx_on_while_idle: bool = true;
132 debug!(
133 "{:#x}",
134 self.send_command_and_get_response(&SetRequest {
135 pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8,
136 pib_attribute: PibId::RxOnWhenIdle,
137 })
138 .await
139 .unwrap()
140 .await
141 );
142
143 critical_section::with(|cs| {
144 let mut network_state = self.network_state.borrow(cs).borrow_mut();
145
146 network_state.link_state = LinkState::Up;
147 network_state.link_waker.wake();
148 });
149 }
150
151 pub async fn send_command<T>(&self, cmd: &T) -> Result<(), MacError>
152 where
153 T: MacCommand,
154 {
155 self.mac_tx.lock().await.send_command(cmd).await
156 }
157
158 pub async fn send_command_and_get_response<T>(&self, cmd: &T) -> Result<EventToken<'a>, MacError>
159 where
160 T: MacCommand,
161 {
162 let token = EventToken::new(self.rx_event_channel);
163
164 compiler_fence(Ordering::Release);
165
166 self.mac_tx.lock().await.send_command(cmd).await?;
167
168 Ok(token)
169 }
170}
171
172pub struct EventToken<'a> {
173 rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,
174}
175
176impl<'a> EventToken<'a> {
177 pub(crate) fn new(rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>) -> Self {
178 // Enable event receiving
179 rx_event_channel.lock(|s| {
180 *s.borrow_mut() = Some(Signal::new());
181 });
182
183 Self { rx_event_channel }
184 }
185}
186
187impl<'a> Future for EventToken<'a> {
188 type Output = MacEvent<'a>;
189
190 fn poll(self: core::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
191 self.rx_event_channel
192 .lock(|s| s.borrow_mut().as_mut().unwrap().wait().poll_unpin(cx))
193 }
194}
195
196impl<'a> Drop for EventToken<'a> {
197 fn drop(&mut self) {
198 // Disable event receiving
199 // This will also drop the contained event, if it exists, and will free up receiving the next event
200 self.rx_event_channel.lock(|s| {
201 *s.borrow_mut() = None;
202 });
203 }
204}
diff --git a/embassy-stm32-wpan/src/wb55/mac/driver.rs b/embassy-stm32-wpan/src/wb55/mac/driver.rs
new file mode 100644
index 000000000..41171ce3d
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/mac/driver.rs
@@ -0,0 +1,213 @@
1#![deny(unused_must_use)]
2
3use core::cell::RefCell;
4use core::task::Context;
5
6use embassy_net_driver::{Capabilities, HardwareAddress, LinkState};
7use embassy_sync::blocking_mutex;
8use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
9use embassy_sync::channel::Channel;
10use embassy_sync::mutex::Mutex;
11use embassy_sync::waitqueue::AtomicWaker;
12
13use crate::mac::event::MacEvent;
14use crate::mac::indications::{write_frame_from_beacon_indication, write_frame_from_data_indication};
15use crate::mac::runner::{BUF_SIZE, ZeroCopyPubSub};
16use crate::mac::{Control, MTU, Runner};
17use crate::sub::mac::{Mac, MacRx, MacTx};
18
19pub struct NetworkState {
20 pub mac_addr: [u8; 8],
21 pub short_addr: [u8; 2],
22 pub pan_id: [u8; 2],
23 pub link_state: LinkState,
24 pub link_waker: AtomicWaker,
25}
26
27impl NetworkState {
28 pub const fn new() -> Self {
29 Self {
30 mac_addr: [0u8; 8],
31 short_addr: [0u8; 2],
32 pan_id: [0u8; 2],
33 link_state: LinkState::Down,
34 link_waker: AtomicWaker::new(),
35 }
36 }
37}
38
39pub struct DriverState<'d> {
40 pub mac_tx: Mutex<CriticalSectionRawMutex, MacTx<'d>>,
41 pub mac_rx: MacRx<'d>,
42 pub rx_event_channel: ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'d>>,
43 pub rx_data_channel: Channel<CriticalSectionRawMutex, MacEvent<'d>, 1>,
44 pub tx_data_channel: Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), BUF_SIZE>,
45 pub tx_buf_channel: Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], BUF_SIZE>,
46 pub tx_buf_queue: [[u8; MTU]; BUF_SIZE],
47 pub network_state: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,
48}
49
50impl<'d> DriverState<'d> {
51 pub const fn new(mac: Mac<'d>) -> Self {
52 let (mac_rx, mac_tx) = mac.split();
53 let mac_tx = Mutex::new(mac_tx);
54
55 Self {
56 mac_tx,
57 mac_rx,
58 rx_event_channel: ZeroCopyPubSub::new(RefCell::new(None)),
59 rx_data_channel: Channel::new(),
60 tx_data_channel: Channel::new(),
61 tx_buf_channel: Channel::new(),
62 tx_buf_queue: [[0u8; MTU]; BUF_SIZE],
63 network_state: blocking_mutex::Mutex::new(RefCell::new(NetworkState::new())),
64 }
65 }
66}
67
68pub struct Driver<'d> {
69 tx_data_channel: &'d Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), BUF_SIZE>,
70 tx_buf_channel: &'d Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], BUF_SIZE>,
71 rx_data_channel: &'d Channel<CriticalSectionRawMutex, MacEvent<'d>, 1>,
72 network_state: &'d blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,
73}
74
75impl<'d> Driver<'d> {
76 pub fn new(
77 driver_state: &'d mut DriverState<'d>,
78 short_address: [u8; 2],
79 mac_address: [u8; 8],
80 ) -> (Self, Runner<'d>, Control<'d>) {
81 (
82 Self {
83 tx_data_channel: &driver_state.tx_data_channel,
84 tx_buf_channel: &driver_state.tx_buf_channel,
85 rx_data_channel: &driver_state.rx_data_channel,
86 network_state: &driver_state.network_state,
87 },
88 Runner::new(
89 &driver_state.rx_event_channel,
90 &driver_state.rx_data_channel,
91 &mut driver_state.mac_rx,
92 &driver_state.tx_data_channel,
93 &driver_state.tx_buf_channel,
94 &driver_state.mac_tx,
95 &mut driver_state.tx_buf_queue,
96 &driver_state.network_state,
97 short_address,
98 mac_address,
99 ),
100 Control::new(
101 &driver_state.rx_event_channel,
102 &driver_state.mac_tx,
103 &driver_state.network_state,
104 ),
105 )
106 }
107}
108
109impl<'d> embassy_net_driver::Driver for Driver<'d> {
110 // type RxToken<'a> = RxToken<'a, 'd> where Self: 'a;
111 // type TxToken<'a> = TxToken<'a, 'd> where Self: 'a;
112 type RxToken<'a>
113 = RxToken<'d>
114 where
115 Self: 'a;
116 type TxToken<'a>
117 = TxToken<'d>
118 where
119 Self: 'a;
120
121 fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
122 if self.rx_data_channel.poll_ready_to_receive(cx).is_ready()
123 && self.tx_buf_channel.poll_ready_to_receive(cx).is_ready()
124 {
125 Some((
126 RxToken {
127 rx: self.rx_data_channel,
128 },
129 TxToken {
130 tx: self.tx_data_channel,
131 tx_buf: self.tx_buf_channel,
132 },
133 ))
134 } else {
135 None
136 }
137 }
138
139 fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
140 if self.tx_buf_channel.poll_ready_to_receive(cx).is_ready() {
141 Some(TxToken {
142 tx: self.tx_data_channel,
143 tx_buf: self.tx_buf_channel,
144 })
145 } else {
146 None
147 }
148 }
149
150 fn capabilities(&self) -> Capabilities {
151 let mut caps = Capabilities::default();
152 caps.max_transmission_unit = MTU;
153 // caps.max_burst_size = Some(self.tx.len());
154 caps
155 }
156
157 fn link_state(&mut self, cx: &mut Context) -> LinkState {
158 critical_section::with(|cs| {
159 let network_state = self.network_state.borrow(cs).borrow_mut();
160
161 // Unconditionally register the waker to avoid a race
162 network_state.link_waker.register(cx.waker());
163 network_state.link_state
164 })
165 }
166
167 fn hardware_address(&self) -> HardwareAddress {
168 HardwareAddress::Ieee802154(critical_section::with(|cs| {
169 self.network_state.borrow(cs).borrow().mac_addr
170 }))
171 }
172}
173
174pub struct RxToken<'d> {
175 rx: &'d Channel<CriticalSectionRawMutex, MacEvent<'d>, 1>,
176}
177
178impl<'d> embassy_net_driver::RxToken for RxToken<'d> {
179 fn consume<R, F>(self, f: F) -> R
180 where
181 F: FnOnce(&mut [u8]) -> R,
182 {
183 let mut buffer = [0u8; MTU];
184 match self.rx.try_receive().unwrap() {
185 MacEvent::McpsDataInd(data_event) => write_frame_from_data_indication(data_event, &mut buffer),
186 MacEvent::MlmeBeaconNotifyInd(data_event) => write_frame_from_beacon_indication(data_event, &mut buffer),
187 _ => {}
188 };
189
190 f(&mut buffer[..])
191 }
192}
193
194pub struct TxToken<'d> {
195 tx: &'d Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), BUF_SIZE>,
196 tx_buf: &'d Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], BUF_SIZE>,
197}
198
199impl<'d> embassy_net_driver::TxToken for TxToken<'d> {
200 fn consume<R, F>(self, len: usize, f: F) -> R
201 where
202 F: FnOnce(&mut [u8]) -> R,
203 {
204 // Only valid tx buffers should be put into the queue
205 let buf = self.tx_buf.try_receive().unwrap();
206 let r = f(&mut buf[..len]);
207
208 // The tx channel should always be of equal capacity to the tx_buf channel
209 self.tx.try_send((buf, len)).unwrap();
210
211 r
212 }
213}
diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/wb55/mac/event.rs
index 9ca4f5a2a..39856e185 100644
--- a/embassy-stm32-wpan/src/mac/event.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/event.rs
@@ -10,7 +10,7 @@ use super::responses::{
10}; 10};
11use crate::evt::{EvtBox, MemoryManager}; 11use crate::evt::{EvtBox, MemoryManager};
12use crate::mac::opcodes::OpcodeM0ToM4; 12use crate::mac::opcodes::OpcodeM0ToM4;
13use crate::sub::mac::{self, Mac}; 13use crate::sub::mac::{self, MacRx};
14 14
15pub(crate) trait ParseableMacEvent: Sized { 15pub(crate) trait ParseableMacEvent: Sized {
16 fn from_buffer<'a>(buf: &'a [u8]) -> Result<&'a Self, ()> { 16 fn from_buffer<'a>(buf: &'a [u8]) -> Result<&'a Self, ()> {
@@ -53,7 +53,7 @@ pub enum MacEvent<'a> {
53} 53}
54 54
55impl<'a> MacEvent<'a> { 55impl<'a> MacEvent<'a> {
56 pub(crate) fn new(event_box: EvtBox<Mac>) -> Result<Self, ()> { 56 pub(crate) fn new(event_box: EvtBox<MacRx>) -> Result<Self, ()> {
57 let payload = event_box.payload(); 57 let payload = event_box.payload();
58 let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap()); 58 let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap());
59 59
@@ -148,6 +148,6 @@ unsafe impl<'a> Send for MacEvent<'a> {}
148 148
149impl<'a> Drop for MacEvent<'a> { 149impl<'a> Drop for MacEvent<'a> {
150 fn drop(&mut self) { 150 fn drop(&mut self) {
151 unsafe { mac::Mac::drop_event_packet(ptr::null_mut()) }; 151 unsafe { mac::MacRx::drop_event_packet(ptr::null_mut()) };
152 } 152 }
153} 153}
diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/wb55/mac/indications.rs
index c0b86d745..5673514c9 100644
--- a/embassy-stm32-wpan/src/mac/indications.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/indications.rs
@@ -1,11 +1,15 @@
1use core::slice; 1use core::slice;
2 2
3use smoltcp::wire::Ieee802154FrameType;
4use smoltcp::wire::ieee802154::Frame;
5
3use super::consts::MAX_PENDING_ADDRESS; 6use super::consts::MAX_PENDING_ADDRESS;
4use super::event::ParseableMacEvent; 7use super::event::ParseableMacEvent;
5use super::typedefs::{ 8use super::typedefs::{
6 AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor, 9 AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor,
7 PanId, SecurityLevel, 10 PanId, SecurityLevel,
8}; 11};
12use crate::mac::typedefs::MacAddressAndMode;
9 13
10/// MLME ASSOCIATE Indication which will be used by the MAC 14/// MLME ASSOCIATE Indication which will be used by the MAC
11/// to indicate the reception of an association request command 15/// to indicate the reception of an association request command
@@ -74,6 +78,22 @@ pub struct BeaconNotifyIndication {
74 78
75impl ParseableMacEvent for BeaconNotifyIndication {} 79impl ParseableMacEvent for BeaconNotifyIndication {}
76 80
81impl BeaconNotifyIndication {
82 pub fn payload<'a>(&'a self) -> &'a mut [u8] {
83 unsafe { slice::from_raw_parts_mut(self.sdu_ptr as *mut _, self.sdu_length as usize) }
84 }
85}
86
87pub fn write_frame_from_beacon_indication<'a, T: AsRef<[u8]> + AsMut<[u8]>>(
88 data: &'a BeaconNotifyIndication,
89 buffer: &'a mut T,
90) {
91 let mut frame = Frame::new_unchecked(buffer);
92
93 frame.set_frame_type(Ieee802154FrameType::Beacon);
94 frame.set_sequence_number(data.bsn);
95}
96
77/// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status 97/// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status
78#[repr(C)] 98#[repr(C)]
79#[derive(Debug)] 99#[derive(Debug)]
@@ -250,6 +270,21 @@ impl DataIndication {
250 } 270 }
251} 271}
252 272
273pub fn write_frame_from_data_indication<'a, T: AsRef<[u8]> + AsMut<[u8]>>(data: &'a DataIndication, buffer: &'a mut T) {
274 let mut frame = Frame::new_unchecked(buffer);
275
276 frame.set_frame_type(Ieee802154FrameType::Data);
277 frame.set_src_addr(MacAddressAndMode(data.src_address, data.src_addr_mode).into());
278 frame.set_dst_addr(MacAddressAndMode(data.dst_address, data.dst_addr_mode).into());
279 frame.set_dst_pan_id(data.dst_pan_id.into());
280 frame.set_src_pan_id(data.src_pan_id.into());
281 frame.set_sequence_number(data.dsn);
282 frame.set_security_enabled(data.security_level == SecurityLevel::Secured);
283
284 // No way around the copy with the current API
285 frame.payload_mut().unwrap().copy_from_slice(data.payload());
286}
287
253/// MLME POLL Indication which will be used for indicating the Data Request 288/// MLME POLL Indication which will be used for indicating the Data Request
254/// reception to upper layer as defined in Zigbee r22 - D.8.2 289/// reception to upper layer as defined in Zigbee r22 - D.8.2
255#[repr(C)] 290#[repr(C)]
diff --git a/embassy-stm32-wpan/src/mac/macros.rs b/embassy-stm32-wpan/src/wb55/mac/macros.rs
index 1a988a779..1a988a779 100644
--- a/embassy-stm32-wpan/src/mac/macros.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/macros.rs
diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/wb55/mac/mod.rs
index c847a5cca..ac50a6b29 100644
--- a/embassy-stm32-wpan/src/mac/mod.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/mod.rs
@@ -11,11 +11,7 @@ pub mod runner;
11pub mod typedefs; 11pub mod typedefs;
12 12
13pub use crate::mac::control::Control; 13pub use crate::mac::control::Control;
14use crate::mac::driver::Driver; 14pub use crate::mac::driver::{Driver, DriverState};
15pub use crate::mac::runner::Runner; 15pub use crate::mac::runner::Runner;
16 16
17const MTU: usize = 127; 17const MTU: usize = 127;
18
19pub async fn new<'a>(runner: &'a Runner<'a>) -> (Control<'a>, Driver<'a>) {
20 (Control::new(runner), Driver::new(runner))
21}
diff --git a/embassy-stm32-wpan/src/mac/opcodes.rs b/embassy-stm32-wpan/src/wb55/mac/opcodes.rs
index fd7011873..fd7011873 100644
--- a/embassy-stm32-wpan/src/mac/opcodes.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/opcodes.rs
diff --git a/embassy-stm32-wpan/src/mac/responses.rs b/embassy-stm32-wpan/src/wb55/mac/responses.rs
index 544fdaae8..544fdaae8 100644
--- a/embassy-stm32-wpan/src/mac/responses.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/responses.rs
diff --git a/embassy-stm32-wpan/src/wb55/mac/runner.rs b/embassy-stm32-wpan/src/wb55/mac/runner.rs
new file mode 100644
index 000000000..3b7d895df
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/mac/runner.rs
@@ -0,0 +1,151 @@
1use core::cell::RefCell;
2
3use embassy_futures::join;
4use embassy_sync::blocking_mutex;
5use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
6use embassy_sync::channel::Channel;
7use embassy_sync::mutex::Mutex;
8use embassy_sync::signal::Signal;
9use smoltcp::wire::Ieee802154FrameType;
10use smoltcp::wire::ieee802154::Frame;
11
12use crate::mac::MTU;
13use crate::mac::commands::*;
14use crate::mac::driver::NetworkState;
15use crate::mac::event::MacEvent;
16use crate::sub::mac::{MacRx, MacTx};
17
18pub type ZeroCopyPubSub<M, T> = blocking_mutex::Mutex<M, RefCell<Option<Signal<NoopRawMutex, T>>>>;
19
20pub const BUF_SIZE: usize = 3;
21
22pub struct Runner<'a> {
23 // rx event backpressure is already provided through the MacEvent drop mechanism
24 // therefore, we don't need to worry about overwriting events
25 rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,
26 rx_data_channel: &'a Channel<CriticalSectionRawMutex, MacEvent<'a>, 1>,
27 mac_rx: Mutex<NoopRawMutex, &'a mut MacRx<'a>>,
28
29 tx_data_channel: &'a Channel<CriticalSectionRawMutex, (&'a mut [u8; MTU], usize), BUF_SIZE>,
30 tx_buf_channel: &'a Channel<CriticalSectionRawMutex, &'a mut [u8; MTU], BUF_SIZE>,
31 mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx<'a>>,
32
33 #[allow(unused)]
34 network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,
35}
36
37impl<'a> Runner<'a> {
38 pub(crate) fn new(
39 rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,
40 rx_data_channel: &'a Channel<CriticalSectionRawMutex, MacEvent<'a>, 1>,
41 mac_rx: &'a mut MacRx<'a>,
42 tx_data_channel: &'a Channel<CriticalSectionRawMutex, (&'a mut [u8; MTU], usize), BUF_SIZE>,
43 tx_buf_channel: &'a Channel<CriticalSectionRawMutex, &'a mut [u8; MTU], BUF_SIZE>,
44 mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx<'a>>,
45 tx_buf_queue: &'a mut [[u8; MTU]; BUF_SIZE],
46 network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>,
47 short_address: [u8; 2],
48 mac_address: [u8; 8],
49 ) -> Self {
50 for buf in tx_buf_queue {
51 tx_buf_channel.try_send(buf).unwrap();
52 }
53
54 critical_section::with(|cs| {
55 let mut network_state = network_state.borrow(cs).borrow_mut();
56
57 network_state.mac_addr = mac_address;
58 network_state.short_addr = short_address;
59 });
60
61 Self {
62 rx_event_channel,
63 rx_data_channel,
64 mac_rx: Mutex::new(mac_rx),
65 tx_data_channel,
66 tx_buf_channel,
67 mac_tx,
68 network_state,
69 }
70 }
71
72 async fn send_request<T: MacCommand, U: TryInto<T>>(&self, frame: U) -> Result<(), ()>
73 where
74 (): From<<U as TryInto<T>>::Error>,
75 {
76 let request: T = frame.try_into()?;
77 self.mac_tx.lock().await.send_command(&request).await.map_err(|_| ())?;
78
79 Ok(())
80 }
81
82 pub async fn run(&'a self) -> ! {
83 join::join(
84 async {
85 loop {
86 if let Ok(mac_event) = self.mac_rx.try_lock().unwrap().read().await {
87 match mac_event {
88 MacEvent::MlmeAssociateCnf(_)
89 | MacEvent::MlmeDisassociateCnf(_)
90 | MacEvent::MlmeGetCnf(_)
91 | MacEvent::MlmeGtsCnf(_)
92 | MacEvent::MlmeResetCnf(_)
93 | MacEvent::MlmeRxEnableCnf(_)
94 | MacEvent::MlmeScanCnf(_)
95 | MacEvent::MlmeSetCnf(_)
96 | MacEvent::MlmeStartCnf(_)
97 | MacEvent::MlmePollCnf(_)
98 | MacEvent::MlmeDpsCnf(_)
99 | MacEvent::MlmeSoundingCnf(_)
100 | MacEvent::MlmeCalibrateCnf(_)
101 | MacEvent::McpsDataCnf(_)
102 | MacEvent::McpsPurgeCnf(_) => {
103 self.rx_event_channel.lock(|s| {
104 s.borrow().as_ref().map(|signal| signal.signal(mac_event));
105 });
106 }
107 MacEvent::McpsDataInd(_) => {
108 // Pattern should match driver
109 self.rx_data_channel.send(mac_event).await;
110 }
111 _ => {
112 debug!("unhandled mac event: {:#x}", mac_event);
113 }
114 }
115 }
116 }
117 },
118 async {
119 loop {
120 let (buf, _) = self.tx_data_channel.receive().await;
121
122 // Smoltcp has created this frame, so there's no need to reparse it.
123 let frame = Frame::new_unchecked(&buf);
124
125 let result: Result<(), ()> = match frame.frame_type() {
126 Ieee802154FrameType::Beacon => Err(()),
127 Ieee802154FrameType::Data => self.send_request::<DataRequest, _>(frame).await,
128 Ieee802154FrameType::Acknowledgement => Err(()),
129 Ieee802154FrameType::MacCommand => Err(()),
130 Ieee802154FrameType::Multipurpose => Err(()),
131 Ieee802154FrameType::FragmentOrFrak => Err(()),
132 Ieee802154FrameType::Extended => Err(()),
133 _ => Err(()),
134 };
135
136 if result.is_err() {
137 debug!("failed to parse mac frame");
138 } else {
139 trace!("data frame sent!");
140 }
141
142 // The tx channel should always be of equal capacity to the tx_buf channel
143 self.tx_buf_channel.try_send(buf).unwrap();
144 }
145 },
146 )
147 .await;
148
149 loop {}
150 }
151}
diff --git a/embassy-stm32-wpan/src/mac/typedefs.rs b/embassy-stm32-wpan/src/wb55/mac/typedefs.rs
index 0552b8ea1..175d4a37d 100644
--- a/embassy-stm32-wpan/src/mac/typedefs.rs
+++ b/embassy-stm32-wpan/src/wb55/mac/typedefs.rs
@@ -1,5 +1,7 @@
1use core::fmt::Debug; 1use core::fmt::Debug;
2 2
3use smoltcp::wire::ieee802154::{Address, AddressingMode, Pan};
4
3use crate::numeric_enum; 5use crate::numeric_enum;
4 6
5#[derive(Debug)] 7#[derive(Debug)]
@@ -109,12 +111,51 @@ numeric_enum! {
109} 111}
110} 112}
111 113
114impl TryFrom<AddressingMode> for AddressMode {
115 type Error = ();
116
117 fn try_from(value: AddressingMode) -> Result<Self, Self::Error> {
118 match value {
119 AddressingMode::Absent => Ok(Self::NoAddress),
120 AddressingMode::Extended => Ok(Self::Extended),
121 AddressingMode::Short => Ok(Self::Short),
122 AddressingMode::Unknown(_) => Err(()),
123 }
124 }
125}
126
112#[derive(Clone, Copy)] 127#[derive(Clone, Copy)]
113pub union MacAddress { 128pub union MacAddress {
114 pub short: [u8; 2], 129 pub short: [u8; 2],
115 pub extended: [u8; 8], 130 pub extended: [u8; 8],
116} 131}
117 132
133impl From<Address> for MacAddress {
134 fn from(value: Address) -> Self {
135 match value {
136 Address::Short(addr) => Self { short: addr },
137 Address::Extended(addr) => Self { extended: addr },
138 Address::Absent => Self { short: [0u8; 2] },
139 }
140 }
141}
142
143pub struct MacAddressAndMode(pub MacAddress, pub AddressMode);
144
145impl From<MacAddressAndMode> for Address {
146 fn from(mac_address_and_mode: MacAddressAndMode) -> Self {
147 let address = mac_address_and_mode.0;
148 let mode = mac_address_and_mode.1;
149
150 match mode {
151 AddressMode::Short => Address::Short(unsafe { address.short }),
152 AddressMode::Extended => Address::Extended(unsafe { address.extended }),
153 AddressMode::NoAddress => Address::Absent,
154 AddressMode::Reserved => Address::Absent,
155 }
156 }
157}
158
118impl Debug for MacAddress { 159impl Debug for MacAddress {
119 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 160 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
120 unsafe { 161 unsafe {
@@ -346,7 +387,7 @@ numeric_enum! {
346 387
347numeric_enum! { 388numeric_enum! {
348 #[repr(u8)] 389 #[repr(u8)]
349 #[derive(Default, Clone, Copy, Debug)] 390 #[derive(Default, Clone, Copy, Debug, PartialEq)]
350 #[cfg_attr(feature = "defmt", derive(defmt::Format))] 391 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
351 pub enum SecurityLevel { 392 pub enum SecurityLevel {
352 /// MAC Unsecured Mode Security 393 /// MAC Unsecured Mode Security
@@ -379,3 +420,15 @@ pub struct PanId(pub [u8; 2]);
379impl PanId { 420impl PanId {
380 pub const BROADCAST: Self = Self([0xFF, 0xFF]); 421 pub const BROADCAST: Self = Self([0xFF, 0xFF]);
381} 422}
423
424impl From<Pan> for PanId {
425 fn from(value: Pan) -> Self {
426 Self(value.0.to_be_bytes())
427 }
428}
429
430impl From<PanId> for Pan {
431 fn from(value: PanId) -> Self {
432 Self(u16::from_be_bytes(value.0))
433 }
434}
diff --git a/embassy-stm32-wpan/src/wb55/mod.rs b/embassy-stm32-wpan/src/wb55/mod.rs
new file mode 100644
index 000000000..95cfe09f1
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/mod.rs
@@ -0,0 +1,198 @@
1// This must go FIRST so that all the other modules see its macros.
2mod fmt;
3
4use core::mem::MaybeUninit;
5use core::sync::atomic::{Ordering, compiler_fence};
6
7use embassy_hal_internal::Peri;
8use embassy_stm32::interrupt;
9use embassy_stm32::ipcc::{Config, Ipcc, IpccRxChannel, ReceiveInterruptHandler, TransmitInterruptHandler};
10use embassy_stm32::peripherals::IPCC;
11use sub::mm::MemoryManager;
12use sub::sys::Sys;
13use tables::*;
14use unsafe_linked_list::LinkedListNode;
15
16pub mod channels;
17pub mod cmd;
18pub mod consts;
19pub mod evt;
20pub mod lhci;
21pub mod shci;
22pub mod sub;
23pub mod tables;
24pub mod unsafe_linked_list;
25
26#[cfg(feature = "wb55_mac")]
27pub mod mac;
28
29#[cfg(feature = "wb55_ble")]
30pub use crate::sub::ble::hci;
31
32type PacketHeader = LinkedListNode;
33
34/// Transport Layer for the Mailbox interface
35pub struct TlMbox<'d> {
36 pub sys_subsystem: Sys<'d>,
37 pub mm_subsystem: MemoryManager<'d>,
38 #[cfg(feature = "wb55_ble")]
39 pub ble_subsystem: sub::ble::Ble<'d>,
40 #[cfg(feature = "wb55_mac")]
41 pub mac_subsystem: sub::mac::Mac<'d>,
42 pub traces: IpccRxChannel<'d>,
43}
44
45impl<'d> TlMbox<'d> {
46 /// Initialise the Transport Layer, and creates and returns a wrapper around it.
47 ///
48 /// This method performs the initialisation laid out in AN5289 annex 14.1. However, it differs
49 /// from the implementation documented in Figure 64, to avoid needing to reference any C
50 /// function pointers.
51 ///
52 /// Annex 14.1 lays out the following methods that should be called:
53 /// 1. tl_mbox.c/TL_Init, which initialises the reference table that is shared between CPU1
54 /// and CPU2.
55 /// 2. shci_tl.c/shci_init(), which initialises the system transport layer, and in turn
56 /// calls tl_mbox.c/TL_SYS_Init, which initialises SYSTEM_EVT_QUEUE channel.
57 /// 3. tl_mbox.c/TL_MM_Init(), which initialises the channel used for sending memory
58 /// manager commands.
59 /// 4. tl_mbox.c/TL_Enable(), which enables the IPCC, and starts CPU2.
60 /// This implementation initialises all of the shared refernce tables and all IPCC channel that
61 /// would be initialised by this process. The developer should therefore treat this method as
62 /// completing all steps in Figure 64.
63 ///
64 /// Once this method has been called, no system commands may be sent until the CPU2 ready
65 /// signal is received, via [sys_subsystem.read]; this completes the procedure laid out in
66 /// Figure 65.
67 ///
68 /// If the `ble` feature is enabled, at this point, the user should call
69 /// [sys_subsystem.shci_c2_ble_init], before any commands are written to the
70 /// [TlMbox.ble_subsystem] ([sub::ble::Ble::new()] completes the process that would otherwise
71 /// be handled by `TL_BLE_Init`; see Figure 66). This completes the procedure laid out in
72 /// Figure 66.
73 // TODO: document what the user should do after calling init to use the mac_802_15_4 subsystem
74 pub async fn init(
75 ipcc: Peri<'d, IPCC>,
76 _irqs: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler>
77 + interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_TX, TransmitInterruptHandler>,
78 config: Config,
79 ) -> Self {
80 // this is an inlined version of TL_Init from the STM32WB firmware as requested by AN5289.
81 // HW_IPCC_Init is not called, and its purpose is (presumably?) covered by this
82 // implementation
83 unsafe {
84 TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable {
85 device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(),
86 ble_table: TL_BLE_TABLE.as_ptr(),
87 thread_table: TL_THREAD_TABLE.as_ptr(),
88 sys_table: TL_SYS_TABLE.as_ptr(),
89 mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(),
90 traces_table: TL_TRACES_TABLE.as_ptr(),
91 mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(),
92 zigbee_table: TL_ZIGBEE_TABLE.as_ptr(),
93 lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(),
94 ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(),
95 });
96
97 TL_SYS_TABLE
98 .as_mut_ptr()
99 .write_volatile(MaybeUninit::zeroed().assume_init());
100 TL_DEVICE_INFO_TABLE
101 .as_mut_ptr()
102 .write_volatile(MaybeUninit::zeroed().assume_init());
103 TL_BLE_TABLE
104 .as_mut_ptr()
105 .write_volatile(MaybeUninit::zeroed().assume_init());
106 TL_THREAD_TABLE
107 .as_mut_ptr()
108 .write_volatile(MaybeUninit::zeroed().assume_init());
109 TL_MEM_MANAGER_TABLE
110 .as_mut_ptr()
111 .write_volatile(MaybeUninit::zeroed().assume_init());
112
113 TL_TRACES_TABLE
114 .as_mut_ptr()
115 .write_volatile(MaybeUninit::zeroed().assume_init());
116 TL_MAC_802_15_4_TABLE
117 .as_mut_ptr()
118 .write_volatile(MaybeUninit::zeroed().assume_init());
119 TL_ZIGBEE_TABLE
120 .as_mut_ptr()
121 .write_volatile(MaybeUninit::zeroed().assume_init());
122 TL_LLD_TESTS_TABLE
123 .as_mut_ptr()
124 .write_volatile(MaybeUninit::zeroed().assume_init());
125 TL_BLE_LLD_TABLE
126 .as_mut_ptr()
127 .write_volatile(MaybeUninit::zeroed().assume_init());
128
129 EVT_POOL
130 .as_mut_ptr()
131 .write_volatile(MaybeUninit::zeroed().assume_init());
132 SYS_SPARE_EVT_BUF
133 .as_mut_ptr()
134 .write_volatile(MaybeUninit::zeroed().assume_init());
135 CS_BUFFER
136 .as_mut_ptr()
137 .write_volatile(MaybeUninit::zeroed().assume_init());
138
139 #[cfg(feature = "wb55_ble")]
140 {
141 BLE_SPARE_EVT_BUF
142 .as_mut_ptr()
143 .write_volatile(MaybeUninit::zeroed().assume_init());
144
145 BLE_CMD_BUFFER
146 .as_mut_ptr()
147 .write_volatile(MaybeUninit::zeroed().assume_init());
148 HCI_ACL_DATA_BUFFER
149 .as_mut_ptr()
150 .write_volatile(MaybeUninit::zeroed().assume_init());
151 }
152
153 #[cfg(feature = "wb55_mac")]
154 {
155 MAC_802_15_4_CMD_BUFFER
156 .as_mut_ptr()
157 .write_volatile(MaybeUninit::zeroed().assume_init());
158 MAC_802_15_4_NOTIF_RSP_EVT_BUFFER
159 .as_mut_ptr()
160 .write_volatile(MaybeUninit::zeroed().assume_init());
161 }
162 }
163
164 compiler_fence(Ordering::SeqCst);
165
166 // this is equivalent to `HW_IPCC_Enable`, which is called by `TL_Enable`
167 let [
168 (_hw_ipcc_ble_cmd_channel, _ipcc_ble_event_channel),
169 (ipcc_system_cmd_rsp_channel, ipcc_system_event_channel),
170 (_ipcc_mac_802_15_4_cmd_rsp_channel, _ipcc_mac_802_15_4_notification_ack_channel),
171 (ipcc_mm_release_buffer_channel, _ipcc_traces_channel),
172 (_ipcc_ble_lld_cmd_channel, _ipcc_ble_lld_rsp_channel),
173 (_ipcc_hci_acl_data_channel, _),
174 ] = Ipcc::new(ipcc, _irqs, config).split();
175
176 let mm = sub::mm::MemoryManager::new(ipcc_mm_release_buffer_channel);
177 let mut sys = sub::sys::Sys::new(ipcc_system_cmd_rsp_channel, ipcc_system_event_channel);
178
179 debug!("sys event: {}", sys.read().await.payload());
180
181 Self {
182 sys_subsystem: sys,
183 #[cfg(feature = "wb55_ble")]
184 ble_subsystem: sub::ble::Ble::new(
185 _hw_ipcc_ble_cmd_channel,
186 _ipcc_ble_event_channel,
187 _ipcc_hci_acl_data_channel,
188 ),
189 #[cfg(feature = "wb55_mac")]
190 mac_subsystem: sub::mac::Mac::new(
191 _ipcc_mac_802_15_4_cmd_rsp_channel,
192 _ipcc_mac_802_15_4_notification_ack_channel,
193 ),
194 mm_subsystem: mm,
195 traces: _ipcc_traces_channel,
196 }
197 }
198}
diff --git a/embassy-stm32-wpan/src/shci.rs b/embassy-stm32-wpan/src/wb55/shci.rs
index 30d689716..3faa79209 100644
--- a/embassy-stm32-wpan/src/shci.rs
+++ b/embassy-stm32-wpan/src/wb55/shci.rs
@@ -1,6 +1,10 @@
1use core::{mem, slice}; 1use core::sync::atomic::{Ordering, compiler_fence};
2use core::{mem, ptr, slice};
2 3
4use crate::cmd::CmdPacket;
3use crate::consts::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; 5use crate::consts::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE};
6use crate::evt::{CcEvt, EvtStub};
7use crate::wb55::PacketHeader;
4 8
5const SHCI_OGF: u16 = 0x3F; 9const SHCI_OGF: u16 = 0x3F;
6 10
@@ -21,6 +25,18 @@ pub enum SchiCommandStatus {
21 ShciFusCmdNotSupported = 0xFF, 25 ShciFusCmdNotSupported = 0xFF,
22} 26}
23 27
28impl SchiCommandStatus {
29 pub unsafe fn from_packet(cmd_buf: *const CmdPacket) -> Result<Self, ()> {
30 let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::<PacketHeader>());
31 let p_evt_payload = p_cmd_serial.add(size_of::<EvtStub>());
32
33 compiler_fence(Ordering::Acquire);
34 let cc_evt = ptr::read_unaligned(p_evt_payload as *const CcEvt);
35
36 cc_evt.payload[0].try_into()
37 }
38}
39
24impl TryFrom<u8> for SchiCommandStatus { 40impl TryFrom<u8> for SchiCommandStatus {
25 type Error = (); 41 type Error = ();
26 42
@@ -274,69 +290,64 @@ pub struct ShciBleInitCmdParam {
274 pub options: u8, 290 pub options: u8,
275 /// Reserved for future use - shall be set to 0 291 /// Reserved for future use - shall be set to 0
276 pub hw_version: u8, 292 pub hw_version: u8,
277 // /** 293 ///
278 // * Maximum number of connection-oriented channels in initiator mode. 294 /// Maximum number of connection-oriented channels in initiator mode.
279 // * Range: 0 .. 64 295 /// Range: 0 .. 64
280 // */ 296 pub max_coc_initiator_nbr: u8,
281 // pub max_coc_initiator_nbr: u8, 297
282 // 298 ///
283 // /** 299 /// Minimum transmit power in dBm supported by the Controller.
284 // * Minimum transmit power in dBm supported by the Controller. 300 /// Range: -127 .. 20
285 // * Range: -127 .. 20 301 pub min_tx_power: i8,
286 // */ 302
287 // pub min_tx_power: i8, 303 ///
288 // 304 /// Maximum transmit power in dBm supported by the Controller.
289 // /** 305 /// Range: -127 .. 20
290 // * Maximum transmit power in dBm supported by the Controller. 306 pub max_tx_power: i8,
291 // * Range: -127 .. 20 307
292 // */ 308 ///
293 // pub max_tx_power: i8, 309 /// RX model configuration
294 // 310 /// - bit 0: 1: agc_rssi model improved vs RF blockers 0: Legacy agc_rssi model
295 // /** 311 /// - other bits: reserved ( shall be set to 0)
296 // * RX model configuration 312 pub rx_model_config: u8,
297 // * - bit 0: 1: agc_rssi model improved vs RF blockers 0: Legacy agc_rssi model 313
298 // * - other bits: reserved ( shall be set to 0) 314 /// Maximum number of advertising sets.
299 // */ 315 /// Range: 1 .. 8 with limitation:
300 // pub rx_model_config: u8, 316 /// This parameter is linked to max_adv_data_len such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based
301 // 317 /// on Max Extended advertising configuration supported.
302 // /* Maximum number of advertising sets. 318 /// This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set
303 // * Range: 1 .. 8 with limitation: 319 pub max_adv_set_nbr: u8,
304 // * This parameter is linked to max_adv_data_len such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based 320
305 // * on Max Extended advertising configuration supported. 321 /// Maximum advertising data length (in bytes)
306 // * This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set 322 /// Range: 31 .. 1650 with limitation:
307 // */ 323 /// This parameter is linked to max_adv_set_nbr such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based
308 // pub max_adv_set_nbr: u8, 324 /// on Max Extended advertising configuration supported.
309 // 325 /// This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set
310 // /* Maximum advertising data length (in bytes) 326 pub max_adv_data_len: u16,
311 // * Range: 31 .. 1650 with limitation: 327
312 // * This parameter is linked to max_adv_set_nbr such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based 328 /// RF TX Path Compensation Value (16-bit signed integer). Units: 0.1 dB.
313 // * on Max Extended advertising configuration supported. 329 /// Range: -1280 .. 1280
314 // * This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set 330 pub tx_path_compens: i16,
315 // */ 331
316 // pub max_adv_data_len: u16, 332 //// RF RX Path Compensation Value (16-bit signed integer). Units: 0.1 dB.
317 // 333 /// Range: -1280 .. 1280
318 // /* RF TX Path Compensation Value (16-bit signed integer). Units: 0.1 dB. 334 pub rx_path_compens: i16,
319 // * Range: -1280 .. 1280 335
320 // */ 336 /// BLE core specification version (8-bit unsigned integer).
321 // pub tx_path_compens: i16, 337 /// values as: 11(5.2), 12(5.3)
322 // 338 pub ble_core_version: u8,
323 // /* RF RX Path Compensation Value (16-bit signed integer). Units: 0.1 dB. 339
324 // * Range: -1280 .. 1280 340 /// Options flags extension
325 // */ 341 /// - bit 0: 1: appearance Writable 0: appearance Read-Only
326 // pub rx_path_compens: i16, 342 /// - bit 1: 1: Enhanced ATT supported 0: Enhanced ATT not supported
327 // 343 /// - other bits: reserved ( shall be set to 0)
328 // /* BLE core specification version (8-bit unsigned integer). 344 pub options_extension: u8,
329 // * values as: 11(5.2), 12(5.3) 345
330 // */ 346 /// MaxAddEattBearers
331 // pub ble_core_version: u8, 347 /// Maximum number of bearers that can be created for Enhanced ATT
332 // 348 /// in addition to the number of links
333 // /** 349 /// - Range: 0 .. 4
334 // * Options flags extension 350 pub max_add_eatt_bearers: u8,
335 // * - bit 0: 1: appearance Writable 0: appearance Read-Only
336 // * - bit 1: 1: Enhanced ATT supported 0: Enhanced ATT not supported
337 // * - other bits: reserved ( shall be set to 0)
338 // */
339 // pub options_extension: u8,
340} 351}
341 352
342impl ShciBleInitCmdParam { 353impl ShciBleInitCmdParam {
@@ -351,7 +362,7 @@ impl Default for ShciBleInitCmdParam {
351 p_ble_buffer_address: 0, 362 p_ble_buffer_address: 0,
352 ble_buffer_size: 0, 363 ble_buffer_size: 0,
353 num_attr_record: 68, 364 num_attr_record: 68,
354 num_attr_serv: 8, 365 num_attr_serv: 4,
355 attr_value_arr_size: 1344, 366 attr_value_arr_size: 1344,
356 num_of_links: 2, 367 num_of_links: 2,
357 extended_packet_length_enable: 1, 368 extended_packet_length_enable: 1,
@@ -366,6 +377,17 @@ impl Default for ShciBleInitCmdParam {
366 viterbi_enable: 1, 377 viterbi_enable: 1,
367 options: 0, 378 options: 0,
368 hw_version: 0, 379 hw_version: 0,
380 max_coc_initiator_nbr: 32,
381 min_tx_power: -40,
382 max_tx_power: 6,
383 rx_model_config: 0,
384 max_adv_set_nbr: 2,
385 max_adv_data_len: 1650,
386 tx_path_compens: 0,
387 rx_path_compens: 0,
388 ble_core_version: 11,
389 options_extension: 0,
390 max_add_eatt_bearers: 4,
369 } 391 }
370 } 392 }
371} 393}
diff --git a/embassy-stm32-wpan/src/sub/ble.rs b/embassy-stm32-wpan/src/wb55/sub/ble.rs
index 0f770d92c..a822d6530 100644
--- a/embassy-stm32-wpan/src/sub/ble.rs
+++ b/embassy-stm32-wpan/src/wb55/sub/ble.rs
@@ -1,15 +1,15 @@
1use core::ptr; 1use core::ptr;
2 2
3use embassy_stm32::ipcc::Ipcc; 3use embassy_stm32::ipcc::{IpccRxChannel, IpccTxChannel};
4use hci::Opcode; 4use hci::Opcode;
5 5
6use crate::cmd::CmdPacket; 6use crate::cmd::CmdPacket;
7use crate::consts::{TlPacketType, TL_BLEEVT_CC_OPCODE, TL_BLEEVT_CS_OPCODE}; 7use crate::consts::{TL_BLEEVT_CC_OPCODE, TL_BLEEVT_CS_OPCODE, TlPacketType};
8use crate::evt;
8use crate::evt::{EvtBox, EvtPacket, EvtStub}; 9use crate::evt::{EvtBox, EvtPacket, EvtStub};
9use crate::sub::mm; 10use crate::sub::mm;
10use crate::tables::{BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; 11use crate::tables::{BLE_CMD_BUFFER, BleTable, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE};
11use crate::unsafe_linked_list::LinkedListNode; 12use crate::unsafe_linked_list::LinkedListNode;
12use crate::{channels, evt};
13 13
14/// A guard that, once constructed, may be used to send BLE commands to CPU2. 14/// A guard that, once constructed, may be used to send BLE commands to CPU2.
15/// 15///
@@ -36,15 +36,21 @@ use crate::{channels, evt};
36/// # mbox.ble_subsystem.reset().await; 36/// # mbox.ble_subsystem.reset().await;
37/// # let _reset_response = mbox.ble_subsystem.read().await; 37/// # let _reset_response = mbox.ble_subsystem.read().await;
38/// ``` 38/// ```
39pub struct Ble { 39pub struct Ble<'a> {
40 _private: (), 40 hw_ipcc_ble_cmd_channel: IpccTxChannel<'a>,
41 ipcc_ble_event_channel: IpccRxChannel<'a>,
42 ipcc_hci_acl_data_channel: IpccTxChannel<'a>,
41} 43}
42 44
43impl Ble { 45impl<'a> Ble<'a> {
44 /// Constructs a guard that allows for BLE commands to be sent to CPU2. 46 /// Constructs a guard that allows for BLE commands to be sent to CPU2.
45 /// 47 ///
46 /// This takes the place of `TL_BLE_Init`, completing that step as laid out in AN5289, Fig 66. 48 /// This takes the place of `TL_BLE_Init`, completing that step as laid out in AN5289, Fig 66.
47 pub(crate) fn new() -> Self { 49 pub(crate) fn new(
50 hw_ipcc_ble_cmd_channel: IpccTxChannel<'a>,
51 ipcc_ble_event_channel: IpccRxChannel<'a>,
52 ipcc_hci_acl_data_channel: IpccTxChannel<'a>,
53 ) -> Self {
48 unsafe { 54 unsafe {
49 LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); 55 LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr());
50 56
@@ -56,44 +62,53 @@ impl Ble {
56 }); 62 });
57 } 63 }
58 64
59 Self { _private: () } 65 Self {
66 hw_ipcc_ble_cmd_channel,
67 ipcc_ble_event_channel,
68 ipcc_hci_acl_data_channel,
69 }
60 } 70 }
61 71
62 /// `HW_IPCC_BLE_EvtNot` 72 /// `HW_IPCC_BLE_EvtNot`
63 pub async fn tl_read(&self) -> EvtBox<Self> { 73 pub async fn tl_read(&mut self) -> EvtBox<Self> {
64 Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe { 74 self.ipcc_ble_event_channel
65 if let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) { 75 .receive(|| unsafe {
66 Some(EvtBox::new(node_ptr.cast())) 76 if let Some(node_ptr) =
67 } else { 77 critical_section::with(|cs| LinkedListNode::remove_head(cs, EVT_QUEUE.as_mut_ptr()))
68 None 78 {
69 } 79 Some(EvtBox::new(node_ptr.cast()))
70 }) 80 } else {
71 .await 81 None
82 }
83 })
84 .await
72 } 85 }
73 86
74 /// `TL_BLE_SendCmd` 87 /// `TL_BLE_SendCmd`
75 pub async fn tl_write(&self, opcode: u16, payload: &[u8]) { 88 pub async fn tl_write(&mut self, opcode: u16, payload: &[u8]) {
76 Ipcc::send(channels::cpu1::IPCC_BLE_CMD_CHANNEL, || unsafe { 89 self.hw_ipcc_ble_cmd_channel
77 CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload); 90 .send(|| unsafe {
78 }) 91 CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload);
79 .await; 92 })
93 .await;
80 } 94 }
81 95
82 /// `TL_BLE_SendAclData` 96 /// `TL_BLE_SendAclData`
83 pub async fn acl_write(&self, handle: u16, payload: &[u8]) { 97 pub async fn acl_write(&mut self, handle: u16, payload: &[u8]) {
84 Ipcc::send(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, || unsafe { 98 self.ipcc_hci_acl_data_channel
85 CmdPacket::write_into( 99 .send(|| unsafe {
86 HCI_ACL_DATA_BUFFER.as_mut_ptr() as *mut _, 100 CmdPacket::write_into(
87 TlPacketType::AclData, 101 HCI_ACL_DATA_BUFFER.as_mut_ptr() as *mut _,
88 handle, 102 TlPacketType::AclData,
89 payload, 103 handle,
90 ); 104 payload,
91 }) 105 );
92 .await; 106 })
107 .await;
93 } 108 }
94} 109}
95 110
96impl evt::MemoryManager for Ble { 111impl<'a> evt::MemoryManager for Ble<'a> {
97 /// SAFETY: passing a pointer to something other than a managed event packet is UB 112 /// SAFETY: passing a pointer to something other than a managed event packet is UB
98 unsafe fn drop_event_packet(evt: *mut EvtPacket) { 113 unsafe fn drop_event_packet(evt: *mut EvtPacket) {
99 let stub = unsafe { 114 let stub = unsafe {
@@ -110,12 +125,12 @@ impl evt::MemoryManager for Ble {
110 125
111pub extern crate stm32wb_hci as hci; 126pub extern crate stm32wb_hci as hci;
112 127
113impl hci::Controller for Ble { 128impl<'a> hci::Controller for Ble<'a> {
114 async fn controller_write(&mut self, opcode: Opcode, payload: &[u8]) { 129 async fn controller_write(&mut self, opcode: Opcode, payload: &[u8]) {
115 self.tl_write(opcode.0, payload).await; 130 self.tl_write(opcode.0, payload).await;
116 } 131 }
117 132
118 async fn controller_read_into(&self, buf: &mut [u8]) { 133 async fn controller_read_into(&mut self, buf: &mut [u8]) {
119 let evt_box = self.tl_read().await; 134 let evt_box = self.tl_read().await;
120 let evt_serial = evt_box.serial(); 135 let evt_serial = evt_box.serial();
121 136
diff --git a/embassy-stm32-wpan/src/wb55/sub/mac.rs b/embassy-stm32-wpan/src/wb55/sub/mac.rs
new file mode 100644
index 000000000..ce2903e61
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/sub/mac.rs
@@ -0,0 +1,173 @@
1use core::future::poll_fn;
2use core::ptr;
3use core::sync::atomic::{AtomicBool, Ordering};
4use core::task::Poll;
5
6use embassy_futures::poll_once;
7use embassy_stm32::ipcc::{Ipcc, IpccRxChannel, IpccTxChannel};
8use embassy_sync::waitqueue::AtomicWaker;
9
10use crate::cmd::CmdPacket;
11use crate::consts::TlPacketType;
12use crate::evt;
13use crate::evt::{EvtBox, EvtPacket};
14use crate::mac::commands::MacCommand;
15use crate::mac::event::MacEvent;
16use crate::mac::typedefs::MacError;
17use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER};
18use crate::unsafe_linked_list::LinkedListNode;
19
20static MAC_WAKER: AtomicWaker = AtomicWaker::new();
21static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false);
22
23pub struct Mac<'a> {
24 ipcc_mac_802_15_4_cmd_rsp_channel: IpccTxChannel<'a>,
25 ipcc_mac_802_15_4_notification_ack_channel: IpccRxChannel<'a>,
26}
27
28impl<'a> Mac<'a> {
29 pub(crate) fn new(
30 ipcc_mac_802_15_4_cmd_rsp_channel: IpccTxChannel<'a>,
31 ipcc_mac_802_15_4_notification_ack_channel: IpccRxChannel<'a>,
32 ) -> Self {
33 use crate::tables::{
34 MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, Mac802_15_4Table, TL_MAC_802_15_4_TABLE,
35 TL_TRACES_TABLE, TRACES_EVT_QUEUE, TracesTable,
36 };
37
38 unsafe {
39 LinkedListNode::init_head(TRACES_EVT_QUEUE.as_mut_ptr() as *mut _);
40
41 TL_TRACES_TABLE.as_mut_ptr().write_volatile(TracesTable {
42 traces_queue: TRACES_EVT_QUEUE.as_ptr() as *const _,
43 });
44
45 TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table {
46 p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(),
47 p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(),
48 evt_queue: core::ptr::null_mut(),
49 });
50 };
51
52 Self {
53 ipcc_mac_802_15_4_cmd_rsp_channel,
54 ipcc_mac_802_15_4_notification_ack_channel,
55 }
56 }
57
58 pub const fn split(self) -> (MacRx<'a>, MacTx<'a>) {
59 (
60 MacRx {
61 ipcc_mac_802_15_4_notification_ack_channel: self.ipcc_mac_802_15_4_notification_ack_channel,
62 },
63 MacTx {
64 ipcc_mac_802_15_4_cmd_rsp_channel: self.ipcc_mac_802_15_4_cmd_rsp_channel,
65 },
66 )
67 }
68}
69
70pub struct MacTx<'a> {
71 ipcc_mac_802_15_4_cmd_rsp_channel: IpccTxChannel<'a>,
72}
73
74impl<'a> MacTx<'a> {
75 /// `HW_IPCC_MAC_802_15_4_CmdEvtNot`
76 pub async fn tl_write_and_get_response(&mut self, opcode: u16, payload: &[u8]) -> u8 {
77 self.tl_write(opcode, payload).await;
78 self.ipcc_mac_802_15_4_cmd_rsp_channel.flush().await;
79
80 unsafe {
81 let p_event_packet = MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket;
82 let p_mac_rsp_evt = &((*p_event_packet).evt_serial.evt.payload) as *const u8;
83
84 ptr::read_volatile(p_mac_rsp_evt)
85 }
86 }
87
88 /// `TL_MAC_802_15_4_SendCmd`
89 pub async fn tl_write(&mut self, opcode: u16, payload: &[u8]) {
90 self.ipcc_mac_802_15_4_cmd_rsp_channel
91 .send(|| unsafe {
92 CmdPacket::write_into(
93 MAC_802_15_4_CMD_BUFFER.as_mut_ptr(),
94 TlPacketType::MacCmd,
95 opcode,
96 payload,
97 );
98 })
99 .await;
100 }
101
102 pub async fn send_command<T>(&mut self, cmd: &T) -> Result<(), MacError>
103 where
104 T: MacCommand,
105 {
106 let response = self.tl_write_and_get_response(T::OPCODE as u16, cmd.payload()).await;
107
108 if response == 0x00 {
109 Ok(())
110 } else {
111 Err(MacError::from(response))
112 }
113 }
114}
115
116pub struct MacRx<'a> {
117 ipcc_mac_802_15_4_notification_ack_channel: IpccRxChannel<'a>,
118}
119
120impl<'a> MacRx<'a> {
121 /// `HW_IPCC_MAC_802_15_4_EvtNot`
122 ///
123 /// This function will stall if the previous `EvtBox` has not been dropped
124 pub async fn tl_read(&mut self) -> EvtBox<MacRx<'a>> {
125 // Wait for the last event box to be dropped
126 poll_fn(|cx| {
127 MAC_WAKER.register(cx.waker());
128 if MAC_EVT_OUT.load(Ordering::Acquire) {
129 Poll::Pending
130 } else {
131 Poll::Ready(())
132 }
133 })
134 .await;
135
136 // Return a new event box
137 self.ipcc_mac_802_15_4_notification_ack_channel
138 .receive(|| unsafe {
139 // The closure is not async, therefore the closure must execute to completion (cannot be dropped)
140 // Therefore, the event box is guaranteed to be cleaned up if it's not leaked
141 MAC_EVT_OUT.store(true, Ordering::SeqCst);
142
143 Some(EvtBox::new(MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _))
144 })
145 .await
146 }
147
148 pub async fn read<'b>(&mut self) -> Result<MacEvent<'b>, ()> {
149 MacEvent::new(self.tl_read().await)
150 }
151}
152
153impl<'a> evt::MemoryManager for MacRx<'a> {
154 /// SAFETY: passing a pointer to something other than a managed event packet is UB
155 unsafe fn drop_event_packet(_: *mut EvtPacket) {
156 trace!("mac drop event");
157
158 // Write the ack
159 CmdPacket::write_into(
160 MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _,
161 TlPacketType::OtAck,
162 0,
163 &[],
164 );
165
166 // Clear the rx flag
167 let _ = poll_once(Ipcc::receive::<()>(3, || None));
168
169 // Allow a new read call
170 MAC_EVT_OUT.store(false, Ordering::Release);
171 MAC_WAKER.wake();
172 }
173}
diff --git a/embassy-stm32-wpan/src/sub/mm.rs b/embassy-stm32-wpan/src/wb55/sub/mm.rs
index 4e4d2f854..0ca7d1835 100644
--- a/embassy-stm32-wpan/src/sub/mm.rs
+++ b/embassy-stm32-wpan/src/wb55/sub/mm.rs
@@ -3,36 +3,35 @@ use core::future::poll_fn;
3use core::mem::MaybeUninit; 3use core::mem::MaybeUninit;
4use core::task::Poll; 4use core::task::Poll;
5 5
6use aligned::{Aligned, A4}; 6use aligned::{A4, Aligned};
7use cortex_m::interrupt; 7use embassy_stm32::ipcc::IpccTxChannel;
8use embassy_stm32::ipcc::Ipcc;
9use embassy_sync::waitqueue::AtomicWaker; 8use embassy_sync::waitqueue::AtomicWaker;
10 9
11use crate::consts::POOL_SIZE; 10use crate::consts::POOL_SIZE;
11use crate::evt;
12use crate::evt::EvtPacket; 12use crate::evt::EvtPacket;
13#[cfg(feature = "ble")] 13#[cfg(feature = "wb55_ble")]
14use crate::tables::BLE_SPARE_EVT_BUF; 14use crate::tables::BLE_SPARE_EVT_BUF;
15use crate::tables::{MemManagerTable, EVT_POOL, FREE_BUF_QUEUE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE}; 15use crate::tables::{EVT_POOL, FREE_BUF_QUEUE, MemManagerTable, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE};
16use crate::unsafe_linked_list::LinkedListNode; 16use crate::unsafe_linked_list::LinkedListNode;
17use crate::{channels, evt};
18 17
19static MM_WAKER: AtomicWaker = AtomicWaker::new(); 18static MM_WAKER: AtomicWaker = AtomicWaker::new();
20static mut LOCAL_FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); 19static mut LOCAL_FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit());
21 20
22pub struct MemoryManager { 21pub struct MemoryManager<'a> {
23 _private: (), 22 ipcc_mm_release_buffer_channel: IpccTxChannel<'a>,
24} 23}
25 24
26impl MemoryManager { 25impl<'a> MemoryManager<'a> {
27 pub(crate) fn new() -> Self { 26 pub(crate) fn new(ipcc_mm_release_buffer_channel: IpccTxChannel<'a>) -> Self {
28 unsafe { 27 unsafe {
29 LinkedListNode::init_head(FREE_BUF_QUEUE.as_mut_ptr()); 28 LinkedListNode::init_head(FREE_BUF_QUEUE.as_mut_ptr());
30 LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); 29 LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr());
31 30
32 TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable { 31 TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable {
33 #[cfg(feature = "ble")] 32 #[cfg(feature = "wb55_ble")]
34 spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(), 33 spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(),
35 #[cfg(not(feature = "ble"))] 34 #[cfg(not(feature = "wb55_ble"))]
36 spare_ble_buffer: core::ptr::null(), 35 spare_ble_buffer: core::ptr::null(),
37 spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(), 36 spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(),
38 blepool: EVT_POOL.as_ptr().cast(), 37 blepool: EVT_POOL.as_ptr().cast(),
@@ -43,14 +42,16 @@ impl MemoryManager {
43 }); 42 });
44 } 43 }
45 44
46 Self { _private: () } 45 Self {
46 ipcc_mm_release_buffer_channel,
47 }
47 } 48 }
48 49
49 pub async fn run_queue(&self) { 50 pub async fn run_queue(&mut self) -> ! {
50 loop { 51 loop {
51 poll_fn(|cx| unsafe { 52 poll_fn(|cx| unsafe {
52 MM_WAKER.register(cx.waker()); 53 MM_WAKER.register(cx.waker());
53 if LinkedListNode::is_empty(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { 54 if critical_section::with(|cs| LinkedListNode::is_empty(cs, LOCAL_FREE_BUF_QUEUE.as_mut_ptr())) {
54 Poll::Pending 55 Poll::Pending
55 } else { 56 } else {
56 Poll::Ready(()) 57 Poll::Ready(())
@@ -58,24 +59,24 @@ impl MemoryManager {
58 }) 59 })
59 .await; 60 .await;
60 61
61 Ipcc::send(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, || { 62 self.ipcc_mm_release_buffer_channel
62 interrupt::free(|_| unsafe { 63 .send(|| {
63 // CS required while moving nodes 64 critical_section::with(|cs| unsafe {
64 while let Some(node_ptr) = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { 65 while let Some(node_ptr) = LinkedListNode::remove_head(cs, LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) {
65 LinkedListNode::insert_head(FREE_BUF_QUEUE.as_mut_ptr(), node_ptr); 66 LinkedListNode::insert_head(cs, FREE_BUF_QUEUE.as_mut_ptr(), node_ptr);
66 } 67 }
68 })
67 }) 69 })
68 }) 70 .await;
69 .await;
70 } 71 }
71 } 72 }
72} 73}
73 74
74impl evt::MemoryManager for MemoryManager { 75impl<'a> evt::MemoryManager for MemoryManager<'a> {
75 /// SAFETY: passing a pointer to something other than a managed event packet is UB 76 /// SAFETY: passing a pointer to something other than a managed event packet is UB
76 unsafe fn drop_event_packet(evt: *mut EvtPacket) { 77 unsafe fn drop_event_packet(evt: *mut EvtPacket) {
77 interrupt::free(|_| unsafe { 78 critical_section::with(|cs| unsafe {
78 LinkedListNode::insert_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), evt as *mut _); 79 LinkedListNode::insert_head(cs, LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), evt as *mut _);
79 }); 80 });
80 81
81 MM_WAKER.wake(); 82 MM_WAKER.wake();
diff --git a/embassy-stm32-wpan/src/wb55/sub/mod.rs b/embassy-stm32-wpan/src/wb55/sub/mod.rs
new file mode 100644
index 000000000..d3ebd822a
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/sub/mod.rs
@@ -0,0 +1,6 @@
1#[cfg(feature = "wb55_ble")]
2pub mod ble;
3#[cfg(feature = "wb55_mac")]
4pub mod mac;
5pub mod mm;
6pub mod sys;
diff --git a/embassy-stm32-wpan/src/wb55/sub/sys.rs b/embassy-stm32-wpan/src/wb55/sub/sys.rs
new file mode 100644
index 000000000..2e625a677
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/sub/sys.rs
@@ -0,0 +1,103 @@
1use embassy_stm32::ipcc::{IpccRxChannel, IpccTxChannel};
2
3use crate::cmd::CmdPacket;
4use crate::consts::TlPacketType;
5use crate::evt::EvtBox;
6#[cfg(feature = "wb55_ble")]
7use crate::shci::ShciBleInitCmdParam;
8use crate::shci::{SchiCommandStatus, ShciOpcode};
9use crate::sub::mm;
10use crate::tables::{SysTable, WirelessFwInfoTable};
11use crate::unsafe_linked_list::LinkedListNode;
12use crate::wb55::{SYS_CMD_BUF, SYSTEM_EVT_QUEUE, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE};
13
14/// A guard that, once constructed, allows for sys commands to be sent to CPU2.
15pub struct Sys<'a> {
16 ipcc_system_cmd_rsp_channel: IpccTxChannel<'a>,
17 ipcc_system_event_channel: IpccRxChannel<'a>,
18}
19
20impl<'a> Sys<'a> {
21 /// TL_Sys_Init
22 pub(crate) fn new(
23 ipcc_system_cmd_rsp_channel: IpccTxChannel<'a>,
24 ipcc_system_event_channel: IpccRxChannel<'a>,
25 ) -> Self {
26 unsafe {
27 LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr());
28
29 TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable {
30 pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(),
31 sys_queue: SYSTEM_EVT_QUEUE.as_ptr(),
32 });
33 }
34
35 Self {
36 ipcc_system_cmd_rsp_channel,
37 ipcc_system_event_channel,
38 }
39 }
40
41 /// Returns CPU2 wireless firmware information (if present).
42 pub fn wireless_fw_info(&self) -> Option<WirelessFwInfoTable> {
43 let info = unsafe { TL_DEVICE_INFO_TABLE.as_mut_ptr().read_volatile().wireless_fw_info_table };
44
45 // Zero version indicates that CPU2 wasn't active and didn't fill the information table
46 if info.version != 0 { Some(info) } else { None }
47 }
48
49 pub async fn write(&mut self, opcode: ShciOpcode, payload: &[u8]) {
50 self.ipcc_system_cmd_rsp_channel
51 .send(|| unsafe {
52 CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode as u16, payload);
53 })
54 .await;
55 }
56
57 /// `HW_IPCC_SYS_CmdEvtNot`
58 pub async fn write_and_get_response(
59 &mut self,
60 opcode: ShciOpcode,
61 payload: &[u8],
62 ) -> Result<SchiCommandStatus, ()> {
63 self.write(opcode, payload).await;
64 self.ipcc_system_cmd_rsp_channel.flush().await;
65
66 unsafe { SchiCommandStatus::from_packet(SYS_CMD_BUF.as_ptr()) }
67 }
68
69 #[cfg(feature = "wb55_mac")]
70 pub async fn shci_c2_mac_802_15_4_init(&mut self) -> Result<SchiCommandStatus, ()> {
71 self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await
72 }
73
74 /// Send a request to CPU2 to initialise the BLE stack.
75 ///
76 /// This must be called before any BLE commands are sent via the BLE channel (according to
77 /// AN5289, Figures 65 and 66). It should only be called after CPU2 sends a system event, via
78 /// `HW_IPCC_SYS_EvtNot`, aka `IoBusCallBackUserEvt` (as detailed in Figure 65), aka
79 /// [crate::sub::ble::hci::host::uart::UartHci::read].
80 #[cfg(feature = "wb55_ble")]
81 pub async fn shci_c2_ble_init(&mut self, param: ShciBleInitCmdParam) -> Result<SchiCommandStatus, ()> {
82 self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await
83 }
84
85 /// `HW_IPCC_SYS_EvtNot`
86 ///
87 /// This method takes the place of the `HW_IPCC_SYS_EvtNot`/`SysUserEvtRx`/`APPE_SysUserEvtRx`,
88 /// as the embassy implementation avoids the need to call C public bindings, and instead
89 /// handles the event channels directly.
90 pub async fn read(&mut self) -> EvtBox<mm::MemoryManager<'_>> {
91 self.ipcc_system_event_channel
92 .receive(|| unsafe {
93 if let Some(node_ptr) =
94 critical_section::with(|cs| LinkedListNode::remove_head(cs, SYSTEM_EVT_QUEUE.as_mut_ptr()))
95 {
96 Some(EvtBox::new(node_ptr.cast()))
97 } else {
98 None
99 }
100 })
101 .await
102 }
103}
diff --git a/embassy-stm32-wpan/src/tables.rs b/embassy-stm32-wpan/src/wb55/tables.rs
index fe6fc47a3..2e6a9199b 100644
--- a/embassy-stm32-wpan/src/tables.rs
+++ b/embassy-stm32-wpan/src/wb55/tables.rs
@@ -1,10 +1,10 @@
1use core::mem::MaybeUninit; 1use core::mem::MaybeUninit;
2 2
3use aligned::{Aligned, A4}; 3use aligned::{A4, Aligned};
4use bit_field::BitField; 4use bit_field::BitField;
5 5
6use crate::cmd::{AclDataPacket, CmdPacket}; 6use crate::cmd::{AclDataPacket, CmdPacket};
7#[cfg(feature = "mac")] 7#[cfg(feature = "wb55_mac")]
8use crate::consts::C_SIZE_CMD_STRING; 8use crate::consts::C_SIZE_CMD_STRING;
9use crate::consts::{POOL_SIZE, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; 9use crate::consts::{POOL_SIZE, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE};
10use crate::unsafe_linked_list::LinkedListNode; 10use crate::unsafe_linked_list::LinkedListNode;
@@ -190,94 +190,94 @@ pub struct RefTable {
190} 190}
191 191
192// --------------------- ref table --------------------- 192// --------------------- ref table ---------------------
193#[link_section = "TL_REF_TABLE"] 193#[unsafe(link_section = "TL_REF_TABLE")]
194pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::uninit(); 194pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::zeroed();
195 195
196#[link_section = "MB_MEM1"] 196#[unsafe(link_section = "MB_MEM1")]
197pub static mut TL_DEVICE_INFO_TABLE: Aligned<A4, MaybeUninit<DeviceInfoTable>> = Aligned(MaybeUninit::uninit()); 197pub static mut TL_DEVICE_INFO_TABLE: Aligned<A4, MaybeUninit<DeviceInfoTable>> = Aligned(MaybeUninit::zeroed());
198 198
199#[link_section = "MB_MEM1"] 199#[unsafe(link_section = "MB_MEM1")]
200pub static mut TL_BLE_TABLE: Aligned<A4, MaybeUninit<BleTable>> = Aligned(MaybeUninit::uninit()); 200pub static mut TL_BLE_TABLE: Aligned<A4, MaybeUninit<BleTable>> = Aligned(MaybeUninit::zeroed());
201 201
202#[link_section = "MB_MEM1"] 202#[unsafe(link_section = "MB_MEM1")]
203pub static mut TL_THREAD_TABLE: Aligned<A4, MaybeUninit<ThreadTable>> = Aligned(MaybeUninit::uninit()); 203pub static mut TL_THREAD_TABLE: Aligned<A4, MaybeUninit<ThreadTable>> = Aligned(MaybeUninit::zeroed());
204 204
205#[link_section = "MB_MEM1"] 205#[unsafe(link_section = "MB_MEM1")]
206pub static mut TL_LLD_TESTS_TABLE: Aligned<A4, MaybeUninit<LldTestsTable>> = Aligned(MaybeUninit::uninit()); 206pub static mut TL_LLD_TESTS_TABLE: Aligned<A4, MaybeUninit<LldTestsTable>> = Aligned(MaybeUninit::zeroed());
207 207
208#[link_section = "MB_MEM1"] 208#[unsafe(link_section = "MB_MEM1")]
209pub static mut TL_BLE_LLD_TABLE: Aligned<A4, MaybeUninit<BleLldTable>> = Aligned(MaybeUninit::uninit()); 209pub static mut TL_BLE_LLD_TABLE: Aligned<A4, MaybeUninit<BleLldTable>> = Aligned(MaybeUninit::zeroed());
210 210
211#[link_section = "MB_MEM1"] 211#[unsafe(link_section = "MB_MEM1")]
212pub static mut TL_SYS_TABLE: Aligned<A4, MaybeUninit<SysTable>> = Aligned(MaybeUninit::uninit()); 212pub static mut TL_SYS_TABLE: Aligned<A4, MaybeUninit<SysTable>> = Aligned(MaybeUninit::zeroed());
213 213
214#[link_section = "MB_MEM1"] 214#[unsafe(link_section = "MB_MEM1")]
215pub static mut TL_MEM_MANAGER_TABLE: Aligned<A4, MaybeUninit<MemManagerTable>> = Aligned(MaybeUninit::uninit()); 215pub static mut TL_MEM_MANAGER_TABLE: Aligned<A4, MaybeUninit<MemManagerTable>> = Aligned(MaybeUninit::zeroed());
216 216
217#[link_section = "MB_MEM1"] 217#[unsafe(link_section = "MB_MEM1")]
218pub static mut TL_TRACES_TABLE: Aligned<A4, MaybeUninit<TracesTable>> = Aligned(MaybeUninit::uninit()); 218pub static mut TL_TRACES_TABLE: Aligned<A4, MaybeUninit<TracesTable>> = Aligned(MaybeUninit::zeroed());
219 219
220#[link_section = "MB_MEM1"] 220#[unsafe(link_section = "MB_MEM1")]
221pub static mut TL_MAC_802_15_4_TABLE: Aligned<A4, MaybeUninit<Mac802_15_4Table>> = Aligned(MaybeUninit::uninit()); 221pub static mut TL_MAC_802_15_4_TABLE: Aligned<A4, MaybeUninit<Mac802_15_4Table>> = Aligned(MaybeUninit::zeroed());
222 222
223#[link_section = "MB_MEM1"] 223#[unsafe(link_section = "MB_MEM1")]
224pub static mut TL_ZIGBEE_TABLE: Aligned<A4, MaybeUninit<ZigbeeTable>> = Aligned(MaybeUninit::uninit()); 224pub static mut TL_ZIGBEE_TABLE: Aligned<A4, MaybeUninit<ZigbeeTable>> = Aligned(MaybeUninit::zeroed());
225 225
226// --------------------- tables --------------------- 226// --------------------- tables ---------------------
227#[link_section = "MB_MEM1"] 227#[unsafe(link_section = "MB_MEM1")]
228pub static mut FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); 228pub static mut FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed());
229 229
230#[allow(dead_code)] 230#[allow(dead_code)]
231#[link_section = "MB_MEM1"] 231#[unsafe(link_section = "MB_MEM1")]
232pub static mut TRACES_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); 232pub static mut TRACES_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed());
233 233
234#[link_section = "MB_MEM2"] 234#[unsafe(link_section = "MB_MEM2")]
235pub static mut CS_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]>> = 235pub static mut CS_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]>> =
236 Aligned(MaybeUninit::uninit()); 236 Aligned(MaybeUninit::zeroed());
237 237
238#[link_section = "MB_MEM2"] 238#[unsafe(link_section = "MB_MEM2")]
239pub static mut EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); 239pub static mut EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed());
240 240
241#[link_section = "MB_MEM2"] 241#[unsafe(link_section = "MB_MEM2")]
242pub static mut SYSTEM_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); 242pub static mut SYSTEM_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed());
243 243
244// --------------------- app tables --------------------- 244// --------------------- app tables ---------------------
245#[cfg(feature = "mac")] 245#[cfg(feature = "wb55_mac")]
246#[link_section = "MB_MEM2"] 246#[unsafe(link_section = "MB_MEM2")]
247pub static mut MAC_802_15_4_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::uninit()); 247pub static mut MAC_802_15_4_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed());
248 248
249#[cfg(feature = "mac")] 249#[cfg(feature = "wb55_mac")]
250#[link_section = "MB_MEM2"] 250#[unsafe(link_section = "MB_MEM2")]
251pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit< 251pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit<
252 Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>, 252 Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>,
253> = MaybeUninit::uninit(); 253> = MaybeUninit::zeroed();
254 254
255#[link_section = "MB_MEM2"] 255#[unsafe(link_section = "MB_MEM2")]
256pub static mut EVT_POOL: Aligned<A4, MaybeUninit<[u8; POOL_SIZE]>> = Aligned(MaybeUninit::uninit()); 256pub static mut EVT_POOL: Aligned<A4, MaybeUninit<[u8; POOL_SIZE]>> = Aligned(MaybeUninit::zeroed());
257 257
258#[link_section = "MB_MEM2"] 258#[unsafe(link_section = "MB_MEM2")]
259pub static mut SYS_CMD_BUF: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::uninit()); 259pub static mut SYS_CMD_BUF: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed());
260 260
261#[link_section = "MB_MEM2"] 261#[unsafe(link_section = "MB_MEM2")]
262pub static mut SYS_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> = 262pub static mut SYS_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
263 Aligned(MaybeUninit::uninit()); 263 Aligned(MaybeUninit::zeroed());
264 264
265#[cfg(feature = "mac")] 265#[cfg(feature = "wb55_mac")]
266#[link_section = "MB_MEM2"] 266#[unsafe(link_section = "MB_MEM2")]
267pub static mut MAC_802_15_4_CNFINDNOT: Aligned<A4, MaybeUninit<[u8; C_SIZE_CMD_STRING]>> = 267pub static mut MAC_802_15_4_CNFINDNOT: Aligned<A4, MaybeUninit<[u8; C_SIZE_CMD_STRING]>> =
268 Aligned(MaybeUninit::uninit()); 268 Aligned(MaybeUninit::zeroed());
269 269
270#[cfg(feature = "ble")] 270#[cfg(feature = "wb55_ble")]
271#[link_section = "MB_MEM1"] 271#[unsafe(link_section = "MB_MEM1")]
272pub static mut BLE_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::uninit()); 272pub static mut BLE_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed());
273 273
274#[cfg(feature = "ble")] 274#[cfg(feature = "wb55_ble")]
275#[link_section = "MB_MEM2"] 275#[unsafe(link_section = "MB_MEM2")]
276pub static mut BLE_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> = 276pub static mut BLE_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
277 Aligned(MaybeUninit::uninit()); 277 Aligned(MaybeUninit::zeroed());
278 278
279#[cfg(feature = "ble")] 279#[cfg(feature = "wb55_ble")]
280#[link_section = "MB_MEM2"] 280#[unsafe(link_section = "MB_MEM2")]
281// fuck these "magic" numbers from ST ---v---v 281// fuck these "magic" numbers from ST ---v---v
282pub static mut HCI_ACL_DATA_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> = 282pub static mut HCI_ACL_DATA_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> =
283 Aligned(MaybeUninit::uninit()); 283 Aligned(MaybeUninit::zeroed());
diff --git a/embassy-stm32-wpan/src/wb55/unsafe_linked_list.rs b/embassy-stm32-wpan/src/wb55/unsafe_linked_list.rs
new file mode 100644
index 000000000..c84ee1bb6
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/unsafe_linked_list.rs
@@ -0,0 +1,269 @@
1//! Unsafe linked list.
2//! Translated from ST's C by `c2rust` tool.
3
4#![allow(
5 dead_code,
6 mutable_transmutes,
7 non_camel_case_types,
8 non_snake_case,
9 non_upper_case_globals,
10 unused_assignments,
11 unused_mut
12)]
13
14use core::fmt::Debug;
15use core::ptr;
16
17use critical_section::CriticalSection;
18
19#[derive(Copy, Clone)]
20#[repr(C, packed(4))]
21pub struct LinkedListNode {
22 pub next: *mut LinkedListNode,
23 pub prev: *mut LinkedListNode,
24}
25
26impl Default for LinkedListNode {
27 fn default() -> Self {
28 LinkedListNode {
29 next: core::ptr::null_mut(),
30 prev: core::ptr::null_mut(),
31 }
32 }
33}
34
35impl LinkedListNode {
36 pub unsafe fn init_head(mut p_list_head: *mut LinkedListNode) {
37 ptr::write_volatile(
38 p_list_head,
39 LinkedListNode {
40 next: p_list_head,
41 prev: p_list_head,
42 },
43 );
44 }
45
46 pub unsafe fn is_empty(_cs: CriticalSection, mut p_list_head: *mut LinkedListNode) -> bool {
47 ptr::read_volatile(p_list_head).next == p_list_head
48 }
49
50 /// Insert `node` after `list_head` and before the next node
51 pub unsafe fn insert_head(
52 _cs: CriticalSection,
53 mut p_list_head: *mut LinkedListNode,
54 mut p_node: *mut LinkedListNode,
55 ) {
56 let mut list_head = ptr::read_volatile(p_list_head);
57 if p_list_head != list_head.next {
58 let mut node_next = ptr::read_volatile(list_head.next);
59 let node = LinkedListNode {
60 next: list_head.next,
61 prev: p_list_head,
62 };
63
64 list_head.next = p_node;
65 node_next.prev = p_node;
66
67 // All nodes must be written because they will all be seen by another core
68 ptr::write_volatile(p_node, node);
69 ptr::write_volatile(node.next, node_next);
70 ptr::write_volatile(p_list_head, list_head);
71 } else {
72 let node = LinkedListNode {
73 next: list_head.next,
74 prev: p_list_head,
75 };
76
77 list_head.next = p_node;
78 list_head.prev = p_node;
79
80 // All nodes must be written because they will all be seen by another core
81 ptr::write_volatile(p_node, node);
82 ptr::write_volatile(p_list_head, list_head);
83 }
84 }
85
86 /// Insert `node` before `list_tail` and after the second-to-last node
87 pub unsafe fn insert_tail(
88 _cs: CriticalSection,
89 mut p_list_tail: *mut LinkedListNode,
90 mut p_node: *mut LinkedListNode,
91 ) {
92 let mut list_tail = ptr::read_volatile(p_list_tail);
93 if p_list_tail != list_tail.prev {
94 let mut node_prev = ptr::read_volatile(list_tail.prev);
95 let node = LinkedListNode {
96 next: p_list_tail,
97 prev: list_tail.prev,
98 };
99
100 list_tail.prev = p_node;
101 node_prev.next = p_node;
102
103 // All nodes must be written because they will all be seen by another core
104 ptr::write_volatile(p_node, node);
105 ptr::write_volatile(node.prev, node_prev);
106 ptr::write_volatile(p_list_tail, list_tail);
107 } else {
108 let node = LinkedListNode {
109 next: p_list_tail,
110 prev: list_tail.prev,
111 };
112
113 list_tail.prev = p_node;
114 list_tail.next = p_node;
115
116 // All nodes must be written because they will all be seen by another core
117 ptr::write_volatile(p_node, node);
118 ptr::write_volatile(p_list_tail, list_tail);
119 }
120 }
121
122 /// Remove `node` from the linked list
123 pub unsafe fn remove_node(_cs: CriticalSection, mut p_node: *mut LinkedListNode) {
124 let node = ptr::read_unaligned(p_node);
125
126 if node.next != node.prev {
127 let mut node_next = ptr::read_volatile(node.next);
128 let mut node_prev = ptr::read_volatile(node.prev);
129
130 node_prev.next = node.next;
131 node_next.prev = node.prev;
132
133 ptr::write_volatile(node.next, node_next);
134 ptr::write_volatile(node.prev, node_prev);
135 } else {
136 let mut node_next = ptr::read_volatile(node.next);
137
138 node_next.next = node.next;
139 node_next.prev = node.prev;
140
141 ptr::write_volatile(node.next, node_next);
142 }
143 }
144
145 /// Remove `list_head` and return a pointer to the `node`.
146 pub unsafe fn remove_head(
147 _cs: CriticalSection,
148 mut p_list_head: *mut LinkedListNode,
149 ) -> Option<*mut LinkedListNode> {
150 let list_head = ptr::read_volatile(p_list_head);
151
152 if list_head.next == p_list_head {
153 None
154 } else {
155 // Allowed because a removed node is not seen by another core
156 let p_node = list_head.next;
157 Self::remove_node(_cs, p_node);
158
159 Some(p_node)
160 }
161 }
162
163 /// Remove `list_tail` and return a pointer to the `node`.
164 pub unsafe fn remove_tail(
165 _cs: CriticalSection,
166 mut p_list_tail: *mut LinkedListNode,
167 ) -> Option<*mut LinkedListNode> {
168 let list_tail = ptr::read_volatile(p_list_tail);
169
170 if list_tail.prev == p_list_tail {
171 None
172 } else {
173 // Allowed because a removed node is not seen by another core
174 let p_node = list_tail.prev;
175 Self::remove_node(_cs, p_node);
176
177 Some(p_node)
178 }
179 }
180
181 pub unsafe fn insert_node_after(
182 _cs: CriticalSection,
183 mut p_node: *mut LinkedListNode,
184 mut p_ref_node: *mut LinkedListNode,
185 ) {
186 let mut node = ptr::read_volatile(p_node);
187 let mut ref_node = ptr::read_volatile(p_ref_node);
188 let mut prev_node = ptr::read_volatile(ref_node.next);
189
190 node.next = ref_node.next;
191 node.prev = p_ref_node;
192 ref_node.next = p_node;
193 prev_node.prev = p_node;
194
195 ptr::write_volatile(p_node, node);
196 ptr::write_volatile(p_ref_node, ref_node);
197 ptr::write_volatile(node.next, prev_node);
198 }
199
200 pub unsafe fn insert_node_before(
201 _cs: CriticalSection,
202 mut node: *mut LinkedListNode,
203 mut ref_node: *mut LinkedListNode,
204 ) {
205 (*node).next = ref_node;
206 (*node).prev = (*ref_node).prev;
207 (*ref_node).prev = node;
208 (*(*node).prev).next = node;
209
210 todo!("this function has not been converted to volatile semantics");
211 }
212
213 pub unsafe fn get_size(_cs: CriticalSection, mut list_head: *mut LinkedListNode) -> usize {
214 let mut size = 0;
215 let mut temp: *mut LinkedListNode = core::ptr::null_mut::<LinkedListNode>();
216
217 temp = (*list_head).next;
218 while temp != list_head {
219 size += 1;
220 temp = (*temp).next
221 }
222
223 let _ = size;
224
225 todo!("this function has not been converted to volatile semantics");
226 }
227
228 pub unsafe fn get_next_node(_cs: CriticalSection, mut p_ref_node: *mut LinkedListNode) -> *mut LinkedListNode {
229 let ref_node = ptr::read_volatile(p_ref_node);
230
231 // Allowed because a removed node is not seen by another core
232 ref_node.next
233 }
234
235 pub unsafe fn get_prev_node(_cs: CriticalSection, mut p_ref_node: *mut LinkedListNode) -> *mut LinkedListNode {
236 let ref_node = ptr::read_volatile(p_ref_node);
237
238 // Allowed because a removed node is not seen by another core
239 ref_node.prev
240 }
241}
242
243pub struct DebuggableLinkedListNode(*const LinkedListNode);
244
245impl Debug for DebuggableLinkedListNode {
246 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
247 // Safe because this is just reading memory, and using unaligned to do it
248 let p_node = self.0;
249
250 f.write_fmt(format_args!("iterating list from node: {:x}", p_node as usize))?;
251
252 let mut p_current_node = p_node;
253 for _ in 0..30 {
254 let current_node = unsafe { ptr::read_unaligned(p_current_node) };
255 f.write_fmt(format_args!(
256 "node (prev, current, next): {:x}, {:x}, {:x}",
257 current_node.prev as usize, p_current_node as usize, current_node.next as usize
258 ))?;
259
260 if current_node.next == p_node as *mut _ {
261 break;
262 }
263
264 p_current_node = current_node.next;
265 }
266
267 Ok(())
268 }
269}
diff --git a/embassy-stm32-wpan/src/wba/bindings.rs b/embassy-stm32-wpan/src/wba/bindings.rs
new file mode 100644
index 000000000..d2030cfb8
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/bindings.rs
@@ -0,0 +1 @@
pub use stm32_bindings::bindings::{mac, wba_ble_stack as ble, wba_link_layer as link_layer};
diff --git a/embassy-stm32-wpan/src/wba/linklayer_plat.rs b/embassy-stm32-wpan/src/wba/linklayer_plat.rs
new file mode 100644
index 000000000..108e84efe
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/linklayer_plat.rs
@@ -0,0 +1,913 @@
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 */
75#![cfg(feature = "wba")]
76#![allow(clippy::missing_safety_doc)]
77
78use core::hint::spin_loop;
79use core::ptr;
80use core::sync::atomic::{AtomicBool, AtomicI32, AtomicPtr, AtomicU32, Ordering};
81
82use cortex_m::asm::{dsb, isb};
83use cortex_m::interrupt::InterruptNumber;
84use cortex_m::peripheral::NVIC;
85use cortex_m::register::basepri;
86use critical_section;
87#[cfg(feature = "defmt")]
88use defmt::trace;
89use embassy_stm32::NVIC_PRIO_BITS;
90use embassy_time::{Duration, block_for};
91
92use super::bindings::{link_layer, mac};
93
94// Missing constant from stm32-bindings - RADIO_SW_LOW interrupt number
95// For STM32WBA, this is typically RADIO_IRQ_BUSY (interrupt 43)
96const RADIO_SW_LOW_INTR_NUM: u32 = 43;
97
98type Callback = unsafe extern "C" fn();
99
100#[derive(Clone, Copy, Debug, Eq, PartialEq)]
101#[repr(transparent)]
102struct RawInterrupt(u16);
103
104impl RawInterrupt {
105 #[inline(always)]
106 fn new(irq: u32) -> Self {
107 debug_assert!(irq <= u16::MAX as u32);
108 Self(irq as u16)
109 }
110}
111
112impl From<u32> for RawInterrupt {
113 #[inline(always)]
114 fn from(value: u32) -> Self {
115 Self::new(value)
116 }
117}
118
119unsafe impl InterruptNumber for RawInterrupt {
120 fn number(self) -> u16 {
121 self.0
122 }
123}
124
125static RADIO_CALLBACK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());
126static LOW_ISR_CALLBACK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());
127
128static IRQ_COUNTER: AtomicI32 = AtomicI32::new(0);
129
130static PRIO_HIGH_ISR_COUNTER: AtomicI32 = AtomicI32::new(0);
131static PRIO_LOW_ISR_COUNTER: AtomicI32 = AtomicI32::new(0);
132static PRIO_SYS_ISR_COUNTER: AtomicI32 = AtomicI32::new(0);
133static LOCAL_BASEPRI_VALUE: AtomicU32 = AtomicU32::new(0);
134
135static RADIO_SW_LOW_ISR_RUNNING_HIGH_PRIO: AtomicBool = AtomicBool::new(false);
136static AHB5_SWITCHED_OFF: AtomicBool = AtomicBool::new(false);
137static RADIO_SLEEP_TIMER_VAL: AtomicU32 = AtomicU32::new(0);
138
139static PRNG_STATE: AtomicU32 = AtomicU32::new(0);
140static PRNG_INIT: AtomicBool = AtomicBool::new(false);
141
142// Critical-section restore token for IRQ enable/disable pairing.
143// Only written when the IRQ disable counter transitions 0->1, and consumed when it transitions 1->0.
144static mut CS_RESTORE_STATE: Option<critical_section::RestoreState> = None;
145
146unsafe extern "C" {
147 static SystemCoreClock: u32;
148}
149
150#[inline(always)]
151fn read_system_core_clock() -> u32 {
152 unsafe { ptr::read_volatile(&SystemCoreClock) }
153}
154
155#[inline(always)]
156fn store_callback(slot: &AtomicPtr<()>, cb: Option<Callback>) {
157 let ptr = cb.map_or(ptr::null_mut(), |f| f as *mut ());
158 slot.store(ptr, Ordering::Release);
159}
160
161#[inline(always)]
162fn load_callback(slot: &AtomicPtr<()>) -> Option<Callback> {
163 let ptr = slot.load(Ordering::Acquire);
164 if ptr.is_null() {
165 None
166 } else {
167 Some(unsafe { core::mem::transmute::<*mut (), Callback>(ptr) })
168 }
169}
170
171#[inline(always)]
172fn priority_shift() -> u8 {
173 8 - NVIC_PRIO_BITS as u8
174}
175
176fn pack_priority(raw: u32) -> u8 {
177 let shift = priority_shift();
178 let priority_bits = NVIC_PRIO_BITS as u32;
179 let mask = if priority_bits >= 32 {
180 u32::MAX
181 } else {
182 (1u32 << priority_bits) - 1
183 };
184 let clamped = raw & mask;
185 (clamped << u32::from(shift)) as u8
186}
187
188#[inline(always)]
189fn counter_release(counter: &AtomicI32) -> bool {
190 counter.fetch_sub(1, Ordering::SeqCst) <= 1
191}
192
193#[inline(always)]
194fn counter_acquire(counter: &AtomicI32) -> bool {
195 counter.fetch_add(1, Ordering::SeqCst) == 0
196}
197
198unsafe fn nvic_enable(irq: u32) {
199 NVIC::unmask(RawInterrupt::new(irq));
200 dsb();
201 isb();
202}
203
204unsafe fn nvic_disable(irq: u32) {
205 NVIC::mask(RawInterrupt::new(irq));
206 dsb();
207 isb();
208}
209
210unsafe fn nvic_set_pending(irq: u32) {
211 NVIC::pend(RawInterrupt::new(irq));
212 dsb();
213 isb();
214}
215
216unsafe fn nvic_get_active(irq: u32) -> bool {
217 NVIC::is_active(RawInterrupt::new(irq))
218}
219
220unsafe fn nvic_set_priority(irq: u32, priority: u8) {
221 // STM32WBA is ARMv8-M, which uses byte-accessible IPR registers
222 let nvic = &*NVIC::PTR;
223 nvic.ipr[irq as usize].write(priority);
224
225 dsb();
226 isb();
227}
228
229#[inline(always)]
230fn set_basepri_max(value: u8) {
231 unsafe {
232 if basepri::read() < value {
233 basepri::write(value);
234 }
235 }
236}
237
238fn prng_next() -> u32 {
239 #[inline]
240 fn xorshift(mut x: u32) -> u32 {
241 x ^= x << 13;
242 x ^= x >> 17;
243 x ^= x << 5;
244 x
245 }
246
247 if !PRNG_INIT.load(Ordering::Acquire) {
248 let seed = unsafe {
249 let timer = link_layer::ll_intf_cmn_get_slptmr_value();
250 let core_clock = read_system_core_clock();
251 timer ^ core_clock ^ 0x6C8E_9CF5
252 };
253 PRNG_STATE.store(seed, Ordering::Relaxed);
254 PRNG_INIT.store(true, Ordering::Release);
255 }
256
257 let mut current = PRNG_STATE.load(Ordering::Relaxed);
258 loop {
259 let next = xorshift(current);
260 match PRNG_STATE.compare_exchange_weak(current, next, Ordering::AcqRel, Ordering::Relaxed) {
261 Ok(_) => return next,
262 Err(v) => current = v,
263 }
264 }
265}
266
267pub unsafe fn run_radio_high_isr() {
268 if let Some(cb) = load_callback(&RADIO_CALLBACK) {
269 cb();
270 }
271}
272
273pub unsafe fn run_radio_sw_low_isr() {
274 if let Some(cb) = load_callback(&LOW_ISR_CALLBACK) {
275 cb();
276 }
277
278 if RADIO_SW_LOW_ISR_RUNNING_HIGH_PRIO.swap(false, Ordering::AcqRel) {
279 nvic_set_priority(RADIO_SW_LOW_INTR_NUM, pack_priority(mac::RADIO_SW_LOW_INTR_PRIO));
280 }
281}
282
283// /**
284// * @brief Configure the necessary clock sources for the radio.
285// * @param None
286// * @retval None
287// */
288#[unsafe(no_mangle)]
289pub unsafe extern "C" fn LINKLAYER_PLAT_ClockInit() {
290 // uint32_t linklayer_slp_clk_src = LL_RCC_RADIOSLEEPSOURCE_NONE;
291 //
292 // /* Get the Link Layer sleep timer clock source */
293 // linklayer_slp_clk_src = LL_RCC_RADIO_GetSleepTimerClockSource();
294 // if(linklayer_slp_clk_src == LL_RCC_RADIOSLEEPSOURCE_NONE)
295 // {
296 // /* If there is no clock source defined, should be selected before */
297 // assert_param(0);
298 // }
299 //
300 // /* Enable AHB5ENR peripheral clock (bus CLK) */
301 // __HAL_RCC_RADIO_CLK_ENABLE();
302 trace!("LINKLAYER_PLAT_ClockInit: get_slptmr_value");
303 let _ = link_layer::ll_intf_cmn_get_slptmr_value();
304}
305
306// /**
307// * @brief Link Layer active waiting loop.
308// * @param delay: delay in us
309// * @retval None
310// */
311#[unsafe(no_mangle)]
312pub unsafe extern "C" fn LINKLAYER_PLAT_DelayUs(delay: u32) {
313 // static uint8_t lock = 0;
314 // uint32_t t0;
315 // uint32_t primask_bit;
316 //
317 // /* Enter critical section */
318 // primask_bit= __get_PRIMASK();
319 // __disable_irq();
320 //
321 // if (lock == 0U)
322 // {
323 // /* Initialize counter */
324 // /* Reset cycle counter to prevent overflow
325 // As a us counter, it is assumed than even with re-entrancy,
326 // overflow will never happen before re-initializing this counter */
327 // DWT->CYCCNT = 0U;
328 // /* Enable DWT by safety but should be useless (as already set) */
329 // SET_BIT(DCB->DEMCR, DCB_DEMCR_TRCENA_Msk);
330 // /* Enable counter */
331 // SET_BIT(DWT->CTRL, DWT_CTRL_CYCCNTENA_Msk);
332 // }
333 // /* Increment 're-entrance' counter */
334 // lock++;
335 // /* Get starting time stamp */
336 // t0 = DWT->CYCCNT;
337 // /* Exit critical section */
338 // __set_PRIMASK(primask_bit);
339 //
340 // /* Turn us into cycles */
341 // delay = delay * (SystemCoreClock / 1000000U);
342 // delay += t0;
343 //
344 // /* Busy waiting loop */
345 // while (DWT->CYCCNT < delay)
346 // {
347 // };
348 //
349 // /* Enter critical section */
350 // primask_bit= __get_PRIMASK();
351 // __disable_irq();
352 // if (lock == 1U)
353 // {
354 // /* Disable counter */
355 // CLEAR_BIT(DWT->CTRL, DWT_CTRL_CYCCNTENA_Msk);
356 // }
357 // /* Decrement 're-entrance' counter */
358 // lock--;
359 // /* Exit critical section */
360 // __set_PRIMASK(primask_bit);
361 //
362 trace!("LINKLAYER_PLAT_DelayUs: delay={}", delay);
363 block_for(Duration::from_micros(u64::from(delay)));
364}
365
366// /**
367// * @brief Link Layer assertion API
368// * @param condition: conditional statement to be checked.
369// * @retval None
370// */
371#[unsafe(no_mangle)]
372pub unsafe extern "C" fn LINKLAYER_PLAT_Assert(condition: u8) {
373 if condition == 0 {
374 panic!("LINKLAYER_PLAT assertion failed");
375 }
376}
377
378// /**
379// * @brief Enable/disable the Link Layer active clock (baseband clock).
380// * @param enable: boolean value to enable (1) or disable (0) the clock.
381// * @retval None
382// */
383#[unsafe(no_mangle)]
384pub unsafe extern "C" fn LINKLAYER_PLAT_WaitHclkRdy() {
385 trace!("LINKLAYER_PLAT_WaitHclkRdy");
386 if AHB5_SWITCHED_OFF.swap(false, Ordering::AcqRel) {
387 let reference = RADIO_SLEEP_TIMER_VAL.load(Ordering::Acquire);
388 trace!("LINKLAYER_PLAT_WaitHclkRdy: reference={}", reference);
389 while reference == link_layer::ll_intf_cmn_get_slptmr_value() {
390 spin_loop();
391 }
392 }
393}
394
395// /**
396// * @brief Notify the Link Layer platform layer the system will enter in WFI
397// * and AHB5 clock may be turned of regarding the 2.4Ghz radio state.
398// * @param None
399// * @retval None
400// */
401#[unsafe(no_mangle)]
402pub unsafe extern "C" fn LINKLAYER_PLAT_NotifyWFIEnter() {
403 // /* Check if Radio state will allow the AHB5 clock to be cut */
404 //
405 // /* AHB5 clock will be cut in the following cases:
406 // * - 2.4GHz radio is not in ACTIVE mode (in SLEEP or DEEPSLEEP mode).
407 // * - RADIOSMEN and STRADIOCLKON bits are at 0.
408 // */
409 // if((LL_PWR_GetRadioMode() != LL_PWR_RADIO_ACTIVE_MODE) ||
410 // ((__HAL_RCC_RADIO_IS_CLK_SLEEP_ENABLED() == 0) && (LL_RCC_RADIO_IsEnabledSleepTimerClock() == 0)))
411 // {
412 // AHB5_SwitchedOff = 1;
413 // }
414 trace!("LINKLAYER_PLAT_NotifyWFIEnter");
415 AHB5_SWITCHED_OFF.store(true, Ordering::Release);
416}
417
418// /**
419// * @brief Notify the Link Layer platform layer the system exited WFI and AHB5
420// * clock may be resynchronized as is may have been turned of during
421// * low power mode entry.
422// * @param None
423// * @retval None
424// */
425#[unsafe(no_mangle)]
426pub unsafe extern "C" fn LINKLAYER_PLAT_NotifyWFIExit() {
427 trace!("LINKLAYER_PLAT_NotifyWFIExit");
428 // /* Check if AHB5 clock has been turned of and needs resynchronisation */
429 if AHB5_SWITCHED_OFF.load(Ordering::Acquire) {
430 // /* Read sleep register as earlier as possible */
431 let value = link_layer::ll_intf_cmn_get_slptmr_value();
432 RADIO_SLEEP_TIMER_VAL.store(value, Ordering::Release);
433 }
434}
435
436// /**
437// * @brief Active wait on bus clock readiness.
438// * @param None
439// * @retval None
440// */
441#[unsafe(no_mangle)]
442pub unsafe extern "C" fn LINKLAYER_PLAT_AclkCtrl(_enable: u8) {
443 trace!("LINKLAYER_PLAT_AclkCtrl: enable={}", _enable);
444 if _enable != 0 {
445 // #if (CFG_SCM_SUPPORTED == 1)
446 // /* SCM HSE BEGIN */
447 // /* Polling on HSE32 activation */
448 // SCM_HSE_WaitUntilReady();
449 // /* Enable RADIO baseband clock (active CLK) */
450 // HAL_RCCEx_EnableRadioBBClock();
451 // /* SCM HSE END */
452 // #else
453 // /* Enable RADIO baseband clock (active CLK) */
454 // HAL_RCCEx_EnableRadioBBClock();
455 // /* Polling on HSE32 activation */
456 // while ( LL_RCC_HSE_IsReady() == 0);
457 // #endif /* CFG_SCM_SUPPORTED */
458 // NOTE: Add a proper assertion once a typed `Radio` peripheral exists in embassy-stm32
459 // that exposes the baseband clock enable status via RCC.
460 } else {
461 // /* Disable RADIO baseband clock (active CLK) */
462 // HAL_RCCEx_DisableRadioBBClock();
463 }
464}
465
466// /**
467// * @brief Link Layer RNG request.
468// * @param ptr_rnd: pointer to the variable that hosts the number.
469// * @param len: number of byte of anthropy to get.
470// * @retval None
471// */
472#[unsafe(no_mangle)]
473pub unsafe extern "C" fn LINKLAYER_PLAT_GetRNG(ptr_rnd: *mut u8, len: u32) {
474 // uint32_t nb_remaining_rng = len;
475 // uint32_t generated_rng;
476 //
477 // /* Get the requested RNGs (4 bytes by 4bytes) */
478 // while(nb_remaining_rng >= 4)
479 // {
480 // generated_rng = 0;
481 // HW_RNG_Get(1, &generated_rng);
482 // memcpy((ptr_rnd+(len-nb_remaining_rng)), &generated_rng, 4);
483 // nb_remaining_rng -=4;
484 // }
485 //
486 // /* Get the remaining number of RNGs */
487 // if(nb_remaining_rng>0){
488 // generated_rng = 0;
489 // HW_RNG_Get(1, &generated_rng);
490 // memcpy((ptr_rnd+(len-nb_remaining_rng)), &generated_rng, nb_remaining_rng);
491 // }
492 trace!("LINKLAYER_PLAT_GetRNG: ptr_rnd={:?}, len={}", ptr_rnd, len);
493 if ptr_rnd.is_null() || len == 0 {
494 return;
495 }
496
497 for i in 0..len {
498 let byte = (prng_next() >> ((i & 3) * 8)) as u8;
499 ptr::write_volatile(ptr_rnd.add(i as usize), byte);
500 }
501}
502
503// /**
504// * @brief Initialize Link Layer radio high priority interrupt.
505// * @param intr_cb: function pointer to assign for the radio high priority ISR routine.
506// * @retval None
507// */
508#[unsafe(no_mangle)]
509pub unsafe extern "C" fn LINKLAYER_PLAT_SetupRadioIT(intr_cb: Option<Callback>) {
510 trace!("LINKLAYER_PLAT_SetupRadioIT: intr_cb={:?}", intr_cb);
511 store_callback(&RADIO_CALLBACK, intr_cb);
512
513 if intr_cb.is_some() {
514 nvic_set_priority(mac::RADIO_INTR_NUM, pack_priority(mac::RADIO_INTR_PRIO_HIGH));
515 nvic_enable(mac::RADIO_INTR_NUM);
516 } else {
517 nvic_disable(mac::RADIO_INTR_NUM);
518 }
519}
520
521// /**
522// * @brief Initialize Link Layer SW low priority interrupt.
523// * @param intr_cb: function pointer to assign for the SW low priority ISR routine.
524// * @retval None
525// */
526#[unsafe(no_mangle)]
527pub unsafe extern "C" fn LINKLAYER_PLAT_SetupSwLowIT(intr_cb: Option<Callback>) {
528 trace!("LINKLAYER_PLAT_SetupSwLowIT: intr_cb={:?}", intr_cb);
529 store_callback(&LOW_ISR_CALLBACK, intr_cb);
530
531 if intr_cb.is_some() {
532 nvic_set_priority(RADIO_SW_LOW_INTR_NUM, pack_priority(mac::RADIO_SW_LOW_INTR_PRIO));
533 nvic_enable(RADIO_SW_LOW_INTR_NUM);
534 } else {
535 nvic_disable(RADIO_SW_LOW_INTR_NUM);
536 }
537}
538
539// /**
540// * @brief Trigger the link layer SW low interrupt.
541// * @param None
542// * @retval None
543// */
544#[unsafe(no_mangle)]
545pub unsafe extern "C" fn LINKLAYER_PLAT_TriggerSwLowIT(priority: u8) {
546 trace!("LINKLAYER_PLAT_TriggerSwLowIT: priority={}", priority);
547 let active = nvic_get_active(RADIO_SW_LOW_INTR_NUM);
548
549 // /* Check if a SW low interrupt as already been raised.
550 // * Nested call far radio low isr are not supported
551 // **/
552 if !active {
553 let prio = if priority == 0 {
554 // /* No nested SW low ISR, default behavior */
555 pack_priority(mac::RADIO_SW_LOW_INTR_PRIO)
556 } else {
557 pack_priority(mac::RADIO_INTR_PRIO_LOW)
558 };
559 nvic_set_priority(RADIO_SW_LOW_INTR_NUM, prio);
560 } else if priority != 0 {
561 // /* Nested call detected */
562 // /* No change for SW radio low interrupt priority for the moment */
563 //
564 // if(priority != 0)
565 // {
566 // /* At the end of current SW radio low ISR, this pending SW low interrupt
567 // * will run with RADIO_INTR_PRIO_LOW priority
568 // **/
569 // radio_sw_low_isr_is_running_high_prio = 1;
570 // }
571 RADIO_SW_LOW_ISR_RUNNING_HIGH_PRIO.store(true, Ordering::Release);
572 }
573
574 nvic_set_pending(RADIO_SW_LOW_INTR_NUM);
575}
576
577// /**
578// * @brief Enable interrupts.
579// * @param None
580// * @retval None
581// */
582#[unsafe(no_mangle)]
583pub unsafe extern "C" fn LINKLAYER_PLAT_EnableIRQ() {
584 trace!("LINKLAYER_PLAT_EnableIRQ");
585 // irq_counter = max(0,irq_counter-1);
586 //
587 // if(irq_counter == 0)
588 // {
589 // /* When irq_counter reaches 0, restore primask bit */
590 // __set_PRIMASK(primask_bit);
591 // }
592 if counter_release(&IRQ_COUNTER) {
593 // When the counter reaches zero, restore prior interrupt state using the captured token.
594 if let Some(token) = CS_RESTORE_STATE.take() {
595 critical_section::release(token);
596 }
597 }
598}
599
600// /**
601// * @brief Disable interrupts.
602// * @param None
603// * @retval None
604// */
605#[unsafe(no_mangle)]
606pub unsafe extern "C" fn LINKLAYER_PLAT_DisableIRQ() {
607 trace!("LINKLAYER_PLAT_DisableIRQ");
608 // if(irq_counter == 0)
609 // {
610 // /* Save primask bit at first interrupt disablement */
611 // primask_bit= __get_PRIMASK();
612 // }
613 // __disable_irq();
614 // irq_counter ++;
615 if counter_acquire(&IRQ_COUNTER) {
616 // Capture and disable using critical-section API on first disable.
617 CS_RESTORE_STATE = Some(critical_section::acquire());
618 }
619}
620
621// /**
622// * @brief Enable specific interrupt group.
623// * @param isr_type: mask for interrupt group to enable.
624// * This parameter can be one of the following:
625// * @arg LL_HIGH_ISR_ONLY: enable link layer high priority ISR.
626// * @arg LL_LOW_ISR_ONLY: enable link layer SW low priority ISR.
627// * @arg SYS_LOW_ISR: mask interrupts for all the other system ISR with
628// * lower priority that link layer SW low interrupt.
629// * @retval None
630// */
631#[unsafe(no_mangle)]
632pub unsafe extern "C" fn LINKLAYER_PLAT_EnableSpecificIRQ(isr_type: u8) {
633 trace!("LINKLAYER_PLAT_EnableSpecificIRQ: isr_type={}", isr_type);
634 // if( (isr_type & LL_HIGH_ISR_ONLY) != 0 )
635 // {
636 // prio_high_isr_counter--;
637 // if(prio_high_isr_counter == 0)
638 // {
639 // /* When specific counter for link layer high ISR reaches 0, interrupt is enabled */
640 // HAL_NVIC_EnableIRQ(RADIO_INTR_NUM);
641 // /* USER CODE BEGIN LINKLAYER_PLAT_EnableSpecificIRQ_1 */
642 //
643 // /* USER CODE END LINKLAYER_PLAT_EnableSpecificIRQ_1 */
644 // }
645 // }
646 //
647 // if( (isr_type & LL_LOW_ISR_ONLY) != 0 )
648 // {
649 // prio_low_isr_counter--;
650 // if(prio_low_isr_counter == 0)
651 // {
652 // /* When specific counter for link layer SW low ISR reaches 0, interrupt is enabled */
653 // HAL_NVIC_EnableIRQ(RADIO_SW_LOW_INTR_NUM);
654 // }
655 //
656 // }
657 //
658 // if( (isr_type & SYS_LOW_ISR) != 0 )
659 // {
660 // prio_sys_isr_counter--;
661 // if(prio_sys_isr_counter == 0)
662 // {
663 // /* Restore basepri value */
664 // __set_BASEPRI(local_basepri_value);
665 // }
666 // }
667 if (isr_type & link_layer::LL_HIGH_ISR_ONLY as u8) != 0 {
668 if counter_release(&PRIO_HIGH_ISR_COUNTER) {
669 nvic_enable(mac::RADIO_INTR_NUM);
670 }
671 }
672
673 if (isr_type & link_layer::LL_LOW_ISR_ONLY as u8) != 0 {
674 if counter_release(&PRIO_LOW_ISR_COUNTER) {
675 nvic_enable(RADIO_SW_LOW_INTR_NUM);
676 }
677 }
678
679 if (isr_type & link_layer::SYS_LOW_ISR as u8) != 0 {
680 if counter_release(&PRIO_SYS_ISR_COUNTER) {
681 let stored = LOCAL_BASEPRI_VALUE.load(Ordering::Relaxed) as u8;
682 basepri::write(stored);
683 }
684 }
685}
686
687// /**
688// * @brief Disable specific interrupt group.
689// * @param isr_type: mask for interrupt group to disable.
690// * This parameter can be one of the following:
691// * @arg LL_HIGH_ISR_ONLY: disable link layer high priority ISR.
692// * @arg LL_LOW_ISR_ONLY: disable link layer SW low priority ISR.
693// * @arg SYS_LOW_ISR: unmask interrupts for all the other system ISR with
694// * lower priority that link layer SW low interrupt.
695// * @retval None
696// */
697#[unsafe(no_mangle)]
698pub unsafe extern "C" fn LINKLAYER_PLAT_DisableSpecificIRQ(isr_type: u8) {
699 // if( (isr_type & LL_HIGH_ISR_ONLY) != 0 )
700 // {
701 // prio_high_isr_counter++;
702 // if(prio_high_isr_counter == 1)
703 // {
704 // /* USER CODE BEGIN LINKLAYER_PLAT_DisableSpecificIRQ_1 */
705 //
706 // /* USER CODE END LINKLAYER_PLAT_DisableSpecificIRQ_1 */
707 // /* When specific counter for link layer high ISR value is 1, interrupt is disabled */
708 // HAL_NVIC_DisableIRQ(RADIO_INTR_NUM);
709 // }
710 // }
711 //
712 // if( (isr_type & LL_LOW_ISR_ONLY) != 0 )
713 // {
714 // prio_low_isr_counter++;
715 // if(prio_low_isr_counter == 1)
716 // {
717 // /* When specific counter for link layer SW low ISR value is 1, interrupt is disabled */
718 // HAL_NVIC_DisableIRQ(RADIO_SW_LOW_INTR_NUM);
719 // }
720 // }
721 //
722 // if( (isr_type & SYS_LOW_ISR) != 0 )
723 // {
724 // prio_sys_isr_counter++;
725 // if(prio_sys_isr_counter == 1)
726 // {
727 // /* Save basepri register value */
728 // local_basepri_value = __get_BASEPRI();
729 //
730 // /* Mask all other interrupts with lower priority that link layer SW low ISR */
731 // __set_BASEPRI_MAX(RADIO_INTR_PRIO_LOW<<4);
732 // }
733 // }
734 trace!("LINKLAYER_PLAT_DisableSpecificIRQ: isr_type={}", isr_type);
735 if (isr_type & link_layer::LL_HIGH_ISR_ONLY as u8) != 0 {
736 if counter_acquire(&PRIO_HIGH_ISR_COUNTER) {
737 nvic_disable(mac::RADIO_INTR_NUM);
738 }
739 }
740
741 if (isr_type & link_layer::LL_LOW_ISR_ONLY as u8) != 0 {
742 if counter_acquire(&PRIO_LOW_ISR_COUNTER) {
743 nvic_disable(RADIO_SW_LOW_INTR_NUM);
744 }
745 }
746
747 if (isr_type & link_layer::SYS_LOW_ISR as u8) != 0 {
748 if counter_acquire(&PRIO_SYS_ISR_COUNTER) {
749 let current = basepri::read();
750 LOCAL_BASEPRI_VALUE.store(current.into(), Ordering::Relaxed);
751 set_basepri_max(pack_priority(mac::RADIO_INTR_PRIO_LOW));
752 }
753 }
754}
755
756// /**
757// * @brief Enable link layer high priority ISR only.
758// * @param None
759// * @retval None
760// */
761#[unsafe(no_mangle)]
762pub unsafe extern "C" fn LINKLAYER_PLAT_EnableRadioIT() {
763 trace!("LINKLAYER_PLAT_EnableRadioIT");
764 nvic_enable(mac::RADIO_INTR_NUM);
765}
766
767// /**
768// * @brief Disable link layer high priority ISR only.
769// * @param None
770// * @retval None
771// */
772#[unsafe(no_mangle)]
773pub unsafe extern "C" fn LINKLAYER_PLAT_DisableRadioIT() {
774 trace!("LINKLAYER_PLAT_DisableRadioIT");
775 nvic_disable(mac::RADIO_INTR_NUM);
776}
777
778// /**
779// * @brief Link Layer notification for radio activity start.
780// * @param None
781// * @retval None
782// */
783#[unsafe(no_mangle)]
784pub unsafe extern "C" fn LINKLAYER_PLAT_StartRadioEvt() {
785 trace!("LINKLAYER_PLAT_StartRadioEvt");
786 // __HAL_RCC_RADIO_CLK_SLEEP_ENABLE();
787 // NVIC_SetPriority(RADIO_INTR_NUM, RADIO_INTR_PRIO_HIGH);
788 // #if (CFG_SCM_SUPPORTED == 1)
789 // scm_notifyradiostate(SCM_RADIO_ACTIVE);
790 // #endif /* CFG_SCM_SUPPORTED */
791 nvic_set_priority(mac::RADIO_INTR_NUM, pack_priority(mac::RADIO_INTR_PRIO_HIGH));
792 nvic_enable(mac::RADIO_INTR_NUM);
793}
794
795// /**
796// * @brief Link Layer notification for radio activity end.
797// * @param None
798// * @retval None
799// */
800#[unsafe(no_mangle)]
801pub unsafe extern "C" fn LINKLAYER_PLAT_StopRadioEvt() {
802 trace!("LINKLAYER_PLAT_StopRadioEvt");
803 // {
804 // __HAL_RCC_RADIO_CLK_SLEEP_DISABLE();
805 // NVIC_SetPriority(RADIO_INTR_NUM, RADIO_INTR_PRIO_LOW);
806 // #if (CFG_SCM_SUPPORTED == 1)
807 // scm_notifyradiostate(SCM_RADIO_NOT_ACTIVE);
808 // #endif /* CFG_SCM_SUPPORTED */
809 nvic_set_priority(mac::RADIO_INTR_NUM, pack_priority(mac::RADIO_INTR_PRIO_LOW));
810}
811
812// /**
813// * @brief Link Layer notification for RCO calibration start.
814// * @param None
815// * @retval None
816// */
817#[unsafe(no_mangle)]
818pub unsafe extern "C" fn LINKLAYER_PLAT_RCOStartClbr() {
819 trace!("LINKLAYER_PLAT_RCOStartClbr");
820 // #if (CFG_LPM_LEVEL != 0)
821 // PWR_DisableSleepMode();
822 // /* Disabling stop mode prevents also from entering in standby */
823 // UTIL_LPM_SetStopMode(1U << CFG_LPM_LL_HW_RCO_CLBR, UTIL_LPM_DISABLE);
824 // #endif /* (CFG_LPM_LEVEL != 0) */
825 // #if (CFG_SCM_SUPPORTED == 1)
826 // scm_setsystemclock(SCM_USER_LL_HW_RCO_CLBR, HSE_32MHZ);
827 // while (LL_PWR_IsActiveFlag_VOS() == 0);
828 // #endif /* (CFG_SCM_SUPPORTED == 1) */
829}
830
831// /**
832// * @brief Link Layer notification for RCO calibration end.
833// * @param None
834// * @retval None
835// */
836#[unsafe(no_mangle)]
837pub unsafe extern "C" fn LINKLAYER_PLAT_RCOStopClbr() {
838 trace!("LINKLAYER_PLAT_RCOStopClbr");
839 // #if (CFG_LPM_LEVEL != 0)
840 // PWR_EnableSleepMode();
841 // UTIL_LPM_SetStopMode(1U << CFG_LPM_LL_HW_RCO_CLBR, UTIL_LPM_ENABLE);
842 // #endif /* (CFG_LPM_LEVEL != 0) */
843 // #if (CFG_SCM_SUPPORTED == 1)
844 // scm_setsystemclock(SCM_USER_LL_HW_RCO_CLBR, HSE_16MHZ);
845 // #endif /* (CFG_SCM_SUPPORTED == 1) */
846}
847
848// /**
849// * @brief Link Layer requests temperature.
850// * @param None
851// * @retval None
852// */
853#[unsafe(no_mangle)]
854pub unsafe extern "C" fn LINKLAYER_PLAT_RequestTemperature() {
855 trace!("LINKLAYER_PLAT_RequestTemperature");
856 // #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)
857 // ll_sys_bg_temperature_measurement();
858 // #endif /* USE_TEMPERATURE_BASED_RADIO_CALIBRATION */
859}
860
861// /**
862// * @brief PHY Start calibration.
863// * @param None
864// * @retval None
865// */
866#[unsafe(no_mangle)]
867pub unsafe extern "C" fn LINKLAYER_PLAT_PhyStartClbr() {
868 trace!("LINKLAYER_PLAT_PhyStartClbr");
869}
870
871// /**
872// * @brief PHY Stop calibration.
873// * @param None
874// * @retval None
875// */
876#[unsafe(no_mangle)]
877pub unsafe extern "C" fn LINKLAYER_PLAT_PhyStopClbr() {
878 trace!("LINKLAYER_PLAT_PhyStopClbr");
879}
880
881// /**
882// * @brief Notify the upper layer that new Link Layer timings have been applied.
883// * @param evnt_timing[in]: Evnt_timing_t pointer to structure contains drift time , execution time and scheduling time
884// * @retval None.
885// */
886#[unsafe(no_mangle)]
887pub unsafe extern "C" fn LINKLAYER_PLAT_SCHLDR_TIMING_UPDATE_NOT(_timings: *const link_layer::Evnt_timing_t) {
888 trace!("LINKLAYER_PLAT_SCHLDR_TIMING_UPDATE_NOT: timings={:?}", _timings);
889}
890
891// /**
892// * @brief Get the ST company ID.
893// * @param None
894// * @retval Company ID
895// */
896#[unsafe(no_mangle)]
897pub unsafe extern "C" fn LINKLAYER_PLAT_GetSTCompanyID() -> u32 {
898 // STMicroelectronics Bluetooth SIG Company Identifier
899 // TODO: Pull in update from latest stm32-generated-data
900 0x0030
901}
902
903// /**
904// * @brief Get the Unique Device Number (UDN).
905// * @param None
906// * @retval UDN
907// */
908#[unsafe(no_mangle)]
909pub unsafe extern "C" fn LINKLAYER_PLAT_GetUDN() -> u32 {
910 // Read the first 32 bits of the STM32 unique 96-bit ID
911 let uid = embassy_stm32::uid::uid();
912 u32::from_le_bytes([uid[0], uid[1], uid[2], uid[3]])
913}
diff --git a/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_cs.rs b/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_cs.rs
new file mode 100644
index 000000000..30103ba27
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_cs.rs
@@ -0,0 +1,77 @@
1use crate::bindings::link_layer::{
2 LINKLAYER_PLAT_DisableIRQ, LINKLAYER_PLAT_DisableSpecificIRQ, LINKLAYER_PLAT_EnableIRQ,
3 LINKLAYER_PLAT_EnableSpecificIRQ, LINKLAYER_PLAT_PhyStartClbr, LINKLAYER_PLAT_PhyStopClbr,
4};
5
6// /**
7// ******************************************************************************
8// * @file ll_sys_cs.c
9// * @author MCD Application Team
10// * @brief Link Layer IP system interface critical sections management
11// ******************************************************************************
12// * @attention
13// *
14// * Copyright (c) 2022 STMicroelectronics.
15// * All rights reserved.
16// *
17// * This software is licensed under terms that can be found in the LICENSE file
18// * in the root directory of this software component.
19// * If no LICENSE file comes with this software, it is provided AS-IS.
20// *
21// ******************************************************************************
22// */
23//
24// #include "linklayer_plat.h"
25// #include "ll_sys.h"
26// #include <stdint.h>
27//
28/**
29 * @brief Enable interrupts
30 * @param None
31 * @retval None
32 */
33#[unsafe(no_mangle)]
34unsafe extern "C" fn ll_sys_enable_irq() {
35 LINKLAYER_PLAT_EnableIRQ();
36}
37//
38// /**
39// * @brief Disable interrupts
40// * @param None
41// * @retval None
42// */
43#[unsafe(no_mangle)]
44unsafe extern "C" fn ll_sys_disable_irq() {
45 LINKLAYER_PLAT_DisableIRQ();
46}
47//
48// /**
49// * @brief Set the Current Interrupt Priority Mask.
50// * All interrupts with low priority level will be masked.
51// * @param None
52// * @retval None
53// */
54#[unsafe(no_mangle)]
55unsafe extern "C" fn ll_sys_enable_specific_irq(isr_type: u8) {
56 LINKLAYER_PLAT_EnableSpecificIRQ(isr_type);
57}
58//
59// /**
60// * @brief Restore the previous interrupt priority level
61// * @param None
62// * @retval None
63// */
64#[unsafe(no_mangle)]
65unsafe extern "C" fn ll_sys_disable_specific_irq(isr_type: u8) {
66 LINKLAYER_PLAT_DisableSpecificIRQ(isr_type);
67}
68//
69#[unsafe(no_mangle)]
70unsafe extern "C" fn ll_sys_phy_start_clbr() {
71 LINKLAYER_PLAT_PhyStartClbr();
72}
73//
74#[unsafe(no_mangle)]
75unsafe extern "C" fn ll_sys_phy_stop_clbr() {
76 LINKLAYER_PLAT_PhyStopClbr();
77}
diff --git a/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_dp_slp.rs b/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_dp_slp.rs
new file mode 100644
index 000000000..ae8223a5a
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_dp_slp.rs
@@ -0,0 +1,163 @@
1use crate::bindings::link_layer::{
2 _NULL as NULL, DPSLP_STATE_DEEP_SLEEP_DISABLE, DPSLP_STATE_DEEP_SLEEP_ENABLE, LINKLAYER_PLAT_DisableRadioIT,
3 LINKLAYER_PLAT_EnableRadioIT, LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_DISABLED,
4 LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_ENABLED, LL_SYS_STATUS_T_LL_SYS_ERROR, LL_SYS_STATUS_T_LL_SYS_OK,
5 OS_TIMER_PRIO_HG_PRIO_TMR, OS_TIMER_STATE_OSTIMERSTOPPED, OS_TIMER_TYPE_OS_TIMER_ONCE, SUCCESS, ble_stat_t,
6 ll_intf_cmn_le_set_dp_slp_mode, ll_sys_dp_slp_state_t, ll_sys_status_t, os_get_tmr_state, os_timer_create,
7 os_timer_id, os_timer_set_prio, os_timer_start, os_timer_stop,
8};
9
10macro_rules! LL_DP_SLP_NO_WAKEUP {
11 () => {
12 !0u32
13 };
14}
15
16macro_rules! LL_INTERNAL_TMR_US_TO_STEPS {
17 ($us:expr) => {
18 ((($us) * 4) / 125)
19 };
20}
21
22// /**
23// ******************************************************************************
24// * @file ll_sys_dp_slp.c
25// * @author MCD Application Team
26// * @brief Link Layer IP system interface deep sleep management
27// ******************************************************************************
28// * @attention
29// *
30// * Copyright (c) 2022 STMicroelectronics.
31// * All rights reserved.
32// *
33// * This software is licensed under terms that can be found in the LICENSE file
34// * in the root directory of this software component.
35// * If no LICENSE file comes with this software, it is provided AS-IS.
36// *
37// ******************************************************************************
38// */
39//
40// #include "linklayer_plat.h"
41// #include "ll_sys.h"
42// #include "ll_intf_cmn.h"
43//
44// /* Link Layer deep sleep timer */
45static mut RADIO_DP_SLP_TMR_ID: os_timer_id = NULL as *mut _;
46//
47// /* Link Layer deep sleep state */
48static mut LINKLAYER_DP_SLP_STATE: ll_sys_dp_slp_state_t = LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_DISABLED;
49//
50// /**
51// * @brief Initialize resources to handle deep sleep entry/exit
52// * @param None
53// * @retval LL_SYS status
54// */
55#[unsafe(no_mangle)]
56unsafe extern "C" fn ll_sys_dp_slp_init() -> ll_sys_status_t {
57 let mut return_status: ll_sys_status_t = LL_SYS_STATUS_T_LL_SYS_ERROR;
58
59 /* Create link layer timer for handling IP DEEP SLEEP mode */
60 RADIO_DP_SLP_TMR_ID = os_timer_create(
61 Some(ll_sys_dp_slp_wakeup_evt_clbk),
62 OS_TIMER_TYPE_OS_TIMER_ONCE,
63 NULL as *mut _,
64 );
65
66 /* Set priority of deep sleep timer */
67 os_timer_set_prio(RADIO_DP_SLP_TMR_ID, OS_TIMER_PRIO_HG_PRIO_TMR);
68
69 if RADIO_DP_SLP_TMR_ID != NULL as *mut _ {
70 return_status = LL_SYS_STATUS_T_LL_SYS_OK;
71 }
72
73 return return_status;
74}
75//
76// /**
77// * @brief Link Layer deep sleep status getter
78// * @param None
79// * @retval Link Layer deep sleep state
80// */
81#[unsafe(no_mangle)]
82unsafe extern "C" fn ll_sys_dp_slp_get_state() -> ll_sys_dp_slp_state_t {
83 return LINKLAYER_DP_SLP_STATE;
84}
85//
86// /**
87// * @brief The Link Layer IP enters deep sleep mode
88// * @param dp_slp_duration deep sleep duration in us
89// * @retval LL_SYS status
90// */
91#[unsafe(no_mangle)]
92unsafe extern "C" fn ll_sys_dp_slp_enter(dp_slp_duration: u32) -> ll_sys_status_t {
93 let cmd_status: ble_stat_t;
94 let os_status: i32;
95 let mut return_status: ll_sys_status_t = LL_SYS_STATUS_T_LL_SYS_ERROR;
96
97 /* Check if deep sleep timer has to be started */
98 if dp_slp_duration < LL_DP_SLP_NO_WAKEUP!() {
99 /* Start deep sleep timer */
100 os_status = os_timer_start(RADIO_DP_SLP_TMR_ID, LL_INTERNAL_TMR_US_TO_STEPS!(dp_slp_duration));
101 } else {
102 /* No timer started */
103 os_status = SUCCESS as i32;
104 }
105
106 if os_status == SUCCESS as i32 {
107 /* Switch Link Layer IP to DEEP SLEEP mode */
108 cmd_status = ll_intf_cmn_le_set_dp_slp_mode(DPSLP_STATE_DEEP_SLEEP_ENABLE as u8);
109 if cmd_status == SUCCESS {
110 LINKLAYER_DP_SLP_STATE = LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_ENABLED;
111 return_status = LL_SYS_STATUS_T_LL_SYS_OK;
112 }
113 }
114
115 return return_status;
116}
117//
118// /**
119// * @brief The Link Layer IP exits deep sleep mode
120// * @param None
121// * @retval LL_SYS status
122// */
123#[unsafe(no_mangle)]
124unsafe extern "C" fn ll_sys_dp_slp_exit() -> ll_sys_status_t {
125 let cmd_status: ble_stat_t;
126 let mut return_status: ll_sys_status_t = LL_SYS_STATUS_T_LL_SYS_ERROR;
127
128 /* Disable radio interrupt */
129 LINKLAYER_PLAT_DisableRadioIT();
130
131 if LINKLAYER_DP_SLP_STATE == LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_DISABLED {
132 /* Radio not in sleep mode */
133 return_status = LL_SYS_STATUS_T_LL_SYS_OK;
134 } else {
135 /* Switch Link Layer IP to SLEEP mode (by deactivate DEEP SLEEP mode) */
136 cmd_status = ll_intf_cmn_le_set_dp_slp_mode(DPSLP_STATE_DEEP_SLEEP_DISABLE as u8);
137 if cmd_status == SUCCESS {
138 LINKLAYER_DP_SLP_STATE = LL_SYS_DP_SLP_STATE_T_LL_SYS_DP_SLP_DISABLED;
139 return_status = LL_SYS_STATUS_T_LL_SYS_OK;
140 }
141
142 /* Stop the deep sleep wake-up timer if running */
143 if os_get_tmr_state(RADIO_DP_SLP_TMR_ID) != OS_TIMER_STATE_OSTIMERSTOPPED {
144 os_timer_stop(RADIO_DP_SLP_TMR_ID);
145 }
146 }
147
148 /* Re-enable radio interrupt */
149 LINKLAYER_PLAT_EnableRadioIT();
150
151 return return_status;
152}
153
154/**
155 * @brief Link Layer deep sleep wake-up timer callback
156 * @param ptr_arg pointer passed through the callback
157 * @retval LL_SYS status
158 */
159#[unsafe(no_mangle)]
160unsafe extern "C" fn ll_sys_dp_slp_wakeup_evt_clbk(_ptr_arg: *const ::core::ffi::c_void) {
161 /* Link Layer IP exits from DEEP SLEEP mode */
162 ll_sys_dp_slp_exit();
163}
diff --git a/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_intf.rs b/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_intf.rs
new file mode 100644
index 000000000..0b4b0b37f
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_intf.rs
@@ -0,0 +1,199 @@
1use crate::bindings::link_layer::{
2 Evnt_timing_t, HostStack_Process, LINKLAYER_PLAT_AclkCtrl, LINKLAYER_PLAT_Assert, LINKLAYER_PLAT_ClockInit,
3 LINKLAYER_PLAT_DelayUs, LINKLAYER_PLAT_GetRNG, LINKLAYER_PLAT_RCOStartClbr, LINKLAYER_PLAT_RCOStopClbr,
4 LINKLAYER_PLAT_RequestTemperature, LINKLAYER_PLAT_SCHLDR_TIMING_UPDATE_NOT, LINKLAYER_PLAT_SetupRadioIT,
5 LINKLAYER_PLAT_SetupSwLowIT, LINKLAYER_PLAT_StartRadioEvt, LINKLAYER_PLAT_StopRadioEvt,
6 LINKLAYER_PLAT_TriggerSwLowIT, LINKLAYER_PLAT_WaitHclkRdy, MAX_NUM_CNCRT_STAT_MCHNS, emngr_can_mcu_sleep,
7 emngr_handle_all_events, ll_sys_schedule_bg_process,
8};
9
10// /**
11// ******************************************************************************
12// * @file ll_sys_intf.c
13// * @author MCD Application Team
14// * @brief Link Layer IP general system interface
15// ******************************************************************************
16// * @attention
17// *
18// * Copyright (c) 2022 STMicroelectronics.
19// * All rights reserved.
20// *
21// * This software is licensed under terms that can be found in the LICENSE file
22// * in the root directory of this software component.
23// * If no LICENSE file comes with this software, it is provided AS-IS.
24// *
25// ******************************************************************************
26// */
27// #include <stdint.h>
28//
29// #include "ll_sys.h"
30// #include "linklayer_plat.h"
31// #include "event_manager.h"
32// #include "ll_intf.h"
33//
34/**
35 * @brief Initialize the Link Layer SoC dependencies
36 * @param None
37 * @retval None
38 */
39#[unsafe(no_mangle)]
40unsafe extern "C" fn ll_sys_init() {
41 LINKLAYER_PLAT_ClockInit();
42}
43//
44/**
45 * @brief Blocking delay in us
46 * @param None
47 * @retval None
48 */
49#[unsafe(no_mangle)]
50unsafe extern "C" fn ll_sys_delay_us(delay: u32) {
51 LINKLAYER_PLAT_DelayUs(delay);
52}
53
54/**
55 * @brief Assert checking
56 * @param None
57 * @retval None
58 */
59#[unsafe(no_mangle)]
60unsafe extern "C" fn ll_sys_assert(condition: u8) {
61 LINKLAYER_PLAT_Assert(condition);
62}
63
64/**
65 * @brief Radio active clock management
66 * @param None
67 * @retval None
68 */
69#[unsafe(no_mangle)]
70unsafe extern "C" fn ll_sys_radio_ack_ctrl(enable: u8) {
71 LINKLAYER_PLAT_AclkCtrl(enable);
72}
73
74/**
75 * @brief Link Layer waits for radio bus clock ready
76 * @param None
77 * @retval None
78 */
79#[unsafe(no_mangle)]
80unsafe extern "C" fn ll_sys_radio_wait_for_busclkrdy() {
81 LINKLAYER_PLAT_WaitHclkRdy();
82}
83
84/**
85 * @brief Get RNG number for the Link Layer IP
86 * @param None
87 * @retval None
88 */
89#[unsafe(no_mangle)]
90unsafe extern "C" fn ll_sys_get_rng(ptr_rnd: *mut u8, len: u32) {
91 LINKLAYER_PLAT_GetRNG(ptr_rnd, len);
92}
93
94/**
95 * @brief Initialize the main radio interrupt
96 * @param intr_cb radio interrupt callback to link with the radio IRQ
97 * @retval None
98 */
99#[unsafe(no_mangle)]
100unsafe extern "C" fn ll_sys_setup_radio_intr(intr_cb: ::core::option::Option<unsafe extern "C" fn()>) {
101 LINKLAYER_PLAT_SetupRadioIT(intr_cb);
102}
103
104/**
105 * @brief Initialize the radio SW low interrupt
106 * @param intr_cb radio SW low interrupt interrupt callback to link
107 * with the defined interrupt vector
108 * @retval None
109 */
110#[unsafe(no_mangle)]
111unsafe extern "C" fn ll_sys_setup_radio_sw_low_intr(intr_cb: ::core::option::Option<unsafe extern "C" fn()>) {
112 LINKLAYER_PLAT_SetupSwLowIT(intr_cb);
113}
114
115/**
116 * @brief Trigger the radio SW low interrupt
117 * @param None
118 * @retval None
119 */
120#[unsafe(no_mangle)]
121unsafe extern "C" fn ll_sys_radio_sw_low_intr_trigger(priority: u8) {
122 LINKLAYER_PLAT_TriggerSwLowIT(priority);
123}
124
125/**
126 * @brief Link Layer radio activity event notification
127 * @param start start/end of radio event
128 * @retval None
129 */
130#[unsafe(no_mangle)]
131unsafe extern "C" fn ll_sys_radio_evt_not(start: u8) {
132 if start != 0 {
133 LINKLAYER_PLAT_StartRadioEvt();
134 } else {
135 LINKLAYER_PLAT_StopRadioEvt();
136 }
137}
138
139/**
140 * @brief Link Layer RCO calibration notification
141 * @param start start/end of RCO calibration
142 * @retval None
143 */
144#[unsafe(no_mangle)]
145unsafe extern "C" fn ll_sys_rco_clbr_not(start: u8) {
146 if start != 0 {
147 LINKLAYER_PLAT_RCOStartClbr();
148 } else {
149 LINKLAYER_PLAT_RCOStopClbr();
150 }
151}
152
153/**
154 * @brief Link Layer temperature request
155 * @param None
156 * @retval None
157 */
158#[unsafe(no_mangle)]
159unsafe extern "C" fn ll_sys_request_temperature() {
160 LINKLAYER_PLAT_RequestTemperature();
161}
162
163/**
164 * @brief Link Layer background task pcoessing procedure
165 * @param None
166 * @retval None
167 */
168#[unsafe(no_mangle)]
169unsafe extern "C" fn ll_sys_bg_process() {
170 if emngr_can_mcu_sleep() == 0 {
171 emngr_handle_all_events();
172
173 HostStack_Process();
174 }
175
176 if emngr_can_mcu_sleep() == 0 {
177 ll_sys_schedule_bg_process();
178 }
179}
180
181#[unsafe(no_mangle)]
182unsafe extern "C" fn ll_sys_schldr_timing_update_not(p_evnt_timing: *mut Evnt_timing_t) {
183 LINKLAYER_PLAT_SCHLDR_TIMING_UPDATE_NOT(p_evnt_timing);
184}
185
186/**
187 * @brief Get the number of concurrent state machines for the Link Layer
188 * @param None
189 * @retval Supported number of concurrent state machines
190 */
191#[unsafe(no_mangle)]
192unsafe extern "C" fn ll_sys_get_concurrent_state_machines_num() -> u8 {
193 return MAX_NUM_CNCRT_STAT_MCHNS as u8;
194}
195//
196// __WEAK void HostStack_Process(void)
197// {
198//
199// }
diff --git a/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_startup.rs b/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_startup.rs
new file mode 100644
index 000000000..074aaeafe
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/ll_sys/ll_sys_startup.rs
@@ -0,0 +1,125 @@
1use crate::bindings::link_layer::{
2 _NULL as NULL, LL_SYS_STATUS_T_LL_SYS_OK, ble_buff_hdr_p, hci_dispatch_tbl, hci_get_dis_tbl, hst_cbk, ll_intf_init,
3 ll_intf_rgstr_hst_cbk, ll_intf_rgstr_hst_cbk_ll_queue_full, ll_sys_assert, ll_sys_bg_process_init,
4 ll_sys_config_params, ll_sys_dp_slp_init, ll_sys_status_t,
5};
6use crate::bindings::mac::ST_MAC_preInit;
7// /**
8// ******************************************************************************
9// * @file ll_sys_startup.c
10// * @author MCD Application Team
11// * @brief Link Layer IP system interface startup module
12// ******************************************************************************
13// * @attention
14// *
15// * Copyright (c) 2022 STMicroelectronics.
16// * All rights reserved.
17// *
18// * This software is licensed under terms that can be found in the LICENSE file
19// * in the root directory of this software component.
20// * If no LICENSE file comes with this software, it is provided AS-IS.
21// *
22// ******************************************************************************
23// */
24//
25// #include "ll_fw_config.h"
26// #include "ll_sys.h"
27// #include "ll_intf.h"
28// #include "ll_sys_startup.h"
29// #include "common_types.h"
30// #if defined(MAC)
31// #ifndef OPENTHREAD_CONFIG_FILE
32// /* Projects with MAC Layer (i.e. 15.4 except Thread) */
33// #include "st_mac_802_15_4_sap.h"
34// #endif /* OPENTHREAD_CONFIG_FILE */
35// #endif /* MAC */
36//
37
38#[allow(dead_code)]
39/**
40 * @brief Missed HCI event flag
41 */
42static mut MISSED_HCI_EVENT_FLAG: u8 = 0;
43
44// static void ll_sys_dependencies_init(void);
45// #if SUPPORT_BLE
46
47#[cfg(feature = "wba_ble")]
48#[allow(dead_code)]
49unsafe extern "C" fn ll_sys_event_missed_cb(_ptr_evnt_hdr: ble_buff_hdr_p) {
50 MISSED_HCI_EVENT_FLAG = 1;
51}
52
53#[cfg(feature = "wba_ble")]
54/**
55 * @brief Initialize the Link Layer IP BLE controller
56 * @param None
57 * @retval None
58 */
59#[unsafe(no_mangle)]
60unsafe extern "C" fn ll_sys_ble_cntrl_init(host_callback: hst_cbk) {
61 let p_hci_dis_tbl: *const hci_dispatch_tbl = NULL as *const _;
62
63 hci_get_dis_tbl(&p_hci_dis_tbl as *const *const _ as *mut *const _);
64
65 ll_intf_init(p_hci_dis_tbl);
66
67 ll_intf_rgstr_hst_cbk(host_callback);
68
69 ll_intf_rgstr_hst_cbk_ll_queue_full(Some(ll_sys_event_missed_cb));
70
71 ll_sys_dependencies_init();
72}
73// #endif /* SUPPORT_BLE */
74// #if defined(MAC)
75// #ifndef OPENTHREAD_CONFIG_FILE
76#[cfg(feature = "wba_mac")]
77/**
78 * @brief Initialize the Link Layer IP 802.15.4 MAC controller
79 * @param None
80 * @retval None
81 */
82#[unsafe(no_mangle)]
83unsafe extern "C" fn ll_sys_mac_cntrl_init() {
84 ST_MAC_preInit();
85 ll_sys_dependencies_init();
86}
87// #endif /* OPENTHREAD_CONFIG_FILE */
88// #endif /* MAC */
89/**
90 * @brief Start the Link Layer IP in OpenThread configuration
91 * @param None
92 * @retval None
93 */
94#[unsafe(no_mangle)]
95unsafe extern "C" fn ll_sys_thread_init() {
96 ll_sys_dependencies_init();
97}
98
99/**
100 * @brief Initialize the Link Layer resources for startup.
101 * This includes: - Deep Sleep feature resources
102 * - Link Layer background task
103 * @param None
104 * @retval None
105 */
106unsafe fn ll_sys_dependencies_init() {
107 static mut IS_LL_INITIALIZED: u8 = 0;
108 let dp_slp_status: ll_sys_status_t;
109
110 /* Ensure Link Layer resources are created only once */
111 if IS_LL_INITIALIZED == 1 {
112 return;
113 }
114 IS_LL_INITIALIZED = 1;
115
116 /* Deep sleep feature initialization */
117 dp_slp_status = ll_sys_dp_slp_init();
118 ll_sys_assert((dp_slp_status == LL_SYS_STATUS_T_LL_SYS_OK) as u8);
119
120 /* Background task initialization */
121 ll_sys_bg_process_init();
122
123 /* Link Layer user parameters application */
124 ll_sys_config_params();
125}
diff --git a/embassy-stm32-wpan/src/wba/ll_sys/ll_version.rs b/embassy-stm32-wpan/src/wba/ll_sys/ll_version.rs
new file mode 100644
index 000000000..a42e8cc67
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/ll_sys/ll_version.rs
@@ -0,0 +1,115 @@
1use crate::bindings::link_layer::{
2 LL_SYS_BRIEF_VERSION_MAJOR, LL_SYS_BRIEF_VERSION_MAJOR_MASK, LL_SYS_BRIEF_VERSION_MAJOR_POS,
3 LL_SYS_BRIEF_VERSION_MINOR, LL_SYS_BRIEF_VERSION_MINOR_MASK, LL_SYS_BRIEF_VERSION_MINOR_POS,
4 LL_SYS_BRIEF_VERSION_PATCH, LL_SYS_BRIEF_VERSION_PATCH_MASK, LL_SYS_BRIEF_VERSION_PATCH_POS,
5};
6
7// /**
8// ******************************************************************************
9// * @file ll_version.c
10// * @author MCD Application Team
11// * @brief Link Layer version interface
12// ******************************************************************************
13// * @attention
14// *
15// * Copyright (c) 2025 STMicroelectronics.
16// * All rights reserved.
17// *
18// * This software is licensed under terms that can be found in the LICENSE file
19// * in the root directory of this software component.
20// * If no LICENSE file comes with this software, it is provided AS-IS.
21// *
22// ******************************************************************************
23// */
24//
25// /* Includes ------------------------------------------------------------------*/
26// /* Integer types */
27// #include <stdint.h>
28//
29// /* Own header file */
30// #include "ll_version.h"
31//
32// /* Temporary header file for version tracking */
33// #include "ll_tmp_version.h"
34//
35// /* Private defines -----------------------------------------------------------*/
36// /**
37// * @brief Magic keyword to identify the system version when debugging
38// */
39// #define LL_SYS_MAGIC_KEYWORD 0xDEADBEEF
40
41const LL_SYS_MAGIC_KEYWORD: u32 = 0xDEADBEEF;
42
43//
44// /* Private macros ------------------------------------------------------------*/
45// /* Macro to set a specific field value */
46// #define LL_SYS_SET_FIELD_VALUE(value, mask, pos) \
47// (((value) << (pos)) & (mask))
48
49macro_rules! LL_SYS_SET_FIELD_VALUE {
50 ($value:expr, $mask:expr, $pos:expr) => {
51 ((($value) << ($pos)) & ($mask))
52 };
53}
54
55//
56// /* Private typedef -----------------------------------------------------------*/
57// /**
58// * @brief Link Layer system version structure definition
59// */
60#[allow(non_camel_case_types)]
61struct ll_sys_version_t {
62 #[allow(unused)]
63 magic_key_word: u32, /* Magic key word to identify the system version */
64 version: u32, /* System version - i.e.: short hash of latest commit */
65}
66//
67// /* Private variables ---------------------------------------------------------*/
68// /**
69// * @brief Link Layer brief version definition
70// */
71const LL_SYS_BRIEF_VERSION: u8 = LL_SYS_SET_FIELD_VALUE!(
72 LL_SYS_BRIEF_VERSION_MAJOR as u8,
73 LL_SYS_BRIEF_VERSION_MAJOR_MASK as u8,
74 LL_SYS_BRIEF_VERSION_MAJOR_POS as u8
75) | LL_SYS_SET_FIELD_VALUE!(
76 LL_SYS_BRIEF_VERSION_MINOR as u8,
77 LL_SYS_BRIEF_VERSION_MINOR_MASK as u8,
78 LL_SYS_BRIEF_VERSION_MINOR_POS as u8
79) | LL_SYS_SET_FIELD_VALUE!(
80 LL_SYS_BRIEF_VERSION_PATCH as u8,
81 LL_SYS_BRIEF_VERSION_PATCH_MASK as u8,
82 LL_SYS_BRIEF_VERSION_PATCH_POS as u8
83);
84//
85// /**
86// * @brief Link Layer system version structure definition
87// */
88const LL_SYS_SYSTEM_VERSION: ll_sys_version_t = ll_sys_version_t {
89 magic_key_word: LL_SYS_MAGIC_KEYWORD,
90 version: 0, // LL_SYS_SYSTEM_VERSION,
91};
92//
93// /**
94// * @brief Link Layer source version structure definition
95// */
96const LL_SYS_SOURCE_VERSION: ll_sys_version_t = ll_sys_version_t {
97 magic_key_word: LL_SYS_MAGIC_KEYWORD,
98 version: 0, // LL_SYS_SOURCE_VERSION
99};
100//
101// /* Functions Definition ------------------------------------------------------*/
102#[unsafe(no_mangle)]
103unsafe extern "C" fn ll_sys_get_brief_fw_version() -> u8 {
104 return LL_SYS_BRIEF_VERSION;
105}
106
107#[unsafe(no_mangle)]
108unsafe extern "C" fn ll_sys_get_system_fw_version() -> u32 {
109 return LL_SYS_SYSTEM_VERSION.version;
110}
111
112#[unsafe(no_mangle)]
113unsafe extern "C" fn ll_sys_get_source_fw_version() -> u32 {
114 return LL_SYS_SOURCE_VERSION.version;
115}
diff --git a/embassy-stm32-wpan/src/wba/ll_sys/mod.rs b/embassy-stm32-wpan/src/wba/ll_sys/mod.rs
new file mode 100644
index 000000000..45e196c96
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/ll_sys/mod.rs
@@ -0,0 +1,5 @@
1mod ll_sys_cs;
2mod ll_sys_dp_slp;
3mod ll_sys_intf;
4mod ll_sys_startup;
5mod ll_version;
diff --git a/embassy-stm32-wpan/src/wba/ll_sys_if.rs b/embassy-stm32-wpan/src/wba/ll_sys_if.rs
new file mode 100644
index 000000000..7218b69c4
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/ll_sys_if.rs
@@ -0,0 +1,416 @@
1#![cfg(feature = "wba")]
2// /* USER CODE BEGIN Header */
3// /**
4// ******************************************************************************
5// * @file ll_sys_if.c
6// * @author MCD Application Team
7// * @brief Source file for initiating system
8// ******************************************************************************
9// * @attention
10// *
11// * Copyright (c) 2022 STMicroelectronics.
12// * All rights reserved.
13// *
14// * This software is licensed under terms that can be found in the LICENSE file
15// * in the root directory of this software component.
16// * If no LICENSE file comes with this software, it is provided AS-IS.
17// *
18// ******************************************************************************
19// */
20// /* USER CODE END Header */
21//
22// #include "main.h"
23// #include "app_common.h"
24// #include "app_conf.h"
25// #include "log_module.h"
26// #include "ll_intf_cmn.h"
27// #include "ll_sys.h"
28// #include "ll_sys_if.h"
29// #include "stm32_rtos.h"
30// #include "utilities_common.h"
31// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)
32// #include "temp_measurement.h"
33// #endif /* (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1) */
34// #if (CFG_LPM_STANDBY_SUPPORTED == 0)
35// extern void profile_reset(void);
36// #endif
37// /* Private defines -----------------------------------------------------------*/
38// /* Radio event scheduling method - must be set at 1 */
39// #define USE_RADIO_LOW_ISR (1)
40// #define NEXT_EVENT_SCHEDULING_FROM_ISR (1)
41//
42// /* USER CODE BEGIN PD */
43//
44// /* USER CODE END PD */
45//
46// /* Private macros ------------------------------------------------------------*/
47// /* USER CODE BEGIN PM */
48//
49// /* USER CODE END PM */
50//
51// /* Private constants ---------------------------------------------------------*/
52// /* USER CODE BEGIN PC */
53//
54// /* USER CODE END PC */
55//
56// /* Private variables ---------------------------------------------------------*/
57// /* USER CODE BEGIN PV */
58//
59// /* USER CODE END PV */
60//
61// /* Global variables ----------------------------------------------------------*/
62//
63// /* USER CODE BEGIN GV */
64//
65// /* USER CODE END GV */
66//
67// /* Private functions prototypes-----------------------------------------------*/
68// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)
69// static void ll_sys_bg_temperature_measurement_init(void);
70// #endif /* USE_TEMPERATURE_BASED_RADIO_CALIBRATION */
71// static void ll_sys_sleep_clock_source_selection(void);
72// static uint8_t ll_sys_BLE_sleep_clock_accuracy_selection(void);
73// void ll_sys_reset(void);
74//
75// /* USER CODE BEGIN PFP */
76//
77// /* USER CODE END PFP */
78//
79// /* External variables --------------------------------------------------------*/
80//
81// /* USER CODE BEGIN EV */
82//
83// /* USER CODE END EV */
84//
85// /* Functions Definition ------------------------------------------------------*/
86//
87// /**
88// * @brief Link Layer background process initialization
89// * @param None
90// * @retval None
91// */
92// void ll_sys_bg_process_init(void)
93// {
94// /* Register Link Layer task */
95// UTIL_SEQ_RegTask(1U << CFG_TASK_LINK_LAYER, UTIL_SEQ_RFU, ll_sys_bg_process);
96// }
97//
98// /**
99// * @brief Link Layer background process next iteration scheduling
100// * @param None
101// * @retval None
102// */
103// void ll_sys_schedule_bg_process(void)
104// {
105// UTIL_SEQ_SetTask(1U << CFG_TASK_LINK_LAYER, TASK_PRIO_LINK_LAYER);
106// }
107//
108// /**
109// * @brief Link Layer background process next iteration scheduling from ISR
110// * @param None
111// * @retval None
112// */
113// void ll_sys_schedule_bg_process_isr(void)
114// {
115// UTIL_SEQ_SetTask(1U << CFG_TASK_LINK_LAYER, TASK_PRIO_LINK_LAYER);
116// }
117//
118// /**
119// * @brief Link Layer configuration phase before application startup.
120// * @param None
121// * @retval None
122// */
123// void ll_sys_config_params(void)
124// {
125// /* USER CODE BEGIN ll_sys_config_params_0 */
126//
127// /* USER CODE END ll_sys_config_params_0 */
128//
129// /* Configure link layer behavior for low ISR use and next event scheduling method:
130// * - SW low ISR is used.
131// * - Next event is scheduled from ISR.
132// */
133// ll_intf_cmn_config_ll_ctx_params(USE_RADIO_LOW_ISR, NEXT_EVENT_SCHEDULING_FROM_ISR);
134// /* Apply the selected link layer sleep timer source */
135// ll_sys_sleep_clock_source_selection();
136//
137// /* USER CODE BEGIN ll_sys_config_params_1 */
138//
139// /* USER CODE END ll_sys_config_params_1 */
140//
141// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)
142// /* Initialize link layer temperature measurement background task */
143// ll_sys_bg_temperature_measurement_init();
144//
145// /* Link layer IP uses temperature based calibration instead of periodic one */
146// ll_intf_cmn_set_temperature_sensor_state();
147// #endif /* USE_TEMPERATURE_BASED_RADIO_CALIBRATION */
148//
149// /* Link Layer power table */
150// ll_intf_cmn_select_tx_power_table(CFG_RF_TX_POWER_TABLE_ID);
151//
152// #if (USE_CTE_DEGRADATION == 1u)
153// /* Apply CTE degradation */
154// ll_sys_apply_cte_settings ();
155// #endif /* (USE_CTE_DEGRADATION == 1u) */
156//
157// /* USER CODE BEGIN ll_sys_config_params_2 */
158//
159// /* USER CODE END ll_sys_config_params_2 */
160// }
161//
162// #if (USE_TEMPERATURE_BASED_RADIO_CALIBRATION == 1)
163//
164// /**
165// * @brief Link Layer temperature request background process initialization
166// * @param None
167// * @retval None
168// */
169// void ll_sys_bg_temperature_measurement_init(void)
170// {
171// /* Register Temperature Measurement task */
172// UTIL_SEQ_RegTask(1U << CFG_TASK_TEMP_MEAS, UTIL_SEQ_RFU, TEMPMEAS_RequestTemperatureMeasurement);
173// }
174//
175// /**
176// * @brief Request backroud task processing for temperature measurement
177// * @param None
178// * @retval None
179// */
180// void ll_sys_bg_temperature_measurement(void)
181// {
182// static uint8_t initial_temperature_acquisition = 0;
183//
184// if(initial_temperature_acquisition == 0)
185// {
186// TEMPMEAS_RequestTemperatureMeasurement();
187// initial_temperature_acquisition = 1;
188// }
189// else
190// {
191// UTIL_SEQ_SetTask(1U << CFG_TASK_TEMP_MEAS, CFG_SEQ_PRIO_0);
192// }
193// }
194//
195// #endif /* USE_TEMPERATURE_BASED_RADIO_CALIBRATION */
196//
197// uint8_t ll_sys_BLE_sleep_clock_accuracy_selection(void)
198// {
199// uint8_t BLE_sleep_clock_accuracy = 0;
200// #if (CFG_RADIO_LSE_SLEEP_TIMER_CUSTOM_SCA_RANGE == 0)
201// uint32_t RevID = LL_DBGMCU_GetRevisionID();
202// #endif
203// uint32_t linklayer_slp_clk_src = LL_RCC_RADIO_GetSleepTimerClockSource();
204//
205// if(linklayer_slp_clk_src == LL_RCC_RADIOSLEEPSOURCE_LSE)
206// {
207// /* LSE selected as Link Layer sleep clock source.
208// Sleep clock accuracy is different regarding the WBA device ID and revision
209// */
210// #if (CFG_RADIO_LSE_SLEEP_TIMER_CUSTOM_SCA_RANGE == 0)
211// #if defined(STM32WBA52xx) || defined(STM32WBA54xx) || defined(STM32WBA55xx)
212// if(RevID == REV_ID_A)
213// {
214// BLE_sleep_clock_accuracy = STM32WBA5x_REV_ID_A_SCA_RANGE;
215// }
216// else if(RevID == REV_ID_B)
217// {
218// BLE_sleep_clock_accuracy = STM32WBA5x_REV_ID_B_SCA_RANGE;
219// }
220// else
221// {
222// /* Revision ID not supported, default value of 500ppm applied */
223// BLE_sleep_clock_accuracy = STM32WBA5x_DEFAULT_SCA_RANGE;
224// }
225// #elif defined(STM32WBA65xx)
226// BLE_sleep_clock_accuracy = STM32WBA6x_SCA_RANGE;
227// UNUSED(RevID);
228// #else
229// UNUSED(RevID);
230// #endif /* defined(STM32WBA52xx) || defined(STM32WBA54xx) || defined(STM32WBA55xx) */
231// #else /* CFG_RADIO_LSE_SLEEP_TIMER_CUSTOM_SCA_RANGE */
232// BLE_sleep_clock_accuracy = CFG_RADIO_LSE_SLEEP_TIMER_CUSTOM_SCA_RANGE;
233// #endif /* CFG_RADIO_LSE_SLEEP_TIMER_CUSTOM_SCA_RANGE */
234// }
235// else
236// {
237// /* LSE is not the Link Layer sleep clock source, sleep clock accurcay default value is 500 ppm */
238// BLE_sleep_clock_accuracy = STM32WBA5x_DEFAULT_SCA_RANGE;
239// }
240//
241// return BLE_sleep_clock_accuracy;
242// }
243//
244// void ll_sys_sleep_clock_source_selection(void)
245// {
246// uint16_t freq_value = 0;
247// uint32_t linklayer_slp_clk_src = LL_RCC_RADIOSLEEPSOURCE_NONE;
248//
249// linklayer_slp_clk_src = LL_RCC_RADIO_GetSleepTimerClockSource();
250// switch(linklayer_slp_clk_src)
251// {
252// case LL_RCC_RADIOSLEEPSOURCE_LSE:
253// linklayer_slp_clk_src = RTC_SLPTMR;
254// break;
255//
256// case LL_RCC_RADIOSLEEPSOURCE_LSI:
257// linklayer_slp_clk_src = RCO_SLPTMR;
258// break;
259//
260// case LL_RCC_RADIOSLEEPSOURCE_HSE_DIV1000:
261// linklayer_slp_clk_src = CRYSTAL_OSCILLATOR_SLPTMR;
262// break;
263//
264// case LL_RCC_RADIOSLEEPSOURCE_NONE:
265// /* No Link Layer sleep clock source selected */
266// assert_param(0);
267// break;
268// }
269// ll_intf_cmn_le_select_slp_clk_src((uint8_t)linklayer_slp_clk_src, &freq_value);
270// }
271//
272// void ll_sys_reset(void)
273// {
274// uint8_t bsca = 0;
275// /* Link layer timings */
276// uint8_t drift_time = DRIFT_TIME_DEFAULT;
277// uint8_t exec_time = EXEC_TIME_DEFAULT;
278//
279// /* USER CODE BEGIN ll_sys_reset_0 */
280//
281// /* USER CODE END ll_sys_reset_0 */
282//
283// /* Apply the selected link layer sleep timer source */
284// ll_sys_sleep_clock_source_selection();
285//
286// /* Configure the link layer sleep clock accuracy */
287// bsca = ll_sys_BLE_sleep_clock_accuracy_selection();
288// ll_intf_le_set_sleep_clock_accuracy(bsca);
289//
290// /* Update link layer timings depending on selected configuration */
291// if(LL_RCC_RADIO_GetSleepTimerClockSource() == LL_RCC_RADIOSLEEPSOURCE_LSI)
292// {
293// drift_time += DRIFT_TIME_EXTRA_LSI2;
294// exec_time += EXEC_TIME_EXTRA_LSI2;
295// }
296// else
297// {
298// #if defined(__GNUC__) && defined(DEBUG)
299// drift_time += DRIFT_TIME_EXTRA_GCC_DEBUG;
300// exec_time += EXEC_TIME_EXTRA_GCC_DEBUG;
301// #endif
302// }
303//
304// /* USER CODE BEGIN ll_sys_reset_1 */
305//
306// /* USER CODE END ll_sys_reset_1 */
307//
308// if((drift_time != DRIFT_TIME_DEFAULT) || (exec_time != EXEC_TIME_DEFAULT))
309// {
310// ll_sys_config_BLE_schldr_timings(drift_time, exec_time);
311// }
312// /* USER CODE BEGIN ll_sys_reset_2 */
313//
314// /* USER CODE END ll_sys_reset_2 */
315// }
316// #if defined(STM32WBA52xx) || defined(STM32WBA54xx) || defined(STM32WBA55xx) || defined(STM32WBA65xx)
317// void ll_sys_apply_cte_settings(void)
318// {
319// ll_intf_apply_cte_degrad_change();
320// }
321// #endif /* defined(STM32WBA52xx) || defined(STM32WBA54xx) || defined(STM32WBA55xx) || defined(STM32WBA65xx) */
322//
323// #if (CFG_LPM_STANDBY_SUPPORTED == 0)
324// void ll_sys_get_ble_profile_statistics(uint32_t* exec_time, uint32_t* drift_time, uint32_t* average_drift_time, uint8_t reset)
325// {
326// if (reset != 0U)
327// {
328// profile_reset();
329// }
330// ll_intf_get_profile_statistics(exec_time, drift_time, average_drift_time);
331// }
332// #endif
333//
334use super::bindings::{link_layer, mac};
335use super::util_seq;
336
337const UTIL_SEQ_RFU: u32 = 0;
338const TASK_LINK_LAYER_MASK: u32 = 1 << mac::CFG_TASK_ID_T_CFG_TASK_LINK_LAYER;
339const TASK_PRIO_LINK_LAYER: u32 = mac::CFG_SEQ_PRIO_ID_T_CFG_SEQ_PRIO_0 as u32;
340
341/**
342 * @brief Link Layer background process initialization
343 * @param None
344 * @retval None
345 */
346#[unsafe(no_mangle)]
347pub unsafe extern "C" fn ll_sys_bg_process_init() {
348 util_seq::UTIL_SEQ_RegTask(TASK_LINK_LAYER_MASK, UTIL_SEQ_RFU, Some(link_layer::ll_sys_bg_process));
349}
350
351/**
352 * @brief Link Layer background process next iteration scheduling
353 * @param None
354 * @retval None
355 */
356#[unsafe(no_mangle)]
357pub unsafe extern "C" fn ll_sys_schedule_bg_process() {
358 util_seq::UTIL_SEQ_SetTask(TASK_LINK_LAYER_MASK, TASK_PRIO_LINK_LAYER);
359}
360
361/**
362 * @brief Link Layer background process next iteration scheduling from ISR
363 * @param None
364 * @retval None
365 */
366#[unsafe(no_mangle)]
367pub unsafe extern "C" fn ll_sys_schedule_bg_process_isr() {
368 util_seq::UTIL_SEQ_SetTask(TASK_LINK_LAYER_MASK, TASK_PRIO_LINK_LAYER);
369}
370
371/**
372 * @brief Link Layer configuration phase before application startup.
373 * @param None
374 * @retval None
375 */
376#[unsafe(no_mangle)]
377pub unsafe extern "C" fn ll_sys_config_params() {
378 let allow_low_isr = mac::USE_RADIO_LOW_ISR as u8;
379 let run_from_isr = mac::NEXT_EVENT_SCHEDULING_FROM_ISR as u8;
380 let _ = link_layer::ll_intf_cmn_config_ll_ctx_params(allow_low_isr, run_from_isr);
381
382 ll_sys_sleep_clock_source_selection();
383 let _ = link_layer::ll_intf_cmn_select_tx_power_table(mac::CFG_RF_TX_POWER_TABLE_ID as u8);
384}
385
386/**
387 * @brief Reset Link Layer timing parameters to their default configuration.
388 * @param None
389 * @retval None
390 */
391#[unsafe(no_mangle)]
392pub unsafe extern "C" fn ll_sys_reset() {
393 ll_sys_sleep_clock_source_selection();
394
395 let sleep_accuracy = ll_sys_BLE_sleep_clock_accuracy_selection();
396 let _ = link_layer::ll_intf_le_set_sleep_clock_accuracy(sleep_accuracy);
397}
398
399/// Select the sleep-clock source used by the Link Layer.
400/// Defaults to the crystal oscillator when no explicit configuration is available.
401#[unsafe(no_mangle)]
402pub unsafe extern "C" fn ll_sys_sleep_clock_source_selection() {
403 let mut frequency: u16 = 0;
404 let _ = link_layer::ll_intf_cmn_le_select_slp_clk_src(
405 link_layer::_SLPTMR_SRC_TYPE_E_CRYSTAL_OSCILLATOR_SLPTMR as u8,
406 &mut frequency as *mut u16,
407 );
408}
409
410/// Determine the BLE sleep-clock accuracy used by the stack.
411/// Returns zero when board-specific calibration data is unavailable.
412#[unsafe(no_mangle)]
413pub unsafe extern "C" fn ll_sys_BLE_sleep_clock_accuracy_selection() -> u8 {
414 // TODO: derive the board-specific sleep clock accuracy once calibration data is available.
415 0
416}
diff --git a/embassy-stm32-wpan/src/wba/mac_sys_if.rs b/embassy-stm32-wpan/src/wba/mac_sys_if.rs
new file mode 100644
index 000000000..273399a19
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/mac_sys_if.rs
@@ -0,0 +1,188 @@
1#![cfg(feature = "wba")]
2#![allow(non_snake_case)]
3
4//
5// /* USER CODE BEGIN Header */
6// /**
7// ******************************************************************************
8// * @file mac_sys_if.c
9// * @author MCD Application Team
10// * @brief Source file for using MAC Layer with a RTOS
11// ******************************************************************************
12// * @attention
13// *
14// * Copyright (c) 2025 STMicroelectronics.
15// * All rights reserved.
16// *
17// * This software is licensed under terms that can be found in the LICENSE file
18// * in the root directory of this software component.
19// * If no LICENSE file comes with this software, it is provided AS-IS.
20// *
21// ******************************************************************************
22// */
23// /* USER CODE END Header */
24//
25// #include "main.h"
26// #include "app_common.h"
27// #include "app_conf.h"
28// #include "log_module.h"
29// #include "stm32_rtos.h"
30// #include "st_mac_802_15_4_sys.h"
31//
32// extern void mac_baremetal_run(void);
33//
34// /* Private defines -----------------------------------------------------------*/
35// /* USER CODE BEGIN PD */
36//
37// /* USER CODE END PD */
38//
39// /* Private macros ------------------------------------------------------------*/
40// /* USER CODE BEGIN PM */
41//
42// /* USER CODE END PM */
43//
44// /* Private variables ---------------------------------------------------------*/
45// /* USER CODE BEGIN PV */
46//
47// /* USER CODE END PV */
48//
49// /* Global variables ----------------------------------------------------------*/
50// /* USER CODE BEGIN GV */
51//
52// /* USER CODE END GV */
53//
54// /* Functions Definition ------------------------------------------------------*/
55//
56// /**
57// * @brief Mac Layer Initialisation
58// * @param None
59// * @retval None
60// */
61// void MacSys_Init(void)
62// {
63// /* Register tasks */
64// UTIL_SEQ_RegTask( TASK_MAC_LAYER, UTIL_SEQ_RFU, mac_baremetal_run);
65// }
66//
67// /**
68// * @brief Mac Layer Resume
69// * @param None
70// * @retval None
71// */
72// void MacSys_Resume(void)
73// {
74// UTIL_SEQ_ResumeTask( TASK_MAC_LAYER );
75// }
76//
77// /**
78// * @brief MAC Layer set Task.
79// * @param None
80// * @retval None
81// */
82// void MacSys_SemaphoreSet(void)
83// {
84// UTIL_SEQ_SetTask( TASK_MAC_LAYER, TASK_PRIO_MAC_LAYER );
85// }
86//
87// /**
88// * @brief MAC Layer Task wait.
89// * @param None
90// * @retval None
91// */
92// void MacSys_SemaphoreWait( void )
93// {
94// /* Not used */
95// }
96//
97// /**
98// * @brief MAC Layer set Event.
99// * @param None
100// * @retval None
101// */
102// void MacSys_EventSet( void )
103// {
104// UTIL_SEQ_SetEvt( EVENT_MAC_LAYER );
105// }
106//
107// /**
108// * @brief MAC Layer wait Event.
109// * @param None
110// * @retval None
111// */
112// void MacSys_EventWait( void )
113// {
114// UTIL_SEQ_WaitEvt( EVENT_MAC_LAYER );
115// }
116//
117
118use super::util_seq;
119use crate::bindings::mac;
120
121/// Placeholder value used by the original ST middleware when registering tasks.
122const UTIL_SEQ_RFU: u32 = 0;
123
124/// Bit mask identifying the MAC layer task within the sequencer.
125const TASK_MAC_LAYER_MASK: u32 = 1 << mac::CFG_TASK_ID_T_CFG_TASK_MAC_LAYER;
126
127/// Sequencer priority assigned to the MAC layer task.
128const TASK_PRIO_MAC_LAYER: u32 = mac::CFG_SEQ_PRIO_ID_T_CFG_SEQ_PRIO_0 as u32;
129
130/// Event flag consumed by the MAC task while waiting on notifications.
131const EVENT_MAC_LAYER_MASK: u32 = 1 << 0;
132
133/// Registers the MAC bare-metal runner with the lightweight sequencer.
134///
135/// Mirrors the behaviour of the reference implementation:
136/// `UTIL_SEQ_RegTask(TASK_MAC_LAYER, UTIL_SEQ_RFU, mac_baremetal_run);`
137#[unsafe(no_mangle)]
138pub unsafe extern "C" fn MacSys_Init() {
139 util_seq::UTIL_SEQ_RegTask(TASK_MAC_LAYER_MASK, UTIL_SEQ_RFU, Some(mac::mac_baremetal_run));
140}
141
142/**
143 * @brief Mac Layer Resume
144 * @param None
145 * @retval None
146 */
147#[unsafe(no_mangle)]
148pub unsafe extern "C" fn MacSys_Resume() {
149 util_seq::UTIL_SEQ_ResumeTask(TASK_MAC_LAYER_MASK);
150}
151
152/**
153 * @brief MAC Layer set Task.
154 * @param None
155 * @retval None
156 */
157#[unsafe(no_mangle)]
158pub unsafe extern "C" fn MacSys_SemaphoreSet() {
159 util_seq::UTIL_SEQ_SetTask(TASK_MAC_LAYER_MASK, TASK_PRIO_MAC_LAYER);
160}
161
162/**
163 * @brief MAC Layer Task wait.
164 * @param None
165 * @retval None
166 */
167#[unsafe(no_mangle)]
168pub unsafe extern "C" fn MacSys_SemaphoreWait() {}
169
170/**
171 * @brief MAC Layer set Event.
172 * @param None
173 * @retval None
174 */
175#[unsafe(no_mangle)]
176pub unsafe extern "C" fn MacSys_EventSet() {
177 util_seq::UTIL_SEQ_SetEvt(EVENT_MAC_LAYER_MASK);
178}
179
180/**
181 * @brief MAC Layer wait Event.
182 * @param None
183 * @retval None
184 */
185#[unsafe(no_mangle)]
186pub unsafe extern "C" fn MacSys_EventWait() {
187 util_seq::UTIL_SEQ_WaitEvt(EVENT_MAC_LAYER_MASK);
188}
diff --git a/embassy-stm32-wpan/src/wba/mod.rs b/embassy-stm32-wpan/src/wba/mod.rs
new file mode 100644
index 000000000..3161b578e
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/mod.rs
@@ -0,0 +1,6 @@
1pub mod bindings;
2pub mod linklayer_plat;
3pub mod ll_sys;
4pub mod ll_sys_if;
5pub mod mac_sys_if;
6pub mod util_seq;
diff --git a/embassy-stm32-wpan/src/wba/util_seq.rs b/embassy-stm32-wpan/src/wba/util_seq.rs
new file mode 100644
index 000000000..b596df908
--- /dev/null
+++ b/embassy-stm32-wpan/src/wba/util_seq.rs
@@ -0,0 +1,243 @@
1#![cfg(feature = "wba")]
2
3use core::cell::UnsafeCell;
4use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
5
6use critical_section::with as critical;
7
8type TaskFn = unsafe extern "C" fn();
9
10const MAX_TASKS: usize = 32;
11const DEFAULT_PRIORITY: u8 = u8::MAX;
12
13struct TaskTable {
14 funcs: UnsafeCell<[Option<TaskFn>; MAX_TASKS]>,
15 priorities: UnsafeCell<[u8; MAX_TASKS]>,
16}
17
18impl TaskTable {
19 const fn new() -> Self {
20 Self {
21 funcs: UnsafeCell::new([None; MAX_TASKS]),
22 priorities: UnsafeCell::new([DEFAULT_PRIORITY; MAX_TASKS]),
23 }
24 }
25
26 unsafe fn set_task(&self, idx: usize, func: Option<TaskFn>, priority: u8) {
27 (*self.funcs.get())[idx] = func;
28 (*self.priorities.get())[idx] = priority;
29 }
30
31 unsafe fn update_priority(&self, idx: usize, priority: u8) {
32 (*self.priorities.get())[idx] = priority;
33 }
34
35 unsafe fn task(&self, idx: usize) -> Option<TaskFn> {
36 (*self.funcs.get())[idx]
37 }
38
39 unsafe fn priority(&self, idx: usize) -> u8 {
40 (*self.priorities.get())[idx]
41 }
42}
43
44unsafe impl Sync for TaskTable {}
45
46#[inline(always)]
47fn wake_event() {
48 #[cfg(target_arch = "arm")]
49 {
50 cortex_m::asm::sev();
51 }
52
53 #[cfg(not(target_arch = "arm"))]
54 {
55 // No-op on architectures without SEV support.
56 }
57}
58
59#[inline(always)]
60fn wait_event() {
61 #[cfg(target_arch = "arm")]
62 {
63 cortex_m::asm::wfe();
64 }
65
66 #[cfg(not(target_arch = "arm"))]
67 {
68 core::hint::spin_loop();
69 }
70}
71
72static TASKS: TaskTable = TaskTable::new();
73static PENDING_TASKS: AtomicU32 = AtomicU32::new(0);
74static EVENTS: AtomicU32 = AtomicU32::new(0);
75static SCHEDULING: AtomicBool = AtomicBool::new(false);
76
77fn mask_to_index(mask: u32) -> Option<usize> {
78 if mask == 0 {
79 return None;
80 }
81 let idx = mask.trailing_zeros() as usize;
82 if idx < MAX_TASKS { Some(idx) } else { None }
83}
84
85fn drain_pending_tasks() {
86 loop {
87 if SCHEDULING
88 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
89 .is_err()
90 {
91 return;
92 }
93
94 loop {
95 let next = critical(|_| select_next_task());
96 match next {
97 Some((idx, task)) => unsafe {
98 task();
99 // Force a fresh read of the pending bitmask after each task completion.
100 let _ = idx;
101 },
102 None => break,
103 }
104 }
105
106 SCHEDULING.store(false, Ordering::Release);
107
108 if PENDING_TASKS.load(Ordering::Acquire) == 0 {
109 break;
110 }
111 }
112}
113
114/// Poll and execute any tasks that have been scheduled via the UTIL sequencer API.
115pub fn poll_pending_tasks() {
116 drain_pending_tasks();
117}
118
119fn select_next_task() -> Option<(usize, TaskFn)> {
120 let pending = PENDING_TASKS.load(Ordering::Acquire);
121 if pending == 0 {
122 return None;
123 }
124
125 let mut remaining = pending;
126 let mut best_idx: Option<usize> = None;
127 let mut best_priority = DEFAULT_PRIORITY;
128 let mut best_fn: Option<TaskFn> = None;
129
130 while remaining != 0 {
131 let idx = remaining.trailing_zeros() as usize;
132 remaining &= remaining - 1;
133
134 if idx >= MAX_TASKS {
135 continue;
136 }
137
138 unsafe {
139 if let Some(func) = TASKS.task(idx) {
140 let prio = TASKS.priority(idx);
141 if prio <= best_priority {
142 if prio < best_priority || best_idx.map_or(true, |current| idx < current) {
143 best_priority = prio;
144 best_idx = Some(idx);
145 best_fn = Some(func);
146 }
147 }
148 } else {
149 PENDING_TASKS.fetch_and(!(1u32 << idx), Ordering::AcqRel);
150 }
151 }
152 }
153
154 if let (Some(idx), Some(func)) = (best_idx, best_fn) {
155 PENDING_TASKS.fetch_and(!(1u32 << idx), Ordering::AcqRel);
156 Some((idx, func))
157 } else {
158 None
159 }
160}
161
162#[unsafe(no_mangle)]
163pub extern "C" fn UTIL_SEQ_RegTask(task_mask: u32, _flags: u32, task: Option<TaskFn>) {
164 if let Some(idx) = mask_to_index(task_mask) {
165 critical(|_| unsafe {
166 TASKS.set_task(idx, task, DEFAULT_PRIORITY);
167 });
168 }
169}
170
171#[unsafe(no_mangle)]
172pub extern "C" fn UTIL_SEQ_UnregTask(task_mask: u32) {
173 if let Some(idx) = mask_to_index(task_mask) {
174 critical(|_| unsafe {
175 TASKS.set_task(idx, None, DEFAULT_PRIORITY);
176 });
177 PENDING_TASKS.fetch_and(!(task_mask), Ordering::AcqRel);
178 }
179}
180
181#[unsafe(no_mangle)]
182pub extern "C" fn UTIL_SEQ_SetTask(task_mask: u32, priority: u32) {
183 let prio = (priority & 0xFF) as u8;
184
185 if let Some(idx) = mask_to_index(task_mask) {
186 let registered = critical(|_| unsafe {
187 if TASKS.task(idx).is_some() {
188 TASKS.update_priority(idx, prio);
189 true
190 } else {
191 false
192 }
193 });
194
195 if registered {
196 PENDING_TASKS.fetch_or(task_mask, Ordering::Release);
197 wake_event();
198 }
199 }
200}
201
202#[unsafe(no_mangle)]
203pub extern "C" fn UTIL_SEQ_ResumeTask(task_mask: u32) {
204 PENDING_TASKS.fetch_or(task_mask, Ordering::Release);
205 wake_event();
206}
207
208#[unsafe(no_mangle)]
209pub extern "C" fn UTIL_SEQ_PauseTask(task_mask: u32) {
210 PENDING_TASKS.fetch_and(!task_mask, Ordering::AcqRel);
211}
212
213#[unsafe(no_mangle)]
214pub extern "C" fn UTIL_SEQ_SetEvt(event_mask: u32) {
215 EVENTS.fetch_or(event_mask, Ordering::Release);
216 wake_event();
217}
218
219#[unsafe(no_mangle)]
220pub extern "C" fn UTIL_SEQ_ClrEvt(event_mask: u32) {
221 EVENTS.fetch_and(!event_mask, Ordering::AcqRel);
222}
223
224#[unsafe(no_mangle)]
225pub extern "C" fn UTIL_SEQ_IsEvtSet(event_mask: u32) -> u32 {
226 let state = EVENTS.load(Ordering::Acquire);
227 if (state & event_mask) == event_mask { 1 } else { 0 }
228}
229
230#[unsafe(no_mangle)]
231pub extern "C" fn UTIL_SEQ_WaitEvt(event_mask: u32) {
232 loop {
233 poll_pending_tasks();
234
235 let current = EVENTS.load(Ordering::Acquire);
236 if (current & event_mask) == event_mask {
237 EVENTS.fetch_and(!event_mask, Ordering::AcqRel);
238 break;
239 }
240
241 wait_event();
242 }
243}