aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32-wpan/src/wb55
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-stm32-wpan/src/wb55')
-rw-r--r--embassy-stm32-wpan/src/wb55/channels.rs107
-rw-r--r--embassy-stm32-wpan/src/wb55/cmd.rs109
-rw-r--r--embassy-stm32-wpan/src/wb55/consts.rs94
-rw-r--r--embassy-stm32-wpan/src/wb55/evt.rs152
-rw-r--r--embassy-stm32-wpan/src/wb55/fmt.rs270
-rw-r--r--embassy-stm32-wpan/src/wb55/lhci.rs112
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/commands.rs509
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/consts.rs4
-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.rs153
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/indications.rs300
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/macros.rs32
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/mod.rs17
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/opcodes.rs92
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/responses.rs273
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/runner.rs151
-rw-r--r--embassy-stm32-wpan/src/wb55/mac/typedefs.rs434
-rw-r--r--embassy-stm32-wpan/src/wb55/mod.rs198
-rw-r--r--embassy-stm32-wpan/src/wb55/shci.rs397
-rw-r--r--embassy-stm32-wpan/src/wb55/sub/ble.rs139
-rw-r--r--embassy-stm32-wpan/src/wb55/sub/mac.rs173
-rw-r--r--embassy-stm32-wpan/src/wb55/sub/mm.rs84
-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.rs283
-rw-r--r--embassy-stm32-wpan/src/wb55/unsafe_linked_list.rs269
27 files changed, 4878 insertions, 0 deletions
diff --git a/embassy-stm32-wpan/src/wb55/channels.rs b/embassy-stm32-wpan/src/wb55/channels.rs
new file mode 100644
index 000000000..58f857136
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/channels.rs
@@ -0,0 +1,107 @@
1//! CPU1 CPU2
2//! | (SYSTEM) |
3//! |----HW_IPCC_SYSTEM_CMD_RSP_CHANNEL-------------->|
4//! | |
5//! |<---HW_IPCC_SYSTEM_EVENT_CHANNEL-----------------|
6//! | |
7//! | (ZIGBEE) |
8//! |----HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL------------>|
9//! | |
10//! |----HW_IPCC_ZIGBEE_CMD_CLI_CHANNEL-------------->|
11//! | |
12//! |<---HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL-------|
13//! | |
14//! |<---HW_IPCC_ZIGBEE_CLI_NOTIF_ACK_CHANNEL---------|
15//! | |
16//! | (THREAD) |
17//! |----HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL----------->|
18//! | |
19//! |----HW_IPCC_THREAD_CLI_CMD_CHANNEL-------------->|
20//! | |
21//! |<---HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL------|
22//! | |
23//! |<---HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL--|
24//! | |
25//! | (BLE) |
26//! |----HW_IPCC_BLE_CMD_CHANNEL--------------------->|
27//! | |
28//! |----HW_IPCC_HCI_ACL_DATA_CHANNEL---------------->|
29//! | |
30//! |<---HW_IPCC_BLE_EVENT_CHANNEL--------------------|
31//! | |
32//! | (BLE LLD) |
33//! |----HW_IPCC_BLE_LLD_CMD_CHANNEL----------------->|
34//! | |
35//! |<---HW_IPCC_BLE_LLD_RSP_CHANNEL------------------|
36//! | |
37//! |<---HW_IPCC_BLE_LLD_M0_CMD_CHANNEL---------------|
38//! | |
39//! | (MAC) |
40//! |----HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL-------->|
41//! | |
42//! |<---HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL|
43//! | |
44//! | (BUFFER) |
45//! |----HW_IPCC_MM_RELEASE_BUFFER_CHANNE------------>|
46//! | |
47//! | (TRACE) |
48//! |<----HW_IPCC_TRACES_CHANNEL----------------------|
49//! | |
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}
62
63pub mod cpu1 {
64 use super::IpccChannel;
65
66 pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1;
67 pub const IPCC_SYSTEM_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel2;
68 pub const IPCC_THREAD_OT_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3;
69 #[allow(dead_code)] // Not used currently but reserved
70 pub const IPCC_ZIGBEE_CMD_APPLI_CHANNEL: IpccChannel = IpccChannel::Channel3;
71 #[allow(dead_code)] // Not used currently but reserved
72 pub const IPCC_MAC_802_15_4_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3;
73 #[allow(dead_code)] // Not used currently but reserved
74 pub const IPCC_MM_RELEASE_BUFFER_CHANNEL: IpccChannel = IpccChannel::Channel4;
75 pub const IPCC_THREAD_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5;
76 #[allow(dead_code)] // Not used currently but reserved
77 pub const IPCC_LLDTESTS_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5;
78 #[allow(dead_code)] // Not used currently but reserved
79 pub const IPCC_BLE_LLD_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5;
80 pub const IPCC_HCI_ACL_DATA_CHANNEL: IpccChannel = IpccChannel::Channel6;
81}
82
83pub mod cpu2 {
84 use super::IpccChannel;
85
86 pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1;
87 pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2;
88 pub const IPCC_THREAD_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3;
89 #[allow(dead_code)] // Not used currently but reserved
90 pub const IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3;
91 #[allow(dead_code)] // Not used currently but reserved
92 pub const IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3;
93 #[allow(dead_code)] // Not used currently but reserved
94 pub const IPCC_LDDTESTS_M0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3;
95 #[allow(dead_code)] // Not used currently but reserved
96 pub const IPCC_BLE_LLDÇM0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3;
97 pub const IPCC_TRACES_CHANNEL: IpccChannel = IpccChannel::Channel4;
98 pub const IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel5;
99 #[allow(dead_code)] // Not used currently but reserved
100 pub const IPCC_LLDTESTS_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5;
101 #[allow(dead_code)] // Not used currently but reserved
102 pub const IPCC_BLE_LLD_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5;
103 #[allow(dead_code)] // Not used currently but reserved
104 pub const IPCC_BLE_LLD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5;
105 #[allow(dead_code)] // Not used currently but reserved
106 pub const IPCC_ZIGBEE_M0_REQUEST_CHANNEL: IpccChannel = IpccChannel::Channel5;
107}
diff --git a/embassy-stm32-wpan/src/wb55/cmd.rs b/embassy-stm32-wpan/src/wb55/cmd.rs
new file mode 100644
index 000000000..34f02d6e7
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/cmd.rs
@@ -0,0 +1,109 @@
1use core::ptr;
2use core::sync::atomic::{Ordering, compiler_fence};
3
4use crate::consts::TlPacketType;
5use crate::wb55::PacketHeader;
6
7#[derive(Copy, Clone)]
8#[repr(C, packed)]
9pub struct Cmd {
10 pub cmd_code: u16,
11 pub payload_len: u8,
12 pub payload: [u8; 255],
13}
14
15impl Default for Cmd {
16 fn default() -> Self {
17 Self {
18 cmd_code: 0,
19 payload_len: 0,
20 payload: [0u8; 255],
21 }
22 }
23}
24
25#[derive(Copy, Clone, Default)]
26#[repr(C, packed)]
27pub struct CmdSerial {
28 pub ty: u8,
29 pub cmd: Cmd,
30}
31
32#[derive(Copy, Clone, Default)]
33#[repr(C, packed)]
34pub struct CmdSerialStub {
35 pub ty: u8,
36 pub cmd_code: u16,
37 pub payload_len: u8,
38}
39
40#[derive(Copy, Clone, Default)]
41#[repr(C, packed)]
42pub struct CmdPacket {
43 pub header: PacketHeader,
44 pub cmdserial: CmdSerial,
45}
46
47impl CmdPacket {
48 pub unsafe fn write_into(cmd_buf: *mut CmdPacket, packet_type: TlPacketType, cmd_code: u16, payload: &[u8]) {
49 let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::<PacketHeader>());
50 let p_payload = p_cmd_serial.add(size_of::<CmdSerialStub>());
51
52 ptr::write_unaligned(
53 p_cmd_serial as *mut _,
54 CmdSerialStub {
55 ty: packet_type as u8,
56 cmd_code,
57 payload_len: payload.len() as u8,
58 },
59 );
60
61 ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len());
62
63 compiler_fence(Ordering::Release);
64 }
65}
66
67#[derive(Copy, Clone)]
68#[repr(C, packed)]
69pub struct AclDataSerial {
70 pub ty: u8,
71 pub handle: u16,
72 pub length: u16,
73 pub acl_data: [u8; 1],
74}
75
76#[derive(Copy, Clone)]
77#[repr(C, packed)]
78pub struct AclDataSerialStub {
79 pub ty: u8,
80 pub handle: u16,
81 pub length: u16,
82}
83
84#[derive(Copy, Clone)]
85#[repr(C, packed)]
86pub struct AclDataPacket {
87 pub header: PacketHeader,
88 pub acl_data_serial: AclDataSerial,
89}
90
91impl AclDataPacket {
92 pub unsafe fn write_into(cmd_buf: *mut AclDataPacket, packet_type: TlPacketType, handle: u16, payload: &[u8]) {
93 let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::<PacketHeader>());
94 let p_payload = p_cmd_serial.add(size_of::<AclDataSerialStub>());
95
96 ptr::write_unaligned(
97 p_cmd_serial as *mut _,
98 AclDataSerialStub {
99 ty: packet_type as u8,
100 handle: handle,
101 length: payload.len() as u16,
102 },
103 );
104
105 ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len());
106
107 compiler_fence(Ordering::Release);
108 }
109}
diff --git a/embassy-stm32-wpan/src/wb55/consts.rs b/embassy-stm32-wpan/src/wb55/consts.rs
new file mode 100644
index 000000000..659e74e69
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/consts.rs
@@ -0,0 +1,94 @@
1use crate::evt::CsEvt;
2use crate::wb55::PacketHeader;
3
4#[derive(Debug)]
5#[repr(C)]
6pub enum TlPacketType {
7 MacCmd = 0x00,
8
9 BleCmd = 0x01,
10 AclData = 0x02,
11 BleEvt = 0x04,
12
13 OtCmd = 0x08,
14 OtRsp = 0x09,
15 CliCmd = 0x0A,
16 OtNot = 0x0C,
17 OtAck = 0x0D,
18 CliNot = 0x0E,
19 CliAck = 0x0F,
20
21 SysCmd = 0x10,
22 SysRsp = 0x11,
23 SysEvt = 0x12,
24
25 LocCmd = 0x20,
26 LocRsp = 0x21,
27
28 TracesApp = 0x40,
29 TracesWl = 0x41,
30}
31
32impl TryFrom<u8> for TlPacketType {
33 type Error = ();
34
35 fn try_from(value: u8) -> Result<Self, Self::Error> {
36 match value {
37 0x01 => Ok(TlPacketType::BleCmd),
38 0x02 => Ok(TlPacketType::AclData),
39 0x04 => Ok(TlPacketType::BleEvt),
40 0x08 => Ok(TlPacketType::OtCmd),
41 0x09 => Ok(TlPacketType::OtRsp),
42 0x0A => Ok(TlPacketType::CliCmd),
43 0x0C => Ok(TlPacketType::OtNot),
44 0x0D => Ok(TlPacketType::OtAck),
45 0x0E => Ok(TlPacketType::CliNot),
46 0x0F => Ok(TlPacketType::CliAck),
47 0x10 => Ok(TlPacketType::SysCmd),
48 0x11 => Ok(TlPacketType::SysRsp),
49 0x12 => Ok(TlPacketType::SysEvt),
50 0x20 => Ok(TlPacketType::LocCmd),
51 0x21 => Ok(TlPacketType::LocRsp),
52 0x40 => Ok(TlPacketType::TracesApp),
53 0x41 => Ok(TlPacketType::TracesWl),
54
55 _ => Err(()),
56 }
57 }
58}
59
60pub const TL_PACKET_HEADER_SIZE: usize = core::mem::size_of::<PacketHeader>();
61pub const TL_EVT_HEADER_SIZE: usize = 3;
62pub const TL_CS_EVT_SIZE: usize = core::mem::size_of::<CsEvt>();
63
64/**
65 * Queue length of BLE Event
66 * This parameter defines the number of asynchronous events that can be stored in the HCI layer before
67 * being reported to the application. When a command is sent to the BLE core coprocessor, the HCI layer
68 * is waiting for the event with the Num_HCI_Command_Packets set to 1. The receive queue shall be large
69 * enough to store all asynchronous events received in between.
70 * When CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE is set to 27, this allow to store three 255 bytes long asynchronous events
71 * between the HCI command and its event.
72 * This parameter depends on the value given to CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE. When the queue size is too small,
73 * the system may hang if the queue is full with asynchronous events and the HCI layer is still waiting
74 * for a CC/CS event, In that case, the notification TL_BLE_HCI_ToNot() is called to indicate
75 * to the application a HCI command did not receive its command event within 30s (Default HCI Timeout).
76 */
77pub const CFG_TL_BLE_EVT_QUEUE_LENGTH: usize = 5;
78pub const CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE: usize = 255;
79pub const TL_BLE_EVENT_FRAME_SIZE: usize = TL_EVT_HEADER_SIZE + CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE;
80
81pub const POOL_SIZE: usize = CFG_TL_BLE_EVT_QUEUE_LENGTH * 4 * divc(TL_PACKET_HEADER_SIZE + TL_BLE_EVENT_FRAME_SIZE, 4);
82pub const C_SIZE_CMD_STRING: usize = 256;
83
84pub const fn divc(x: usize, y: usize) -> usize {
85 (x + y - 1) / y
86}
87
88pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE;
89#[allow(dead_code)]
90pub const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE;
91
92pub const TL_BLEEVT_CC_OPCODE: u8 = 0x0E;
93pub const TL_BLEEVT_CS_OPCODE: u8 = 0x0F;
94pub const TL_BLEEVT_VS_OPCODE: u8 = 0xFF;
diff --git a/embassy-stm32-wpan/src/wb55/evt.rs b/embassy-stm32-wpan/src/wb55/evt.rs
new file mode 100644
index 000000000..f32821269
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/evt.rs
@@ -0,0 +1,152 @@
1use core::marker::PhantomData;
2use core::{ptr, slice};
3
4use super::PacketHeader;
5use crate::consts::TL_EVT_HEADER_SIZE;
6
7/**
8 * The payload of `Evt` for a command status event
9 */
10#[derive(Copy, Clone)]
11#[repr(C, packed)]
12pub struct CsEvt {
13 pub status: u8,
14 pub num_cmd: u8,
15 pub cmd_code: u16,
16}
17
18/**
19 * The payload of `Evt` for a command complete event
20 */
21#[derive(Copy, Clone, Default)]
22#[repr(C, packed)]
23pub struct CcEvt {
24 pub num_cmd: u8,
25 pub cmd_code: u16,
26 pub payload: [u8; 1],
27}
28
29impl CcEvt {
30 pub fn write(&self, buf: &mut [u8]) {
31 unsafe {
32 let len = core::mem::size_of::<CcEvt>();
33 assert!(buf.len() >= len);
34
35 let self_ptr: *const CcEvt = self;
36 let self_buf_ptr: *const u8 = self_ptr.cast();
37
38 core::ptr::copy(self_buf_ptr, buf.as_mut_ptr(), len);
39 }
40 }
41}
42
43#[derive(Copy, Clone, Default)]
44#[repr(C, packed)]
45pub struct AsynchEvt {
46 sub_evt_code: u16,
47 payload: [u8; 1],
48}
49
50#[derive(Copy, Clone)]
51#[repr(C, packed)]
52pub struct Evt {
53 pub evt_code: u8,
54 pub payload_len: u8,
55 pub payload: [u8; 255],
56}
57
58#[derive(Copy, Clone)]
59#[repr(C, packed)]
60pub struct EvtSerial {
61 pub kind: u8,
62 pub evt: Evt,
63}
64
65#[derive(Copy, Clone, Default)]
66#[repr(C, packed)]
67pub struct EvtStub {
68 pub kind: u8,
69 pub evt_code: u8,
70 pub payload_len: u8,
71}
72
73/// This format shall be used for all events (asynchronous and command response) reported
74/// by the CPU2 except for the command response of a system command where the header is not there
75/// and the format to be used shall be `EvtSerial`.
76///
77/// ### Note:
78/// Be careful that the asynchronous events reported by the CPU2 on the system channel do
79/// include the header and shall use `EvtPacket` format. Only the command response format on the
80/// system channel is different.
81#[derive(Copy, Clone)]
82#[repr(C, packed)]
83pub struct EvtPacket {
84 pub header: PacketHeader,
85 pub evt_serial: EvtSerial,
86}
87
88impl EvtPacket {
89 pub fn kind(&self) -> u8 {
90 self.evt_serial.kind
91 }
92
93 pub fn evt(&self) -> &Evt {
94 &self.evt_serial.evt
95 }
96}
97
98pub trait MemoryManager {
99 unsafe fn drop_event_packet(evt: *mut EvtPacket);
100}
101
102/// smart pointer to the [`EvtPacket`] that will dispose of [`EvtPacket`] buffer automatically
103/// on [`Drop`]
104#[derive(Debug)]
105pub struct EvtBox<T: MemoryManager> {
106 ptr: *mut EvtPacket,
107 mm: PhantomData<T>,
108}
109
110unsafe impl<T: MemoryManager> Send for EvtBox<T> {}
111impl<T: MemoryManager> EvtBox<T> {
112 pub(super) fn new(ptr: *mut EvtPacket) -> Self {
113 Self { ptr, mm: PhantomData }
114 }
115
116 /// Returns information about the event
117 pub fn stub(&self) -> EvtStub {
118 unsafe {
119 let p_evt_stub = &(*self.ptr).evt_serial as *const _ as *const EvtStub;
120
121 ptr::read_volatile(p_evt_stub)
122 }
123 }
124
125 pub fn payload<'a>(&'a self) -> &'a [u8] {
126 unsafe {
127 let p_payload_len = &(*self.ptr).evt_serial.evt.payload_len as *const u8;
128 let p_payload = &(*self.ptr).evt_serial.evt.payload as *const u8;
129
130 let payload_len = ptr::read_volatile(p_payload_len);
131
132 slice::from_raw_parts(p_payload, payload_len as usize)
133 }
134 }
135
136 pub fn serial<'a>(&'a self) -> &'a [u8] {
137 unsafe {
138 let evt_serial: *const EvtSerial = &(*self.ptr).evt_serial;
139 let evt_serial_buf: *const u8 = evt_serial.cast();
140
141 let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE;
142
143 slice::from_raw_parts(evt_serial_buf, len)
144 }
145 }
146}
147
148impl<T: MemoryManager> Drop for EvtBox<T> {
149 fn drop(&mut self) {
150 unsafe { T::drop_event_packet(self.ptr) };
151 }
152}
diff --git a/embassy-stm32-wpan/src/wb55/fmt.rs b/embassy-stm32-wpan/src/wb55/fmt.rs
new file mode 100644
index 000000000..8ca61bc39
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/fmt.rs
@@ -0,0 +1,270 @@
1#![macro_use]
2#![allow(unused)]
3
4use core::fmt::{Debug, Display, LowerHex};
5
6#[cfg(all(feature = "defmt", feature = "log"))]
7compile_error!("You may not enable both `defmt` and `log` features.");
8
9#[collapse_debuginfo(yes)]
10macro_rules! assert {
11 ($($x:tt)*) => {
12 {
13 #[cfg(not(feature = "defmt"))]
14 ::core::assert!($($x)*);
15 #[cfg(feature = "defmt")]
16 ::defmt::assert!($($x)*);
17 }
18 };
19}
20
21#[collapse_debuginfo(yes)]
22macro_rules! assert_eq {
23 ($($x:tt)*) => {
24 {
25 #[cfg(not(feature = "defmt"))]
26 ::core::assert_eq!($($x)*);
27 #[cfg(feature = "defmt")]
28 ::defmt::assert_eq!($($x)*);
29 }
30 };
31}
32
33#[collapse_debuginfo(yes)]
34macro_rules! assert_ne {
35 ($($x:tt)*) => {
36 {
37 #[cfg(not(feature = "defmt"))]
38 ::core::assert_ne!($($x)*);
39 #[cfg(feature = "defmt")]
40 ::defmt::assert_ne!($($x)*);
41 }
42 };
43}
44
45#[collapse_debuginfo(yes)]
46macro_rules! debug_assert {
47 ($($x:tt)*) => {
48 {
49 #[cfg(not(feature = "defmt"))]
50 ::core::debug_assert!($($x)*);
51 #[cfg(feature = "defmt")]
52 ::defmt::debug_assert!($($x)*);
53 }
54 };
55}
56
57#[collapse_debuginfo(yes)]
58macro_rules! debug_assert_eq {
59 ($($x:tt)*) => {
60 {
61 #[cfg(not(feature = "defmt"))]
62 ::core::debug_assert_eq!($($x)*);
63 #[cfg(feature = "defmt")]
64 ::defmt::debug_assert_eq!($($x)*);
65 }
66 };
67}
68
69#[collapse_debuginfo(yes)]
70macro_rules! debug_assert_ne {
71 ($($x:tt)*) => {
72 {
73 #[cfg(not(feature = "defmt"))]
74 ::core::debug_assert_ne!($($x)*);
75 #[cfg(feature = "defmt")]
76 ::defmt::debug_assert_ne!($($x)*);
77 }
78 };
79}
80
81#[collapse_debuginfo(yes)]
82macro_rules! todo {
83 ($($x:tt)*) => {
84 {
85 #[cfg(not(feature = "defmt"))]
86 ::core::todo!($($x)*);
87 #[cfg(feature = "defmt")]
88 ::defmt::todo!($($x)*);
89 }
90 };
91}
92
93#[collapse_debuginfo(yes)]
94macro_rules! unreachable {
95 ($($x:tt)*) => {
96 {
97 #[cfg(not(feature = "defmt"))]
98 ::core::unreachable!($($x)*);
99 #[cfg(feature = "defmt")]
100 ::defmt::unreachable!($($x)*);
101 }
102 };
103}
104
105#[collapse_debuginfo(yes)]
106macro_rules! panic {
107 ($($x:tt)*) => {
108 {
109 #[cfg(not(feature = "defmt"))]
110 ::core::panic!($($x)*);
111 #[cfg(feature = "defmt")]
112 ::defmt::panic!($($x)*);
113 }
114 };
115}
116
117#[collapse_debuginfo(yes)]
118macro_rules! trace {
119 ($s:literal $(, $x:expr)* $(,)?) => {
120 {
121 #[cfg(feature = "log")]
122 ::log::trace!($s $(, $x)*);
123 #[cfg(feature = "defmt")]
124 ::defmt::trace!($s $(, $x)*);
125 #[cfg(not(any(feature = "log", feature="defmt")))]
126 let _ = ($( & $x ),*);
127 }
128 };
129}
130
131#[collapse_debuginfo(yes)]
132macro_rules! debug {
133 ($s:literal $(, $x:expr)* $(,)?) => {
134 {
135 #[cfg(feature = "log")]
136 ::log::debug!($s $(, $x)*);
137 #[cfg(feature = "defmt")]
138 ::defmt::debug!($s $(, $x)*);
139 #[cfg(not(any(feature = "log", feature="defmt")))]
140 let _ = ($( & $x ),*);
141 }
142 };
143}
144
145#[collapse_debuginfo(yes)]
146macro_rules! info {
147 ($s:literal $(, $x:expr)* $(,)?) => {
148 {
149 #[cfg(feature = "log")]
150 ::log::info!($s $(, $x)*);
151 #[cfg(feature = "defmt")]
152 ::defmt::info!($s $(, $x)*);
153 #[cfg(not(any(feature = "log", feature="defmt")))]
154 let _ = ($( & $x ),*);
155 }
156 };
157}
158
159#[collapse_debuginfo(yes)]
160macro_rules! warn {
161 ($s:literal $(, $x:expr)* $(,)?) => {
162 {
163 #[cfg(feature = "log")]
164 ::log::warn!($s $(, $x)*);
165 #[cfg(feature = "defmt")]
166 ::defmt::warn!($s $(, $x)*);
167 #[cfg(not(any(feature = "log", feature="defmt")))]
168 let _ = ($( & $x ),*);
169 }
170 };
171}
172
173#[collapse_debuginfo(yes)]
174macro_rules! error {
175 ($s:literal $(, $x:expr)* $(,)?) => {
176 {
177 #[cfg(feature = "log")]
178 ::log::error!($s $(, $x)*);
179 #[cfg(feature = "defmt")]
180 ::defmt::error!($s $(, $x)*);
181 #[cfg(not(any(feature = "log", feature="defmt")))]
182 let _ = ($( & $x ),*);
183 }
184 };
185}
186
187#[cfg(feature = "defmt")]
188#[collapse_debuginfo(yes)]
189macro_rules! unwrap {
190 ($($x:tt)*) => {
191 ::defmt::unwrap!($($x)*)
192 };
193}
194
195#[cfg(not(feature = "defmt"))]
196#[collapse_debuginfo(yes)]
197macro_rules! unwrap {
198 ($arg:expr) => {
199 match $crate::fmt::Try::into_result($arg) {
200 ::core::result::Result::Ok(t) => t,
201 ::core::result::Result::Err(e) => {
202 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
203 }
204 }
205 };
206 ($arg:expr, $($msg:expr),+ $(,)? ) => {
207 match $crate::fmt::Try::into_result($arg) {
208 ::core::result::Result::Ok(t) => t,
209 ::core::result::Result::Err(e) => {
210 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
211 }
212 }
213 }
214}
215
216#[derive(Debug, Copy, Clone, Eq, PartialEq)]
217pub struct NoneError;
218
219pub trait Try {
220 type Ok;
221 type Error;
222 fn into_result(self) -> Result<Self::Ok, Self::Error>;
223}
224
225impl<T> Try for Option<T> {
226 type Ok = T;
227 type Error = NoneError;
228
229 #[inline]
230 fn into_result(self) -> Result<T, NoneError> {
231 self.ok_or(NoneError)
232 }
233}
234
235impl<T, E> Try for Result<T, E> {
236 type Ok = T;
237 type Error = E;
238
239 #[inline]
240 fn into_result(self) -> Self {
241 self
242 }
243}
244
245pub(crate) struct Bytes<'a>(pub &'a [u8]);
246
247impl<'a> Debug for Bytes<'a> {
248 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
249 write!(f, "{:#02x?}", self.0)
250 }
251}
252
253impl<'a> Display for Bytes<'a> {
254 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
255 write!(f, "{:#02x?}", self.0)
256 }
257}
258
259impl<'a> LowerHex for Bytes<'a> {
260 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
261 write!(f, "{:#02x?}", self.0)
262 }
263}
264
265#[cfg(feature = "defmt")]
266impl<'a> defmt::Format for Bytes<'a> {
267 fn format(&self, fmt: defmt::Formatter) {
268 defmt::write!(fmt, "{:02x}", self.0)
269 }
270}
diff --git a/embassy-stm32-wpan/src/wb55/lhci.rs b/embassy-stm32-wpan/src/wb55/lhci.rs
new file mode 100644
index 000000000..59c8bfb5d
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/lhci.rs
@@ -0,0 +1,112 @@
1use core::ptr;
2
3use crate::cmd::CmdPacket;
4use crate::consts::{TL_EVT_HEADER_SIZE, TlPacketType};
5use crate::evt::{CcEvt, EvtPacket, EvtSerial};
6use crate::tables::{DeviceInfoTable, RssInfoTable, SafeBootInfoTable, TL_DEVICE_INFO_TABLE, WirelessFwInfoTable};
7
8const TL_BLEEVT_CC_OPCODE: u8 = 0x0e;
9const LHCI_OPCODE_C1_DEVICE_INF: u16 = 0xfd62;
10
11const PACKAGE_DATA_PTR: *const u8 = 0x1FFF_7500 as _;
12const UID64_PTR: *const u32 = 0x1FFF_7580 as _;
13
14#[derive(Debug, Copy, Clone)]
15#[repr(C, packed)]
16pub struct LhciC1DeviceInformationCcrp {
17 pub status: u8,
18 pub rev_id: u16,
19 pub dev_code_id: u16,
20 pub package_type: u8,
21 pub device_type_id: u8,
22 pub st_company_id: u32,
23 pub uid64: u32,
24
25 pub uid96_0: u32,
26 pub uid96_1: u32,
27 pub uid96_2: u32,
28
29 pub safe_boot_info_table: SafeBootInfoTable,
30 pub rss_info_table: RssInfoTable,
31 pub wireless_fw_info_table: WirelessFwInfoTable,
32
33 pub app_fw_inf: u32,
34}
35
36impl Default for LhciC1DeviceInformationCcrp {
37 fn default() -> Self {
38 let DeviceInfoTable {
39 safe_boot_info_table,
40 rss_info_table,
41 wireless_fw_info_table,
42 } = unsafe { ptr::read_volatile(TL_DEVICE_INFO_TABLE.as_ptr()) };
43
44 let device_id = stm32_device_signature::device_id();
45 let uid96_0 = (device_id[3] as u32) << 24
46 | (device_id[2] as u32) << 16
47 | (device_id[1] as u32) << 8
48 | device_id[0] as u32;
49 let uid96_1 = (device_id[7] as u32) << 24
50 | (device_id[6] as u32) << 16
51 | (device_id[5] as u32) << 8
52 | device_id[4] as u32;
53 let uid96_2 = (device_id[11] as u32) << 24
54 | (device_id[10] as u32) << 16
55 | (device_id[9] as u32) << 8
56 | device_id[8] as u32;
57
58 let package_type = unsafe { *PACKAGE_DATA_PTR };
59 let uid64 = unsafe { *UID64_PTR };
60 let st_company_id = unsafe { *UID64_PTR.offset(1) } >> 8 & 0x00FF_FFFF;
61 let device_type_id = (unsafe { *UID64_PTR.offset(1) } & 0x000000FF) as u8;
62
63 LhciC1DeviceInformationCcrp {
64 status: 0,
65 rev_id: 0,
66 dev_code_id: 0,
67 package_type,
68 device_type_id,
69 st_company_id,
70 uid64,
71 uid96_0,
72 uid96_1,
73 uid96_2,
74 safe_boot_info_table,
75 rss_info_table,
76 wireless_fw_info_table,
77 app_fw_inf: (1 << 8), // 0.0.1
78 }
79 }
80}
81
82impl LhciC1DeviceInformationCcrp {
83 pub fn new() -> Self {
84 Self::default()
85 }
86
87 pub fn write(&self, cmd_packet: &mut CmdPacket) {
88 let self_size = core::mem::size_of::<LhciC1DeviceInformationCcrp>();
89
90 unsafe {
91 let cmd_packet_ptr: *mut CmdPacket = cmd_packet;
92 let evet_packet_ptr: *mut EvtPacket = cmd_packet_ptr.cast();
93
94 let evt_serial: *mut EvtSerial = &mut (*evet_packet_ptr).evt_serial;
95 let evt_payload = (*evt_serial).evt.payload.as_mut_ptr();
96 let evt_cc: *mut CcEvt = evt_payload.cast();
97 let evt_cc_payload_buf = (*evt_cc).payload.as_mut_ptr();
98
99 (*evt_serial).kind = TlPacketType::LocRsp as u8;
100 (*evt_serial).evt.evt_code = TL_BLEEVT_CC_OPCODE;
101 (*evt_serial).evt.payload_len = TL_EVT_HEADER_SIZE as u8 + self_size as u8;
102
103 (*evt_cc).cmd_code = LHCI_OPCODE_C1_DEVICE_INF;
104 (*evt_cc).num_cmd = 1;
105
106 let self_ptr: *const LhciC1DeviceInformationCcrp = self;
107 let self_buf = self_ptr.cast();
108
109 ptr::copy(self_buf, evt_cc_payload_buf, self_size);
110 }
111 }
112}
diff --git a/embassy-stm32-wpan/src/wb55/mac/commands.rs b/embassy-stm32-wpan/src/wb55/mac/commands.rs
new file mode 100644
index 000000000..d96f0094a
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/mac/commands.rs
@@ -0,0 +1,509 @@
1#![allow(unused)]
2
3use core::{mem, slice};
4
5use smoltcp::wire::ieee802154::Frame;
6
7use super::opcodes::OpcodeM4ToM0;
8use super::typedefs::{
9 AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, MacStatus,
10 PanId, PibId, ScanType, SecurityLevel,
11};
12
13pub trait MacCommand: Sized {
14 const OPCODE: OpcodeM4ToM0;
15
16 fn payload<'a>(&'a self) -> &'a [u8] {
17 unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::<Self>()) }
18 }
19}
20
21/// MLME ASSOCIATE Request used to request an association
22#[repr(C)]
23#[cfg_attr(feature = "defmt", derive(defmt::Format))]
24pub struct AssociateRequest {
25 /// the logical channel on which to attempt association
26 pub channel_number: MacChannel,
27 /// the channel page on which to attempt association
28 pub channel_page: u8,
29 /// coordinator addressing mode
30 pub coord_addr_mode: AddressMode,
31 /// operational capabilities of the associating device
32 pub capability_information: Capabilities,
33 /// the identifier of the PAN with which to associate
34 pub coord_pan_id: PanId,
35 /// the security level to be used
36 pub security_level: SecurityLevel,
37 /// the mode used to identify the key to be used
38 pub key_id_mode: KeyIdMode,
39 /// the originator of the key to be used
40 pub key_source: [u8; 8],
41 /// Coordinator address
42 pub coord_address: MacAddress,
43 /// the index of the key to be used
44 pub key_index: u8,
45}
46
47impl MacCommand for AssociateRequest {
48 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateReq;
49}
50
51/// MLME DISASSOCIATE Request sed to request a disassociation
52#[repr(C)]
53#[cfg_attr(feature = "defmt", derive(defmt::Format))]
54pub struct DisassociateRequest {
55 /// device addressing mode used
56 pub device_addr_mode: AddressMode,
57 /// the identifier of the PAN of the device
58 pub device_pan_id: PanId,
59 /// the reason for the disassociation
60 pub disassociation_reason: DisassociationReason,
61 /// device address
62 pub device_address: MacAddress,
63 /// `true` if the disassociation notification command is to be sent indirectly
64 pub tx_indirect: bool,
65 /// the security level to be used
66 pub security_level: SecurityLevel,
67 /// the mode to be used to indetify the key to be used
68 pub key_id_mode: KeyIdMode,
69 /// the index of the key to be used
70 pub key_index: u8,
71 /// the originator of the key to be used
72 pub key_source: [u8; 8],
73}
74
75impl MacCommand for DisassociateRequest {
76 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDisassociateReq;
77}
78
79/// MLME GET Request used to request a PIB value
80#[repr(C)]
81#[derive(Default)]
82#[cfg_attr(feature = "defmt", derive(defmt::Format))]
83pub struct GetRequest {
84 /// the name of the PIB attribute to read
85 pub pib_attribute: PibId,
86
87 /// byte stuffing to keep 32 bit alignment
88 pub a_stuffing: [u8; 3],
89}
90
91impl MacCommand for GetRequest {
92 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq;
93}
94
95/// MLME GTS Request used to request and maintain GTSs
96#[repr(C)]
97#[cfg_attr(feature = "defmt", derive(defmt::Format))]
98pub struct GtsRequest {
99 /// the characteristics of the GTS
100 pub characteristics: GtsCharacteristics,
101 /// the security level to be used
102 pub security_level: SecurityLevel,
103 /// the mode used to identify the key to be used
104 pub key_id_mode: KeyIdMode,
105 /// the index of the key to be used
106 pub key_index: u8,
107 /// the originator of the key to be used
108 pub key_source: [u8; 8],
109}
110
111impl MacCommand for GtsRequest {
112 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq;
113}
114
115#[repr(C)]
116#[derive(Default)]
117#[cfg_attr(feature = "defmt", derive(defmt::Format))]
118pub struct ResetRequest {
119 /// MAC PIB attributes are set to their default values or not during reset
120 pub set_default_pib: bool,
121 /// byte stuffing to keep 32 bit alignment
122 pub a_stuffing: [u8; 3],
123}
124
125impl MacCommand for ResetRequest {
126 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeResetReq;
127}
128
129/// MLME RX ENABLE Request used to request that the receiver is either enabled
130/// for a finite period of time or disabled
131#[repr(C)]
132#[cfg_attr(feature = "defmt", derive(defmt::Format))]
133pub struct RxEnableRequest {
134 /// the request operation can be deferred or not
135 pub defer_permit: bool,
136 /// configure the transceiver to RX with ranging for a value of
137 /// RANGING_ON or to not enable ranging for RANGING_OFF
138 pub ranging_rx_control: u8,
139 /// byte stuffing to keep 32 bit alignment
140 pub a_stuffing: [u8; 2],
141 /// number of symbols measured before the receiver is to be enabled or disabled
142 pub rx_on_time: [u8; 4],
143 /// number of symbols for which the receiver is to be enabled
144 pub rx_on_duration: [u8; 4],
145}
146
147impl MacCommand for RxEnableRequest {
148 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeRxEnableReq;
149}
150
151/// MLME SCAN Request used to initiate a channel scan over a given list of channels
152#[repr(C)]
153#[cfg_attr(feature = "defmt", derive(defmt::Format))]
154pub struct ScanRequest {
155 /// the type of scan to be performed
156 pub scan_type: ScanType,
157 /// the time spent on scanning each channel
158 pub scan_duration: u8,
159 /// channel page on which to perform the scan
160 pub channel_page: u8,
161 /// security level to be used
162 pub security_level: SecurityLevel,
163 /// indicate which channels are to be scanned
164 pub scan_channels: [u8; 4],
165 /// originator the key to be used
166 pub key_source: [u8; 8],
167 /// mode used to identify the key to be used
168 pub key_id_mode: KeyIdMode,
169 /// index of the key to be used
170 pub key_index: u8,
171 /// byte stuffing to keep 32 bit alignment
172 pub a_stuffing: [u8; 2],
173}
174
175impl MacCommand for ScanRequest {
176 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeScanReq;
177}
178
179/// MLME SET Request used to attempt to write the given value to the indicated PIB attribute
180#[repr(C)]
181#[cfg_attr(feature = "defmt", derive(defmt::Format))]
182pub struct SetRequest {
183 /// the pointer to the value of the PIB attribute to set
184 pub pib_attribute_ptr: *const u8,
185 /// the name of the PIB attribute to set
186 pub pib_attribute: PibId,
187}
188
189impl MacCommand for SetRequest {
190 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSetReq;
191}
192
193/// MLME START Request used by the FFDs to intiate a new PAN or to begin using a new superframe
194/// configuration
195#[repr(C)]
196#[derive(Default)]
197#[cfg_attr(feature = "defmt", derive(defmt::Format))]
198pub struct StartRequest {
199 /// PAN indentifier to used by the device
200 pub pan_id: PanId,
201 /// logical channel on which to begin
202 pub channel_number: MacChannel,
203 /// channel page on which to begin
204 pub channel_page: u8,
205 /// time at which to begin transmitting beacons
206 pub start_time: [u8; 4],
207 /// indicated how often the beacon is to be transmitted
208 pub beacon_order: u8,
209 /// length of the active portion of the superframe
210 pub superframe_order: u8,
211 /// indicated wheter the device is a PAN coordinator or not
212 pub pan_coordinator: bool,
213 /// indicates if the receiver of the beaconing device is disabled or not
214 pub battery_life_extension: bool,
215 /// indicated if the coordinator realignment command is to be trasmitted
216 pub coord_realignment: u8,
217 /// indicated if the coordinator realignment command is to be trasmitted
218 pub coord_realign_security_level: SecurityLevel,
219 /// index of the key to be used
220 pub coord_realign_key_id_index: u8,
221 /// originator of the key to be used
222 pub coord_realign_key_source: [u8; 8],
223 /// security level to be used for beacon frames
224 pub beacon_security_level: SecurityLevel,
225 /// mode used to identify the key to be used
226 pub beacon_key_id_mode: KeyIdMode,
227 /// index of the key to be used
228 pub beacon_key_index: u8,
229 /// originator of the key to be used
230 pub beacon_key_source: [u8; 8],
231}
232
233impl MacCommand for StartRequest {
234 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeStartReq;
235}
236
237/// MLME SYNC Request used to synchronize with the coordinator by acquiring and, if
238/// specified, tracking its beacons
239#[repr(C)]
240#[cfg_attr(feature = "defmt", derive(defmt::Format))]
241pub struct SyncRequest {
242 /// the channel number on which to attempt coordinator synchronization
243 pub channel_number: MacChannel,
244 /// the channel page on which to attempt coordinator synchronization
245 pub channel_page: u8,
246 /// `true` if the MLME is to synchronize with the next beacon and attempts
247 /// to track all future beacons.
248 ///
249 /// `false` if the MLME is to synchronize with only the next beacon
250 pub track_beacon: bool,
251 /// byte stuffing to keep 32 bit alignment
252 pub a_stuffing: [u8; 1],
253}
254
255impl MacCommand for SyncRequest {
256 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSyncReq;
257}
258
259/// MLME POLL Request propmts the device to request data from the coordinator
260#[repr(C)]
261#[cfg_attr(feature = "defmt", derive(defmt::Format))]
262pub struct PollRequest {
263 /// addressing mode of the coordinator
264 pub coord_addr_mode: AddressMode,
265 /// security level to be used
266 pub security_level: SecurityLevel,
267 /// mode used to identify the key to be used
268 pub key_id_mode: KeyIdMode,
269 /// index of the key to be used
270 pub key_index: u8,
271 /// coordinator address
272 pub coord_address: MacAddress,
273 /// originator of the key to be used
274 pub key_source: [u8; 8],
275 /// PAN identifier of the coordinator
276 pub coord_pan_id: PanId,
277 /// byte stuffing to keep 32 bit alignment
278 pub a_stuffing: [u8; 2],
279}
280
281impl MacCommand for PollRequest {
282 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmePollReq;
283}
284
285/// MLME DPS Request allows the next higher layer to request that the PHY utilize a
286/// given pair of preamble codes for a single use pending expiration of the DPSIndexDuration
287#[repr(C)]
288#[cfg_attr(feature = "defmt", derive(defmt::Format))]
289pub struct DpsRequest {
290 /// the index value for the transmitter
291 tx_dps_index: u8,
292 /// the index value of the receiver
293 rx_dps_index: u8,
294 /// the number of symbols for which the transmitter and receiver will utilize the
295 /// respective DPS indices
296 dps_index_duration: u8,
297 /// byte stuffing to keep 32 bit alignment
298 pub a_stuffing: [u8; 1],
299}
300
301impl MacCommand for DpsRequest {
302 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDpsReq;
303}
304
305/// MLME SOUNDING request primitive which is used by the next higher layer to request that
306/// the PHY respond with channel sounding information
307#[repr(C)]
308#[cfg_attr(feature = "defmt", derive(defmt::Format))]
309pub struct SoundingRequest {
310 /// byte stuffing to keep 32 bit alignment
311 pub a_stuffing: [u8; 4],
312}
313
314impl MacCommand for SoundingRequest {
315 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSoundingReq;
316}
317
318/// MLME CALIBRATE request primitive which used to obtain the results of a ranging
319/// calibration request from an RDEV
320#[repr(C)]
321#[cfg_attr(feature = "defmt", derive(defmt::Format))]
322pub struct CalibrateRequest {
323 /// byte stuffing to keep 32 bit alignment
324 pub a_stuffing: [u8; 4],
325}
326
327impl MacCommand for CalibrateRequest {
328 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeCalibrateReq;
329}
330
331/// MCPS DATA Request used for MAC data related requests from the application
332#[repr(C)]
333#[cfg_attr(feature = "defmt", derive(defmt::Format))]
334pub struct DataRequest {
335 /// the handle assocated with the MSDU to be transmitted
336 pub msdu_ptr: *const u8,
337 /// source addressing mode used
338 pub src_addr_mode: AddressMode,
339 /// destination addressing mode used
340 pub dst_addr_mode: AddressMode,
341 /// destination PAN Id
342 pub dst_pan_id: PanId,
343 /// destination address
344 pub dst_address: MacAddress,
345 /// the number of octets contained in the MSDU
346 pub msdu_length: u8,
347 /// the handle assocated with the MSDU to be transmitted
348 pub msdu_handle: u8,
349 /// the ACK transmittion options for the MSDU
350 pub ack_tx: u8,
351 /// `true` if a GTS is to be used for transmission
352 ///
353 /// `false` indicates that the CAP will be used
354 pub gts_tx: bool,
355 /// the pending bit transmission options for the MSDU
356 pub indirect_tx: u8,
357 /// the security level to be used
358 pub security_level: SecurityLevel,
359 /// the mode used to indentify the key to be used
360 pub key_id_mode: KeyIdMode,
361 /// the index of the key to be used
362 pub key_index: u8,
363 /// the originator of the key to be used
364 pub key_source: [u8; 8],
365 /// 2011 - the pulse repitition value
366 pub uwbprf: u8,
367 /// 2011 - the ranging configuration
368 pub ranging: u8,
369 /// 2011 - the preamble symbol repititions
370 pub uwb_preamble_symbol_repetitions: u8,
371 /// 2011 - indicates the data rate
372 pub datrate: u8,
373}
374
375impl DataRequest {
376 pub fn set_buffer<'a>(&'a mut self, buf: &'a [u8]) -> &'a mut Self {
377 self.msdu_ptr = buf as *const _ as *const u8;
378 self.msdu_length = buf.len() as u8;
379
380 self
381 }
382}
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
413impl Default for DataRequest {
414 fn default() -> Self {
415 Self {
416 msdu_ptr: 0 as *const u8,
417 src_addr_mode: AddressMode::NoAddress,
418 dst_addr_mode: AddressMode::NoAddress,
419 dst_pan_id: PanId([0, 0]),
420 dst_address: MacAddress { short: [0, 0] },
421 msdu_length: 0,
422 msdu_handle: 0,
423 ack_tx: 0,
424 gts_tx: false,
425 indirect_tx: 0,
426 security_level: SecurityLevel::Unsecure,
427 key_id_mode: KeyIdMode::Implicite,
428 key_index: 0,
429 key_source: [0u8; 8],
430 uwbprf: 0,
431 ranging: 0,
432 uwb_preamble_symbol_repetitions: 0,
433 datrate: 0,
434 }
435 }
436}
437
438impl MacCommand for DataRequest {
439 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsDataReq;
440}
441
442/// for MCPS PURGE Request used to purge an MSDU from the transaction queue
443#[repr(C)]
444#[cfg_attr(feature = "defmt", derive(defmt::Format))]
445pub struct PurgeRequest {
446 /// the handle associated with the MSDU to be purged from the transaction
447 /// queue
448 pub msdu_handle: u8,
449 /// byte stuffing to keep 32 bit alignment
450 pub a_stuffing: [u8; 3],
451}
452
453impl MacCommand for PurgeRequest {
454 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsPurgeReq;
455}
456
457/// MLME ASSOCIATE Response used to initiate a response to an MLME-ASSOCIATE.indication
458#[repr(C)]
459#[derive(Default)]
460#[cfg_attr(feature = "defmt", derive(defmt::Format))]
461pub struct AssociateResponse {
462 /// extended address of the device requesting association
463 pub device_address: [u8; 8],
464 /// 16-bitshort device address allocated by the coordinator on successful
465 /// association
466 pub assoc_short_address: [u8; 2],
467 /// status of the association attempt
468 pub status: MacStatus,
469 /// security level to be used
470 pub security_level: SecurityLevel,
471 /// the originator of the key to be used
472 pub key_source: [u8; 8],
473 /// the mode used to identify the key to be used
474 pub key_id_mode: KeyIdMode,
475 /// the index of the key to be used
476 pub key_index: u8,
477 /// byte stuffing to keep 32 bit alignment
478 pub a_stuffing: [u8; 2],
479}
480
481impl MacCommand for AssociateResponse {
482 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateRes;
483}
484
485/// MLME ORPHAN Response used to respond to the MLME ORPHAN Indication
486#[repr(C)]
487#[cfg_attr(feature = "defmt", derive(defmt::Format))]
488pub struct OrphanResponse {
489 /// extended address of the orphaned device
490 pub orphan_address: [u8; 8],
491 /// short address allocated to the orphaned device
492 pub short_address: [u8; 2],
493 /// if the orphaned device is associated with coordinator or not
494 pub associated_member: bool,
495 /// security level to be used
496 pub security_level: SecurityLevel,
497 /// the originator of the key to be used
498 pub key_source: [u8; 8],
499 /// the mode used to identify the key to be used
500 pub key_id_mode: KeyIdMode,
501 /// the index of the key to be used
502 pub key_index: u8,
503 /// byte stuffing to keep 32 bit alignment
504 pub a_stuffing: [u8; 2],
505}
506
507impl MacCommand for OrphanResponse {
508 const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeOrphanRes;
509}
diff --git a/embassy-stm32-wpan/src/wb55/mac/consts.rs b/embassy-stm32-wpan/src/wb55/mac/consts.rs
new file mode 100644
index 000000000..56903d980
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/mac/consts.rs
@@ -0,0 +1,4 @@
1pub const MAX_PAN_DESC_SUPPORTED: usize = 6;
2pub const MAX_SOUNDING_LIST_SUPPORTED: usize = 6;
3pub const MAX_PENDING_ADDRESS: usize = 7;
4pub const MAX_ED_SCAN_RESULTS_SUPPORTED: usize = 16;
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/wb55/mac/event.rs b/embassy-stm32-wpan/src/wb55/mac/event.rs
new file mode 100644
index 000000000..39856e185
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/mac/event.rs
@@ -0,0 +1,153 @@
1use core::{mem, ptr};
2
3use super::indications::{
4 AssociateIndication, BeaconNotifyIndication, CommStatusIndication, DataIndication, DisassociateIndication,
5 DpsIndication, GtsIndication, OrphanIndication, PollIndication, SyncLossIndication,
6};
7use super::responses::{
8 AssociateConfirm, CalibrateConfirm, DataConfirm, DisassociateConfirm, DpsConfirm, GetConfirm, GtsConfirm,
9 PollConfirm, PurgeConfirm, ResetConfirm, RxEnableConfirm, ScanConfirm, SetConfirm, SoundingConfirm, StartConfirm,
10};
11use crate::evt::{EvtBox, MemoryManager};
12use crate::mac::opcodes::OpcodeM0ToM4;
13use crate::sub::mac::{self, MacRx};
14
15pub(crate) trait ParseableMacEvent: Sized {
16 fn from_buffer<'a>(buf: &'a [u8]) -> Result<&'a Self, ()> {
17 if buf.len() < mem::size_of::<Self>() {
18 Err(())
19 } else {
20 Ok(unsafe { &*(buf as *const _ as *const Self) })
21 }
22 }
23}
24
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26#[derive(Debug)]
27pub enum MacEvent<'a> {
28 MlmeAssociateCnf(&'a AssociateConfirm),
29 MlmeDisassociateCnf(&'a DisassociateConfirm),
30 MlmeGetCnf(&'a GetConfirm),
31 MlmeGtsCnf(&'a GtsConfirm),
32 MlmeResetCnf(&'a ResetConfirm),
33 MlmeRxEnableCnf(&'a RxEnableConfirm),
34 MlmeScanCnf(&'a ScanConfirm),
35 MlmeSetCnf(&'a SetConfirm),
36 MlmeStartCnf(&'a StartConfirm),
37 MlmePollCnf(&'a PollConfirm),
38 MlmeDpsCnf(&'a DpsConfirm),
39 MlmeSoundingCnf(&'a SoundingConfirm),
40 MlmeCalibrateCnf(&'a CalibrateConfirm),
41 McpsDataCnf(&'a DataConfirm),
42 McpsPurgeCnf(&'a PurgeConfirm),
43 MlmeAssociateInd(&'a AssociateIndication),
44 MlmeDisassociateInd(&'a DisassociateIndication),
45 MlmeBeaconNotifyInd(&'a BeaconNotifyIndication),
46 MlmeCommStatusInd(&'a CommStatusIndication),
47 MlmeGtsInd(&'a GtsIndication),
48 MlmeOrphanInd(&'a OrphanIndication),
49 MlmeSyncLossInd(&'a SyncLossIndication),
50 MlmeDpsInd(&'a DpsIndication),
51 McpsDataInd(&'a DataIndication),
52 MlmePollInd(&'a PollIndication),
53}
54
55impl<'a> MacEvent<'a> {
56 pub(crate) fn new(event_box: EvtBox<MacRx>) -> Result<Self, ()> {
57 let payload = event_box.payload();
58 let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap());
59
60 let opcode = OpcodeM0ToM4::try_from(opcode)?;
61 let buf = &payload[2..];
62
63 // To avoid re-parsing the opcode, we store the result of the parse
64 // this requires use of unsafe because rust cannot assume that a reference will become
65 // invalid when the underlying result is moved. However, because we refer to a "heap"
66 // allocation, the underlying reference will not move until the struct is dropped.
67
68 let mac_event = match opcode {
69 OpcodeM0ToM4::MlmeAssociateCnf => {
70 MacEvent::MlmeAssociateCnf(unsafe { &*(AssociateConfirm::from_buffer(buf)? as *const _) })
71 }
72 OpcodeM0ToM4::MlmeDisassociateCnf => {
73 MacEvent::MlmeDisassociateCnf(unsafe { &*(DisassociateConfirm::from_buffer(buf)? as *const _) })
74 }
75 OpcodeM0ToM4::MlmeGetCnf => MacEvent::MlmeGetCnf(unsafe { &*(GetConfirm::from_buffer(buf)? as *const _) }),
76 OpcodeM0ToM4::MlmeGtsCnf => MacEvent::MlmeGtsCnf(unsafe { &*(GtsConfirm::from_buffer(buf)? as *const _) }),
77 OpcodeM0ToM4::MlmeResetCnf => {
78 MacEvent::MlmeResetCnf(unsafe { &*(ResetConfirm::from_buffer(buf)? as *const _) })
79 }
80 OpcodeM0ToM4::MlmeRxEnableCnf => {
81 MacEvent::MlmeRxEnableCnf(unsafe { &*(RxEnableConfirm::from_buffer(buf)? as *const _) })
82 }
83 OpcodeM0ToM4::MlmeScanCnf => {
84 MacEvent::MlmeScanCnf(unsafe { &*(ScanConfirm::from_buffer(buf)? as *const _) })
85 }
86 OpcodeM0ToM4::MlmeSetCnf => MacEvent::MlmeSetCnf(unsafe { &*(SetConfirm::from_buffer(buf)? as *const _) }),
87 OpcodeM0ToM4::MlmeStartCnf => {
88 MacEvent::MlmeStartCnf(unsafe { &*(StartConfirm::from_buffer(buf)? as *const _) })
89 }
90 OpcodeM0ToM4::MlmePollCnf => {
91 MacEvent::MlmePollCnf(unsafe { &*(PollConfirm::from_buffer(buf)? as *const _) })
92 }
93 OpcodeM0ToM4::MlmeDpsCnf => MacEvent::MlmeDpsCnf(unsafe { &*(DpsConfirm::from_buffer(buf)? as *const _) }),
94 OpcodeM0ToM4::MlmeSoundingCnf => {
95 MacEvent::MlmeSoundingCnf(unsafe { &*(SoundingConfirm::from_buffer(buf)? as *const _) })
96 }
97 OpcodeM0ToM4::MlmeCalibrateCnf => {
98 MacEvent::MlmeCalibrateCnf(unsafe { &*(CalibrateConfirm::from_buffer(buf)? as *const _) })
99 }
100 OpcodeM0ToM4::McpsDataCnf => {
101 MacEvent::McpsDataCnf(unsafe { &*(DataConfirm::from_buffer(buf)? as *const _) })
102 }
103 OpcodeM0ToM4::McpsPurgeCnf => {
104 MacEvent::McpsPurgeCnf(unsafe { &*(PurgeConfirm::from_buffer(buf)? as *const _) })
105 }
106 OpcodeM0ToM4::MlmeAssociateInd => {
107 MacEvent::MlmeAssociateInd(unsafe { &*(AssociateIndication::from_buffer(buf)? as *const _) })
108 }
109 OpcodeM0ToM4::MlmeDisassociateInd => {
110 MacEvent::MlmeDisassociateInd(unsafe { &*(DisassociateIndication::from_buffer(buf)? as *const _) })
111 }
112 OpcodeM0ToM4::MlmeBeaconNotifyInd => {
113 MacEvent::MlmeBeaconNotifyInd(unsafe { &*(BeaconNotifyIndication::from_buffer(buf)? as *const _) })
114 }
115 OpcodeM0ToM4::MlmeCommStatusInd => {
116 MacEvent::MlmeCommStatusInd(unsafe { &*(CommStatusIndication::from_buffer(buf)? as *const _) })
117 }
118 OpcodeM0ToM4::MlmeGtsInd => {
119 MacEvent::MlmeGtsInd(unsafe { &*(GtsIndication::from_buffer(buf)? as *const _) })
120 }
121 OpcodeM0ToM4::MlmeOrphanInd => {
122 MacEvent::MlmeOrphanInd(unsafe { &*(OrphanIndication::from_buffer(buf)? as *const _) })
123 }
124 OpcodeM0ToM4::MlmeSyncLossInd => {
125 MacEvent::MlmeSyncLossInd(unsafe { &*(SyncLossIndication::from_buffer(buf)? as *const _) })
126 }
127 OpcodeM0ToM4::MlmeDpsInd => {
128 MacEvent::MlmeDpsInd(unsafe { &*(DpsIndication::from_buffer(buf)? as *const _) })
129 }
130 OpcodeM0ToM4::McpsDataInd => {
131 MacEvent::McpsDataInd(unsafe { &*(DataIndication::from_buffer(buf)? as *const _) })
132 }
133 OpcodeM0ToM4::MlmePollInd => {
134 MacEvent::MlmePollInd(unsafe { &*(PollIndication::from_buffer(buf)? as *const _) })
135 }
136 };
137
138 // Forget the event box so that drop isn't called
139 // We want to handle the lifetime ourselves
140
141 mem::forget(event_box);
142
143 Ok(mac_event)
144 }
145}
146
147unsafe impl<'a> Send for MacEvent<'a> {}
148
149impl<'a> Drop for MacEvent<'a> {
150 fn drop(&mut self) {
151 unsafe { mac::MacRx::drop_event_packet(ptr::null_mut()) };
152 }
153}
diff --git a/embassy-stm32-wpan/src/wb55/mac/indications.rs b/embassy-stm32-wpan/src/wb55/mac/indications.rs
new file mode 100644
index 000000000..5673514c9
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/mac/indications.rs
@@ -0,0 +1,300 @@
1use core::slice;
2
3use smoltcp::wire::Ieee802154FrameType;
4use smoltcp::wire::ieee802154::Frame;
5
6use super::consts::MAX_PENDING_ADDRESS;
7use super::event::ParseableMacEvent;
8use super::typedefs::{
9 AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor,
10 PanId, SecurityLevel,
11};
12use crate::mac::typedefs::MacAddressAndMode;
13
14/// MLME ASSOCIATE Indication which will be used by the MAC
15/// to indicate the reception of an association request command
16#[repr(C)]
17#[derive(Debug)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19pub struct AssociateIndication {
20 /// Extended address of the device requesting association
21 pub device_address: [u8; 8],
22 /// Operational capabilities of the device requesting association
23 pub capability_information: Capabilities,
24 /// Security level purportedly used by the received MAC command frame
25 pub security_level: SecurityLevel,
26 /// The mode used to identify the key used by the originator of frame
27 pub key_id_mode: KeyIdMode,
28 /// Index of the key used by the originator of the received frame
29 pub key_index: u8,
30 /// The originator of the key used by the originator of the received frame
31 pub key_source: [u8; 8],
32}
33
34impl ParseableMacEvent for AssociateIndication {}
35
36/// MLME DISASSOCIATE indication which will be used to send
37/// disassociation indication to the application.
38#[repr(C)]
39#[derive(Debug)]
40#[cfg_attr(feature = "defmt", derive(defmt::Format))]
41pub struct DisassociateIndication {
42 /// Extended address of the device requesting association
43 pub device_address: [u8; 8],
44 /// The reason for the disassociation
45 pub disassociation_reason: DisassociationReason,
46 /// The security level to be used
47 pub security_level: SecurityLevel,
48 /// The mode used to identify the key to be used
49 pub key_id_mode: KeyIdMode,
50 /// The index of the key to be used
51 pub key_index: u8,
52 /// The originator of the key to be used
53 pub key_source: [u8; 8],
54}
55
56impl ParseableMacEvent for DisassociateIndication {}
57
58/// MLME BEACON NOTIIFY Indication which is used to send parameters contained
59/// within a beacon frame received by the MAC to the application
60#[repr(C)]
61#[derive(Debug)]
62#[cfg_attr(feature = "defmt", derive(defmt::Format))]
63pub struct BeaconNotifyIndication {
64 /// he set of octets comprising the beacon payload to be transferred
65 /// from the MAC sublayer entity to the next higher layer
66 pub sdu_ptr: *const u8,
67 /// The PAN Descriptor for the received beacon
68 pub pan_descriptor: PanDescriptor,
69 /// The list of addresses of the devices
70 pub addr_list: [MacAddress; MAX_PENDING_ADDRESS],
71 /// Beacon Sequence Number
72 pub bsn: u8,
73 /// The beacon pending address specification
74 pub pend_addr_spec: u8,
75 /// Number of octets contained in the beacon payload of the beacon frame
76 pub sdu_length: u8,
77}
78
79impl ParseableMacEvent for BeaconNotifyIndication {}
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
97/// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status
98#[repr(C)]
99#[derive(Debug)]
100#[cfg_attr(feature = "defmt", derive(defmt::Format))]
101pub struct CommStatusIndication {
102 /// The 16-bit PAN identifier of the device from which the frame
103 /// was received or to which the frame was being sent
104 pub pan_id: PanId,
105 /// Source addressing mode
106 pub src_addr_mode: AddressMode,
107 /// Destination addressing mode
108 pub dst_addr_mode: AddressMode,
109 /// Source address
110 pub src_address: MacAddress,
111 /// Destination address
112 pub dst_address: MacAddress,
113 /// The communications status
114 pub status: MacStatus,
115 /// Security level to be used
116 pub security_level: SecurityLevel,
117 /// Mode used to identify the key to be used
118 pub key_id_mode: KeyIdMode,
119 /// Index of the key to be used
120 pub key_index: u8,
121 /// Originator of the key to be used
122 pub key_source: [u8; 8],
123}
124
125impl ParseableMacEvent for CommStatusIndication {}
126
127/// MLME GTS Indication indicates that a GTS has been allocated or that a
128/// previously allocated GTS has been deallocated
129#[repr(C)]
130#[derive(Debug)]
131#[cfg_attr(feature = "defmt", derive(defmt::Format))]
132pub struct GtsIndication {
133 /// The short address of the device that has been allocated or deallocated a GTS
134 pub device_address: [u8; 2],
135 /// The characteristics of the GTS
136 pub gts_characteristics: u8,
137 /// Security level to be used
138 pub security_level: SecurityLevel,
139 /// Mode used to identify the key to be used
140 pub key_id_mode: KeyIdMode,
141 /// Index of the key to be used
142 pub key_index: u8,
143 /// byte stuffing to keep 32 bit alignment
144 a_stuffing: [u8; 2],
145 /// Originator of the key to be used
146 pub key_source: [u8; 8],
147}
148
149impl ParseableMacEvent for GtsIndication {}
150
151/// MLME ORPHAN Indication which is used by the coordinator to notify the
152/// application of the presence of an orphaned device
153#[repr(C)]
154#[derive(Debug)]
155#[cfg_attr(feature = "defmt", derive(defmt::Format))]
156pub struct OrphanIndication {
157 /// Extended address of the orphaned device
158 pub orphan_address: [u8; 8],
159 /// Originator of the key used by the originator of the received frame
160 pub key_source: [u8; 8],
161 /// Security level purportedly used by the received MAC command frame
162 pub security_level: SecurityLevel,
163 /// Mode used to identify the key used by originator of received frame
164 pub key_id_mode: KeyIdMode,
165 /// Index of the key used by the originator of the received frame
166 pub key_index: u8,
167 /// byte stuffing to keep 32 bit alignment
168 a_stuffing: [u8; 1],
169}
170
171impl ParseableMacEvent for OrphanIndication {}
172
173/// MLME SYNC LOSS Indication which is used by the MAC to indicate the loss
174/// of synchronization with the coordinator
175#[repr(C)]
176#[derive(Debug)]
177#[cfg_attr(feature = "defmt", derive(defmt::Format))]
178pub struct SyncLossIndication {
179 /// The PAN identifier with which the device lost synchronization or to which it was realigned
180 pub pan_id: PanId,
181 /// The reason that synchronization was lost
182 pub loss_reason: u8,
183 /// The logical channel on which the device lost synchronization or to whi
184 pub channel_number: MacChannel,
185 /// The channel page on which the device lost synchronization or to which
186 pub channel_page: u8,
187 /// The security level used by the received MAC frame
188 pub security_level: SecurityLevel,
189 /// Mode used to identify the key used by originator of received frame
190 pub key_id_mode: KeyIdMode,
191 /// Index of the key used by the originator of the received frame
192 pub key_index: u8,
193 /// Originator of the key used by the originator of the received frame
194 pub key_source: [u8; 8],
195}
196
197impl ParseableMacEvent for SyncLossIndication {}
198
199/// MLME DPS Indication which indicates the expiration of the DPSIndexDuration
200/// and the resetting of the DPS values in the PHY
201#[repr(C)]
202#[derive(Debug)]
203#[cfg_attr(feature = "defmt", derive(defmt::Format))]
204pub struct DpsIndication {
205 /// byte stuffing to keep 32 bit alignment
206 a_stuffing: [u8; 4],
207}
208
209impl ParseableMacEvent for DpsIndication {}
210
211#[repr(C)]
212#[derive(Debug)]
213#[cfg_attr(feature = "defmt", derive(defmt::Format))]
214pub struct DataIndication {
215 /// Pointer to the set of octets forming the MSDU being indicated
216 pub msdu_ptr: *const u8,
217 /// Source addressing mode used
218 pub src_addr_mode: AddressMode,
219 /// Source PAN ID
220 pub src_pan_id: PanId,
221 /// Source address
222 pub src_address: MacAddress,
223 /// Destination addressing mode used
224 pub dst_addr_mode: AddressMode,
225 /// Destination PAN ID
226 pub dst_pan_id: PanId,
227 /// Destination address
228 pub dst_address: MacAddress,
229 /// The number of octets contained in the MSDU being indicated
230 pub msdu_length: u8,
231 /// QI value measured during reception of the MPDU
232 pub mpdu_link_quality: u8,
233 /// The data sequence number of the received data frame
234 pub dsn: u8,
235 /// The time, in symbols, at which the data were received
236 pub time_stamp: [u8; 4],
237 /// The security level purportedly used by the received data frame
238 security_level: SecurityLevel,
239 /// Mode used to identify the key used by originator of received frame
240 key_id_mode: KeyIdMode,
241 /// The originator of the key
242 pub key_source: [u8; 8],
243 /// The index of the key
244 pub key_index: u8,
245 /// he pulse repetition value of the received PPDU
246 pub uwbprf: u8,
247 /// The preamble symbol repetitions of the UWB PHY frame
248 pub uwn_preamble_symbol_repetitions: u8,
249 /// Indicates the data rate
250 pub datrate: u8,
251 /// time units corresponding to an RMARKER at the antenna at the end of a ranging exchange,
252 pub ranging_received: u8,
253 pub ranging_counter_start: u32,
254 pub ranging_counter_stop: u32,
255 /// ime units in a message exchange over which the tracking offset was measured
256 pub ranging_tracking_interval: u32,
257 /// time units slipped or advanced by the radio tracking system
258 pub ranging_offset: u32,
259 /// The FoM characterizing the ranging measurement
260 pub ranging_fom: u8,
261 /// The Received Signal Strength Indicator measured
262 pub rssi: u8,
263}
264
265impl ParseableMacEvent for DataIndication {}
266
267impl DataIndication {
268 pub fn payload<'a>(&'a self) -> &'a mut [u8] {
269 unsafe { slice::from_raw_parts_mut(self.msdu_ptr as *mut _, self.msdu_length as usize) }
270 }
271}
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
288/// MLME POLL Indication which will be used for indicating the Data Request
289/// reception to upper layer as defined in Zigbee r22 - D.8.2
290#[repr(C)]
291#[derive(Debug)]
292#[cfg_attr(feature = "defmt", derive(defmt::Format))]
293pub struct PollIndication {
294 /// addressing mode used
295 pub addr_mode: AddressMode,
296 /// Poll requester address
297 pub request_address: MacAddress,
298}
299
300impl ParseableMacEvent for PollIndication {}
diff --git a/embassy-stm32-wpan/src/wb55/mac/macros.rs b/embassy-stm32-wpan/src/wb55/mac/macros.rs
new file mode 100644
index 000000000..1a988a779
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/mac/macros.rs
@@ -0,0 +1,32 @@
1#[macro_export]
2macro_rules! numeric_enum {
3 (#[repr($repr:ident)]
4 $(#$attrs:tt)* $vis:vis enum $name:ident {
5 $($(#$enum_attrs:tt)* $enum:ident = $constant:expr),* $(,)?
6 } ) => {
7 #[repr($repr)]
8 $(#$attrs)*
9 $vis enum $name {
10 $($(#$enum_attrs)* $enum = $constant),*
11 }
12
13 impl ::core::convert::TryFrom<$repr> for $name {
14 type Error = ();
15
16 fn try_from(value: $repr) -> ::core::result::Result<Self, ()> {
17 match value {
18 $($constant => Ok( $name :: $enum ),)*
19 _ => Err(())
20 }
21 }
22 }
23
24 impl ::core::convert::From<$name> for $repr {
25 fn from(value: $name) -> $repr {
26 match value {
27 $($name :: $enum => $constant,)*
28 }
29 }
30 }
31 }
32}
diff --git a/embassy-stm32-wpan/src/wb55/mac/mod.rs b/embassy-stm32-wpan/src/wb55/mac/mod.rs
new file mode 100644
index 000000000..ac50a6b29
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/mac/mod.rs
@@ -0,0 +1,17 @@
1pub mod commands;
2mod consts;
3pub mod control;
4mod driver;
5pub mod event;
6pub mod indications;
7mod macros;
8mod opcodes;
9pub mod responses;
10pub mod runner;
11pub mod typedefs;
12
13pub use crate::mac::control::Control;
14pub use crate::mac::driver::{Driver, DriverState};
15pub use crate::mac::runner::Runner;
16
17const MTU: usize = 127;
diff --git a/embassy-stm32-wpan/src/wb55/mac/opcodes.rs b/embassy-stm32-wpan/src/wb55/mac/opcodes.rs
new file mode 100644
index 000000000..fd7011873
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/mac/opcodes.rs
@@ -0,0 +1,92 @@
1const ST_VENDOR_OGF: u16 = 0x3F;
2const MAC_802_15_4_CMD_OPCODE_OFFSET: u16 = 0x280;
3
4const fn opcode(ocf: u16) -> isize {
5 ((ST_VENDOR_OGF << 9) | (MAC_802_15_4_CMD_OPCODE_OFFSET + ocf)) as isize
6}
7
8#[cfg_attr(feature = "defmt", derive(defmt::Format))]
9pub enum OpcodeM4ToM0 {
10 MlmeAssociateReq = opcode(0x00),
11 MlmeAssociateRes = opcode(0x01),
12 MlmeDisassociateReq = opcode(0x02),
13 MlmeGetReq = opcode(0x03),
14 MlmeGtsReq = opcode(0x04),
15 MlmeOrphanRes = opcode(0x05),
16 MlmeResetReq = opcode(0x06),
17 MlmeRxEnableReq = opcode(0x07),
18 MlmeScanReq = opcode(0x08),
19 MlmeSetReq = opcode(0x09),
20 MlmeStartReq = opcode(0x0A),
21 MlmeSyncReq = opcode(0x0B),
22 MlmePollReq = opcode(0x0C),
23 MlmeDpsReq = opcode(0x0D),
24 MlmeSoundingReq = opcode(0x0E),
25 MlmeCalibrateReq = opcode(0x0F),
26 McpsDataReq = opcode(0x10),
27 McpsPurgeReq = opcode(0x11),
28}
29
30#[cfg_attr(feature = "defmt", derive(defmt::Format))]
31pub enum OpcodeM0ToM4 {
32 MlmeAssociateCnf = 0x00,
33 MlmeDisassociateCnf,
34 MlmeGetCnf,
35 MlmeGtsCnf,
36 MlmeResetCnf,
37 MlmeRxEnableCnf,
38 MlmeScanCnf,
39 MlmeSetCnf,
40 MlmeStartCnf,
41 MlmePollCnf,
42 MlmeDpsCnf,
43 MlmeSoundingCnf,
44 MlmeCalibrateCnf,
45 McpsDataCnf,
46 McpsPurgeCnf,
47 MlmeAssociateInd,
48 MlmeDisassociateInd,
49 MlmeBeaconNotifyInd,
50 MlmeCommStatusInd,
51 MlmeGtsInd,
52 MlmeOrphanInd,
53 MlmeSyncLossInd,
54 MlmeDpsInd,
55 McpsDataInd,
56 MlmePollInd,
57}
58
59impl TryFrom<u16> for OpcodeM0ToM4 {
60 type Error = ();
61
62 fn try_from(value: u16) -> Result<Self, Self::Error> {
63 match value {
64 0 => Ok(Self::MlmeAssociateCnf),
65 1 => Ok(Self::MlmeDisassociateCnf),
66 2 => Ok(Self::MlmeGetCnf),
67 3 => Ok(Self::MlmeGtsCnf),
68 4 => Ok(Self::MlmeResetCnf),
69 5 => Ok(Self::MlmeRxEnableCnf),
70 6 => Ok(Self::MlmeScanCnf),
71 7 => Ok(Self::MlmeSetCnf),
72 8 => Ok(Self::MlmeStartCnf),
73 9 => Ok(Self::MlmePollCnf),
74 10 => Ok(Self::MlmeDpsCnf),
75 11 => Ok(Self::MlmeSoundingCnf),
76 12 => Ok(Self::MlmeCalibrateCnf),
77 13 => Ok(Self::McpsDataCnf),
78 14 => Ok(Self::McpsPurgeCnf),
79 15 => Ok(Self::MlmeAssociateInd),
80 16 => Ok(Self::MlmeDisassociateInd),
81 17 => Ok(Self::MlmeBeaconNotifyInd),
82 18 => Ok(Self::MlmeCommStatusInd),
83 19 => Ok(Self::MlmeGtsInd),
84 20 => Ok(Self::MlmeOrphanInd),
85 21 => Ok(Self::MlmeSyncLossInd),
86 22 => Ok(Self::MlmeDpsInd),
87 23 => Ok(Self::McpsDataInd),
88 24 => Ok(Self::MlmePollInd),
89 _ => Err(()),
90 }
91 }
92}
diff --git a/embassy-stm32-wpan/src/wb55/mac/responses.rs b/embassy-stm32-wpan/src/wb55/mac/responses.rs
new file mode 100644
index 000000000..544fdaae8
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/mac/responses.rs
@@ -0,0 +1,273 @@
1use super::consts::{MAX_ED_SCAN_RESULTS_SUPPORTED, MAX_PAN_DESC_SUPPORTED, MAX_SOUNDING_LIST_SUPPORTED};
2use super::event::ParseableMacEvent;
3use super::typedefs::{
4 AddressMode, AssociationStatus, KeyIdMode, MacAddress, MacStatus, PanDescriptor, PanId, PibId, ScanType,
5 SecurityLevel,
6};
7
8/// MLME ASSOCIATE Confirm used to inform of the initiating device whether
9/// its request to associate was successful or unsuccessful
10#[repr(C)]
11#[derive(Debug)]
12#[cfg_attr(feature = "defmt", derive(defmt::Format))]
13pub struct AssociateConfirm {
14 /// short address allocated by the coordinator on successful association
15 pub assoc_short_address: [u8; 2],
16 /// status of the association request
17 pub status: AssociationStatus,
18 /// security level to be used
19 pub security_level: SecurityLevel,
20 /// the originator of the key to be used
21 pub key_source: [u8; 8],
22 /// the mode used to identify the key to be used
23 pub key_id_mode: KeyIdMode,
24 /// the index of the key to be used
25 pub key_index: u8,
26 /// byte stuffing to keep 32 bit alignment
27 a_stuffing: [u8; 2],
28}
29
30impl ParseableMacEvent for AssociateConfirm {}
31
32/// MLME DISASSOCIATE Confirm used to send disassociation Confirmation to the application.
33#[repr(C)]
34#[derive(Debug)]
35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36pub struct DisassociateConfirm {
37 /// status of the disassociation attempt
38 pub status: MacStatus,
39 /// device addressing mode used
40 pub device_addr_mode: AddressMode,
41 /// the identifier of the PAN of the device
42 pub device_pan_id: PanId,
43 /// device address
44 pub device_address: MacAddress,
45}
46
47impl ParseableMacEvent for DisassociateConfirm {}
48
49/// MLME GET Confirm which requests information about a given PIB attribute
50#[repr(C)]
51#[derive(Debug)]
52#[cfg_attr(feature = "defmt", derive(defmt::Format))]
53pub struct GetConfirm {
54 /// The pointer to the value of the PIB attribute attempted to read
55 pub pib_attribute_value_ptr: *const u8,
56 /// Status of the GET attempt
57 pub status: MacStatus,
58 /// The name of the PIB attribute attempted to read
59 pub pib_attribute: PibId,
60 /// The lenght of the PIB attribute Value return
61 pub pib_attribute_value_len: u8,
62 /// byte stuffing to keep 32 bit alignment
63 a_stuffing: [u8; 1],
64}
65
66impl ParseableMacEvent for GetConfirm {}
67
68/// MLME GTS Confirm which eports the results of a request to allocate a new GTS
69/// or to deallocate an existing GTS
70#[repr(C)]
71#[derive(Debug)]
72#[cfg_attr(feature = "defmt", derive(defmt::Format))]
73pub struct GtsConfirm {
74 /// The characteristics of the GTS
75 pub gts_characteristics: u8,
76 /// The status of the GTS reques
77 pub status: MacStatus,
78 /// byte stuffing to keep 32 bit alignment
79 a_stuffing: [u8; 2],
80}
81
82impl ParseableMacEvent for GtsConfirm {}
83
84/// MLME RESET Confirm which is used to report the results of the reset operation
85#[repr(C)]
86#[derive(Debug)]
87#[cfg_attr(feature = "defmt", derive(defmt::Format))]
88pub struct ResetConfirm {
89 /// The result of the reset operation
90 pub status: MacStatus,
91 /// byte stuffing to keep 32 bit alignment
92 a_stuffing: [u8; 3],
93}
94
95impl ParseableMacEvent for ResetConfirm {}
96
97/// MLME RX ENABLE Confirm which is used to report the results of the attempt
98/// to enable or disable the receiver
99#[repr(C)]
100#[derive(Debug)]
101#[cfg_attr(feature = "defmt", derive(defmt::Format))]
102pub struct RxEnableConfirm {
103 /// Result of the request to enable or disable the receiver
104 pub status: MacStatus,
105 /// byte stuffing to keep 32 bit alignment
106 a_stuffing: [u8; 3],
107}
108
109impl ParseableMacEvent for RxEnableConfirm {}
110
111/// MLME SCAN Confirm which is used to report the result of the channel scan request
112#[repr(C)]
113#[derive(Debug)]
114#[cfg_attr(feature = "defmt", derive(defmt::Format))]
115pub struct ScanConfirm {
116 /// Status of the scan request
117 pub status: MacStatus,
118 /// The type of scan performed
119 pub scan_type: ScanType,
120 /// Channel page on which the scan was performed
121 pub channel_page: u8,
122 /// Channels given in the request which were not scanned
123 pub unscanned_channels: [u8; 4],
124 /// Number of elements returned in the appropriate result lists
125 pub result_list_size: u8,
126 /// List of energy measurements
127 pub energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED],
128 /// List of PAN descriptors
129 pub pan_descriptor_list: [PanDescriptor; MAX_PAN_DESC_SUPPORTED],
130 /// Categorization of energy detected in channel
131 pub detected_category: u8,
132 /// For UWB PHYs, the list of energy measurements taken
133 pub uwb_energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED],
134}
135
136impl ParseableMacEvent for ScanConfirm {}
137
138/// MLME SET Confirm which reports the result of an attempt to write a value to a PIB attribute
139#[repr(C)]
140#[derive(Debug)]
141#[cfg_attr(feature = "defmt", derive(defmt::Format))]
142pub struct SetConfirm {
143 /// The result of the set operation
144 pub status: MacStatus,
145 /// The name of the PIB attribute that was written
146 pub pin_attribute: PibId,
147 /// byte stuffing to keep 32 bit alignment
148 a_stuffing: [u8; 2],
149}
150
151impl ParseableMacEvent for SetConfirm {}
152
153/// MLME START Confirm which is used to report the results of the attempt to
154/// start using a new superframe configuration
155#[repr(C)]
156#[derive(Debug)]
157#[cfg_attr(feature = "defmt", derive(defmt::Format))]
158pub struct StartConfirm {
159 /// Result of the attempt to start using an updated superframe configuration
160 pub status: MacStatus,
161 /// byte stuffing to keep 32 bit alignment
162 a_stuffing: [u8; 3],
163}
164
165impl ParseableMacEvent for StartConfirm {}
166
167/// MLME POLL Confirm which is used to report the result of a request to poll the coordinator for data
168#[repr(C)]
169#[derive(Debug)]
170#[cfg_attr(feature = "defmt", derive(defmt::Format))]
171pub struct PollConfirm {
172 /// The status of the data request
173 pub status: MacStatus,
174 /// byte stuffing to keep 32 bit alignment
175 a_stuffing: [u8; 3],
176}
177
178impl ParseableMacEvent for PollConfirm {}
179
180/// MLME DPS Confirm which reports the results of the attempt to enable or disable the DPS
181#[repr(C)]
182#[derive(Debug)]
183#[cfg_attr(feature = "defmt", derive(defmt::Format))]
184pub struct DpsConfirm {
185 /// The status of the DPS request
186 pub status: MacStatus,
187 /// byte stuffing to keep 32 bit alignment
188 a_stuffing: [u8; 3],
189}
190
191impl ParseableMacEvent for DpsConfirm {}
192
193/// MLME SOUNDING Confirm which reports the result of a request to the PHY to provide
194/// channel sounding information
195#[repr(C)]
196#[derive(Debug)]
197#[cfg_attr(feature = "defmt", derive(defmt::Format))]
198pub struct SoundingConfirm {
199 /// Results of the sounding measurement
200 pub sounding_list: [u8; MAX_SOUNDING_LIST_SUPPORTED],
201
202 status: u8,
203}
204
205impl ParseableMacEvent for SoundingConfirm {}
206
207/// MLME CALIBRATE Confirm which reports the result of a request to the PHY
208/// to provide internal propagation path information
209#[repr(C)]
210#[derive(Debug)]
211#[cfg_attr(feature = "defmt", derive(defmt::Format))]
212pub struct CalibrateConfirm {
213 /// The status of the attempt to return sounding data
214 pub status: MacStatus,
215 /// byte stuffing to keep 32 bit alignment
216 a_stuffing: [u8; 3],
217 /// A count of the propagation time from the ranging counter
218 /// to the transmit antenna
219 pub cal_tx_rmaker_offset: u32,
220 /// A count of the propagation time from the receive antenna
221 /// to the ranging counter
222 pub cal_rx_rmaker_offset: u32,
223}
224
225impl ParseableMacEvent for CalibrateConfirm {}
226
227/// MCPS DATA Confirm which will be used for reporting the results of
228/// MAC data related requests from the application
229#[repr(C)]
230#[derive(Debug)]
231#[cfg_attr(feature = "defmt", derive(defmt::Format))]
232pub struct DataConfirm {
233 /// The handle associated with the MSDU being confirmed
234 pub msdu_handle: u8,
235 /// The time, in symbols, at which the data were transmitted
236 pub time_stamp: [u8; 4],
237 /// ranging status
238 pub ranging_received: u8,
239 /// The status of the last MSDU transmission
240 pub status: MacStatus,
241 /// time units corresponding to an RMARKER at the antenna at
242 /// the beginning of a ranging exchange
243 pub ranging_counter_start: u32,
244 /// time units corresponding to an RMARKER at the antenna
245 /// at the end of a ranging exchange
246 pub ranging_counter_stop: u32,
247 /// time units in a message exchange over which the tracking offset was measured
248 pub ranging_tracking_interval: u32,
249 /// time units slipped or advanced by the radio tracking system
250 pub ranging_offset: u32,
251 /// The FoM characterizing the ranging measurement
252 pub ranging_fom: u8,
253 /// byte stuffing to keep 32 bit alignment
254 a_stuffing: [u8; 3],
255}
256
257impl ParseableMacEvent for DataConfirm {}
258
259/// MCPS PURGE Confirm which will be used by the MAC to notify the application of
260/// the status of its request to purge an MSDU from the transaction queue
261#[repr(C)]
262#[derive(Debug)]
263#[cfg_attr(feature = "defmt", derive(defmt::Format))]
264pub struct PurgeConfirm {
265 /// Handle associated with the MSDU requested to be purged from the transaction queue
266 pub msdu_handle: u8,
267 /// The status of the request
268 pub status: MacStatus,
269 /// byte stuffing to keep 32 bit alignment
270 a_stuffing: [u8; 2],
271}
272
273impl ParseableMacEvent for PurgeConfirm {}
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/wb55/mac/typedefs.rs b/embassy-stm32-wpan/src/wb55/mac/typedefs.rs
new file mode 100644
index 000000000..175d4a37d
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/mac/typedefs.rs
@@ -0,0 +1,434 @@
1use core::fmt::Debug;
2
3use smoltcp::wire::ieee802154::{Address, AddressingMode, Pan};
4
5use crate::numeric_enum;
6
7#[derive(Debug)]
8#[cfg_attr(feature = "defmt", derive(defmt::Format))]
9pub enum MacError {
10 Error = 0x01,
11 NotImplemented = 0x02,
12 NotSupported = 0x03,
13 HardwareNotSupported = 0x04,
14 Undefined = 0x05,
15}
16
17impl From<u8> for MacError {
18 fn from(value: u8) -> Self {
19 match value {
20 0x01 => Self::Error,
21 0x02 => Self::NotImplemented,
22 0x03 => Self::NotSupported,
23 0x04 => Self::HardwareNotSupported,
24 0x05 => Self::Undefined,
25 _ => Self::Undefined,
26 }
27 }
28}
29
30numeric_enum! {
31 #[repr(u8)]
32 #[derive(Debug, Default)]
33 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
34 pub enum MacStatus {
35 #[default]
36 Success = 0x00,
37 Failure = 0xFF
38 }
39}
40
41numeric_enum! {
42 #[repr(u8)]
43 /// this enum contains all the MAC PIB Ids
44 #[derive(Default, Debug)]
45 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
46 pub enum PibId {
47 // PHY
48 #[default]
49 CurrentChannel = 0x00,
50 ChannelsSupported = 0x01,
51 TransmitPower = 0x02,
52 CCAMode = 0x03,
53 CurrentPage = 0x04,
54 MaxFrameDuration = 0x05,
55 SHRDuration = 0x06,
56 SymbolsPerOctet = 0x07,
57
58 // MAC
59 AckWaitDuration = 0x40,
60 AssociationPermit = 0x41,
61 AutoRequest = 0x42,
62 BeaconPayload = 0x45,
63 BeaconPayloadLength = 0x46,
64 BeaconOrder = 0x47,
65 Bsn = 0x49,
66 CoordExtendedAdddress = 0x4A,
67 CoordShortAddress = 0x4B,
68 Dsn = 0x4C,
69 MaxFrameTotalWaitTime = 0x58,
70 MaxFrameRetries = 0x59,
71 PanId = 0x50,
72 ResponseWaitTime = 0x5A,
73 RxOnWhenIdle = 0x52,
74 SecurityEnabled = 0x5D,
75 ShortAddress = 0x53,
76 SuperframeOrder = 0x54,
77 TimestampSupported = 0x5C,
78 TransactionPersistenceTime = 0x55,
79 MaxBe = 0x57,
80 LifsPeriod = 0x5E,
81 SifsPeriod = 0x5F,
82 MaxCsmaBackoffs = 0x4E,
83 MinBe = 0x4F,
84 PanCoordinator = 0x10,
85 AssocPanCoordinator = 0x11,
86 ExtendedAddress = 0x6F,
87 AclEntryDescriptor = 0x70,
88 AclEntryDescriptorSize = 0x71,
89 DefaultSecurity = 0x72,
90 DefaultSecurityMaterialLength = 0x73,
91 DefaultSecurityMaterial = 0x74,
92 DefaultSecuritySuite = 0x75,
93 SecurityMode = 0x76,
94 CurrentAclEntries = 0x80,
95 DefaultSecurityExtendedAddress = 0x81,
96 AssociatedPanCoordinator = 0x56,
97 PromiscuousMode = 0x51,
98 }
99}
100
101numeric_enum! {
102 #[repr(u8)]
103 #[derive(Default, Clone, Copy, Debug)]
104 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
105 pub enum AddressMode {
106 #[default]
107 NoAddress = 0x00,
108 Reserved = 0x01,
109 Short = 0x02,
110 Extended = 0x03,
111}
112}
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
127#[derive(Clone, Copy)]
128pub union MacAddress {
129 pub short: [u8; 2],
130 pub extended: [u8; 8],
131}
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
159impl Debug for MacAddress {
160 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
161 unsafe {
162 write!(
163 fmt,
164 "MacAddress {{ short: {:?}, extended: {:?} }}",
165 self.short, self.extended
166 )
167 }
168 }
169}
170
171#[cfg(feature = "defmt")]
172impl defmt::Format for MacAddress {
173 fn format(&self, fmt: defmt::Formatter) {
174 unsafe {
175 defmt::write!(
176 fmt,
177 "MacAddress {{ short: {}, extended: {} }}",
178 self.short,
179 self.extended
180 )
181 }
182 }
183}
184
185impl Default for MacAddress {
186 fn default() -> Self {
187 Self { short: [0, 0] }
188 }
189}
190
191impl MacAddress {
192 pub const BROADCAST: Self = Self { short: [0xFF, 0xFF] };
193}
194
195impl TryFrom<&[u8]> for MacAddress {
196 type Error = ();
197
198 fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
199 const SIZE: usize = 8;
200 if buf.len() < SIZE {
201 return Err(());
202 }
203
204 Ok(Self {
205 extended: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]],
206 })
207 }
208}
209
210#[cfg_attr(feature = "defmt", derive(defmt::Format))]
211pub struct GtsCharacteristics {
212 pub fields: u8,
213}
214
215/// MAC PAN Descriptor which contains the network details of the device from
216/// which the beacon is received
217#[derive(Default, Clone, Copy, Debug)]
218#[cfg_attr(feature = "defmt", derive(defmt::Format))]
219pub struct PanDescriptor {
220 /// PAN identifier of the coordinator
221 pub coord_pan_id: PanId,
222 /// Coordinator addressing mode
223 pub coord_addr_mode: AddressMode,
224 /// The current logical channel occupied by the network
225 pub logical_channel: MacChannel,
226 /// Coordinator address
227 pub coord_addr: MacAddress,
228 /// The current channel page occupied by the network
229 pub channel_page: u8,
230 /// PAN coordinator is accepting GTS requests or not
231 pub gts_permit: bool,
232 /// Superframe specification as specified in the received beacon frame
233 pub superframe_spec: [u8; 2],
234 /// The time at which the beacon frame was received, in symbols
235 pub time_stamp: [u8; 4],
236 /// The LQI at which the network beacon was received
237 pub link_quality: u8,
238 /// Security level purportedly used by the received beacon frame
239 pub security_level: u8,
240}
241
242impl TryFrom<&[u8]> for PanDescriptor {
243 type Error = ();
244
245 fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
246 const SIZE: usize = 22;
247 if buf.len() < SIZE {
248 return Err(());
249 }
250
251 let coord_addr_mode = AddressMode::try_from(buf[2])?;
252 let coord_addr = match coord_addr_mode {
253 AddressMode::NoAddress => MacAddress { short: [0, 0] },
254 AddressMode::Reserved => MacAddress { short: [0, 0] },
255 AddressMode::Short => MacAddress {
256 short: [buf[4], buf[5]],
257 },
258 AddressMode::Extended => MacAddress {
259 extended: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]],
260 },
261 };
262
263 Ok(Self {
264 coord_pan_id: PanId([buf[0], buf[1]]),
265 coord_addr_mode,
266 logical_channel: MacChannel::try_from(buf[3])?,
267 coord_addr,
268 channel_page: buf[12],
269 gts_permit: buf[13] != 0,
270 superframe_spec: [buf[14], buf[15]],
271 time_stamp: [buf[16], buf[17], buf[18], buf[19]],
272 link_quality: buf[20],
273 security_level: buf[21],
274 // 2 byte stuffing
275 })
276 }
277}
278
279numeric_enum! {
280 #[repr(u8)]
281 #[derive(Default, Clone, Copy, Debug)]
282 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
283 /// Building wireless applications with STM32WB series MCUs - Application note 13.10.3
284 pub enum MacChannel {
285 Channel11 = 0x0B,
286 Channel12 = 0x0C,
287 Channel13 = 0x0D,
288 Channel14 = 0x0E,
289 Channel15 = 0x0F,
290 #[default]
291 Channel16 = 0x10,
292 Channel17 = 0x11,
293 Channel18 = 0x12,
294 Channel19 = 0x13,
295 Channel20 = 0x14,
296 Channel21 = 0x15,
297 Channel22 = 0x16,
298 Channel23 = 0x17,
299 Channel24 = 0x18,
300 Channel25 = 0x19,
301 Channel26 = 0x1A,
302 }
303}
304
305#[cfg(not(feature = "defmt"))]
306bitflags::bitflags! {
307 pub struct Capabilities: u8 {
308 /// 1 if the device is capabaleof becoming a PAN coordinator
309 const IS_COORDINATOR_CAPABLE = 0b00000001;
310 /// 1 if the device is an FFD, 0 if it is an RFD
311 const IS_FFD = 0b00000010;
312 /// 1 if the device is receiving power from mains, 0 if it is battery-powered
313 const IS_MAINS_POWERED = 0b00000100;
314 /// 1 if the device does not disable its receiver to conserver power during idle periods
315 const RECEIVER_ON_WHEN_IDLE = 0b00001000;
316 // 0b00010000 reserved
317 // 0b00100000 reserved
318 /// 1 if the device is capable of sending and receiving secured MAC frames
319 const IS_SECURE = 0b01000000;
320 /// 1 if the device wishes the coordinator to allocate a short address as a result of the association
321 const ALLOCATE_ADDRESS = 0b10000000;
322 }
323}
324
325#[cfg(feature = "defmt")]
326defmt::bitflags! {
327 pub struct Capabilities: u8 {
328 /// 1 if the device is capabaleof becoming a PAN coordinator
329 const IS_COORDINATOR_CAPABLE = 0b00000001;
330 /// 1 if the device is an FFD, 0 if it is an RFD
331 const IS_FFD = 0b00000010;
332 /// 1 if the device is receiving power from mains, 0 if it is battery-powered
333 const IS_MAINS_POWERED = 0b00000100;
334 /// 1 if the device does not disable its receiver to conserver power during idle periods
335 const RECEIVER_ON_WHEN_IDLE = 0b00001000;
336 // 0b00010000 reserved
337 // 0b00100000 reserved
338 /// 1 if the device is capable of sending and receiving secured MAC frames
339 const IS_SECURE = 0b01000000;
340 /// 1 if the device wishes the coordinator to allocate a short address as a result of the association
341 const ALLOCATE_ADDRESS = 0b10000000;
342 }
343}
344
345numeric_enum! {
346 #[repr(u8)]
347 #[derive(Default, Clone, Copy, Debug)]
348 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
349 pub enum KeyIdMode {
350 #[default]
351 /// the key is determined implicitly from the originator and recipient(s) of the frame
352 Implicite = 0x00,
353 /// the key is determined explicitly using a 1 bytes key source and a 1 byte key index
354 Explicite1Byte = 0x01,
355 /// the key is determined explicitly using a 4 bytes key source and a 1 byte key index
356 Explicite4Byte = 0x02,
357 /// the key is determined explicitly using a 8 bytes key source and a 1 byte key index
358 Explicite8Byte = 0x03,
359 }
360}
361
362numeric_enum! {
363 #[repr(u8)]
364 #[derive(Debug)]
365 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
366 pub enum AssociationStatus {
367 /// Association successful
368 Success = 0x00,
369 /// PAN at capacity
370 PanAtCapacity = 0x01,
371 /// PAN access denied
372 PanAccessDenied = 0x02
373 }
374}
375
376numeric_enum! {
377 #[repr(u8)]
378 #[derive(Clone, Copy, Debug)]
379 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
380 pub enum DisassociationReason {
381 /// The coordinator wishes the device to leave the PAN.
382 CoordRequested = 0x01,
383 /// The device wishes to leave the PAN.
384 DeviceRequested = 0x02,
385 }
386}
387
388numeric_enum! {
389 #[repr(u8)]
390 #[derive(Default, Clone, Copy, Debug, PartialEq)]
391 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
392 pub enum SecurityLevel {
393 /// MAC Unsecured Mode Security
394 #[default]
395 Unsecure = 0x00,
396 /// MAC ACL Mode Security
397 AclMode = 0x01,
398 /// MAC Secured Mode Security
399 Secured = 0x02,
400 }
401}
402
403numeric_enum! {
404 #[repr(u8)]
405 #[derive(Debug)]
406 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
407 pub enum ScanType {
408 EdScan = 0x00,
409 Active = 0x01,
410 Passive = 0x02,
411 Orphan = 0x03
412 }
413}
414
415/// newtype for Pan Id
416#[derive(Default, Clone, Copy, Debug)]
417#[cfg_attr(feature = "defmt", derive(defmt::Format))]
418pub struct PanId(pub [u8; 2]);
419
420impl PanId {
421 pub const BROADCAST: Self = Self([0xFF, 0xFF]);
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/wb55/shci.rs b/embassy-stm32-wpan/src/wb55/shci.rs
new file mode 100644
index 000000000..3faa79209
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/shci.rs
@@ -0,0 +1,397 @@
1use core::sync::atomic::{Ordering, compiler_fence};
2use core::{mem, ptr, slice};
3
4use crate::cmd::CmdPacket;
5use crate::consts::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE};
6use crate::evt::{CcEvt, EvtStub};
7use crate::wb55::PacketHeader;
8
9const SHCI_OGF: u16 = 0x3F;
10
11const fn opcode(ogf: u16, ocf: u16) -> isize {
12 ((ogf << 10) + ocf) as isize
13}
14
15#[allow(dead_code)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17pub enum SchiCommandStatus {
18 ShciSuccess = 0x00,
19 ShciUnknownCmd = 0x01,
20 ShciMemoryCapacityExceededErrCode = 0x07,
21 ShciErrUnsupportedFeature = 0x11,
22 ShciErrInvalidHciCmdParams = 0x12,
23 ShciErrInvalidParams = 0x42, /* only used for release < v1.13.0 */
24 ShciErrInvalidParamsV2 = 0x92, /* available for release >= v1.13.0 */
25 ShciFusCmdNotSupported = 0xFF,
26}
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
40impl TryFrom<u8> for SchiCommandStatus {
41 type Error = ();
42
43 fn try_from(v: u8) -> Result<Self, Self::Error> {
44 match v {
45 x if x == SchiCommandStatus::ShciSuccess as u8 => Ok(SchiCommandStatus::ShciSuccess),
46 x if x == SchiCommandStatus::ShciUnknownCmd as u8 => Ok(SchiCommandStatus::ShciUnknownCmd),
47 x if x == SchiCommandStatus::ShciMemoryCapacityExceededErrCode as u8 => {
48 Ok(SchiCommandStatus::ShciMemoryCapacityExceededErrCode)
49 }
50 x if x == SchiCommandStatus::ShciErrUnsupportedFeature as u8 => {
51 Ok(SchiCommandStatus::ShciErrUnsupportedFeature)
52 }
53 x if x == SchiCommandStatus::ShciErrInvalidHciCmdParams as u8 => {
54 Ok(SchiCommandStatus::ShciErrInvalidHciCmdParams)
55 }
56 x if x == SchiCommandStatus::ShciErrInvalidParams as u8 => Ok(SchiCommandStatus::ShciErrInvalidParams), /* only used for release < v1.13.0 */
57 x if x == SchiCommandStatus::ShciErrInvalidParamsV2 as u8 => Ok(SchiCommandStatus::ShciErrInvalidParamsV2), /* available for release >= v1.13.0 */
58 x if x == SchiCommandStatus::ShciFusCmdNotSupported as u8 => Ok(SchiCommandStatus::ShciFusCmdNotSupported),
59 _ => Err(()),
60 }
61 }
62}
63
64#[allow(dead_code)]
65#[cfg_attr(feature = "defmt", derive(defmt::Format))]
66pub enum ShciOpcode {
67 // 0x50 reserved
68 // 0x51 reserved
69 FusGetState = opcode(SHCI_OGF, 0x52),
70 // 0x53 reserved
71 FusFirmwareUpgrade = opcode(SHCI_OGF, 0x54),
72 FusFirmwareDelete = opcode(SHCI_OGF, 0x55),
73 FusUpdateAuthKey = opcode(SHCI_OGF, 0x56),
74 FusLockAuthKey = opcode(SHCI_OGF, 0x57),
75 FusStoreUserKey = opcode(SHCI_OGF, 0x58),
76 FusLoadUserKey = opcode(SHCI_OGF, 0x59),
77 FusStartWirelessStack = opcode(SHCI_OGF, 0x5a),
78 // 0x5b reserved
79 // 0x5c reserved
80 FusLockUserKey = opcode(SHCI_OGF, 0x5d),
81 FusUnloadUserKey = opcode(SHCI_OGF, 0x5e),
82 FusActivateAntirollback = opcode(SHCI_OGF, 0x5f),
83 // 0x60 reserved
84 // 0x61 reserved
85 // 0x62 reserved
86 // 0x63 reserved
87 // 0x64 reserved
88 // 0x65 reserved
89 BleInit = opcode(SHCI_OGF, 0x66),
90 ThreadInit = opcode(SHCI_OGF, 0x67),
91 DebugInit = opcode(SHCI_OGF, 0x68),
92 FlashEraseActivity = opcode(SHCI_OGF, 0x69),
93 ConcurrentSetMode = opcode(SHCI_OGF, 0x6a),
94 FlashStoreData = opcode(SHCI_OGF, 0x6b),
95 FlashEraseData = opcode(SHCI_OGF, 0x6c),
96 RadioAllowLowPower = opcode(SHCI_OGF, 0x6d),
97 Mac802_15_4Init = opcode(SHCI_OGF, 0x6e),
98 ReInit = opcode(SHCI_OGF, 0x6f),
99 ZigbeeInit = opcode(SHCI_OGF, 0x70),
100 LldTestsInit = opcode(SHCI_OGF, 0x71),
101 ExtraConfig = opcode(SHCI_OGF, 0x72),
102 SetFlashActivityControl = opcode(SHCI_OGF, 0x73),
103 BleLldInit = opcode(SHCI_OGF, 0x74),
104 Config = opcode(SHCI_OGF, 0x75),
105 ConcurrentGetNextBleEvtTime = opcode(SHCI_OGF, 0x76),
106 ConcurrentEnableNext802_15_4EvtNotification = opcode(SHCI_OGF, 0x77),
107 Mac802_15_4DeInit = opcode(SHCI_OGF, 0x78),
108}
109
110pub const SHCI_C2_CONFIG_EVTMASK1_BIT0_ERROR_NOTIF_ENABLE: u8 = 1 << 0;
111pub const SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE: u8 = 1 << 1;
112pub const SHCI_C2_CONFIG_EVTMASK1_BIT2_THREAD_NVM_RAM_UPDATE_ENABLE: u8 = 1 << 2;
113pub const SHCI_C2_CONFIG_EVTMASK1_BIT3_NVM_START_WRITE_ENABLE: u8 = 1 << 3;
114pub const SHCI_C2_CONFIG_EVTMASK1_BIT4_NVM_END_WRITE_ENABLE: u8 = 1 << 4;
115pub const SHCI_C2_CONFIG_EVTMASK1_BIT5_NVM_START_ERASE_ENABLE: u8 = 1 << 5;
116pub const SHCI_C2_CONFIG_EVTMASK1_BIT6_NVM_END_ERASE_ENABLE: u8 = 1 << 6;
117
118#[derive(Clone, Copy)]
119#[repr(C, packed)]
120pub struct ShciConfigParam {
121 pub payload_cmd_size: u8,
122 pub config: u8,
123 pub event_mask: u8,
124 pub spare: u8,
125 pub ble_nvm_ram_address: u32,
126 pub thread_nvm_ram_address: u32,
127 pub revision_id: u16,
128 pub device_id: u16,
129}
130
131impl ShciConfigParam {
132 pub fn payload<'a>(&'a self) -> &'a [u8] {
133 unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::<Self>()) }
134 }
135}
136
137impl Default for ShciConfigParam {
138 fn default() -> Self {
139 Self {
140 payload_cmd_size: (mem::size_of::<Self>() - 1) as u8,
141 config: 0,
142 event_mask: SHCI_C2_CONFIG_EVTMASK1_BIT0_ERROR_NOTIF_ENABLE
143 + SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE
144 + SHCI_C2_CONFIG_EVTMASK1_BIT2_THREAD_NVM_RAM_UPDATE_ENABLE
145 + SHCI_C2_CONFIG_EVTMASK1_BIT3_NVM_START_WRITE_ENABLE
146 + SHCI_C2_CONFIG_EVTMASK1_BIT4_NVM_END_WRITE_ENABLE
147 + SHCI_C2_CONFIG_EVTMASK1_BIT5_NVM_START_ERASE_ENABLE
148 + SHCI_C2_CONFIG_EVTMASK1_BIT6_NVM_END_ERASE_ENABLE,
149 spare: 0,
150 ble_nvm_ram_address: 0,
151 thread_nvm_ram_address: 0,
152 revision_id: 0,
153 device_id: 0,
154 }
155 }
156}
157
158#[derive(Clone, Copy)]
159#[repr(C, packed)]
160pub struct ShciBleInitCmdParam {
161 /// NOT USED - shall be set to 0
162 pub p_ble_buffer_address: u32,
163 /// NOT USED - shall be set to 0
164 pub ble_buffer_size: u32,
165 /// Maximum number of attribute records related to all the required characteristics (excluding the services)
166 /// that can be stored in the GATT database, for the specific BLE user application.
167 /// For each characteristic, the number of attribute records goes from two to five depending on the characteristic properties:
168 /// - minimum of two (one for declaration and one for the value)
169 /// - add one more record for each additional property: notify or indicate, broadcast, extended property.
170 /// The total calculated value must be increased by 9, due to the records related to the standard attribute profile and
171 /// GAP service characteristics, and automatically added when initializing GATT and GAP layers
172 /// - Min value: <number of user attributes> + 9
173 /// - Max value: depending on the GATT database defined by user application
174 pub num_attr_record: u16,
175 /// Defines the maximum number of services that can be stored in the GATT database. Note that the GAP and GATT services
176 /// are automatically added at initialization so this parameter must be the number of user services increased by two.
177 /// - Min value: <number of user service> + 2
178 /// - Max value: depending GATT database defined by user application
179 pub num_attr_serv: u16,
180 /// NOTE: This parameter is ignored by the CPU2 when the parameter "Options" is set to "LL_only" ( see Options description in that structure )
181 ///
182 /// Size of the storage area for the attribute values.
183 /// Each characteristic contributes to the attrValueArrSize value as follows:
184 /// - Characteristic value length plus:
185 /// + 5 bytes if characteristic UUID is 16 bits
186 /// + 19 bytes if characteristic UUID is 128 bits
187 /// + 2 bytes if characteristic has a server configuration descriptor
188 /// + 2 bytes * NumOfLinks if the characteristic has a client configuration descriptor
189 /// + 2 bytes if the characteristic has extended properties
190 /// Each descriptor contributes to the attrValueArrSize value as follows:
191 /// - Descriptor length
192 pub attr_value_arr_size: u16,
193 /// Maximum number of BLE links supported
194 /// - Min value: 1
195 /// - Max value: 8
196 pub num_of_links: u8,
197 /// Disable/enable the extended packet length BLE 5.0 feature
198 /// - Disable: 0
199 /// - Enable: 1
200 pub extended_packet_length_enable: u8,
201 /// NOTE: This parameter is ignored by the CPU2 when the parameter "Options" is set to "LL_only" ( see Options description in that structure )
202 ///
203 /// Maximum number of supported "prepare write request"
204 /// - Min value: given by the macro DEFAULT_PREP_WRITE_LIST_SIZE
205 /// - Max value: a value higher than the minimum required can be specified, but it is not recommended
206 pub prepare_write_list_size: u8,
207 /// NOTE: This parameter is overwritten by the CPU2 with an hardcoded optimal value when the parameter "Options" is set to "LL_only"
208 /// ( see Options description in that structure )
209 ///
210 /// Number of allocated memory blocks for the BLE stack
211 /// - Min value: given by the macro MBLOCKS_CALC
212 /// - Max value: a higher value can improve data throughput performance, but uses more memory
213 pub block_count: u8,
214 /// NOTE: This parameter is ignored by the CPU2 when the parameter "Options" is set to "LL_only" ( see Options description in that structure )
215 ///
216 /// Maximum ATT MTU size supported
217 /// - Min value: 23
218 /// - Max value: 512
219 pub att_mtu: u16,
220 /// The sleep clock accuracy (ppm value) that used in BLE connected slave mode to calculate the window widening
221 /// (in combination with the sleep clock accuracy sent by master in CONNECT_REQ PDU),
222 /// refer to BLE 5.0 specifications - Vol 6 - Part B - chap 4.5.7 and 4.2.2
223 /// - Min value: 0
224 /// - Max value: 500 (worst possible admitted by specification)
225 pub slave_sca: u16,
226 /// The sleep clock accuracy handled in master mode. It is used to determine the connection and advertising events timing.
227 /// It is transmitted to the slave in CONNEC_REQ PDU used by the slave to calculate the window widening,
228 /// see SlaveSca and Bluetooth Core Specification v5.0 Vol 6 - Part B - chap 4.5.7 and 4.2.2
229 /// Possible values:
230 /// - 251 ppm to 500 ppm: 0
231 /// - 151 ppm to 250 ppm: 1
232 /// - 101 ppm to 150 ppm: 2
233 /// - 76 ppm to 100 ppm: 3
234 /// - 51 ppm to 75 ppm: 4
235 /// - 31 ppm to 50 ppm: 5
236 /// - 21 ppm to 30 ppm: 6
237 /// - 0 ppm to 20 ppm: 7
238 pub master_sca: u8,
239 /// Some information for Low speed clock mapped in bits field
240 /// - bit 0:
241 /// - 1: Calibration for the RF system wakeup clock source
242 /// - 0: No calibration for the RF system wakeup clock source
243 /// - bit 1:
244 /// - 1: STM32W5M Module device
245 /// - 0: Other devices as STM32WBxx SOC, STM32WB1M module
246 /// - bit 2:
247 /// - 1: HSE/1024 Clock config
248 /// - 0: LSE Clock config
249 pub ls_source: u8,
250 /// This parameter determines the maximum duration of a slave connection event. When this duration is reached the slave closes
251 /// the current connections event (whatever is the CE_length parameter specified by the master in HCI_CREATE_CONNECTION HCI command),
252 /// expressed in units of 625/256 µs (~2.44 µs)
253 /// - Min value: 0 (if 0 is specified, the master and slave perform only a single TX-RX exchange per connection event)
254 /// - Max value: 1638400 (4000 ms). A higher value can be specified (max 0xFFFFFFFF) but results in a maximum connection time
255 /// of 4000 ms as specified. In this case the parameter is not applied, and the predicted CE length calculated on slave is not shortened
256 pub max_conn_event_length: u32,
257 /// Startup time of the high speed (16 or 32 MHz) crystal oscillator in units of 625/256 µs (~2.44 µs).
258 /// - Min value: 0
259 /// - Max value: 820 (~2 ms). A higher value can be specified, but the value that implemented in stack is forced to ~2 ms
260 pub hs_startup_time: u16,
261 /// Viterbi implementation in BLE LL reception.
262 /// - 0: Enable
263 /// - 1: Disable
264 pub viterbi_enable: u8,
265 /// - bit 0:
266 /// - 1: LL only
267 /// - 0: LL + host
268 /// - bit 1:
269 /// - 1: no service change desc.
270 /// - 0: with service change desc.
271 /// - bit 2:
272 /// - 1: device name Read-Only
273 /// - 0: device name R/W
274 /// - bit 3:
275 /// - 1: extended advertizing supported
276 /// - 0: extended advertizing not supported
277 /// - bit 4:
278 /// - 1: CS Algo #2 supported
279 /// - 0: CS Algo #2 not supported
280 /// - bit 5:
281 /// - 1: Reduced GATT database in NVM
282 /// - 0: Full GATT database in NVM
283 /// - bit 6:
284 /// - 1: GATT caching is used
285 /// - 0: GATT caching is not used
286 /// - bit 7:
287 /// - 1: LE Power Class 1
288 /// - 0: LE Power Classe 2-3
289 /// - other bits: complete with Options_extension flag
290 pub options: u8,
291 /// Reserved for future use - shall be set to 0
292 pub hw_version: u8,
293 ///
294 /// Maximum number of connection-oriented channels in initiator mode.
295 /// Range: 0 .. 64
296 pub max_coc_initiator_nbr: u8,
297
298 ///
299 /// Minimum transmit power in dBm supported by the Controller.
300 /// Range: -127 .. 20
301 pub min_tx_power: i8,
302
303 ///
304 /// Maximum transmit power in dBm supported by the Controller.
305 /// Range: -127 .. 20
306 pub max_tx_power: i8,
307
308 ///
309 /// RX model configuration
310 /// - bit 0: 1: agc_rssi model improved vs RF blockers 0: Legacy agc_rssi model
311 /// - other bits: reserved ( shall be set to 0)
312 pub rx_model_config: u8,
313
314 /// Maximum number of advertising sets.
315 /// Range: 1 .. 8 with limitation:
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
317 /// on Max Extended advertising configuration supported.
318 /// This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set
319 pub max_adv_set_nbr: u8,
320
321 /// Maximum advertising data length (in bytes)
322 /// Range: 31 .. 1650 with limitation:
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
324 /// on Max Extended advertising configuration supported.
325 /// This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set
326 pub max_adv_data_len: u16,
327
328 /// RF TX Path Compensation Value (16-bit signed integer). Units: 0.1 dB.
329 /// Range: -1280 .. 1280
330 pub tx_path_compens: i16,
331
332 //// RF RX Path Compensation Value (16-bit signed integer). Units: 0.1 dB.
333 /// Range: -1280 .. 1280
334 pub rx_path_compens: i16,
335
336 /// BLE core specification version (8-bit unsigned integer).
337 /// values as: 11(5.2), 12(5.3)
338 pub ble_core_version: u8,
339
340 /// Options flags extension
341 /// - bit 0: 1: appearance Writable 0: appearance Read-Only
342 /// - bit 1: 1: Enhanced ATT supported 0: Enhanced ATT not supported
343 /// - other bits: reserved ( shall be set to 0)
344 pub options_extension: u8,
345
346 /// MaxAddEattBearers
347 /// Maximum number of bearers that can be created for Enhanced ATT
348 /// in addition to the number of links
349 /// - Range: 0 .. 4
350 pub max_add_eatt_bearers: u8,
351}
352
353impl ShciBleInitCmdParam {
354 pub fn payload<'a>(&'a self) -> &'a [u8] {
355 unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::<Self>()) }
356 }
357}
358
359impl Default for ShciBleInitCmdParam {
360 fn default() -> Self {
361 Self {
362 p_ble_buffer_address: 0,
363 ble_buffer_size: 0,
364 num_attr_record: 68,
365 num_attr_serv: 4,
366 attr_value_arr_size: 1344,
367 num_of_links: 2,
368 extended_packet_length_enable: 1,
369 prepare_write_list_size: 0x3A,
370 block_count: 0x79,
371 att_mtu: 156,
372 slave_sca: 500,
373 master_sca: 0,
374 ls_source: 1,
375 max_conn_event_length: 0xFFFFFFFF,
376 hs_startup_time: 0x148,
377 viterbi_enable: 1,
378 options: 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,
391 }
392 }
393}
394
395pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE;
396#[allow(dead_code)] // Not used currently but reserved
397const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE;
diff --git a/embassy-stm32-wpan/src/wb55/sub/ble.rs b/embassy-stm32-wpan/src/wb55/sub/ble.rs
new file mode 100644
index 000000000..a822d6530
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/sub/ble.rs
@@ -0,0 +1,139 @@
1use core::ptr;
2
3use embassy_stm32::ipcc::{IpccRxChannel, IpccTxChannel};
4use hci::Opcode;
5
6use crate::cmd::CmdPacket;
7use crate::consts::{TL_BLEEVT_CC_OPCODE, TL_BLEEVT_CS_OPCODE, TlPacketType};
8use crate::evt;
9use crate::evt::{EvtBox, EvtPacket, EvtStub};
10use crate::sub::mm;
11use crate::tables::{BLE_CMD_BUFFER, BleTable, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE};
12use crate::unsafe_linked_list::LinkedListNode;
13
14/// A guard that, once constructed, may be used to send BLE commands to CPU2.
15///
16/// It is the responsibility of the caller to ensure that they have awaited an event via
17/// [crate::sub::sys::Sys::read] before sending any of these commands, and to call
18/// [crate::sub::sys::Sys::shci_c2_ble_init] and await the HCI_COMMAND_COMPLETE_EVENT before
19/// sending any other commands.
20///
21/// # Example
22///
23/// ```
24/// # embassy_stm32::bind_interrupts!(struct Irqs{
25/// # IPCC_C1_RX => ReceiveInterruptHandler;
26/// # IPCC_C1_TX => TransmitInterruptHandler;
27/// # });
28/// #
29/// # let p = embassy_stm32::init(embassy_stm32::Config::default());
30/// # let mut mbox = embassy_stm32_wpan::TlMbox::init(p.IPCC, Irqs, embassy_stm32::ipcc::Config::default());
31/// #
32/// # let sys_event = mbox.sys_subsystem.read().await;
33/// # let _command_status = mbox.sys_subsystem.shci_c2_ble_init(Default::default());
34/// # // BLE commands may now be sent
35/// #
36/// # mbox.ble_subsystem.reset().await;
37/// # let _reset_response = mbox.ble_subsystem.read().await;
38/// ```
39pub struct Ble<'a> {
40 hw_ipcc_ble_cmd_channel: IpccTxChannel<'a>,
41 ipcc_ble_event_channel: IpccRxChannel<'a>,
42 ipcc_hci_acl_data_channel: IpccTxChannel<'a>,
43}
44
45impl<'a> Ble<'a> {
46 /// Constructs a guard that allows for BLE commands to be sent to CPU2.
47 ///
48 /// This takes the place of `TL_BLE_Init`, completing that step as laid out in AN5289, Fig 66.
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 {
54 unsafe {
55 LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr());
56
57 TL_BLE_TABLE.as_mut_ptr().write_volatile(BleTable {
58 pcmd_buffer: BLE_CMD_BUFFER.as_mut_ptr().cast(),
59 pcs_buffer: CS_BUFFER.as_ptr().cast(),
60 pevt_queue: EVT_QUEUE.as_ptr().cast(),
61 phci_acl_data_buffer: HCI_ACL_DATA_BUFFER.as_mut_ptr().cast(),
62 });
63 }
64
65 Self {
66 hw_ipcc_ble_cmd_channel,
67 ipcc_ble_event_channel,
68 ipcc_hci_acl_data_channel,
69 }
70 }
71
72 /// `HW_IPCC_BLE_EvtNot`
73 pub async fn tl_read(&mut self) -> EvtBox<Self> {
74 self.ipcc_ble_event_channel
75 .receive(|| unsafe {
76 if let Some(node_ptr) =
77 critical_section::with(|cs| LinkedListNode::remove_head(cs, EVT_QUEUE.as_mut_ptr()))
78 {
79 Some(EvtBox::new(node_ptr.cast()))
80 } else {
81 None
82 }
83 })
84 .await
85 }
86
87 /// `TL_BLE_SendCmd`
88 pub async fn tl_write(&mut self, opcode: u16, payload: &[u8]) {
89 self.hw_ipcc_ble_cmd_channel
90 .send(|| unsafe {
91 CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload);
92 })
93 .await;
94 }
95
96 /// `TL_BLE_SendAclData`
97 pub async fn acl_write(&mut self, handle: u16, payload: &[u8]) {
98 self.ipcc_hci_acl_data_channel
99 .send(|| unsafe {
100 CmdPacket::write_into(
101 HCI_ACL_DATA_BUFFER.as_mut_ptr() as *mut _,
102 TlPacketType::AclData,
103 handle,
104 payload,
105 );
106 })
107 .await;
108 }
109}
110
111impl<'a> evt::MemoryManager for Ble<'a> {
112 /// SAFETY: passing a pointer to something other than a managed event packet is UB
113 unsafe fn drop_event_packet(evt: *mut EvtPacket) {
114 let stub = unsafe {
115 let p_evt_stub = &(*evt).evt_serial as *const _ as *const EvtStub;
116
117 ptr::read_volatile(p_evt_stub)
118 };
119
120 if !(stub.evt_code == TL_BLEEVT_CS_OPCODE || stub.evt_code == TL_BLEEVT_CC_OPCODE) {
121 mm::MemoryManager::drop_event_packet(evt);
122 }
123 }
124}
125
126pub extern crate stm32wb_hci as hci;
127
128impl<'a> hci::Controller for Ble<'a> {
129 async fn controller_write(&mut self, opcode: Opcode, payload: &[u8]) {
130 self.tl_write(opcode.0, payload).await;
131 }
132
133 async fn controller_read_into(&mut self, buf: &mut [u8]) {
134 let evt_box = self.tl_read().await;
135 let evt_serial = evt_box.serial();
136
137 buf[..evt_serial.len()].copy_from_slice(evt_serial);
138 }
139}
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/wb55/sub/mm.rs b/embassy-stm32-wpan/src/wb55/sub/mm.rs
new file mode 100644
index 000000000..0ca7d1835
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/sub/mm.rs
@@ -0,0 +1,84 @@
1//! Memory manager routines
2use core::future::poll_fn;
3use core::mem::MaybeUninit;
4use core::task::Poll;
5
6use aligned::{A4, Aligned};
7use embassy_stm32::ipcc::IpccTxChannel;
8use embassy_sync::waitqueue::AtomicWaker;
9
10use crate::consts::POOL_SIZE;
11use crate::evt;
12use crate::evt::EvtPacket;
13#[cfg(feature = "wb55_ble")]
14use crate::tables::BLE_SPARE_EVT_BUF;
15use crate::tables::{EVT_POOL, FREE_BUF_QUEUE, MemManagerTable, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE};
16use crate::unsafe_linked_list::LinkedListNode;
17
18static MM_WAKER: AtomicWaker = AtomicWaker::new();
19static mut LOCAL_FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit());
20
21pub struct MemoryManager<'a> {
22 ipcc_mm_release_buffer_channel: IpccTxChannel<'a>,
23}
24
25impl<'a> MemoryManager<'a> {
26 pub(crate) fn new(ipcc_mm_release_buffer_channel: IpccTxChannel<'a>) -> Self {
27 unsafe {
28 LinkedListNode::init_head(FREE_BUF_QUEUE.as_mut_ptr());
29 LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr());
30
31 TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable {
32 #[cfg(feature = "wb55_ble")]
33 spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(),
34 #[cfg(not(feature = "wb55_ble"))]
35 spare_ble_buffer: core::ptr::null(),
36 spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(),
37 blepool: EVT_POOL.as_ptr().cast(),
38 blepoolsize: POOL_SIZE as u32,
39 pevt_free_buffer_queue: FREE_BUF_QUEUE.as_mut_ptr(),
40 traces_evt_pool: core::ptr::null(),
41 tracespoolsize: 0,
42 });
43 }
44
45 Self {
46 ipcc_mm_release_buffer_channel,
47 }
48 }
49
50 pub async fn run_queue(&mut self) -> ! {
51 loop {
52 poll_fn(|cx| unsafe {
53 MM_WAKER.register(cx.waker());
54 if critical_section::with(|cs| LinkedListNode::is_empty(cs, LOCAL_FREE_BUF_QUEUE.as_mut_ptr())) {
55 Poll::Pending
56 } else {
57 Poll::Ready(())
58 }
59 })
60 .await;
61
62 self.ipcc_mm_release_buffer_channel
63 .send(|| {
64 critical_section::with(|cs| unsafe {
65 while let Some(node_ptr) = LinkedListNode::remove_head(cs, LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) {
66 LinkedListNode::insert_head(cs, FREE_BUF_QUEUE.as_mut_ptr(), node_ptr);
67 }
68 })
69 })
70 .await;
71 }
72 }
73}
74
75impl<'a> evt::MemoryManager for MemoryManager<'a> {
76 /// SAFETY: passing a pointer to something other than a managed event packet is UB
77 unsafe fn drop_event_packet(evt: *mut EvtPacket) {
78 critical_section::with(|cs| unsafe {
79 LinkedListNode::insert_head(cs, LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), evt as *mut _);
80 });
81
82 MM_WAKER.wake();
83 }
84}
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/wb55/tables.rs b/embassy-stm32-wpan/src/wb55/tables.rs
new file mode 100644
index 000000000..2e6a9199b
--- /dev/null
+++ b/embassy-stm32-wpan/src/wb55/tables.rs
@@ -0,0 +1,283 @@
1use core::mem::MaybeUninit;
2
3use aligned::{A4, Aligned};
4use bit_field::BitField;
5
6use crate::cmd::{AclDataPacket, CmdPacket};
7#[cfg(feature = "wb55_mac")]
8use crate::consts::C_SIZE_CMD_STRING;
9use crate::consts::{POOL_SIZE, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE};
10use crate::unsafe_linked_list::LinkedListNode;
11
12#[derive(Debug, Copy, Clone)]
13#[repr(C, packed)]
14pub struct SafeBootInfoTable {
15 version: u32,
16}
17
18#[derive(Debug, Copy, Clone)]
19#[repr(C, packed)]
20pub struct RssInfoTable {
21 pub version: u32,
22 pub memory_size: u32,
23 pub rss_info: u32,
24}
25
26/**
27 * Version
28 * \[0:3\] = Build - 0: Untracked - 15:Released - x: Tracked version
29 * \[4:7\] = branch - 0: Mass Market - x: ...
30 * \[8:15\] = Subversion
31 * \[16:23\] = Version minor
32 * \[24:31\] = Version major
33 *
34 * Memory Size
35 * \[0:7\] = Flash ( Number of 4k sector)
36 * \[8:15\] = Reserved ( Shall be set to 0 - may be used as flash extension )
37 * \[16:23\] = SRAM2b ( Number of 1k sector)
38 * \[24:31\] = SRAM2a ( Number of 1k sector)
39 */
40#[derive(Debug, Copy, Clone)]
41#[repr(C, packed)]
42pub struct WirelessFwInfoTable {
43 pub version: u32,
44 pub memory_size: u32,
45 pub thread_info: u32,
46 pub ble_info: u32,
47}
48
49impl WirelessFwInfoTable {
50 pub fn version_major(&self) -> u8 {
51 let version = self.version;
52 (version.get_bits(24..31) & 0xff) as u8
53 }
54
55 pub fn version_minor(&self) -> u8 {
56 let version = self.version;
57 (version.clone().get_bits(16..23) & 0xff) as u8
58 }
59
60 pub fn subversion(&self) -> u8 {
61 let version = self.version;
62 (version.clone().get_bits(8..15) & 0xff) as u8
63 }
64
65 /// Size of FLASH, expressed in number of 4K sectors.
66 pub fn flash_size(&self) -> u8 {
67 let memory_size = self.memory_size;
68 (memory_size.clone().get_bits(0..7) & 0xff) as u8
69 }
70
71 /// Size of SRAM2a, expressed in number of 1K sectors.
72 pub fn sram2a_size(&self) -> u8 {
73 let memory_size = self.memory_size;
74 (memory_size.clone().get_bits(24..31) & 0xff) as u8
75 }
76
77 /// Size of SRAM2b, expressed in number of 1K sectors.
78 pub fn sram2b_size(&self) -> u8 {
79 let memory_size = self.memory_size;
80 (memory_size.clone().get_bits(16..23) & 0xff) as u8
81 }
82}
83
84#[derive(Debug, Clone)]
85#[repr(C)]
86pub struct DeviceInfoTable {
87 pub safe_boot_info_table: SafeBootInfoTable,
88 pub rss_info_table: RssInfoTable,
89 pub wireless_fw_info_table: WirelessFwInfoTable,
90}
91
92/// The bluetooth reference table, as defined in figure 67 of STM32WX AN5289.
93#[derive(Debug)]
94#[repr(C)]
95pub struct BleTable {
96 /// A pointer to the buffer that is used for sending BLE commands.
97 pub pcmd_buffer: *mut CmdPacket,
98 /// A pointer to the buffer used for storing Command statuses.
99 pub pcs_buffer: *const u8,
100 /// A pointer to the event queue, over which IPCC BLE events are sent. This may be accessed via
101 /// [crate::sub::ble::Ble::tl_read].
102 pub pevt_queue: *const u8,
103 /// A pointer to the buffer that is used for sending HCI (Host-Controller Interface) ACL
104 /// (Asynchronous Connection-oriented Logical transport) commands (unused).
105 pub phci_acl_data_buffer: *mut AclDataPacket,
106}
107
108#[derive(Debug)]
109#[repr(C)]
110pub struct ThreadTable {
111 pub nostack_buffer: *const u8,
112 pub clicmdrsp_buffer: *const u8,
113 pub otcmdrsp_buffer: *const u8,
114}
115
116#[derive(Debug)]
117#[repr(C)]
118pub struct LldTestsTable {
119 pub clicmdrsp_buffer: *const u8,
120 pub m0cmd_buffer: *const u8,
121}
122
123// TODO: use later
124#[derive(Debug)]
125#[repr(C)]
126pub struct BleLldTable {
127 pub cmdrsp_buffer: *const u8,
128 pub m0cmd_buffer: *const u8,
129}
130
131// TODO: use later
132#[derive(Debug)]
133#[repr(C)]
134pub struct ZigbeeTable {
135 pub notif_m0_to_m4_buffer: *const u8,
136 pub appli_cmd_m4_to_m0_bufer: *const u8,
137 pub request_m0_to_m4_buffer: *const u8,
138}
139
140#[derive(Debug)]
141#[repr(C)]
142pub struct SysTable {
143 pub pcmd_buffer: *mut CmdPacket,
144 pub sys_queue: *const LinkedListNode,
145}
146
147#[derive(Debug)]
148#[repr(C)]
149pub struct MemManagerTable {
150 pub spare_ble_buffer: *const u8,
151 pub spare_sys_buffer: *const u8,
152
153 pub blepool: *const u8,
154 pub blepoolsize: u32,
155
156 pub pevt_free_buffer_queue: *mut LinkedListNode,
157
158 pub traces_evt_pool: *const u8,
159 pub tracespoolsize: u32,
160}
161
162#[derive(Debug)]
163#[repr(C)]
164pub struct TracesTable {
165 pub traces_queue: *const u8,
166}
167
168#[derive(Debug)]
169#[repr(C)]
170pub struct Mac802_15_4Table {
171 pub p_cmdrsp_buffer: *const u8,
172 pub p_notack_buffer: *const u8,
173 pub evt_queue: *const u8,
174}
175
176/// Reference table. Contains pointers to all other tables.
177#[derive(Debug, Copy, Clone)]
178#[repr(C)]
179pub struct RefTable {
180 pub device_info_table: *const DeviceInfoTable,
181 pub ble_table: *const BleTable,
182 pub thread_table: *const ThreadTable,
183 pub sys_table: *const SysTable,
184 pub mem_manager_table: *const MemManagerTable,
185 pub traces_table: *const TracesTable,
186 pub mac_802_15_4_table: *const Mac802_15_4Table,
187 pub zigbee_table: *const ZigbeeTable,
188 pub lld_tests_table: *const LldTestsTable,
189 pub ble_lld_table: *const BleLldTable,
190}
191
192// --------------------- ref table ---------------------
193#[unsafe(link_section = "TL_REF_TABLE")]
194pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::zeroed();
195
196#[unsafe(link_section = "MB_MEM1")]
197pub static mut TL_DEVICE_INFO_TABLE: Aligned<A4, MaybeUninit<DeviceInfoTable>> = Aligned(MaybeUninit::zeroed());
198
199#[unsafe(link_section = "MB_MEM1")]
200pub static mut TL_BLE_TABLE: Aligned<A4, MaybeUninit<BleTable>> = Aligned(MaybeUninit::zeroed());
201
202#[unsafe(link_section = "MB_MEM1")]
203pub static mut TL_THREAD_TABLE: Aligned<A4, MaybeUninit<ThreadTable>> = Aligned(MaybeUninit::zeroed());
204
205#[unsafe(link_section = "MB_MEM1")]
206pub static mut TL_LLD_TESTS_TABLE: Aligned<A4, MaybeUninit<LldTestsTable>> = Aligned(MaybeUninit::zeroed());
207
208#[unsafe(link_section = "MB_MEM1")]
209pub static mut TL_BLE_LLD_TABLE: Aligned<A4, MaybeUninit<BleLldTable>> = Aligned(MaybeUninit::zeroed());
210
211#[unsafe(link_section = "MB_MEM1")]
212pub static mut TL_SYS_TABLE: Aligned<A4, MaybeUninit<SysTable>> = Aligned(MaybeUninit::zeroed());
213
214#[unsafe(link_section = "MB_MEM1")]
215pub static mut TL_MEM_MANAGER_TABLE: Aligned<A4, MaybeUninit<MemManagerTable>> = Aligned(MaybeUninit::zeroed());
216
217#[unsafe(link_section = "MB_MEM1")]
218pub static mut TL_TRACES_TABLE: Aligned<A4, MaybeUninit<TracesTable>> = Aligned(MaybeUninit::zeroed());
219
220#[unsafe(link_section = "MB_MEM1")]
221pub static mut TL_MAC_802_15_4_TABLE: Aligned<A4, MaybeUninit<Mac802_15_4Table>> = Aligned(MaybeUninit::zeroed());
222
223#[unsafe(link_section = "MB_MEM1")]
224pub static mut TL_ZIGBEE_TABLE: Aligned<A4, MaybeUninit<ZigbeeTable>> = Aligned(MaybeUninit::zeroed());
225
226// --------------------- tables ---------------------
227#[unsafe(link_section = "MB_MEM1")]
228pub static mut FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed());
229
230#[allow(dead_code)]
231#[unsafe(link_section = "MB_MEM1")]
232pub static mut TRACES_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed());
233
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]>> =
236 Aligned(MaybeUninit::zeroed());
237
238#[unsafe(link_section = "MB_MEM2")]
239pub static mut EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed());
240
241#[unsafe(link_section = "MB_MEM2")]
242pub static mut SYSTEM_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed());
243
244// --------------------- app tables ---------------------
245#[cfg(feature = "wb55_mac")]
246#[unsafe(link_section = "MB_MEM2")]
247pub static mut MAC_802_15_4_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed());
248
249#[cfg(feature = "wb55_mac")]
250#[unsafe(link_section = "MB_MEM2")]
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]>,
253> = MaybeUninit::zeroed();
254
255#[unsafe(link_section = "MB_MEM2")]
256pub static mut EVT_POOL: Aligned<A4, MaybeUninit<[u8; POOL_SIZE]>> = Aligned(MaybeUninit::zeroed());
257
258#[unsafe(link_section = "MB_MEM2")]
259pub static mut SYS_CMD_BUF: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed());
260
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]>> =
263 Aligned(MaybeUninit::zeroed());
264
265#[cfg(feature = "wb55_mac")]
266#[unsafe(link_section = "MB_MEM2")]
267pub static mut MAC_802_15_4_CNFINDNOT: Aligned<A4, MaybeUninit<[u8; C_SIZE_CMD_STRING]>> =
268 Aligned(MaybeUninit::zeroed());
269
270#[cfg(feature = "wb55_ble")]
271#[unsafe(link_section = "MB_MEM1")]
272pub static mut BLE_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed());
273
274#[cfg(feature = "wb55_ble")]
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]>> =
277 Aligned(MaybeUninit::zeroed());
278
279#[cfg(feature = "wb55_ble")]
280#[unsafe(link_section = "MB_MEM2")]
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]>> =
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}