aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2023-06-17 12:00:33 -0500
committerxoviat <[email protected]>2023-06-17 12:00:33 -0500
commit6b5d55eb29aa55795cfdf98593feba2f53b85b9c (patch)
tree8bf1c67f2464dcf17245fd97360915cbe6a79005
parentb0a2f0c4fec5358063e6323bf9f9ee001341c473 (diff)
stm32/wpan: convert to new ipcc
-rw-r--r--embassy-stm32-wpan/src/ble.rs54
-rw-r--r--embassy-stm32-wpan/src/evt.rs4
-rw-r--r--embassy-stm32-wpan/src/lib.rs60
-rw-r--r--embassy-stm32-wpan/src/mm.rs66
-rw-r--r--embassy-stm32-wpan/src/rc.rs43
-rw-r--r--embassy-stm32-wpan/src/sys.rs87
-rw-r--r--embassy-stm32-wpan/src/unsafe_linked_list.rs8
-rw-r--r--embassy-stm32/src/ipcc.rs274
-rw-r--r--tests/stm32/src/bin/tl_mbox.rs43
9 files changed, 317 insertions, 322 deletions
diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs
index 3a6cc6f07..b7d152631 100644
--- a/embassy-stm32-wpan/src/ble.rs
+++ b/embassy-stm32-wpan/src/ble.rs
@@ -1,10 +1,11 @@
1use core::mem::MaybeUninit; 1use core::mem::MaybeUninit;
2use core::ptr;
2 3
3use embassy_stm32::ipcc::Ipcc; 4use embassy_stm32::ipcc::Ipcc;
4 5
5use crate::cmd::CmdPacket; 6use crate::cmd::{Cmd, CmdPacket, CmdSerial};
6use crate::consts::TlPacketType; 7use crate::consts::TlPacketType;
7use crate::evt::EvtBox; 8use crate::evt::{EvtBox, EvtPacket};
8use crate::tables::BleTable; 9use crate::tables::BleTable;
9use crate::unsafe_linked_list::LinkedListNode; 10use crate::unsafe_linked_list::LinkedListNode;
10use crate::{ 11use crate::{
@@ -25,45 +26,26 @@ impl Ble {
25 phci_acl_data_buffer: HCI_ACL_DATA_BUFFER.as_mut_ptr().cast(), 26 phci_acl_data_buffer: HCI_ACL_DATA_BUFFER.as_mut_ptr().cast(),
26 }); 27 });
27 } 28 }
28
29 Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, true);
30 } 29 }
31 30 /// `HW_IPCC_BLE_EvtNot`
32 pub(super) fn evt_handler() { 31 pub async fn read() -> EvtBox {
33 unsafe { 32 Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe {
34 while let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) { 33 if let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) {
35 let event = EvtBox::new(node_ptr.cast()); 34 Some(EvtBox::new(node_ptr.cast()))
36 35 } else {
37 EVT_CHANNEL.try_send(event).unwrap(); 36 None
38 } 37 }
39 } 38 })
40 39 .await
41 Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL);
42 }
43
44 pub(super) fn acl_data_handler() {
45 Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, false);
46
47 // TODO: ACL data ack to the user
48 } 40 }
49 41
50 pub fn send_cmd(opcode: u16, payload: &[u8]) { 42 /// `TL_BLE_SendCmd`
51 debug!("writing ble cmd"); 43 pub async fn write(opcode: u16, payload: &[u8]) {
52 44 Ipcc::send(channels::cpu1::IPCC_BLE_CMD_CHANNEL, || unsafe {
53 unsafe {
54 CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload); 45 CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload);
55 } 46 })
56 47 .await;
57 Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_BLE_CMD_CHANNEL);
58 } 48 }
59 49
60 #[allow(dead_code)] // Not used currently but reserved 50 // TODO: acl commands
61 pub(super) fn ble_send_acl_data() {
62 let cmd_packet = unsafe { &mut *(*TL_REF_TABLE.assume_init().ble_table).phci_acl_data_buffer };
63
64 cmd_packet.acl_data_serial.ty = TlPacketType::AclData as u8;
65
66 Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL);
67 Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, true);
68 }
69} 51}
diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/evt.rs
index b53fe506e..82f73a6f8 100644
--- a/embassy-stm32-wpan/src/evt.rs
+++ b/embassy-stm32-wpan/src/evt.rs
@@ -171,6 +171,8 @@ impl EvtBox {
171 171
172impl Drop for EvtBox { 172impl Drop for EvtBox {
173 fn drop(&mut self) { 173 fn drop(&mut self) {
174 mm::MemoryManager::evt_drop(self.ptr); 174 trace!("evt box drop packet");
175
176 unsafe { mm::MemoryManager::drop_event_packet(self.ptr) };
175 } 177 }
176} 178}
diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs
index 2852d6270..78516263c 100644
--- a/embassy-stm32-wpan/src/lib.rs
+++ b/embassy-stm32-wpan/src/lib.rs
@@ -7,11 +7,10 @@ use core::mem::MaybeUninit;
7use core::sync::atomic::{compiler_fence, Ordering}; 7use core::sync::atomic::{compiler_fence, Ordering};
8 8
9use cmd::CmdPacket; 9use cmd::CmdPacket;
10use embassy_futures::block_on;
11use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; 10use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
12use embassy_stm32::interrupt; 11use embassy_stm32::interrupt;
13use embassy_stm32::interrupt::typelevel::Interrupt; 12use embassy_stm32::interrupt::typelevel::Interrupt;
14use embassy_stm32::ipcc::{Config, Ipcc}; 13use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler};
15use embassy_stm32::peripherals::IPCC; 14use embassy_stm32::peripherals::IPCC;
16use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 15use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
17use embassy_sync::channel::Channel; 16use embassy_sync::channel::Channel;
@@ -29,50 +28,11 @@ pub mod cmd;
29pub mod consts; 28pub mod consts;
30pub mod evt; 29pub mod evt;
31pub mod mm; 30pub mod mm;
32pub mod rc;
33pub mod shci; 31pub mod shci;
34pub mod sys; 32pub mod sys;
35pub mod tables; 33pub mod tables;
36pub mod unsafe_linked_list; 34pub mod unsafe_linked_list;
37 35
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"] 36#[link_section = "TL_REF_TABLE"]
77pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::uninit(); 37pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::uninit();
78 38
@@ -289,22 +249,4 @@ impl<'d> TlMbox<'d> {
289 None 249 None
290 } 250 }
291 } 251 }
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 }
309 }
310} 252}
diff --git a/embassy-stm32-wpan/src/mm.rs b/embassy-stm32-wpan/src/mm.rs
index 4ccae06f4..d0551d690 100644
--- a/embassy-stm32-wpan/src/mm.rs
+++ b/embassy-stm32-wpan/src/mm.rs
@@ -1,6 +1,11 @@
1//! Memory manager routines 1//! Memory manager routines
2 2
3use core::future::poll_fn;
4use core::task::Poll;
5
6use cortex_m::interrupt;
3use embassy_stm32::ipcc::Ipcc; 7use embassy_stm32::ipcc::Ipcc;
8use embassy_sync::waitqueue::AtomicWaker;
4 9
5use crate::evt::EvtPacket; 10use crate::evt::EvtPacket;
6use crate::tables::MemManagerTable; 11use crate::tables::MemManagerTable;
@@ -10,7 +15,9 @@ use crate::{
10 TL_MEM_MANAGER_TABLE, 15 TL_MEM_MANAGER_TABLE,
11}; 16};
12 17
13pub(super) struct MemoryManager; 18static MM_WAKER: AtomicWaker = AtomicWaker::new();
19
20pub struct MemoryManager;
14 21
15impl MemoryManager { 22impl MemoryManager {
16 pub fn enable() { 23 pub fn enable() {
@@ -30,37 +37,36 @@ impl MemoryManager {
30 } 37 }
31 } 38 }
32 39
33 pub fn evt_drop(evt: *mut EvtPacket) { 40 /// SAFETY: passing a pointer to something other than an event packet is UB
34 // unsafe { 41 pub unsafe fn drop_event_packet(evt: *mut EvtPacket) {
35 // let list_node = evt.cast(); 42 interrupt::free(|_| unsafe {
36 // 43 LinkedListNode::insert_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), evt as *mut _);
37 // LinkedListNode::insert_tail(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), list_node); 44 });
38 //
39 // let channel_is_busy = Ipcc::c1_is_active_flag(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL);
40 //
41 // // postpone event buffer freeing to IPCC interrupt handler
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 }
50 45
51 /// gives free event buffers back to CPU2 from local buffer queue 46 MM_WAKER.wake();
52 pub fn send_free_buf() {
53 // unsafe {
54 // while let Some(node_ptr) = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) {
55 // LinkedListNode::insert_head(FREE_BUF_QUEUE.as_mut_ptr(), node_ptr);
56 // }
57 // }
58 } 47 }
59 48
60 /// free buffer channel interrupt handler 49 pub async fn run_queue() {
61 pub fn free_buf_handler() { 50 loop {
62 // Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, false); 51 poll_fn(|cx| unsafe {
63 // Self::send_free_buf(); 52 MM_WAKER.register(cx.waker());
64 // Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); 53 if LinkedListNode::is_empty(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) {
54 Poll::Pending
55 } else {
56 Poll::Ready(())
57 }
58 })
59 .await;
60
61 Ipcc::send(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, || {
62 interrupt::free(|_| unsafe {
63 // CS required while moving nodes
64 while let Some(node_ptr) = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) {
65 LinkedListNode::insert_head(FREE_BUF_QUEUE.as_mut_ptr(), node_ptr);
66 }
67 })
68 })
69 .await;
70 }
65 } 71 }
66} 72}
diff --git a/embassy-stm32-wpan/src/rc.rs b/embassy-stm32-wpan/src/rc.rs
deleted file mode 100644
index e67350f79..000000000
--- a/embassy-stm32-wpan/src/rc.rs
+++ /dev/null
@@ -1,43 +0,0 @@
1use crate::ble::Ble;
2use crate::consts::TlPacketType;
3use crate::{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, opcode: u16, 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::send_cmd(opcode, 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 evt.write(&mut self.rx_buf).unwrap();
34 }
35
36 if self.mbox.pop_last_cc_evt().is_some() {
37 continue;
38 }
39
40 return &self.rx_buf;
41 }
42 }
43}
diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sys.rs
index 36b4a144d..f10327b9d 100644
--- a/embassy-stm32-wpan/src/sys.rs
+++ b/embassy-stm32-wpan/src/sys.rs
@@ -1,19 +1,18 @@
1use core::mem::MaybeUninit; 1use core::mem::MaybeUninit;
2use core::sync::atomic::{compiler_fence, Ordering}; 2use core::{mem, ptr};
3 3
4use embassy_stm32::ipcc::Ipcc; 4use crate::cmd::{CmdPacket, CmdSerialStub};
5
6use crate::cmd::{CmdPacket, CmdSerial};
7use crate::consts::TlPacketType; 5use crate::consts::TlPacketType;
8use crate::evt::{CcEvt, EvtBox, EvtSerial}; 6use crate::evt::{CcEvt, EvtBox, EvtPacket, EvtSerial};
9use crate::shci::{ShciBleInitCmdParam, SCHI_OPCODE_BLE_INIT}; 7use crate::shci::{ShciBleInitCmdPacket, ShciBleInitCmdParam, ShciHeader, SCHI_OPCODE_BLE_INIT};
10use crate::tables::SysTable; 8use crate::tables::SysTable;
11use crate::unsafe_linked_list::LinkedListNode; 9use crate::unsafe_linked_list::LinkedListNode;
12use crate::{channels, EVT_CHANNEL, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE}; 10use crate::{channels, mm, Ipcc, EVT_CHANNEL, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE};
13 11
14pub struct Sys; 12pub struct Sys;
15 13
16impl Sys { 14impl Sys {
15 /// TL_Sys_Init
17 pub fn enable() { 16 pub fn enable() {
18 unsafe { 17 unsafe {
19 LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); 18 LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr());
@@ -21,59 +20,47 @@ impl Sys {
21 TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable { 20 TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable {
22 pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(), 21 pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(),
23 sys_queue: SYSTEM_EVT_QUEUE.as_ptr(), 22 sys_queue: SYSTEM_EVT_QUEUE.as_ptr(),
24 }) 23 });
25 } 24 }
26
27 Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, true);
28 } 25 }
29 26
30 pub fn cmd_evt_handler() -> CcEvt { 27 // pub async fn shci_c2_ble_init(&mut self, param: ShciBleInitCmdParam) -> SchiCommandStatus {
31 Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, false); 28 // let command_event = self
32 29 // .write_and_get_response(TlPacketType::SysCmd, ShciOpcode::BleInit as u16, param.payload())
33 // ST's command response data structure is really convoluted. 30 // .await;
34 // 31 //
35 // for command response events on SYS channel, the header is missing 32 // let payload = command_event.payload[0];
36 // and one should: 33 // // info!("payload: {:x}", payload);
37 // 1. interpret the content of CMD_BUFFER as CmdPacket 34 //
38 // 2. Access CmdPacket's cmdserial field and interpret its content as EvtSerial 35 // payload.try_into().unwrap()
39 // 3. Access EvtSerial's evt field (as Evt) and interpret its payload as CcEvt 36 // }
40 // 4. CcEvt type is the actual SHCI response 37
41 // 5. profit 38 pub fn write(opcode: u16, payload: &[u8]) {
42 unsafe { 39 unsafe {
43 let pcmd: *const CmdPacket = (*TL_SYS_TABLE.as_ptr()).pcmd_buffer; 40 CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode, payload);
44 let cmd_serial: *const CmdSerial = &(*pcmd).cmdserial;
45 let evt_serial: *const EvtSerial = cmd_serial.cast();
46 let cc: *const CcEvt = (*evt_serial).evt.payload.as_ptr().cast();
47 *cc
48 }
49 }
50
51 pub fn evt_handler() {
52 unsafe {
53 while let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) {
54 let event = EvtBox::new(node_ptr.cast());
55
56 EVT_CHANNEL.try_send(event).unwrap();
57 }
58 } 41 }
59
60 Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL);
61 } 42 }
62 43
63 pub fn shci_ble_init(param: ShciBleInitCmdParam) { 44 pub async fn shci_c2_ble_init(param: ShciBleInitCmdParam) {
64 debug!("sending SHCI"); 45 debug!("sending SHCI");
65 46
66 Self::send_cmd(SCHI_OPCODE_BLE_INIT, param.payload()); 47 Ipcc::send(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, || {
67 } 48 Self::write(SCHI_OPCODE_BLE_INIT, param.payload());
49 })
50 .await;
68 51
69 pub fn send_cmd(opcode: u16, payload: &[u8]) { 52 Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await;
70 unsafe { 53 }
71 CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode, payload);
72 }
73
74 compiler_fence(Ordering::SeqCst);
75 54
76 Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); 55 /// `HW_IPCC_SYS_EvtNot`
77 Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); 56 pub async fn read() -> EvtBox {
57 Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe {
58 if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) {
59 Some(EvtBox::new(node_ptr.cast()))
60 } else {
61 None
62 }
63 })
64 .await
78 } 65 }
79} 66}
diff --git a/embassy-stm32-wpan/src/unsafe_linked_list.rs b/embassy-stm32-wpan/src/unsafe_linked_list.rs
index a2d2840f0..d8bc29763 100644
--- a/embassy-stm32-wpan/src/unsafe_linked_list.rs
+++ b/embassy-stm32-wpan/src/unsafe_linked_list.rs
@@ -117,9 +117,11 @@ 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 trace!("remove node: {:x}", p_node); 120 // trace!("remove node: {:x}", p_node);
121 let node = ptr::read_volatile(p_node); 121 // apparently linked list nodes are not always aligned.
122 trace!("remove node: prev/next {:x}/{:x}", node.prev, node.next); 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);
123 125
124 if node.next != node.prev { 126 if node.next != node.prev {
125 let mut node_next = ptr::read_volatile(node.next); 127 let mut node_next = ptr::read_volatile(node.next);
diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs
index 28f51baa5..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,129 +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 trace!("ipcc: ch {}: clear rx", channel as u8); 178 compiler_fence(Ordering::SeqCst);
115 unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)) }
116 }
117 179
118 #[allow(dead_code)] 180 if unsafe { regs.cpu(1).sr().read() }.chf(channel as usize) {
119 /// clears IPCC receive channel status for CPU2 181 // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt
120 pub fn c2_clear_flag_channel(channel: IpccChannel) { 182 unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)) }
121 let regs = IPCC::regs();
122 183
123 unsafe { regs.cpu(1).scr().write(|w| w.set_chc(channel as usize, true)) } 184 Poll::Ready(())
124 } 185 } else {
186 Poll::Pending
187 }
188 })
189 .await;
125 190
126 pub fn c1_set_flag_channel(channel: IpccChannel) { 191 trace!("ipcc: ch {}: read data", channel as u8);
127 let regs = IPCC::regs(); 192 compiler_fence(Ordering::SeqCst);
128 193
129 unsafe { regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)) } 194 match f() {
130 } 195 Some(ret) => return ret,
196 None => {}
197 }
131 198
132 #[allow(dead_code)] 199 trace!("ipcc: ch {}: clear rx", channel as u8);
133 pub fn c2_set_flag_channel(channel: IpccChannel) { 200 compiler_fence(Ordering::SeqCst);
134 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}
135 206
136 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
137 } 210 }
138 211
139 pub fn c1_is_active_flag(channel: IpccChannel) -> bool { 212 fn set_cpu2(enabled: bool) {
140 let regs = IPCC::regs(); 213 unsafe { crate::pac::PWR.cr4().modify(|w| w.set_c2boot(enabled)) }
214 }
141 215
142 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
143 } 219 }
220}
144 221
145 pub fn c2_is_active_flag(channel: IpccChannel) -> bool { 222pub(crate) mod sealed {
146 let regs = IPCC::regs(); 223 use embassy_sync::waitqueue::AtomicWaker;
147 224
148 unsafe { regs.cpu(1).sr().read().chf(channel as usize) } 225 use super::*;
149 }
150 226
151 pub fn is_tx_pending(channel: IpccChannel) -> bool { 227 pub struct State {
152 !Self::c1_is_active_flag(channel) && Self::c1_get_tx_channel(channel) 228 rx_wakers: [AtomicWaker; 6],
229 tx_wakers: [AtomicWaker; 6],
153 } 230 }
154 231
155 pub fn is_rx_pending(channel: IpccChannel) -> bool { 232 impl State {
156 Self::c2_is_active_flag(channel) && Self::c1_get_rx_channel(channel) 233 pub const fn new() -> Self {
157 } 234 const WAKER: AtomicWaker = AtomicWaker::new();
158}
159 235
160impl sealed::Instance for crate::peripherals::IPCC { 236 Self {
161 fn regs() -> crate::pac::ipcc::Ipcc { 237 rx_wakers: [WAKER; 6],
162 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 }
163 } 263 }
164 264
165 fn set_cpu2(enabled: bool) { 265 pub trait Instance: crate::rcc::RccPeripheral {
166 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;
167 } 269 }
168} 270}
169 271
170unsafe fn _configure_pwr() { 272unsafe fn _configure_pwr() {
273 // TODO: move this to RCC
274
171 let pwr = crate::pac::PWR; 275 let pwr = crate::pac::PWR;
172 let rcc = crate::pac::RCC; 276 let rcc = crate::pac::RCC;
173 277
diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs
index 5a2309263..bb38204b6 100644
--- a/tests/stm32/src/bin/tl_mbox.rs
+++ b/tests/stm32/src/bin/tl_mbox.rs
@@ -8,27 +8,41 @@ mod common;
8 8
9use common::*; 9use common::*;
10use embassy_executor::Spawner; 10use embassy_executor::Spawner;
11use embassy_futures::poll_once;
11use embassy_stm32::bind_interrupts; 12use embassy_stm32::bind_interrupts;
12use embassy_stm32::ipcc::Config; 13use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};
13use embassy_stm32_wpan::ble::Ble; 14use embassy_stm32_wpan::ble::Ble;
14use embassy_stm32_wpan::rc::RadioCoprocessor;
15use embassy_stm32_wpan::sys::Sys; 15use embassy_stm32_wpan::sys::Sys;
16use embassy_stm32_wpan::TlMbox; 16use embassy_stm32_wpan::{mm, TlMbox};
17use embassy_time::{Duration, Timer}; 17use embassy_time::{Duration, Timer};
18 18
19bind_interrupts!(struct Irqs{ 19bind_interrupts!(struct Irqs{
20 IPCC_C1_RX => embassy_stm32_wpan::ReceiveInterruptHandler; 20 IPCC_C1_RX => ReceiveInterruptHandler;
21 IPCC_C1_TX => embassy_stm32_wpan::TransmitInterruptHandler; 21 IPCC_C1_TX => TransmitInterruptHandler;
22}); 22});
23 23
24#[embassy_executor::task]
25async fn run_mm_queue() {
26 mm::MemoryManager::run_queue().await;
27}
28
24#[embassy_executor::main] 29#[embassy_executor::main]
25async fn main(_spawner: Spawner) { 30async fn main(spawner: Spawner) {
26 let p = embassy_stm32::init(config()); 31 let p = embassy_stm32::init(config());
27 info!("Hello World!"); 32 info!("Hello World!");
28 33
34 spawner.spawn(run_mm_queue()).unwrap();
35
29 let config = Config::default(); 36 let config = Config::default();
30 let mbox = TlMbox::init(p.IPCC, Irqs, config); 37 let mbox = TlMbox::init(p.IPCC, Irqs, config);
31 38
39 let mut rx_buf = [0u8; 500];
40 let ready_event = Sys::read().await;
41 let _ = poll_once(Sys::read()); // clear rx not
42 ready_event.write(&mut rx_buf).unwrap();
43
44 info!("coprocessor ready {}", rx_buf);
45
32 loop { 46 loop {
33 let wireless_fw_info = mbox.wireless_fw_info(); 47 let wireless_fw_info = mbox.wireless_fw_info();
34 match wireless_fw_info { 48 match wireless_fw_info {
@@ -53,19 +67,18 @@ async fn main(_spawner: Spawner) {
53 Timer::after(Duration::from_millis(50)).await; 67 Timer::after(Duration::from_millis(50)).await;
54 } 68 }
55 69
56 let mut rc = RadioCoprocessor::new(mbox); 70 Sys::shci_c2_ble_init(Default::default()).await;
57
58 let response = rc.read().await;
59 info!("coprocessor ready {}", response);
60 71
61 Sys::shci_ble_init(Default::default()); 72 info!("starting ble...");
73 Ble::write(0x0c, &[]).await;
62 74
63 // rc.write(&[0x01, 0x03, 0x0c, 0x00, 0x00]); 75 info!("waiting for ble...");
64 Ble::send_cmd(0x0c, &[]); 76 let ble_event = Ble::read().await;
77 ble_event.write(&mut rx_buf).unwrap();
65 78
66 let response = rc.read().await; 79 info!("ble event: {}", rx_buf);
67 info!("ble reset rsp {}", response);
68 80
81 // Timer::after(Duration::from_secs(3)).await;
69 info!("Test OK"); 82 info!("Test OK");
70 cortex_m::asm::bkpt(); 83 cortex_m::asm::bkpt();
71} 84}