aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2023-06-17 20:48:37 +0000
committerGitHub <[email protected]>2023-06-17 20:48:37 +0000
commitae83e6f5367197feb8361b9a28adbdedbe37e0c5 (patch)
tree79ad59816285ee938f6352e8abde15a62d06d9ba
parentec36225f8ab35fab149971e587ef506aa1c9d1ca (diff)
parent443550b353c733aee7d122468a82df432014d8fe (diff)
Merge pull request #1566 from xoviat/tl-mbox-2
tl-mbox: switch to new ipcc mechanism
-rw-r--r--embassy-futures/src/block_on.rs12
-rw-r--r--embassy-stm32-wpan/src/ble.rs84
-rw-r--r--embassy-stm32-wpan/src/cmd.rs73
-rw-r--r--embassy-stm32-wpan/src/evt.rs141
-rw-r--r--embassy-stm32-wpan/src/lib.rs95
-rw-r--r--embassy-stm32-wpan/src/mm.rs76
-rw-r--r--embassy-stm32-wpan/src/rc.rs50
-rw-r--r--embassy-stm32-wpan/src/shci.rs43
-rw-r--r--embassy-stm32-wpan/src/sys.rs81
-rw-r--r--embassy-stm32-wpan/src/unsafe_linked_list.rs35
-rw-r--r--embassy-stm32/src/ipcc.rs273
-rw-r--r--examples/stm32wb/src/bin/tl_mbox.rs8
-rw-r--r--examples/stm32wb/src/bin/tl_mbox_tx_rx.rs23
-rw-r--r--tests/stm32/src/bin/tl_mbox.rs72
14 files changed, 564 insertions, 502 deletions
diff --git a/embassy-futures/src/block_on.rs b/embassy-futures/src/block_on.rs
index da90351ec..77695216c 100644
--- a/embassy-futures/src/block_on.rs
+++ b/embassy-futures/src/block_on.rs
@@ -31,3 +31,15 @@ pub fn block_on<F: Future>(mut fut: F) -> F::Output {
31 } 31 }
32 } 32 }
33} 33}
34
35/// Poll a future once.
36pub fn poll_once<F: Future>(mut fut: F) -> Poll<F::Output> {
37 // safety: we don't move the future after this line.
38 let mut fut = unsafe { Pin::new_unchecked(&mut fut) };
39
40 let raw_waker = RawWaker::new(ptr::null(), &VTABLE);
41 let waker = unsafe { Waker::from_raw(raw_waker) };
42 let mut cx = Context::from_waker(&waker);
43
44 fut.as_mut().poll(&mut cx)
45}
diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs
index 57348a925..f0bd6f48c 100644
--- a/embassy-stm32-wpan/src/ble.rs
+++ b/embassy-stm32-wpan/src/ble.rs
@@ -1,18 +1,20 @@
1use core::marker::PhantomData;
2
1use embassy_stm32::ipcc::Ipcc; 3use embassy_stm32::ipcc::Ipcc;
2 4
3use crate::cmd::{CmdPacket, CmdSerial}; 5use crate::cmd::CmdPacket;
4use crate::consts::TlPacketType; 6use crate::consts::TlPacketType;
5use crate::evt::EvtBox; 7use crate::evt::EvtBox;
6use crate::tables::BleTable; 8use crate::tables::BleTable;
7use crate::unsafe_linked_list::LinkedListNode; 9use crate::unsafe_linked_list::LinkedListNode;
8use crate::{ 10use crate::{channels, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE};
9 channels, BLE_CMD_BUFFER, CS_BUFFER, EVT_CHANNEL, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE, TL_REF_TABLE,
10};
11 11
12pub struct Ble; 12pub struct Ble {
13 phantom: PhantomData<Ble>,
14}
13 15
14impl Ble { 16impl Ble {
15 pub(super) fn enable() { 17 pub(crate) fn new() -> Self {
16 unsafe { 18 unsafe {
17 LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); 19 LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr());
18 20
@@ -24,54 +26,38 @@ impl Ble {
24 }); 26 });
25 } 27 }
26 28
27 Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, true); 29 Self { phantom: PhantomData }
28 } 30 }
29 31 /// `HW_IPCC_BLE_EvtNot`
30 pub(super) fn evt_handler() { 32 pub async fn read(&self) -> EvtBox {
31 unsafe { 33 Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe {
32 while !LinkedListNode::is_empty(EVT_QUEUE.as_mut_ptr()) { 34 if let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) {
33 let node_ptr = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()); 35 Some(EvtBox::new(node_ptr.cast()))
34 36 } else {
35 let event = node_ptr.cast(); 37 None
36 let event = EvtBox::new(event);
37
38 EVT_CHANNEL.try_send(event).unwrap();
39 } 38 }
40 } 39 })
41 40 .await
42 Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL);
43 } 41 }
44 42
45 pub(super) fn acl_data_handler() { 43 /// `TL_BLE_SendCmd`
46 Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, false); 44 pub async fn write(&self, opcode: u16, payload: &[u8]) {
47 45 Ipcc::send(channels::cpu1::IPCC_BLE_CMD_CHANNEL, || unsafe {
48 // TODO: ACL data ack to the user 46 CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload);
47 })
48 .await;
49 } 49 }
50 50
51 pub fn ble_send_cmd(buf: &[u8]) { 51 /// `TL_BLE_SendAclData`
52 debug!("writing ble cmd"); 52 pub async fn acl_write(&self, handle: u16, payload: &[u8]) {
53 53 Ipcc::send(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, || unsafe {
54 unsafe { 54 CmdPacket::write_into(
55 let pcmd_buffer: *mut CmdPacket = (*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; 55 HCI_ACL_DATA_BUFFER.as_mut_ptr() as *mut _,
56 let pcmd_serial: *mut CmdSerial = &mut (*pcmd_buffer).cmdserial; 56 TlPacketType::AclData,
57 let pcmd_serial_buf: *mut u8 = pcmd_serial.cast(); 57 handle,
58 58 payload,
59 core::ptr::copy(buf.as_ptr(), pcmd_serial_buf, buf.len()); 59 );
60 60 })
61 let cmd_packet = &mut *(*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; 61 .await;
62 cmd_packet.cmdserial.ty = TlPacketType::BleCmd as u8;
63 }
64
65 Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_BLE_CMD_CHANNEL);
66 }
67
68 #[allow(dead_code)] // Not used currently but reserved
69 pub(super) fn ble_send_acl_data() {
70 let cmd_packet = unsafe { &mut *(*TL_REF_TABLE.assume_init().ble_table).phci_acl_data_buffer };
71
72 cmd_packet.acl_data_serial.ty = TlPacketType::AclData as u8;
73
74 Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL);
75 Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, true);
76 } 62 }
77} 63}
diff --git a/embassy-stm32-wpan/src/cmd.rs b/embassy-stm32-wpan/src/cmd.rs
index 1f7dae7f7..edca82390 100644
--- a/embassy-stm32-wpan/src/cmd.rs
+++ b/embassy-stm32-wpan/src/cmd.rs
@@ -1,5 +1,7 @@
1use crate::evt::{EvtPacket, EvtSerial}; 1use core::ptr;
2use crate::{PacketHeader, TL_EVT_HEADER_SIZE}; 2
3use crate::consts::TlPacketType;
4use crate::PacketHeader;
3 5
4#[derive(Copy, Clone)] 6#[derive(Copy, Clone)]
5#[repr(C, packed)] 7#[repr(C, packed)]
@@ -28,35 +30,34 @@ pub struct CmdSerial {
28 30
29#[derive(Copy, Clone, Default)] 31#[derive(Copy, Clone, Default)]
30#[repr(C, packed)] 32#[repr(C, packed)]
33pub struct CmdSerialStub {
34 pub ty: u8,
35 pub cmd_code: u16,
36 pub payload_len: u8,
37}
38
39#[derive(Copy, Clone, Default)]
40#[repr(C, packed)]
31pub struct CmdPacket { 41pub struct CmdPacket {
32 pub header: PacketHeader, 42 pub header: PacketHeader,
33 pub cmdserial: CmdSerial, 43 pub cmdserial: CmdSerial,
34} 44}
35 45
36impl CmdPacket { 46impl CmdPacket {
37 /// Writes an underlying CmdPacket into the provided buffer. 47 pub unsafe fn write_into(cmd_buf: *mut CmdPacket, packet_type: TlPacketType, cmd_code: u16, payload: &[u8]) {
38 /// Returns a number of bytes that were written. 48 let p_cmd_serial = &mut (*cmd_buf).cmdserial as *mut _ as *mut CmdSerialStub;
39 /// Returns an error if event kind is unknown or if provided buffer size is not enough. 49 let p_payload = &mut (*cmd_buf).cmdserial.cmd.payload as *mut _;
40 #[allow(clippy::result_unit_err)]
41 pub fn write(&self, buf: &mut [u8]) -> Result<usize, ()> {
42 unsafe {
43 let cmd_ptr: *const CmdPacket = self;
44 let self_as_evt_ptr: *const EvtPacket = cmd_ptr.cast();
45 let evt_serial: *const EvtSerial = &(*self_as_evt_ptr).evt_serial;
46
47 let acl_data: *const AclDataPacket = cmd_ptr.cast();
48 let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial;
49 let acl_serial_buf: *const u8 = acl_serial.cast();
50 50
51 let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE; 51 ptr::write_volatile(
52 if len > buf.len() { 52 p_cmd_serial,
53 return Err(()); 53 CmdSerialStub {
54 } 54 ty: packet_type as u8,
55 cmd_code: cmd_code,
56 payload_len: payload.len() as u8,
57 },
58 );
55 59
56 core::ptr::copy(acl_serial_buf, buf.as_mut_ptr(), len); 60 ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len());
57
58 Ok(len)
59 }
60 } 61 }
61} 62}
62 63
@@ -71,7 +72,33 @@ pub struct AclDataSerial {
71 72
72#[derive(Copy, Clone)] 73#[derive(Copy, Clone)]
73#[repr(C, packed)] 74#[repr(C, packed)]
75pub struct AclDataSerialStub {
76 pub ty: u8,
77 pub handle: u16,
78 pub length: u16,
79}
80
81#[derive(Copy, Clone)]
82#[repr(C, packed)]
74pub struct AclDataPacket { 83pub struct AclDataPacket {
75 pub header: PacketHeader, 84 pub header: PacketHeader,
76 pub acl_data_serial: AclDataSerial, 85 pub acl_data_serial: AclDataSerial,
77} 86}
87
88impl AclDataPacket {
89 pub unsafe fn write_into(cmd_buf: *mut AclDataPacket, packet_type: TlPacketType, handle: u16, payload: &[u8]) {
90 let p_cmd_serial = &mut (*cmd_buf).acl_data_serial as *mut _ as *mut AclDataSerialStub;
91 let p_payload = &mut (*cmd_buf).acl_data_serial.acl_data as *mut _;
92
93 ptr::write_volatile(
94 p_cmd_serial,
95 AclDataSerialStub {
96 ty: packet_type as u8,
97 handle: handle,
98 length: payload.len() as u16,
99 },
100 );
101
102 ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len());
103 }
104}
diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/evt.rs
index b53fe506e..3a9d03576 100644
--- a/embassy-stm32-wpan/src/evt.rs
+++ b/embassy-stm32-wpan/src/evt.rs
@@ -1,8 +1,6 @@
1use core::mem::MaybeUninit; 1use core::{ptr, slice};
2 2
3use super::cmd::{AclDataPacket, AclDataSerial}; 3use super::PacketHeader;
4use super::consts::TlPacketType;
5use super::{PacketHeader, TL_EVT_HEADER_SIZE};
6use crate::mm; 4use crate::mm;
7 5
8/** 6/**
@@ -63,6 +61,12 @@ pub struct EvtSerial {
63 pub evt: Evt, 61 pub evt: Evt,
64} 62}
65 63
64#[derive(Copy, Clone, Default)]
65pub struct EvtStub {
66 pub kind: u8,
67 pub evt_code: u8,
68}
69
66/// This format shall be used for all events (asynchronous and command response) reported 70/// This format shall be used for all events (asynchronous and command response) reported
67/// by the CPU2 except for the command response of a system command where the header is not there 71/// by the CPU2 except for the command response of a system command where the header is not there
68/// and the format to be used shall be `EvtSerial`. 72/// and the format to be used shall be `EvtSerial`.
@@ -101,76 +105,91 @@ impl EvtBox {
101 Self { ptr } 105 Self { ptr }
102 } 106 }
103 107
104 /// copies event data from inner pointer and returns an event structure 108 /// Returns information about the event
105 pub fn evt(&self) -> EvtPacket { 109 pub fn stub(&self) -> EvtStub {
106 let mut evt = MaybeUninit::uninit();
107 unsafe {
108 self.ptr.copy_to(evt.as_mut_ptr(), 1);
109 evt.assume_init()
110 }
111 }
112
113 /// writes an underlying [`EvtPacket`] into the provided buffer.
114 /// Returns the number of bytes that were written.
115 /// Returns an error if event kind is unknown or if provided buffer size is not enough.
116 #[allow(clippy::result_unit_err)]
117 pub fn write(&self, buf: &mut [u8]) -> Result<usize, ()> {
118 unsafe { 110 unsafe {
119 let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?; 111 let p_evt_stub = &(*self.ptr).evt_serial as *const _ as *const EvtStub;
120 112
121 let evt_data: *const EvtPacket = self.ptr.cast(); 113 ptr::read_volatile(p_evt_stub)
122 let evt_serial: *const EvtSerial = &(*evt_data).evt_serial;
123 let evt_serial_buf: *const u8 = evt_serial.cast();
124
125 let acl_data: *const AclDataPacket = self.ptr.cast();
126 let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial;
127 let acl_serial_buf: *const u8 = acl_serial.cast();
128
129 if let TlPacketType::AclData = evt_kind {
130 let len = (*acl_serial).length as usize + 5;
131 if len > buf.len() {
132 return Err(());
133 }
134
135 core::ptr::copy(evt_serial_buf, buf.as_mut_ptr(), len);
136
137 Ok(len)
138 } else {
139 let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE;
140 if len > buf.len() {
141 return Err(());
142 }
143
144 core::ptr::copy(acl_serial_buf, buf.as_mut_ptr(), len);
145
146 Ok(len)
147 }
148 } 114 }
149 } 115 }
150 116
151 /// returns the size of a buffer required to hold this event 117 pub fn payload<'a>(&self) -> &'a [u8] {
152 #[allow(clippy::result_unit_err)]
153 pub fn size(&self) -> Result<usize, ()> {
154 unsafe { 118 unsafe {
155 let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?; 119 let p_payload_len = &(*self.ptr).evt_serial.evt.payload_len as *const u8;
156 120 let p_payload = &(*self.ptr).evt_serial.evt.payload as *const u8;
157 let evt_data: *const EvtPacket = self.ptr.cast();
158 let evt_serial: *const EvtSerial = &(*evt_data).evt_serial;
159 121
160 let acl_data: *const AclDataPacket = self.ptr.cast(); 122 let payload_len = ptr::read_volatile(p_payload_len);
161 let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial;
162 123
163 if let TlPacketType::AclData = evt_kind { 124 slice::from_raw_parts(p_payload, payload_len as usize)
164 Ok((*acl_serial).length as usize + 5)
165 } else {
166 Ok((*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE)
167 }
168 } 125 }
169 } 126 }
127
128 // TODO: bring back acl
129
130 // /// writes an underlying [`EvtPacket`] into the provided buffer.
131 // /// Returns the number of bytes that were written.
132 // /// Returns an error if event kind is unknown or if provided buffer size is not enough.
133 // #[allow(clippy::result_unit_err)]
134 // pub fn write(&self, buf: &mut [u8]) -> Result<usize, ()> {
135 // unsafe {
136 // let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?;
137 //
138 // let evt_data: *const EvtPacket = self.ptr.cast();
139 // let evt_serial: *const EvtSerial = &(*evt_data).evt_serial;
140 // let evt_serial_buf: *const u8 = evt_serial.cast();
141 //
142 // let acl_data: *const AclDataPacket = self.ptr.cast();
143 // let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial;
144 // let acl_serial_buf: *const u8 = acl_serial.cast();
145 //
146 // if let TlPacketType::AclData = evt_kind {
147 // let len = (*acl_serial).length as usize + 5;
148 // if len > buf.len() {
149 // return Err(());
150 // }
151 //
152 // core::ptr::copy(evt_serial_buf, buf.as_mut_ptr(), len);
153 //
154 // Ok(len)
155 // } else {
156 // let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE;
157 // if len > buf.len() {
158 // return Err(());
159 // }
160 //
161 // core::ptr::copy(acl_serial_buf, buf.as_mut_ptr(), len);
162 //
163 // Ok(len)
164 // }
165 // }
166 // }
167 //
168 // /// returns the size of a buffer required to hold this event
169 // #[allow(clippy::result_unit_err)]
170 // pub fn size(&self) -> Result<usize, ()> {
171 // unsafe {
172 // let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?;
173 //
174 // let evt_data: *const EvtPacket = self.ptr.cast();
175 // let evt_serial: *const EvtSerial = &(*evt_data).evt_serial;
176 //
177 // let acl_data: *const AclDataPacket = self.ptr.cast();
178 // let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial;
179 //
180 // if let TlPacketType::AclData = evt_kind {
181 // Ok((*acl_serial).length as usize + 5)
182 // } else {
183 // Ok((*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE)
184 // }
185 // }
186 // }
170} 187}
171 188
172impl Drop for EvtBox { 189impl Drop for EvtBox {
173 fn drop(&mut self) { 190 fn drop(&mut self) {
174 mm::MemoryManager::evt_drop(self.ptr); 191 trace!("evt box drop packet");
192
193 unsafe { mm::MemoryManager::drop_event_packet(self.ptr) };
175 } 194 }
176} 195}
diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs
index 2852d6270..833db0df3 100644
--- a/embassy-stm32-wpan/src/lib.rs
+++ b/embassy-stm32-wpan/src/lib.rs
@@ -6,20 +6,21 @@ pub mod fmt;
6use core::mem::MaybeUninit; 6use core::mem::MaybeUninit;
7use core::sync::atomic::{compiler_fence, Ordering}; 7use core::sync::atomic::{compiler_fence, Ordering};
8 8
9use ble::Ble;
9use cmd::CmdPacket; 10use cmd::CmdPacket;
10use embassy_futures::block_on;
11use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; 11use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
12use embassy_stm32::interrupt; 12use embassy_stm32::interrupt;
13use embassy_stm32::interrupt::typelevel::Interrupt; 13use embassy_stm32::interrupt::typelevel::Interrupt;
14use embassy_stm32::ipcc::{Config, Ipcc}; 14use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler};
15use embassy_stm32::peripherals::IPCC; 15use embassy_stm32::peripherals::IPCC;
16use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 16use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
17use embassy_sync::channel::Channel; 17use embassy_sync::channel::Channel;
18use embassy_sync::signal::Signal; 18use embassy_sync::signal::Signal;
19use evt::{CcEvt, EvtBox}; 19use evt::{CcEvt, EvtBox};
20use mm::MemoryManager;
21use sys::Sys;
20use tables::{ 22use tables::{
21 BleTable, DeviceInfoTable, Mac802_15_4Table, MemManagerTable, RefTable, SysTable, ThreadTable, TracesTable, 23 BleTable, DeviceInfoTable, Mac802_15_4Table, MemManagerTable, RefTable, SysTable, ThreadTable, TracesTable,
22 WirelessFwInfoTable,
23}; 24};
24use unsafe_linked_list::LinkedListNode; 25use unsafe_linked_list::LinkedListNode;
25 26
@@ -29,50 +30,11 @@ pub mod cmd;
29pub mod consts; 30pub mod consts;
30pub mod evt; 31pub mod evt;
31pub mod mm; 32pub mod mm;
32pub mod rc;
33pub mod shci; 33pub mod shci;
34pub mod sys; 34pub mod sys;
35pub mod tables; 35pub mod tables;
36pub mod unsafe_linked_list; 36pub mod unsafe_linked_list;
37 37
38/// Interrupt handler.
39pub struct ReceiveInterruptHandler {}
40
41impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_RX> for ReceiveInterruptHandler {
42 unsafe fn on_interrupt() {
43 if Ipcc::is_rx_pending(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL) {
44 debug!("RX SYS evt");
45 sys::Sys::evt_handler();
46 } else if Ipcc::is_rx_pending(channels::cpu2::IPCC_BLE_EVENT_CHANNEL) {
47 debug!("RX BLE evt");
48 ble::Ble::evt_handler();
49 }
50
51 STATE.signal(());
52 }
53}
54
55pub struct TransmitInterruptHandler {}
56
57impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_TX> for TransmitInterruptHandler {
58 unsafe fn on_interrupt() {
59 if Ipcc::is_tx_pending(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL) {
60 debug!("TX SYS cmd rsp");
61 let cc = sys::Sys::cmd_evt_handler();
62
63 LAST_CC_EVT.signal(cc);
64 } else if Ipcc::is_tx_pending(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL) {
65 debug!("TX MM release");
66 mm::MemoryManager::free_buf_handler();
67 } else if Ipcc::is_tx_pending(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL) {
68 debug!("TX HCI acl");
69 ble::Ble::acl_data_handler();
70 }
71
72 STATE.signal(());
73 }
74}
75
76#[link_section = "TL_REF_TABLE"] 38#[link_section = "TL_REF_TABLE"]
77pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::uninit(); 39pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::uninit();
78 40
@@ -167,10 +129,14 @@ static mut BLE_CMD_BUFFER: MaybeUninit<CmdPacket> = MaybeUninit::uninit();
167// fuck these "magic" numbers from ST ---v---v 129// fuck these "magic" numbers from ST ---v---v
168static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]> = MaybeUninit::uninit(); 130static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]> = MaybeUninit::uninit();
169 131
132// TODO: remove these items
133
134#[allow(dead_code)]
170/// current event that is produced during IPCC IRQ handler execution 135/// current event that is produced during IPCC IRQ handler execution
171/// on SYS channel 136/// on SYS channel
172static EVT_CHANNEL: Channel<CriticalSectionRawMutex, EvtBox, 32> = Channel::new(); 137static EVT_CHANNEL: Channel<CriticalSectionRawMutex, EvtBox, 32> = Channel::new();
173 138
139#[allow(dead_code)]
174/// last received Command Complete event 140/// last received Command Complete event
175static LAST_CC_EVT: Signal<CriticalSectionRawMutex, CcEvt> = Signal::new(); 141static LAST_CC_EVT: Signal<CriticalSectionRawMutex, CcEvt> = Signal::new();
176 142
@@ -178,6 +144,10 @@ static STATE: Signal<CriticalSectionRawMutex, ()> = Signal::new();
178 144
179pub struct TlMbox<'d> { 145pub struct TlMbox<'d> {
180 _ipcc: PeripheralRef<'d, IPCC>, 146 _ipcc: PeripheralRef<'d, IPCC>,
147
148 pub sys_subsystem: Sys,
149 pub mm_subsystem: MemoryManager,
150 pub ble_subsystem: Ble,
181} 151}
182 152
183impl<'d> TlMbox<'d> { 153impl<'d> TlMbox<'d> {
@@ -262,9 +232,9 @@ impl<'d> TlMbox<'d> {
262 232
263 Ipcc::enable(config); 233 Ipcc::enable(config);
264 234
265 sys::Sys::enable(); 235 let sys = sys::Sys::new();
266 ble::Ble::enable(); 236 let ble = ble::Ble::new();
267 mm::MemoryManager::enable(); 237 let mm = mm::MemoryManager::new();
268 238
269 // enable interrupts 239 // enable interrupts
270 interrupt::typelevel::IPCC_C1_RX::unpend(); 240 interrupt::typelevel::IPCC_C1_RX::unpend();
@@ -275,36 +245,11 @@ impl<'d> TlMbox<'d> {
275 245
276 STATE.reset(); 246 STATE.reset();
277 247
278 Self { _ipcc: ipcc } 248 Self {
279 } 249 _ipcc: ipcc,
280 250 sys_subsystem: sys,
281 /// Returns CPU2 wireless firmware information (if present). 251 ble_subsystem: ble,
282 pub fn wireless_fw_info(&self) -> Option<WirelessFwInfoTable> { 252 mm_subsystem: mm,
283 let info = unsafe { &(*(*TL_REF_TABLE.as_ptr()).device_info_table).wireless_fw_info_table };
284
285 // Zero version indicates that CPU2 wasn't active and didn't fill the information table
286 if info.version != 0 {
287 Some(*info)
288 } else {
289 None
290 }
291 }
292
293 /// picks single [`EvtBox`] from internal event queue.
294 ///
295 /// Internal event queu is populated in IPCC_RX_IRQ handler
296 pub fn dequeue_event(&mut self) -> Option<EvtBox> {
297 EVT_CHANNEL.try_recv().ok()
298 }
299
300 /// retrieves last Command Complete event and removes it from mailbox
301 pub fn pop_last_cc_evt(&mut self) -> Option<CcEvt> {
302 if LAST_CC_EVT.signaled() {
303 let cc = block_on(LAST_CC_EVT.wait());
304 LAST_CC_EVT.reset();
305 Some(cc)
306 } else {
307 None
308 } 253 }
309 } 254 }
310} 255}
diff --git a/embassy-stm32-wpan/src/mm.rs b/embassy-stm32-wpan/src/mm.rs
index 06063b89a..21f42409a 100644
--- a/embassy-stm32-wpan/src/mm.rs
+++ b/embassy-stm32-wpan/src/mm.rs
@@ -1,19 +1,29 @@
1//! Memory manager routines 1//! Memory manager routines
2 2
3use core::future::poll_fn;
4use core::marker::PhantomData;
5use core::task::Poll;
6
7use cortex_m::interrupt;
3use embassy_stm32::ipcc::Ipcc; 8use embassy_stm32::ipcc::Ipcc;
9use embassy_sync::waitqueue::AtomicWaker;
4 10
5use crate::evt::EvtPacket; 11use crate::evt::EvtPacket;
6use crate::tables::MemManagerTable; 12use crate::tables::MemManagerTable;
7use crate::unsafe_linked_list::LinkedListNode; 13use crate::unsafe_linked_list::LinkedListNode;
8use crate::{ 14use crate::{
9 channels, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, SYS_SPARE_EVT_BUF, 15 channels, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, SYS_SPARE_EVT_BUF,
10 TL_MEM_MANAGER_TABLE, TL_REF_TABLE, 16 TL_MEM_MANAGER_TABLE,
11}; 17};
12 18
13pub(super) struct MemoryManager; 19static MM_WAKER: AtomicWaker = AtomicWaker::new();
20
21pub struct MemoryManager {
22 phantom: PhantomData<MemoryManager>,
23}
14 24
15impl MemoryManager { 25impl MemoryManager {
16 pub fn enable() { 26 pub(crate) fn new() -> Self {
17 unsafe { 27 unsafe {
18 LinkedListNode::init_head(FREE_BUF_QUEUE.as_mut_ptr()); 28 LinkedListNode::init_head(FREE_BUF_QUEUE.as_mut_ptr());
19 LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); 29 LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr());
@@ -28,44 +38,40 @@ impl MemoryManager {
28 tracespoolsize: 0, 38 tracespoolsize: 0,
29 }); 39 });
30 } 40 }
31 }
32
33 pub fn evt_drop(evt: *mut EvtPacket) {
34 unsafe {
35 let list_node = evt.cast();
36 41
37 LinkedListNode::insert_tail(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), list_node); 42 Self { phantom: PhantomData }
43 }
38 44
39 let channel_is_busy = Ipcc::c1_is_active_flag(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); 45 /// SAFETY: passing a pointer to something other than an event packet is UB
46 pub(crate) unsafe fn drop_event_packet(evt: *mut EvtPacket) {
47 interrupt::free(|_| unsafe {
48 LinkedListNode::insert_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), evt as *mut _);
49 });
40 50
41 // postpone event buffer freeing to IPCC interrupt handler 51 MM_WAKER.wake();
42 if channel_is_busy {
43 Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, true);
44 } else {
45 Self::send_free_buf();
46 Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL);
47 }
48 }
49 } 52 }
50 53
51 /// gives free event buffers back to CPU2 from local buffer queue 54 pub async fn run_queue(&self) {
52 pub fn send_free_buf() { 55 loop {
53 unsafe { 56 poll_fn(|cx| unsafe {
54 while !LinkedListNode::is_empty(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { 57 MM_WAKER.register(cx.waker());
55 let node_ptr = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); 58 if LinkedListNode::is_empty(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) {
59 Poll::Pending
60 } else {
61 Poll::Ready(())
62 }
63 })
64 .await;
56 65
57 LinkedListNode::insert_tail( 66 Ipcc::send(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, || {
58 (*(*TL_REF_TABLE.as_ptr()).mem_manager_table).pevt_free_buffer_queue, 67 interrupt::free(|_| unsafe {
59 node_ptr, 68 // CS required while moving nodes
60 ); 69 while let Some(node_ptr) = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) {
61 } 70 LinkedListNode::insert_head(FREE_BUF_QUEUE.as_mut_ptr(), node_ptr);
71 }
72 })
73 })
74 .await;
62 } 75 }
63 } 76 }
64
65 /// free buffer channel interrupt handler
66 pub fn free_buf_handler() {
67 Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, false);
68 Self::send_free_buf();
69 Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL);
70 }
71} 77}
diff --git a/embassy-stm32-wpan/src/rc.rs b/embassy-stm32-wpan/src/rc.rs
deleted file mode 100644
index aae2265ed..000000000
--- a/embassy-stm32-wpan/src/rc.rs
+++ /dev/null
@@ -1,50 +0,0 @@
1use crate::ble::Ble;
2use crate::consts::TlPacketType;
3use crate::{shci, TlMbox, STATE};
4
5pub struct RadioCoprocessor<'d> {
6 mbox: TlMbox<'d>,
7 rx_buf: [u8; 500],
8}
9
10impl<'d> RadioCoprocessor<'d> {
11 pub fn new(mbox: TlMbox<'d>) -> Self {
12 Self {
13 mbox,
14 rx_buf: [0u8; 500],
15 }
16 }
17
18 pub fn write(&self, buf: &[u8]) {
19 let cmd_code = buf[0];
20 let cmd = TlPacketType::try_from(cmd_code).unwrap();
21
22 match &cmd {
23 TlPacketType::BleCmd => Ble::ble_send_cmd(buf),
24 _ => todo!(),
25 }
26 }
27
28 pub async fn read(&mut self) -> &[u8] {
29 loop {
30 STATE.wait().await;
31
32 while let Some(evt) = self.mbox.dequeue_event() {
33 let event = evt.evt();
34
35 evt.write(&mut self.rx_buf).unwrap();
36
37 if event.kind() == 18 {
38 shci::shci_ble_init(Default::default());
39 self.rx_buf[0] = 0x04;
40 }
41 }
42
43 if self.mbox.pop_last_cc_evt().is_some() {
44 continue;
45 }
46
47 return &self.rx_buf;
48 }
49 }
50}
diff --git a/embassy-stm32-wpan/src/shci.rs b/embassy-stm32-wpan/src/shci.rs
index 8537995ff..cdf027d5e 100644
--- a/embassy-stm32-wpan/src/shci.rs
+++ b/embassy-stm32-wpan/src/shci.rs
@@ -1,8 +1,8 @@
1use super::cmd::CmdPacket; 1use core::{mem, slice};
2use super::consts::TlPacketType;
3use super::{sys, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE, TL_SYS_TABLE};
4 2
5const SCHI_OPCODE_BLE_INIT: u16 = 0xfc66; 3use super::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE};
4
5pub const SCHI_OPCODE_BLE_INIT: u16 = 0xfc66;
6 6
7#[derive(Debug, Clone, Copy)] 7#[derive(Debug, Clone, Copy)]
8#[repr(C, packed)] 8#[repr(C, packed)]
@@ -32,6 +32,12 @@ pub struct ShciBleInitCmdParam {
32 pub hw_version: u8, 32 pub hw_version: u8,
33} 33}
34 34
35impl ShciBleInitCmdParam {
36 pub fn payload<'a>(&self) -> &'a [u8] {
37 unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::<Self>()) }
38 }
39}
40
35impl Default for ShciBleInitCmdParam { 41impl Default for ShciBleInitCmdParam {
36 fn default() -> Self { 42 fn default() -> Self {
37 Self { 43 Self {
@@ -66,35 +72,10 @@ pub struct ShciHeader {
66#[derive(Debug, Clone, Copy)] 72#[derive(Debug, Clone, Copy)]
67#[repr(C, packed)] 73#[repr(C, packed)]
68pub struct ShciBleInitCmdPacket { 74pub struct ShciBleInitCmdPacket {
69 header: ShciHeader, 75 pub header: ShciHeader,
70 param: ShciBleInitCmdParam, 76 pub param: ShciBleInitCmdParam,
71} 77}
72 78
73pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE; 79pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE;
74#[allow(dead_code)] // Not used currently but reserved 80#[allow(dead_code)] // Not used currently but reserved
75const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE; 81const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE;
76
77pub fn shci_ble_init(param: ShciBleInitCmdParam) {
78 debug!("sending SHCI");
79
80 let mut packet = ShciBleInitCmdPacket {
81 header: ShciHeader::default(),
82 param,
83 };
84
85 let packet_ptr: *mut _ = &mut packet;
86
87 unsafe {
88 let cmd_ptr: *mut CmdPacket = packet_ptr.cast();
89
90 (*cmd_ptr).cmdserial.cmd.cmd_code = SCHI_OPCODE_BLE_INIT;
91 (*cmd_ptr).cmdserial.cmd.payload_len = core::mem::size_of::<ShciBleInitCmdParam>() as u8;
92
93 let p_cmd_buffer = &mut *(*TL_SYS_TABLE.as_mut_ptr()).pcmd_buffer;
94 core::ptr::write(p_cmd_buffer, *cmd_ptr);
95
96 p_cmd_buffer.cmdserial.ty = TlPacketType::SysCmd as u8;
97
98 sys::Sys::send_cmd();
99 }
100}
diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sys.rs
index 0cff5c746..a185cd4f1 100644
--- a/embassy-stm32-wpan/src/sys.rs
+++ b/embassy-stm32-wpan/src/sys.rs
@@ -1,65 +1,70 @@
1use embassy_stm32::ipcc::Ipcc; 1use core::marker::PhantomData;
2 2
3use crate::cmd::{CmdPacket, CmdSerial}; 3use crate::cmd::CmdPacket;
4use crate::evt::{CcEvt, EvtBox, EvtSerial}; 4use crate::consts::TlPacketType;
5use crate::tables::SysTable; 5use crate::evt::EvtBox;
6use crate::shci::{ShciBleInitCmdParam, SCHI_OPCODE_BLE_INIT};
7use crate::tables::{SysTable, WirelessFwInfoTable};
6use crate::unsafe_linked_list::LinkedListNode; 8use crate::unsafe_linked_list::LinkedListNode;
7use crate::{channels, EVT_CHANNEL, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE}; 9use crate::{channels, Ipcc, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE};
8 10
9pub struct Sys; 11pub struct Sys {
12 phantom: PhantomData<Sys>,
13}
10 14
11impl Sys { 15impl Sys {
12 pub fn enable() { 16 /// TL_Sys_Init
17 pub(crate) fn new() -> Self {
13 unsafe { 18 unsafe {
14 LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); 19 LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr());
15 20
16 TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable { 21 TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable {
17 pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(), 22 pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(),
18 sys_queue: SYSTEM_EVT_QUEUE.as_ptr(), 23 sys_queue: SYSTEM_EVT_QUEUE.as_ptr(),
19 }) 24 });
20 } 25 }
21 26
22 Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, true); 27 Self { phantom: PhantomData }
23 } 28 }
24 29
25 pub fn cmd_evt_handler() -> CcEvt { 30 /// Returns CPU2 wireless firmware information (if present).
26 Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, false); 31 pub fn wireless_fw_info(&self) -> Option<WirelessFwInfoTable> {
32 let info = unsafe { TL_DEVICE_INFO_TABLE.as_mut_ptr().read_volatile().wireless_fw_info_table };
27 33
28 // ST's command response data structure is really convoluted. 34 // Zero version indicates that CPU2 wasn't active and didn't fill the information table
29 // 35 if info.version != 0 {
30 // for command response events on SYS channel, the header is missing 36 Some(info)
31 // and one should: 37 } else {
32 // 1. interpret the content of CMD_BUFFER as CmdPacket 38 None
33 // 2. Access CmdPacket's cmdserial field and interpret its content as EvtSerial
34 // 3. Access EvtSerial's evt field (as Evt) and interpret its payload as CcEvt
35 // 4. CcEvt type is the actual SHCI response
36 // 5. profit
37 unsafe {
38 let pcmd: *const CmdPacket = (*TL_SYS_TABLE.as_ptr()).pcmd_buffer;
39 let cmd_serial: *const CmdSerial = &(*pcmd).cmdserial;
40 let evt_serial: *const EvtSerial = cmd_serial.cast();
41 let cc: *const CcEvt = (*evt_serial).evt.payload.as_ptr().cast();
42 *cc
43 } 39 }
44 } 40 }
45 41
46 pub fn evt_handler() { 42 pub fn write(&self, opcode: u16, payload: &[u8]) {
47 unsafe { 43 unsafe {
48 while !LinkedListNode::is_empty(SYSTEM_EVT_QUEUE.as_mut_ptr()) { 44 CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode, payload);
49 let node_ptr = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); 45 }
46 }
50 47
51 let event = node_ptr.cast(); 48 pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) {
52 let event = EvtBox::new(event); 49 debug!("sending SHCI");
53 50
54 EVT_CHANNEL.try_send(event).unwrap(); 51 Ipcc::send(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, || {
55 } 52 self.write(SCHI_OPCODE_BLE_INIT, param.payload());
56 } 53 })
54 .await;
57 55
58 Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL); 56 Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await;
59 } 57 }
60 58
61 pub fn send_cmd() { 59 /// `HW_IPCC_SYS_EvtNot`
62 Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); 60 pub async fn read(&self) -> EvtBox {
63 Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); 61 Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe {
62 if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) {
63 Some(EvtBox::new(node_ptr.cast()))
64 } else {
65 None
66 }
67 })
68 .await
64 } 69 }
65} 70}
diff --git a/embassy-stm32-wpan/src/unsafe_linked_list.rs b/embassy-stm32-wpan/src/unsafe_linked_list.rs
index a312178b3..d8bc29763 100644
--- a/embassy-stm32-wpan/src/unsafe_linked_list.rs
+++ b/embassy-stm32-wpan/src/unsafe_linked_list.rs
@@ -117,7 +117,12 @@ impl LinkedListNode {
117 /// Remove `node` from the linked list 117 /// Remove `node` from the linked list
118 pub unsafe fn remove_node(mut p_node: *mut LinkedListNode) { 118 pub unsafe fn remove_node(mut p_node: *mut LinkedListNode) {
119 interrupt::free(|_| { 119 interrupt::free(|_| {
120 let node = ptr::read_volatile(p_node); 120 // trace!("remove node: {:x}", p_node);
121 // apparently linked list nodes are not always aligned.
122 // if more hardfaults occur, more of these may need to be converted to unaligned.
123 let node = ptr::read_unaligned(p_node);
124 // trace!("remove node: prev/next {:x}/{:x}", node.prev, node.next);
125
121 if node.next != node.prev { 126 if node.next != node.prev {
122 let mut node_next = ptr::read_volatile(node.next); 127 let mut node_next = ptr::read_volatile(node.next);
123 let mut node_prev = ptr::read_volatile(node.prev); 128 let mut node_prev = ptr::read_volatile(node.prev);
@@ -139,28 +144,36 @@ impl LinkedListNode {
139 } 144 }
140 145
141 /// Remove `list_head` and return a pointer to the `node`. 146 /// Remove `list_head` and return a pointer to the `node`.
142 pub unsafe fn remove_head(mut p_list_head: *mut LinkedListNode) -> *mut LinkedListNode { 147 pub unsafe fn remove_head(mut p_list_head: *mut LinkedListNode) -> Option<*mut LinkedListNode> {
143 interrupt::free(|_| { 148 interrupt::free(|_| {
144 let list_head = ptr::read_volatile(p_list_head); 149 let list_head = ptr::read_volatile(p_list_head);
145 150
146 // Allowed because a removed node is not seen by another core 151 if list_head.next == p_list_head {
147 let p_node = list_head.next; 152 None
148 Self::remove_node(p_node); 153 } else {
154 // Allowed because a removed node is not seen by another core
155 let p_node = list_head.next;
156 Self::remove_node(p_node);
149 157
150 p_node 158 Some(p_node)
159 }
151 }) 160 })
152 } 161 }
153 162
154 /// Remove `list_tail` and return a pointer to the `node`. 163 /// Remove `list_tail` and return a pointer to the `node`.
155 pub unsafe fn remove_tail(mut p_list_tail: *mut LinkedListNode) -> *mut LinkedListNode { 164 pub unsafe fn remove_tail(mut p_list_tail: *mut LinkedListNode) -> Option<*mut LinkedListNode> {
156 interrupt::free(|_| { 165 interrupt::free(|_| {
157 let list_tail = ptr::read_volatile(p_list_tail); 166 let list_tail = ptr::read_volatile(p_list_tail);
158 167
159 // Allowed because a removed node is not seen by another core 168 if list_tail.prev == p_list_tail {
160 let p_node = list_tail.prev; 169 None
161 Self::remove_node(p_node); 170 } else {
171 // Allowed because a removed node is not seen by another core
172 let p_node = list_tail.prev;
173 Self::remove_node(p_node);
162 174
163 p_node 175 Some(p_node)
176 }
164 }) 177 })
165 } 178 }
166 179
diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs
index 8bb0774b8..609c4d2c3 100644
--- a/embassy-stm32/src/ipcc.rs
+++ b/embassy-stm32/src/ipcc.rs
@@ -1,7 +1,77 @@
1use core::future::poll_fn;
2use core::task::Poll;
3
4use atomic_polyfill::{compiler_fence, Ordering};
5
1use self::sealed::Instance; 6use self::sealed::Instance;
7use crate::interrupt;
8use crate::interrupt::typelevel::Interrupt;
2use crate::peripherals::IPCC; 9use crate::peripherals::IPCC;
3use crate::rcc::sealed::RccPeripheral; 10use crate::rcc::sealed::RccPeripheral;
4 11
12/// Interrupt handler.
13pub struct ReceiveInterruptHandler {}
14
15impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_RX> for ReceiveInterruptHandler {
16 unsafe fn on_interrupt() {
17 let regs = IPCC::regs();
18
19 let channels = [
20 IpccChannel::Channel1,
21 IpccChannel::Channel2,
22 IpccChannel::Channel3,
23 IpccChannel::Channel4,
24 IpccChannel::Channel5,
25 IpccChannel::Channel6,
26 ];
27
28 // Status register gives channel occupied status. For rx, use cpu1.
29 let sr = unsafe { regs.cpu(1).sr().read() };
30 regs.cpu(0).mr().modify(|w| {
31 for channel in channels {
32 if sr.chf(channel as usize) {
33 // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt
34 w.set_chom(channel as usize, true);
35
36 // There shouldn't be a race because the channel is masked only if the interrupt has fired
37 IPCC::state().rx_waker_for(channel).wake();
38 }
39 }
40 })
41 }
42}
43
44pub struct TransmitInterruptHandler {}
45
46impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_TX> for TransmitInterruptHandler {
47 unsafe fn on_interrupt() {
48 let regs = IPCC::regs();
49
50 let channels = [
51 IpccChannel::Channel1,
52 IpccChannel::Channel2,
53 IpccChannel::Channel3,
54 IpccChannel::Channel4,
55 IpccChannel::Channel5,
56 IpccChannel::Channel6,
57 ];
58
59 // Status register gives channel occupied status. For tx, use cpu0.
60 let sr = unsafe { regs.cpu(0).sr().read() };
61 regs.cpu(0).mr().modify(|w| {
62 for channel in channels {
63 if !sr.chf(channel as usize) {
64 // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt
65 w.set_chfm(channel as usize, true);
66
67 // There shouldn't be a race because the channel is masked only if the interrupt has fired
68 IPCC::state().tx_waker_for(channel).wake();
69 }
70 }
71 });
72 }
73}
74
5#[non_exhaustive] 75#[non_exhaustive]
6#[derive(Clone, Copy, Default)] 76#[derive(Clone, Copy, Default)]
7pub struct Config { 77pub struct Config {
@@ -20,13 +90,6 @@ pub enum IpccChannel {
20 Channel6 = 5, 90 Channel6 = 5,
21} 91}
22 92
23pub mod sealed {
24 pub trait Instance: crate::rcc::RccPeripheral {
25 fn regs() -> crate::pac::ipcc::Ipcc;
26 fn set_cpu2(enabled: bool);
27 }
28}
29
30pub struct Ipcc; 93pub struct Ipcc;
31 94
32impl Ipcc { 95impl Ipcc {
@@ -45,128 +108,170 @@ impl Ipcc {
45 w.set_txfie(true); 108 w.set_txfie(true);
46 }) 109 })
47 } 110 }
48 }
49 111
50 pub fn c1_set_rx_channel(channel: IpccChannel, enabled: bool) { 112 // enable interrupts
51 let regs = IPCC::regs(); 113 crate::interrupt::typelevel::IPCC_C1_RX::unpend();
114 crate::interrupt::typelevel::IPCC_C1_TX::unpend();
52 115
53 // If bit is set to 1 then interrupt is disabled 116 unsafe { crate::interrupt::typelevel::IPCC_C1_RX::enable() };
54 unsafe { regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, !enabled)) } 117 unsafe { crate::interrupt::typelevel::IPCC_C1_TX::enable() };
55 } 118 }
56 119
57 pub fn c1_get_rx_channel(channel: IpccChannel) -> bool { 120 /// Send data to an IPCC channel. The closure is called to write the data when appropriate.
121 pub async fn send(channel: IpccChannel, f: impl FnOnce()) {
58 let regs = IPCC::regs(); 122 let regs = IPCC::regs();
59 123
60 // If bit is set to 1 then interrupt is disabled 124 Self::flush(channel).await;
61 unsafe { !regs.cpu(0).mr().read().chom(channel as usize) } 125 compiler_fence(Ordering::SeqCst);
62 }
63 126
64 #[allow(dead_code)] 127 f();
65 pub fn c2_set_rx_channel(channel: IpccChannel, enabled: bool) {
66 let regs = IPCC::regs();
67
68 // If bit is set to 1 then interrupt is disabled
69 unsafe { regs.cpu(1).mr().modify(|w| w.set_chom(channel as usize, !enabled)) }
70 }
71 128
72 #[allow(dead_code)] 129 compiler_fence(Ordering::SeqCst);
73 pub fn c2_get_rx_channel(channel: IpccChannel) -> bool {
74 let regs = IPCC::regs();
75 130
76 // If bit is set to 1 then interrupt is disabled 131 trace!("ipcc: ch {}: send data", channel as u8);
77 unsafe { !regs.cpu(1).mr().read().chom(channel as usize) } 132 unsafe { regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)) }
78 } 133 }
79 134
80 pub fn c1_set_tx_channel(channel: IpccChannel, enabled: bool) { 135 /// Wait for the tx channel to become clear
136 pub async fn flush(channel: IpccChannel) {
81 let regs = IPCC::regs(); 137 let regs = IPCC::regs();
82 138
83 // If bit is set to 1 then interrupt is disabled 139 // This is a race, but is nice for debugging
84 unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) } 140 if unsafe { regs.cpu(0).sr().read() }.chf(channel as usize) {
85 } 141 trace!("ipcc: ch {}: wait for tx free", channel as u8);
142 }
86 143
87 pub fn c1_get_tx_channel(channel: IpccChannel) -> bool { 144 poll_fn(|cx| {
88 let regs = IPCC::regs(); 145 IPCC::state().tx_waker_for(channel).register(cx.waker());
146 // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt
147 unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, false)) }
89 148
90 // If bit is set to 1 then interrupt is disabled 149 compiler_fence(Ordering::SeqCst);
91 unsafe { !regs.cpu(0).mr().read().chfm(channel as usize) }
92 }
93 150
94 #[allow(dead_code)] 151 if !unsafe { regs.cpu(0).sr().read() }.chf(channel as usize) {
95 pub fn c2_set_tx_channel(channel: IpccChannel, enabled: bool) { 152 // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt
96 let regs = IPCC::regs(); 153 unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)) }
97 154
98 // If bit is set to 1 then interrupt is disabled 155 Poll::Ready(())
99 unsafe { regs.cpu(1).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) } 156 } else {
157 Poll::Pending
158 }
159 })
160 .await;
100 } 161 }
101 162
102 #[allow(dead_code)] 163 /// Receive data from an IPCC channel. The closure is called to read the data when appropriate.
103 pub fn c2_get_tx_channel(channel: IpccChannel) -> bool { 164 pub async fn receive<R>(channel: IpccChannel, mut f: impl FnMut() -> Option<R>) -> R {
104 let regs = IPCC::regs(); 165 let regs = IPCC::regs();
105 166
106 // If bit is set to 1 then interrupt is disabled 167 loop {
107 unsafe { !regs.cpu(1).mr().read().chfm(channel as usize) } 168 // This is a race, but is nice for debugging
108 } 169 if !unsafe { regs.cpu(1).sr().read() }.chf(channel as usize) {
170 trace!("ipcc: ch {}: wait for rx occupied", channel as u8);
171 }
109 172
110 /// clears IPCC receive channel status for CPU1 173 poll_fn(|cx| {
111 pub fn c1_clear_flag_channel(channel: IpccChannel) { 174 IPCC::state().rx_waker_for(channel).register(cx.waker());
112 let regs = IPCC::regs(); 175 // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt
176 unsafe { regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, false)) }
113 177
114 unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)) } 178 compiler_fence(Ordering::SeqCst);
115 }
116 179
117 #[allow(dead_code)] 180 if unsafe { regs.cpu(1).sr().read() }.chf(channel as usize) {
118 /// clears IPCC receive channel status for CPU2 181 // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt
119 pub fn c2_clear_flag_channel(channel: IpccChannel) { 182 unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)) }
120 let regs = IPCC::regs();
121 183
122 unsafe { regs.cpu(1).scr().write(|w| w.set_chc(channel as usize, true)) } 184 Poll::Ready(())
123 } 185 } else {
186 Poll::Pending
187 }
188 })
189 .await;
124 190
125 pub fn c1_set_flag_channel(channel: IpccChannel) { 191 trace!("ipcc: ch {}: read data", channel as u8);
126 let regs = IPCC::regs(); 192 compiler_fence(Ordering::SeqCst);
127 193
128 unsafe { regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)) } 194 match f() {
129 } 195 Some(ret) => return ret,
196 None => {}
197 }
130 198
131 #[allow(dead_code)] 199 trace!("ipcc: ch {}: clear rx", channel as u8);
132 pub fn c2_set_flag_channel(channel: IpccChannel) { 200 compiler_fence(Ordering::SeqCst);
133 let regs = IPCC::regs(); 201 // If the channel is clear and the read function returns none, fetch more data
202 unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)) }
203 }
204 }
205}
134 206
135 unsafe { regs.cpu(1).scr().write(|w| w.set_chs(channel as usize, true)) } 207impl sealed::Instance for crate::peripherals::IPCC {
208 fn regs() -> crate::pac::ipcc::Ipcc {
209 crate::pac::IPCC
136 } 210 }
137 211
138 pub fn c1_is_active_flag(channel: IpccChannel) -> bool { 212 fn set_cpu2(enabled: bool) {
139 let regs = IPCC::regs(); 213 unsafe { crate::pac::PWR.cr4().modify(|w| w.set_c2boot(enabled)) }
214 }
140 215
141 unsafe { regs.cpu(0).sr().read().chf(channel as usize) } 216 fn state() -> &'static self::sealed::State {
217 static STATE: self::sealed::State = self::sealed::State::new();
218 &STATE
142 } 219 }
220}
143 221
144 pub fn c2_is_active_flag(channel: IpccChannel) -> bool { 222pub(crate) mod sealed {
145 let regs = IPCC::regs(); 223 use embassy_sync::waitqueue::AtomicWaker;
146 224
147 unsafe { regs.cpu(1).sr().read().chf(channel as usize) } 225 use super::*;
148 }
149 226
150 pub fn is_tx_pending(channel: IpccChannel) -> bool { 227 pub struct State {
151 !Self::c1_is_active_flag(channel) && Self::c1_get_tx_channel(channel) 228 rx_wakers: [AtomicWaker; 6],
229 tx_wakers: [AtomicWaker; 6],
152 } 230 }
153 231
154 pub fn is_rx_pending(channel: IpccChannel) -> bool { 232 impl State {
155 Self::c2_is_active_flag(channel) && Self::c1_get_rx_channel(channel) 233 pub const fn new() -> Self {
156 } 234 const WAKER: AtomicWaker = AtomicWaker::new();
157}
158 235
159impl sealed::Instance for crate::peripherals::IPCC { 236 Self {
160 fn regs() -> crate::pac::ipcc::Ipcc { 237 rx_wakers: [WAKER; 6],
161 crate::pac::IPCC 238 tx_wakers: [WAKER; 6],
239 }
240 }
241
242 pub fn rx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker {
243 match channel {
244 IpccChannel::Channel1 => &self.rx_wakers[0],
245 IpccChannel::Channel2 => &self.rx_wakers[1],
246 IpccChannel::Channel3 => &self.rx_wakers[2],
247 IpccChannel::Channel4 => &self.rx_wakers[3],
248 IpccChannel::Channel5 => &self.rx_wakers[4],
249 IpccChannel::Channel6 => &self.rx_wakers[5],
250 }
251 }
252
253 pub fn tx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker {
254 match channel {
255 IpccChannel::Channel1 => &self.tx_wakers[0],
256 IpccChannel::Channel2 => &self.tx_wakers[1],
257 IpccChannel::Channel3 => &self.tx_wakers[2],
258 IpccChannel::Channel4 => &self.tx_wakers[3],
259 IpccChannel::Channel5 => &self.tx_wakers[4],
260 IpccChannel::Channel6 => &self.tx_wakers[5],
261 }
262 }
162 } 263 }
163 264
164 fn set_cpu2(enabled: bool) { 265 pub trait Instance: crate::rcc::RccPeripheral {
165 unsafe { crate::pac::PWR.cr4().modify(|w| w.set_c2boot(enabled)) } 266 fn regs() -> crate::pac::ipcc::Ipcc;
267 fn set_cpu2(enabled: bool);
268 fn state() -> &'static State;
166 } 269 }
167} 270}
168 271
169unsafe fn _configure_pwr() { 272unsafe fn _configure_pwr() {
273 // TODO: move this to RCC
274
170 let pwr = crate::pac::PWR; 275 let pwr = crate::pac::PWR;
171 let rcc = crate::pac::RCC; 276 let rcc = crate::pac::RCC;
172 277
diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs
index ae36a7e79..9fc4b8aac 100644
--- a/examples/stm32wb/src/bin/tl_mbox.rs
+++ b/examples/stm32wb/src/bin/tl_mbox.rs
@@ -5,14 +5,14 @@
5use defmt::*; 5use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_stm32::bind_interrupts; 7use embassy_stm32::bind_interrupts;
8use embassy_stm32::ipcc::Config; 8use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};
9use embassy_stm32_wpan::TlMbox; 9use embassy_stm32_wpan::TlMbox;
10use embassy_time::{Duration, Timer}; 10use embassy_time::{Duration, Timer};
11use {defmt_rtt as _, panic_probe as _}; 11use {defmt_rtt as _, panic_probe as _};
12 12
13bind_interrupts!(struct Irqs{ 13bind_interrupts!(struct Irqs{
14 IPCC_C1_RX => embassy_stm32_wpan::ReceiveInterruptHandler; 14 IPCC_C1_RX => ReceiveInterruptHandler;
15 IPCC_C1_TX => embassy_stm32_wpan::TransmitInterruptHandler; 15 IPCC_C1_TX => TransmitInterruptHandler;
16}); 16});
17 17
18#[embassy_executor::main] 18#[embassy_executor::main]
@@ -48,7 +48,7 @@ async fn main(_spawner: Spawner) {
48 let mbox = TlMbox::init(p.IPCC, Irqs, config); 48 let mbox = TlMbox::init(p.IPCC, Irqs, config);
49 49
50 loop { 50 loop {
51 let wireless_fw_info = mbox.wireless_fw_info(); 51 let wireless_fw_info = mbox.sys_subsystem.wireless_fw_info();
52 match wireless_fw_info { 52 match wireless_fw_info {
53 None => info!("not yet initialized"), 53 None => info!("not yet initialized"),
54 Some(fw_info) => { 54 Some(fw_info) => {
diff --git a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs
index 3132ab3e4..439bd01ac 100644
--- a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs
+++ b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs
@@ -5,14 +5,13 @@
5use defmt::*; 5use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_stm32::bind_interrupts; 7use embassy_stm32::bind_interrupts;
8use embassy_stm32::ipcc::Config; 8use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};
9use embassy_stm32_wpan::rc::RadioCoprocessor;
10use embassy_stm32_wpan::TlMbox; 9use embassy_stm32_wpan::TlMbox;
11use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
12 11
13bind_interrupts!(struct Irqs{ 12bind_interrupts!(struct Irqs{
14 IPCC_C1_RX => embassy_stm32_wpan::ReceiveInterruptHandler; 13 IPCC_C1_RX => ReceiveInterruptHandler;
15 IPCC_C1_TX => embassy_stm32_wpan::TransmitInterruptHandler; 14 IPCC_C1_TX => TransmitInterruptHandler;
16}); 15});
17 16
18#[embassy_executor::main] 17#[embassy_executor::main]
@@ -47,14 +46,18 @@ async fn main(_spawner: Spawner) {
47 let config = Config::default(); 46 let config = Config::default();
48 let mbox = TlMbox::init(p.IPCC, Irqs, config); 47 let mbox = TlMbox::init(p.IPCC, Irqs, config);
49 48
50 let mut rc = RadioCoprocessor::new(mbox); 49 let sys_event = mbox.sys_subsystem.read().await;
50 info!("sys event: {}", sys_event.payload());
51 51
52 let response = rc.read().await; 52 mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await;
53 info!("coprocessor ready {}", response);
54 53
55 rc.write(&[0x01, 0x03, 0x0c, 0x00, 0x00]); 54 info!("starting ble...");
56 let response = rc.read().await; 55 mbox.ble_subsystem.write(0x0c, &[]).await;
57 info!("ble reset rsp {}", response); 56
57 info!("waiting for ble...");
58 let ble_event = mbox.ble_subsystem.read().await;
59
60 info!("ble event: {}", ble_event.payload());
58 61
59 info!("Test OK"); 62 info!("Test OK");
60 cortex_m::asm::bkpt(); 63 cortex_m::asm::bkpt();
diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs
index 4669cbc62..f6641ae31 100644
--- a/tests/stm32/src/bin/tl_mbox.rs
+++ b/tests/stm32/src/bin/tl_mbox.rs
@@ -6,60 +6,70 @@
6#[path = "../common.rs"] 6#[path = "../common.rs"]
7mod common; 7mod common;
8 8
9use core::mem;
10
9use common::*; 11use common::*;
10use embassy_executor::Spawner; 12use embassy_executor::Spawner;
13use embassy_futures::poll_once;
11use embassy_stm32::bind_interrupts; 14use embassy_stm32::bind_interrupts;
12use embassy_stm32::ipcc::Config; 15use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};
13use embassy_stm32_wpan::rc::RadioCoprocessor; 16use embassy_stm32_wpan::{mm, TlMbox};
14use embassy_stm32_wpan::TlMbox;
15use embassy_time::{Duration, Timer}; 17use embassy_time::{Duration, Timer};
16 18
17bind_interrupts!(struct Irqs{ 19bind_interrupts!(struct Irqs{
18 IPCC_C1_RX => embassy_stm32_wpan::ReceiveInterruptHandler; 20 IPCC_C1_RX => ReceiveInterruptHandler;
19 IPCC_C1_TX => embassy_stm32_wpan::TransmitInterruptHandler; 21 IPCC_C1_TX => TransmitInterruptHandler;
20}); 22});
21 23
24#[embassy_executor::task]
25async fn run_mm_queue(memory_manager: mm::MemoryManager) {
26 memory_manager.run_queue().await;
27}
28
22#[embassy_executor::main] 29#[embassy_executor::main]
23async fn main(_spawner: Spawner) { 30async fn main(spawner: Spawner) {
24 let p = embassy_stm32::init(config()); 31 let p = embassy_stm32::init(config());
25 info!("Hello World!"); 32 info!("Hello World!");
26 33
27 let config = Config::default(); 34 let config = Config::default();
28 let mbox = TlMbox::init(p.IPCC, Irqs, config); 35 let mbox = TlMbox::init(p.IPCC, Irqs, config);
29 36
30 loop { 37 spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap();
31 let wireless_fw_info = mbox.wireless_fw_info(); 38
32 match wireless_fw_info { 39 let ready_event = mbox.sys_subsystem.read().await;
33 None => {} 40 let _ = poll_once(mbox.sys_subsystem.read()); // clear rx not
34 Some(fw_info) => { 41
35 let version_major = fw_info.version_major(); 42 info!("coprocessor ready {}", ready_event.payload());
36 let version_minor = fw_info.version_minor(); 43
37 let subversion = fw_info.subversion(); 44 // test memory manager
45 mem::drop(ready_event);
46
47 let fw_info = mbox.sys_subsystem.wireless_fw_info().unwrap();
48 let version_major = fw_info.version_major();
49 let version_minor = fw_info.version_minor();
50 let subversion = fw_info.subversion();
38 51
39 let sram2a_size = fw_info.sram2a_size(); 52 let sram2a_size = fw_info.sram2a_size();
40 let sram2b_size = fw_info.sram2b_size(); 53 let sram2b_size = fw_info.sram2b_size();
41 54
42 info!( 55 info!(
43 "version {}.{}.{} - SRAM2a {} - SRAM2b {}", 56 "version {}.{}.{} - SRAM2a {} - SRAM2b {}",
44 version_major, version_minor, subversion, sram2a_size, sram2b_size 57 version_major, version_minor, subversion, sram2a_size, sram2b_size
45 ); 58 );
46 59
47 break; 60 Timer::after(Duration::from_millis(50)).await;
48 }
49 }
50 61
51 Timer::after(Duration::from_millis(50)).await; 62 mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await;
52 }
53 63
54 let mut rc = RadioCoprocessor::new(mbox); 64 info!("starting ble...");
65 mbox.ble_subsystem.write(0x0c, &[]).await;
55 66
56 let response = rc.read().await; 67 info!("waiting for ble...");
57 info!("coprocessor ready {}", response); 68 let ble_event = mbox.ble_subsystem.read().await;
58 69
59 rc.write(&[0x01, 0x03, 0x0c, 0x00, 0x00]); 70 info!("ble event: {}", ble_event.payload());
60 let response = rc.read().await;
61 info!("ble reset rsp {}", response);
62 71
72 Timer::after(Duration::from_millis(150)).await;
63 info!("Test OK"); 73 info!("Test OK");
64 cortex_m::asm::bkpt(); 74 cortex_m::asm::bkpt();
65} 75}