aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32-wpan
diff options
context:
space:
mode:
authorLofty Inclination <[email protected]>2025-01-01 00:56:16 +0000
committerLofty Inclination <[email protected]>2025-01-01 01:35:41 +0000
commitb2cc2fda26a7ecd6d64482e1f53eaf213f2b0a54 (patch)
tree7917014ad83e8b84b87dbc0357f9bd91acb061b5 /embassy-stm32-wpan
parentc84aef75af6c1e7b60b6c86b7dcd20293f3f5a89 (diff)
Add docs for the BLE bindings
These new docs explain a lot of the current implementation for the BLE stack, with reference to AN5289 14.1. They also detail the additional requirements around BLE startup behaviour; specifically, that certain SYS events must be awaited, and that shci_c2_ble_init should be called. Since the ble feature is now enabled by default for the docs, the remaining documentation for the BLE behaviour (as implemented by the `stm32wb-hci` crate) should be bought in automatically.
Diffstat (limited to 'embassy-stm32-wpan')
-rw-r--r--embassy-stm32-wpan/src/lib.rs32
-rw-r--r--embassy-stm32-wpan/src/sub/ble.rs10
-rw-r--r--embassy-stm32-wpan/src/sub/sys.rs10
-rw-r--r--embassy-stm32-wpan/src/tables.rs7
4 files changed, 59 insertions, 0 deletions
diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs
index fb34d4ba0..1a01be200 100644
--- a/embassy-stm32-wpan/src/lib.rs
+++ b/embassy-stm32-wpan/src/lib.rs
@@ -37,6 +37,7 @@ pub use crate::sub::ble::hci;
37 37
38type PacketHeader = LinkedListNode; 38type PacketHeader = LinkedListNode;
39 39
40/// Transport Layer for the Mailbox interface
40pub struct TlMbox<'d> { 41pub struct TlMbox<'d> {
41 _ipcc: PeripheralRef<'d, IPCC>, 42 _ipcc: PeripheralRef<'d, IPCC>,
42 43
@@ -49,6 +50,33 @@ pub struct TlMbox<'d> {
49} 50}
50 51
51impl<'d> TlMbox<'d> { 52impl<'d> TlMbox<'d> {
53 /// Initialise the Transport Layer, and creates and returns a wrapper around it.
54 ///
55 /// This method performs the initialisation laid out in AN5289 annex 14.1. However, it differs
56 /// from the implementation documented in Figure 64, to avoid needing to reference any C
57 /// function pointers.
58 ///
59 /// Annex 14.1 lays out the following methods that should be called:
60 /// 1. tl_mbox.c/TL_Init, which initialises the reference table that is shared between CPU1
61 /// and CPU2.
62 /// 2. shci_tl.c/shci_init(), which initialises the system transport layer, and in turn
63 /// calls tl_mbox.c/TL_SYS_Init, which initialises SYSTEM_EVT_QUEUE channel.
64 /// 3. tl_mbox.c/TL_MM_Init(), which initialises the channel used for sending memory
65 /// manager commands.
66 /// 4. tl_mbox.c/TL_Enable(), which enables the IPCC, and starts CPU2.
67 /// This implementation initialises all of the shared refernce tables and all IPCC channel that
68 /// would be initialised by this process. The developer should therefore treat this method as
69 /// completing all steps in Figure 64.
70 ///
71 /// Once this method has been called, no system commands may be sent until the CPU2 ready
72 /// signal is received, via [sys_subsystem.read]; this completes the procedure laid out in
73 /// Figure 65.
74 ///
75 /// If the `ble` feature is enabled, at this point, the user should call
76 /// [sys_subsystem.shci_c2_ble_init], before any commands are written to the [ble_subsystem]
77 /// ([Ble::new()] completes the process that would otherwise be handled by `TL_BLE_Init`; see
78 /// Figure 66). This completes the procedure laid out in Figure 66.
79 // TODO: document what the user should do after calling init to use the mac_802_15_4 subsystem
52 pub fn init( 80 pub fn init(
53 ipcc: impl Peripheral<P = IPCC> + 'd, 81 ipcc: impl Peripheral<P = IPCC> + 'd,
54 _irqs: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler> 82 _irqs: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler>
@@ -57,6 +85,9 @@ impl<'d> TlMbox<'d> {
57 ) -> Self { 85 ) -> Self {
58 into_ref!(ipcc); 86 into_ref!(ipcc);
59 87
88 // this is an inlined version of TL_Init from the STM32WB firmware as requested by AN5289.
89 // HW_IPCC_Init is not called, and its purpose is (presumably?) covered by this
90 // implementation
60 unsafe { 91 unsafe {
61 TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable { 92 TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable {
62 device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(), 93 device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(),
@@ -140,6 +171,7 @@ impl<'d> TlMbox<'d> {
140 171
141 compiler_fence(Ordering::SeqCst); 172 compiler_fence(Ordering::SeqCst);
142 173
174 // this is equivalent to `HW_IPCC_Enable`, which is called by `TL_Enable`
143 Ipcc::enable(config); 175 Ipcc::enable(config);
144 176
145 Self { 177 Self {
diff --git a/embassy-stm32-wpan/src/sub/ble.rs b/embassy-stm32-wpan/src/sub/ble.rs
index c5f2334f4..a47c6a699 100644
--- a/embassy-stm32-wpan/src/sub/ble.rs
+++ b/embassy-stm32-wpan/src/sub/ble.rs
@@ -11,11 +11,20 @@ use crate::tables::{BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA
11use crate::unsafe_linked_list::LinkedListNode; 11use crate::unsafe_linked_list::LinkedListNode;
12use crate::{channels, evt}; 12use crate::{channels, evt};
13 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::read] before sending any of these commands, and to call
18/// [crate::sub::Sys::shci_c2_ble_init] and await the HCI_COMMAND_COMPLETE_EVENT before sending any
19/// other commands.
14pub struct Ble { 20pub struct Ble {
15 _private: (), 21 _private: (),
16} 22}
17 23
18impl Ble { 24impl Ble {
25 /// Constructs a guard that allows for BLE commands to be sent to CPU2.
26 ///
27 /// This takes the place of `TL_BLE_Init`, completing that step as laid out in AN5289, Fig 66.
19 pub(crate) fn new() -> Self { 28 pub(crate) fn new() -> Self {
20 unsafe { 29 unsafe {
21 LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); 30 LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr());
@@ -30,6 +39,7 @@ impl Ble {
30 39
31 Self { _private: () } 40 Self { _private: () }
32 } 41 }
42
33 /// `HW_IPCC_BLE_EvtNot` 43 /// `HW_IPCC_BLE_EvtNot`
34 pub async fn tl_read(&self) -> EvtBox<Self> { 44 pub async fn tl_read(&self) -> EvtBox<Self> {
35 Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe { 45 Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe {
diff --git a/embassy-stm32-wpan/src/sub/sys.rs b/embassy-stm32-wpan/src/sub/sys.rs
index bd2ea3f74..fcc2c651e 100644
--- a/embassy-stm32-wpan/src/sub/sys.rs
+++ b/embassy-stm32-wpan/src/sub/sys.rs
@@ -10,6 +10,7 @@ use crate::tables::{SysTable, WirelessFwInfoTable};
10use crate::unsafe_linked_list::LinkedListNode; 10use crate::unsafe_linked_list::LinkedListNode;
11use crate::{channels, Ipcc, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE}; 11use crate::{channels, Ipcc, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE};
12 12
13/// A guard that, once constructed, allows for sys commands to be sent to CPU2.
13pub struct Sys { 14pub struct Sys {
14 _private: (), 15 _private: (),
15} 16}
@@ -86,12 +87,21 @@ impl Sys {
86 self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await 87 self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await
87 } 88 }
88 89
90 /// Send a request to CPU2 to initialise the BLE stack.
91 ///
92 /// This must be called before any BLE commands are sent via the BLE channel (according to
93 /// AN5289, Figures 65 and 66). It should only be called after CPU2 sends a system event, via
94 /// `HW_IPCC_SYS_EvtNot`, aka `IoBusCallBackUserEvt` (as detailed in Figure 65), aka [read].
89 #[cfg(feature = "ble")] 95 #[cfg(feature = "ble")]
90 pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> Result<SchiCommandStatus, ()> { 96 pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> Result<SchiCommandStatus, ()> {
91 self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await 97 self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await
92 } 98 }
93 99
94 /// `HW_IPCC_SYS_EvtNot` 100 /// `HW_IPCC_SYS_EvtNot`
101 ///
102 /// This method takes the place of the `HW_IPCC_SYS_EvtNot`/`SysUserEvtRx`/`APPE_SysUserEvtRx`,
103 /// as the embassy implementation avoids the need to call C public bindings, and instead
104 /// handles the event channels directly.
95 pub async fn read(&self) -> EvtBox<mm::MemoryManager> { 105 pub async fn read(&self) -> EvtBox<mm::MemoryManager> {
96 Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe { 106 Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe {
97 if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) { 107 if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) {
diff --git a/embassy-stm32-wpan/src/tables.rs b/embassy-stm32-wpan/src/tables.rs
index f2c250527..e730d6d87 100644
--- a/embassy-stm32-wpan/src/tables.rs
+++ b/embassy-stm32-wpan/src/tables.rs
@@ -89,12 +89,19 @@ pub struct DeviceInfoTable {
89 pub wireless_fw_info_table: WirelessFwInfoTable, 89 pub wireless_fw_info_table: WirelessFwInfoTable,
90} 90}
91 91
92/// The bluetooth reference table, as defined in figure 67 of STM32WX AN5289.
92#[derive(Debug)] 93#[derive(Debug)]
93#[repr(C)] 94#[repr(C)]
94pub struct BleTable { 95pub struct BleTable {
96 /// A pointer to the buffer that is used for sending BLE commands.
95 pub pcmd_buffer: *mut CmdPacket, 97 pub pcmd_buffer: *mut CmdPacket,
98 /// A pointer to the buffer used for storing Command statuses.
96 pub pcs_buffer: *const u8, 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::tl_read].
97 pub pevt_queue: *const u8, 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).
98 pub phci_acl_data_buffer: *mut AclDataPacket, 105 pub phci_acl_data_buffer: *mut AclDataPacket,
99} 106}
100 107