diff options
Diffstat (limited to 'embassy-stm32-wpan/src/wb55/sub/sys.rs')
| -rw-r--r-- | embassy-stm32-wpan/src/wb55/sub/sys.rs | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/embassy-stm32-wpan/src/wb55/sub/sys.rs b/embassy-stm32-wpan/src/wb55/sub/sys.rs new file mode 100644 index 000000000..2e625a677 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/sub/sys.rs | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | use embassy_stm32::ipcc::{IpccRxChannel, IpccTxChannel}; | ||
| 2 | |||
| 3 | use crate::cmd::CmdPacket; | ||
| 4 | use crate::consts::TlPacketType; | ||
| 5 | use crate::evt::EvtBox; | ||
| 6 | #[cfg(feature = "wb55_ble")] | ||
| 7 | use crate::shci::ShciBleInitCmdParam; | ||
| 8 | use crate::shci::{SchiCommandStatus, ShciOpcode}; | ||
| 9 | use crate::sub::mm; | ||
| 10 | use crate::tables::{SysTable, WirelessFwInfoTable}; | ||
| 11 | use crate::unsafe_linked_list::LinkedListNode; | ||
| 12 | use crate::wb55::{SYS_CMD_BUF, SYSTEM_EVT_QUEUE, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE}; | ||
| 13 | |||
| 14 | /// A guard that, once constructed, allows for sys commands to be sent to CPU2. | ||
| 15 | pub struct Sys<'a> { | ||
| 16 | ipcc_system_cmd_rsp_channel: IpccTxChannel<'a>, | ||
| 17 | ipcc_system_event_channel: IpccRxChannel<'a>, | ||
| 18 | } | ||
| 19 | |||
| 20 | impl<'a> Sys<'a> { | ||
| 21 | /// TL_Sys_Init | ||
| 22 | pub(crate) fn new( | ||
| 23 | ipcc_system_cmd_rsp_channel: IpccTxChannel<'a>, | ||
| 24 | ipcc_system_event_channel: IpccRxChannel<'a>, | ||
| 25 | ) -> Self { | ||
| 26 | unsafe { | ||
| 27 | LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); | ||
| 28 | |||
| 29 | TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable { | ||
| 30 | pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(), | ||
| 31 | sys_queue: SYSTEM_EVT_QUEUE.as_ptr(), | ||
| 32 | }); | ||
| 33 | } | ||
| 34 | |||
| 35 | Self { | ||
| 36 | ipcc_system_cmd_rsp_channel, | ||
| 37 | ipcc_system_event_channel, | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | /// Returns CPU2 wireless firmware information (if present). | ||
| 42 | pub fn wireless_fw_info(&self) -> Option<WirelessFwInfoTable> { | ||
| 43 | let info = unsafe { TL_DEVICE_INFO_TABLE.as_mut_ptr().read_volatile().wireless_fw_info_table }; | ||
| 44 | |||
| 45 | // Zero version indicates that CPU2 wasn't active and didn't fill the information table | ||
| 46 | if info.version != 0 { Some(info) } else { None } | ||
| 47 | } | ||
| 48 | |||
| 49 | pub async fn write(&mut self, opcode: ShciOpcode, payload: &[u8]) { | ||
| 50 | self.ipcc_system_cmd_rsp_channel | ||
| 51 | .send(|| unsafe { | ||
| 52 | CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode as u16, payload); | ||
| 53 | }) | ||
| 54 | .await; | ||
| 55 | } | ||
| 56 | |||
| 57 | /// `HW_IPCC_SYS_CmdEvtNot` | ||
| 58 | pub async fn write_and_get_response( | ||
| 59 | &mut self, | ||
| 60 | opcode: ShciOpcode, | ||
| 61 | payload: &[u8], | ||
| 62 | ) -> Result<SchiCommandStatus, ()> { | ||
| 63 | self.write(opcode, payload).await; | ||
| 64 | self.ipcc_system_cmd_rsp_channel.flush().await; | ||
| 65 | |||
| 66 | unsafe { SchiCommandStatus::from_packet(SYS_CMD_BUF.as_ptr()) } | ||
| 67 | } | ||
| 68 | |||
| 69 | #[cfg(feature = "wb55_mac")] | ||
| 70 | pub async fn shci_c2_mac_802_15_4_init(&mut self) -> Result<SchiCommandStatus, ()> { | ||
| 71 | self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await | ||
| 72 | } | ||
| 73 | |||
| 74 | /// Send a request to CPU2 to initialise the BLE stack. | ||
| 75 | /// | ||
| 76 | /// This must be called before any BLE commands are sent via the BLE channel (according to | ||
| 77 | /// AN5289, Figures 65 and 66). It should only be called after CPU2 sends a system event, via | ||
| 78 | /// `HW_IPCC_SYS_EvtNot`, aka `IoBusCallBackUserEvt` (as detailed in Figure 65), aka | ||
| 79 | /// [crate::sub::ble::hci::host::uart::UartHci::read]. | ||
| 80 | #[cfg(feature = "wb55_ble")] | ||
| 81 | pub async fn shci_c2_ble_init(&mut self, param: ShciBleInitCmdParam) -> Result<SchiCommandStatus, ()> { | ||
| 82 | self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await | ||
| 83 | } | ||
| 84 | |||
| 85 | /// `HW_IPCC_SYS_EvtNot` | ||
| 86 | /// | ||
| 87 | /// This method takes the place of the `HW_IPCC_SYS_EvtNot`/`SysUserEvtRx`/`APPE_SysUserEvtRx`, | ||
| 88 | /// as the embassy implementation avoids the need to call C public bindings, and instead | ||
| 89 | /// handles the event channels directly. | ||
| 90 | pub async fn read(&mut self) -> EvtBox<mm::MemoryManager<'_>> { | ||
| 91 | self.ipcc_system_event_channel | ||
| 92 | .receive(|| unsafe { | ||
| 93 | if let Some(node_ptr) = | ||
| 94 | critical_section::with(|cs| LinkedListNode::remove_head(cs, SYSTEM_EVT_QUEUE.as_mut_ptr())) | ||
| 95 | { | ||
| 96 | Some(EvtBox::new(node_ptr.cast())) | ||
| 97 | } else { | ||
| 98 | None | ||
| 99 | } | ||
| 100 | }) | ||
| 101 | .await | ||
| 102 | } | ||
| 103 | } | ||
