diff options
Diffstat (limited to 'embassy-stm32-wpan/src/wb55')
27 files changed, 4878 insertions, 0 deletions
diff --git a/embassy-stm32-wpan/src/wb55/channels.rs b/embassy-stm32-wpan/src/wb55/channels.rs new file mode 100644 index 000000000..58f857136 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/channels.rs | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | //! CPU1 CPU2 | ||
| 2 | //! | (SYSTEM) | | ||
| 3 | //! |----HW_IPCC_SYSTEM_CMD_RSP_CHANNEL-------------->| | ||
| 4 | //! | | | ||
| 5 | //! |<---HW_IPCC_SYSTEM_EVENT_CHANNEL-----------------| | ||
| 6 | //! | | | ||
| 7 | //! | (ZIGBEE) | | ||
| 8 | //! |----HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL------------>| | ||
| 9 | //! | | | ||
| 10 | //! |----HW_IPCC_ZIGBEE_CMD_CLI_CHANNEL-------------->| | ||
| 11 | //! | | | ||
| 12 | //! |<---HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL-------| | ||
| 13 | //! | | | ||
| 14 | //! |<---HW_IPCC_ZIGBEE_CLI_NOTIF_ACK_CHANNEL---------| | ||
| 15 | //! | | | ||
| 16 | //! | (THREAD) | | ||
| 17 | //! |----HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL----------->| | ||
| 18 | //! | | | ||
| 19 | //! |----HW_IPCC_THREAD_CLI_CMD_CHANNEL-------------->| | ||
| 20 | //! | | | ||
| 21 | //! |<---HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL------| | ||
| 22 | //! | | | ||
| 23 | //! |<---HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL--| | ||
| 24 | //! | | | ||
| 25 | //! | (BLE) | | ||
| 26 | //! |----HW_IPCC_BLE_CMD_CHANNEL--------------------->| | ||
| 27 | //! | | | ||
| 28 | //! |----HW_IPCC_HCI_ACL_DATA_CHANNEL---------------->| | ||
| 29 | //! | | | ||
| 30 | //! |<---HW_IPCC_BLE_EVENT_CHANNEL--------------------| | ||
| 31 | //! | | | ||
| 32 | //! | (BLE LLD) | | ||
| 33 | //! |----HW_IPCC_BLE_LLD_CMD_CHANNEL----------------->| | ||
| 34 | //! | | | ||
| 35 | //! |<---HW_IPCC_BLE_LLD_RSP_CHANNEL------------------| | ||
| 36 | //! | | | ||
| 37 | //! |<---HW_IPCC_BLE_LLD_M0_CMD_CHANNEL---------------| | ||
| 38 | //! | | | ||
| 39 | //! | (MAC) | | ||
| 40 | //! |----HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL-------->| | ||
| 41 | //! | | | ||
| 42 | //! |<---HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL| | ||
| 43 | //! | | | ||
| 44 | //! | (BUFFER) | | ||
| 45 | //! |----HW_IPCC_MM_RELEASE_BUFFER_CHANNE------------>| | ||
| 46 | //! | | | ||
| 47 | //! | (TRACE) | | ||
| 48 | //! |<----HW_IPCC_TRACES_CHANNEL----------------------| | ||
| 49 | //! | | | ||
| 50 | //! | ||
| 51 | //! | ||
| 52 | |||
| 53 | #[repr(u8)] | ||
| 54 | pub enum IpccChannel { | ||
| 55 | Channel1 = 1, | ||
| 56 | Channel2 = 2, | ||
| 57 | Channel3 = 3, | ||
| 58 | Channel4 = 4, | ||
| 59 | Channel5 = 5, | ||
| 60 | Channel6 = 6, | ||
| 61 | } | ||
| 62 | |||
| 63 | pub mod cpu1 { | ||
| 64 | use super::IpccChannel; | ||
| 65 | |||
| 66 | pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1; | ||
| 67 | pub const IPCC_SYSTEM_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel2; | ||
| 68 | pub const IPCC_THREAD_OT_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3; | ||
| 69 | #[allow(dead_code)] // Not used currently but reserved | ||
| 70 | pub const IPCC_ZIGBEE_CMD_APPLI_CHANNEL: IpccChannel = IpccChannel::Channel3; | ||
| 71 | #[allow(dead_code)] // Not used currently but reserved | ||
| 72 | pub const IPCC_MAC_802_15_4_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel3; | ||
| 73 | #[allow(dead_code)] // Not used currently but reserved | ||
| 74 | pub const IPCC_MM_RELEASE_BUFFER_CHANNEL: IpccChannel = IpccChannel::Channel4; | ||
| 75 | pub const IPCC_THREAD_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; | ||
| 76 | #[allow(dead_code)] // Not used currently but reserved | ||
| 77 | pub const IPCC_LLDTESTS_CLI_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; | ||
| 78 | #[allow(dead_code)] // Not used currently but reserved | ||
| 79 | pub const IPCC_BLE_LLD_CMD_CHANNEL: IpccChannel = IpccChannel::Channel5; | ||
| 80 | pub const IPCC_HCI_ACL_DATA_CHANNEL: IpccChannel = IpccChannel::Channel6; | ||
| 81 | } | ||
| 82 | |||
| 83 | pub mod cpu2 { | ||
| 84 | use super::IpccChannel; | ||
| 85 | |||
| 86 | pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1; | ||
| 87 | pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2; | ||
| 88 | pub const IPCC_THREAD_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; | ||
| 89 | #[allow(dead_code)] // Not used currently but reserved | ||
| 90 | pub const IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; | ||
| 91 | #[allow(dead_code)] // Not used currently but reserved | ||
| 92 | pub const IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel3; | ||
| 93 | #[allow(dead_code)] // Not used currently but reserved | ||
| 94 | pub const IPCC_LDDTESTS_M0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3; | ||
| 95 | #[allow(dead_code)] // Not used currently but reserved | ||
| 96 | pub const IPCC_BLE_LLDÇM0_CMD_CHANNEL: IpccChannel = IpccChannel::Channel3; | ||
| 97 | pub const IPCC_TRACES_CHANNEL: IpccChannel = IpccChannel::Channel4; | ||
| 98 | pub const IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL: IpccChannel = IpccChannel::Channel5; | ||
| 99 | #[allow(dead_code)] // Not used currently but reserved | ||
| 100 | pub const IPCC_LLDTESTS_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5; | ||
| 101 | #[allow(dead_code)] // Not used currently but reserved | ||
| 102 | pub const IPCC_BLE_LLD_CLI_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5; | ||
| 103 | #[allow(dead_code)] // Not used currently but reserved | ||
| 104 | pub const IPCC_BLE_LLD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel5; | ||
| 105 | #[allow(dead_code)] // Not used currently but reserved | ||
| 106 | pub const IPCC_ZIGBEE_M0_REQUEST_CHANNEL: IpccChannel = IpccChannel::Channel5; | ||
| 107 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/cmd.rs b/embassy-stm32-wpan/src/wb55/cmd.rs new file mode 100644 index 000000000..34f02d6e7 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/cmd.rs | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | use core::ptr; | ||
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 3 | |||
| 4 | use crate::consts::TlPacketType; | ||
| 5 | use crate::wb55::PacketHeader; | ||
| 6 | |||
| 7 | #[derive(Copy, Clone)] | ||
| 8 | #[repr(C, packed)] | ||
| 9 | pub struct Cmd { | ||
| 10 | pub cmd_code: u16, | ||
| 11 | pub payload_len: u8, | ||
| 12 | pub payload: [u8; 255], | ||
| 13 | } | ||
| 14 | |||
| 15 | impl Default for Cmd { | ||
| 16 | fn default() -> Self { | ||
| 17 | Self { | ||
| 18 | cmd_code: 0, | ||
| 19 | payload_len: 0, | ||
| 20 | payload: [0u8; 255], | ||
| 21 | } | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | #[derive(Copy, Clone, Default)] | ||
| 26 | #[repr(C, packed)] | ||
| 27 | pub struct CmdSerial { | ||
| 28 | pub ty: u8, | ||
| 29 | pub cmd: Cmd, | ||
| 30 | } | ||
| 31 | |||
| 32 | #[derive(Copy, Clone, Default)] | ||
| 33 | #[repr(C, packed)] | ||
| 34 | pub struct CmdSerialStub { | ||
| 35 | pub ty: u8, | ||
| 36 | pub cmd_code: u16, | ||
| 37 | pub payload_len: u8, | ||
| 38 | } | ||
| 39 | |||
| 40 | #[derive(Copy, Clone, Default)] | ||
| 41 | #[repr(C, packed)] | ||
| 42 | pub struct CmdPacket { | ||
| 43 | pub header: PacketHeader, | ||
| 44 | pub cmdserial: CmdSerial, | ||
| 45 | } | ||
| 46 | |||
| 47 | impl CmdPacket { | ||
| 48 | pub unsafe fn write_into(cmd_buf: *mut CmdPacket, packet_type: TlPacketType, cmd_code: u16, payload: &[u8]) { | ||
| 49 | let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::<PacketHeader>()); | ||
| 50 | let p_payload = p_cmd_serial.add(size_of::<CmdSerialStub>()); | ||
| 51 | |||
| 52 | ptr::write_unaligned( | ||
| 53 | p_cmd_serial as *mut _, | ||
| 54 | CmdSerialStub { | ||
| 55 | ty: packet_type as u8, | ||
| 56 | cmd_code, | ||
| 57 | payload_len: payload.len() as u8, | ||
| 58 | }, | ||
| 59 | ); | ||
| 60 | |||
| 61 | ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len()); | ||
| 62 | |||
| 63 | compiler_fence(Ordering::Release); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | #[derive(Copy, Clone)] | ||
| 68 | #[repr(C, packed)] | ||
| 69 | pub struct AclDataSerial { | ||
| 70 | pub ty: u8, | ||
| 71 | pub handle: u16, | ||
| 72 | pub length: u16, | ||
| 73 | pub acl_data: [u8; 1], | ||
| 74 | } | ||
| 75 | |||
| 76 | #[derive(Copy, Clone)] | ||
| 77 | #[repr(C, packed)] | ||
| 78 | pub struct AclDataSerialStub { | ||
| 79 | pub ty: u8, | ||
| 80 | pub handle: u16, | ||
| 81 | pub length: u16, | ||
| 82 | } | ||
| 83 | |||
| 84 | #[derive(Copy, Clone)] | ||
| 85 | #[repr(C, packed)] | ||
| 86 | pub struct AclDataPacket { | ||
| 87 | pub header: PacketHeader, | ||
| 88 | pub acl_data_serial: AclDataSerial, | ||
| 89 | } | ||
| 90 | |||
| 91 | impl AclDataPacket { | ||
| 92 | pub unsafe fn write_into(cmd_buf: *mut AclDataPacket, packet_type: TlPacketType, handle: u16, payload: &[u8]) { | ||
| 93 | let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::<PacketHeader>()); | ||
| 94 | let p_payload = p_cmd_serial.add(size_of::<AclDataSerialStub>()); | ||
| 95 | |||
| 96 | ptr::write_unaligned( | ||
| 97 | p_cmd_serial as *mut _, | ||
| 98 | AclDataSerialStub { | ||
| 99 | ty: packet_type as u8, | ||
| 100 | handle: handle, | ||
| 101 | length: payload.len() as u16, | ||
| 102 | }, | ||
| 103 | ); | ||
| 104 | |||
| 105 | ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len()); | ||
| 106 | |||
| 107 | compiler_fence(Ordering::Release); | ||
| 108 | } | ||
| 109 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/consts.rs b/embassy-stm32-wpan/src/wb55/consts.rs new file mode 100644 index 000000000..659e74e69 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/consts.rs | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | use crate::evt::CsEvt; | ||
| 2 | use crate::wb55::PacketHeader; | ||
| 3 | |||
| 4 | #[derive(Debug)] | ||
| 5 | #[repr(C)] | ||
| 6 | pub enum TlPacketType { | ||
| 7 | MacCmd = 0x00, | ||
| 8 | |||
| 9 | BleCmd = 0x01, | ||
| 10 | AclData = 0x02, | ||
| 11 | BleEvt = 0x04, | ||
| 12 | |||
| 13 | OtCmd = 0x08, | ||
| 14 | OtRsp = 0x09, | ||
| 15 | CliCmd = 0x0A, | ||
| 16 | OtNot = 0x0C, | ||
| 17 | OtAck = 0x0D, | ||
| 18 | CliNot = 0x0E, | ||
| 19 | CliAck = 0x0F, | ||
| 20 | |||
| 21 | SysCmd = 0x10, | ||
| 22 | SysRsp = 0x11, | ||
| 23 | SysEvt = 0x12, | ||
| 24 | |||
| 25 | LocCmd = 0x20, | ||
| 26 | LocRsp = 0x21, | ||
| 27 | |||
| 28 | TracesApp = 0x40, | ||
| 29 | TracesWl = 0x41, | ||
| 30 | } | ||
| 31 | |||
| 32 | impl TryFrom<u8> for TlPacketType { | ||
| 33 | type Error = (); | ||
| 34 | |||
| 35 | fn try_from(value: u8) -> Result<Self, Self::Error> { | ||
| 36 | match value { | ||
| 37 | 0x01 => Ok(TlPacketType::BleCmd), | ||
| 38 | 0x02 => Ok(TlPacketType::AclData), | ||
| 39 | 0x04 => Ok(TlPacketType::BleEvt), | ||
| 40 | 0x08 => Ok(TlPacketType::OtCmd), | ||
| 41 | 0x09 => Ok(TlPacketType::OtRsp), | ||
| 42 | 0x0A => Ok(TlPacketType::CliCmd), | ||
| 43 | 0x0C => Ok(TlPacketType::OtNot), | ||
| 44 | 0x0D => Ok(TlPacketType::OtAck), | ||
| 45 | 0x0E => Ok(TlPacketType::CliNot), | ||
| 46 | 0x0F => Ok(TlPacketType::CliAck), | ||
| 47 | 0x10 => Ok(TlPacketType::SysCmd), | ||
| 48 | 0x11 => Ok(TlPacketType::SysRsp), | ||
| 49 | 0x12 => Ok(TlPacketType::SysEvt), | ||
| 50 | 0x20 => Ok(TlPacketType::LocCmd), | ||
| 51 | 0x21 => Ok(TlPacketType::LocRsp), | ||
| 52 | 0x40 => Ok(TlPacketType::TracesApp), | ||
| 53 | 0x41 => Ok(TlPacketType::TracesWl), | ||
| 54 | |||
| 55 | _ => Err(()), | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | pub const TL_PACKET_HEADER_SIZE: usize = core::mem::size_of::<PacketHeader>(); | ||
| 61 | pub const TL_EVT_HEADER_SIZE: usize = 3; | ||
| 62 | pub const TL_CS_EVT_SIZE: usize = core::mem::size_of::<CsEvt>(); | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Queue length of BLE Event | ||
| 66 | * This parameter defines the number of asynchronous events that can be stored in the HCI layer before | ||
| 67 | * being reported to the application. When a command is sent to the BLE core coprocessor, the HCI layer | ||
| 68 | * is waiting for the event with the Num_HCI_Command_Packets set to 1. The receive queue shall be large | ||
| 69 | * enough to store all asynchronous events received in between. | ||
| 70 | * When CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE is set to 27, this allow to store three 255 bytes long asynchronous events | ||
| 71 | * between the HCI command and its event. | ||
| 72 | * This parameter depends on the value given to CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE. When the queue size is too small, | ||
| 73 | * the system may hang if the queue is full with asynchronous events and the HCI layer is still waiting | ||
| 74 | * for a CC/CS event, In that case, the notification TL_BLE_HCI_ToNot() is called to indicate | ||
| 75 | * to the application a HCI command did not receive its command event within 30s (Default HCI Timeout). | ||
| 76 | */ | ||
| 77 | pub const CFG_TL_BLE_EVT_QUEUE_LENGTH: usize = 5; | ||
| 78 | pub const CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE: usize = 255; | ||
| 79 | pub const TL_BLE_EVENT_FRAME_SIZE: usize = TL_EVT_HEADER_SIZE + CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE; | ||
| 80 | |||
| 81 | pub const POOL_SIZE: usize = CFG_TL_BLE_EVT_QUEUE_LENGTH * 4 * divc(TL_PACKET_HEADER_SIZE + TL_BLE_EVENT_FRAME_SIZE, 4); | ||
| 82 | pub const C_SIZE_CMD_STRING: usize = 256; | ||
| 83 | |||
| 84 | pub const fn divc(x: usize, y: usize) -> usize { | ||
| 85 | (x + y - 1) / y | ||
| 86 | } | ||
| 87 | |||
| 88 | pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE; | ||
| 89 | #[allow(dead_code)] | ||
| 90 | pub const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE; | ||
| 91 | |||
| 92 | pub const TL_BLEEVT_CC_OPCODE: u8 = 0x0E; | ||
| 93 | pub const TL_BLEEVT_CS_OPCODE: u8 = 0x0F; | ||
| 94 | pub const TL_BLEEVT_VS_OPCODE: u8 = 0xFF; | ||
diff --git a/embassy-stm32-wpan/src/wb55/evt.rs b/embassy-stm32-wpan/src/wb55/evt.rs new file mode 100644 index 000000000..f32821269 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/evt.rs | |||
| @@ -0,0 +1,152 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use core::{ptr, slice}; | ||
| 3 | |||
| 4 | use super::PacketHeader; | ||
| 5 | use crate::consts::TL_EVT_HEADER_SIZE; | ||
| 6 | |||
| 7 | /** | ||
| 8 | * The payload of `Evt` for a command status event | ||
| 9 | */ | ||
| 10 | #[derive(Copy, Clone)] | ||
| 11 | #[repr(C, packed)] | ||
| 12 | pub struct CsEvt { | ||
| 13 | pub status: u8, | ||
| 14 | pub num_cmd: u8, | ||
| 15 | pub cmd_code: u16, | ||
| 16 | } | ||
| 17 | |||
| 18 | /** | ||
| 19 | * The payload of `Evt` for a command complete event | ||
| 20 | */ | ||
| 21 | #[derive(Copy, Clone, Default)] | ||
| 22 | #[repr(C, packed)] | ||
| 23 | pub struct CcEvt { | ||
| 24 | pub num_cmd: u8, | ||
| 25 | pub cmd_code: u16, | ||
| 26 | pub payload: [u8; 1], | ||
| 27 | } | ||
| 28 | |||
| 29 | impl CcEvt { | ||
| 30 | pub fn write(&self, buf: &mut [u8]) { | ||
| 31 | unsafe { | ||
| 32 | let len = core::mem::size_of::<CcEvt>(); | ||
| 33 | assert!(buf.len() >= len); | ||
| 34 | |||
| 35 | let self_ptr: *const CcEvt = self; | ||
| 36 | let self_buf_ptr: *const u8 = self_ptr.cast(); | ||
| 37 | |||
| 38 | core::ptr::copy(self_buf_ptr, buf.as_mut_ptr(), len); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | #[derive(Copy, Clone, Default)] | ||
| 44 | #[repr(C, packed)] | ||
| 45 | pub struct AsynchEvt { | ||
| 46 | sub_evt_code: u16, | ||
| 47 | payload: [u8; 1], | ||
| 48 | } | ||
| 49 | |||
| 50 | #[derive(Copy, Clone)] | ||
| 51 | #[repr(C, packed)] | ||
| 52 | pub struct Evt { | ||
| 53 | pub evt_code: u8, | ||
| 54 | pub payload_len: u8, | ||
| 55 | pub payload: [u8; 255], | ||
| 56 | } | ||
| 57 | |||
| 58 | #[derive(Copy, Clone)] | ||
| 59 | #[repr(C, packed)] | ||
| 60 | pub struct EvtSerial { | ||
| 61 | pub kind: u8, | ||
| 62 | pub evt: Evt, | ||
| 63 | } | ||
| 64 | |||
| 65 | #[derive(Copy, Clone, Default)] | ||
| 66 | #[repr(C, packed)] | ||
| 67 | pub struct EvtStub { | ||
| 68 | pub kind: u8, | ||
| 69 | pub evt_code: u8, | ||
| 70 | pub payload_len: u8, | ||
| 71 | } | ||
| 72 | |||
| 73 | /// This format shall be used for all events (asynchronous and command response) reported | ||
| 74 | /// by the CPU2 except for the command response of a system command where the header is not there | ||
| 75 | /// and the format to be used shall be `EvtSerial`. | ||
| 76 | /// | ||
| 77 | /// ### Note: | ||
| 78 | /// Be careful that the asynchronous events reported by the CPU2 on the system channel do | ||
| 79 | /// include the header and shall use `EvtPacket` format. Only the command response format on the | ||
| 80 | /// system channel is different. | ||
| 81 | #[derive(Copy, Clone)] | ||
| 82 | #[repr(C, packed)] | ||
| 83 | pub struct EvtPacket { | ||
| 84 | pub header: PacketHeader, | ||
| 85 | pub evt_serial: EvtSerial, | ||
| 86 | } | ||
| 87 | |||
| 88 | impl EvtPacket { | ||
| 89 | pub fn kind(&self) -> u8 { | ||
| 90 | self.evt_serial.kind | ||
| 91 | } | ||
| 92 | |||
| 93 | pub fn evt(&self) -> &Evt { | ||
| 94 | &self.evt_serial.evt | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | pub trait MemoryManager { | ||
| 99 | unsafe fn drop_event_packet(evt: *mut EvtPacket); | ||
| 100 | } | ||
| 101 | |||
| 102 | /// smart pointer to the [`EvtPacket`] that will dispose of [`EvtPacket`] buffer automatically | ||
| 103 | /// on [`Drop`] | ||
| 104 | #[derive(Debug)] | ||
| 105 | pub struct EvtBox<T: MemoryManager> { | ||
| 106 | ptr: *mut EvtPacket, | ||
| 107 | mm: PhantomData<T>, | ||
| 108 | } | ||
| 109 | |||
| 110 | unsafe impl<T: MemoryManager> Send for EvtBox<T> {} | ||
| 111 | impl<T: MemoryManager> EvtBox<T> { | ||
| 112 | pub(super) fn new(ptr: *mut EvtPacket) -> Self { | ||
| 113 | Self { ptr, mm: PhantomData } | ||
| 114 | } | ||
| 115 | |||
| 116 | /// Returns information about the event | ||
| 117 | pub fn stub(&self) -> EvtStub { | ||
| 118 | unsafe { | ||
| 119 | let p_evt_stub = &(*self.ptr).evt_serial as *const _ as *const EvtStub; | ||
| 120 | |||
| 121 | ptr::read_volatile(p_evt_stub) | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | pub fn payload<'a>(&'a self) -> &'a [u8] { | ||
| 126 | unsafe { | ||
| 127 | let p_payload_len = &(*self.ptr).evt_serial.evt.payload_len as *const u8; | ||
| 128 | let p_payload = &(*self.ptr).evt_serial.evt.payload as *const u8; | ||
| 129 | |||
| 130 | let payload_len = ptr::read_volatile(p_payload_len); | ||
| 131 | |||
| 132 | slice::from_raw_parts(p_payload, payload_len as usize) | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | pub fn serial<'a>(&'a self) -> &'a [u8] { | ||
| 137 | unsafe { | ||
| 138 | let evt_serial: *const EvtSerial = &(*self.ptr).evt_serial; | ||
| 139 | let evt_serial_buf: *const u8 = evt_serial.cast(); | ||
| 140 | |||
| 141 | let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE; | ||
| 142 | |||
| 143 | slice::from_raw_parts(evt_serial_buf, len) | ||
| 144 | } | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | impl<T: MemoryManager> Drop for EvtBox<T> { | ||
| 149 | fn drop(&mut self) { | ||
| 150 | unsafe { T::drop_event_packet(self.ptr) }; | ||
| 151 | } | ||
| 152 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/fmt.rs b/embassy-stm32-wpan/src/wb55/fmt.rs new file mode 100644 index 000000000..8ca61bc39 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/fmt.rs | |||
| @@ -0,0 +1,270 @@ | |||
| 1 | #![macro_use] | ||
| 2 | #![allow(unused)] | ||
| 3 | |||
| 4 | use core::fmt::{Debug, Display, LowerHex}; | ||
| 5 | |||
| 6 | #[cfg(all(feature = "defmt", feature = "log"))] | ||
| 7 | compile_error!("You may not enable both `defmt` and `log` features."); | ||
| 8 | |||
| 9 | #[collapse_debuginfo(yes)] | ||
| 10 | macro_rules! assert { | ||
| 11 | ($($x:tt)*) => { | ||
| 12 | { | ||
| 13 | #[cfg(not(feature = "defmt"))] | ||
| 14 | ::core::assert!($($x)*); | ||
| 15 | #[cfg(feature = "defmt")] | ||
| 16 | ::defmt::assert!($($x)*); | ||
| 17 | } | ||
| 18 | }; | ||
| 19 | } | ||
| 20 | |||
| 21 | #[collapse_debuginfo(yes)] | ||
| 22 | macro_rules! assert_eq { | ||
| 23 | ($($x:tt)*) => { | ||
| 24 | { | ||
| 25 | #[cfg(not(feature = "defmt"))] | ||
| 26 | ::core::assert_eq!($($x)*); | ||
| 27 | #[cfg(feature = "defmt")] | ||
| 28 | ::defmt::assert_eq!($($x)*); | ||
| 29 | } | ||
| 30 | }; | ||
| 31 | } | ||
| 32 | |||
| 33 | #[collapse_debuginfo(yes)] | ||
| 34 | macro_rules! assert_ne { | ||
| 35 | ($($x:tt)*) => { | ||
| 36 | { | ||
| 37 | #[cfg(not(feature = "defmt"))] | ||
| 38 | ::core::assert_ne!($($x)*); | ||
| 39 | #[cfg(feature = "defmt")] | ||
| 40 | ::defmt::assert_ne!($($x)*); | ||
| 41 | } | ||
| 42 | }; | ||
| 43 | } | ||
| 44 | |||
| 45 | #[collapse_debuginfo(yes)] | ||
| 46 | macro_rules! debug_assert { | ||
| 47 | ($($x:tt)*) => { | ||
| 48 | { | ||
| 49 | #[cfg(not(feature = "defmt"))] | ||
| 50 | ::core::debug_assert!($($x)*); | ||
| 51 | #[cfg(feature = "defmt")] | ||
| 52 | ::defmt::debug_assert!($($x)*); | ||
| 53 | } | ||
| 54 | }; | ||
| 55 | } | ||
| 56 | |||
| 57 | #[collapse_debuginfo(yes)] | ||
| 58 | macro_rules! debug_assert_eq { | ||
| 59 | ($($x:tt)*) => { | ||
| 60 | { | ||
| 61 | #[cfg(not(feature = "defmt"))] | ||
| 62 | ::core::debug_assert_eq!($($x)*); | ||
| 63 | #[cfg(feature = "defmt")] | ||
| 64 | ::defmt::debug_assert_eq!($($x)*); | ||
| 65 | } | ||
| 66 | }; | ||
| 67 | } | ||
| 68 | |||
| 69 | #[collapse_debuginfo(yes)] | ||
| 70 | macro_rules! debug_assert_ne { | ||
| 71 | ($($x:tt)*) => { | ||
| 72 | { | ||
| 73 | #[cfg(not(feature = "defmt"))] | ||
| 74 | ::core::debug_assert_ne!($($x)*); | ||
| 75 | #[cfg(feature = "defmt")] | ||
| 76 | ::defmt::debug_assert_ne!($($x)*); | ||
| 77 | } | ||
| 78 | }; | ||
| 79 | } | ||
| 80 | |||
| 81 | #[collapse_debuginfo(yes)] | ||
| 82 | macro_rules! todo { | ||
| 83 | ($($x:tt)*) => { | ||
| 84 | { | ||
| 85 | #[cfg(not(feature = "defmt"))] | ||
| 86 | ::core::todo!($($x)*); | ||
| 87 | #[cfg(feature = "defmt")] | ||
| 88 | ::defmt::todo!($($x)*); | ||
| 89 | } | ||
| 90 | }; | ||
| 91 | } | ||
| 92 | |||
| 93 | #[collapse_debuginfo(yes)] | ||
| 94 | macro_rules! unreachable { | ||
| 95 | ($($x:tt)*) => { | ||
| 96 | { | ||
| 97 | #[cfg(not(feature = "defmt"))] | ||
| 98 | ::core::unreachable!($($x)*); | ||
| 99 | #[cfg(feature = "defmt")] | ||
| 100 | ::defmt::unreachable!($($x)*); | ||
| 101 | } | ||
| 102 | }; | ||
| 103 | } | ||
| 104 | |||
| 105 | #[collapse_debuginfo(yes)] | ||
| 106 | macro_rules! panic { | ||
| 107 | ($($x:tt)*) => { | ||
| 108 | { | ||
| 109 | #[cfg(not(feature = "defmt"))] | ||
| 110 | ::core::panic!($($x)*); | ||
| 111 | #[cfg(feature = "defmt")] | ||
| 112 | ::defmt::panic!($($x)*); | ||
| 113 | } | ||
| 114 | }; | ||
| 115 | } | ||
| 116 | |||
| 117 | #[collapse_debuginfo(yes)] | ||
| 118 | macro_rules! trace { | ||
| 119 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 120 | { | ||
| 121 | #[cfg(feature = "log")] | ||
| 122 | ::log::trace!($s $(, $x)*); | ||
| 123 | #[cfg(feature = "defmt")] | ||
| 124 | ::defmt::trace!($s $(, $x)*); | ||
| 125 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 126 | let _ = ($( & $x ),*); | ||
| 127 | } | ||
| 128 | }; | ||
| 129 | } | ||
| 130 | |||
| 131 | #[collapse_debuginfo(yes)] | ||
| 132 | macro_rules! debug { | ||
| 133 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 134 | { | ||
| 135 | #[cfg(feature = "log")] | ||
| 136 | ::log::debug!($s $(, $x)*); | ||
| 137 | #[cfg(feature = "defmt")] | ||
| 138 | ::defmt::debug!($s $(, $x)*); | ||
| 139 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 140 | let _ = ($( & $x ),*); | ||
| 141 | } | ||
| 142 | }; | ||
| 143 | } | ||
| 144 | |||
| 145 | #[collapse_debuginfo(yes)] | ||
| 146 | macro_rules! info { | ||
| 147 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 148 | { | ||
| 149 | #[cfg(feature = "log")] | ||
| 150 | ::log::info!($s $(, $x)*); | ||
| 151 | #[cfg(feature = "defmt")] | ||
| 152 | ::defmt::info!($s $(, $x)*); | ||
| 153 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 154 | let _ = ($( & $x ),*); | ||
| 155 | } | ||
| 156 | }; | ||
| 157 | } | ||
| 158 | |||
| 159 | #[collapse_debuginfo(yes)] | ||
| 160 | macro_rules! warn { | ||
| 161 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 162 | { | ||
| 163 | #[cfg(feature = "log")] | ||
| 164 | ::log::warn!($s $(, $x)*); | ||
| 165 | #[cfg(feature = "defmt")] | ||
| 166 | ::defmt::warn!($s $(, $x)*); | ||
| 167 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 168 | let _ = ($( & $x ),*); | ||
| 169 | } | ||
| 170 | }; | ||
| 171 | } | ||
| 172 | |||
| 173 | #[collapse_debuginfo(yes)] | ||
| 174 | macro_rules! error { | ||
| 175 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 176 | { | ||
| 177 | #[cfg(feature = "log")] | ||
| 178 | ::log::error!($s $(, $x)*); | ||
| 179 | #[cfg(feature = "defmt")] | ||
| 180 | ::defmt::error!($s $(, $x)*); | ||
| 181 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 182 | let _ = ($( & $x ),*); | ||
| 183 | } | ||
| 184 | }; | ||
| 185 | } | ||
| 186 | |||
| 187 | #[cfg(feature = "defmt")] | ||
| 188 | #[collapse_debuginfo(yes)] | ||
| 189 | macro_rules! unwrap { | ||
| 190 | ($($x:tt)*) => { | ||
| 191 | ::defmt::unwrap!($($x)*) | ||
| 192 | }; | ||
| 193 | } | ||
| 194 | |||
| 195 | #[cfg(not(feature = "defmt"))] | ||
| 196 | #[collapse_debuginfo(yes)] | ||
| 197 | macro_rules! unwrap { | ||
| 198 | ($arg:expr) => { | ||
| 199 | match $crate::fmt::Try::into_result($arg) { | ||
| 200 | ::core::result::Result::Ok(t) => t, | ||
| 201 | ::core::result::Result::Err(e) => { | ||
| 202 | ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); | ||
| 203 | } | ||
| 204 | } | ||
| 205 | }; | ||
| 206 | ($arg:expr, $($msg:expr),+ $(,)? ) => { | ||
| 207 | match $crate::fmt::Try::into_result($arg) { | ||
| 208 | ::core::result::Result::Ok(t) => t, | ||
| 209 | ::core::result::Result::Err(e) => { | ||
| 210 | ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); | ||
| 211 | } | ||
| 212 | } | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 217 | pub struct NoneError; | ||
| 218 | |||
| 219 | pub trait Try { | ||
| 220 | type Ok; | ||
| 221 | type Error; | ||
| 222 | fn into_result(self) -> Result<Self::Ok, Self::Error>; | ||
| 223 | } | ||
| 224 | |||
| 225 | impl<T> Try for Option<T> { | ||
| 226 | type Ok = T; | ||
| 227 | type Error = NoneError; | ||
| 228 | |||
| 229 | #[inline] | ||
| 230 | fn into_result(self) -> Result<T, NoneError> { | ||
| 231 | self.ok_or(NoneError) | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | impl<T, E> Try for Result<T, E> { | ||
| 236 | type Ok = T; | ||
| 237 | type Error = E; | ||
| 238 | |||
| 239 | #[inline] | ||
| 240 | fn into_result(self) -> Self { | ||
| 241 | self | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | pub(crate) struct Bytes<'a>(pub &'a [u8]); | ||
| 246 | |||
| 247 | impl<'a> Debug for Bytes<'a> { | ||
| 248 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 249 | write!(f, "{:#02x?}", self.0) | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | impl<'a> Display for Bytes<'a> { | ||
| 254 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 255 | write!(f, "{:#02x?}", self.0) | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | impl<'a> LowerHex for Bytes<'a> { | ||
| 260 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 261 | write!(f, "{:#02x?}", self.0) | ||
| 262 | } | ||
| 263 | } | ||
| 264 | |||
| 265 | #[cfg(feature = "defmt")] | ||
| 266 | impl<'a> defmt::Format for Bytes<'a> { | ||
| 267 | fn format(&self, fmt: defmt::Formatter) { | ||
| 268 | defmt::write!(fmt, "{:02x}", self.0) | ||
| 269 | } | ||
| 270 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/lhci.rs b/embassy-stm32-wpan/src/wb55/lhci.rs new file mode 100644 index 000000000..59c8bfb5d --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/lhci.rs | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | use core::ptr; | ||
| 2 | |||
| 3 | use crate::cmd::CmdPacket; | ||
| 4 | use crate::consts::{TL_EVT_HEADER_SIZE, TlPacketType}; | ||
| 5 | use crate::evt::{CcEvt, EvtPacket, EvtSerial}; | ||
| 6 | use crate::tables::{DeviceInfoTable, RssInfoTable, SafeBootInfoTable, TL_DEVICE_INFO_TABLE, WirelessFwInfoTable}; | ||
| 7 | |||
| 8 | const TL_BLEEVT_CC_OPCODE: u8 = 0x0e; | ||
| 9 | const LHCI_OPCODE_C1_DEVICE_INF: u16 = 0xfd62; | ||
| 10 | |||
| 11 | const PACKAGE_DATA_PTR: *const u8 = 0x1FFF_7500 as _; | ||
| 12 | const UID64_PTR: *const u32 = 0x1FFF_7580 as _; | ||
| 13 | |||
| 14 | #[derive(Debug, Copy, Clone)] | ||
| 15 | #[repr(C, packed)] | ||
| 16 | pub struct LhciC1DeviceInformationCcrp { | ||
| 17 | pub status: u8, | ||
| 18 | pub rev_id: u16, | ||
| 19 | pub dev_code_id: u16, | ||
| 20 | pub package_type: u8, | ||
| 21 | pub device_type_id: u8, | ||
| 22 | pub st_company_id: u32, | ||
| 23 | pub uid64: u32, | ||
| 24 | |||
| 25 | pub uid96_0: u32, | ||
| 26 | pub uid96_1: u32, | ||
| 27 | pub uid96_2: u32, | ||
| 28 | |||
| 29 | pub safe_boot_info_table: SafeBootInfoTable, | ||
| 30 | pub rss_info_table: RssInfoTable, | ||
| 31 | pub wireless_fw_info_table: WirelessFwInfoTable, | ||
| 32 | |||
| 33 | pub app_fw_inf: u32, | ||
| 34 | } | ||
| 35 | |||
| 36 | impl Default for LhciC1DeviceInformationCcrp { | ||
| 37 | fn default() -> Self { | ||
| 38 | let DeviceInfoTable { | ||
| 39 | safe_boot_info_table, | ||
| 40 | rss_info_table, | ||
| 41 | wireless_fw_info_table, | ||
| 42 | } = unsafe { ptr::read_volatile(TL_DEVICE_INFO_TABLE.as_ptr()) }; | ||
| 43 | |||
| 44 | let device_id = stm32_device_signature::device_id(); | ||
| 45 | let uid96_0 = (device_id[3] as u32) << 24 | ||
| 46 | | (device_id[2] as u32) << 16 | ||
| 47 | | (device_id[1] as u32) << 8 | ||
| 48 | | device_id[0] as u32; | ||
| 49 | let uid96_1 = (device_id[7] as u32) << 24 | ||
| 50 | | (device_id[6] as u32) << 16 | ||
| 51 | | (device_id[5] as u32) << 8 | ||
| 52 | | device_id[4] as u32; | ||
| 53 | let uid96_2 = (device_id[11] as u32) << 24 | ||
| 54 | | (device_id[10] as u32) << 16 | ||
| 55 | | (device_id[9] as u32) << 8 | ||
| 56 | | device_id[8] as u32; | ||
| 57 | |||
| 58 | let package_type = unsafe { *PACKAGE_DATA_PTR }; | ||
| 59 | let uid64 = unsafe { *UID64_PTR }; | ||
| 60 | let st_company_id = unsafe { *UID64_PTR.offset(1) } >> 8 & 0x00FF_FFFF; | ||
| 61 | let device_type_id = (unsafe { *UID64_PTR.offset(1) } & 0x000000FF) as u8; | ||
| 62 | |||
| 63 | LhciC1DeviceInformationCcrp { | ||
| 64 | status: 0, | ||
| 65 | rev_id: 0, | ||
| 66 | dev_code_id: 0, | ||
| 67 | package_type, | ||
| 68 | device_type_id, | ||
| 69 | st_company_id, | ||
| 70 | uid64, | ||
| 71 | uid96_0, | ||
| 72 | uid96_1, | ||
| 73 | uid96_2, | ||
| 74 | safe_boot_info_table, | ||
| 75 | rss_info_table, | ||
| 76 | wireless_fw_info_table, | ||
| 77 | app_fw_inf: (1 << 8), // 0.0.1 | ||
| 78 | } | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | impl LhciC1DeviceInformationCcrp { | ||
| 83 | pub fn new() -> Self { | ||
| 84 | Self::default() | ||
| 85 | } | ||
| 86 | |||
| 87 | pub fn write(&self, cmd_packet: &mut CmdPacket) { | ||
| 88 | let self_size = core::mem::size_of::<LhciC1DeviceInformationCcrp>(); | ||
| 89 | |||
| 90 | unsafe { | ||
| 91 | let cmd_packet_ptr: *mut CmdPacket = cmd_packet; | ||
| 92 | let evet_packet_ptr: *mut EvtPacket = cmd_packet_ptr.cast(); | ||
| 93 | |||
| 94 | let evt_serial: *mut EvtSerial = &mut (*evet_packet_ptr).evt_serial; | ||
| 95 | let evt_payload = (*evt_serial).evt.payload.as_mut_ptr(); | ||
| 96 | let evt_cc: *mut CcEvt = evt_payload.cast(); | ||
| 97 | let evt_cc_payload_buf = (*evt_cc).payload.as_mut_ptr(); | ||
| 98 | |||
| 99 | (*evt_serial).kind = TlPacketType::LocRsp as u8; | ||
| 100 | (*evt_serial).evt.evt_code = TL_BLEEVT_CC_OPCODE; | ||
| 101 | (*evt_serial).evt.payload_len = TL_EVT_HEADER_SIZE as u8 + self_size as u8; | ||
| 102 | |||
| 103 | (*evt_cc).cmd_code = LHCI_OPCODE_C1_DEVICE_INF; | ||
| 104 | (*evt_cc).num_cmd = 1; | ||
| 105 | |||
| 106 | let self_ptr: *const LhciC1DeviceInformationCcrp = self; | ||
| 107 | let self_buf = self_ptr.cast(); | ||
| 108 | |||
| 109 | ptr::copy(self_buf, evt_cc_payload_buf, self_size); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/mac/commands.rs b/embassy-stm32-wpan/src/wb55/mac/commands.rs new file mode 100644 index 000000000..d96f0094a --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/mac/commands.rs | |||
| @@ -0,0 +1,509 @@ | |||
| 1 | #![allow(unused)] | ||
| 2 | |||
| 3 | use core::{mem, slice}; | ||
| 4 | |||
| 5 | use smoltcp::wire::ieee802154::Frame; | ||
| 6 | |||
| 7 | use super::opcodes::OpcodeM4ToM0; | ||
| 8 | use super::typedefs::{ | ||
| 9 | AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, MacStatus, | ||
| 10 | PanId, PibId, ScanType, SecurityLevel, | ||
| 11 | }; | ||
| 12 | |||
| 13 | pub trait MacCommand: Sized { | ||
| 14 | const OPCODE: OpcodeM4ToM0; | ||
| 15 | |||
| 16 | fn payload<'a>(&'a self) -> &'a [u8] { | ||
| 17 | unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::<Self>()) } | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | /// MLME ASSOCIATE Request used to request an association | ||
| 22 | #[repr(C)] | ||
| 23 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 24 | pub struct AssociateRequest { | ||
| 25 | /// the logical channel on which to attempt association | ||
| 26 | pub channel_number: MacChannel, | ||
| 27 | /// the channel page on which to attempt association | ||
| 28 | pub channel_page: u8, | ||
| 29 | /// coordinator addressing mode | ||
| 30 | pub coord_addr_mode: AddressMode, | ||
| 31 | /// operational capabilities of the associating device | ||
| 32 | pub capability_information: Capabilities, | ||
| 33 | /// the identifier of the PAN with which to associate | ||
| 34 | pub coord_pan_id: PanId, | ||
| 35 | /// the security level to be used | ||
| 36 | pub security_level: SecurityLevel, | ||
| 37 | /// the mode used to identify the key to be used | ||
| 38 | pub key_id_mode: KeyIdMode, | ||
| 39 | /// the originator of the key to be used | ||
| 40 | pub key_source: [u8; 8], | ||
| 41 | /// Coordinator address | ||
| 42 | pub coord_address: MacAddress, | ||
| 43 | /// the index of the key to be used | ||
| 44 | pub key_index: u8, | ||
| 45 | } | ||
| 46 | |||
| 47 | impl MacCommand for AssociateRequest { | ||
| 48 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateReq; | ||
| 49 | } | ||
| 50 | |||
| 51 | /// MLME DISASSOCIATE Request sed to request a disassociation | ||
| 52 | #[repr(C)] | ||
| 53 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 54 | pub struct DisassociateRequest { | ||
| 55 | /// device addressing mode used | ||
| 56 | pub device_addr_mode: AddressMode, | ||
| 57 | /// the identifier of the PAN of the device | ||
| 58 | pub device_pan_id: PanId, | ||
| 59 | /// the reason for the disassociation | ||
| 60 | pub disassociation_reason: DisassociationReason, | ||
| 61 | /// device address | ||
| 62 | pub device_address: MacAddress, | ||
| 63 | /// `true` if the disassociation notification command is to be sent indirectly | ||
| 64 | pub tx_indirect: bool, | ||
| 65 | /// the security level to be used | ||
| 66 | pub security_level: SecurityLevel, | ||
| 67 | /// the mode to be used to indetify the key to be used | ||
| 68 | pub key_id_mode: KeyIdMode, | ||
| 69 | /// the index of the key to be used | ||
| 70 | pub key_index: u8, | ||
| 71 | /// the originator of the key to be used | ||
| 72 | pub key_source: [u8; 8], | ||
| 73 | } | ||
| 74 | |||
| 75 | impl MacCommand for DisassociateRequest { | ||
| 76 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDisassociateReq; | ||
| 77 | } | ||
| 78 | |||
| 79 | /// MLME GET Request used to request a PIB value | ||
| 80 | #[repr(C)] | ||
| 81 | #[derive(Default)] | ||
| 82 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 83 | pub struct GetRequest { | ||
| 84 | /// the name of the PIB attribute to read | ||
| 85 | pub pib_attribute: PibId, | ||
| 86 | |||
| 87 | /// byte stuffing to keep 32 bit alignment | ||
| 88 | pub a_stuffing: [u8; 3], | ||
| 89 | } | ||
| 90 | |||
| 91 | impl MacCommand for GetRequest { | ||
| 92 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq; | ||
| 93 | } | ||
| 94 | |||
| 95 | /// MLME GTS Request used to request and maintain GTSs | ||
| 96 | #[repr(C)] | ||
| 97 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 98 | pub struct GtsRequest { | ||
| 99 | /// the characteristics of the GTS | ||
| 100 | pub characteristics: GtsCharacteristics, | ||
| 101 | /// the security level to be used | ||
| 102 | pub security_level: SecurityLevel, | ||
| 103 | /// the mode used to identify the key to be used | ||
| 104 | pub key_id_mode: KeyIdMode, | ||
| 105 | /// the index of the key to be used | ||
| 106 | pub key_index: u8, | ||
| 107 | /// the originator of the key to be used | ||
| 108 | pub key_source: [u8; 8], | ||
| 109 | } | ||
| 110 | |||
| 111 | impl MacCommand for GtsRequest { | ||
| 112 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq; | ||
| 113 | } | ||
| 114 | |||
| 115 | #[repr(C)] | ||
| 116 | #[derive(Default)] | ||
| 117 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 118 | pub struct ResetRequest { | ||
| 119 | /// MAC PIB attributes are set to their default values or not during reset | ||
| 120 | pub set_default_pib: bool, | ||
| 121 | /// byte stuffing to keep 32 bit alignment | ||
| 122 | pub a_stuffing: [u8; 3], | ||
| 123 | } | ||
| 124 | |||
| 125 | impl MacCommand for ResetRequest { | ||
| 126 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeResetReq; | ||
| 127 | } | ||
| 128 | |||
| 129 | /// MLME RX ENABLE Request used to request that the receiver is either enabled | ||
| 130 | /// for a finite period of time or disabled | ||
| 131 | #[repr(C)] | ||
| 132 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 133 | pub struct RxEnableRequest { | ||
| 134 | /// the request operation can be deferred or not | ||
| 135 | pub defer_permit: bool, | ||
| 136 | /// configure the transceiver to RX with ranging for a value of | ||
| 137 | /// RANGING_ON or to not enable ranging for RANGING_OFF | ||
| 138 | pub ranging_rx_control: u8, | ||
| 139 | /// byte stuffing to keep 32 bit alignment | ||
| 140 | pub a_stuffing: [u8; 2], | ||
| 141 | /// number of symbols measured before the receiver is to be enabled or disabled | ||
| 142 | pub rx_on_time: [u8; 4], | ||
| 143 | /// number of symbols for which the receiver is to be enabled | ||
| 144 | pub rx_on_duration: [u8; 4], | ||
| 145 | } | ||
| 146 | |||
| 147 | impl MacCommand for RxEnableRequest { | ||
| 148 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeRxEnableReq; | ||
| 149 | } | ||
| 150 | |||
| 151 | /// MLME SCAN Request used to initiate a channel scan over a given list of channels | ||
| 152 | #[repr(C)] | ||
| 153 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 154 | pub struct ScanRequest { | ||
| 155 | /// the type of scan to be performed | ||
| 156 | pub scan_type: ScanType, | ||
| 157 | /// the time spent on scanning each channel | ||
| 158 | pub scan_duration: u8, | ||
| 159 | /// channel page on which to perform the scan | ||
| 160 | pub channel_page: u8, | ||
| 161 | /// security level to be used | ||
| 162 | pub security_level: SecurityLevel, | ||
| 163 | /// indicate which channels are to be scanned | ||
| 164 | pub scan_channels: [u8; 4], | ||
| 165 | /// originator the key to be used | ||
| 166 | pub key_source: [u8; 8], | ||
| 167 | /// mode used to identify the key to be used | ||
| 168 | pub key_id_mode: KeyIdMode, | ||
| 169 | /// index of the key to be used | ||
| 170 | pub key_index: u8, | ||
| 171 | /// byte stuffing to keep 32 bit alignment | ||
| 172 | pub a_stuffing: [u8; 2], | ||
| 173 | } | ||
| 174 | |||
| 175 | impl MacCommand for ScanRequest { | ||
| 176 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeScanReq; | ||
| 177 | } | ||
| 178 | |||
| 179 | /// MLME SET Request used to attempt to write the given value to the indicated PIB attribute | ||
| 180 | #[repr(C)] | ||
| 181 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 182 | pub struct SetRequest { | ||
| 183 | /// the pointer to the value of the PIB attribute to set | ||
| 184 | pub pib_attribute_ptr: *const u8, | ||
| 185 | /// the name of the PIB attribute to set | ||
| 186 | pub pib_attribute: PibId, | ||
| 187 | } | ||
| 188 | |||
| 189 | impl MacCommand for SetRequest { | ||
| 190 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSetReq; | ||
| 191 | } | ||
| 192 | |||
| 193 | /// MLME START Request used by the FFDs to intiate a new PAN or to begin using a new superframe | ||
| 194 | /// configuration | ||
| 195 | #[repr(C)] | ||
| 196 | #[derive(Default)] | ||
| 197 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 198 | pub struct StartRequest { | ||
| 199 | /// PAN indentifier to used by the device | ||
| 200 | pub pan_id: PanId, | ||
| 201 | /// logical channel on which to begin | ||
| 202 | pub channel_number: MacChannel, | ||
| 203 | /// channel page on which to begin | ||
| 204 | pub channel_page: u8, | ||
| 205 | /// time at which to begin transmitting beacons | ||
| 206 | pub start_time: [u8; 4], | ||
| 207 | /// indicated how often the beacon is to be transmitted | ||
| 208 | pub beacon_order: u8, | ||
| 209 | /// length of the active portion of the superframe | ||
| 210 | pub superframe_order: u8, | ||
| 211 | /// indicated wheter the device is a PAN coordinator or not | ||
| 212 | pub pan_coordinator: bool, | ||
| 213 | /// indicates if the receiver of the beaconing device is disabled or not | ||
| 214 | pub battery_life_extension: bool, | ||
| 215 | /// indicated if the coordinator realignment command is to be trasmitted | ||
| 216 | pub coord_realignment: u8, | ||
| 217 | /// indicated if the coordinator realignment command is to be trasmitted | ||
| 218 | pub coord_realign_security_level: SecurityLevel, | ||
| 219 | /// index of the key to be used | ||
| 220 | pub coord_realign_key_id_index: u8, | ||
| 221 | /// originator of the key to be used | ||
| 222 | pub coord_realign_key_source: [u8; 8], | ||
| 223 | /// security level to be used for beacon frames | ||
| 224 | pub beacon_security_level: SecurityLevel, | ||
| 225 | /// mode used to identify the key to be used | ||
| 226 | pub beacon_key_id_mode: KeyIdMode, | ||
| 227 | /// index of the key to be used | ||
| 228 | pub beacon_key_index: u8, | ||
| 229 | /// originator of the key to be used | ||
| 230 | pub beacon_key_source: [u8; 8], | ||
| 231 | } | ||
| 232 | |||
| 233 | impl MacCommand for StartRequest { | ||
| 234 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeStartReq; | ||
| 235 | } | ||
| 236 | |||
| 237 | /// MLME SYNC Request used to synchronize with the coordinator by acquiring and, if | ||
| 238 | /// specified, tracking its beacons | ||
| 239 | #[repr(C)] | ||
| 240 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 241 | pub struct SyncRequest { | ||
| 242 | /// the channel number on which to attempt coordinator synchronization | ||
| 243 | pub channel_number: MacChannel, | ||
| 244 | /// the channel page on which to attempt coordinator synchronization | ||
| 245 | pub channel_page: u8, | ||
| 246 | /// `true` if the MLME is to synchronize with the next beacon and attempts | ||
| 247 | /// to track all future beacons. | ||
| 248 | /// | ||
| 249 | /// `false` if the MLME is to synchronize with only the next beacon | ||
| 250 | pub track_beacon: bool, | ||
| 251 | /// byte stuffing to keep 32 bit alignment | ||
| 252 | pub a_stuffing: [u8; 1], | ||
| 253 | } | ||
| 254 | |||
| 255 | impl MacCommand for SyncRequest { | ||
| 256 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSyncReq; | ||
| 257 | } | ||
| 258 | |||
| 259 | /// MLME POLL Request propmts the device to request data from the coordinator | ||
| 260 | #[repr(C)] | ||
| 261 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 262 | pub struct PollRequest { | ||
| 263 | /// addressing mode of the coordinator | ||
| 264 | pub coord_addr_mode: AddressMode, | ||
| 265 | /// security level to be used | ||
| 266 | pub security_level: SecurityLevel, | ||
| 267 | /// mode used to identify the key to be used | ||
| 268 | pub key_id_mode: KeyIdMode, | ||
| 269 | /// index of the key to be used | ||
| 270 | pub key_index: u8, | ||
| 271 | /// coordinator address | ||
| 272 | pub coord_address: MacAddress, | ||
| 273 | /// originator of the key to be used | ||
| 274 | pub key_source: [u8; 8], | ||
| 275 | /// PAN identifier of the coordinator | ||
| 276 | pub coord_pan_id: PanId, | ||
| 277 | /// byte stuffing to keep 32 bit alignment | ||
| 278 | pub a_stuffing: [u8; 2], | ||
| 279 | } | ||
| 280 | |||
| 281 | impl MacCommand for PollRequest { | ||
| 282 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmePollReq; | ||
| 283 | } | ||
| 284 | |||
| 285 | /// MLME DPS Request allows the next higher layer to request that the PHY utilize a | ||
| 286 | /// given pair of preamble codes for a single use pending expiration of the DPSIndexDuration | ||
| 287 | #[repr(C)] | ||
| 288 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 289 | pub struct DpsRequest { | ||
| 290 | /// the index value for the transmitter | ||
| 291 | tx_dps_index: u8, | ||
| 292 | /// the index value of the receiver | ||
| 293 | rx_dps_index: u8, | ||
| 294 | /// the number of symbols for which the transmitter and receiver will utilize the | ||
| 295 | /// respective DPS indices | ||
| 296 | dps_index_duration: u8, | ||
| 297 | /// byte stuffing to keep 32 bit alignment | ||
| 298 | pub a_stuffing: [u8; 1], | ||
| 299 | } | ||
| 300 | |||
| 301 | impl MacCommand for DpsRequest { | ||
| 302 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDpsReq; | ||
| 303 | } | ||
| 304 | |||
| 305 | /// MLME SOUNDING request primitive which is used by the next higher layer to request that | ||
| 306 | /// the PHY respond with channel sounding information | ||
| 307 | #[repr(C)] | ||
| 308 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 309 | pub struct SoundingRequest { | ||
| 310 | /// byte stuffing to keep 32 bit alignment | ||
| 311 | pub a_stuffing: [u8; 4], | ||
| 312 | } | ||
| 313 | |||
| 314 | impl MacCommand for SoundingRequest { | ||
| 315 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSoundingReq; | ||
| 316 | } | ||
| 317 | |||
| 318 | /// MLME CALIBRATE request primitive which used to obtain the results of a ranging | ||
| 319 | /// calibration request from an RDEV | ||
| 320 | #[repr(C)] | ||
| 321 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 322 | pub struct CalibrateRequest { | ||
| 323 | /// byte stuffing to keep 32 bit alignment | ||
| 324 | pub a_stuffing: [u8; 4], | ||
| 325 | } | ||
| 326 | |||
| 327 | impl MacCommand for CalibrateRequest { | ||
| 328 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeCalibrateReq; | ||
| 329 | } | ||
| 330 | |||
| 331 | /// MCPS DATA Request used for MAC data related requests from the application | ||
| 332 | #[repr(C)] | ||
| 333 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 334 | pub struct DataRequest { | ||
| 335 | /// the handle assocated with the MSDU to be transmitted | ||
| 336 | pub msdu_ptr: *const u8, | ||
| 337 | /// source addressing mode used | ||
| 338 | pub src_addr_mode: AddressMode, | ||
| 339 | /// destination addressing mode used | ||
| 340 | pub dst_addr_mode: AddressMode, | ||
| 341 | /// destination PAN Id | ||
| 342 | pub dst_pan_id: PanId, | ||
| 343 | /// destination address | ||
| 344 | pub dst_address: MacAddress, | ||
| 345 | /// the number of octets contained in the MSDU | ||
| 346 | pub msdu_length: u8, | ||
| 347 | /// the handle assocated with the MSDU to be transmitted | ||
| 348 | pub msdu_handle: u8, | ||
| 349 | /// the ACK transmittion options for the MSDU | ||
| 350 | pub ack_tx: u8, | ||
| 351 | /// `true` if a GTS is to be used for transmission | ||
| 352 | /// | ||
| 353 | /// `false` indicates that the CAP will be used | ||
| 354 | pub gts_tx: bool, | ||
| 355 | /// the pending bit transmission options for the MSDU | ||
| 356 | pub indirect_tx: u8, | ||
| 357 | /// the security level to be used | ||
| 358 | pub security_level: SecurityLevel, | ||
| 359 | /// the mode used to indentify the key to be used | ||
| 360 | pub key_id_mode: KeyIdMode, | ||
| 361 | /// the index of the key to be used | ||
| 362 | pub key_index: u8, | ||
| 363 | /// the originator of the key to be used | ||
| 364 | pub key_source: [u8; 8], | ||
| 365 | /// 2011 - the pulse repitition value | ||
| 366 | pub uwbprf: u8, | ||
| 367 | /// 2011 - the ranging configuration | ||
| 368 | pub ranging: u8, | ||
| 369 | /// 2011 - the preamble symbol repititions | ||
| 370 | pub uwb_preamble_symbol_repetitions: u8, | ||
| 371 | /// 2011 - indicates the data rate | ||
| 372 | pub datrate: u8, | ||
| 373 | } | ||
| 374 | |||
| 375 | impl DataRequest { | ||
| 376 | pub fn set_buffer<'a>(&'a mut self, buf: &'a [u8]) -> &'a mut Self { | ||
| 377 | self.msdu_ptr = buf as *const _ as *const u8; | ||
| 378 | self.msdu_length = buf.len() as u8; | ||
| 379 | |||
| 380 | self | ||
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | impl<'a, T: AsRef<[u8]>> TryFrom<Frame<&'a T>> for DataRequest { | ||
| 385 | type Error = (); | ||
| 386 | |||
| 387 | fn try_from(frame: Frame<&'a T>) -> Result<Self, Self::Error> { | ||
| 388 | // TODO: map the rest of these | ||
| 389 | |||
| 390 | let mut request = DataRequest { | ||
| 391 | src_addr_mode: frame.src_addressing_mode().try_into()?, | ||
| 392 | dst_addr_mode: frame.dst_addressing_mode().try_into()?, | ||
| 393 | dst_pan_id: frame.dst_pan_id().ok_or(())?.into(), | ||
| 394 | dst_address: frame.dst_addr().ok_or(())?.into(), | ||
| 395 | msdu_handle: frame.sequence_number().ok_or(())?, | ||
| 396 | key_source: frame.key_source().unwrap_or_default().try_into().unwrap_or_default(), | ||
| 397 | ack_tx: frame.ack_request() as u8, | ||
| 398 | gts_tx: false, | ||
| 399 | security_level: if frame.security_enabled() { | ||
| 400 | SecurityLevel::Secured | ||
| 401 | } else { | ||
| 402 | SecurityLevel::Unsecure | ||
| 403 | }, | ||
| 404 | ..Default::default() | ||
| 405 | }; | ||
| 406 | |||
| 407 | request.set_buffer(frame.payload().ok_or(())?); | ||
| 408 | |||
| 409 | Ok(request) | ||
| 410 | } | ||
| 411 | } | ||
| 412 | |||
| 413 | impl Default for DataRequest { | ||
| 414 | fn default() -> Self { | ||
| 415 | Self { | ||
| 416 | msdu_ptr: 0 as *const u8, | ||
| 417 | src_addr_mode: AddressMode::NoAddress, | ||
| 418 | dst_addr_mode: AddressMode::NoAddress, | ||
| 419 | dst_pan_id: PanId([0, 0]), | ||
| 420 | dst_address: MacAddress { short: [0, 0] }, | ||
| 421 | msdu_length: 0, | ||
| 422 | msdu_handle: 0, | ||
| 423 | ack_tx: 0, | ||
| 424 | gts_tx: false, | ||
| 425 | indirect_tx: 0, | ||
| 426 | security_level: SecurityLevel::Unsecure, | ||
| 427 | key_id_mode: KeyIdMode::Implicite, | ||
| 428 | key_index: 0, | ||
| 429 | key_source: [0u8; 8], | ||
| 430 | uwbprf: 0, | ||
| 431 | ranging: 0, | ||
| 432 | uwb_preamble_symbol_repetitions: 0, | ||
| 433 | datrate: 0, | ||
| 434 | } | ||
| 435 | } | ||
| 436 | } | ||
| 437 | |||
| 438 | impl MacCommand for DataRequest { | ||
| 439 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsDataReq; | ||
| 440 | } | ||
| 441 | |||
| 442 | /// for MCPS PURGE Request used to purge an MSDU from the transaction queue | ||
| 443 | #[repr(C)] | ||
| 444 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 445 | pub struct PurgeRequest { | ||
| 446 | /// the handle associated with the MSDU to be purged from the transaction | ||
| 447 | /// queue | ||
| 448 | pub msdu_handle: u8, | ||
| 449 | /// byte stuffing to keep 32 bit alignment | ||
| 450 | pub a_stuffing: [u8; 3], | ||
| 451 | } | ||
| 452 | |||
| 453 | impl MacCommand for PurgeRequest { | ||
| 454 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsPurgeReq; | ||
| 455 | } | ||
| 456 | |||
| 457 | /// MLME ASSOCIATE Response used to initiate a response to an MLME-ASSOCIATE.indication | ||
| 458 | #[repr(C)] | ||
| 459 | #[derive(Default)] | ||
| 460 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 461 | pub struct AssociateResponse { | ||
| 462 | /// extended address of the device requesting association | ||
| 463 | pub device_address: [u8; 8], | ||
| 464 | /// 16-bitshort device address allocated by the coordinator on successful | ||
| 465 | /// association | ||
| 466 | pub assoc_short_address: [u8; 2], | ||
| 467 | /// status of the association attempt | ||
| 468 | pub status: MacStatus, | ||
| 469 | /// security level to be used | ||
| 470 | pub security_level: SecurityLevel, | ||
| 471 | /// the originator of the key to be used | ||
| 472 | pub key_source: [u8; 8], | ||
| 473 | /// the mode used to identify the key to be used | ||
| 474 | pub key_id_mode: KeyIdMode, | ||
| 475 | /// the index of the key to be used | ||
| 476 | pub key_index: u8, | ||
| 477 | /// byte stuffing to keep 32 bit alignment | ||
| 478 | pub a_stuffing: [u8; 2], | ||
| 479 | } | ||
| 480 | |||
| 481 | impl MacCommand for AssociateResponse { | ||
| 482 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateRes; | ||
| 483 | } | ||
| 484 | |||
| 485 | /// MLME ORPHAN Response used to respond to the MLME ORPHAN Indication | ||
| 486 | #[repr(C)] | ||
| 487 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 488 | pub struct OrphanResponse { | ||
| 489 | /// extended address of the orphaned device | ||
| 490 | pub orphan_address: [u8; 8], | ||
| 491 | /// short address allocated to the orphaned device | ||
| 492 | pub short_address: [u8; 2], | ||
| 493 | /// if the orphaned device is associated with coordinator or not | ||
| 494 | pub associated_member: bool, | ||
| 495 | /// security level to be used | ||
| 496 | pub security_level: SecurityLevel, | ||
| 497 | /// the originator of the key to be used | ||
| 498 | pub key_source: [u8; 8], | ||
| 499 | /// the mode used to identify the key to be used | ||
| 500 | pub key_id_mode: KeyIdMode, | ||
| 501 | /// the index of the key to be used | ||
| 502 | pub key_index: u8, | ||
| 503 | /// byte stuffing to keep 32 bit alignment | ||
| 504 | pub a_stuffing: [u8; 2], | ||
| 505 | } | ||
| 506 | |||
| 507 | impl MacCommand for OrphanResponse { | ||
| 508 | const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeOrphanRes; | ||
| 509 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/mac/consts.rs b/embassy-stm32-wpan/src/wb55/mac/consts.rs new file mode 100644 index 000000000..56903d980 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/mac/consts.rs | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | pub const MAX_PAN_DESC_SUPPORTED: usize = 6; | ||
| 2 | pub const MAX_SOUNDING_LIST_SUPPORTED: usize = 6; | ||
| 3 | pub const MAX_PENDING_ADDRESS: usize = 7; | ||
| 4 | pub const MAX_ED_SCAN_RESULTS_SUPPORTED: usize = 16; | ||
diff --git a/embassy-stm32-wpan/src/wb55/mac/control.rs b/embassy-stm32-wpan/src/wb55/mac/control.rs new file mode 100644 index 000000000..14c6fdd2b --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/mac/control.rs | |||
| @@ -0,0 +1,204 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 2 | use core::future::Future; | ||
| 3 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 4 | use core::task; | ||
| 5 | use core::task::Poll; | ||
| 6 | |||
| 7 | use embassy_net_driver::LinkState; | ||
| 8 | use embassy_sync::blocking_mutex; | ||
| 9 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 10 | use embassy_sync::mutex::Mutex; | ||
| 11 | use embassy_sync::signal::Signal; | ||
| 12 | use futures_util::FutureExt; | ||
| 13 | |||
| 14 | use crate::mac::commands::*; | ||
| 15 | use crate::mac::driver::NetworkState; | ||
| 16 | use crate::mac::event::MacEvent; | ||
| 17 | use crate::mac::runner::ZeroCopyPubSub; | ||
| 18 | use crate::mac::typedefs::*; | ||
| 19 | use crate::sub::mac::MacTx; | ||
| 20 | |||
| 21 | pub struct Control<'a> { | ||
| 22 | rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>, | ||
| 23 | mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx<'a>>, | ||
| 24 | #[allow(unused)] | ||
| 25 | network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>, | ||
| 26 | } | ||
| 27 | |||
| 28 | impl<'a> Control<'a> { | ||
| 29 | pub(crate) fn new( | ||
| 30 | rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>, | ||
| 31 | mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx<'a>>, | ||
| 32 | network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>, | ||
| 33 | ) -> Self { | ||
| 34 | Self { | ||
| 35 | rx_event_channel, | ||
| 36 | mac_tx, | ||
| 37 | network_state, | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | pub async fn init_link(&mut self, pan_id: [u8; 2]) { | ||
| 42 | debug!("resetting"); | ||
| 43 | |||
| 44 | debug!( | ||
| 45 | "{:#x}", | ||
| 46 | self.send_command_and_get_response(&ResetRequest { | ||
| 47 | set_default_pib: true, | ||
| 48 | ..Default::default() | ||
| 49 | }) | ||
| 50 | .await | ||
| 51 | .unwrap() | ||
| 52 | .await | ||
| 53 | ); | ||
| 54 | |||
| 55 | let (short_address, mac_address) = critical_section::with(|cs| { | ||
| 56 | let mut network_state = self.network_state.borrow(cs).borrow_mut(); | ||
| 57 | |||
| 58 | network_state.pan_id = pan_id; | ||
| 59 | |||
| 60 | (network_state.short_addr, network_state.mac_addr) | ||
| 61 | }); | ||
| 62 | |||
| 63 | debug!("setting extended address"); | ||
| 64 | debug!( | ||
| 65 | "{:#x}", | ||
| 66 | self.send_command_and_get_response(&SetRequest { | ||
| 67 | pib_attribute_ptr: &u64::from_be_bytes(mac_address) as *const _ as *const u8, | ||
| 68 | pib_attribute: PibId::ExtendedAddress, | ||
| 69 | }) | ||
| 70 | .await | ||
| 71 | .unwrap() | ||
| 72 | .await | ||
| 73 | ); | ||
| 74 | |||
| 75 | debug!("setting short address"); | ||
| 76 | debug!( | ||
| 77 | "{:#x}", | ||
| 78 | self.send_command_and_get_response(&SetRequest { | ||
| 79 | pib_attribute_ptr: &u16::from_be_bytes(short_address) as *const _ as *const u8, | ||
| 80 | pib_attribute: PibId::ShortAddress, | ||
| 81 | }) | ||
| 82 | .await | ||
| 83 | .unwrap() | ||
| 84 | .await | ||
| 85 | ); | ||
| 86 | |||
| 87 | debug!("setting association permit"); | ||
| 88 | let association_permit: bool = true; | ||
| 89 | debug!( | ||
| 90 | "{:#x}", | ||
| 91 | self.send_command_and_get_response(&SetRequest { | ||
| 92 | pib_attribute_ptr: &association_permit as *const _ as *const u8, | ||
| 93 | pib_attribute: PibId::AssociationPermit, | ||
| 94 | }) | ||
| 95 | .await | ||
| 96 | .unwrap() | ||
| 97 | .await | ||
| 98 | ); | ||
| 99 | |||
| 100 | debug!("setting TX power"); | ||
| 101 | let transmit_power: i8 = 2; | ||
| 102 | debug!( | ||
| 103 | "{:#x}", | ||
| 104 | self.send_command_and_get_response(&SetRequest { | ||
| 105 | pib_attribute_ptr: &transmit_power as *const _ as *const u8, | ||
| 106 | pib_attribute: PibId::TransmitPower, | ||
| 107 | }) | ||
| 108 | .await | ||
| 109 | .unwrap() | ||
| 110 | .await | ||
| 111 | ); | ||
| 112 | |||
| 113 | debug!("starting FFD device"); | ||
| 114 | debug!( | ||
| 115 | "{:#x}", | ||
| 116 | self.send_command_and_get_response(&StartRequest { | ||
| 117 | pan_id: PanId(pan_id), | ||
| 118 | channel_number: MacChannel::Channel16, | ||
| 119 | beacon_order: 0x0F, | ||
| 120 | superframe_order: 0x0F, | ||
| 121 | pan_coordinator: true, | ||
| 122 | battery_life_extension: false, | ||
| 123 | ..Default::default() | ||
| 124 | }) | ||
| 125 | .await | ||
| 126 | .unwrap() | ||
| 127 | .await | ||
| 128 | ); | ||
| 129 | |||
| 130 | debug!("setting RX on when idle"); | ||
| 131 | let rx_on_while_idle: bool = true; | ||
| 132 | debug!( | ||
| 133 | "{:#x}", | ||
| 134 | self.send_command_and_get_response(&SetRequest { | ||
| 135 | pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, | ||
| 136 | pib_attribute: PibId::RxOnWhenIdle, | ||
| 137 | }) | ||
| 138 | .await | ||
| 139 | .unwrap() | ||
| 140 | .await | ||
| 141 | ); | ||
| 142 | |||
| 143 | critical_section::with(|cs| { | ||
| 144 | let mut network_state = self.network_state.borrow(cs).borrow_mut(); | ||
| 145 | |||
| 146 | network_state.link_state = LinkState::Up; | ||
| 147 | network_state.link_waker.wake(); | ||
| 148 | }); | ||
| 149 | } | ||
| 150 | |||
| 151 | pub async fn send_command<T>(&self, cmd: &T) -> Result<(), MacError> | ||
| 152 | where | ||
| 153 | T: MacCommand, | ||
| 154 | { | ||
| 155 | self.mac_tx.lock().await.send_command(cmd).await | ||
| 156 | } | ||
| 157 | |||
| 158 | pub async fn send_command_and_get_response<T>(&self, cmd: &T) -> Result<EventToken<'a>, MacError> | ||
| 159 | where | ||
| 160 | T: MacCommand, | ||
| 161 | { | ||
| 162 | let token = EventToken::new(self.rx_event_channel); | ||
| 163 | |||
| 164 | compiler_fence(Ordering::Release); | ||
| 165 | |||
| 166 | self.mac_tx.lock().await.send_command(cmd).await?; | ||
| 167 | |||
| 168 | Ok(token) | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | pub struct EventToken<'a> { | ||
| 173 | rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>, | ||
| 174 | } | ||
| 175 | |||
| 176 | impl<'a> EventToken<'a> { | ||
| 177 | pub(crate) fn new(rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>) -> Self { | ||
| 178 | // Enable event receiving | ||
| 179 | rx_event_channel.lock(|s| { | ||
| 180 | *s.borrow_mut() = Some(Signal::new()); | ||
| 181 | }); | ||
| 182 | |||
| 183 | Self { rx_event_channel } | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | impl<'a> Future for EventToken<'a> { | ||
| 188 | type Output = MacEvent<'a>; | ||
| 189 | |||
| 190 | fn poll(self: core::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> { | ||
| 191 | self.rx_event_channel | ||
| 192 | .lock(|s| s.borrow_mut().as_mut().unwrap().wait().poll_unpin(cx)) | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | impl<'a> Drop for EventToken<'a> { | ||
| 197 | fn drop(&mut self) { | ||
| 198 | // Disable event receiving | ||
| 199 | // This will also drop the contained event, if it exists, and will free up receiving the next event | ||
| 200 | self.rx_event_channel.lock(|s| { | ||
| 201 | *s.borrow_mut() = None; | ||
| 202 | }); | ||
| 203 | } | ||
| 204 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/mac/driver.rs b/embassy-stm32-wpan/src/wb55/mac/driver.rs new file mode 100644 index 000000000..41171ce3d --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/mac/driver.rs | |||
| @@ -0,0 +1,213 @@ | |||
| 1 | #![deny(unused_must_use)] | ||
| 2 | |||
| 3 | use core::cell::RefCell; | ||
| 4 | use core::task::Context; | ||
| 5 | |||
| 6 | use embassy_net_driver::{Capabilities, HardwareAddress, LinkState}; | ||
| 7 | use embassy_sync::blocking_mutex; | ||
| 8 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 9 | use embassy_sync::channel::Channel; | ||
| 10 | use embassy_sync::mutex::Mutex; | ||
| 11 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 12 | |||
| 13 | use crate::mac::event::MacEvent; | ||
| 14 | use crate::mac::indications::{write_frame_from_beacon_indication, write_frame_from_data_indication}; | ||
| 15 | use crate::mac::runner::{BUF_SIZE, ZeroCopyPubSub}; | ||
| 16 | use crate::mac::{Control, MTU, Runner}; | ||
| 17 | use crate::sub::mac::{Mac, MacRx, MacTx}; | ||
| 18 | |||
| 19 | pub struct NetworkState { | ||
| 20 | pub mac_addr: [u8; 8], | ||
| 21 | pub short_addr: [u8; 2], | ||
| 22 | pub pan_id: [u8; 2], | ||
| 23 | pub link_state: LinkState, | ||
| 24 | pub link_waker: AtomicWaker, | ||
| 25 | } | ||
| 26 | |||
| 27 | impl NetworkState { | ||
| 28 | pub const fn new() -> Self { | ||
| 29 | Self { | ||
| 30 | mac_addr: [0u8; 8], | ||
| 31 | short_addr: [0u8; 2], | ||
| 32 | pan_id: [0u8; 2], | ||
| 33 | link_state: LinkState::Down, | ||
| 34 | link_waker: AtomicWaker::new(), | ||
| 35 | } | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | pub struct DriverState<'d> { | ||
| 40 | pub mac_tx: Mutex<CriticalSectionRawMutex, MacTx<'d>>, | ||
| 41 | pub mac_rx: MacRx<'d>, | ||
| 42 | pub rx_event_channel: ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'d>>, | ||
| 43 | pub rx_data_channel: Channel<CriticalSectionRawMutex, MacEvent<'d>, 1>, | ||
| 44 | pub tx_data_channel: Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), BUF_SIZE>, | ||
| 45 | pub tx_buf_channel: Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], BUF_SIZE>, | ||
| 46 | pub tx_buf_queue: [[u8; MTU]; BUF_SIZE], | ||
| 47 | pub network_state: blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>, | ||
| 48 | } | ||
| 49 | |||
| 50 | impl<'d> DriverState<'d> { | ||
| 51 | pub const fn new(mac: Mac<'d>) -> Self { | ||
| 52 | let (mac_rx, mac_tx) = mac.split(); | ||
| 53 | let mac_tx = Mutex::new(mac_tx); | ||
| 54 | |||
| 55 | Self { | ||
| 56 | mac_tx, | ||
| 57 | mac_rx, | ||
| 58 | rx_event_channel: ZeroCopyPubSub::new(RefCell::new(None)), | ||
| 59 | rx_data_channel: Channel::new(), | ||
| 60 | tx_data_channel: Channel::new(), | ||
| 61 | tx_buf_channel: Channel::new(), | ||
| 62 | tx_buf_queue: [[0u8; MTU]; BUF_SIZE], | ||
| 63 | network_state: blocking_mutex::Mutex::new(RefCell::new(NetworkState::new())), | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | pub struct Driver<'d> { | ||
| 69 | tx_data_channel: &'d Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), BUF_SIZE>, | ||
| 70 | tx_buf_channel: &'d Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], BUF_SIZE>, | ||
| 71 | rx_data_channel: &'d Channel<CriticalSectionRawMutex, MacEvent<'d>, 1>, | ||
| 72 | network_state: &'d blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>, | ||
| 73 | } | ||
| 74 | |||
| 75 | impl<'d> Driver<'d> { | ||
| 76 | pub fn new( | ||
| 77 | driver_state: &'d mut DriverState<'d>, | ||
| 78 | short_address: [u8; 2], | ||
| 79 | mac_address: [u8; 8], | ||
| 80 | ) -> (Self, Runner<'d>, Control<'d>) { | ||
| 81 | ( | ||
| 82 | Self { | ||
| 83 | tx_data_channel: &driver_state.tx_data_channel, | ||
| 84 | tx_buf_channel: &driver_state.tx_buf_channel, | ||
| 85 | rx_data_channel: &driver_state.rx_data_channel, | ||
| 86 | network_state: &driver_state.network_state, | ||
| 87 | }, | ||
| 88 | Runner::new( | ||
| 89 | &driver_state.rx_event_channel, | ||
| 90 | &driver_state.rx_data_channel, | ||
| 91 | &mut driver_state.mac_rx, | ||
| 92 | &driver_state.tx_data_channel, | ||
| 93 | &driver_state.tx_buf_channel, | ||
| 94 | &driver_state.mac_tx, | ||
| 95 | &mut driver_state.tx_buf_queue, | ||
| 96 | &driver_state.network_state, | ||
| 97 | short_address, | ||
| 98 | mac_address, | ||
| 99 | ), | ||
| 100 | Control::new( | ||
| 101 | &driver_state.rx_event_channel, | ||
| 102 | &driver_state.mac_tx, | ||
| 103 | &driver_state.network_state, | ||
| 104 | ), | ||
| 105 | ) | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | impl<'d> embassy_net_driver::Driver for Driver<'d> { | ||
| 110 | // type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; | ||
| 111 | // type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; | ||
| 112 | type RxToken<'a> | ||
| 113 | = RxToken<'d> | ||
| 114 | where | ||
| 115 | Self: 'a; | ||
| 116 | type TxToken<'a> | ||
| 117 | = TxToken<'d> | ||
| 118 | where | ||
| 119 | Self: 'a; | ||
| 120 | |||
| 121 | fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { | ||
| 122 | if self.rx_data_channel.poll_ready_to_receive(cx).is_ready() | ||
| 123 | && self.tx_buf_channel.poll_ready_to_receive(cx).is_ready() | ||
| 124 | { | ||
| 125 | Some(( | ||
| 126 | RxToken { | ||
| 127 | rx: self.rx_data_channel, | ||
| 128 | }, | ||
| 129 | TxToken { | ||
| 130 | tx: self.tx_data_channel, | ||
| 131 | tx_buf: self.tx_buf_channel, | ||
| 132 | }, | ||
| 133 | )) | ||
| 134 | } else { | ||
| 135 | None | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> { | ||
| 140 | if self.tx_buf_channel.poll_ready_to_receive(cx).is_ready() { | ||
| 141 | Some(TxToken { | ||
| 142 | tx: self.tx_data_channel, | ||
| 143 | tx_buf: self.tx_buf_channel, | ||
| 144 | }) | ||
| 145 | } else { | ||
| 146 | None | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | fn capabilities(&self) -> Capabilities { | ||
| 151 | let mut caps = Capabilities::default(); | ||
| 152 | caps.max_transmission_unit = MTU; | ||
| 153 | // caps.max_burst_size = Some(self.tx.len()); | ||
| 154 | caps | ||
| 155 | } | ||
| 156 | |||
| 157 | fn link_state(&mut self, cx: &mut Context) -> LinkState { | ||
| 158 | critical_section::with(|cs| { | ||
| 159 | let network_state = self.network_state.borrow(cs).borrow_mut(); | ||
| 160 | |||
| 161 | // Unconditionally register the waker to avoid a race | ||
| 162 | network_state.link_waker.register(cx.waker()); | ||
| 163 | network_state.link_state | ||
| 164 | }) | ||
| 165 | } | ||
| 166 | |||
| 167 | fn hardware_address(&self) -> HardwareAddress { | ||
| 168 | HardwareAddress::Ieee802154(critical_section::with(|cs| { | ||
| 169 | self.network_state.borrow(cs).borrow().mac_addr | ||
| 170 | })) | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | pub struct RxToken<'d> { | ||
| 175 | rx: &'d Channel<CriticalSectionRawMutex, MacEvent<'d>, 1>, | ||
| 176 | } | ||
| 177 | |||
| 178 | impl<'d> embassy_net_driver::RxToken for RxToken<'d> { | ||
| 179 | fn consume<R, F>(self, f: F) -> R | ||
| 180 | where | ||
| 181 | F: FnOnce(&mut [u8]) -> R, | ||
| 182 | { | ||
| 183 | let mut buffer = [0u8; MTU]; | ||
| 184 | match self.rx.try_receive().unwrap() { | ||
| 185 | MacEvent::McpsDataInd(data_event) => write_frame_from_data_indication(data_event, &mut buffer), | ||
| 186 | MacEvent::MlmeBeaconNotifyInd(data_event) => write_frame_from_beacon_indication(data_event, &mut buffer), | ||
| 187 | _ => {} | ||
| 188 | }; | ||
| 189 | |||
| 190 | f(&mut buffer[..]) | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | pub struct TxToken<'d> { | ||
| 195 | tx: &'d Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), BUF_SIZE>, | ||
| 196 | tx_buf: &'d Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], BUF_SIZE>, | ||
| 197 | } | ||
| 198 | |||
| 199 | impl<'d> embassy_net_driver::TxToken for TxToken<'d> { | ||
| 200 | fn consume<R, F>(self, len: usize, f: F) -> R | ||
| 201 | where | ||
| 202 | F: FnOnce(&mut [u8]) -> R, | ||
| 203 | { | ||
| 204 | // Only valid tx buffers should be put into the queue | ||
| 205 | let buf = self.tx_buf.try_receive().unwrap(); | ||
| 206 | let r = f(&mut buf[..len]); | ||
| 207 | |||
| 208 | // The tx channel should always be of equal capacity to the tx_buf channel | ||
| 209 | self.tx.try_send((buf, len)).unwrap(); | ||
| 210 | |||
| 211 | r | ||
| 212 | } | ||
| 213 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/mac/event.rs b/embassy-stm32-wpan/src/wb55/mac/event.rs new file mode 100644 index 000000000..39856e185 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/mac/event.rs | |||
| @@ -0,0 +1,153 @@ | |||
| 1 | use core::{mem, ptr}; | ||
| 2 | |||
| 3 | use super::indications::{ | ||
| 4 | AssociateIndication, BeaconNotifyIndication, CommStatusIndication, DataIndication, DisassociateIndication, | ||
| 5 | DpsIndication, GtsIndication, OrphanIndication, PollIndication, SyncLossIndication, | ||
| 6 | }; | ||
| 7 | use super::responses::{ | ||
| 8 | AssociateConfirm, CalibrateConfirm, DataConfirm, DisassociateConfirm, DpsConfirm, GetConfirm, GtsConfirm, | ||
| 9 | PollConfirm, PurgeConfirm, ResetConfirm, RxEnableConfirm, ScanConfirm, SetConfirm, SoundingConfirm, StartConfirm, | ||
| 10 | }; | ||
| 11 | use crate::evt::{EvtBox, MemoryManager}; | ||
| 12 | use crate::mac::opcodes::OpcodeM0ToM4; | ||
| 13 | use crate::sub::mac::{self, MacRx}; | ||
| 14 | |||
| 15 | pub(crate) trait ParseableMacEvent: Sized { | ||
| 16 | fn from_buffer<'a>(buf: &'a [u8]) -> Result<&'a Self, ()> { | ||
| 17 | if buf.len() < mem::size_of::<Self>() { | ||
| 18 | Err(()) | ||
| 19 | } else { | ||
| 20 | Ok(unsafe { &*(buf as *const _ as *const Self) }) | ||
| 21 | } | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 26 | #[derive(Debug)] | ||
| 27 | pub enum MacEvent<'a> { | ||
| 28 | MlmeAssociateCnf(&'a AssociateConfirm), | ||
| 29 | MlmeDisassociateCnf(&'a DisassociateConfirm), | ||
| 30 | MlmeGetCnf(&'a GetConfirm), | ||
| 31 | MlmeGtsCnf(&'a GtsConfirm), | ||
| 32 | MlmeResetCnf(&'a ResetConfirm), | ||
| 33 | MlmeRxEnableCnf(&'a RxEnableConfirm), | ||
| 34 | MlmeScanCnf(&'a ScanConfirm), | ||
| 35 | MlmeSetCnf(&'a SetConfirm), | ||
| 36 | MlmeStartCnf(&'a StartConfirm), | ||
| 37 | MlmePollCnf(&'a PollConfirm), | ||
| 38 | MlmeDpsCnf(&'a DpsConfirm), | ||
| 39 | MlmeSoundingCnf(&'a SoundingConfirm), | ||
| 40 | MlmeCalibrateCnf(&'a CalibrateConfirm), | ||
| 41 | McpsDataCnf(&'a DataConfirm), | ||
| 42 | McpsPurgeCnf(&'a PurgeConfirm), | ||
| 43 | MlmeAssociateInd(&'a AssociateIndication), | ||
| 44 | MlmeDisassociateInd(&'a DisassociateIndication), | ||
| 45 | MlmeBeaconNotifyInd(&'a BeaconNotifyIndication), | ||
| 46 | MlmeCommStatusInd(&'a CommStatusIndication), | ||
| 47 | MlmeGtsInd(&'a GtsIndication), | ||
| 48 | MlmeOrphanInd(&'a OrphanIndication), | ||
| 49 | MlmeSyncLossInd(&'a SyncLossIndication), | ||
| 50 | MlmeDpsInd(&'a DpsIndication), | ||
| 51 | McpsDataInd(&'a DataIndication), | ||
| 52 | MlmePollInd(&'a PollIndication), | ||
| 53 | } | ||
| 54 | |||
| 55 | impl<'a> MacEvent<'a> { | ||
| 56 | pub(crate) fn new(event_box: EvtBox<MacRx>) -> Result<Self, ()> { | ||
| 57 | let payload = event_box.payload(); | ||
| 58 | let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap()); | ||
| 59 | |||
| 60 | let opcode = OpcodeM0ToM4::try_from(opcode)?; | ||
| 61 | let buf = &payload[2..]; | ||
| 62 | |||
| 63 | // To avoid re-parsing the opcode, we store the result of the parse | ||
| 64 | // this requires use of unsafe because rust cannot assume that a reference will become | ||
| 65 | // invalid when the underlying result is moved. However, because we refer to a "heap" | ||
| 66 | // allocation, the underlying reference will not move until the struct is dropped. | ||
| 67 | |||
| 68 | let mac_event = match opcode { | ||
| 69 | OpcodeM0ToM4::MlmeAssociateCnf => { | ||
| 70 | MacEvent::MlmeAssociateCnf(unsafe { &*(AssociateConfirm::from_buffer(buf)? as *const _) }) | ||
| 71 | } | ||
| 72 | OpcodeM0ToM4::MlmeDisassociateCnf => { | ||
| 73 | MacEvent::MlmeDisassociateCnf(unsafe { &*(DisassociateConfirm::from_buffer(buf)? as *const _) }) | ||
| 74 | } | ||
| 75 | OpcodeM0ToM4::MlmeGetCnf => MacEvent::MlmeGetCnf(unsafe { &*(GetConfirm::from_buffer(buf)? as *const _) }), | ||
| 76 | OpcodeM0ToM4::MlmeGtsCnf => MacEvent::MlmeGtsCnf(unsafe { &*(GtsConfirm::from_buffer(buf)? as *const _) }), | ||
| 77 | OpcodeM0ToM4::MlmeResetCnf => { | ||
| 78 | MacEvent::MlmeResetCnf(unsafe { &*(ResetConfirm::from_buffer(buf)? as *const _) }) | ||
| 79 | } | ||
| 80 | OpcodeM0ToM4::MlmeRxEnableCnf => { | ||
| 81 | MacEvent::MlmeRxEnableCnf(unsafe { &*(RxEnableConfirm::from_buffer(buf)? as *const _) }) | ||
| 82 | } | ||
| 83 | OpcodeM0ToM4::MlmeScanCnf => { | ||
| 84 | MacEvent::MlmeScanCnf(unsafe { &*(ScanConfirm::from_buffer(buf)? as *const _) }) | ||
| 85 | } | ||
| 86 | OpcodeM0ToM4::MlmeSetCnf => MacEvent::MlmeSetCnf(unsafe { &*(SetConfirm::from_buffer(buf)? as *const _) }), | ||
| 87 | OpcodeM0ToM4::MlmeStartCnf => { | ||
| 88 | MacEvent::MlmeStartCnf(unsafe { &*(StartConfirm::from_buffer(buf)? as *const _) }) | ||
| 89 | } | ||
| 90 | OpcodeM0ToM4::MlmePollCnf => { | ||
| 91 | MacEvent::MlmePollCnf(unsafe { &*(PollConfirm::from_buffer(buf)? as *const _) }) | ||
| 92 | } | ||
| 93 | OpcodeM0ToM4::MlmeDpsCnf => MacEvent::MlmeDpsCnf(unsafe { &*(DpsConfirm::from_buffer(buf)? as *const _) }), | ||
| 94 | OpcodeM0ToM4::MlmeSoundingCnf => { | ||
| 95 | MacEvent::MlmeSoundingCnf(unsafe { &*(SoundingConfirm::from_buffer(buf)? as *const _) }) | ||
| 96 | } | ||
| 97 | OpcodeM0ToM4::MlmeCalibrateCnf => { | ||
| 98 | MacEvent::MlmeCalibrateCnf(unsafe { &*(CalibrateConfirm::from_buffer(buf)? as *const _) }) | ||
| 99 | } | ||
| 100 | OpcodeM0ToM4::McpsDataCnf => { | ||
| 101 | MacEvent::McpsDataCnf(unsafe { &*(DataConfirm::from_buffer(buf)? as *const _) }) | ||
| 102 | } | ||
| 103 | OpcodeM0ToM4::McpsPurgeCnf => { | ||
| 104 | MacEvent::McpsPurgeCnf(unsafe { &*(PurgeConfirm::from_buffer(buf)? as *const _) }) | ||
| 105 | } | ||
| 106 | OpcodeM0ToM4::MlmeAssociateInd => { | ||
| 107 | MacEvent::MlmeAssociateInd(unsafe { &*(AssociateIndication::from_buffer(buf)? as *const _) }) | ||
| 108 | } | ||
| 109 | OpcodeM0ToM4::MlmeDisassociateInd => { | ||
| 110 | MacEvent::MlmeDisassociateInd(unsafe { &*(DisassociateIndication::from_buffer(buf)? as *const _) }) | ||
| 111 | } | ||
| 112 | OpcodeM0ToM4::MlmeBeaconNotifyInd => { | ||
| 113 | MacEvent::MlmeBeaconNotifyInd(unsafe { &*(BeaconNotifyIndication::from_buffer(buf)? as *const _) }) | ||
| 114 | } | ||
| 115 | OpcodeM0ToM4::MlmeCommStatusInd => { | ||
| 116 | MacEvent::MlmeCommStatusInd(unsafe { &*(CommStatusIndication::from_buffer(buf)? as *const _) }) | ||
| 117 | } | ||
| 118 | OpcodeM0ToM4::MlmeGtsInd => { | ||
| 119 | MacEvent::MlmeGtsInd(unsafe { &*(GtsIndication::from_buffer(buf)? as *const _) }) | ||
| 120 | } | ||
| 121 | OpcodeM0ToM4::MlmeOrphanInd => { | ||
| 122 | MacEvent::MlmeOrphanInd(unsafe { &*(OrphanIndication::from_buffer(buf)? as *const _) }) | ||
| 123 | } | ||
| 124 | OpcodeM0ToM4::MlmeSyncLossInd => { | ||
| 125 | MacEvent::MlmeSyncLossInd(unsafe { &*(SyncLossIndication::from_buffer(buf)? as *const _) }) | ||
| 126 | } | ||
| 127 | OpcodeM0ToM4::MlmeDpsInd => { | ||
| 128 | MacEvent::MlmeDpsInd(unsafe { &*(DpsIndication::from_buffer(buf)? as *const _) }) | ||
| 129 | } | ||
| 130 | OpcodeM0ToM4::McpsDataInd => { | ||
| 131 | MacEvent::McpsDataInd(unsafe { &*(DataIndication::from_buffer(buf)? as *const _) }) | ||
| 132 | } | ||
| 133 | OpcodeM0ToM4::MlmePollInd => { | ||
| 134 | MacEvent::MlmePollInd(unsafe { &*(PollIndication::from_buffer(buf)? as *const _) }) | ||
| 135 | } | ||
| 136 | }; | ||
| 137 | |||
| 138 | // Forget the event box so that drop isn't called | ||
| 139 | // We want to handle the lifetime ourselves | ||
| 140 | |||
| 141 | mem::forget(event_box); | ||
| 142 | |||
| 143 | Ok(mac_event) | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | unsafe impl<'a> Send for MacEvent<'a> {} | ||
| 148 | |||
| 149 | impl<'a> Drop for MacEvent<'a> { | ||
| 150 | fn drop(&mut self) { | ||
| 151 | unsafe { mac::MacRx::drop_event_packet(ptr::null_mut()) }; | ||
| 152 | } | ||
| 153 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/mac/indications.rs b/embassy-stm32-wpan/src/wb55/mac/indications.rs new file mode 100644 index 000000000..5673514c9 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/mac/indications.rs | |||
| @@ -0,0 +1,300 @@ | |||
| 1 | use core::slice; | ||
| 2 | |||
| 3 | use smoltcp::wire::Ieee802154FrameType; | ||
| 4 | use smoltcp::wire::ieee802154::Frame; | ||
| 5 | |||
| 6 | use super::consts::MAX_PENDING_ADDRESS; | ||
| 7 | use super::event::ParseableMacEvent; | ||
| 8 | use super::typedefs::{ | ||
| 9 | AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor, | ||
| 10 | PanId, SecurityLevel, | ||
| 11 | }; | ||
| 12 | use crate::mac::typedefs::MacAddressAndMode; | ||
| 13 | |||
| 14 | /// MLME ASSOCIATE Indication which will be used by the MAC | ||
| 15 | /// to indicate the reception of an association request command | ||
| 16 | #[repr(C)] | ||
| 17 | #[derive(Debug)] | ||
| 18 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 19 | pub struct AssociateIndication { | ||
| 20 | /// Extended address of the device requesting association | ||
| 21 | pub device_address: [u8; 8], | ||
| 22 | /// Operational capabilities of the device requesting association | ||
| 23 | pub capability_information: Capabilities, | ||
| 24 | /// Security level purportedly used by the received MAC command frame | ||
| 25 | pub security_level: SecurityLevel, | ||
| 26 | /// The mode used to identify the key used by the originator of frame | ||
| 27 | pub key_id_mode: KeyIdMode, | ||
| 28 | /// Index of the key used by the originator of the received frame | ||
| 29 | pub key_index: u8, | ||
| 30 | /// The originator of the key used by the originator of the received frame | ||
| 31 | pub key_source: [u8; 8], | ||
| 32 | } | ||
| 33 | |||
| 34 | impl ParseableMacEvent for AssociateIndication {} | ||
| 35 | |||
| 36 | /// MLME DISASSOCIATE indication which will be used to send | ||
| 37 | /// disassociation indication to the application. | ||
| 38 | #[repr(C)] | ||
| 39 | #[derive(Debug)] | ||
| 40 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 41 | pub struct DisassociateIndication { | ||
| 42 | /// Extended address of the device requesting association | ||
| 43 | pub device_address: [u8; 8], | ||
| 44 | /// The reason for the disassociation | ||
| 45 | pub disassociation_reason: DisassociationReason, | ||
| 46 | /// The security level to be used | ||
| 47 | pub security_level: SecurityLevel, | ||
| 48 | /// The mode used to identify the key to be used | ||
| 49 | pub key_id_mode: KeyIdMode, | ||
| 50 | /// The index of the key to be used | ||
| 51 | pub key_index: u8, | ||
| 52 | /// The originator of the key to be used | ||
| 53 | pub key_source: [u8; 8], | ||
| 54 | } | ||
| 55 | |||
| 56 | impl ParseableMacEvent for DisassociateIndication {} | ||
| 57 | |||
| 58 | /// MLME BEACON NOTIIFY Indication which is used to send parameters contained | ||
| 59 | /// within a beacon frame received by the MAC to the application | ||
| 60 | #[repr(C)] | ||
| 61 | #[derive(Debug)] | ||
| 62 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 63 | pub struct BeaconNotifyIndication { | ||
| 64 | /// he set of octets comprising the beacon payload to be transferred | ||
| 65 | /// from the MAC sublayer entity to the next higher layer | ||
| 66 | pub sdu_ptr: *const u8, | ||
| 67 | /// The PAN Descriptor for the received beacon | ||
| 68 | pub pan_descriptor: PanDescriptor, | ||
| 69 | /// The list of addresses of the devices | ||
| 70 | pub addr_list: [MacAddress; MAX_PENDING_ADDRESS], | ||
| 71 | /// Beacon Sequence Number | ||
| 72 | pub bsn: u8, | ||
| 73 | /// The beacon pending address specification | ||
| 74 | pub pend_addr_spec: u8, | ||
| 75 | /// Number of octets contained in the beacon payload of the beacon frame | ||
| 76 | pub sdu_length: u8, | ||
| 77 | } | ||
| 78 | |||
| 79 | impl ParseableMacEvent for BeaconNotifyIndication {} | ||
| 80 | |||
| 81 | impl BeaconNotifyIndication { | ||
| 82 | pub fn payload<'a>(&'a self) -> &'a mut [u8] { | ||
| 83 | unsafe { slice::from_raw_parts_mut(self.sdu_ptr as *mut _, self.sdu_length as usize) } | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | pub fn write_frame_from_beacon_indication<'a, T: AsRef<[u8]> + AsMut<[u8]>>( | ||
| 88 | data: &'a BeaconNotifyIndication, | ||
| 89 | buffer: &'a mut T, | ||
| 90 | ) { | ||
| 91 | let mut frame = Frame::new_unchecked(buffer); | ||
| 92 | |||
| 93 | frame.set_frame_type(Ieee802154FrameType::Beacon); | ||
| 94 | frame.set_sequence_number(data.bsn); | ||
| 95 | } | ||
| 96 | |||
| 97 | /// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status | ||
| 98 | #[repr(C)] | ||
| 99 | #[derive(Debug)] | ||
| 100 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 101 | pub struct CommStatusIndication { | ||
| 102 | /// The 16-bit PAN identifier of the device from which the frame | ||
| 103 | /// was received or to which the frame was being sent | ||
| 104 | pub pan_id: PanId, | ||
| 105 | /// Source addressing mode | ||
| 106 | pub src_addr_mode: AddressMode, | ||
| 107 | /// Destination addressing mode | ||
| 108 | pub dst_addr_mode: AddressMode, | ||
| 109 | /// Source address | ||
| 110 | pub src_address: MacAddress, | ||
| 111 | /// Destination address | ||
| 112 | pub dst_address: MacAddress, | ||
| 113 | /// The communications status | ||
| 114 | pub status: MacStatus, | ||
| 115 | /// Security level to be used | ||
| 116 | pub security_level: SecurityLevel, | ||
| 117 | /// Mode used to identify the key to be used | ||
| 118 | pub key_id_mode: KeyIdMode, | ||
| 119 | /// Index of the key to be used | ||
| 120 | pub key_index: u8, | ||
| 121 | /// Originator of the key to be used | ||
| 122 | pub key_source: [u8; 8], | ||
| 123 | } | ||
| 124 | |||
| 125 | impl ParseableMacEvent for CommStatusIndication {} | ||
| 126 | |||
| 127 | /// MLME GTS Indication indicates that a GTS has been allocated or that a | ||
| 128 | /// previously allocated GTS has been deallocated | ||
| 129 | #[repr(C)] | ||
| 130 | #[derive(Debug)] | ||
| 131 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 132 | pub struct GtsIndication { | ||
| 133 | /// The short address of the device that has been allocated or deallocated a GTS | ||
| 134 | pub device_address: [u8; 2], | ||
| 135 | /// The characteristics of the GTS | ||
| 136 | pub gts_characteristics: u8, | ||
| 137 | /// Security level to be used | ||
| 138 | pub security_level: SecurityLevel, | ||
| 139 | /// Mode used to identify the key to be used | ||
| 140 | pub key_id_mode: KeyIdMode, | ||
| 141 | /// Index of the key to be used | ||
| 142 | pub key_index: u8, | ||
| 143 | /// byte stuffing to keep 32 bit alignment | ||
| 144 | a_stuffing: [u8; 2], | ||
| 145 | /// Originator of the key to be used | ||
| 146 | pub key_source: [u8; 8], | ||
| 147 | } | ||
| 148 | |||
| 149 | impl ParseableMacEvent for GtsIndication {} | ||
| 150 | |||
| 151 | /// MLME ORPHAN Indication which is used by the coordinator to notify the | ||
| 152 | /// application of the presence of an orphaned device | ||
| 153 | #[repr(C)] | ||
| 154 | #[derive(Debug)] | ||
| 155 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 156 | pub struct OrphanIndication { | ||
| 157 | /// Extended address of the orphaned device | ||
| 158 | pub orphan_address: [u8; 8], | ||
| 159 | /// Originator of the key used by the originator of the received frame | ||
| 160 | pub key_source: [u8; 8], | ||
| 161 | /// Security level purportedly used by the received MAC command frame | ||
| 162 | pub security_level: SecurityLevel, | ||
| 163 | /// Mode used to identify the key used by originator of received frame | ||
| 164 | pub key_id_mode: KeyIdMode, | ||
| 165 | /// Index of the key used by the originator of the received frame | ||
| 166 | pub key_index: u8, | ||
| 167 | /// byte stuffing to keep 32 bit alignment | ||
| 168 | a_stuffing: [u8; 1], | ||
| 169 | } | ||
| 170 | |||
| 171 | impl ParseableMacEvent for OrphanIndication {} | ||
| 172 | |||
| 173 | /// MLME SYNC LOSS Indication which is used by the MAC to indicate the loss | ||
| 174 | /// of synchronization with the coordinator | ||
| 175 | #[repr(C)] | ||
| 176 | #[derive(Debug)] | ||
| 177 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 178 | pub struct SyncLossIndication { | ||
| 179 | /// The PAN identifier with which the device lost synchronization or to which it was realigned | ||
| 180 | pub pan_id: PanId, | ||
| 181 | /// The reason that synchronization was lost | ||
| 182 | pub loss_reason: u8, | ||
| 183 | /// The logical channel on which the device lost synchronization or to whi | ||
| 184 | pub channel_number: MacChannel, | ||
| 185 | /// The channel page on which the device lost synchronization or to which | ||
| 186 | pub channel_page: u8, | ||
| 187 | /// The security level used by the received MAC frame | ||
| 188 | pub security_level: SecurityLevel, | ||
| 189 | /// Mode used to identify the key used by originator of received frame | ||
| 190 | pub key_id_mode: KeyIdMode, | ||
| 191 | /// Index of the key used by the originator of the received frame | ||
| 192 | pub key_index: u8, | ||
| 193 | /// Originator of the key used by the originator of the received frame | ||
| 194 | pub key_source: [u8; 8], | ||
| 195 | } | ||
| 196 | |||
| 197 | impl ParseableMacEvent for SyncLossIndication {} | ||
| 198 | |||
| 199 | /// MLME DPS Indication which indicates the expiration of the DPSIndexDuration | ||
| 200 | /// and the resetting of the DPS values in the PHY | ||
| 201 | #[repr(C)] | ||
| 202 | #[derive(Debug)] | ||
| 203 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 204 | pub struct DpsIndication { | ||
| 205 | /// byte stuffing to keep 32 bit alignment | ||
| 206 | a_stuffing: [u8; 4], | ||
| 207 | } | ||
| 208 | |||
| 209 | impl ParseableMacEvent for DpsIndication {} | ||
| 210 | |||
| 211 | #[repr(C)] | ||
| 212 | #[derive(Debug)] | ||
| 213 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 214 | pub struct DataIndication { | ||
| 215 | /// Pointer to the set of octets forming the MSDU being indicated | ||
| 216 | pub msdu_ptr: *const u8, | ||
| 217 | /// Source addressing mode used | ||
| 218 | pub src_addr_mode: AddressMode, | ||
| 219 | /// Source PAN ID | ||
| 220 | pub src_pan_id: PanId, | ||
| 221 | /// Source address | ||
| 222 | pub src_address: MacAddress, | ||
| 223 | /// Destination addressing mode used | ||
| 224 | pub dst_addr_mode: AddressMode, | ||
| 225 | /// Destination PAN ID | ||
| 226 | pub dst_pan_id: PanId, | ||
| 227 | /// Destination address | ||
| 228 | pub dst_address: MacAddress, | ||
| 229 | /// The number of octets contained in the MSDU being indicated | ||
| 230 | pub msdu_length: u8, | ||
| 231 | /// QI value measured during reception of the MPDU | ||
| 232 | pub mpdu_link_quality: u8, | ||
| 233 | /// The data sequence number of the received data frame | ||
| 234 | pub dsn: u8, | ||
| 235 | /// The time, in symbols, at which the data were received | ||
| 236 | pub time_stamp: [u8; 4], | ||
| 237 | /// The security level purportedly used by the received data frame | ||
| 238 | security_level: SecurityLevel, | ||
| 239 | /// Mode used to identify the key used by originator of received frame | ||
| 240 | key_id_mode: KeyIdMode, | ||
| 241 | /// The originator of the key | ||
| 242 | pub key_source: [u8; 8], | ||
| 243 | /// The index of the key | ||
| 244 | pub key_index: u8, | ||
| 245 | /// he pulse repetition value of the received PPDU | ||
| 246 | pub uwbprf: u8, | ||
| 247 | /// The preamble symbol repetitions of the UWB PHY frame | ||
| 248 | pub uwn_preamble_symbol_repetitions: u8, | ||
| 249 | /// Indicates the data rate | ||
| 250 | pub datrate: u8, | ||
| 251 | /// time units corresponding to an RMARKER at the antenna at the end of a ranging exchange, | ||
| 252 | pub ranging_received: u8, | ||
| 253 | pub ranging_counter_start: u32, | ||
| 254 | pub ranging_counter_stop: u32, | ||
| 255 | /// ime units in a message exchange over which the tracking offset was measured | ||
| 256 | pub ranging_tracking_interval: u32, | ||
| 257 | /// time units slipped or advanced by the radio tracking system | ||
| 258 | pub ranging_offset: u32, | ||
| 259 | /// The FoM characterizing the ranging measurement | ||
| 260 | pub ranging_fom: u8, | ||
| 261 | /// The Received Signal Strength Indicator measured | ||
| 262 | pub rssi: u8, | ||
| 263 | } | ||
| 264 | |||
| 265 | impl ParseableMacEvent for DataIndication {} | ||
| 266 | |||
| 267 | impl DataIndication { | ||
| 268 | pub fn payload<'a>(&'a self) -> &'a mut [u8] { | ||
| 269 | unsafe { slice::from_raw_parts_mut(self.msdu_ptr as *mut _, self.msdu_length as usize) } | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 273 | pub fn write_frame_from_data_indication<'a, T: AsRef<[u8]> + AsMut<[u8]>>(data: &'a DataIndication, buffer: &'a mut T) { | ||
| 274 | let mut frame = Frame::new_unchecked(buffer); | ||
| 275 | |||
| 276 | frame.set_frame_type(Ieee802154FrameType::Data); | ||
| 277 | frame.set_src_addr(MacAddressAndMode(data.src_address, data.src_addr_mode).into()); | ||
| 278 | frame.set_dst_addr(MacAddressAndMode(data.dst_address, data.dst_addr_mode).into()); | ||
| 279 | frame.set_dst_pan_id(data.dst_pan_id.into()); | ||
| 280 | frame.set_src_pan_id(data.src_pan_id.into()); | ||
| 281 | frame.set_sequence_number(data.dsn); | ||
| 282 | frame.set_security_enabled(data.security_level == SecurityLevel::Secured); | ||
| 283 | |||
| 284 | // No way around the copy with the current API | ||
| 285 | frame.payload_mut().unwrap().copy_from_slice(data.payload()); | ||
| 286 | } | ||
| 287 | |||
| 288 | /// MLME POLL Indication which will be used for indicating the Data Request | ||
| 289 | /// reception to upper layer as defined in Zigbee r22 - D.8.2 | ||
| 290 | #[repr(C)] | ||
| 291 | #[derive(Debug)] | ||
| 292 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 293 | pub struct PollIndication { | ||
| 294 | /// addressing mode used | ||
| 295 | pub addr_mode: AddressMode, | ||
| 296 | /// Poll requester address | ||
| 297 | pub request_address: MacAddress, | ||
| 298 | } | ||
| 299 | |||
| 300 | impl ParseableMacEvent for PollIndication {} | ||
diff --git a/embassy-stm32-wpan/src/wb55/mac/macros.rs b/embassy-stm32-wpan/src/wb55/mac/macros.rs new file mode 100644 index 000000000..1a988a779 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/mac/macros.rs | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | #[macro_export] | ||
| 2 | macro_rules! numeric_enum { | ||
| 3 | (#[repr($repr:ident)] | ||
| 4 | $(#$attrs:tt)* $vis:vis enum $name:ident { | ||
| 5 | $($(#$enum_attrs:tt)* $enum:ident = $constant:expr),* $(,)? | ||
| 6 | } ) => { | ||
| 7 | #[repr($repr)] | ||
| 8 | $(#$attrs)* | ||
| 9 | $vis enum $name { | ||
| 10 | $($(#$enum_attrs)* $enum = $constant),* | ||
| 11 | } | ||
| 12 | |||
| 13 | impl ::core::convert::TryFrom<$repr> for $name { | ||
| 14 | type Error = (); | ||
| 15 | |||
| 16 | fn try_from(value: $repr) -> ::core::result::Result<Self, ()> { | ||
| 17 | match value { | ||
| 18 | $($constant => Ok( $name :: $enum ),)* | ||
| 19 | _ => Err(()) | ||
| 20 | } | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | impl ::core::convert::From<$name> for $repr { | ||
| 25 | fn from(value: $name) -> $repr { | ||
| 26 | match value { | ||
| 27 | $($name :: $enum => $constant,)* | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | } | ||
| 32 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/mac/mod.rs b/embassy-stm32-wpan/src/wb55/mac/mod.rs new file mode 100644 index 000000000..ac50a6b29 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/mac/mod.rs | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | pub mod commands; | ||
| 2 | mod consts; | ||
| 3 | pub mod control; | ||
| 4 | mod driver; | ||
| 5 | pub mod event; | ||
| 6 | pub mod indications; | ||
| 7 | mod macros; | ||
| 8 | mod opcodes; | ||
| 9 | pub mod responses; | ||
| 10 | pub mod runner; | ||
| 11 | pub mod typedefs; | ||
| 12 | |||
| 13 | pub use crate::mac::control::Control; | ||
| 14 | pub use crate::mac::driver::{Driver, DriverState}; | ||
| 15 | pub use crate::mac::runner::Runner; | ||
| 16 | |||
| 17 | const MTU: usize = 127; | ||
diff --git a/embassy-stm32-wpan/src/wb55/mac/opcodes.rs b/embassy-stm32-wpan/src/wb55/mac/opcodes.rs new file mode 100644 index 000000000..fd7011873 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/mac/opcodes.rs | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | const ST_VENDOR_OGF: u16 = 0x3F; | ||
| 2 | const MAC_802_15_4_CMD_OPCODE_OFFSET: u16 = 0x280; | ||
| 3 | |||
| 4 | const fn opcode(ocf: u16) -> isize { | ||
| 5 | ((ST_VENDOR_OGF << 9) | (MAC_802_15_4_CMD_OPCODE_OFFSET + ocf)) as isize | ||
| 6 | } | ||
| 7 | |||
| 8 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 9 | pub enum OpcodeM4ToM0 { | ||
| 10 | MlmeAssociateReq = opcode(0x00), | ||
| 11 | MlmeAssociateRes = opcode(0x01), | ||
| 12 | MlmeDisassociateReq = opcode(0x02), | ||
| 13 | MlmeGetReq = opcode(0x03), | ||
| 14 | MlmeGtsReq = opcode(0x04), | ||
| 15 | MlmeOrphanRes = opcode(0x05), | ||
| 16 | MlmeResetReq = opcode(0x06), | ||
| 17 | MlmeRxEnableReq = opcode(0x07), | ||
| 18 | MlmeScanReq = opcode(0x08), | ||
| 19 | MlmeSetReq = opcode(0x09), | ||
| 20 | MlmeStartReq = opcode(0x0A), | ||
| 21 | MlmeSyncReq = opcode(0x0B), | ||
| 22 | MlmePollReq = opcode(0x0C), | ||
| 23 | MlmeDpsReq = opcode(0x0D), | ||
| 24 | MlmeSoundingReq = opcode(0x0E), | ||
| 25 | MlmeCalibrateReq = opcode(0x0F), | ||
| 26 | McpsDataReq = opcode(0x10), | ||
| 27 | McpsPurgeReq = opcode(0x11), | ||
| 28 | } | ||
| 29 | |||
| 30 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 31 | pub enum OpcodeM0ToM4 { | ||
| 32 | MlmeAssociateCnf = 0x00, | ||
| 33 | MlmeDisassociateCnf, | ||
| 34 | MlmeGetCnf, | ||
| 35 | MlmeGtsCnf, | ||
| 36 | MlmeResetCnf, | ||
| 37 | MlmeRxEnableCnf, | ||
| 38 | MlmeScanCnf, | ||
| 39 | MlmeSetCnf, | ||
| 40 | MlmeStartCnf, | ||
| 41 | MlmePollCnf, | ||
| 42 | MlmeDpsCnf, | ||
| 43 | MlmeSoundingCnf, | ||
| 44 | MlmeCalibrateCnf, | ||
| 45 | McpsDataCnf, | ||
| 46 | McpsPurgeCnf, | ||
| 47 | MlmeAssociateInd, | ||
| 48 | MlmeDisassociateInd, | ||
| 49 | MlmeBeaconNotifyInd, | ||
| 50 | MlmeCommStatusInd, | ||
| 51 | MlmeGtsInd, | ||
| 52 | MlmeOrphanInd, | ||
| 53 | MlmeSyncLossInd, | ||
| 54 | MlmeDpsInd, | ||
| 55 | McpsDataInd, | ||
| 56 | MlmePollInd, | ||
| 57 | } | ||
| 58 | |||
| 59 | impl TryFrom<u16> for OpcodeM0ToM4 { | ||
| 60 | type Error = (); | ||
| 61 | |||
| 62 | fn try_from(value: u16) -> Result<Self, Self::Error> { | ||
| 63 | match value { | ||
| 64 | 0 => Ok(Self::MlmeAssociateCnf), | ||
| 65 | 1 => Ok(Self::MlmeDisassociateCnf), | ||
| 66 | 2 => Ok(Self::MlmeGetCnf), | ||
| 67 | 3 => Ok(Self::MlmeGtsCnf), | ||
| 68 | 4 => Ok(Self::MlmeResetCnf), | ||
| 69 | 5 => Ok(Self::MlmeRxEnableCnf), | ||
| 70 | 6 => Ok(Self::MlmeScanCnf), | ||
| 71 | 7 => Ok(Self::MlmeSetCnf), | ||
| 72 | 8 => Ok(Self::MlmeStartCnf), | ||
| 73 | 9 => Ok(Self::MlmePollCnf), | ||
| 74 | 10 => Ok(Self::MlmeDpsCnf), | ||
| 75 | 11 => Ok(Self::MlmeSoundingCnf), | ||
| 76 | 12 => Ok(Self::MlmeCalibrateCnf), | ||
| 77 | 13 => Ok(Self::McpsDataCnf), | ||
| 78 | 14 => Ok(Self::McpsPurgeCnf), | ||
| 79 | 15 => Ok(Self::MlmeAssociateInd), | ||
| 80 | 16 => Ok(Self::MlmeDisassociateInd), | ||
| 81 | 17 => Ok(Self::MlmeBeaconNotifyInd), | ||
| 82 | 18 => Ok(Self::MlmeCommStatusInd), | ||
| 83 | 19 => Ok(Self::MlmeGtsInd), | ||
| 84 | 20 => Ok(Self::MlmeOrphanInd), | ||
| 85 | 21 => Ok(Self::MlmeSyncLossInd), | ||
| 86 | 22 => Ok(Self::MlmeDpsInd), | ||
| 87 | 23 => Ok(Self::McpsDataInd), | ||
| 88 | 24 => Ok(Self::MlmePollInd), | ||
| 89 | _ => Err(()), | ||
| 90 | } | ||
| 91 | } | ||
| 92 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/mac/responses.rs b/embassy-stm32-wpan/src/wb55/mac/responses.rs new file mode 100644 index 000000000..544fdaae8 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/mac/responses.rs | |||
| @@ -0,0 +1,273 @@ | |||
| 1 | use super::consts::{MAX_ED_SCAN_RESULTS_SUPPORTED, MAX_PAN_DESC_SUPPORTED, MAX_SOUNDING_LIST_SUPPORTED}; | ||
| 2 | use super::event::ParseableMacEvent; | ||
| 3 | use super::typedefs::{ | ||
| 4 | AddressMode, AssociationStatus, KeyIdMode, MacAddress, MacStatus, PanDescriptor, PanId, PibId, ScanType, | ||
| 5 | SecurityLevel, | ||
| 6 | }; | ||
| 7 | |||
| 8 | /// MLME ASSOCIATE Confirm used to inform of the initiating device whether | ||
| 9 | /// its request to associate was successful or unsuccessful | ||
| 10 | #[repr(C)] | ||
| 11 | #[derive(Debug)] | ||
| 12 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 13 | pub struct AssociateConfirm { | ||
| 14 | /// short address allocated by the coordinator on successful association | ||
| 15 | pub assoc_short_address: [u8; 2], | ||
| 16 | /// status of the association request | ||
| 17 | pub status: AssociationStatus, | ||
| 18 | /// security level to be used | ||
| 19 | pub security_level: SecurityLevel, | ||
| 20 | /// the originator of the key to be used | ||
| 21 | pub key_source: [u8; 8], | ||
| 22 | /// the mode used to identify the key to be used | ||
| 23 | pub key_id_mode: KeyIdMode, | ||
| 24 | /// the index of the key to be used | ||
| 25 | pub key_index: u8, | ||
| 26 | /// byte stuffing to keep 32 bit alignment | ||
| 27 | a_stuffing: [u8; 2], | ||
| 28 | } | ||
| 29 | |||
| 30 | impl ParseableMacEvent for AssociateConfirm {} | ||
| 31 | |||
| 32 | /// MLME DISASSOCIATE Confirm used to send disassociation Confirmation to the application. | ||
| 33 | #[repr(C)] | ||
| 34 | #[derive(Debug)] | ||
| 35 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 36 | pub struct DisassociateConfirm { | ||
| 37 | /// status of the disassociation attempt | ||
| 38 | pub status: MacStatus, | ||
| 39 | /// device addressing mode used | ||
| 40 | pub device_addr_mode: AddressMode, | ||
| 41 | /// the identifier of the PAN of the device | ||
| 42 | pub device_pan_id: PanId, | ||
| 43 | /// device address | ||
| 44 | pub device_address: MacAddress, | ||
| 45 | } | ||
| 46 | |||
| 47 | impl ParseableMacEvent for DisassociateConfirm {} | ||
| 48 | |||
| 49 | /// MLME GET Confirm which requests information about a given PIB attribute | ||
| 50 | #[repr(C)] | ||
| 51 | #[derive(Debug)] | ||
| 52 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 53 | pub struct GetConfirm { | ||
| 54 | /// The pointer to the value of the PIB attribute attempted to read | ||
| 55 | pub pib_attribute_value_ptr: *const u8, | ||
| 56 | /// Status of the GET attempt | ||
| 57 | pub status: MacStatus, | ||
| 58 | /// The name of the PIB attribute attempted to read | ||
| 59 | pub pib_attribute: PibId, | ||
| 60 | /// The lenght of the PIB attribute Value return | ||
| 61 | pub pib_attribute_value_len: u8, | ||
| 62 | /// byte stuffing to keep 32 bit alignment | ||
| 63 | a_stuffing: [u8; 1], | ||
| 64 | } | ||
| 65 | |||
| 66 | impl ParseableMacEvent for GetConfirm {} | ||
| 67 | |||
| 68 | /// MLME GTS Confirm which eports the results of a request to allocate a new GTS | ||
| 69 | /// or to deallocate an existing GTS | ||
| 70 | #[repr(C)] | ||
| 71 | #[derive(Debug)] | ||
| 72 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 73 | pub struct GtsConfirm { | ||
| 74 | /// The characteristics of the GTS | ||
| 75 | pub gts_characteristics: u8, | ||
| 76 | /// The status of the GTS reques | ||
| 77 | pub status: MacStatus, | ||
| 78 | /// byte stuffing to keep 32 bit alignment | ||
| 79 | a_stuffing: [u8; 2], | ||
| 80 | } | ||
| 81 | |||
| 82 | impl ParseableMacEvent for GtsConfirm {} | ||
| 83 | |||
| 84 | /// MLME RESET Confirm which is used to report the results of the reset operation | ||
| 85 | #[repr(C)] | ||
| 86 | #[derive(Debug)] | ||
| 87 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 88 | pub struct ResetConfirm { | ||
| 89 | /// The result of the reset operation | ||
| 90 | pub status: MacStatus, | ||
| 91 | /// byte stuffing to keep 32 bit alignment | ||
| 92 | a_stuffing: [u8; 3], | ||
| 93 | } | ||
| 94 | |||
| 95 | impl ParseableMacEvent for ResetConfirm {} | ||
| 96 | |||
| 97 | /// MLME RX ENABLE Confirm which is used to report the results of the attempt | ||
| 98 | /// to enable or disable the receiver | ||
| 99 | #[repr(C)] | ||
| 100 | #[derive(Debug)] | ||
| 101 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 102 | pub struct RxEnableConfirm { | ||
| 103 | /// Result of the request to enable or disable the receiver | ||
| 104 | pub status: MacStatus, | ||
| 105 | /// byte stuffing to keep 32 bit alignment | ||
| 106 | a_stuffing: [u8; 3], | ||
| 107 | } | ||
| 108 | |||
| 109 | impl ParseableMacEvent for RxEnableConfirm {} | ||
| 110 | |||
| 111 | /// MLME SCAN Confirm which is used to report the result of the channel scan request | ||
| 112 | #[repr(C)] | ||
| 113 | #[derive(Debug)] | ||
| 114 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 115 | pub struct ScanConfirm { | ||
| 116 | /// Status of the scan request | ||
| 117 | pub status: MacStatus, | ||
| 118 | /// The type of scan performed | ||
| 119 | pub scan_type: ScanType, | ||
| 120 | /// Channel page on which the scan was performed | ||
| 121 | pub channel_page: u8, | ||
| 122 | /// Channels given in the request which were not scanned | ||
| 123 | pub unscanned_channels: [u8; 4], | ||
| 124 | /// Number of elements returned in the appropriate result lists | ||
| 125 | pub result_list_size: u8, | ||
| 126 | /// List of energy measurements | ||
| 127 | pub energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED], | ||
| 128 | /// List of PAN descriptors | ||
| 129 | pub pan_descriptor_list: [PanDescriptor; MAX_PAN_DESC_SUPPORTED], | ||
| 130 | /// Categorization of energy detected in channel | ||
| 131 | pub detected_category: u8, | ||
| 132 | /// For UWB PHYs, the list of energy measurements taken | ||
| 133 | pub uwb_energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED], | ||
| 134 | } | ||
| 135 | |||
| 136 | impl ParseableMacEvent for ScanConfirm {} | ||
| 137 | |||
| 138 | /// MLME SET Confirm which reports the result of an attempt to write a value to a PIB attribute | ||
| 139 | #[repr(C)] | ||
| 140 | #[derive(Debug)] | ||
| 141 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 142 | pub struct SetConfirm { | ||
| 143 | /// The result of the set operation | ||
| 144 | pub status: MacStatus, | ||
| 145 | /// The name of the PIB attribute that was written | ||
| 146 | pub pin_attribute: PibId, | ||
| 147 | /// byte stuffing to keep 32 bit alignment | ||
| 148 | a_stuffing: [u8; 2], | ||
| 149 | } | ||
| 150 | |||
| 151 | impl ParseableMacEvent for SetConfirm {} | ||
| 152 | |||
| 153 | /// MLME START Confirm which is used to report the results of the attempt to | ||
| 154 | /// start using a new superframe configuration | ||
| 155 | #[repr(C)] | ||
| 156 | #[derive(Debug)] | ||
| 157 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 158 | pub struct StartConfirm { | ||
| 159 | /// Result of the attempt to start using an updated superframe configuration | ||
| 160 | pub status: MacStatus, | ||
| 161 | /// byte stuffing to keep 32 bit alignment | ||
| 162 | a_stuffing: [u8; 3], | ||
| 163 | } | ||
| 164 | |||
| 165 | impl ParseableMacEvent for StartConfirm {} | ||
| 166 | |||
| 167 | /// MLME POLL Confirm which is used to report the result of a request to poll the coordinator for data | ||
| 168 | #[repr(C)] | ||
| 169 | #[derive(Debug)] | ||
| 170 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 171 | pub struct PollConfirm { | ||
| 172 | /// The status of the data request | ||
| 173 | pub status: MacStatus, | ||
| 174 | /// byte stuffing to keep 32 bit alignment | ||
| 175 | a_stuffing: [u8; 3], | ||
| 176 | } | ||
| 177 | |||
| 178 | impl ParseableMacEvent for PollConfirm {} | ||
| 179 | |||
| 180 | /// MLME DPS Confirm which reports the results of the attempt to enable or disable the DPS | ||
| 181 | #[repr(C)] | ||
| 182 | #[derive(Debug)] | ||
| 183 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 184 | pub struct DpsConfirm { | ||
| 185 | /// The status of the DPS request | ||
| 186 | pub status: MacStatus, | ||
| 187 | /// byte stuffing to keep 32 bit alignment | ||
| 188 | a_stuffing: [u8; 3], | ||
| 189 | } | ||
| 190 | |||
| 191 | impl ParseableMacEvent for DpsConfirm {} | ||
| 192 | |||
| 193 | /// MLME SOUNDING Confirm which reports the result of a request to the PHY to provide | ||
| 194 | /// channel sounding information | ||
| 195 | #[repr(C)] | ||
| 196 | #[derive(Debug)] | ||
| 197 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 198 | pub struct SoundingConfirm { | ||
| 199 | /// Results of the sounding measurement | ||
| 200 | pub sounding_list: [u8; MAX_SOUNDING_LIST_SUPPORTED], | ||
| 201 | |||
| 202 | status: u8, | ||
| 203 | } | ||
| 204 | |||
| 205 | impl ParseableMacEvent for SoundingConfirm {} | ||
| 206 | |||
| 207 | /// MLME CALIBRATE Confirm which reports the result of a request to the PHY | ||
| 208 | /// to provide internal propagation path information | ||
| 209 | #[repr(C)] | ||
| 210 | #[derive(Debug)] | ||
| 211 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 212 | pub struct CalibrateConfirm { | ||
| 213 | /// The status of the attempt to return sounding data | ||
| 214 | pub status: MacStatus, | ||
| 215 | /// byte stuffing to keep 32 bit alignment | ||
| 216 | a_stuffing: [u8; 3], | ||
| 217 | /// A count of the propagation time from the ranging counter | ||
| 218 | /// to the transmit antenna | ||
| 219 | pub cal_tx_rmaker_offset: u32, | ||
| 220 | /// A count of the propagation time from the receive antenna | ||
| 221 | /// to the ranging counter | ||
| 222 | pub cal_rx_rmaker_offset: u32, | ||
| 223 | } | ||
| 224 | |||
| 225 | impl ParseableMacEvent for CalibrateConfirm {} | ||
| 226 | |||
| 227 | /// MCPS DATA Confirm which will be used for reporting the results of | ||
| 228 | /// MAC data related requests from the application | ||
| 229 | #[repr(C)] | ||
| 230 | #[derive(Debug)] | ||
| 231 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 232 | pub struct DataConfirm { | ||
| 233 | /// The handle associated with the MSDU being confirmed | ||
| 234 | pub msdu_handle: u8, | ||
| 235 | /// The time, in symbols, at which the data were transmitted | ||
| 236 | pub time_stamp: [u8; 4], | ||
| 237 | /// ranging status | ||
| 238 | pub ranging_received: u8, | ||
| 239 | /// The status of the last MSDU transmission | ||
| 240 | pub status: MacStatus, | ||
| 241 | /// time units corresponding to an RMARKER at the antenna at | ||
| 242 | /// the beginning of a ranging exchange | ||
| 243 | pub ranging_counter_start: u32, | ||
| 244 | /// time units corresponding to an RMARKER at the antenna | ||
| 245 | /// at the end of a ranging exchange | ||
| 246 | pub ranging_counter_stop: u32, | ||
| 247 | /// time units in a message exchange over which the tracking offset was measured | ||
| 248 | pub ranging_tracking_interval: u32, | ||
| 249 | /// time units slipped or advanced by the radio tracking system | ||
| 250 | pub ranging_offset: u32, | ||
| 251 | /// The FoM characterizing the ranging measurement | ||
| 252 | pub ranging_fom: u8, | ||
| 253 | /// byte stuffing to keep 32 bit alignment | ||
| 254 | a_stuffing: [u8; 3], | ||
| 255 | } | ||
| 256 | |||
| 257 | impl ParseableMacEvent for DataConfirm {} | ||
| 258 | |||
| 259 | /// MCPS PURGE Confirm which will be used by the MAC to notify the application of | ||
| 260 | /// the status of its request to purge an MSDU from the transaction queue | ||
| 261 | #[repr(C)] | ||
| 262 | #[derive(Debug)] | ||
| 263 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 264 | pub struct PurgeConfirm { | ||
| 265 | /// Handle associated with the MSDU requested to be purged from the transaction queue | ||
| 266 | pub msdu_handle: u8, | ||
| 267 | /// The status of the request | ||
| 268 | pub status: MacStatus, | ||
| 269 | /// byte stuffing to keep 32 bit alignment | ||
| 270 | a_stuffing: [u8; 2], | ||
| 271 | } | ||
| 272 | |||
| 273 | impl ParseableMacEvent for PurgeConfirm {} | ||
diff --git a/embassy-stm32-wpan/src/wb55/mac/runner.rs b/embassy-stm32-wpan/src/wb55/mac/runner.rs new file mode 100644 index 000000000..3b7d895df --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/mac/runner.rs | |||
| @@ -0,0 +1,151 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 2 | |||
| 3 | use embassy_futures::join; | ||
| 4 | use embassy_sync::blocking_mutex; | ||
| 5 | use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; | ||
| 6 | use embassy_sync::channel::Channel; | ||
| 7 | use embassy_sync::mutex::Mutex; | ||
| 8 | use embassy_sync::signal::Signal; | ||
| 9 | use smoltcp::wire::Ieee802154FrameType; | ||
| 10 | use smoltcp::wire::ieee802154::Frame; | ||
| 11 | |||
| 12 | use crate::mac::MTU; | ||
| 13 | use crate::mac::commands::*; | ||
| 14 | use crate::mac::driver::NetworkState; | ||
| 15 | use crate::mac::event::MacEvent; | ||
| 16 | use crate::sub::mac::{MacRx, MacTx}; | ||
| 17 | |||
| 18 | pub type ZeroCopyPubSub<M, T> = blocking_mutex::Mutex<M, RefCell<Option<Signal<NoopRawMutex, T>>>>; | ||
| 19 | |||
| 20 | pub const BUF_SIZE: usize = 3; | ||
| 21 | |||
| 22 | pub struct Runner<'a> { | ||
| 23 | // rx event backpressure is already provided through the MacEvent drop mechanism | ||
| 24 | // therefore, we don't need to worry about overwriting events | ||
| 25 | rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>, | ||
| 26 | rx_data_channel: &'a Channel<CriticalSectionRawMutex, MacEvent<'a>, 1>, | ||
| 27 | mac_rx: Mutex<NoopRawMutex, &'a mut MacRx<'a>>, | ||
| 28 | |||
| 29 | tx_data_channel: &'a Channel<CriticalSectionRawMutex, (&'a mut [u8; MTU], usize), BUF_SIZE>, | ||
| 30 | tx_buf_channel: &'a Channel<CriticalSectionRawMutex, &'a mut [u8; MTU], BUF_SIZE>, | ||
| 31 | mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx<'a>>, | ||
| 32 | |||
| 33 | #[allow(unused)] | ||
| 34 | network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>, | ||
| 35 | } | ||
| 36 | |||
| 37 | impl<'a> Runner<'a> { | ||
| 38 | pub(crate) fn new( | ||
| 39 | rx_event_channel: &'a ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>, | ||
| 40 | rx_data_channel: &'a Channel<CriticalSectionRawMutex, MacEvent<'a>, 1>, | ||
| 41 | mac_rx: &'a mut MacRx<'a>, | ||
| 42 | tx_data_channel: &'a Channel<CriticalSectionRawMutex, (&'a mut [u8; MTU], usize), BUF_SIZE>, | ||
| 43 | tx_buf_channel: &'a Channel<CriticalSectionRawMutex, &'a mut [u8; MTU], BUF_SIZE>, | ||
| 44 | mac_tx: &'a Mutex<CriticalSectionRawMutex, MacTx<'a>>, | ||
| 45 | tx_buf_queue: &'a mut [[u8; MTU]; BUF_SIZE], | ||
| 46 | network_state: &'a blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<NetworkState>>, | ||
| 47 | short_address: [u8; 2], | ||
| 48 | mac_address: [u8; 8], | ||
| 49 | ) -> Self { | ||
| 50 | for buf in tx_buf_queue { | ||
| 51 | tx_buf_channel.try_send(buf).unwrap(); | ||
| 52 | } | ||
| 53 | |||
| 54 | critical_section::with(|cs| { | ||
| 55 | let mut network_state = network_state.borrow(cs).borrow_mut(); | ||
| 56 | |||
| 57 | network_state.mac_addr = mac_address; | ||
| 58 | network_state.short_addr = short_address; | ||
| 59 | }); | ||
| 60 | |||
| 61 | Self { | ||
| 62 | rx_event_channel, | ||
| 63 | rx_data_channel, | ||
| 64 | mac_rx: Mutex::new(mac_rx), | ||
| 65 | tx_data_channel, | ||
| 66 | tx_buf_channel, | ||
| 67 | mac_tx, | ||
| 68 | network_state, | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | async fn send_request<T: MacCommand, U: TryInto<T>>(&self, frame: U) -> Result<(), ()> | ||
| 73 | where | ||
| 74 | (): From<<U as TryInto<T>>::Error>, | ||
| 75 | { | ||
| 76 | let request: T = frame.try_into()?; | ||
| 77 | self.mac_tx.lock().await.send_command(&request).await.map_err(|_| ())?; | ||
| 78 | |||
| 79 | Ok(()) | ||
| 80 | } | ||
| 81 | |||
| 82 | pub async fn run(&'a self) -> ! { | ||
| 83 | join::join( | ||
| 84 | async { | ||
| 85 | loop { | ||
| 86 | if let Ok(mac_event) = self.mac_rx.try_lock().unwrap().read().await { | ||
| 87 | match mac_event { | ||
| 88 | MacEvent::MlmeAssociateCnf(_) | ||
| 89 | | MacEvent::MlmeDisassociateCnf(_) | ||
| 90 | | MacEvent::MlmeGetCnf(_) | ||
| 91 | | MacEvent::MlmeGtsCnf(_) | ||
| 92 | | MacEvent::MlmeResetCnf(_) | ||
| 93 | | MacEvent::MlmeRxEnableCnf(_) | ||
| 94 | | MacEvent::MlmeScanCnf(_) | ||
| 95 | | MacEvent::MlmeSetCnf(_) | ||
| 96 | | MacEvent::MlmeStartCnf(_) | ||
| 97 | | MacEvent::MlmePollCnf(_) | ||
| 98 | | MacEvent::MlmeDpsCnf(_) | ||
| 99 | | MacEvent::MlmeSoundingCnf(_) | ||
| 100 | | MacEvent::MlmeCalibrateCnf(_) | ||
| 101 | | MacEvent::McpsDataCnf(_) | ||
| 102 | | MacEvent::McpsPurgeCnf(_) => { | ||
| 103 | self.rx_event_channel.lock(|s| { | ||
| 104 | s.borrow().as_ref().map(|signal| signal.signal(mac_event)); | ||
| 105 | }); | ||
| 106 | } | ||
| 107 | MacEvent::McpsDataInd(_) => { | ||
| 108 | // Pattern should match driver | ||
| 109 | self.rx_data_channel.send(mac_event).await; | ||
| 110 | } | ||
| 111 | _ => { | ||
| 112 | debug!("unhandled mac event: {:#x}", mac_event); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | }, | ||
| 118 | async { | ||
| 119 | loop { | ||
| 120 | let (buf, _) = self.tx_data_channel.receive().await; | ||
| 121 | |||
| 122 | // Smoltcp has created this frame, so there's no need to reparse it. | ||
| 123 | let frame = Frame::new_unchecked(&buf); | ||
| 124 | |||
| 125 | let result: Result<(), ()> = match frame.frame_type() { | ||
| 126 | Ieee802154FrameType::Beacon => Err(()), | ||
| 127 | Ieee802154FrameType::Data => self.send_request::<DataRequest, _>(frame).await, | ||
| 128 | Ieee802154FrameType::Acknowledgement => Err(()), | ||
| 129 | Ieee802154FrameType::MacCommand => Err(()), | ||
| 130 | Ieee802154FrameType::Multipurpose => Err(()), | ||
| 131 | Ieee802154FrameType::FragmentOrFrak => Err(()), | ||
| 132 | Ieee802154FrameType::Extended => Err(()), | ||
| 133 | _ => Err(()), | ||
| 134 | }; | ||
| 135 | |||
| 136 | if result.is_err() { | ||
| 137 | debug!("failed to parse mac frame"); | ||
| 138 | } else { | ||
| 139 | trace!("data frame sent!"); | ||
| 140 | } | ||
| 141 | |||
| 142 | // The tx channel should always be of equal capacity to the tx_buf channel | ||
| 143 | self.tx_buf_channel.try_send(buf).unwrap(); | ||
| 144 | } | ||
| 145 | }, | ||
| 146 | ) | ||
| 147 | .await; | ||
| 148 | |||
| 149 | loop {} | ||
| 150 | } | ||
| 151 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/mac/typedefs.rs b/embassy-stm32-wpan/src/wb55/mac/typedefs.rs new file mode 100644 index 000000000..175d4a37d --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/mac/typedefs.rs | |||
| @@ -0,0 +1,434 @@ | |||
| 1 | use core::fmt::Debug; | ||
| 2 | |||
| 3 | use smoltcp::wire::ieee802154::{Address, AddressingMode, Pan}; | ||
| 4 | |||
| 5 | use crate::numeric_enum; | ||
| 6 | |||
| 7 | #[derive(Debug)] | ||
| 8 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 9 | pub enum MacError { | ||
| 10 | Error = 0x01, | ||
| 11 | NotImplemented = 0x02, | ||
| 12 | NotSupported = 0x03, | ||
| 13 | HardwareNotSupported = 0x04, | ||
| 14 | Undefined = 0x05, | ||
| 15 | } | ||
| 16 | |||
| 17 | impl From<u8> for MacError { | ||
| 18 | fn from(value: u8) -> Self { | ||
| 19 | match value { | ||
| 20 | 0x01 => Self::Error, | ||
| 21 | 0x02 => Self::NotImplemented, | ||
| 22 | 0x03 => Self::NotSupported, | ||
| 23 | 0x04 => Self::HardwareNotSupported, | ||
| 24 | 0x05 => Self::Undefined, | ||
| 25 | _ => Self::Undefined, | ||
| 26 | } | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | numeric_enum! { | ||
| 31 | #[repr(u8)] | ||
| 32 | #[derive(Debug, Default)] | ||
| 33 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 34 | pub enum MacStatus { | ||
| 35 | #[default] | ||
| 36 | Success = 0x00, | ||
| 37 | Failure = 0xFF | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | numeric_enum! { | ||
| 42 | #[repr(u8)] | ||
| 43 | /// this enum contains all the MAC PIB Ids | ||
| 44 | #[derive(Default, Debug)] | ||
| 45 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 46 | pub enum PibId { | ||
| 47 | // PHY | ||
| 48 | #[default] | ||
| 49 | CurrentChannel = 0x00, | ||
| 50 | ChannelsSupported = 0x01, | ||
| 51 | TransmitPower = 0x02, | ||
| 52 | CCAMode = 0x03, | ||
| 53 | CurrentPage = 0x04, | ||
| 54 | MaxFrameDuration = 0x05, | ||
| 55 | SHRDuration = 0x06, | ||
| 56 | SymbolsPerOctet = 0x07, | ||
| 57 | |||
| 58 | // MAC | ||
| 59 | AckWaitDuration = 0x40, | ||
| 60 | AssociationPermit = 0x41, | ||
| 61 | AutoRequest = 0x42, | ||
| 62 | BeaconPayload = 0x45, | ||
| 63 | BeaconPayloadLength = 0x46, | ||
| 64 | BeaconOrder = 0x47, | ||
| 65 | Bsn = 0x49, | ||
| 66 | CoordExtendedAdddress = 0x4A, | ||
| 67 | CoordShortAddress = 0x4B, | ||
| 68 | Dsn = 0x4C, | ||
| 69 | MaxFrameTotalWaitTime = 0x58, | ||
| 70 | MaxFrameRetries = 0x59, | ||
| 71 | PanId = 0x50, | ||
| 72 | ResponseWaitTime = 0x5A, | ||
| 73 | RxOnWhenIdle = 0x52, | ||
| 74 | SecurityEnabled = 0x5D, | ||
| 75 | ShortAddress = 0x53, | ||
| 76 | SuperframeOrder = 0x54, | ||
| 77 | TimestampSupported = 0x5C, | ||
| 78 | TransactionPersistenceTime = 0x55, | ||
| 79 | MaxBe = 0x57, | ||
| 80 | LifsPeriod = 0x5E, | ||
| 81 | SifsPeriod = 0x5F, | ||
| 82 | MaxCsmaBackoffs = 0x4E, | ||
| 83 | MinBe = 0x4F, | ||
| 84 | PanCoordinator = 0x10, | ||
| 85 | AssocPanCoordinator = 0x11, | ||
| 86 | ExtendedAddress = 0x6F, | ||
| 87 | AclEntryDescriptor = 0x70, | ||
| 88 | AclEntryDescriptorSize = 0x71, | ||
| 89 | DefaultSecurity = 0x72, | ||
| 90 | DefaultSecurityMaterialLength = 0x73, | ||
| 91 | DefaultSecurityMaterial = 0x74, | ||
| 92 | DefaultSecuritySuite = 0x75, | ||
| 93 | SecurityMode = 0x76, | ||
| 94 | CurrentAclEntries = 0x80, | ||
| 95 | DefaultSecurityExtendedAddress = 0x81, | ||
| 96 | AssociatedPanCoordinator = 0x56, | ||
| 97 | PromiscuousMode = 0x51, | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | numeric_enum! { | ||
| 102 | #[repr(u8)] | ||
| 103 | #[derive(Default, Clone, Copy, Debug)] | ||
| 104 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 105 | pub enum AddressMode { | ||
| 106 | #[default] | ||
| 107 | NoAddress = 0x00, | ||
| 108 | Reserved = 0x01, | ||
| 109 | Short = 0x02, | ||
| 110 | Extended = 0x03, | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | impl TryFrom<AddressingMode> for AddressMode { | ||
| 115 | type Error = (); | ||
| 116 | |||
| 117 | fn try_from(value: AddressingMode) -> Result<Self, Self::Error> { | ||
| 118 | match value { | ||
| 119 | AddressingMode::Absent => Ok(Self::NoAddress), | ||
| 120 | AddressingMode::Extended => Ok(Self::Extended), | ||
| 121 | AddressingMode::Short => Ok(Self::Short), | ||
| 122 | AddressingMode::Unknown(_) => Err(()), | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | #[derive(Clone, Copy)] | ||
| 128 | pub union MacAddress { | ||
| 129 | pub short: [u8; 2], | ||
| 130 | pub extended: [u8; 8], | ||
| 131 | } | ||
| 132 | |||
| 133 | impl From<Address> for MacAddress { | ||
| 134 | fn from(value: Address) -> Self { | ||
| 135 | match value { | ||
| 136 | Address::Short(addr) => Self { short: addr }, | ||
| 137 | Address::Extended(addr) => Self { extended: addr }, | ||
| 138 | Address::Absent => Self { short: [0u8; 2] }, | ||
| 139 | } | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | pub struct MacAddressAndMode(pub MacAddress, pub AddressMode); | ||
| 144 | |||
| 145 | impl From<MacAddressAndMode> for Address { | ||
| 146 | fn from(mac_address_and_mode: MacAddressAndMode) -> Self { | ||
| 147 | let address = mac_address_and_mode.0; | ||
| 148 | let mode = mac_address_and_mode.1; | ||
| 149 | |||
| 150 | match mode { | ||
| 151 | AddressMode::Short => Address::Short(unsafe { address.short }), | ||
| 152 | AddressMode::Extended => Address::Extended(unsafe { address.extended }), | ||
| 153 | AddressMode::NoAddress => Address::Absent, | ||
| 154 | AddressMode::Reserved => Address::Absent, | ||
| 155 | } | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | impl Debug for MacAddress { | ||
| 160 | fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 161 | unsafe { | ||
| 162 | write!( | ||
| 163 | fmt, | ||
| 164 | "MacAddress {{ short: {:?}, extended: {:?} }}", | ||
| 165 | self.short, self.extended | ||
| 166 | ) | ||
| 167 | } | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | #[cfg(feature = "defmt")] | ||
| 172 | impl defmt::Format for MacAddress { | ||
| 173 | fn format(&self, fmt: defmt::Formatter) { | ||
| 174 | unsafe { | ||
| 175 | defmt::write!( | ||
| 176 | fmt, | ||
| 177 | "MacAddress {{ short: {}, extended: {} }}", | ||
| 178 | self.short, | ||
| 179 | self.extended | ||
| 180 | ) | ||
| 181 | } | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | impl Default for MacAddress { | ||
| 186 | fn default() -> Self { | ||
| 187 | Self { short: [0, 0] } | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | impl MacAddress { | ||
| 192 | pub const BROADCAST: Self = Self { short: [0xFF, 0xFF] }; | ||
| 193 | } | ||
| 194 | |||
| 195 | impl TryFrom<&[u8]> for MacAddress { | ||
| 196 | type Error = (); | ||
| 197 | |||
| 198 | fn try_from(buf: &[u8]) -> Result<Self, Self::Error> { | ||
| 199 | const SIZE: usize = 8; | ||
| 200 | if buf.len() < SIZE { | ||
| 201 | return Err(()); | ||
| 202 | } | ||
| 203 | |||
| 204 | Ok(Self { | ||
| 205 | extended: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], | ||
| 206 | }) | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 211 | pub struct GtsCharacteristics { | ||
| 212 | pub fields: u8, | ||
| 213 | } | ||
| 214 | |||
| 215 | /// MAC PAN Descriptor which contains the network details of the device from | ||
| 216 | /// which the beacon is received | ||
| 217 | #[derive(Default, Clone, Copy, Debug)] | ||
| 218 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 219 | pub struct PanDescriptor { | ||
| 220 | /// PAN identifier of the coordinator | ||
| 221 | pub coord_pan_id: PanId, | ||
| 222 | /// Coordinator addressing mode | ||
| 223 | pub coord_addr_mode: AddressMode, | ||
| 224 | /// The current logical channel occupied by the network | ||
| 225 | pub logical_channel: MacChannel, | ||
| 226 | /// Coordinator address | ||
| 227 | pub coord_addr: MacAddress, | ||
| 228 | /// The current channel page occupied by the network | ||
| 229 | pub channel_page: u8, | ||
| 230 | /// PAN coordinator is accepting GTS requests or not | ||
| 231 | pub gts_permit: bool, | ||
| 232 | /// Superframe specification as specified in the received beacon frame | ||
| 233 | pub superframe_spec: [u8; 2], | ||
| 234 | /// The time at which the beacon frame was received, in symbols | ||
| 235 | pub time_stamp: [u8; 4], | ||
| 236 | /// The LQI at which the network beacon was received | ||
| 237 | pub link_quality: u8, | ||
| 238 | /// Security level purportedly used by the received beacon frame | ||
| 239 | pub security_level: u8, | ||
| 240 | } | ||
| 241 | |||
| 242 | impl TryFrom<&[u8]> for PanDescriptor { | ||
| 243 | type Error = (); | ||
| 244 | |||
| 245 | fn try_from(buf: &[u8]) -> Result<Self, Self::Error> { | ||
| 246 | const SIZE: usize = 22; | ||
| 247 | if buf.len() < SIZE { | ||
| 248 | return Err(()); | ||
| 249 | } | ||
| 250 | |||
| 251 | let coord_addr_mode = AddressMode::try_from(buf[2])?; | ||
| 252 | let coord_addr = match coord_addr_mode { | ||
| 253 | AddressMode::NoAddress => MacAddress { short: [0, 0] }, | ||
| 254 | AddressMode::Reserved => MacAddress { short: [0, 0] }, | ||
| 255 | AddressMode::Short => MacAddress { | ||
| 256 | short: [buf[4], buf[5]], | ||
| 257 | }, | ||
| 258 | AddressMode::Extended => MacAddress { | ||
| 259 | extended: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]], | ||
| 260 | }, | ||
| 261 | }; | ||
| 262 | |||
| 263 | Ok(Self { | ||
| 264 | coord_pan_id: PanId([buf[0], buf[1]]), | ||
| 265 | coord_addr_mode, | ||
| 266 | logical_channel: MacChannel::try_from(buf[3])?, | ||
| 267 | coord_addr, | ||
| 268 | channel_page: buf[12], | ||
| 269 | gts_permit: buf[13] != 0, | ||
| 270 | superframe_spec: [buf[14], buf[15]], | ||
| 271 | time_stamp: [buf[16], buf[17], buf[18], buf[19]], | ||
| 272 | link_quality: buf[20], | ||
| 273 | security_level: buf[21], | ||
| 274 | // 2 byte stuffing | ||
| 275 | }) | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | numeric_enum! { | ||
| 280 | #[repr(u8)] | ||
| 281 | #[derive(Default, Clone, Copy, Debug)] | ||
| 282 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 283 | /// Building wireless applications with STM32WB series MCUs - Application note 13.10.3 | ||
| 284 | pub enum MacChannel { | ||
| 285 | Channel11 = 0x0B, | ||
| 286 | Channel12 = 0x0C, | ||
| 287 | Channel13 = 0x0D, | ||
| 288 | Channel14 = 0x0E, | ||
| 289 | Channel15 = 0x0F, | ||
| 290 | #[default] | ||
| 291 | Channel16 = 0x10, | ||
| 292 | Channel17 = 0x11, | ||
| 293 | Channel18 = 0x12, | ||
| 294 | Channel19 = 0x13, | ||
| 295 | Channel20 = 0x14, | ||
| 296 | Channel21 = 0x15, | ||
| 297 | Channel22 = 0x16, | ||
| 298 | Channel23 = 0x17, | ||
| 299 | Channel24 = 0x18, | ||
| 300 | Channel25 = 0x19, | ||
| 301 | Channel26 = 0x1A, | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | #[cfg(not(feature = "defmt"))] | ||
| 306 | bitflags::bitflags! { | ||
| 307 | pub struct Capabilities: u8 { | ||
| 308 | /// 1 if the device is capabaleof becoming a PAN coordinator | ||
| 309 | const IS_COORDINATOR_CAPABLE = 0b00000001; | ||
| 310 | /// 1 if the device is an FFD, 0 if it is an RFD | ||
| 311 | const IS_FFD = 0b00000010; | ||
| 312 | /// 1 if the device is receiving power from mains, 0 if it is battery-powered | ||
| 313 | const IS_MAINS_POWERED = 0b00000100; | ||
| 314 | /// 1 if the device does not disable its receiver to conserver power during idle periods | ||
| 315 | const RECEIVER_ON_WHEN_IDLE = 0b00001000; | ||
| 316 | // 0b00010000 reserved | ||
| 317 | // 0b00100000 reserved | ||
| 318 | /// 1 if the device is capable of sending and receiving secured MAC frames | ||
| 319 | const IS_SECURE = 0b01000000; | ||
| 320 | /// 1 if the device wishes the coordinator to allocate a short address as a result of the association | ||
| 321 | const ALLOCATE_ADDRESS = 0b10000000; | ||
| 322 | } | ||
| 323 | } | ||
| 324 | |||
| 325 | #[cfg(feature = "defmt")] | ||
| 326 | defmt::bitflags! { | ||
| 327 | pub struct Capabilities: u8 { | ||
| 328 | /// 1 if the device is capabaleof becoming a PAN coordinator | ||
| 329 | const IS_COORDINATOR_CAPABLE = 0b00000001; | ||
| 330 | /// 1 if the device is an FFD, 0 if it is an RFD | ||
| 331 | const IS_FFD = 0b00000010; | ||
| 332 | /// 1 if the device is receiving power from mains, 0 if it is battery-powered | ||
| 333 | const IS_MAINS_POWERED = 0b00000100; | ||
| 334 | /// 1 if the device does not disable its receiver to conserver power during idle periods | ||
| 335 | const RECEIVER_ON_WHEN_IDLE = 0b00001000; | ||
| 336 | // 0b00010000 reserved | ||
| 337 | // 0b00100000 reserved | ||
| 338 | /// 1 if the device is capable of sending and receiving secured MAC frames | ||
| 339 | const IS_SECURE = 0b01000000; | ||
| 340 | /// 1 if the device wishes the coordinator to allocate a short address as a result of the association | ||
| 341 | const ALLOCATE_ADDRESS = 0b10000000; | ||
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | numeric_enum! { | ||
| 346 | #[repr(u8)] | ||
| 347 | #[derive(Default, Clone, Copy, Debug)] | ||
| 348 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 349 | pub enum KeyIdMode { | ||
| 350 | #[default] | ||
| 351 | /// the key is determined implicitly from the originator and recipient(s) of the frame | ||
| 352 | Implicite = 0x00, | ||
| 353 | /// the key is determined explicitly using a 1 bytes key source and a 1 byte key index | ||
| 354 | Explicite1Byte = 0x01, | ||
| 355 | /// the key is determined explicitly using a 4 bytes key source and a 1 byte key index | ||
| 356 | Explicite4Byte = 0x02, | ||
| 357 | /// the key is determined explicitly using a 8 bytes key source and a 1 byte key index | ||
| 358 | Explicite8Byte = 0x03, | ||
| 359 | } | ||
| 360 | } | ||
| 361 | |||
| 362 | numeric_enum! { | ||
| 363 | #[repr(u8)] | ||
| 364 | #[derive(Debug)] | ||
| 365 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 366 | pub enum AssociationStatus { | ||
| 367 | /// Association successful | ||
| 368 | Success = 0x00, | ||
| 369 | /// PAN at capacity | ||
| 370 | PanAtCapacity = 0x01, | ||
| 371 | /// PAN access denied | ||
| 372 | PanAccessDenied = 0x02 | ||
| 373 | } | ||
| 374 | } | ||
| 375 | |||
| 376 | numeric_enum! { | ||
| 377 | #[repr(u8)] | ||
| 378 | #[derive(Clone, Copy, Debug)] | ||
| 379 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 380 | pub enum DisassociationReason { | ||
| 381 | /// The coordinator wishes the device to leave the PAN. | ||
| 382 | CoordRequested = 0x01, | ||
| 383 | /// The device wishes to leave the PAN. | ||
| 384 | DeviceRequested = 0x02, | ||
| 385 | } | ||
| 386 | } | ||
| 387 | |||
| 388 | numeric_enum! { | ||
| 389 | #[repr(u8)] | ||
| 390 | #[derive(Default, Clone, Copy, Debug, PartialEq)] | ||
| 391 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 392 | pub enum SecurityLevel { | ||
| 393 | /// MAC Unsecured Mode Security | ||
| 394 | #[default] | ||
| 395 | Unsecure = 0x00, | ||
| 396 | /// MAC ACL Mode Security | ||
| 397 | AclMode = 0x01, | ||
| 398 | /// MAC Secured Mode Security | ||
| 399 | Secured = 0x02, | ||
| 400 | } | ||
| 401 | } | ||
| 402 | |||
| 403 | numeric_enum! { | ||
| 404 | #[repr(u8)] | ||
| 405 | #[derive(Debug)] | ||
| 406 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 407 | pub enum ScanType { | ||
| 408 | EdScan = 0x00, | ||
| 409 | Active = 0x01, | ||
| 410 | Passive = 0x02, | ||
| 411 | Orphan = 0x03 | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | /// newtype for Pan Id | ||
| 416 | #[derive(Default, Clone, Copy, Debug)] | ||
| 417 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 418 | pub struct PanId(pub [u8; 2]); | ||
| 419 | |||
| 420 | impl PanId { | ||
| 421 | pub const BROADCAST: Self = Self([0xFF, 0xFF]); | ||
| 422 | } | ||
| 423 | |||
| 424 | impl From<Pan> for PanId { | ||
| 425 | fn from(value: Pan) -> Self { | ||
| 426 | Self(value.0.to_be_bytes()) | ||
| 427 | } | ||
| 428 | } | ||
| 429 | |||
| 430 | impl From<PanId> for Pan { | ||
| 431 | fn from(value: PanId) -> Self { | ||
| 432 | Self(u16::from_be_bytes(value.0)) | ||
| 433 | } | ||
| 434 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/mod.rs b/embassy-stm32-wpan/src/wb55/mod.rs new file mode 100644 index 000000000..95cfe09f1 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/mod.rs | |||
| @@ -0,0 +1,198 @@ | |||
| 1 | // This must go FIRST so that all the other modules see its macros. | ||
| 2 | mod fmt; | ||
| 3 | |||
| 4 | use core::mem::MaybeUninit; | ||
| 5 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 6 | |||
| 7 | use embassy_hal_internal::Peri; | ||
| 8 | use embassy_stm32::interrupt; | ||
| 9 | use embassy_stm32::ipcc::{Config, Ipcc, IpccRxChannel, ReceiveInterruptHandler, TransmitInterruptHandler}; | ||
| 10 | use embassy_stm32::peripherals::IPCC; | ||
| 11 | use sub::mm::MemoryManager; | ||
| 12 | use sub::sys::Sys; | ||
| 13 | use tables::*; | ||
| 14 | use unsafe_linked_list::LinkedListNode; | ||
| 15 | |||
| 16 | pub mod channels; | ||
| 17 | pub mod cmd; | ||
| 18 | pub mod consts; | ||
| 19 | pub mod evt; | ||
| 20 | pub mod lhci; | ||
| 21 | pub mod shci; | ||
| 22 | pub mod sub; | ||
| 23 | pub mod tables; | ||
| 24 | pub mod unsafe_linked_list; | ||
| 25 | |||
| 26 | #[cfg(feature = "wb55_mac")] | ||
| 27 | pub mod mac; | ||
| 28 | |||
| 29 | #[cfg(feature = "wb55_ble")] | ||
| 30 | pub use crate::sub::ble::hci; | ||
| 31 | |||
| 32 | type PacketHeader = LinkedListNode; | ||
| 33 | |||
| 34 | /// Transport Layer for the Mailbox interface | ||
| 35 | pub struct TlMbox<'d> { | ||
| 36 | pub sys_subsystem: Sys<'d>, | ||
| 37 | pub mm_subsystem: MemoryManager<'d>, | ||
| 38 | #[cfg(feature = "wb55_ble")] | ||
| 39 | pub ble_subsystem: sub::ble::Ble<'d>, | ||
| 40 | #[cfg(feature = "wb55_mac")] | ||
| 41 | pub mac_subsystem: sub::mac::Mac<'d>, | ||
| 42 | pub traces: IpccRxChannel<'d>, | ||
| 43 | } | ||
| 44 | |||
| 45 | impl<'d> TlMbox<'d> { | ||
| 46 | /// Initialise the Transport Layer, and creates and returns a wrapper around it. | ||
| 47 | /// | ||
| 48 | /// This method performs the initialisation laid out in AN5289 annex 14.1. However, it differs | ||
| 49 | /// from the implementation documented in Figure 64, to avoid needing to reference any C | ||
| 50 | /// function pointers. | ||
| 51 | /// | ||
| 52 | /// Annex 14.1 lays out the following methods that should be called: | ||
| 53 | /// 1. tl_mbox.c/TL_Init, which initialises the reference table that is shared between CPU1 | ||
| 54 | /// and CPU2. | ||
| 55 | /// 2. shci_tl.c/shci_init(), which initialises the system transport layer, and in turn | ||
| 56 | /// calls tl_mbox.c/TL_SYS_Init, which initialises SYSTEM_EVT_QUEUE channel. | ||
| 57 | /// 3. tl_mbox.c/TL_MM_Init(), which initialises the channel used for sending memory | ||
| 58 | /// manager commands. | ||
| 59 | /// 4. tl_mbox.c/TL_Enable(), which enables the IPCC, and starts CPU2. | ||
| 60 | /// This implementation initialises all of the shared refernce tables and all IPCC channel that | ||
| 61 | /// would be initialised by this process. The developer should therefore treat this method as | ||
| 62 | /// completing all steps in Figure 64. | ||
| 63 | /// | ||
| 64 | /// Once this method has been called, no system commands may be sent until the CPU2 ready | ||
| 65 | /// signal is received, via [sys_subsystem.read]; this completes the procedure laid out in | ||
| 66 | /// Figure 65. | ||
| 67 | /// | ||
| 68 | /// If the `ble` feature is enabled, at this point, the user should call | ||
| 69 | /// [sys_subsystem.shci_c2_ble_init], before any commands are written to the | ||
| 70 | /// [TlMbox.ble_subsystem] ([sub::ble::Ble::new()] completes the process that would otherwise | ||
| 71 | /// be handled by `TL_BLE_Init`; see Figure 66). This completes the procedure laid out in | ||
| 72 | /// Figure 66. | ||
| 73 | // TODO: document what the user should do after calling init to use the mac_802_15_4 subsystem | ||
| 74 | pub async fn init( | ||
| 75 | ipcc: Peri<'d, IPCC>, | ||
| 76 | _irqs: impl interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_RX, ReceiveInterruptHandler> | ||
| 77 | + interrupt::typelevel::Binding<interrupt::typelevel::IPCC_C1_TX, TransmitInterruptHandler>, | ||
| 78 | config: Config, | ||
| 79 | ) -> Self { | ||
| 80 | // this is an inlined version of TL_Init from the STM32WB firmware as requested by AN5289. | ||
| 81 | // HW_IPCC_Init is not called, and its purpose is (presumably?) covered by this | ||
| 82 | // implementation | ||
| 83 | unsafe { | ||
| 84 | TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable { | ||
| 85 | device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(), | ||
| 86 | ble_table: TL_BLE_TABLE.as_ptr(), | ||
| 87 | thread_table: TL_THREAD_TABLE.as_ptr(), | ||
| 88 | sys_table: TL_SYS_TABLE.as_ptr(), | ||
| 89 | mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(), | ||
| 90 | traces_table: TL_TRACES_TABLE.as_ptr(), | ||
| 91 | mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(), | ||
| 92 | zigbee_table: TL_ZIGBEE_TABLE.as_ptr(), | ||
| 93 | lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(), | ||
| 94 | ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(), | ||
| 95 | }); | ||
| 96 | |||
| 97 | TL_SYS_TABLE | ||
| 98 | .as_mut_ptr() | ||
| 99 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 100 | TL_DEVICE_INFO_TABLE | ||
| 101 | .as_mut_ptr() | ||
| 102 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 103 | TL_BLE_TABLE | ||
| 104 | .as_mut_ptr() | ||
| 105 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 106 | TL_THREAD_TABLE | ||
| 107 | .as_mut_ptr() | ||
| 108 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 109 | TL_MEM_MANAGER_TABLE | ||
| 110 | .as_mut_ptr() | ||
| 111 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 112 | |||
| 113 | TL_TRACES_TABLE | ||
| 114 | .as_mut_ptr() | ||
| 115 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 116 | TL_MAC_802_15_4_TABLE | ||
| 117 | .as_mut_ptr() | ||
| 118 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 119 | TL_ZIGBEE_TABLE | ||
| 120 | .as_mut_ptr() | ||
| 121 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 122 | TL_LLD_TESTS_TABLE | ||
| 123 | .as_mut_ptr() | ||
| 124 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 125 | TL_BLE_LLD_TABLE | ||
| 126 | .as_mut_ptr() | ||
| 127 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 128 | |||
| 129 | EVT_POOL | ||
| 130 | .as_mut_ptr() | ||
| 131 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 132 | SYS_SPARE_EVT_BUF | ||
| 133 | .as_mut_ptr() | ||
| 134 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 135 | CS_BUFFER | ||
| 136 | .as_mut_ptr() | ||
| 137 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 138 | |||
| 139 | #[cfg(feature = "wb55_ble")] | ||
| 140 | { | ||
| 141 | BLE_SPARE_EVT_BUF | ||
| 142 | .as_mut_ptr() | ||
| 143 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 144 | |||
| 145 | BLE_CMD_BUFFER | ||
| 146 | .as_mut_ptr() | ||
| 147 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 148 | HCI_ACL_DATA_BUFFER | ||
| 149 | .as_mut_ptr() | ||
| 150 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 151 | } | ||
| 152 | |||
| 153 | #[cfg(feature = "wb55_mac")] | ||
| 154 | { | ||
| 155 | MAC_802_15_4_CMD_BUFFER | ||
| 156 | .as_mut_ptr() | ||
| 157 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 158 | MAC_802_15_4_NOTIF_RSP_EVT_BUFFER | ||
| 159 | .as_mut_ptr() | ||
| 160 | .write_volatile(MaybeUninit::zeroed().assume_init()); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | compiler_fence(Ordering::SeqCst); | ||
| 165 | |||
| 166 | // this is equivalent to `HW_IPCC_Enable`, which is called by `TL_Enable` | ||
| 167 | let [ | ||
| 168 | (_hw_ipcc_ble_cmd_channel, _ipcc_ble_event_channel), | ||
| 169 | (ipcc_system_cmd_rsp_channel, ipcc_system_event_channel), | ||
| 170 | (_ipcc_mac_802_15_4_cmd_rsp_channel, _ipcc_mac_802_15_4_notification_ack_channel), | ||
| 171 | (ipcc_mm_release_buffer_channel, _ipcc_traces_channel), | ||
| 172 | (_ipcc_ble_lld_cmd_channel, _ipcc_ble_lld_rsp_channel), | ||
| 173 | (_ipcc_hci_acl_data_channel, _), | ||
| 174 | ] = Ipcc::new(ipcc, _irqs, config).split(); | ||
| 175 | |||
| 176 | let mm = sub::mm::MemoryManager::new(ipcc_mm_release_buffer_channel); | ||
| 177 | let mut sys = sub::sys::Sys::new(ipcc_system_cmd_rsp_channel, ipcc_system_event_channel); | ||
| 178 | |||
| 179 | debug!("sys event: {}", sys.read().await.payload()); | ||
| 180 | |||
| 181 | Self { | ||
| 182 | sys_subsystem: sys, | ||
| 183 | #[cfg(feature = "wb55_ble")] | ||
| 184 | ble_subsystem: sub::ble::Ble::new( | ||
| 185 | _hw_ipcc_ble_cmd_channel, | ||
| 186 | _ipcc_ble_event_channel, | ||
| 187 | _ipcc_hci_acl_data_channel, | ||
| 188 | ), | ||
| 189 | #[cfg(feature = "wb55_mac")] | ||
| 190 | mac_subsystem: sub::mac::Mac::new( | ||
| 191 | _ipcc_mac_802_15_4_cmd_rsp_channel, | ||
| 192 | _ipcc_mac_802_15_4_notification_ack_channel, | ||
| 193 | ), | ||
| 194 | mm_subsystem: mm, | ||
| 195 | traces: _ipcc_traces_channel, | ||
| 196 | } | ||
| 197 | } | ||
| 198 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/shci.rs b/embassy-stm32-wpan/src/wb55/shci.rs new file mode 100644 index 000000000..3faa79209 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/shci.rs | |||
| @@ -0,0 +1,397 @@ | |||
| 1 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 2 | use core::{mem, ptr, slice}; | ||
| 3 | |||
| 4 | use crate::cmd::CmdPacket; | ||
| 5 | use crate::consts::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; | ||
| 6 | use crate::evt::{CcEvt, EvtStub}; | ||
| 7 | use crate::wb55::PacketHeader; | ||
| 8 | |||
| 9 | const SHCI_OGF: u16 = 0x3F; | ||
| 10 | |||
| 11 | const fn opcode(ogf: u16, ocf: u16) -> isize { | ||
| 12 | ((ogf << 10) + ocf) as isize | ||
| 13 | } | ||
| 14 | |||
| 15 | #[allow(dead_code)] | ||
| 16 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 17 | pub enum SchiCommandStatus { | ||
| 18 | ShciSuccess = 0x00, | ||
| 19 | ShciUnknownCmd = 0x01, | ||
| 20 | ShciMemoryCapacityExceededErrCode = 0x07, | ||
| 21 | ShciErrUnsupportedFeature = 0x11, | ||
| 22 | ShciErrInvalidHciCmdParams = 0x12, | ||
| 23 | ShciErrInvalidParams = 0x42, /* only used for release < v1.13.0 */ | ||
| 24 | ShciErrInvalidParamsV2 = 0x92, /* available for release >= v1.13.0 */ | ||
| 25 | ShciFusCmdNotSupported = 0xFF, | ||
| 26 | } | ||
| 27 | |||
| 28 | impl SchiCommandStatus { | ||
| 29 | pub unsafe fn from_packet(cmd_buf: *const CmdPacket) -> Result<Self, ()> { | ||
| 30 | let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::<PacketHeader>()); | ||
| 31 | let p_evt_payload = p_cmd_serial.add(size_of::<EvtStub>()); | ||
| 32 | |||
| 33 | compiler_fence(Ordering::Acquire); | ||
| 34 | let cc_evt = ptr::read_unaligned(p_evt_payload as *const CcEvt); | ||
| 35 | |||
| 36 | cc_evt.payload[0].try_into() | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | impl TryFrom<u8> for SchiCommandStatus { | ||
| 41 | type Error = (); | ||
| 42 | |||
| 43 | fn try_from(v: u8) -> Result<Self, Self::Error> { | ||
| 44 | match v { | ||
| 45 | x if x == SchiCommandStatus::ShciSuccess as u8 => Ok(SchiCommandStatus::ShciSuccess), | ||
| 46 | x if x == SchiCommandStatus::ShciUnknownCmd as u8 => Ok(SchiCommandStatus::ShciUnknownCmd), | ||
| 47 | x if x == SchiCommandStatus::ShciMemoryCapacityExceededErrCode as u8 => { | ||
| 48 | Ok(SchiCommandStatus::ShciMemoryCapacityExceededErrCode) | ||
| 49 | } | ||
| 50 | x if x == SchiCommandStatus::ShciErrUnsupportedFeature as u8 => { | ||
| 51 | Ok(SchiCommandStatus::ShciErrUnsupportedFeature) | ||
| 52 | } | ||
| 53 | x if x == SchiCommandStatus::ShciErrInvalidHciCmdParams as u8 => { | ||
| 54 | Ok(SchiCommandStatus::ShciErrInvalidHciCmdParams) | ||
| 55 | } | ||
| 56 | x if x == SchiCommandStatus::ShciErrInvalidParams as u8 => Ok(SchiCommandStatus::ShciErrInvalidParams), /* only used for release < v1.13.0 */ | ||
| 57 | x if x == SchiCommandStatus::ShciErrInvalidParamsV2 as u8 => Ok(SchiCommandStatus::ShciErrInvalidParamsV2), /* available for release >= v1.13.0 */ | ||
| 58 | x if x == SchiCommandStatus::ShciFusCmdNotSupported as u8 => Ok(SchiCommandStatus::ShciFusCmdNotSupported), | ||
| 59 | _ => Err(()), | ||
| 60 | } | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | #[allow(dead_code)] | ||
| 65 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 66 | pub enum ShciOpcode { | ||
| 67 | // 0x50 reserved | ||
| 68 | // 0x51 reserved | ||
| 69 | FusGetState = opcode(SHCI_OGF, 0x52), | ||
| 70 | // 0x53 reserved | ||
| 71 | FusFirmwareUpgrade = opcode(SHCI_OGF, 0x54), | ||
| 72 | FusFirmwareDelete = opcode(SHCI_OGF, 0x55), | ||
| 73 | FusUpdateAuthKey = opcode(SHCI_OGF, 0x56), | ||
| 74 | FusLockAuthKey = opcode(SHCI_OGF, 0x57), | ||
| 75 | FusStoreUserKey = opcode(SHCI_OGF, 0x58), | ||
| 76 | FusLoadUserKey = opcode(SHCI_OGF, 0x59), | ||
| 77 | FusStartWirelessStack = opcode(SHCI_OGF, 0x5a), | ||
| 78 | // 0x5b reserved | ||
| 79 | // 0x5c reserved | ||
| 80 | FusLockUserKey = opcode(SHCI_OGF, 0x5d), | ||
| 81 | FusUnloadUserKey = opcode(SHCI_OGF, 0x5e), | ||
| 82 | FusActivateAntirollback = opcode(SHCI_OGF, 0x5f), | ||
| 83 | // 0x60 reserved | ||
| 84 | // 0x61 reserved | ||
| 85 | // 0x62 reserved | ||
| 86 | // 0x63 reserved | ||
| 87 | // 0x64 reserved | ||
| 88 | // 0x65 reserved | ||
| 89 | BleInit = opcode(SHCI_OGF, 0x66), | ||
| 90 | ThreadInit = opcode(SHCI_OGF, 0x67), | ||
| 91 | DebugInit = opcode(SHCI_OGF, 0x68), | ||
| 92 | FlashEraseActivity = opcode(SHCI_OGF, 0x69), | ||
| 93 | ConcurrentSetMode = opcode(SHCI_OGF, 0x6a), | ||
| 94 | FlashStoreData = opcode(SHCI_OGF, 0x6b), | ||
| 95 | FlashEraseData = opcode(SHCI_OGF, 0x6c), | ||
| 96 | RadioAllowLowPower = opcode(SHCI_OGF, 0x6d), | ||
| 97 | Mac802_15_4Init = opcode(SHCI_OGF, 0x6e), | ||
| 98 | ReInit = opcode(SHCI_OGF, 0x6f), | ||
| 99 | ZigbeeInit = opcode(SHCI_OGF, 0x70), | ||
| 100 | LldTestsInit = opcode(SHCI_OGF, 0x71), | ||
| 101 | ExtraConfig = opcode(SHCI_OGF, 0x72), | ||
| 102 | SetFlashActivityControl = opcode(SHCI_OGF, 0x73), | ||
| 103 | BleLldInit = opcode(SHCI_OGF, 0x74), | ||
| 104 | Config = opcode(SHCI_OGF, 0x75), | ||
| 105 | ConcurrentGetNextBleEvtTime = opcode(SHCI_OGF, 0x76), | ||
| 106 | ConcurrentEnableNext802_15_4EvtNotification = opcode(SHCI_OGF, 0x77), | ||
| 107 | Mac802_15_4DeInit = opcode(SHCI_OGF, 0x78), | ||
| 108 | } | ||
| 109 | |||
| 110 | pub const SHCI_C2_CONFIG_EVTMASK1_BIT0_ERROR_NOTIF_ENABLE: u8 = 1 << 0; | ||
| 111 | pub const SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE: u8 = 1 << 1; | ||
| 112 | pub const SHCI_C2_CONFIG_EVTMASK1_BIT2_THREAD_NVM_RAM_UPDATE_ENABLE: u8 = 1 << 2; | ||
| 113 | pub const SHCI_C2_CONFIG_EVTMASK1_BIT3_NVM_START_WRITE_ENABLE: u8 = 1 << 3; | ||
| 114 | pub const SHCI_C2_CONFIG_EVTMASK1_BIT4_NVM_END_WRITE_ENABLE: u8 = 1 << 4; | ||
| 115 | pub const SHCI_C2_CONFIG_EVTMASK1_BIT5_NVM_START_ERASE_ENABLE: u8 = 1 << 5; | ||
| 116 | pub const SHCI_C2_CONFIG_EVTMASK1_BIT6_NVM_END_ERASE_ENABLE: u8 = 1 << 6; | ||
| 117 | |||
| 118 | #[derive(Clone, Copy)] | ||
| 119 | #[repr(C, packed)] | ||
| 120 | pub struct ShciConfigParam { | ||
| 121 | pub payload_cmd_size: u8, | ||
| 122 | pub config: u8, | ||
| 123 | pub event_mask: u8, | ||
| 124 | pub spare: u8, | ||
| 125 | pub ble_nvm_ram_address: u32, | ||
| 126 | pub thread_nvm_ram_address: u32, | ||
| 127 | pub revision_id: u16, | ||
| 128 | pub device_id: u16, | ||
| 129 | } | ||
| 130 | |||
| 131 | impl ShciConfigParam { | ||
| 132 | pub fn payload<'a>(&'a self) -> &'a [u8] { | ||
| 133 | unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::<Self>()) } | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | impl Default for ShciConfigParam { | ||
| 138 | fn default() -> Self { | ||
| 139 | Self { | ||
| 140 | payload_cmd_size: (mem::size_of::<Self>() - 1) as u8, | ||
| 141 | config: 0, | ||
| 142 | event_mask: SHCI_C2_CONFIG_EVTMASK1_BIT0_ERROR_NOTIF_ENABLE | ||
| 143 | + SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE | ||
| 144 | + SHCI_C2_CONFIG_EVTMASK1_BIT2_THREAD_NVM_RAM_UPDATE_ENABLE | ||
| 145 | + SHCI_C2_CONFIG_EVTMASK1_BIT3_NVM_START_WRITE_ENABLE | ||
| 146 | + SHCI_C2_CONFIG_EVTMASK1_BIT4_NVM_END_WRITE_ENABLE | ||
| 147 | + SHCI_C2_CONFIG_EVTMASK1_BIT5_NVM_START_ERASE_ENABLE | ||
| 148 | + SHCI_C2_CONFIG_EVTMASK1_BIT6_NVM_END_ERASE_ENABLE, | ||
| 149 | spare: 0, | ||
| 150 | ble_nvm_ram_address: 0, | ||
| 151 | thread_nvm_ram_address: 0, | ||
| 152 | revision_id: 0, | ||
| 153 | device_id: 0, | ||
| 154 | } | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | #[derive(Clone, Copy)] | ||
| 159 | #[repr(C, packed)] | ||
| 160 | pub struct ShciBleInitCmdParam { | ||
| 161 | /// NOT USED - shall be set to 0 | ||
| 162 | pub p_ble_buffer_address: u32, | ||
| 163 | /// NOT USED - shall be set to 0 | ||
| 164 | pub ble_buffer_size: u32, | ||
| 165 | /// Maximum number of attribute records related to all the required characteristics (excluding the services) | ||
| 166 | /// that can be stored in the GATT database, for the specific BLE user application. | ||
| 167 | /// For each characteristic, the number of attribute records goes from two to five depending on the characteristic properties: | ||
| 168 | /// - minimum of two (one for declaration and one for the value) | ||
| 169 | /// - add one more record for each additional property: notify or indicate, broadcast, extended property. | ||
| 170 | /// The total calculated value must be increased by 9, due to the records related to the standard attribute profile and | ||
| 171 | /// GAP service characteristics, and automatically added when initializing GATT and GAP layers | ||
| 172 | /// - Min value: <number of user attributes> + 9 | ||
| 173 | /// - Max value: depending on the GATT database defined by user application | ||
| 174 | pub num_attr_record: u16, | ||
| 175 | /// Defines the maximum number of services that can be stored in the GATT database. Note that the GAP and GATT services | ||
| 176 | /// are automatically added at initialization so this parameter must be the number of user services increased by two. | ||
| 177 | /// - Min value: <number of user service> + 2 | ||
| 178 | /// - Max value: depending GATT database defined by user application | ||
| 179 | pub num_attr_serv: u16, | ||
| 180 | /// NOTE: This parameter is ignored by the CPU2 when the parameter "Options" is set to "LL_only" ( see Options description in that structure ) | ||
| 181 | /// | ||
| 182 | /// Size of the storage area for the attribute values. | ||
| 183 | /// Each characteristic contributes to the attrValueArrSize value as follows: | ||
| 184 | /// - Characteristic value length plus: | ||
| 185 | /// + 5 bytes if characteristic UUID is 16 bits | ||
| 186 | /// + 19 bytes if characteristic UUID is 128 bits | ||
| 187 | /// + 2 bytes if characteristic has a server configuration descriptor | ||
| 188 | /// + 2 bytes * NumOfLinks if the characteristic has a client configuration descriptor | ||
| 189 | /// + 2 bytes if the characteristic has extended properties | ||
| 190 | /// Each descriptor contributes to the attrValueArrSize value as follows: | ||
| 191 | /// - Descriptor length | ||
| 192 | pub attr_value_arr_size: u16, | ||
| 193 | /// Maximum number of BLE links supported | ||
| 194 | /// - Min value: 1 | ||
| 195 | /// - Max value: 8 | ||
| 196 | pub num_of_links: u8, | ||
| 197 | /// Disable/enable the extended packet length BLE 5.0 feature | ||
| 198 | /// - Disable: 0 | ||
| 199 | /// - Enable: 1 | ||
| 200 | pub extended_packet_length_enable: u8, | ||
| 201 | /// NOTE: This parameter is ignored by the CPU2 when the parameter "Options" is set to "LL_only" ( see Options description in that structure ) | ||
| 202 | /// | ||
| 203 | /// Maximum number of supported "prepare write request" | ||
| 204 | /// - Min value: given by the macro DEFAULT_PREP_WRITE_LIST_SIZE | ||
| 205 | /// - Max value: a value higher than the minimum required can be specified, but it is not recommended | ||
| 206 | pub prepare_write_list_size: u8, | ||
| 207 | /// NOTE: This parameter is overwritten by the CPU2 with an hardcoded optimal value when the parameter "Options" is set to "LL_only" | ||
| 208 | /// ( see Options description in that structure ) | ||
| 209 | /// | ||
| 210 | /// Number of allocated memory blocks for the BLE stack | ||
| 211 | /// - Min value: given by the macro MBLOCKS_CALC | ||
| 212 | /// - Max value: a higher value can improve data throughput performance, but uses more memory | ||
| 213 | pub block_count: u8, | ||
| 214 | /// NOTE: This parameter is ignored by the CPU2 when the parameter "Options" is set to "LL_only" ( see Options description in that structure ) | ||
| 215 | /// | ||
| 216 | /// Maximum ATT MTU size supported | ||
| 217 | /// - Min value: 23 | ||
| 218 | /// - Max value: 512 | ||
| 219 | pub att_mtu: u16, | ||
| 220 | /// The sleep clock accuracy (ppm value) that used in BLE connected slave mode to calculate the window widening | ||
| 221 | /// (in combination with the sleep clock accuracy sent by master in CONNECT_REQ PDU), | ||
| 222 | /// refer to BLE 5.0 specifications - Vol 6 - Part B - chap 4.5.7 and 4.2.2 | ||
| 223 | /// - Min value: 0 | ||
| 224 | /// - Max value: 500 (worst possible admitted by specification) | ||
| 225 | pub slave_sca: u16, | ||
| 226 | /// The sleep clock accuracy handled in master mode. It is used to determine the connection and advertising events timing. | ||
| 227 | /// It is transmitted to the slave in CONNEC_REQ PDU used by the slave to calculate the window widening, | ||
| 228 | /// see SlaveSca and Bluetooth Core Specification v5.0 Vol 6 - Part B - chap 4.5.7 and 4.2.2 | ||
| 229 | /// Possible values: | ||
| 230 | /// - 251 ppm to 500 ppm: 0 | ||
| 231 | /// - 151 ppm to 250 ppm: 1 | ||
| 232 | /// - 101 ppm to 150 ppm: 2 | ||
| 233 | /// - 76 ppm to 100 ppm: 3 | ||
| 234 | /// - 51 ppm to 75 ppm: 4 | ||
| 235 | /// - 31 ppm to 50 ppm: 5 | ||
| 236 | /// - 21 ppm to 30 ppm: 6 | ||
| 237 | /// - 0 ppm to 20 ppm: 7 | ||
| 238 | pub master_sca: u8, | ||
| 239 | /// Some information for Low speed clock mapped in bits field | ||
| 240 | /// - bit 0: | ||
| 241 | /// - 1: Calibration for the RF system wakeup clock source | ||
| 242 | /// - 0: No calibration for the RF system wakeup clock source | ||
| 243 | /// - bit 1: | ||
| 244 | /// - 1: STM32W5M Module device | ||
| 245 | /// - 0: Other devices as STM32WBxx SOC, STM32WB1M module | ||
| 246 | /// - bit 2: | ||
| 247 | /// - 1: HSE/1024 Clock config | ||
| 248 | /// - 0: LSE Clock config | ||
| 249 | pub ls_source: u8, | ||
| 250 | /// This parameter determines the maximum duration of a slave connection event. When this duration is reached the slave closes | ||
| 251 | /// the current connections event (whatever is the CE_length parameter specified by the master in HCI_CREATE_CONNECTION HCI command), | ||
| 252 | /// expressed in units of 625/256 µs (~2.44 µs) | ||
| 253 | /// - Min value: 0 (if 0 is specified, the master and slave perform only a single TX-RX exchange per connection event) | ||
| 254 | /// - Max value: 1638400 (4000 ms). A higher value can be specified (max 0xFFFFFFFF) but results in a maximum connection time | ||
| 255 | /// of 4000 ms as specified. In this case the parameter is not applied, and the predicted CE length calculated on slave is not shortened | ||
| 256 | pub max_conn_event_length: u32, | ||
| 257 | /// Startup time of the high speed (16 or 32 MHz) crystal oscillator in units of 625/256 µs (~2.44 µs). | ||
| 258 | /// - Min value: 0 | ||
| 259 | /// - Max value: 820 (~2 ms). A higher value can be specified, but the value that implemented in stack is forced to ~2 ms | ||
| 260 | pub hs_startup_time: u16, | ||
| 261 | /// Viterbi implementation in BLE LL reception. | ||
| 262 | /// - 0: Enable | ||
| 263 | /// - 1: Disable | ||
| 264 | pub viterbi_enable: u8, | ||
| 265 | /// - bit 0: | ||
| 266 | /// - 1: LL only | ||
| 267 | /// - 0: LL + host | ||
| 268 | /// - bit 1: | ||
| 269 | /// - 1: no service change desc. | ||
| 270 | /// - 0: with service change desc. | ||
| 271 | /// - bit 2: | ||
| 272 | /// - 1: device name Read-Only | ||
| 273 | /// - 0: device name R/W | ||
| 274 | /// - bit 3: | ||
| 275 | /// - 1: extended advertizing supported | ||
| 276 | /// - 0: extended advertizing not supported | ||
| 277 | /// - bit 4: | ||
| 278 | /// - 1: CS Algo #2 supported | ||
| 279 | /// - 0: CS Algo #2 not supported | ||
| 280 | /// - bit 5: | ||
| 281 | /// - 1: Reduced GATT database in NVM | ||
| 282 | /// - 0: Full GATT database in NVM | ||
| 283 | /// - bit 6: | ||
| 284 | /// - 1: GATT caching is used | ||
| 285 | /// - 0: GATT caching is not used | ||
| 286 | /// - bit 7: | ||
| 287 | /// - 1: LE Power Class 1 | ||
| 288 | /// - 0: LE Power Classe 2-3 | ||
| 289 | /// - other bits: complete with Options_extension flag | ||
| 290 | pub options: u8, | ||
| 291 | /// Reserved for future use - shall be set to 0 | ||
| 292 | pub hw_version: u8, | ||
| 293 | /// | ||
| 294 | /// Maximum number of connection-oriented channels in initiator mode. | ||
| 295 | /// Range: 0 .. 64 | ||
| 296 | pub max_coc_initiator_nbr: u8, | ||
| 297 | |||
| 298 | /// | ||
| 299 | /// Minimum transmit power in dBm supported by the Controller. | ||
| 300 | /// Range: -127 .. 20 | ||
| 301 | pub min_tx_power: i8, | ||
| 302 | |||
| 303 | /// | ||
| 304 | /// Maximum transmit power in dBm supported by the Controller. | ||
| 305 | /// Range: -127 .. 20 | ||
| 306 | pub max_tx_power: i8, | ||
| 307 | |||
| 308 | /// | ||
| 309 | /// RX model configuration | ||
| 310 | /// - bit 0: 1: agc_rssi model improved vs RF blockers 0: Legacy agc_rssi model | ||
| 311 | /// - other bits: reserved ( shall be set to 0) | ||
| 312 | pub rx_model_config: u8, | ||
| 313 | |||
| 314 | /// Maximum number of advertising sets. | ||
| 315 | /// Range: 1 .. 8 with limitation: | ||
| 316 | /// This parameter is linked to max_adv_data_len such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based | ||
| 317 | /// on Max Extended advertising configuration supported. | ||
| 318 | /// This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set | ||
| 319 | pub max_adv_set_nbr: u8, | ||
| 320 | |||
| 321 | /// Maximum advertising data length (in bytes) | ||
| 322 | /// Range: 31 .. 1650 with limitation: | ||
| 323 | /// This parameter is linked to max_adv_set_nbr such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based | ||
| 324 | /// on Max Extended advertising configuration supported. | ||
| 325 | /// This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set | ||
| 326 | pub max_adv_data_len: u16, | ||
| 327 | |||
| 328 | /// RF TX Path Compensation Value (16-bit signed integer). Units: 0.1 dB. | ||
| 329 | /// Range: -1280 .. 1280 | ||
| 330 | pub tx_path_compens: i16, | ||
| 331 | |||
| 332 | //// RF RX Path Compensation Value (16-bit signed integer). Units: 0.1 dB. | ||
| 333 | /// Range: -1280 .. 1280 | ||
| 334 | pub rx_path_compens: i16, | ||
| 335 | |||
| 336 | /// BLE core specification version (8-bit unsigned integer). | ||
| 337 | /// values as: 11(5.2), 12(5.3) | ||
| 338 | pub ble_core_version: u8, | ||
| 339 | |||
| 340 | /// Options flags extension | ||
| 341 | /// - bit 0: 1: appearance Writable 0: appearance Read-Only | ||
| 342 | /// - bit 1: 1: Enhanced ATT supported 0: Enhanced ATT not supported | ||
| 343 | /// - other bits: reserved ( shall be set to 0) | ||
| 344 | pub options_extension: u8, | ||
| 345 | |||
| 346 | /// MaxAddEattBearers | ||
| 347 | /// Maximum number of bearers that can be created for Enhanced ATT | ||
| 348 | /// in addition to the number of links | ||
| 349 | /// - Range: 0 .. 4 | ||
| 350 | pub max_add_eatt_bearers: u8, | ||
| 351 | } | ||
| 352 | |||
| 353 | impl ShciBleInitCmdParam { | ||
| 354 | pub fn payload<'a>(&'a self) -> &'a [u8] { | ||
| 355 | unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::<Self>()) } | ||
| 356 | } | ||
| 357 | } | ||
| 358 | |||
| 359 | impl Default for ShciBleInitCmdParam { | ||
| 360 | fn default() -> Self { | ||
| 361 | Self { | ||
| 362 | p_ble_buffer_address: 0, | ||
| 363 | ble_buffer_size: 0, | ||
| 364 | num_attr_record: 68, | ||
| 365 | num_attr_serv: 4, | ||
| 366 | attr_value_arr_size: 1344, | ||
| 367 | num_of_links: 2, | ||
| 368 | extended_packet_length_enable: 1, | ||
| 369 | prepare_write_list_size: 0x3A, | ||
| 370 | block_count: 0x79, | ||
| 371 | att_mtu: 156, | ||
| 372 | slave_sca: 500, | ||
| 373 | master_sca: 0, | ||
| 374 | ls_source: 1, | ||
| 375 | max_conn_event_length: 0xFFFFFFFF, | ||
| 376 | hs_startup_time: 0x148, | ||
| 377 | viterbi_enable: 1, | ||
| 378 | options: 0, | ||
| 379 | hw_version: 0, | ||
| 380 | max_coc_initiator_nbr: 32, | ||
| 381 | min_tx_power: -40, | ||
| 382 | max_tx_power: 6, | ||
| 383 | rx_model_config: 0, | ||
| 384 | max_adv_set_nbr: 2, | ||
| 385 | max_adv_data_len: 1650, | ||
| 386 | tx_path_compens: 0, | ||
| 387 | rx_path_compens: 0, | ||
| 388 | ble_core_version: 11, | ||
| 389 | options_extension: 0, | ||
| 390 | max_add_eatt_bearers: 4, | ||
| 391 | } | ||
| 392 | } | ||
| 393 | } | ||
| 394 | |||
| 395 | pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE; | ||
| 396 | #[allow(dead_code)] // Not used currently but reserved | ||
| 397 | const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE; | ||
diff --git a/embassy-stm32-wpan/src/wb55/sub/ble.rs b/embassy-stm32-wpan/src/wb55/sub/ble.rs new file mode 100644 index 000000000..a822d6530 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/sub/ble.rs | |||
| @@ -0,0 +1,139 @@ | |||
| 1 | use core::ptr; | ||
| 2 | |||
| 3 | use embassy_stm32::ipcc::{IpccRxChannel, IpccTxChannel}; | ||
| 4 | use hci::Opcode; | ||
| 5 | |||
| 6 | use crate::cmd::CmdPacket; | ||
| 7 | use crate::consts::{TL_BLEEVT_CC_OPCODE, TL_BLEEVT_CS_OPCODE, TlPacketType}; | ||
| 8 | use crate::evt; | ||
| 9 | use crate::evt::{EvtBox, EvtPacket, EvtStub}; | ||
| 10 | use crate::sub::mm; | ||
| 11 | use crate::tables::{BLE_CMD_BUFFER, BleTable, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; | ||
| 12 | use crate::unsafe_linked_list::LinkedListNode; | ||
| 13 | |||
| 14 | /// A guard that, once constructed, may be used to send BLE commands to CPU2. | ||
| 15 | /// | ||
| 16 | /// It is the responsibility of the caller to ensure that they have awaited an event via | ||
| 17 | /// [crate::sub::sys::Sys::read] before sending any of these commands, and to call | ||
| 18 | /// [crate::sub::sys::Sys::shci_c2_ble_init] and await the HCI_COMMAND_COMPLETE_EVENT before | ||
| 19 | /// sending any other commands. | ||
| 20 | /// | ||
| 21 | /// # Example | ||
| 22 | /// | ||
| 23 | /// ``` | ||
| 24 | /// # embassy_stm32::bind_interrupts!(struct Irqs{ | ||
| 25 | /// # IPCC_C1_RX => ReceiveInterruptHandler; | ||
| 26 | /// # IPCC_C1_TX => TransmitInterruptHandler; | ||
| 27 | /// # }); | ||
| 28 | /// # | ||
| 29 | /// # let p = embassy_stm32::init(embassy_stm32::Config::default()); | ||
| 30 | /// # let mut mbox = embassy_stm32_wpan::TlMbox::init(p.IPCC, Irqs, embassy_stm32::ipcc::Config::default()); | ||
| 31 | /// # | ||
| 32 | /// # let sys_event = mbox.sys_subsystem.read().await; | ||
| 33 | /// # let _command_status = mbox.sys_subsystem.shci_c2_ble_init(Default::default()); | ||
| 34 | /// # // BLE commands may now be sent | ||
| 35 | /// # | ||
| 36 | /// # mbox.ble_subsystem.reset().await; | ||
| 37 | /// # let _reset_response = mbox.ble_subsystem.read().await; | ||
| 38 | /// ``` | ||
| 39 | pub struct Ble<'a> { | ||
| 40 | hw_ipcc_ble_cmd_channel: IpccTxChannel<'a>, | ||
| 41 | ipcc_ble_event_channel: IpccRxChannel<'a>, | ||
| 42 | ipcc_hci_acl_data_channel: IpccTxChannel<'a>, | ||
| 43 | } | ||
| 44 | |||
| 45 | impl<'a> Ble<'a> { | ||
| 46 | /// Constructs a guard that allows for BLE commands to be sent to CPU2. | ||
| 47 | /// | ||
| 48 | /// This takes the place of `TL_BLE_Init`, completing that step as laid out in AN5289, Fig 66. | ||
| 49 | pub(crate) fn new( | ||
| 50 | hw_ipcc_ble_cmd_channel: IpccTxChannel<'a>, | ||
| 51 | ipcc_ble_event_channel: IpccRxChannel<'a>, | ||
| 52 | ipcc_hci_acl_data_channel: IpccTxChannel<'a>, | ||
| 53 | ) -> Self { | ||
| 54 | unsafe { | ||
| 55 | LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); | ||
| 56 | |||
| 57 | TL_BLE_TABLE.as_mut_ptr().write_volatile(BleTable { | ||
| 58 | pcmd_buffer: BLE_CMD_BUFFER.as_mut_ptr().cast(), | ||
| 59 | pcs_buffer: CS_BUFFER.as_ptr().cast(), | ||
| 60 | pevt_queue: EVT_QUEUE.as_ptr().cast(), | ||
| 61 | phci_acl_data_buffer: HCI_ACL_DATA_BUFFER.as_mut_ptr().cast(), | ||
| 62 | }); | ||
| 63 | } | ||
| 64 | |||
| 65 | Self { | ||
| 66 | hw_ipcc_ble_cmd_channel, | ||
| 67 | ipcc_ble_event_channel, | ||
| 68 | ipcc_hci_acl_data_channel, | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | /// `HW_IPCC_BLE_EvtNot` | ||
| 73 | pub async fn tl_read(&mut self) -> EvtBox<Self> { | ||
| 74 | self.ipcc_ble_event_channel | ||
| 75 | .receive(|| unsafe { | ||
| 76 | if let Some(node_ptr) = | ||
| 77 | critical_section::with(|cs| LinkedListNode::remove_head(cs, EVT_QUEUE.as_mut_ptr())) | ||
| 78 | { | ||
| 79 | Some(EvtBox::new(node_ptr.cast())) | ||
| 80 | } else { | ||
| 81 | None | ||
| 82 | } | ||
| 83 | }) | ||
| 84 | .await | ||
| 85 | } | ||
| 86 | |||
| 87 | /// `TL_BLE_SendCmd` | ||
| 88 | pub async fn tl_write(&mut self, opcode: u16, payload: &[u8]) { | ||
| 89 | self.hw_ipcc_ble_cmd_channel | ||
| 90 | .send(|| unsafe { | ||
| 91 | CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload); | ||
| 92 | }) | ||
| 93 | .await; | ||
| 94 | } | ||
| 95 | |||
| 96 | /// `TL_BLE_SendAclData` | ||
| 97 | pub async fn acl_write(&mut self, handle: u16, payload: &[u8]) { | ||
| 98 | self.ipcc_hci_acl_data_channel | ||
| 99 | .send(|| unsafe { | ||
| 100 | CmdPacket::write_into( | ||
| 101 | HCI_ACL_DATA_BUFFER.as_mut_ptr() as *mut _, | ||
| 102 | TlPacketType::AclData, | ||
| 103 | handle, | ||
| 104 | payload, | ||
| 105 | ); | ||
| 106 | }) | ||
| 107 | .await; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | impl<'a> evt::MemoryManager for Ble<'a> { | ||
| 112 | /// SAFETY: passing a pointer to something other than a managed event packet is UB | ||
| 113 | unsafe fn drop_event_packet(evt: *mut EvtPacket) { | ||
| 114 | let stub = unsafe { | ||
| 115 | let p_evt_stub = &(*evt).evt_serial as *const _ as *const EvtStub; | ||
| 116 | |||
| 117 | ptr::read_volatile(p_evt_stub) | ||
| 118 | }; | ||
| 119 | |||
| 120 | if !(stub.evt_code == TL_BLEEVT_CS_OPCODE || stub.evt_code == TL_BLEEVT_CC_OPCODE) { | ||
| 121 | mm::MemoryManager::drop_event_packet(evt); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | pub extern crate stm32wb_hci as hci; | ||
| 127 | |||
| 128 | impl<'a> hci::Controller for Ble<'a> { | ||
| 129 | async fn controller_write(&mut self, opcode: Opcode, payload: &[u8]) { | ||
| 130 | self.tl_write(opcode.0, payload).await; | ||
| 131 | } | ||
| 132 | |||
| 133 | async fn controller_read_into(&mut self, buf: &mut [u8]) { | ||
| 134 | let evt_box = self.tl_read().await; | ||
| 135 | let evt_serial = evt_box.serial(); | ||
| 136 | |||
| 137 | buf[..evt_serial.len()].copy_from_slice(evt_serial); | ||
| 138 | } | ||
| 139 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/sub/mac.rs b/embassy-stm32-wpan/src/wb55/sub/mac.rs new file mode 100644 index 000000000..ce2903e61 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/sub/mac.rs | |||
| @@ -0,0 +1,173 @@ | |||
| 1 | use core::future::poll_fn; | ||
| 2 | use core::ptr; | ||
| 3 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 4 | use core::task::Poll; | ||
| 5 | |||
| 6 | use embassy_futures::poll_once; | ||
| 7 | use embassy_stm32::ipcc::{Ipcc, IpccRxChannel, IpccTxChannel}; | ||
| 8 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 9 | |||
| 10 | use crate::cmd::CmdPacket; | ||
| 11 | use crate::consts::TlPacketType; | ||
| 12 | use crate::evt; | ||
| 13 | use crate::evt::{EvtBox, EvtPacket}; | ||
| 14 | use crate::mac::commands::MacCommand; | ||
| 15 | use crate::mac::event::MacEvent; | ||
| 16 | use crate::mac::typedefs::MacError; | ||
| 17 | use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER}; | ||
| 18 | use crate::unsafe_linked_list::LinkedListNode; | ||
| 19 | |||
| 20 | static MAC_WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 21 | static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false); | ||
| 22 | |||
| 23 | pub struct Mac<'a> { | ||
| 24 | ipcc_mac_802_15_4_cmd_rsp_channel: IpccTxChannel<'a>, | ||
| 25 | ipcc_mac_802_15_4_notification_ack_channel: IpccRxChannel<'a>, | ||
| 26 | } | ||
| 27 | |||
| 28 | impl<'a> Mac<'a> { | ||
| 29 | pub(crate) fn new( | ||
| 30 | ipcc_mac_802_15_4_cmd_rsp_channel: IpccTxChannel<'a>, | ||
| 31 | ipcc_mac_802_15_4_notification_ack_channel: IpccRxChannel<'a>, | ||
| 32 | ) -> Self { | ||
| 33 | use crate::tables::{ | ||
| 34 | MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, Mac802_15_4Table, TL_MAC_802_15_4_TABLE, | ||
| 35 | TL_TRACES_TABLE, TRACES_EVT_QUEUE, TracesTable, | ||
| 36 | }; | ||
| 37 | |||
| 38 | unsafe { | ||
| 39 | LinkedListNode::init_head(TRACES_EVT_QUEUE.as_mut_ptr() as *mut _); | ||
| 40 | |||
| 41 | TL_TRACES_TABLE.as_mut_ptr().write_volatile(TracesTable { | ||
| 42 | traces_queue: TRACES_EVT_QUEUE.as_ptr() as *const _, | ||
| 43 | }); | ||
| 44 | |||
| 45 | TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table { | ||
| 46 | p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(), | ||
| 47 | p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(), | ||
| 48 | evt_queue: core::ptr::null_mut(), | ||
| 49 | }); | ||
| 50 | }; | ||
| 51 | |||
| 52 | Self { | ||
| 53 | ipcc_mac_802_15_4_cmd_rsp_channel, | ||
| 54 | ipcc_mac_802_15_4_notification_ack_channel, | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | pub const fn split(self) -> (MacRx<'a>, MacTx<'a>) { | ||
| 59 | ( | ||
| 60 | MacRx { | ||
| 61 | ipcc_mac_802_15_4_notification_ack_channel: self.ipcc_mac_802_15_4_notification_ack_channel, | ||
| 62 | }, | ||
| 63 | MacTx { | ||
| 64 | ipcc_mac_802_15_4_cmd_rsp_channel: self.ipcc_mac_802_15_4_cmd_rsp_channel, | ||
| 65 | }, | ||
| 66 | ) | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | pub struct MacTx<'a> { | ||
| 71 | ipcc_mac_802_15_4_cmd_rsp_channel: IpccTxChannel<'a>, | ||
| 72 | } | ||
| 73 | |||
| 74 | impl<'a> MacTx<'a> { | ||
| 75 | /// `HW_IPCC_MAC_802_15_4_CmdEvtNot` | ||
| 76 | pub async fn tl_write_and_get_response(&mut self, opcode: u16, payload: &[u8]) -> u8 { | ||
| 77 | self.tl_write(opcode, payload).await; | ||
| 78 | self.ipcc_mac_802_15_4_cmd_rsp_channel.flush().await; | ||
| 79 | |||
| 80 | unsafe { | ||
| 81 | let p_event_packet = MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket; | ||
| 82 | let p_mac_rsp_evt = &((*p_event_packet).evt_serial.evt.payload) as *const u8; | ||
| 83 | |||
| 84 | ptr::read_volatile(p_mac_rsp_evt) | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | /// `TL_MAC_802_15_4_SendCmd` | ||
| 89 | pub async fn tl_write(&mut self, opcode: u16, payload: &[u8]) { | ||
| 90 | self.ipcc_mac_802_15_4_cmd_rsp_channel | ||
| 91 | .send(|| unsafe { | ||
| 92 | CmdPacket::write_into( | ||
| 93 | MAC_802_15_4_CMD_BUFFER.as_mut_ptr(), | ||
| 94 | TlPacketType::MacCmd, | ||
| 95 | opcode, | ||
| 96 | payload, | ||
| 97 | ); | ||
| 98 | }) | ||
| 99 | .await; | ||
| 100 | } | ||
| 101 | |||
| 102 | pub async fn send_command<T>(&mut self, cmd: &T) -> Result<(), MacError> | ||
| 103 | where | ||
| 104 | T: MacCommand, | ||
| 105 | { | ||
| 106 | let response = self.tl_write_and_get_response(T::OPCODE as u16, cmd.payload()).await; | ||
| 107 | |||
| 108 | if response == 0x00 { | ||
| 109 | Ok(()) | ||
| 110 | } else { | ||
| 111 | Err(MacError::from(response)) | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | pub struct MacRx<'a> { | ||
| 117 | ipcc_mac_802_15_4_notification_ack_channel: IpccRxChannel<'a>, | ||
| 118 | } | ||
| 119 | |||
| 120 | impl<'a> MacRx<'a> { | ||
| 121 | /// `HW_IPCC_MAC_802_15_4_EvtNot` | ||
| 122 | /// | ||
| 123 | /// This function will stall if the previous `EvtBox` has not been dropped | ||
| 124 | pub async fn tl_read(&mut self) -> EvtBox<MacRx<'a>> { | ||
| 125 | // Wait for the last event box to be dropped | ||
| 126 | poll_fn(|cx| { | ||
| 127 | MAC_WAKER.register(cx.waker()); | ||
| 128 | if MAC_EVT_OUT.load(Ordering::Acquire) { | ||
| 129 | Poll::Pending | ||
| 130 | } else { | ||
| 131 | Poll::Ready(()) | ||
| 132 | } | ||
| 133 | }) | ||
| 134 | .await; | ||
| 135 | |||
| 136 | // Return a new event box | ||
| 137 | self.ipcc_mac_802_15_4_notification_ack_channel | ||
| 138 | .receive(|| unsafe { | ||
| 139 | // The closure is not async, therefore the closure must execute to completion (cannot be dropped) | ||
| 140 | // Therefore, the event box is guaranteed to be cleaned up if it's not leaked | ||
| 141 | MAC_EVT_OUT.store(true, Ordering::SeqCst); | ||
| 142 | |||
| 143 | Some(EvtBox::new(MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _)) | ||
| 144 | }) | ||
| 145 | .await | ||
| 146 | } | ||
| 147 | |||
| 148 | pub async fn read<'b>(&mut self) -> Result<MacEvent<'b>, ()> { | ||
| 149 | MacEvent::new(self.tl_read().await) | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | impl<'a> evt::MemoryManager for MacRx<'a> { | ||
| 154 | /// SAFETY: passing a pointer to something other than a managed event packet is UB | ||
| 155 | unsafe fn drop_event_packet(_: *mut EvtPacket) { | ||
| 156 | trace!("mac drop event"); | ||
| 157 | |||
| 158 | // Write the ack | ||
| 159 | CmdPacket::write_into( | ||
| 160 | MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _, | ||
| 161 | TlPacketType::OtAck, | ||
| 162 | 0, | ||
| 163 | &[], | ||
| 164 | ); | ||
| 165 | |||
| 166 | // Clear the rx flag | ||
| 167 | let _ = poll_once(Ipcc::receive::<()>(3, || None)); | ||
| 168 | |||
| 169 | // Allow a new read call | ||
| 170 | MAC_EVT_OUT.store(false, Ordering::Release); | ||
| 171 | MAC_WAKER.wake(); | ||
| 172 | } | ||
| 173 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/sub/mm.rs b/embassy-stm32-wpan/src/wb55/sub/mm.rs new file mode 100644 index 000000000..0ca7d1835 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/sub/mm.rs | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | //! Memory manager routines | ||
| 2 | use core::future::poll_fn; | ||
| 3 | use core::mem::MaybeUninit; | ||
| 4 | use core::task::Poll; | ||
| 5 | |||
| 6 | use aligned::{A4, Aligned}; | ||
| 7 | use embassy_stm32::ipcc::IpccTxChannel; | ||
| 8 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 9 | |||
| 10 | use crate::consts::POOL_SIZE; | ||
| 11 | use crate::evt; | ||
| 12 | use crate::evt::EvtPacket; | ||
| 13 | #[cfg(feature = "wb55_ble")] | ||
| 14 | use crate::tables::BLE_SPARE_EVT_BUF; | ||
| 15 | use crate::tables::{EVT_POOL, FREE_BUF_QUEUE, MemManagerTable, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE}; | ||
| 16 | use crate::unsafe_linked_list::LinkedListNode; | ||
| 17 | |||
| 18 | static MM_WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 19 | static mut LOCAL_FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit()); | ||
| 20 | |||
| 21 | pub struct MemoryManager<'a> { | ||
| 22 | ipcc_mm_release_buffer_channel: IpccTxChannel<'a>, | ||
| 23 | } | ||
| 24 | |||
| 25 | impl<'a> MemoryManager<'a> { | ||
| 26 | pub(crate) fn new(ipcc_mm_release_buffer_channel: IpccTxChannel<'a>) -> Self { | ||
| 27 | unsafe { | ||
| 28 | LinkedListNode::init_head(FREE_BUF_QUEUE.as_mut_ptr()); | ||
| 29 | LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); | ||
| 30 | |||
| 31 | TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable { | ||
| 32 | #[cfg(feature = "wb55_ble")] | ||
| 33 | spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(), | ||
| 34 | #[cfg(not(feature = "wb55_ble"))] | ||
| 35 | spare_ble_buffer: core::ptr::null(), | ||
| 36 | spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(), | ||
| 37 | blepool: EVT_POOL.as_ptr().cast(), | ||
| 38 | blepoolsize: POOL_SIZE as u32, | ||
| 39 | pevt_free_buffer_queue: FREE_BUF_QUEUE.as_mut_ptr(), | ||
| 40 | traces_evt_pool: core::ptr::null(), | ||
| 41 | tracespoolsize: 0, | ||
| 42 | }); | ||
| 43 | } | ||
| 44 | |||
| 45 | Self { | ||
| 46 | ipcc_mm_release_buffer_channel, | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | pub async fn run_queue(&mut self) -> ! { | ||
| 51 | loop { | ||
| 52 | poll_fn(|cx| unsafe { | ||
| 53 | MM_WAKER.register(cx.waker()); | ||
| 54 | if critical_section::with(|cs| LinkedListNode::is_empty(cs, LOCAL_FREE_BUF_QUEUE.as_mut_ptr())) { | ||
| 55 | Poll::Pending | ||
| 56 | } else { | ||
| 57 | Poll::Ready(()) | ||
| 58 | } | ||
| 59 | }) | ||
| 60 | .await; | ||
| 61 | |||
| 62 | self.ipcc_mm_release_buffer_channel | ||
| 63 | .send(|| { | ||
| 64 | critical_section::with(|cs| unsafe { | ||
| 65 | while let Some(node_ptr) = LinkedListNode::remove_head(cs, LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { | ||
| 66 | LinkedListNode::insert_head(cs, FREE_BUF_QUEUE.as_mut_ptr(), node_ptr); | ||
| 67 | } | ||
| 68 | }) | ||
| 69 | }) | ||
| 70 | .await; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | impl<'a> evt::MemoryManager for MemoryManager<'a> { | ||
| 76 | /// SAFETY: passing a pointer to something other than a managed event packet is UB | ||
| 77 | unsafe fn drop_event_packet(evt: *mut EvtPacket) { | ||
| 78 | critical_section::with(|cs| unsafe { | ||
| 79 | LinkedListNode::insert_head(cs, LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), evt as *mut _); | ||
| 80 | }); | ||
| 81 | |||
| 82 | MM_WAKER.wake(); | ||
| 83 | } | ||
| 84 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/sub/mod.rs b/embassy-stm32-wpan/src/wb55/sub/mod.rs new file mode 100644 index 000000000..d3ebd822a --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/sub/mod.rs | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #[cfg(feature = "wb55_ble")] | ||
| 2 | pub mod ble; | ||
| 3 | #[cfg(feature = "wb55_mac")] | ||
| 4 | pub mod mac; | ||
| 5 | pub mod mm; | ||
| 6 | pub mod sys; | ||
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 | } | ||
diff --git a/embassy-stm32-wpan/src/wb55/tables.rs b/embassy-stm32-wpan/src/wb55/tables.rs new file mode 100644 index 000000000..2e6a9199b --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/tables.rs | |||
| @@ -0,0 +1,283 @@ | |||
| 1 | use core::mem::MaybeUninit; | ||
| 2 | |||
| 3 | use aligned::{A4, Aligned}; | ||
| 4 | use bit_field::BitField; | ||
| 5 | |||
| 6 | use crate::cmd::{AclDataPacket, CmdPacket}; | ||
| 7 | #[cfg(feature = "wb55_mac")] | ||
| 8 | use crate::consts::C_SIZE_CMD_STRING; | ||
| 9 | use crate::consts::{POOL_SIZE, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; | ||
| 10 | use crate::unsafe_linked_list::LinkedListNode; | ||
| 11 | |||
| 12 | #[derive(Debug, Copy, Clone)] | ||
| 13 | #[repr(C, packed)] | ||
| 14 | pub struct SafeBootInfoTable { | ||
| 15 | version: u32, | ||
| 16 | } | ||
| 17 | |||
| 18 | #[derive(Debug, Copy, Clone)] | ||
| 19 | #[repr(C, packed)] | ||
| 20 | pub struct RssInfoTable { | ||
| 21 | pub version: u32, | ||
| 22 | pub memory_size: u32, | ||
| 23 | pub rss_info: u32, | ||
| 24 | } | ||
| 25 | |||
| 26 | /** | ||
| 27 | * Version | ||
| 28 | * \[0:3\] = Build - 0: Untracked - 15:Released - x: Tracked version | ||
| 29 | * \[4:7\] = branch - 0: Mass Market - x: ... | ||
| 30 | * \[8:15\] = Subversion | ||
| 31 | * \[16:23\] = Version minor | ||
| 32 | * \[24:31\] = Version major | ||
| 33 | * | ||
| 34 | * Memory Size | ||
| 35 | * \[0:7\] = Flash ( Number of 4k sector) | ||
| 36 | * \[8:15\] = Reserved ( Shall be set to 0 - may be used as flash extension ) | ||
| 37 | * \[16:23\] = SRAM2b ( Number of 1k sector) | ||
| 38 | * \[24:31\] = SRAM2a ( Number of 1k sector) | ||
| 39 | */ | ||
| 40 | #[derive(Debug, Copy, Clone)] | ||
| 41 | #[repr(C, packed)] | ||
| 42 | pub struct WirelessFwInfoTable { | ||
| 43 | pub version: u32, | ||
| 44 | pub memory_size: u32, | ||
| 45 | pub thread_info: u32, | ||
| 46 | pub ble_info: u32, | ||
| 47 | } | ||
| 48 | |||
| 49 | impl WirelessFwInfoTable { | ||
| 50 | pub fn version_major(&self) -> u8 { | ||
| 51 | let version = self.version; | ||
| 52 | (version.get_bits(24..31) & 0xff) as u8 | ||
| 53 | } | ||
| 54 | |||
| 55 | pub fn version_minor(&self) -> u8 { | ||
| 56 | let version = self.version; | ||
| 57 | (version.clone().get_bits(16..23) & 0xff) as u8 | ||
| 58 | } | ||
| 59 | |||
| 60 | pub fn subversion(&self) -> u8 { | ||
| 61 | let version = self.version; | ||
| 62 | (version.clone().get_bits(8..15) & 0xff) as u8 | ||
| 63 | } | ||
| 64 | |||
| 65 | /// Size of FLASH, expressed in number of 4K sectors. | ||
| 66 | pub fn flash_size(&self) -> u8 { | ||
| 67 | let memory_size = self.memory_size; | ||
| 68 | (memory_size.clone().get_bits(0..7) & 0xff) as u8 | ||
| 69 | } | ||
| 70 | |||
| 71 | /// Size of SRAM2a, expressed in number of 1K sectors. | ||
| 72 | pub fn sram2a_size(&self) -> u8 { | ||
| 73 | let memory_size = self.memory_size; | ||
| 74 | (memory_size.clone().get_bits(24..31) & 0xff) as u8 | ||
| 75 | } | ||
| 76 | |||
| 77 | /// Size of SRAM2b, expressed in number of 1K sectors. | ||
| 78 | pub fn sram2b_size(&self) -> u8 { | ||
| 79 | let memory_size = self.memory_size; | ||
| 80 | (memory_size.clone().get_bits(16..23) & 0xff) as u8 | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | #[derive(Debug, Clone)] | ||
| 85 | #[repr(C)] | ||
| 86 | pub struct DeviceInfoTable { | ||
| 87 | pub safe_boot_info_table: SafeBootInfoTable, | ||
| 88 | pub rss_info_table: RssInfoTable, | ||
| 89 | pub wireless_fw_info_table: WirelessFwInfoTable, | ||
| 90 | } | ||
| 91 | |||
| 92 | /// The bluetooth reference table, as defined in figure 67 of STM32WX AN5289. | ||
| 93 | #[derive(Debug)] | ||
| 94 | #[repr(C)] | ||
| 95 | pub struct BleTable { | ||
| 96 | /// A pointer to the buffer that is used for sending BLE commands. | ||
| 97 | pub pcmd_buffer: *mut CmdPacket, | ||
| 98 | /// A pointer to the buffer used for storing Command statuses. | ||
| 99 | pub pcs_buffer: *const u8, | ||
| 100 | /// A pointer to the event queue, over which IPCC BLE events are sent. This may be accessed via | ||
| 101 | /// [crate::sub::ble::Ble::tl_read]. | ||
| 102 | pub pevt_queue: *const u8, | ||
| 103 | /// A pointer to the buffer that is used for sending HCI (Host-Controller Interface) ACL | ||
| 104 | /// (Asynchronous Connection-oriented Logical transport) commands (unused). | ||
| 105 | pub phci_acl_data_buffer: *mut AclDataPacket, | ||
| 106 | } | ||
| 107 | |||
| 108 | #[derive(Debug)] | ||
| 109 | #[repr(C)] | ||
| 110 | pub struct ThreadTable { | ||
| 111 | pub nostack_buffer: *const u8, | ||
| 112 | pub clicmdrsp_buffer: *const u8, | ||
| 113 | pub otcmdrsp_buffer: *const u8, | ||
| 114 | } | ||
| 115 | |||
| 116 | #[derive(Debug)] | ||
| 117 | #[repr(C)] | ||
| 118 | pub struct LldTestsTable { | ||
| 119 | pub clicmdrsp_buffer: *const u8, | ||
| 120 | pub m0cmd_buffer: *const u8, | ||
| 121 | } | ||
| 122 | |||
| 123 | // TODO: use later | ||
| 124 | #[derive(Debug)] | ||
| 125 | #[repr(C)] | ||
| 126 | pub struct BleLldTable { | ||
| 127 | pub cmdrsp_buffer: *const u8, | ||
| 128 | pub m0cmd_buffer: *const u8, | ||
| 129 | } | ||
| 130 | |||
| 131 | // TODO: use later | ||
| 132 | #[derive(Debug)] | ||
| 133 | #[repr(C)] | ||
| 134 | pub struct ZigbeeTable { | ||
| 135 | pub notif_m0_to_m4_buffer: *const u8, | ||
| 136 | pub appli_cmd_m4_to_m0_bufer: *const u8, | ||
| 137 | pub request_m0_to_m4_buffer: *const u8, | ||
| 138 | } | ||
| 139 | |||
| 140 | #[derive(Debug)] | ||
| 141 | #[repr(C)] | ||
| 142 | pub struct SysTable { | ||
| 143 | pub pcmd_buffer: *mut CmdPacket, | ||
| 144 | pub sys_queue: *const LinkedListNode, | ||
| 145 | } | ||
| 146 | |||
| 147 | #[derive(Debug)] | ||
| 148 | #[repr(C)] | ||
| 149 | pub struct MemManagerTable { | ||
| 150 | pub spare_ble_buffer: *const u8, | ||
| 151 | pub spare_sys_buffer: *const u8, | ||
| 152 | |||
| 153 | pub blepool: *const u8, | ||
| 154 | pub blepoolsize: u32, | ||
| 155 | |||
| 156 | pub pevt_free_buffer_queue: *mut LinkedListNode, | ||
| 157 | |||
| 158 | pub traces_evt_pool: *const u8, | ||
| 159 | pub tracespoolsize: u32, | ||
| 160 | } | ||
| 161 | |||
| 162 | #[derive(Debug)] | ||
| 163 | #[repr(C)] | ||
| 164 | pub struct TracesTable { | ||
| 165 | pub traces_queue: *const u8, | ||
| 166 | } | ||
| 167 | |||
| 168 | #[derive(Debug)] | ||
| 169 | #[repr(C)] | ||
| 170 | pub struct Mac802_15_4Table { | ||
| 171 | pub p_cmdrsp_buffer: *const u8, | ||
| 172 | pub p_notack_buffer: *const u8, | ||
| 173 | pub evt_queue: *const u8, | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Reference table. Contains pointers to all other tables. | ||
| 177 | #[derive(Debug, Copy, Clone)] | ||
| 178 | #[repr(C)] | ||
| 179 | pub struct RefTable { | ||
| 180 | pub device_info_table: *const DeviceInfoTable, | ||
| 181 | pub ble_table: *const BleTable, | ||
| 182 | pub thread_table: *const ThreadTable, | ||
| 183 | pub sys_table: *const SysTable, | ||
| 184 | pub mem_manager_table: *const MemManagerTable, | ||
| 185 | pub traces_table: *const TracesTable, | ||
| 186 | pub mac_802_15_4_table: *const Mac802_15_4Table, | ||
| 187 | pub zigbee_table: *const ZigbeeTable, | ||
| 188 | pub lld_tests_table: *const LldTestsTable, | ||
| 189 | pub ble_lld_table: *const BleLldTable, | ||
| 190 | } | ||
| 191 | |||
| 192 | // --------------------- ref table --------------------- | ||
| 193 | #[unsafe(link_section = "TL_REF_TABLE")] | ||
| 194 | pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::zeroed(); | ||
| 195 | |||
| 196 | #[unsafe(link_section = "MB_MEM1")] | ||
| 197 | pub static mut TL_DEVICE_INFO_TABLE: Aligned<A4, MaybeUninit<DeviceInfoTable>> = Aligned(MaybeUninit::zeroed()); | ||
| 198 | |||
| 199 | #[unsafe(link_section = "MB_MEM1")] | ||
| 200 | pub static mut TL_BLE_TABLE: Aligned<A4, MaybeUninit<BleTable>> = Aligned(MaybeUninit::zeroed()); | ||
| 201 | |||
| 202 | #[unsafe(link_section = "MB_MEM1")] | ||
| 203 | pub static mut TL_THREAD_TABLE: Aligned<A4, MaybeUninit<ThreadTable>> = Aligned(MaybeUninit::zeroed()); | ||
| 204 | |||
| 205 | #[unsafe(link_section = "MB_MEM1")] | ||
| 206 | pub static mut TL_LLD_TESTS_TABLE: Aligned<A4, MaybeUninit<LldTestsTable>> = Aligned(MaybeUninit::zeroed()); | ||
| 207 | |||
| 208 | #[unsafe(link_section = "MB_MEM1")] | ||
| 209 | pub static mut TL_BLE_LLD_TABLE: Aligned<A4, MaybeUninit<BleLldTable>> = Aligned(MaybeUninit::zeroed()); | ||
| 210 | |||
| 211 | #[unsafe(link_section = "MB_MEM1")] | ||
| 212 | pub static mut TL_SYS_TABLE: Aligned<A4, MaybeUninit<SysTable>> = Aligned(MaybeUninit::zeroed()); | ||
| 213 | |||
| 214 | #[unsafe(link_section = "MB_MEM1")] | ||
| 215 | pub static mut TL_MEM_MANAGER_TABLE: Aligned<A4, MaybeUninit<MemManagerTable>> = Aligned(MaybeUninit::zeroed()); | ||
| 216 | |||
| 217 | #[unsafe(link_section = "MB_MEM1")] | ||
| 218 | pub static mut TL_TRACES_TABLE: Aligned<A4, MaybeUninit<TracesTable>> = Aligned(MaybeUninit::zeroed()); | ||
| 219 | |||
| 220 | #[unsafe(link_section = "MB_MEM1")] | ||
| 221 | pub static mut TL_MAC_802_15_4_TABLE: Aligned<A4, MaybeUninit<Mac802_15_4Table>> = Aligned(MaybeUninit::zeroed()); | ||
| 222 | |||
| 223 | #[unsafe(link_section = "MB_MEM1")] | ||
| 224 | pub static mut TL_ZIGBEE_TABLE: Aligned<A4, MaybeUninit<ZigbeeTable>> = Aligned(MaybeUninit::zeroed()); | ||
| 225 | |||
| 226 | // --------------------- tables --------------------- | ||
| 227 | #[unsafe(link_section = "MB_MEM1")] | ||
| 228 | pub static mut FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed()); | ||
| 229 | |||
| 230 | #[allow(dead_code)] | ||
| 231 | #[unsafe(link_section = "MB_MEM1")] | ||
| 232 | pub static mut TRACES_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed()); | ||
| 233 | |||
| 234 | #[unsafe(link_section = "MB_MEM2")] | ||
| 235 | pub static mut CS_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]>> = | ||
| 236 | Aligned(MaybeUninit::zeroed()); | ||
| 237 | |||
| 238 | #[unsafe(link_section = "MB_MEM2")] | ||
| 239 | pub static mut EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed()); | ||
| 240 | |||
| 241 | #[unsafe(link_section = "MB_MEM2")] | ||
| 242 | pub static mut SYSTEM_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::zeroed()); | ||
| 243 | |||
| 244 | // --------------------- app tables --------------------- | ||
| 245 | #[cfg(feature = "wb55_mac")] | ||
| 246 | #[unsafe(link_section = "MB_MEM2")] | ||
| 247 | pub static mut MAC_802_15_4_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed()); | ||
| 248 | |||
| 249 | #[cfg(feature = "wb55_mac")] | ||
| 250 | #[unsafe(link_section = "MB_MEM2")] | ||
| 251 | pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit< | ||
| 252 | Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>, | ||
| 253 | > = MaybeUninit::zeroed(); | ||
| 254 | |||
| 255 | #[unsafe(link_section = "MB_MEM2")] | ||
| 256 | pub static mut EVT_POOL: Aligned<A4, MaybeUninit<[u8; POOL_SIZE]>> = Aligned(MaybeUninit::zeroed()); | ||
| 257 | |||
| 258 | #[unsafe(link_section = "MB_MEM2")] | ||
| 259 | pub static mut SYS_CMD_BUF: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed()); | ||
| 260 | |||
| 261 | #[unsafe(link_section = "MB_MEM2")] | ||
| 262 | pub static mut SYS_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> = | ||
| 263 | Aligned(MaybeUninit::zeroed()); | ||
| 264 | |||
| 265 | #[cfg(feature = "wb55_mac")] | ||
| 266 | #[unsafe(link_section = "MB_MEM2")] | ||
| 267 | pub static mut MAC_802_15_4_CNFINDNOT: Aligned<A4, MaybeUninit<[u8; C_SIZE_CMD_STRING]>> = | ||
| 268 | Aligned(MaybeUninit::zeroed()); | ||
| 269 | |||
| 270 | #[cfg(feature = "wb55_ble")] | ||
| 271 | #[unsafe(link_section = "MB_MEM1")] | ||
| 272 | pub static mut BLE_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::zeroed()); | ||
| 273 | |||
| 274 | #[cfg(feature = "wb55_ble")] | ||
| 275 | #[unsafe(link_section = "MB_MEM2")] | ||
| 276 | pub static mut BLE_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> = | ||
| 277 | Aligned(MaybeUninit::zeroed()); | ||
| 278 | |||
| 279 | #[cfg(feature = "wb55_ble")] | ||
| 280 | #[unsafe(link_section = "MB_MEM2")] | ||
| 281 | // fuck these "magic" numbers from ST ---v---v | ||
| 282 | pub static mut HCI_ACL_DATA_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> = | ||
| 283 | Aligned(MaybeUninit::zeroed()); | ||
diff --git a/embassy-stm32-wpan/src/wb55/unsafe_linked_list.rs b/embassy-stm32-wpan/src/wb55/unsafe_linked_list.rs new file mode 100644 index 000000000..c84ee1bb6 --- /dev/null +++ b/embassy-stm32-wpan/src/wb55/unsafe_linked_list.rs | |||
| @@ -0,0 +1,269 @@ | |||
| 1 | //! Unsafe linked list. | ||
| 2 | //! Translated from ST's C by `c2rust` tool. | ||
| 3 | |||
| 4 | #![allow( | ||
| 5 | dead_code, | ||
| 6 | mutable_transmutes, | ||
| 7 | non_camel_case_types, | ||
| 8 | non_snake_case, | ||
| 9 | non_upper_case_globals, | ||
| 10 | unused_assignments, | ||
| 11 | unused_mut | ||
| 12 | )] | ||
| 13 | |||
| 14 | use core::fmt::Debug; | ||
| 15 | use core::ptr; | ||
| 16 | |||
| 17 | use critical_section::CriticalSection; | ||
| 18 | |||
| 19 | #[derive(Copy, Clone)] | ||
| 20 | #[repr(C, packed(4))] | ||
| 21 | pub struct LinkedListNode { | ||
| 22 | pub next: *mut LinkedListNode, | ||
| 23 | pub prev: *mut LinkedListNode, | ||
| 24 | } | ||
| 25 | |||
| 26 | impl Default for LinkedListNode { | ||
| 27 | fn default() -> Self { | ||
| 28 | LinkedListNode { | ||
| 29 | next: core::ptr::null_mut(), | ||
| 30 | prev: core::ptr::null_mut(), | ||
| 31 | } | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | impl LinkedListNode { | ||
| 36 | pub unsafe fn init_head(mut p_list_head: *mut LinkedListNode) { | ||
| 37 | ptr::write_volatile( | ||
| 38 | p_list_head, | ||
| 39 | LinkedListNode { | ||
| 40 | next: p_list_head, | ||
| 41 | prev: p_list_head, | ||
| 42 | }, | ||
| 43 | ); | ||
| 44 | } | ||
| 45 | |||
| 46 | pub unsafe fn is_empty(_cs: CriticalSection, mut p_list_head: *mut LinkedListNode) -> bool { | ||
| 47 | ptr::read_volatile(p_list_head).next == p_list_head | ||
| 48 | } | ||
| 49 | |||
| 50 | /// Insert `node` after `list_head` and before the next node | ||
| 51 | pub unsafe fn insert_head( | ||
| 52 | _cs: CriticalSection, | ||
| 53 | mut p_list_head: *mut LinkedListNode, | ||
| 54 | mut p_node: *mut LinkedListNode, | ||
| 55 | ) { | ||
| 56 | let mut list_head = ptr::read_volatile(p_list_head); | ||
| 57 | if p_list_head != list_head.next { | ||
| 58 | let mut node_next = ptr::read_volatile(list_head.next); | ||
| 59 | let node = LinkedListNode { | ||
| 60 | next: list_head.next, | ||
| 61 | prev: p_list_head, | ||
| 62 | }; | ||
| 63 | |||
| 64 | list_head.next = p_node; | ||
| 65 | node_next.prev = p_node; | ||
| 66 | |||
| 67 | // All nodes must be written because they will all be seen by another core | ||
| 68 | ptr::write_volatile(p_node, node); | ||
| 69 | ptr::write_volatile(node.next, node_next); | ||
| 70 | ptr::write_volatile(p_list_head, list_head); | ||
| 71 | } else { | ||
| 72 | let node = LinkedListNode { | ||
| 73 | next: list_head.next, | ||
| 74 | prev: p_list_head, | ||
| 75 | }; | ||
| 76 | |||
| 77 | list_head.next = p_node; | ||
| 78 | list_head.prev = p_node; | ||
| 79 | |||
| 80 | // All nodes must be written because they will all be seen by another core | ||
| 81 | ptr::write_volatile(p_node, node); | ||
| 82 | ptr::write_volatile(p_list_head, list_head); | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | /// Insert `node` before `list_tail` and after the second-to-last node | ||
| 87 | pub unsafe fn insert_tail( | ||
| 88 | _cs: CriticalSection, | ||
| 89 | mut p_list_tail: *mut LinkedListNode, | ||
| 90 | mut p_node: *mut LinkedListNode, | ||
| 91 | ) { | ||
| 92 | let mut list_tail = ptr::read_volatile(p_list_tail); | ||
| 93 | if p_list_tail != list_tail.prev { | ||
| 94 | let mut node_prev = ptr::read_volatile(list_tail.prev); | ||
| 95 | let node = LinkedListNode { | ||
| 96 | next: p_list_tail, | ||
| 97 | prev: list_tail.prev, | ||
| 98 | }; | ||
| 99 | |||
| 100 | list_tail.prev = p_node; | ||
| 101 | node_prev.next = p_node; | ||
| 102 | |||
| 103 | // All nodes must be written because they will all be seen by another core | ||
| 104 | ptr::write_volatile(p_node, node); | ||
| 105 | ptr::write_volatile(node.prev, node_prev); | ||
| 106 | ptr::write_volatile(p_list_tail, list_tail); | ||
| 107 | } else { | ||
| 108 | let node = LinkedListNode { | ||
| 109 | next: p_list_tail, | ||
| 110 | prev: list_tail.prev, | ||
| 111 | }; | ||
| 112 | |||
| 113 | list_tail.prev = p_node; | ||
| 114 | list_tail.next = p_node; | ||
| 115 | |||
| 116 | // All nodes must be written because they will all be seen by another core | ||
| 117 | ptr::write_volatile(p_node, node); | ||
| 118 | ptr::write_volatile(p_list_tail, list_tail); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Remove `node` from the linked list | ||
| 123 | pub unsafe fn remove_node(_cs: CriticalSection, mut p_node: *mut LinkedListNode) { | ||
| 124 | let node = ptr::read_unaligned(p_node); | ||
| 125 | |||
| 126 | if node.next != node.prev { | ||
| 127 | let mut node_next = ptr::read_volatile(node.next); | ||
| 128 | let mut node_prev = ptr::read_volatile(node.prev); | ||
| 129 | |||
| 130 | node_prev.next = node.next; | ||
| 131 | node_next.prev = node.prev; | ||
| 132 | |||
| 133 | ptr::write_volatile(node.next, node_next); | ||
| 134 | ptr::write_volatile(node.prev, node_prev); | ||
| 135 | } else { | ||
| 136 | let mut node_next = ptr::read_volatile(node.next); | ||
| 137 | |||
| 138 | node_next.next = node.next; | ||
| 139 | node_next.prev = node.prev; | ||
| 140 | |||
| 141 | ptr::write_volatile(node.next, node_next); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | /// Remove `list_head` and return a pointer to the `node`. | ||
| 146 | pub unsafe fn remove_head( | ||
| 147 | _cs: CriticalSection, | ||
| 148 | mut p_list_head: *mut LinkedListNode, | ||
| 149 | ) -> Option<*mut LinkedListNode> { | ||
| 150 | let list_head = ptr::read_volatile(p_list_head); | ||
| 151 | |||
| 152 | if list_head.next == p_list_head { | ||
| 153 | None | ||
| 154 | } else { | ||
| 155 | // Allowed because a removed node is not seen by another core | ||
| 156 | let p_node = list_head.next; | ||
| 157 | Self::remove_node(_cs, p_node); | ||
| 158 | |||
| 159 | Some(p_node) | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | /// Remove `list_tail` and return a pointer to the `node`. | ||
| 164 | pub unsafe fn remove_tail( | ||
| 165 | _cs: CriticalSection, | ||
| 166 | mut p_list_tail: *mut LinkedListNode, | ||
| 167 | ) -> Option<*mut LinkedListNode> { | ||
| 168 | let list_tail = ptr::read_volatile(p_list_tail); | ||
| 169 | |||
| 170 | if list_tail.prev == p_list_tail { | ||
| 171 | None | ||
| 172 | } else { | ||
| 173 | // Allowed because a removed node is not seen by another core | ||
| 174 | let p_node = list_tail.prev; | ||
| 175 | Self::remove_node(_cs, p_node); | ||
| 176 | |||
| 177 | Some(p_node) | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | pub unsafe fn insert_node_after( | ||
| 182 | _cs: CriticalSection, | ||
| 183 | mut p_node: *mut LinkedListNode, | ||
| 184 | mut p_ref_node: *mut LinkedListNode, | ||
| 185 | ) { | ||
| 186 | let mut node = ptr::read_volatile(p_node); | ||
| 187 | let mut ref_node = ptr::read_volatile(p_ref_node); | ||
| 188 | let mut prev_node = ptr::read_volatile(ref_node.next); | ||
| 189 | |||
| 190 | node.next = ref_node.next; | ||
| 191 | node.prev = p_ref_node; | ||
| 192 | ref_node.next = p_node; | ||
| 193 | prev_node.prev = p_node; | ||
| 194 | |||
| 195 | ptr::write_volatile(p_node, node); | ||
| 196 | ptr::write_volatile(p_ref_node, ref_node); | ||
| 197 | ptr::write_volatile(node.next, prev_node); | ||
| 198 | } | ||
| 199 | |||
| 200 | pub unsafe fn insert_node_before( | ||
| 201 | _cs: CriticalSection, | ||
| 202 | mut node: *mut LinkedListNode, | ||
| 203 | mut ref_node: *mut LinkedListNode, | ||
| 204 | ) { | ||
| 205 | (*node).next = ref_node; | ||
| 206 | (*node).prev = (*ref_node).prev; | ||
| 207 | (*ref_node).prev = node; | ||
| 208 | (*(*node).prev).next = node; | ||
| 209 | |||
| 210 | todo!("this function has not been converted to volatile semantics"); | ||
| 211 | } | ||
| 212 | |||
| 213 | pub unsafe fn get_size(_cs: CriticalSection, mut list_head: *mut LinkedListNode) -> usize { | ||
| 214 | let mut size = 0; | ||
| 215 | let mut temp: *mut LinkedListNode = core::ptr::null_mut::<LinkedListNode>(); | ||
| 216 | |||
| 217 | temp = (*list_head).next; | ||
| 218 | while temp != list_head { | ||
| 219 | size += 1; | ||
| 220 | temp = (*temp).next | ||
| 221 | } | ||
| 222 | |||
| 223 | let _ = size; | ||
| 224 | |||
| 225 | todo!("this function has not been converted to volatile semantics"); | ||
| 226 | } | ||
| 227 | |||
| 228 | pub unsafe fn get_next_node(_cs: CriticalSection, mut p_ref_node: *mut LinkedListNode) -> *mut LinkedListNode { | ||
| 229 | let ref_node = ptr::read_volatile(p_ref_node); | ||
| 230 | |||
| 231 | // Allowed because a removed node is not seen by another core | ||
| 232 | ref_node.next | ||
| 233 | } | ||
| 234 | |||
| 235 | pub unsafe fn get_prev_node(_cs: CriticalSection, mut p_ref_node: *mut LinkedListNode) -> *mut LinkedListNode { | ||
| 236 | let ref_node = ptr::read_volatile(p_ref_node); | ||
| 237 | |||
| 238 | // Allowed because a removed node is not seen by another core | ||
| 239 | ref_node.prev | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | pub struct DebuggableLinkedListNode(*const LinkedListNode); | ||
| 244 | |||
| 245 | impl Debug for DebuggableLinkedListNode { | ||
| 246 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 247 | // Safe because this is just reading memory, and using unaligned to do it | ||
| 248 | let p_node = self.0; | ||
| 249 | |||
| 250 | f.write_fmt(format_args!("iterating list from node: {:x}", p_node as usize))?; | ||
| 251 | |||
| 252 | let mut p_current_node = p_node; | ||
| 253 | for _ in 0..30 { | ||
| 254 | let current_node = unsafe { ptr::read_unaligned(p_current_node) }; | ||
| 255 | f.write_fmt(format_args!( | ||
| 256 | "node (prev, current, next): {:x}, {:x}, {:x}", | ||
| 257 | current_node.prev as usize, p_current_node as usize, current_node.next as usize | ||
| 258 | ))?; | ||
| 259 | |||
| 260 | if current_node.next == p_node as *mut _ { | ||
| 261 | break; | ||
| 262 | } | ||
| 263 | |||
| 264 | p_current_node = current_node.next; | ||
| 265 | } | ||
| 266 | |||
| 267 | Ok(()) | ||
| 268 | } | ||
| 269 | } | ||
