aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2025-01-02 00:56:05 +0100
committerGitHub <[email protected]>2025-01-02 00:56:05 +0100
commite7c3e1b2663b924f65d5ddf898030ddeffe6f368 (patch)
tree2c2a1f10bdafbdd2bed2f655c544f91af4503417
parent667dfa34b525f727936d621ba91001fa25d80426 (diff)
parentc6e0508da0e5a8689b833c60e0d8e59b922ebd8f (diff)
Merge pull request #3703 from loftyinclination/main
add documentation for the BLE feature of the embassy-stm32-wpan crate
-rw-r--r--embassy-stm32-wpan/Cargo.toml4
-rw-r--r--embassy-stm32-wpan/src/consts.rs2
-rw-r--r--embassy-stm32-wpan/src/lib.rs46
-rw-r--r--embassy-stm32-wpan/src/sub/ble.rs29
-rw-r--r--embassy-stm32-wpan/src/sub/sys.rs11
-rw-r--r--embassy-stm32-wpan/src/tables.rs27
-rw-r--r--examples/stm32wb/src/bin/gatt_server.rs5
7 files changed, 106 insertions, 18 deletions
diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml
index 552d80982..74e7a67d9 100644
--- a/embassy-stm32-wpan/Cargo.toml
+++ b/embassy-stm32-wpan/Cargo.toml
@@ -13,10 +13,10 @@ documentation = "https://docs.embassy.dev/embassy-stm32-wpan"
13src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src/" 13src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src/"
14src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src/" 14src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src/"
15target = "thumbv7em-none-eabihf" 15target = "thumbv7em-none-eabihf"
16features = ["stm32wb55rg"] 16features = ["stm32wb55rg", "ble", "mac"]
17 17
18[package.metadata.docs.rs] 18[package.metadata.docs.rs]
19features = ["stm32wb55rg"] 19features = ["stm32wb55rg", "ble", "mac"]
20 20
21[dependencies] 21[dependencies]
22embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" } 22embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" }
diff --git a/embassy-stm32-wpan/src/consts.rs b/embassy-stm32-wpan/src/consts.rs
index 6aaef1d35..e2ae6ca86 100644
--- a/embassy-stm32-wpan/src/consts.rs
+++ b/embassy-stm32-wpan/src/consts.rs
@@ -69,7 +69,7 @@ pub const TL_CS_EVT_SIZE: usize = core::mem::size_of::<CsEvt>();
69 * enough to store all asynchronous events received in between. 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 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. 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 to small, 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 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 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). 75 * to the application a HCI command did not receive its command event within 30s (Default HCI Timeout).
diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs
index fb34d4ba0..25e6d965a 100644
--- a/embassy-stm32-wpan/src/lib.rs
+++ b/embassy-stm32-wpan/src/lib.rs
@@ -1,3 +1,16 @@
1//! The embassy-stm32-wpan crate aims to provide safe use of the commands necessary to interface
2//! with the Cortex C0 CPU2 coprocessor of STM32WB microcontrollers. It implements safe wrappers
3//! around the Transport Layer, and in particular the system, memory, BLE and Mac channels.
4//!
5//! # Design
6//!
7//! This crate loosely follows the Application Note 5289 "How to build wireless applications with
8//! STM32WB MCUs"; several of the startup procedures laid out in Annex 14.1 are implemented using
9//! inline copies of the code contained within the `stm32wb_copro` C library.
10//!
11//! BLE commands are implemented via use of the [stm32wb_hci] crate, for which the
12//! [stm32wb_hci::Controller] trait has been implemented.
13
1#![no_std] 14#![no_std]
2#![allow(async_fn_in_trait)] 15#![allow(async_fn_in_trait)]
3#![doc = include_str!("../README.md")] 16#![doc = include_str!("../README.md")]
@@ -37,6 +50,7 @@ pub use crate::sub::ble::hci;
37 50
38type PacketHeader = LinkedListNode; 51type PacketHeader = LinkedListNode;
39 52
53/// Transport Layer for the Mailbox interface
40pub struct TlMbox<'d> { 54pub struct TlMbox<'d> {
41 _ipcc: PeripheralRef<'d, IPCC>, 55 _ipcc: PeripheralRef<'d, IPCC>,
42 56
@@ -49,6 +63,34 @@ pub struct TlMbox<'d> {
49} 63}
50 64
51impl<'d> TlMbox<'d> { 65impl<'d> TlMbox<'d> {
66 /// Initialise the Transport Layer, and creates and returns a wrapper around it.
67 ///
68 /// This method performs the initialisation laid out in AN5289 annex 14.1. However, it differs
69 /// from the implementation documented in Figure 64, to avoid needing to reference any C
70 /// function pointers.
71 ///
72 /// Annex 14.1 lays out the following methods that should be called:
73 /// 1. tl_mbox.c/TL_Init, which initialises the reference table that is shared between CPU1
74 /// and CPU2.
75 /// 2. shci_tl.c/shci_init(), which initialises the system transport layer, and in turn
76 /// calls tl_mbox.c/TL_SYS_Init, which initialises SYSTEM_EVT_QUEUE channel.
77 /// 3. tl_mbox.c/TL_MM_Init(), which initialises the channel used for sending memory
78 /// manager commands.
79 /// 4. tl_mbox.c/TL_Enable(), which enables the IPCC, and starts CPU2.
80 /// This implementation initialises all of the shared refernce tables and all IPCC channel that
81 /// would be initialised by this process. The developer should therefore treat this method as
82 /// completing all steps in Figure 64.
83 ///
84 /// Once this method has been called, no system commands may be sent until the CPU2 ready
85 /// signal is received, via [sys_subsystem.read]; this completes the procedure laid out in
86 /// Figure 65.
87 ///
88 /// If the `ble` feature is enabled, at this point, the user should call
89 /// [sys_subsystem.shci_c2_ble_init], before any commands are written to the
90 /// [TlMbox.ble_subsystem] ([sub::ble::Ble::new()] completes the process that would otherwise
91 /// be handled by `TL_BLE_Init`; see Figure 66). This completes the procedure laid out in
92 /// Figure 66.
93 // TODO: document what the user should do after calling init to use the mac_802_15_4 subsystem
52 pub fn init( 94 pub fn init(
53 ipcc: impl Peripheral<P = IPCC> + 'd, 95 ipcc: impl Peripheral<P = IPCC> + 'd,
54 _irqs: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler> 96 _irqs: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler>
@@ -57,6 +99,9 @@ impl<'d> TlMbox<'d> {
57 ) -> Self { 99 ) -> Self {
58 into_ref!(ipcc); 100 into_ref!(ipcc);
59 101
102 // this is an inlined version of TL_Init from the STM32WB firmware as requested by AN5289.
103 // HW_IPCC_Init is not called, and its purpose is (presumably?) covered by this
104 // implementation
60 unsafe { 105 unsafe {
61 TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable { 106 TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable {
62 device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(), 107 device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(),
@@ -140,6 +185,7 @@ impl<'d> TlMbox<'d> {
140 185
141 compiler_fence(Ordering::SeqCst); 186 compiler_fence(Ordering::SeqCst);
142 187
188 // this is equivalent to `HW_IPCC_Enable`, which is called by `TL_Enable`
143 Ipcc::enable(config); 189 Ipcc::enable(config);
144 190
145 Self { 191 Self {
diff --git a/embassy-stm32-wpan/src/sub/ble.rs b/embassy-stm32-wpan/src/sub/ble.rs
index c5f2334f4..0f770d92c 100644
--- a/embassy-stm32-wpan/src/sub/ble.rs
+++ b/embassy-stm32-wpan/src/sub/ble.rs
@@ -11,11 +11,39 @@ 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::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/// ```
14pub struct Ble { 39pub struct Ble {
15 _private: (), 40 _private: (),
16} 41}
17 42
18impl Ble { 43impl Ble {
44 /// Constructs a guard that allows for BLE commands to be sent to CPU2.
45 ///
46 /// This takes the place of `TL_BLE_Init`, completing that step as laid out in AN5289, Fig 66.
19 pub(crate) fn new() -> Self { 47 pub(crate) fn new() -> Self {
20 unsafe { 48 unsafe {
21 LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); 49 LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr());
@@ -30,6 +58,7 @@ impl Ble {
30 58
31 Self { _private: () } 59 Self { _private: () }
32 } 60 }
61
33 /// `HW_IPCC_BLE_EvtNot` 62 /// `HW_IPCC_BLE_EvtNot`
34 pub async fn tl_read(&self) -> EvtBox<Self> { 63 pub async fn tl_read(&self) -> EvtBox<Self> {
35 Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe { 64 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..cf6df58bf 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,22 @@ 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
95 /// [crate::sub::ble::hci::host::uart::UartHci::read].
89 #[cfg(feature = "ble")] 96 #[cfg(feature = "ble")]
90 pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> Result<SchiCommandStatus, ()> { 97 pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> Result<SchiCommandStatus, ()> {
91 self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await 98 self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await
92 } 99 }
93 100
94 /// `HW_IPCC_SYS_EvtNot` 101 /// `HW_IPCC_SYS_EvtNot`
102 ///
103 /// This method takes the place of the `HW_IPCC_SYS_EvtNot`/`SysUserEvtRx`/`APPE_SysUserEvtRx`,
104 /// as the embassy implementation avoids the need to call C public bindings, and instead
105 /// handles the event channels directly.
95 pub async fn read(&self) -> EvtBox<mm::MemoryManager> { 106 pub async fn read(&self) -> EvtBox<mm::MemoryManager> {
96 Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe { 107 Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe {
97 if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) { 108 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..fe6fc47a3 100644
--- a/embassy-stm32-wpan/src/tables.rs
+++ b/embassy-stm32-wpan/src/tables.rs
@@ -25,17 +25,17 @@ pub struct RssInfoTable {
25 25
26/** 26/**
27 * Version 27 * Version
28 * [0:3] = Build - 0: Untracked - 15:Released - x: Tracked version 28 * \[0:3\] = Build - 0: Untracked - 15:Released - x: Tracked version
29 * [4:7] = branch - 0: Mass Market - x: ... 29 * \[4:7\] = branch - 0: Mass Market - x: ...
30 * [8:15] = Subversion 30 * \[8:15\] = Subversion
31 * [16:23] = Version minor 31 * \[16:23\] = Version minor
32 * [24:31] = Version major 32 * \[24:31\] = Version major
33 * 33 *
34 * Memory Size 34 * Memory Size
35 * [0:7] = Flash ( Number of 4k sector) 35 * \[0:7\] = Flash ( Number of 4k sector)
36 * [8:15] = Reserved ( Shall be set to 0 - may be used as flash extension ) 36 * \[8:15\] = Reserved ( Shall be set to 0 - may be used as flash extension )
37 * [16:23] = SRAM2b ( Number of 1k sector) 37 * \[16:23\] = SRAM2b ( Number of 1k sector)
38 * [24:31] = SRAM2a ( Number of 1k sector) 38 * \[24:31\] = SRAM2a ( Number of 1k sector)
39 */ 39 */
40#[derive(Debug, Copy, Clone)] 40#[derive(Debug, Copy, Clone)]
41#[repr(C, packed)] 41#[repr(C, packed)]
@@ -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::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
@@ -271,6 +278,6 @@ pub static mut BLE_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_
271 278
272#[cfg(feature = "ble")] 279#[cfg(feature = "ble")]
273#[link_section = "MB_MEM2"] 280#[link_section = "MB_MEM2"]
274// fuck these "magic" numbers from ST ---v---v 281// fuck these "magic" numbers from ST ---v---v
275pub static mut HCI_ACL_DATA_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> = 282pub static mut HCI_ACL_DATA_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> =
276 Aligned(MaybeUninit::uninit()); 283 Aligned(MaybeUninit::uninit());
diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs
index 1cc50e134..041dc0cf5 100644
--- a/examples/stm32wb/src/bin/gatt_server.rs
+++ b/examples/stm32wb/src/bin/gatt_server.rs
@@ -151,11 +151,6 @@ async fn main(_spawner: Spawner) {
151 let response = mbox.ble_subsystem.read().await; 151 let response = mbox.ble_subsystem.read().await;
152 defmt::debug!("{}", response); 152 defmt::debug!("{}", response);
153 153
154 info!("set scan response data...");
155 mbox.ble_subsystem.le_set_scan_response_data(b"TXTX").await.unwrap();
156 let response = mbox.ble_subsystem.read().await;
157 defmt::debug!("{}", response);
158
159 defmt::info!("initializing services and characteristics..."); 154 defmt::info!("initializing services and characteristics...");
160 let mut ble_context = init_gatt_services(&mut mbox.ble_subsystem).await.unwrap(); 155 let mut ble_context = init_gatt_services(&mut mbox.ble_subsystem).await.unwrap();
161 defmt::info!("{}", ble_context); 156 defmt::info!("{}", ble_context);