From d5a4457b5e3a95a12f249315fb1d9b6a577307f2 Mon Sep 17 00:00:00 2001 From: goueslati Date: Wed, 12 Jul 2023 15:06:56 +0100 Subject: [PATCH] parsing MAC structs --- embassy-stm32-wpan/Cargo.toml | 7 +- embassy-stm32-wpan/src/sub/mac/commands.rs | 55 +-- embassy-stm32-wpan/src/sub/mac/event.rs | 94 +++++ embassy-stm32-wpan/src/sub/mac/helpers.rs | 7 + embassy-stm32-wpan/src/sub/mac/indications.rs | 295 ++++++++++++-- embassy-stm32-wpan/src/sub/mac/macros.rs | 32 ++ embassy-stm32-wpan/src/sub/mac/mod.rs | 63 ++- embassy-stm32-wpan/src/sub/mac/opcodes.rs | 63 +++ embassy-stm32-wpan/src/sub/mac/responses.rs | 302 +++++++++++++-- embassy-stm32-wpan/src/sub/mac/typedefs.rs | 359 ++++++++++++++---- examples/stm32wb/src/bin/mac_ffd.rs | 99 ++--- examples/stm32wb/src/bin/mac_rfd.rs | 14 +- 12 files changed, 1107 insertions(+), 283 deletions(-) create mode 100644 embassy-stm32-wpan/src/sub/mac/event.rs create mode 100644 embassy-stm32-wpan/src/sub/mac/helpers.rs create mode 100644 embassy-stm32-wpan/src/sub/mac/macros.rs diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 4b5dcdd29..1325faed9 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -26,13 +26,14 @@ aligned = "0.4.1" bit_field = "0.10.2" stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } stm32wb-hci = { version = "0.1.2", features = ["version-5-0"], optional = true } +bitflags = { version = "2.3.3", optional = true } [features] -default = ["stm32wb55rg", "mac", "ble"] +default = ["stm32wb55rg", "mac", "ble", "defmt"] defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"] ble = ["dep:stm32wb-hci"] -mac = [] +mac = ["dep:bitflags"] stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ] stm32wb15cc = [ "embassy-stm32/stm32wb15cc" ] @@ -49,4 +50,4 @@ stm32wb55rg = [ "embassy-stm32/stm32wb55rg" ] stm32wb55vc = [ "embassy-stm32/stm32wb55vc" ] stm32wb55ve = [ "embassy-stm32/stm32wb55ve" ] stm32wb55vg = [ "embassy-stm32/stm32wb55vg" ] -stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ] \ No newline at end of file +stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ] diff --git a/embassy-stm32-wpan/src/sub/mac/commands.rs b/embassy-stm32-wpan/src/sub/mac/commands.rs index 4965a46eb..d8a4e3ee1 100644 --- a/embassy-stm32-wpan/src/sub/mac/commands.rs +++ b/embassy-stm32-wpan/src/sub/mac/commands.rs @@ -1,5 +1,8 @@ use super::opcodes::OpcodeM4ToM0; -use super::typedefs::{AddressMode, GtsCharacteristics, MacAddress, PibId}; +use super::typedefs::{ + AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, PibId, + ScanType, SecurityLevel, +}; pub trait MacCommand { const OPCODE: OpcodeM4ToM0; @@ -14,19 +17,19 @@ pub trait MacCommand { #[repr(C)] pub struct AssociateRequest { /// the logical channel on which to attempt association - pub channel_number: u8, + pub channel_number: MacChannel, /// the channel page on which to attempt association pub channel_page: u8, /// coordinator addressing mode pub coord_addr_mode: AddressMode, /// operational capabilities of the associating device - pub capability_information: u8, + pub capability_information: Capabilities, /// the identifier of the PAN with which to associate pub coord_pan_id: [u8; 2], /// the security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// the mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// the originator of the key to be used pub key_source: [u8; 8], /// Coordinator address @@ -48,15 +51,15 @@ pub struct DisassociateRequest { /// the identifier of the PAN of the device pub device_pan_id: [u8; 2], /// the reason for the disassociation - pub disassociate_reason: u8, + pub disassociation_reason: DisassociationReason, /// device address pub device_address: MacAddress, /// `true` if the disassociation notification command is to be sent indirectly pub tx_indirect: bool, /// the security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// the mode to be used to indetify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, /// the originator of the key to be used @@ -86,9 +89,9 @@ pub struct GtsRequest { /// the characteristics of the GTS pub characteristics: GtsCharacteristics, /// the security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// the mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, /// the originator of the key to be used @@ -147,19 +150,19 @@ impl MacCommand for RxEnableRequest { #[repr(C)] pub struct ScanRequest { /// the type of scan to be performed - pub scan_type: u8, + pub scan_type: ScanType, /// the time spent on scanning each channel pub scan_duration: u8, /// channel page on which to perform the scan pub channel_page: u8, /// security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// indicate which channels are to be scanned pub scan_channels: [u8; 4], /// originator the key to be used pub key_source: [u8; 8], /// mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// index of the key to be used pub key_index: u8, } @@ -191,7 +194,7 @@ pub struct StartRequest { /// PAN indentifier to used by the device pub pan_id: [u8; 2], /// logical channel on which to begin - pub channel_number: u8, + pub channel_number: MacChannel, /// channel page on which to begin pub channel_page: u8, /// time at which to begin transmitting beacons @@ -207,15 +210,15 @@ pub struct StartRequest { /// indicated if the coordinator realignment command is to be trasmitted pub coord_realignment: u8, /// indicated if the coordinator realignment command is to be trasmitted - pub coord_realign_security_level: u8, + pub coord_realign_security_level: SecurityLevel, /// index of the key to be used pub coord_realign_key_id_index: u8, /// originator of the key to be used pub coord_realign_key_source: [u8; 8], /// security level to be used for beacon frames - pub beacon_security_level: u8, + pub beacon_security_level: SecurityLevel, /// mode used to identify the key to be used - pub beacon_key_id_mode: u8, + pub beacon_key_id_mode: KeyIdMode, /// index of the key to be used pub beacon_key_index: u8, /// originator of the key to be used @@ -232,7 +235,7 @@ impl MacCommand for StartRequest { #[repr(C)] pub struct SyncRequest { /// the channel number on which to attempt coordinator synchronization - pub channel_number: u8, + pub channel_number: MacChannel, /// the channel page on which to attempt coordinator synchronization pub channel_page: u8, /// `true` if the MLME is to synchronize with the next beacon and attempts @@ -253,9 +256,9 @@ pub struct PollRequest { /// addressing mode of the coordinator pub coord_addr_mode: AddressMode, /// security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// index of the key to be used pub key_index: u8, /// coordinator address @@ -335,9 +338,9 @@ pub struct DataRequest { /// the pending bit transmission options for the MSDU pub indirect_tx: u8, /// the security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// the mode used to indentify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, /// the originator of the key to be used @@ -381,11 +384,11 @@ pub struct AssociateResponse { /// status of the association attempt pub status: u8, /// security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// the originator of the key to be used pub key_source: [u8; 8], /// the mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, } @@ -405,11 +408,11 @@ pub struct OrphanResponse { /// if the orphaned device is associated with coordinator or not pub associated_member: bool, /// security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// the originator of the key to be used pub key_source: [u8; 8], /// the mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, } diff --git a/embassy-stm32-wpan/src/sub/mac/event.rs b/embassy-stm32-wpan/src/sub/mac/event.rs new file mode 100644 index 000000000..aaf965565 --- /dev/null +++ b/embassy-stm32-wpan/src/sub/mac/event.rs @@ -0,0 +1,94 @@ +use super::helpers::to_u16; +use super::indications::{ + AssociateIndication, BeaconNotifyIndication, CommStatusIndication, DataIndication, DisassociateIndication, + DpsIndication, GtsIndication, OrphanIndication, PollIndication, SyncLossIndication, +}; +use super::responses::{ + AssociateConfirm, CalibrateConfirm, DataConfirm, DisassociateConfirm, DpsConfirm, GetConfirm, GtsConfirm, + PollConfirm, PurgeConfirm, ResetConfirm, RxEnableConfirm, ScanConfirm, SetConfirm, SoundingConfirm, StartConfirm, +}; +use crate::sub::mac::opcodes::OpcodeM0ToM4; + +pub trait ParseableMacEvent { + const SIZE: usize; + + fn validate(buf: &[u8]) -> Result<(), ()> { + if buf.len() < Self::SIZE { + return Err(()); + } + + Ok(()) + } + + fn try_parse(buf: &[u8]) -> Result + where + Self: Sized; +} + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum MacEvent { + MlmeAssociateCnf(AssociateConfirm), + MlmeDisassociateCnf(DisassociateConfirm), + MlmeGetCnf(GetConfirm), + MlmeGtsCnf(GtsConfirm), + MlmeResetCnf(ResetConfirm), + MlmeRxEnableCnf(RxEnableConfirm), + MlmeScanCnf(ScanConfirm), + MlmeSetCnf(SetConfirm), + MlmeStartCnf(StartConfirm), + MlmePollCnf(PollConfirm), + MlmeDpsCnf(DpsConfirm), + MlmeSoundingCnf(SoundingConfirm), + MlmeCalibrateCnf(CalibrateConfirm), + McpsDataCnf(DataConfirm), + McpsPurgeCnf(PurgeConfirm), + MlmeAssociateInd(AssociateIndication), + MlmeDisassociateInd(DisassociateIndication), + MlmeBeaconNotifyInd(BeaconNotifyIndication), + MlmeCommStatusInd(CommStatusIndication), + MlmeGtsInd(GtsIndication), + MlmeOrphanInd(OrphanIndication), + MlmeSyncLossInd(SyncLossIndication), + MlmeDpsInd(DpsIndication), + McpsDataInd(DataIndication), + MlmePollInd(PollIndication), +} + +impl TryFrom<&[u8]> for MacEvent { + type Error = (); + + fn try_from(value: &[u8]) -> Result { + let opcode = to_u16(&value[0..2]); + let opcode = OpcodeM0ToM4::try_from(opcode)?; + + let buf = &value[2..]; + + match opcode { + OpcodeM0ToM4::MlmeAssociateCnf => Ok(Self::MlmeAssociateCnf(AssociateConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeDisassociateCnf => Ok(Self::MlmeDisassociateCnf(DisassociateConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeGetCnf => Ok(Self::MlmeGetCnf(GetConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeGtsCnf => Ok(Self::MlmeGtsCnf(GtsConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeResetCnf => Ok(Self::MlmeResetCnf(ResetConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeRxEnableCnf => Ok(Self::MlmeRxEnableCnf(RxEnableConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeScanCnf => Ok(Self::MlmeScanCnf(ScanConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeSetCnf => Ok(Self::MlmeSetCnf(SetConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeStartCnf => Ok(Self::MlmeStartCnf(StartConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmePollCnf => Ok(Self::MlmePollCnf(PollConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeDpsCnf => Ok(Self::MlmeDpsCnf(DpsConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeSoundingCnf => Ok(Self::MlmeSoundingCnf(SoundingConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeCalibrateCnf => Ok(Self::MlmeCalibrateCnf(CalibrateConfirm::try_parse(buf)?)), + OpcodeM0ToM4::McpsDataCnf => Ok(Self::McpsDataCnf(DataConfirm::try_parse(buf)?)), + OpcodeM0ToM4::McpsPurgeCnf => Ok(Self::McpsPurgeCnf(PurgeConfirm::try_parse(buf)?)), + OpcodeM0ToM4::MlmeAssociateInd => Ok(Self::MlmeAssociateInd(AssociateIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmeDisassociateInd => Ok(Self::MlmeDisassociateInd(DisassociateIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmeBeaconNotifyInd => Ok(Self::MlmeBeaconNotifyInd(BeaconNotifyIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmeCommStatusInd => Ok(Self::MlmeCommStatusInd(CommStatusIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmeGtsInd => Ok(Self::MlmeGtsInd(GtsIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmeOrphanInd => Ok(Self::MlmeOrphanInd(OrphanIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmeSyncLossInd => Ok(Self::MlmeSyncLossInd(SyncLossIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmeDpsInd => Ok(Self::MlmeDpsInd(DpsIndication::try_parse(buf)?)), + OpcodeM0ToM4::McpsDataInd => Ok(Self::McpsDataInd(DataIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmePollInd => Ok(Self::MlmePollInd(PollIndication::try_parse(buf)?)), + } + } +} diff --git a/embassy-stm32-wpan/src/sub/mac/helpers.rs b/embassy-stm32-wpan/src/sub/mac/helpers.rs new file mode 100644 index 000000000..5a5bf8a85 --- /dev/null +++ b/embassy-stm32-wpan/src/sub/mac/helpers.rs @@ -0,0 +1,7 @@ +pub fn to_u16(buf: &[u8]) -> u16 { + ((buf[1] as u16) << 8) | buf[0] as u16 +} + +pub fn to_u32(buf: &[u8]) -> u32 { + ((buf[0] as u32) << 0) + ((buf[1] as u32) << 8) + ((buf[2] as u32) << 16) + ((buf[3] as u32) << 24) +} diff --git a/embassy-stm32-wpan/src/sub/mac/indications.rs b/embassy-stm32-wpan/src/sub/mac/indications.rs index ebca16f72..dc5ae4c44 100644 --- a/embassy-stm32-wpan/src/sub/mac/indications.rs +++ b/embassy-stm32-wpan/src/sub/mac/indications.rs @@ -1,45 +1,84 @@ use super::consts::MAX_PENDING_ADDRESS; -use super::typedefs::{AddressMode, MacAddress, PanDescriptor}; +use super::event::ParseableMacEvent; +use super::helpers::to_u32; +use super::typedefs::{ + AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor, + SecurityLevel, +}; /// MLME ASSOCIATE Indication which will be used by the MAC /// to indicate the reception of an association request command -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssociateIndication { /// Extended address of the device requesting association pub device_address: [u8; 8], /// Operational capabilities of the device requesting association - pub capability_information: u8, + pub capability_information: Capabilities, /// Security level purportedly used by the received MAC command frame - pub security_level: u8, + pub security_level: SecurityLevel, /// The mode used to identify the key used by the originator of frame - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// Index of the key used by the originator of the received frame pub key_index: u8, /// The originator of the key used by the originator of the received frame pub key_source: [u8; 8], } +impl ParseableMacEvent for AssociateIndication { + const SIZE: usize = 20; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + device_address: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], + capability_information: Capabilities::from_bits(buf[8]).ok_or(())?, + security_level: SecurityLevel::try_from(buf[9])?, + key_id_mode: KeyIdMode::try_from(buf[10])?, + key_index: buf[11], + key_source: [buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]], + }) + } +} + /// MLME DISASSOCIATE indication which will be used to send /// disassociation indication to the application. -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DisassociateIndication { /// Extended address of the device requesting association pub device_address: [u8; 8], /// The reason for the disassociation - pub disassociate_reason: u8, + pub disassociation_reason: DisassociationReason, /// The security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// The mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// The index of the key to be used pub key_index: u8, /// The originator of the key to be used pub key_source: [u8; 8], } +impl ParseableMacEvent for DisassociateIndication { + const SIZE: usize = 20; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + device_address: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], + disassociation_reason: DisassociationReason::try_from(buf[8])?, + security_level: SecurityLevel::try_from(buf[9])?, + key_id_mode: KeyIdMode::try_from(buf[10])?, + key_index: buf[11], + key_source: [buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]], + }) + } +} + /// MLME BEACON NOTIIFY Indication which is used to send parameters contained /// within a beacon frame received by the MAC to the application -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct BeaconNotifyIndication { /// he set of octets comprising the beacon payload to be transferred /// from the MAC sublayer entity to the next higher layer @@ -56,8 +95,18 @@ pub struct BeaconNotifyIndication { pub sdu_length: u8, } +impl ParseableMacEvent for BeaconNotifyIndication { + const SIZE: usize = 12; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + todo!() + } +} + /// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CommStatusIndication { /// The 16-bit PAN identifier of the device from which the frame /// was received or to which the frame was being sent @@ -71,83 +120,190 @@ pub struct CommStatusIndication { /// Destination address pub dst_address: MacAddress, /// The communications status - pub status: u8, + pub status: MacStatus, /// Security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// Mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// Index of the key to be used pub key_index: u8, /// Originator of the key to be used pub key_source: [u8; 8], } +impl ParseableMacEvent for CommStatusIndication { + const SIZE: usize = 32; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + let src_addr_mode = AddressMode::try_from(buf[2])?; + let dst_addr_mode = AddressMode::try_from(buf[3])?; + + let src_address = match src_addr_mode { + AddressMode::NoAddress => MacAddress::Short([0, 0]), + AddressMode::Reserved => MacAddress::Short([0, 0]), + AddressMode::Short => MacAddress::Short([buf[4], buf[5]]), + AddressMode::Extended => { + MacAddress::Extended([buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]]) + } + }; + + let dst_address = match dst_addr_mode { + AddressMode::NoAddress => MacAddress::Short([0, 0]), + AddressMode::Reserved => MacAddress::Short([0, 0]), + AddressMode::Short => MacAddress::Short([buf[12], buf[13]]), + AddressMode::Extended => { + MacAddress::Extended([buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]]) + } + }; + + Ok(Self { + pan_id: [buf[0], buf[1]], + src_addr_mode, + dst_addr_mode, + src_address, + dst_address, + status: MacStatus::try_from(buf[20])?, + security_level: SecurityLevel::try_from(buf[21])?, + key_id_mode: KeyIdMode::try_from(buf[22])?, + key_index: buf[23], + key_source: [buf[24], buf[25], buf[26], buf[27], buf[28], buf[29], buf[30], buf[31]], + }) + } +} + /// MLME GTS Indication indicates that a GTS has been allocated or that a /// previously allocated GTS has been deallocated -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GtsIndication { /// The short address of the device that has been allocated or deallocated a GTS pub device_address: [u8; 2], /// The characteristics of the GTS pub gts_characteristics: u8, /// Security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// Mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// Index of the key to be used pub key_index: u8, /// Originator of the key to be used pub key_source: [u8; 8], } +impl ParseableMacEvent for GtsIndication { + const SIZE: usize = 16; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + device_address: [buf[0], buf[1]], + gts_characteristics: buf[2], + security_level: SecurityLevel::try_from(buf[3])?, + key_id_mode: KeyIdMode::try_from(buf[4])?, + key_index: buf[5], + // 2 byte stuffing + key_source: [buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]], + }) + } +} + /// MLME ORPHAN Indication which is used by the coordinator to notify the /// application of the presence of an orphaned device -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct OrphanIndication { /// Extended address of the orphaned device pub orphan_address: [u8; 8], /// Originator of the key used by the originator of the received frame pub key_source: [u8; 8], /// Security level purportedly used by the received MAC command frame - pub security_level: u8, + pub security_level: SecurityLevel, /// Mode used to identify the key used by originator of received frame - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// Index of the key used by the originator of the received frame pub key_index: u8, } +impl ParseableMacEvent for OrphanIndication { + const SIZE: usize = 20; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + orphan_address: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], + key_source: [buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]], + security_level: SecurityLevel::try_from(buf[16])?, + key_id_mode: KeyIdMode::try_from(buf[17])?, + key_index: buf[18], + // 1 byte stuffing + }) + } +} + /// MLME SYNC LOSS Indication which is used by the MAC to indicate the loss /// of synchronization with the coordinator -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SyncLossIndication { /// The PAN identifier with which the device lost synchronization or to which it was realigned pub pan_id: [u8; 2], /// The reason that synchronization was lost pub loss_reason: u8, /// The logical channel on which the device lost synchronization or to whi - pub channel_number: u8, + pub channel_number: MacChannel, /// The channel page on which the device lost synchronization or to which pub channel_page: u8, /// The security level used by the received MAC frame - pub security_level: u8, + pub security_level: SecurityLevel, /// Mode used to identify the key used by originator of received frame - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// Index of the key used by the originator of the received frame pub key_index: u8, /// Originator of the key used by the originator of the received frame pub key_source: [u8; 8], } +impl ParseableMacEvent for SyncLossIndication { + const SIZE: usize = 16; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + pan_id: [buf[0], buf[1]], + loss_reason: buf[2], + channel_number: MacChannel::try_from(buf[3])?, + channel_page: buf[4], + security_level: SecurityLevel::try_from(buf[5])?, + key_id_mode: KeyIdMode::try_from(buf[6])?, + key_index: buf[7], + key_source: [buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]], + }) + } +} + /// MLME DPS Indication which indicates the expiration of the DPSIndexDuration /// and the resetting of the DPS values in the PHY +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DpsIndication; -#[repr(C)] +impl ParseableMacEvent for DpsIndication { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self) + } +} + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DataIndication { /// Pointer to the set of octets forming the MSDU being indicated pub msdu_ptr: *const u8, /// Source addressing mode used - pub src_addr_mode: u8, + pub src_addr_mode: AddressMode, /// Source PAN ID pub src_pan_id: [u8; 2], /// Source address @@ -167,9 +323,9 @@ pub struct DataIndication { /// The time, in symbols, at which the data were received pub time_stamp: [u8; 4], /// The security level purportedly used by the received data frame - pub security_level: u8, + pub security_level: SecurityLevel, /// Mode used to identify the key used by originator of received frame - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// The originator of the key pub key_source: [u8; 8], /// The index of the key @@ -194,12 +350,91 @@ pub struct DataIndication { pub rssi: u8, } +impl ParseableMacEvent for DataIndication { + const SIZE: usize = 72; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + let src_addr_mode = AddressMode::try_from(buf[4])?; + let src_address = match src_addr_mode { + AddressMode::NoAddress => MacAddress::Short([0, 0]), + AddressMode::Reserved => MacAddress::Short([0, 0]), + AddressMode::Short => MacAddress::Short([buf[7], buf[8]]), + AddressMode::Extended => { + MacAddress::Extended([buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14]]) + } + }; + + let dst_addr_mode = AddressMode::try_from(buf[15])?; + let dst_address = match dst_addr_mode { + AddressMode::NoAddress => MacAddress::Short([0, 0]), + AddressMode::Reserved => MacAddress::Short([0, 0]), + AddressMode::Short => MacAddress::Short([buf[18], buf[19]]), + AddressMode::Extended => { + MacAddress::Extended([buf[18], buf[19], buf[20], buf[21], buf[22], buf[23], buf[24], buf[25]]) + } + }; + + Ok(Self { + msdu_ptr: to_u32(&buf[0..4]) as *const u8, + src_addr_mode, + src_pan_id: [buf[5], buf[6]], + src_address, + dst_addr_mode, + dst_pan_id: [buf[16], buf[17]], + dst_address, + msdu_length: buf[26], + mpdu_link_quality: buf[27], + dsn: buf[28], + time_stamp: [buf[29], buf[30], buf[31], buf[32]], + security_level: SecurityLevel::try_from(buf[33])?, + key_id_mode: KeyIdMode::try_from(buf[34])?, + key_source: [buf[35], buf[36], buf[37], buf[38], buf[39], buf[40], buf[41], buf[42]], + key_index: buf[43], + uwbprf: buf[44], + uwn_preamble_symbol_repetitions: buf[45], + datrate: buf[46], + ranging_received: buf[47], + ranging_counter_start: to_u32(&buf[58..52]), + ranging_counter_stop: to_u32(&buf[52..56]), + ranging_tracking_interval: to_u32(&buf[56..60]), + ranging_offset: to_u32(&buf[60..64]), + ranging_fom: buf[65], + rssi: buf[66], + }) + } +} + /// MLME POLL Indication which will be used for indicating the Data Request /// reception to upper layer as defined in Zigbee r22 - D.8.2 -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PollIndication { /// addressing mode used - pub addr_mode: u8, + pub addr_mode: AddressMode, /// Poll requester address pub request_address: MacAddress, } + +impl ParseableMacEvent for PollIndication { + const SIZE: usize = 9; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + let addr_mode = AddressMode::try_from(buf[0])?; + let request_address = match addr_mode { + AddressMode::NoAddress => MacAddress::Short([0, 0]), + AddressMode::Reserved => MacAddress::Short([0, 0]), + AddressMode::Short => MacAddress::Short([buf[1], buf[2]]), + AddressMode::Extended => { + MacAddress::Extended([buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8]]) + } + }; + + Ok(Self { + addr_mode, + request_address, + }) + } +} diff --git a/embassy-stm32-wpan/src/sub/mac/macros.rs b/embassy-stm32-wpan/src/sub/mac/macros.rs new file mode 100644 index 000000000..1a988a779 --- /dev/null +++ b/embassy-stm32-wpan/src/sub/mac/macros.rs @@ -0,0 +1,32 @@ +#[macro_export] +macro_rules! numeric_enum { + (#[repr($repr:ident)] + $(#$attrs:tt)* $vis:vis enum $name:ident { + $($(#$enum_attrs:tt)* $enum:ident = $constant:expr),* $(,)? + } ) => { + #[repr($repr)] + $(#$attrs)* + $vis enum $name { + $($(#$enum_attrs)* $enum = $constant),* + } + + impl ::core::convert::TryFrom<$repr> for $name { + type Error = (); + + fn try_from(value: $repr) -> ::core::result::Result { + match value { + $($constant => Ok( $name :: $enum ),)* + _ => Err(()) + } + } + } + + impl ::core::convert::From<$name> for $repr { + fn from(value: $name) -> $repr { + match value { + $($name :: $enum => $constant,)* + } + } + } + } +} diff --git a/embassy-stm32-wpan/src/sub/mac/mod.rs b/embassy-stm32-wpan/src/sub/mac/mod.rs index 756d7d5b1..0524f135a 100644 --- a/embassy-stm32-wpan/src/sub/mac/mod.rs +++ b/embassy-stm32-wpan/src/sub/mac/mod.rs @@ -9,7 +9,8 @@ use embassy_stm32::ipcc::Ipcc; use embassy_sync::waitqueue::AtomicWaker; use self::commands::MacCommand; -use self::typedefs::MacStatus; +use self::event::MacEvent; +use self::typedefs::MacError; use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::{EvtBox, EvtPacket}; @@ -18,7 +19,10 @@ use crate::{channels, evt}; pub mod commands; mod consts; +pub mod event; +mod helpers; pub mod indications; +mod macros; mod opcodes; pub mod responses; pub mod typedefs; @@ -38,7 +42,7 @@ impl Mac { /// `HW_IPCC_MAC_802_15_4_EvtNot` /// /// This function will stall if the previous `EvtBox` has not been dropped - pub async fn read(&self) -> EvtBox { + pub async fn tl_read(&self) -> EvtBox { // Wait for the last event box to be dropped poll_fn(|cx| { MAC_WAKER.register(cx.waker()); @@ -62,33 +66,20 @@ impl Mac { } /// `HW_IPCC_MAC_802_15_4_CmdEvtNot` - pub async fn write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 { - self.write(opcode, payload).await; + pub async fn tl_write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 { + self.tl_write(opcode, payload).await; Ipcc::flush(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL).await; unsafe { let p_event_packet = MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket; let p_mac_rsp_evt = &((*p_event_packet).evt_serial.evt.payload) as *const u8; - let evt_serial = (MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket) - .read_volatile() - .evt_serial; - let kind = (evt_serial).kind; - let evt_code = evt_serial.evt.evt_code; - let payload_len = evt_serial.evt.payload_len; - let payload = evt_serial.evt.payload; - - debug!( - "evt kind {} evt_code {} len {} payload {}", - kind, evt_code, payload_len, payload - ); - ptr::read_volatile(p_mac_rsp_evt) } } /// `TL_MAC_802_15_4_SendCmd` - pub async fn write(&self, opcode: u16, payload: &[u8]) { + pub async fn tl_write(&self, opcode: u16, payload: &[u8]) { Ipcc::send(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL, || unsafe { CmdPacket::write_into( MAC_802_15_4_CMD_BUFFER.as_mut_ptr(), @@ -98,37 +89,31 @@ impl Mac { ); }) .await; - - unsafe { - let typ = MAC_802_15_4_CMD_BUFFER.as_ptr().read_volatile().cmdserial.ty; - let cmd_code = MAC_802_15_4_CMD_BUFFER.as_ptr().read_volatile().cmdserial.cmd.cmd_code; - let payload_len = MAC_802_15_4_CMD_BUFFER - .as_ptr() - .read_volatile() - .cmdserial - .cmd - .payload_len; - let payload = MAC_802_15_4_CMD_BUFFER.as_ptr().read_volatile().cmdserial.cmd.payload; - - debug!( - "serial type {} cmd_code {} len {} payload {}", - typ, cmd_code, payload_len, payload - ); - } } - pub async fn send_command(&self, cmd: T) -> Result + pub async fn send_command(&self, cmd: T) -> Result<(), MacError> where T: MacCommand, { let mut payload = [0u8; MAX_PACKET_SIZE]; cmd.copy_into_slice(&mut payload); - debug!("sending {:#x}", payload[..T::SIZE]); + let response = self + .tl_write_and_get_response(T::OPCODE as u16, &payload[..T::SIZE]) + .await; - let response = self.write_and_get_response(T::OPCODE as u16, &payload[..T::SIZE]).await; + if response == 0x00 { + Ok(()) + } else { + Err(MacError::from(response)) + } + } - MacStatus::try_from(response) + pub async fn read(&self) -> Result { + let evt_box = self.tl_read().await; + let payload = evt_box.payload(); + + MacEvent::try_from(payload) } } diff --git a/embassy-stm32-wpan/src/sub/mac/opcodes.rs b/embassy-stm32-wpan/src/sub/mac/opcodes.rs index 511b78157..c9a07d6af 100644 --- a/embassy-stm32-wpan/src/sub/mac/opcodes.rs +++ b/embassy-stm32-wpan/src/sub/mac/opcodes.rs @@ -25,3 +25,66 @@ pub enum OpcodeM4ToM0 { McpsDataReq = opcode(0x10), McpsPurgeReq = opcode(0x11), } + +pub enum OpcodeM0ToM4 { + MlmeAssociateCnf = 0x00, + MlmeDisassociateCnf, + MlmeGetCnf, + MlmeGtsCnf, + MlmeResetCnf, + MlmeRxEnableCnf, + MlmeScanCnf, + MlmeSetCnf, + MlmeStartCnf, + MlmePollCnf, + MlmeDpsCnf, + MlmeSoundingCnf, + MlmeCalibrateCnf, + McpsDataCnf, + McpsPurgeCnf, + MlmeAssociateInd, + MlmeDisassociateInd, + MlmeBeaconNotifyInd, + MlmeCommStatusInd, + MlmeGtsInd, + MlmeOrphanInd, + MlmeSyncLossInd, + MlmeDpsInd, + McpsDataInd, + MlmePollInd, +} + +impl TryFrom for OpcodeM0ToM4 { + type Error = (); + + fn try_from(value: u16) -> Result { + match value { + 0 => Ok(Self::MlmeAssociateCnf), + 1 => Ok(Self::MlmeDisassociateCnf), + 2 => Ok(Self::MlmeGetCnf), + 3 => Ok(Self::MlmeGtsCnf), + 4 => Ok(Self::MlmeResetCnf), + 5 => Ok(Self::MlmeRxEnableCnf), + 6 => Ok(Self::MlmeScanCnf), + 7 => Ok(Self::MlmeSetCnf), + 8 => Ok(Self::MlmeStartCnf), + 9 => Ok(Self::MlmePollCnf), + 10 => Ok(Self::MlmeDpsCnf), + 11 => Ok(Self::MlmeSoundingCnf), + 12 => Ok(Self::MlmeCalibrateCnf), + 13 => Ok(Self::McpsDataCnf), + 14 => Ok(Self::McpsPurgeCnf), + 15 => Ok(Self::MlmeAssociateInd), + 16 => Ok(Self::MlmeDisassociateInd), + 17 => Ok(Self::MlmeBeaconNotifyInd), + 18 => Ok(Self::MlmeCommStatusInd), + 19 => Ok(Self::MlmeGtsInd), + 20 => Ok(Self::MlmeOrphanInd), + 21 => Ok(Self::MlmeSyncLossInd), + 22 => Ok(Self::MlmeDpsInd), + 23 => Ok(Self::McpsDataInd), + 24 => Ok(Self::MlmePollInd), + _ => Err(()), + } + } +} diff --git a/embassy-stm32-wpan/src/sub/mac/responses.rs b/embassy-stm32-wpan/src/sub/mac/responses.rs index 8c30a1823..ce2ca2fb2 100644 --- a/embassy-stm32-wpan/src/sub/mac/responses.rs +++ b/embassy-stm32-wpan/src/sub/mac/responses.rs @@ -1,35 +1,50 @@ use super::consts::{MAX_ED_SCAN_RESULTS_SUPPORTED, MAX_PAN_DESC_SUPPORTED, MAX_SOUNDING_LIST_SUPPORTED}; -use super::typedefs::{AddressMode, MacAddress, PanDescriptor}; - -pub trait MacResponse { - const SIZE: usize; - - fn parse(buf: &[u8]) -> Self; -} +use super::event::ParseableMacEvent; +use super::helpers::to_u32; +use super::typedefs::{ + AddressMode, AssociationStatus, KeyIdMode, MacAddress, MacStatus, PanDescriptor, PibId, ScanType, SecurityLevel, +}; /// MLME ASSOCIATE Confirm used to inform of the initiating device whether /// its request to associate was successful or unsuccessful -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssociateConfirm { /// short address allocated by the coordinator on successful association pub assoc_short_address: [u8; 2], /// status of the association request - pub status: u8, + pub status: AssociationStatus, /// security level to be used - pub security_level: u8, + pub security_level: SecurityLevel, /// the originator of the key to be used pub key_source: [u8; 8], /// the mode used to identify the key to be used - pub key_id_mode: u8, + pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, } +impl ParseableMacEvent for AssociateConfirm { + const SIZE: usize = 16; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + assoc_short_address: [buf[0], buf[1]], + status: AssociationStatus::try_from(buf[2])?, + security_level: SecurityLevel::try_from(buf[3])?, + key_source: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]], + key_id_mode: KeyIdMode::try_from(buf[12])?, + key_index: buf[13], + }) + } +} + /// MLME DISASSOCIATE Confirm used to send disassociation Confirmation to the application. -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DisassociateConfirm { /// status of the disassociation attempt - pub status: u8, + pub status: MacStatus, /// device addressing mode used pub device_addr_mode: AddressMode, /// the identifier of the PAN of the device @@ -38,51 +53,130 @@ pub struct DisassociateConfirm { pub device_address: MacAddress, } +impl ParseableMacEvent for DisassociateConfirm { + const SIZE: usize = 12; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + let device_addr_mode = AddressMode::try_from(buf[1])?; + let device_address = match device_addr_mode { + AddressMode::NoAddress => MacAddress::Short([0, 0]), + AddressMode::Reserved => MacAddress::Short([0, 0]), + AddressMode::Short => MacAddress::Short([buf[4], buf[5]]), + AddressMode::Extended => { + MacAddress::Extended([buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]]) + } + }; + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + device_addr_mode, + device_pan_id: [buf[2], buf[3]], + device_address, + }) + } +} + /// MLME GET Confirm which requests information about a given PIB attribute -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GetConfirm { /// The pointer to the value of the PIB attribute attempted to read pub pib_attribute_value_ptr: *const u8, /// Status of the GET attempt - pub status: u8, + pub status: MacStatus, /// The name of the PIB attribute attempted to read - pub pib_attribute: u8, + pub pib_attribute: PibId, /// The lenght of the PIB attribute Value return pub pib_attribute_value_len: u8, } +impl ParseableMacEvent for GetConfirm { + const SIZE: usize = 8; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + let address = to_u32(&buf[0..4]); + + Ok(Self { + pib_attribute_value_ptr: address as *const u8, + status: MacStatus::try_from(buf[4])?, + pib_attribute: PibId::try_from(buf[5])?, + pib_attribute_value_len: buf[6], + }) + } +} + /// MLME GTS Confirm which eports the results of a request to allocate a new GTS /// or to deallocate an existing GTS -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GtsConfirm { /// The characteristics of the GTS pub gts_characteristics: u8, /// The status of the GTS reques - pub status: u8, + pub status: MacStatus, +} + +impl ParseableMacEvent for GtsConfirm { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + gts_characteristics: buf[0], + status: MacStatus::try_from(buf[1])?, + }) + } } /// MLME RESET Confirm which is used to report the results of the reset operation -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ResetConfirm { /// The result of the reset operation - status: u8, + status: MacStatus, +} + +impl ParseableMacEvent for ResetConfirm { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + }) + } } /// MLME RX ENABLE Confirm which is used to report the results of the attempt /// to enable or disable the receiver -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RxEnableConfirm { /// Result of the request to enable or disable the receiver - status: u8, + status: MacStatus, +} + +impl ParseableMacEvent for RxEnableConfirm { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + }) + } } /// MLME SCAN Confirm which is used to report the result of the channel scan request -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ScanConfirm { /// Status of the scan request - pub status: u8, + pub status: MacStatus, /// The type of scan performed - pub scan_type: u8, + pub scan_type: ScanType, /// Channel page on which the scan was performed pub channel_page: u8, /// Channels given in the request which were not scanned @@ -99,44 +193,124 @@ pub struct ScanConfirm { pub uwb_energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED], } +impl ParseableMacEvent for ScanConfirm { + const SIZE: usize = 9; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + todo!() + } +} + /// MLME SET Confirm which reports the result of an attempt to write a value to a PIB attribute -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SetConfirm { /// The result of the set operation - pub status: u8, + pub status: MacStatus, /// The name of the PIB attribute that was written - pub pin_attribute: u8, + pub pin_attribute: PibId, +} + +impl ParseableMacEvent for SetConfirm { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + pin_attribute: PibId::try_from(buf[1])?, + }) + } } /// MLME START Confirm which is used to report the results of the attempt to /// start using a new superframe configuration -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct StartConfirm { /// Result of the attempt to start using an updated superframe configuration - pub status: u8, + pub status: MacStatus, +} + +impl ParseableMacEvent for StartConfirm { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + debug!("{:#x}", buf); + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + }) + } } /// MLME POLL Confirm which is used to report the result of a request to poll the coordinator for data -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PollConfirm { /// The status of the data request - pub status: u8, + pub status: MacStatus, +} + +impl ParseableMacEvent for PollConfirm { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + }) + } +} + +/// MLME DPS Confirm which reports the results of the attempt to enable or disable the DPS +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct DpsConfirm { + /// The status of the DPS request + pub status: MacStatus, +} + +impl ParseableMacEvent for DpsConfirm { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + }) + } } /// MLME SOUNDING Confirm which reports the result of a request to the PHY to provide /// channel sounding information -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SoundingConfirm { /// Results of the sounding measurement sounding_list: [u8; MAX_SOUNDING_LIST_SUPPORTED], } +impl ParseableMacEvent for SoundingConfirm { + const SIZE: usize = 1; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + let mut sounding_list = [0u8; MAX_SOUNDING_LIST_SUPPORTED]; + sounding_list[..buf.len()].copy_from_slice(buf); + + Ok(Self { sounding_list }) + } +} + /// MLME CALIBRATE Confirm which reports the result of a request to the PHY /// to provide internal propagation path information -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CalibrateConfirm { /// The status of the attempt to return sounding data - pub status: u8, + pub status: MacStatus, /// A count of the propagation time from the ranging counter /// to the transmit antenna pub cal_tx_rmaker_offset: u32, @@ -145,18 +319,33 @@ pub struct CalibrateConfirm { pub cal_rx_rmaker_offset: u32, } +impl ParseableMacEvent for CalibrateConfirm { + const SIZE: usize = 12; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + status: MacStatus::try_from(buf[0])?, + // 3 byte stuffing + cal_tx_rmaker_offset: to_u32(&buf[4..8]), + cal_rx_rmaker_offset: to_u32(&buf[8..12]), + }) + } +} + /// MCPS DATA Confirm which will be used for reporting the results of /// MAC data related requests from the application -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DataConfirm { /// The handle associated with the MSDU being confirmed pub msdu_handle: u8, /// The time, in symbols, at which the data were transmitted - pub a_time_stamp: [u8; 4], + pub time_stamp: [u8; 4], /// ranging status pub ranging_received: u8, /// The status of the last MSDU transmission - pub status: u8, + pub status: MacStatus, /// time units corresponding to an RMARKER at the antenna at /// the beginning of a ranging exchange pub ranging_counter_start: u32, @@ -171,12 +360,45 @@ pub struct DataConfirm { pub ranging_fom: u8, } +impl ParseableMacEvent for DataConfirm { + const SIZE: usize = 28; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + msdu_handle: buf[0], + time_stamp: [buf[1], buf[2], buf[3], buf[4]], + ranging_received: buf[5], + status: MacStatus::try_from(buf[6])?, + ranging_counter_start: to_u32(&buf[7..11]), + ranging_counter_stop: to_u32(&buf[11..15]), + ranging_tracking_interval: to_u32(&buf[15..19]), + ranging_offset: to_u32(&buf[19..23]), + ranging_fom: buf[24], + }) + } +} + /// MCPS PURGE Confirm which will be used by the MAC to notify the application of /// the status of its request to purge an MSDU from the transaction queue -#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PurgeConfirm { /// Handle associated with the MSDU requested to be purged from the transaction queue pub msdu_handle: u8, /// The status of the request - pub status: u8, + pub status: MacStatus, +} + +impl ParseableMacEvent for PurgeConfirm { + const SIZE: usize = 4; + + fn try_parse(buf: &[u8]) -> Result { + Self::validate(buf)?; + + Ok(Self { + msdu_handle: buf[0], + status: MacStatus::try_from(buf[1])?, + }) + } } diff --git a/embassy-stm32-wpan/src/sub/mac/typedefs.rs b/embassy-stm32-wpan/src/sub/mac/typedefs.rs index 7f0dd75c0..fe79b3a79 100644 --- a/embassy-stm32-wpan/src/sub/mac/typedefs.rs +++ b/embassy-stm32-wpan/src/sub/mac/typedefs.rs @@ -1,6 +1,8 @@ +use crate::numeric_enum; + +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum MacStatus { - Success = 0x00, +pub enum MacError { Error = 0x01, NotImplemented = 0x02, NotSupported = 0x03, @@ -8,88 +10,115 @@ pub enum MacStatus { Undefined = 0x05, } -impl TryFrom for MacStatus { - type Error = (); - - fn try_from(value: u8) -> Result>::Error> { +impl From for MacError { + fn from(value: u8) -> Self { match value { - 0x00 => Ok(Self::Success), - 0x01 => Ok(Self::Error), - 0x02 => Ok(Self::NotImplemented), - 0x03 => Ok(Self::NotSupported), - 0x04 => Ok(Self::HardwareNotSupported), - 0x05 => Ok(Self::Undefined), - _ => Err(()), + 0x01 => Self::Error, + 0x02 => Self::NotImplemented, + 0x03 => Self::NotSupported, + 0x04 => Self::HardwareNotSupported, + 0x05 => Self::Undefined, + _ => Self::Undefined, } } } -/// this enum contains all the MAC PIB Ids -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PibId { - // PHY - CurrentChannel = 0x00, - ChannelsSupported = 0x01, - TransmitPower = 0x02, - CCAMode = 0x03, - CurrentPage = 0x04, - MaxFrameDuration = 0x05, - SHRDuration = 0x06, - SymbolsPerOctet = 0x07, - - // MAC - AckWaitDuration = 0x40, - AssociationPermit = 0x41, - AutoRequest = 0x42, - BeaconPayload = 0x45, - BeaconPayloadLength = 0x46, - BeaconOrder = 0x47, - Bsn = 0x49, - CoordExtendedAdddress = 0x4A, - CoordShortAddress = 0x4B, - Dsn = 0x4C, - MaxFrameTotalWaitTime = 0x58, - MaxFrameRetries = 0x59, - PanId = 0x50, - ResponseWaitTime = 0x5A, - RxOnWhenIdle = 0x52, - SecurityEnabled = 0x5D, - ShortAddress = 0x53, - SuperframeOrder = 0x54, - TimestampSupported = 0x5C, - TransactionPersistenceTime = 0x55, - MaxBe = 0x57, - LifsPeriod = 0x5E, - SifsPeriod = 0x5F, - MaxCsmaBackoffs = 0x4E, - MinBe = 0x4F, - PanCoordinator = 0x10, - AssocPanCoordinator = 0x11, - ExtendedAddress = 0x6F, - AclEntryDescriptor = 0x70, - AclEntryDescriptorSize = 0x71, - DefaultSecurity = 0x72, - DefaultSecurityMaterialLength = 0x73, - DefaultSecurityMaterial = 0x74, - DefaultSecuritySuite = 0x75, - SecurityMode = 0x76, - CurrentAclEntries = 0x80, - DefaultSecurityExtendedAddress = 0x81, - AssociatedPanCoordinator = 0x56, - PromiscuousMode = 0x51, +numeric_enum! { + #[repr(u8)] + #[derive(Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum MacStatus { + Success = 0x00, + Error = 0x01, + NotImplemented = 0x02, + NotSupported = 0x03, + HardwareNotSupported = 0x04, + Undefined = 0x05, + } } -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum AddressMode { - NoAddress = 0x00, - Reserved = 0x01, - Short = 0x02, - Extended = 0x03, +numeric_enum! { + #[repr(u8)] + /// this enum contains all the MAC PIB Ids + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum PibId { + // PHY + CurrentChannel = 0x00, + ChannelsSupported = 0x01, + TransmitPower = 0x02, + CCAMode = 0x03, + CurrentPage = 0x04, + MaxFrameDuration = 0x05, + SHRDuration = 0x06, + SymbolsPerOctet = 0x07, + + // MAC + AckWaitDuration = 0x40, + AssociationPermit = 0x41, + AutoRequest = 0x42, + BeaconPayload = 0x45, + BeaconPayloadLength = 0x46, + BeaconOrder = 0x47, + Bsn = 0x49, + CoordExtendedAdddress = 0x4A, + CoordShortAddress = 0x4B, + Dsn = 0x4C, + MaxFrameTotalWaitTime = 0x58, + MaxFrameRetries = 0x59, + PanId = 0x50, + ResponseWaitTime = 0x5A, + RxOnWhenIdle = 0x52, + SecurityEnabled = 0x5D, + ShortAddress = 0x53, + SuperframeOrder = 0x54, + TimestampSupported = 0x5C, + TransactionPersistenceTime = 0x55, + MaxBe = 0x57, + LifsPeriod = 0x5E, + SifsPeriod = 0x5F, + MaxCsmaBackoffs = 0x4E, + MinBe = 0x4F, + PanCoordinator = 0x10, + AssocPanCoordinator = 0x11, + ExtendedAddress = 0x6F, + AclEntryDescriptor = 0x70, + AclEntryDescriptorSize = 0x71, + DefaultSecurity = 0x72, + DefaultSecurityMaterialLength = 0x73, + DefaultSecurityMaterial = 0x74, + DefaultSecuritySuite = 0x75, + SecurityMode = 0x76, + CurrentAclEntries = 0x80, + DefaultSecurityExtendedAddress = 0x81, + AssociatedPanCoordinator = 0x56, + PromiscuousMode = 0x51, + } } -pub union MacAddress { - pub short: [u8; 2], - pub extended: [u8; 8], +numeric_enum! { + #[repr(u8)] + #[derive(Default, Clone, Copy)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum AddressMode { + #[default] + NoAddress = 0x00, + Reserved = 0x01, + Short = 0x02, + Extended = 0x03, +} +} + +#[derive(Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum MacAddress { + Short([u8; 2]), + Extended([u8; 8]), +} + +impl Default for MacAddress { + fn default() -> Self { + Self::Short([0, 0]) + } } pub struct GtsCharacteristics { @@ -98,13 +127,15 @@ pub struct GtsCharacteristics { /// MAC PAN Descriptor which contains the network details of the device from /// which the beacon is received +#[derive(Default, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PanDescriptor { /// PAN identifier of the coordinator - pub a_coord_pan_id: [u8; 2], + pub coord_pan_id: [u8; 2], /// Coordinator addressing mode pub coord_addr_mode: AddressMode, /// The current logical channel occupied by the network - pub logical_channel: u8, + pub logical_channel: MacChannel, /// Coordinator address pub coord_addr: MacAddress, /// The current channel page occupied by the network @@ -112,13 +143,179 @@ pub struct PanDescriptor { /// PAN coordinator is accepting GTS requests or not pub gts_permit: bool, /// Superframe specification as specified in the received beacon frame - pub a_superframe_spec: [u8; 2], + pub superframe_spec: [u8; 2], /// The time at which the beacon frame was received, in symbols - pub a_time_stamp: [u8; 4], + pub time_stamp: [u8; 4], /// The LQI at which the network beacon was received pub link_quality: u8, /// Security level purportedly used by the received beacon frame pub security_level: u8, - /// Byte Stuffing to keep 32 bit alignment - pub a_stuffing: [u8; 2], +} + +impl TryFrom<&[u8]> for PanDescriptor { + type Error = (); + + fn try_from(buf: &[u8]) -> Result { + const SIZE: usize = 24; + if buf.len() < SIZE { + return Err(()); + } + + let coord_addr_mode = AddressMode::try_from(buf[2])?; + let coord_addr = match coord_addr_mode { + AddressMode::NoAddress => MacAddress::Short([0, 0]), + AddressMode::Reserved => MacAddress::Short([0, 0]), + AddressMode::Short => MacAddress::Short([buf[4], buf[5]]), + AddressMode::Extended => { + MacAddress::Extended([buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]]) + } + }; + + Ok(Self { + coord_pan_id: [buf[0], buf[1]], + coord_addr_mode, + logical_channel: MacChannel::try_from(buf[3])?, + coord_addr, + channel_page: buf[12], + gts_permit: buf[13] != 0, + superframe_spec: [buf[14], buf[15]], + time_stamp: [buf[16], buf[17], buf[18], buf[19]], + link_quality: buf[20], + security_level: buf[21], + // 2 byte stuffing + }) + } +} + +numeric_enum! { + #[repr(u8)] + #[derive(Default, Clone, Copy)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + /// Building wireless applications with STM32WB series MCUs - Application note 13.10.3 + pub enum MacChannel { + Channel11 = 0x0B, + Channel12 = 0x0C, + Channel13 = 0x0D, + Channel14 = 0x0E, + Channel15 = 0x0F, + #[default] + Channel16 = 0x10, + Channel17 = 0x11, + Channel18 = 0x12, + Channel19 = 0x13, + Channel20 = 0x14, + Channel21 = 0x15, + Channel22 = 0x16, + Channel23 = 0x17, + Channel24 = 0x18, + Channel25 = 0x19, + Channel26 = 0x1A, + } +} + +#[cfg(not(feature = "defmt"))] +bitflags::bitflags! { + pub struct Capabilities: u8 { + /// 1 if the device is capabaleof becoming a PAN coordinator + const IS_COORDINATOR_CAPABLE = 0b00000001; + /// 1 if the device is an FFD, 0 if it is an RFD + const IS_FFD = 0b00000010; + /// 1 if the device is receiving power from mains, 0 if it is battery-powered + const IS_MAINS_POWERED = 0b00000100; + /// 1 if the device does not disable its receiver to conserver power during idle periods + const RECEIVER_ON_WHEN_IDLE = 0b00001000; + // 0b00010000 reserved + // 0b00100000 reserved + /// 1 if the device is capable of sending and receiving secured MAC frames + const IS_SECURE = 0b01000000; + /// 1 if the device wishes the coordinator to allocate a short address as a result of the association + const ALLOCATE_ADDRESS = 0b10000000; + } +} + +#[cfg(feature = "defmt")] +defmt::bitflags! { + pub struct Capabilities: u8 { + /// 1 if the device is capabaleof becoming a PAN coordinator + const IS_COORDINATOR_CAPABLE = 0b00000001; + /// 1 if the device is an FFD, 0 if it is an RFD + const IS_FFD = 0b00000010; + /// 1 if the device is receiving power from mains, 0 if it is battery-powered + const IS_MAINS_POWERED = 0b00000100; + /// 1 if the device does not disable its receiver to conserver power during idle periods + const RECEIVER_ON_WHEN_IDLE = 0b00001000; + // 0b00010000 reserved + // 0b00100000 reserved + /// 1 if the device is capable of sending and receiving secured MAC frames + const IS_SECURE = 0b01000000; + /// 1 if the device wishes the coordinator to allocate a short address as a result of the association + const ALLOCATE_ADDRESS = 0b10000000; + } +} + +numeric_enum! { + #[repr(u8)] + #[derive(Default)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum KeyIdMode { + #[default] + /// the key is determined implicitly from the originator and recipient(s) of the frame + Implicite = 0x00, + /// the key is determined explicitly using a 1 bytes key source and a 1 byte key index + Explicite1Byte = 0x01, + /// the key is determined explicitly using a 4 bytes key source and a 1 byte key index + Explicite4Byte = 0x02, + /// the key is determined explicitly using a 8 bytes key source and a 1 byte key index + Explicite8Byte = 0x03, + } +} + +numeric_enum! { + #[repr(u8)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum AssociationStatus { + /// Association successful + Success = 0x00, + /// PAN at capacity + PanAtCapacity = 0x01, + /// PAN access denied + PanAccessDenied = 0x02 + } +} + +numeric_enum! { + #[repr(u8)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum DisassociationReason { + /// The coordinator wishes the device to leave the PAN. + CoordRequested = 0x01, + /// The device wishes to leave the PAN. + DeviceRequested = 0x02, + } +} + +numeric_enum! { + #[repr(u8)] + #[derive(Default)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum SecurityLevel { + /// MAC Unsecured Mode Security + #[default] + Unsecure = 0x00, + /// MAC ACL Mode Security + AclMode = 0x01, + /// MAC Secured Mode Security + Secured = 0x02, + } +} + +numeric_enum! { + #[repr(u8)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum ScanType { + EdScan = 0x00, + Active = 0x01, + Passive = 0x02, + Orphan = 0x03 + } } diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index 4100d1ac5..18b29362b 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -7,7 +7,7 @@ use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; use embassy_stm32_wpan::sub::mac::commands::{ResetRequest, SetRequest, StartRequest}; -use embassy_stm32_wpan::sub::mac::typedefs::PibId; +use embassy_stm32_wpan::sub::mac::typedefs::{MacChannel, PibId}; use embassy_stm32_wpan::sub::mm; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; @@ -65,109 +65,92 @@ async fn main(spawner: Spawner) { info!("initialized mac: {}", result); info!("resetting"); - let response = mbox - .mac_subsystem + mbox.mac_subsystem .send_command(ResetRequest { set_default_pib: true }) - .await; - info!("{}", response); + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt); info!("setting extended address"); let extended_address: u64 = 0xACDE480000000001; - let response = mbox - .mac_subsystem + mbox.mac_subsystem .send_command(SetRequest { pib_attribute_ptr: &extended_address as *const _ as *const u8, pib_attribute: PibId::ExtendedAddress, }) - .await; - info!("{}", response); + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt); info!("setting short address"); let short_address: u16 = 0x1122; - let response = mbox - .mac_subsystem + mbox.mac_subsystem .send_command(SetRequest { pib_attribute_ptr: &short_address as *const _ as *const u8, pib_attribute: PibId::ShortAddress, }) - .await; - info!("{}", response); + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt); info!("setting association permit"); let association_permit: bool = true; - let response = mbox - .mac_subsystem + mbox.mac_subsystem .send_command(SetRequest { pib_attribute_ptr: &association_permit as *const _ as *const u8, pib_attribute: PibId::AssociationPermit, }) - .await; - info!("{}", response); + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt); info!("setting TX power"); let transmit_power: i8 = 2; - let response = mbox - .mac_subsystem + mbox.mac_subsystem .send_command(SetRequest { pib_attribute_ptr: &transmit_power as *const _ as *const u8, pib_attribute: PibId::TransmitPower, }) - .await; - info!("{}", response); + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt); info!("starting FFD device"); - let response = mbox - .mac_subsystem + mbox.mac_subsystem .send_command(StartRequest { - channel_number: 16, + channel_number: MacChannel::Channel16, beacon_order: 0x0F, superframe_order: 0x0F, pan_coordinator: true, battery_life_extension: false, ..Default::default() }) - .await; - info!("{}", response); + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt); info!("setting RX on when idle"); let rx_on_while_idle: bool = true; - let response = mbox - .mac_subsystem + mbox.mac_subsystem .send_command(SetRequest { pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, pib_attribute: PibId::RxOnWhenIdle, }) - .await; - info!("{}", response); + .await + .unwrap(); + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt); - // info!("association request"); - // mbox.mac_subsystem - // .send_command(AssociateRequest { - // channel_number: 16, - // channel_page: 0, - // coord_addr_mode: 2, - // coord_address: MacAddress { short: [0x22, 0x11] }, - // capability_information: 0x80, - // coord_pan_id: [0xAA, 0x1A], - // security_level: 0, - - // key_id_mode: 0, - // key_index: 0, - // key_source: [0; 8], - // }) - // .await; - // info!("reading"); - // let result = mbox.mac_subsystem.read().await; - // info!("{}", result.payload()); - - // - // info!("starting ble..."); - // mbox.ble_subsystem.t_write(0x0c, &[]).await; - // - // info!("waiting for ble..."); - // let ble_event = mbox.ble_subsystem.tl_read().await; - // - // info!("ble event: {}", ble_event.payload()); + loop { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt); + } info!("Test OK"); cortex_m::asm::bkpt(); diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index 938fe754f..8042a3704 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -7,7 +7,9 @@ use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; use embassy_stm32_wpan::sub::mac::commands::{AssociateRequest, ResetRequest, SetRequest, StartRequest}; -use embassy_stm32_wpan::sub::mac::typedefs::{AddressMode, MacAddress, PibId}; +use embassy_stm32_wpan::sub::mac::typedefs::{ + AddressMode, Capabilities, KeyIdMode, MacAddress, MacChannel, PibId, SecurityLevel, +}; use embassy_stm32_wpan::sub::mm; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; @@ -86,14 +88,14 @@ async fn main(spawner: Spawner) { let response = mbox .mac_subsystem .send_command(AssociateRequest { - channel_number: 16, + channel_number: MacChannel::Channel16, channel_page: 0, coord_addr_mode: AddressMode::Short, - coord_address: MacAddress { short: [0x22, 0x11] }, - capability_information: 0x80, + coord_address: MacAddress::Short([0x22, 0x11]), + capability_information: Capabilities::ALLOCATE_ADDRESS, coord_pan_id: [0xAA, 0x1A], - security_level: 0x00, - key_id_mode: 0, + security_level: SecurityLevel::Unsecure, + key_id_mode: KeyIdMode::Implicite, key_source: [0; 8], key_index: 0, })