From 6884c0ea578f052e6441e23c61ec2503361d6c1c Mon Sep 17 00:00:00 2001 From: Henri Chataing Date: Thu, 29 Feb 2024 14:09:14 -0800 Subject: [PATCH] Refactor APP config management - Move enum definitions to uci_packets.pdl - Move AppConfig declaration to separate module for readability - Sync APP config definitions with UCI 2.0 --- py/pica/pica/packets/uci.py | 484 ++++++++++++++++++++-- src/app_config.rs | 498 +++++++++++++++++++++++ src/lib.rs | 12 +- src/mac_address.rs | 15 + src/session.rs | 784 +++++------------------------------- src/uci_packets.pdl | 235 +++++++++-- tests/data_transfer.py | 56 ++- tests/ranging.py | 56 ++- 8 files changed, 1354 insertions(+), 786 deletions(-) create mode 100644 src/app_config.rs diff --git a/py/pica/pica/packets/uci.py b/py/pica/pica/packets/uci.py index 32ffbdc..5ad8a2d 100644 --- a/py/pica/pica/packets/uci.py +++ b/py/pica/pica/packets/uci.py @@ -1,5 +1,5 @@ # File generated from , with the command: -# ./pdl-compiler/scripts/generate_python_backend.py +# /home/henrichataing/Projects/github/google/pdl/pdl-compiler/scripts/generate_python_backend.py # /!\ Do not edit by hand. from dataclasses import dataclass, field, fields from typing import Optional, List, Tuple, Union @@ -351,7 +351,7 @@ class AppConfigTlvType(enum.IntEnum): STS_CONFIG = 0x2 MULTI_NODE_MODE = 0x3 CHANNEL_NUMBER = 0x4 - NO_OF_CONTROLEE = 0x5 + NUMBER_OF_CONTROLEES = 0x5 DEVICE_MAC_ADDRESS = 0x6 DST_MAC_ADDRESS = 0x7 SLOT_DURATION = 0x8 @@ -360,9 +360,9 @@ class AppConfigTlvType(enum.IntEnum): MAC_FCS_TYPE = 0xb RANGING_ROUND_CONTROL = 0xc AOA_RESULT_REQ = 0xd - RNG_DATA_NTF = 0xe - RNG_DATA_NTF_PROXIMITY_NEAR = 0xf - RNG_DATA_NTF_PROXIMITY_FAR = 0x10 + SESSION_INFO_NTF_CONFIG = 0xe + NEAR_PROXIMITY_CONFIG = 0xf + FAR_PROXIMITY_CONFIG = 0x10 DEVICE_ROLE = 0x11 RFRAME_CONFIG = 0x12 RSSI_REPORTING = 0x13 @@ -374,13 +374,11 @@ class AppConfigTlvType(enum.IntEnum): DATA_REPETITION_COUNT = 0x19 RANGING_TIME_STRUCT = 0x1a SLOTS_PER_RR = 0x1b - TX_ADAPTIVE_PAYLOAD_POWER = 0x1c - RNG_DATA_NTF_AOA_BOUND = 0x1d - RESPONDER_SLOT_INDEX = 0x1e + AOA_BOUND_CONFIG = 0x1d PRF_MODE = 0x1f CAP_SIZE_RANGE = 0x20 TX_JITTER_WINDOW_SIZE = 0x21 - SCHEDULED_MODE = 0x22 + SCHEDULE_MODE = 0x22 KEY_ROTATION = 0x23 KEY_ROTATION_RATE = 0x24 SESSION_PRIORITY = 0x25 @@ -397,13 +395,7 @@ class AppConfigTlvType(enum.IntEnum): SUB_SESSION_ID = 0x30 BPRF_PHR_DATA_RATE = 0x31 MAX_NUMBER_OF_MEASUREMENTS = 0x32 - UL_TDOA_TX_INTERVAL = 0x33 - UL_TDOA_RANDOM_WINDOW = 0x34 STS_LENGTH = 0x35 - SUSPEND_RANGING_ROUNDS = 0x36 - UL_TDOA_NTF_REPORT_CONFIG = 0x37 - UL_TDOA_DEVICE_ID = 0x38 - UL_TDOA_TX_TIMESTAMP = 0x39 MIN_FRAMES_PER_RR = 0x3a MTU_SIZE = 0x3b INTER_FRAME_INTERVAL = 0x3c @@ -413,10 +405,10 @@ class AppConfigTlvType(enum.IntEnum): DL_TDOA_ANCHOR_CFO = 0x40 DL_TDOA_ANCHOR_LOCATION = 0x41 DL_TDOA_TX_ACTIVE_RANGING_ROUNDS = 0x42 - DL_TDOA_BLOCK_STRIDING = 0x43 + DL_TDOA_BLOCK_SKIPPING = 0x43 DL_TDOA_TIME_REFERENCE_ANCHOR = 0x44 SESSION_KEY = 0x45 - SUBSESSION_KEY = 0x46 + SUB_SESSION_KEY = 0x46 SESSION_DATA_TRANSFER_STATUS_NTF_CONFIG = 0x47 SESSION_TIME_BASE = 0x48 DL_TDOA_RESPONDER_TOF = 0x49 @@ -433,15 +425,434 @@ def from_int(v: int) -> Union[int, 'AppConfigTlvType']: return v -class FrameReportTlvType(enum.IntEnum): - RSSI = 0x0 - AOA = 0x1 - CIR = 0x2 +class DeviceType(enum.IntEnum): + CONTROLEE = 0x0 + CONTROLLER = 0x1 @staticmethod - def from_int(v: int) -> Union[int, 'FrameReportTlvType']: + def from_int(v: int) -> Union[int, 'DeviceType']: try: - return FrameReportTlvType(v) + return DeviceType(v) + except ValueError as exn: + raise exn + + +class RangingRoundUsage(enum.IntEnum): + SS_TWR_DEFERRED_MODE = 0x1 + DS_TWR_DEFERRED_MODE = 0x2 + SS_TWR_NON_DEFERRED_MODE = 0x3 + DS_TWR_NON_DEFERRED_MODE = 0x4 + ON_WAY_RANGING_DL_TDOA = 0x5 + OWR_AOA_MEASUREMENT = 0x6 + ESS_TWR_NON_DEFERRED = 0x7 + ADS_TWR_NON_DEFERRED = 0x8 + + @staticmethod + def from_int(v: int) -> Union[int, 'RangingRoundUsage']: + try: + return RangingRoundUsage(v) + except ValueError as exn: + raise exn + + +class StsConfig(enum.IntEnum): + STATIC = 0x0 + DYNAMIC = 0x1 + DYNAMIC_FOR_RESPONDER_SUB_SESSION_KEY = 0x2 + PROVISIONED = 0x3 + PROVISIONED_FOR_RESPONDER_SUB_SESSION_KEY = 0x4 + + @staticmethod + def from_int(v: int) -> Union[int, 'StsConfig']: + try: + return StsConfig(v) + except ValueError as exn: + raise exn + + +class MultiNodeMode(enum.IntEnum): + ONE_TO_ONE = 0x0 + ONE_TO_MANY = 0x1 + + @staticmethod + def from_int(v: int) -> Union[int, 'MultiNodeMode']: + try: + return MultiNodeMode(v) + except ValueError as exn: + raise exn + + +class ChannelNumber(enum.IntEnum): + CHANNEL_NUMBER_5 = 0x5 + CHANNEL_NUMBER_6 = 0x6 + CHANNEL_NUMBER_8 = 0x8 + CHANNEL_NUMBER_9 = 0x9 + CHANNEL_NUMBER_10 = 0xa + CHANNEL_NUMBER_12 = 0xc + CHANNEL_NUMBER_13 = 0xd + CHANNEL_NUMBER_14 = 0xe + + @staticmethod + def from_int(v: int) -> Union[int, 'ChannelNumber']: + try: + return ChannelNumber(v) + except ValueError as exn: + raise exn + + +class MacFcsType(enum.IntEnum): + CRC_16 = 0x0 + CRC_32 = 0x1 + + @staticmethod + def from_int(v: int) -> Union[int, 'MacFcsType']: + try: + return MacFcsType(v) + except ValueError as exn: + raise exn + + +@dataclass +class RangingRoundControl(Packet): + rrrm: int = field(kw_only=True, default=0) + rcp: int = field(kw_only=True, default=0) + mrp: int = field(kw_only=True, default=0) + mrm: int = field(kw_only=True, default=0) + + def __post_init__(self): + pass + + @staticmethod + def parse(span: bytes) -> Tuple['RangingRoundControl', bytes]: + fields = {'payload': None} + if len(span) < 1: + raise Exception('Invalid packet size') + fields['rrrm'] = (span[0] >> 0) & 0x1 + if (span[0] >> 1) & 0x1 != 0x1: + raise Exception('Unexpected fixed field value') + fields['rcp'] = (span[0] >> 2) & 0x1 + fields['mrp'] = (span[0] >> 6) & 0x1 + fields['mrm'] = (span[0] >> 7) & 0x1 + span = span[1:] + return RangingRoundControl(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.rrrm > 1: + print(f"Invalid value for field RangingRoundControl::rrrm: {self.rrrm} > 1; the value will be truncated") + self.rrrm &= 1 + if self.rcp > 1: + print(f"Invalid value for field RangingRoundControl::rcp: {self.rcp} > 1; the value will be truncated") + self.rcp &= 1 + if self.mrp > 1: + print(f"Invalid value for field RangingRoundControl::mrp: {self.mrp} > 1; the value will be truncated") + self.mrp &= 1 + if self.mrm > 1: + print(f"Invalid value for field RangingRoundControl::mrm: {self.mrm} > 1; the value will be truncated") + self.mrm &= 1 + _value = ( + (self.rrrm << 0) | + (1 << 1) | + (self.rcp << 2) | + (self.mrp << 6) | + (self.mrm << 7) + ) + _span.append(_value) + return bytes(_span) + + @property + def size(self) -> int: + return 1 + +class AoaResultReq(enum.IntEnum): + AOA_DISABLED = 0x0 + AOA_ENABLED = 0x1 + AOA_ENABLED_AZIMUTH_ONLY = 0x2 + AOA_ENABLED_ELEVATION_ONLY = 0x3 + + @staticmethod + def from_int(v: int) -> Union[int, 'AoaResultReq']: + try: + return AoaResultReq(v) + except ValueError as exn: + raise exn + + +class SessionInfoNtfConfig(enum.IntEnum): + DISABLE = 0x0 + ENABLE = 0x1 + ENABLE_PROXIMITY_TRIGGER = 0x2 + ENABLE_AOA_TRIGGER = 0x3 + ENABLE_PROXIMITY_AOA_TRIGGER = 0x4 + ENABLE_PROXIMITY_EDGE_TRIGGER = 0x5 + ENABLE_AOA_EDGE_TRIGGER = 0x6 + ENABLE_PROXIMITY_AOA_EDGE_TRIGGER = 0x7 + + @staticmethod + def from_int(v: int) -> Union[int, 'SessionInfoNtfConfig']: + try: + return SessionInfoNtfConfig(v) + except ValueError as exn: + raise exn + + +class DeviceRole(enum.IntEnum): + RESPONDER = 0x0 + INITIATOR = 0x1 + ADVERTISER = 0x5 + OBSERVER = 0x6 + DT_ANCHOR = 0x7 + DT_TAG = 0x8 + + @staticmethod + def from_int(v: int) -> Union[int, 'DeviceRole']: + try: + return DeviceRole(v) + except ValueError as exn: + raise exn + + +class RframeConfig(enum.IntEnum): + SP0 = 0x0 + SP1 = 0x1 + SP3 = 0x3 + + @staticmethod + def from_int(v: int) -> Union[int, 'RframeConfig']: + try: + return RframeConfig(v) + except ValueError as exn: + raise exn + + +class RssiReporting(enum.IntEnum): + DISABLE = 0x0 + ENABLE = 0x1 + + @staticmethod + def from_int(v: int) -> Union[int, 'RssiReporting']: + try: + return RssiReporting(v) + except ValueError as exn: + raise exn + + +class PsduDataRate(enum.IntEnum): + DATA_RATE_6M81 = 0x0 + DATA_RATE_7M80 = 0x1 + DATA_RATE_27M2 = 0x2 + DATA_RATE_31M2 = 0x3 + + @staticmethod + def from_int(v: int) -> Union[int, 'PsduDataRate']: + try: + return PsduDataRate(v) + except ValueError as exn: + raise exn + + +class PreambleDuration(enum.IntEnum): + DURATION_32_SYMBOLS = 0x0 + DURATION_64_SYMBOLS = 0x1 + + @staticmethod + def from_int(v: int) -> Union[int, 'PreambleDuration']: + try: + return PreambleDuration(v) + except ValueError as exn: + raise exn + + +class LinkLayerMode(enum.IntEnum): + BYPASS_MODE = 0x0 + + @staticmethod + def from_int(v: int) -> Union[int, 'LinkLayerMode']: + try: + return LinkLayerMode(v) + except ValueError as exn: + raise exn + + +class RangingTimeStruct(enum.IntEnum): + BLOCK_BASED_SCHEDULING = 0x1 + + @staticmethod + def from_int(v: int) -> Union[int, 'RangingTimeStruct']: + try: + return RangingTimeStruct(v) + except ValueError as exn: + raise exn + + +class PrfMode(enum.IntEnum): + BPRF_MODE = 0x0 + HPRF_MODE_124M8 = 0x1 + HPRF_MODE_249M6 = 0x2 + + @staticmethod + def from_int(v: int) -> Union[int, 'PrfMode']: + try: + return PrfMode(v) + except ValueError as exn: + raise exn + + +class ScheduleMode(enum.IntEnum): + CONTENTION_BASED = 0x0 + TIME_SCHEDULED = 0x1 + + @staticmethod + def from_int(v: int) -> Union[int, 'ScheduleMode']: + try: + return ScheduleMode(v) + except ValueError as exn: + raise exn + + +class KeyRotation(enum.IntEnum): + DISABLE = 0x0 + ENABLE = 0x1 + + @staticmethod + def from_int(v: int) -> Union[int, 'KeyRotation']: + try: + return KeyRotation(v) + except ValueError as exn: + raise exn + + +class MacAddressMode(enum.IntEnum): + MODE_0 = 0x0 + MODE_1 = 0x1 + MODE_2 = 0x2 + + @staticmethod + def from_int(v: int) -> Union[int, 'MacAddressMode']: + try: + return MacAddressMode(v) + except ValueError as exn: + raise exn + + +class HoppingMode(enum.IntEnum): + DISABLE = 0x0 + ENABLE = 0x1 + + @staticmethod + def from_int(v: int) -> Union[int, 'HoppingMode']: + try: + return HoppingMode(v) + except ValueError as exn: + raise exn + + +@dataclass +class ResultReportConfig(Packet): + tof: int = field(kw_only=True, default=0) + aoa_azimuth: int = field(kw_only=True, default=0) + aoa_elevation: int = field(kw_only=True, default=0) + aoa_fom: int = field(kw_only=True, default=0) + + def __post_init__(self): + pass + + @staticmethod + def parse(span: bytes) -> Tuple['ResultReportConfig', bytes]: + fields = {'payload': None} + if len(span) < 1: + raise Exception('Invalid packet size') + fields['tof'] = (span[0] >> 0) & 0x1 + fields['aoa_azimuth'] = (span[0] >> 1) & 0x1 + fields['aoa_elevation'] = (span[0] >> 2) & 0x1 + fields['aoa_fom'] = (span[0] >> 3) & 0x1 + span = span[1:] + return ResultReportConfig(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + if self.tof > 1: + print(f"Invalid value for field ResultReportConfig::tof: {self.tof} > 1; the value will be truncated") + self.tof &= 1 + if self.aoa_azimuth > 1: + print(f"Invalid value for field ResultReportConfig::aoa_azimuth: {self.aoa_azimuth} > 1; the value will be truncated") + self.aoa_azimuth &= 1 + if self.aoa_elevation > 1: + print(f"Invalid value for field ResultReportConfig::aoa_elevation: {self.aoa_elevation} > 1; the value will be truncated") + self.aoa_elevation &= 1 + if self.aoa_fom > 1: + print(f"Invalid value for field ResultReportConfig::aoa_fom: {self.aoa_fom} > 1; the value will be truncated") + self.aoa_fom &= 1 + _value = ( + (self.tof << 0) | + (self.aoa_azimuth << 1) | + (self.aoa_elevation << 2) | + (self.aoa_fom << 3) + ) + _span.append(_value) + return bytes(_span) + + @property + def size(self) -> int: + return 1 + +class BprfPhrDataRate(enum.IntEnum): + DATA_RATE_850K = 0x0 + DATA_RATE_6M81 = 0x1 + + @staticmethod + def from_int(v: int) -> Union[int, 'BprfPhrDataRate']: + try: + return BprfPhrDataRate(v) + except ValueError as exn: + raise exn + + +class StsLength(enum.IntEnum): + LENGTH_32_SYMBOLS = 0x0 + LENGTH_64_SYMBOLS = 0x1 + LENGTH_128_SYMBOLS = 0x2 + + @staticmethod + def from_int(v: int) -> Union[int, 'StsLength']: + try: + return StsLength(v) + except ValueError as exn: + raise exn + + +class DlTdoaRangingMethod(enum.IntEnum): + SS_TWR = 0x0 + DS_TWR = 0x1 + + @staticmethod + def from_int(v: int) -> Union[int, 'DlTdoaRangingMethod']: + try: + return DlTdoaRangingMethod(v) + except ValueError as exn: + raise exn + + +class DlTdoaAnchorCfo(enum.IntEnum): + ANCHOR_CFO_NOT_INCLUDED = 0x0 + ANCHOR_CFO_INCLUDED = 0x1 + + @staticmethod + def from_int(v: int) -> Union[int, 'DlTdoaAnchorCfo']: + try: + return DlTdoaAnchorCfo(v) + except ValueError as exn: + raise exn + + +class SessionDataTransferStatusNtfConfig(enum.IntEnum): + DISABLE = 0x0 + ENABLE = 0x1 + + @staticmethod + def from_int(v: int) -> Union[int, 'SessionDataTransferStatusNtfConfig']: + try: + return SessionDataTransferStatusNtfConfig(v) except ValueError as exn: raise exn @@ -2875,7 +3286,7 @@ def size(self) -> int: @dataclass class SessionGetAppConfigCmd(SessionConfigCommand): session_token: int = field(kw_only=True, default=0) - app_cfg: bytearray = field(kw_only=True, default_factory=bytearray) + app_cfg: List[AppConfigTlvType] = field(kw_only=True, default_factory=list) def __post_init__(self): self.opcode = 4 @@ -2894,7 +3305,10 @@ def parse(fields: dict, span: bytes) -> Tuple['SessionGetAppConfigCmd', bytes]: span = span[5:] if len(span) < app_cfg_count: raise Exception('Invalid packet size') - fields['app_cfg'] = list(span[:app_cfg_count]) + app_cfg = [] + for n in range(app_cfg_count): + app_cfg.append(AppConfigTlvType(int.from_bytes(span[n:n + 1], byteorder='little'))) + fields['app_cfg'] = app_cfg span = span[app_cfg_count:] return SessionGetAppConfigCmd(**fields), span @@ -2908,12 +3322,13 @@ def serialize(self, payload: bytes = None) -> bytes: print(f"Invalid length for field SessionGetAppConfigCmd::app_cfg: {len(self.app_cfg)} > 255; the array will be truncated") del self.app_cfg[255:] _span.append((len(self.app_cfg) << 0)) - _span.extend(self.app_cfg) + for _elt in self.app_cfg: + _span.append(_elt) return SessionConfigCommand.serialize(self, payload = bytes(_span)) @property def size(self) -> int: - return len(self.app_cfg) * 1 + 5 + return len(self.app_cfg) * 8 + 5 @dataclass class SessionGetAppConfigRsp(SessionConfigResponse): @@ -3279,7 +3694,7 @@ class UpdateMulticastListAction(enum.IntEnum): ADD_CONTROLEE = 0x0 REMOVE_CONTROLEE = 0x1 ADD_CONTROLEE_WITH_SHORT_SUB_SESSION_KEY = 0x2 - ADD_CONTROLEE_WITH_LONG_SUB_SESSION_KEY = 0x3 + ADD_CONTROLEE_WITH_EXTENDED_SUB_SESSION_KEY = 0x3 @staticmethod def from_int(v: int) -> Union[int, 'UpdateMulticastListAction']: @@ -4908,6 +5323,19 @@ def serialize(self, payload: bytes = None) -> bytes: def size(self) -> int: return 1 +class FrameReportTlvType(enum.IntEnum): + RSSI = 0x0 + AOA = 0x1 + CIR = 0x2 + + @staticmethod + def from_int(v: int) -> Union[int, 'FrameReportTlvType']: + try: + return FrameReportTlvType(v) + except ValueError as exn: + raise exn + + @dataclass class FrameReportTlv(Packet): t: FrameReportTlvType = field(kw_only=True, default=FrameReportTlvType.RSSI) diff --git a/src/app_config.rs b/src/app_config.rs new file mode 100644 index 0000000..b3bbc1a --- /dev/null +++ b/src/app_config.rs @@ -0,0 +1,498 @@ +use crate::packets::uci; +use crate::MacAddress; + +/// [UCI] 8.3 Application Configuration Parameters. +/// The configuration is initially filled with default values from the +/// specification. +/// See [UCI] Table 45: APP Configuration Parameters IDs +/// for the format of each parameter and the default value. +/// Mandatory APP configuration parameters are declared as optional, +/// and must be set before moving the session from SESSION_STATE_INIT to +/// SESSION_STATE_IDLE. +#[derive(Clone, PartialEq, Eq)] +pub struct AppConfig { + pub device_type: Option, + pub ranging_round_usage: Option, + pub sts_config: uci::StsConfig, + pub multi_node_mode: Option, + channel_number: uci::ChannelNumber, + /// Number of Controlees(N) 1<=N<=8 (Default is 1) + pub number_of_controlees: u8, + /// MAC Address of the UWBS itself participating in UWB session. + /// The short address (2 bytes) or extended MAC address (8 bytes) + /// shall be indicated via MAC_ADDRESS_MODE config. + pub device_mac_address: Option, + /// MAC Address list(N) for NUMBER_OF_CONTROLEES + /// devices participating in UWB Session. + /// + /// The size of this list shall be: + /// - equal to 1 when MULTI_NODE_MODE is set 0x00 (O2O). + /// - ranging from 1 to 8 when MULTI_NODE_MODE is set to 0x01 (O2M). + pub dst_mac_address: Vec, + slot_duration: u16, + pub ranging_duration: u32, + sts_index: u32, + mac_fcs_type: uci::MacFcsType, + ranging_round_control: u8, + aoa_result_req: uci::AoaResultReq, + pub session_info_ntf_config: uci::SessionInfoNtfConfig, + near_proximity_config: u16, + far_proximity_config: u16, + pub device_role: Option, + rframe_config: uci::RframeConfig, + rssi_reporting: uci::RssiReporting, + preamble_code_index: u8, + sfd_id: u8, + psdu_data_rate: uci::PsduDataRate, + preamble_duration: uci::PreambleDuration, + link_layer_mode: uci::LinkLayerMode, + data_repetition_count: u8, + ranging_time_struct: uci::RangingTimeStruct, + slots_per_rr: u8, + aoa_bound_config: [u16; 4], + prf_mode: uci::PrfMode, + cap_size_range: [u8; 2], + tx_jitter_window_size: u8, + pub schedule_mode: Option, + key_rotation: uci::KeyRotation, + key_rotation_rate: u8, + session_priority: u8, + pub mac_address_mode: uci::MacAddressMode, + vendor_id: u16, + static_sts_iv: [u8; 6], + number_of_sts_segments: u8, + max_rr_retry: u16, + uwb_initiation_time: u64, + hopping_mode: uci::HoppingMode, + block_stride_length: u8, + result_report_config: u8, + pub in_band_termination_attempt_count: u8, + sub_session_id: u32, + bprf_phr_data_rate: uci::BprfPhrDataRate, + max_number_of_measurements: u16, + sts_length: uci::StsLength, + min_frames_per_rr: u8, + mtu_size: u16, + inter_frame_interval: u8, + session_key: Vec, + sub_session_key: Vec, + pub session_data_transfer_status_ntf_config: uci::SessionDataTransferStatusNtfConfig, + session_time_base: [u8; 9], + application_data_endpoint: u8, +} + +impl Default for AppConfig { + fn default() -> Self { + AppConfig { + device_type: None, + ranging_round_usage: None, + sts_config: uci::StsConfig::Static, + multi_node_mode: None, + channel_number: uci::ChannelNumber::ChannelNumber9, + number_of_controlees: 1, + device_mac_address: None, + dst_mac_address: vec![], + slot_duration: 2400, + ranging_duration: 200, + sts_index: 0, + mac_fcs_type: uci::MacFcsType::Crc16, + // The default is 0x03 when Time Scheduled Ranging is used, + // 0x06 when Contention-based Ranging is used. + ranging_round_control: 0x06, + aoa_result_req: uci::AoaResultReq::AoaEnabled, + session_info_ntf_config: uci::SessionInfoNtfConfig::Enable, + near_proximity_config: 0, + far_proximity_config: 20000, + device_role: None, + rframe_config: uci::RframeConfig::Sp3, + rssi_reporting: uci::RssiReporting::Disable, + preamble_code_index: 10, + sfd_id: 2, + psdu_data_rate: uci::PsduDataRate::DataRate6m81, + preamble_duration: uci::PreambleDuration::Duration64Symbols, + link_layer_mode: uci::LinkLayerMode::BypassMode, + data_repetition_count: 0, + ranging_time_struct: uci::RangingTimeStruct::BlockBasedScheduling, + slots_per_rr: 25, + aoa_bound_config: [0; 4], + prf_mode: uci::PrfMode::BprfMode, + // Default for Octet[0] is SLOTS_PER_RR - 1 + cap_size_range: [24, 5], + tx_jitter_window_size: 0, + schedule_mode: None, + key_rotation: uci::KeyRotation::Disable, + key_rotation_rate: 0, + session_priority: 50, + mac_address_mode: uci::MacAddressMode::Mode0, + vendor_id: 0, + static_sts_iv: [0; 6], + number_of_sts_segments: 1, + max_rr_retry: 0, + uwb_initiation_time: 0, + hopping_mode: uci::HoppingMode::Disable, + block_stride_length: 0, + result_report_config: 0x01, + in_band_termination_attempt_count: 1, + sub_session_id: 0, // XX + bprf_phr_data_rate: uci::BprfPhrDataRate::DataRate850k, + max_number_of_measurements: 0, + sts_length: uci::StsLength::Length64Symbols, + min_frames_per_rr: 4, + mtu_size: 0, // XX + inter_frame_interval: 1, + session_key: vec![], + sub_session_key: vec![], + session_data_transfer_status_ntf_config: + uci::SessionDataTransferStatusNtfConfig::Disable, + session_time_base: [0; 9], + application_data_endpoint: 0, + } + } +} + +impl AppConfig { + /// Set the APP configuration value with the selected identifier + /// and value. Returns `Ok` if the identifier is known and the value + /// well formatted, `Err` otherwise. + pub fn set(&mut self, id: uci::AppConfigTlvType, value: &[u8]) -> anyhow::Result<()> { + fn try_parse>(value: &[u8]) -> anyhow::Result { + T::try_from(u8::from_le_bytes(value.try_into()?)).map_err(anyhow::Error::msg) + } + + fn try_parse_u8(value: &[u8]) -> anyhow::Result { + Ok(u8::from_le_bytes(value.try_into()?)) + } + + fn try_parse_u16(value: &[u8]) -> anyhow::Result { + Ok(u16::from_le_bytes(value.try_into()?)) + } + + fn try_parse_u32(value: &[u8]) -> anyhow::Result { + Ok(u32::from_le_bytes(value.try_into()?)) + } + + fn try_parse_u64(value: &[u8]) -> anyhow::Result { + Ok(u64::from_le_bytes(value.try_into()?)) + } + + match id { + uci::AppConfigTlvType::DeviceType => self.device_type = Some(try_parse(value)?), + uci::AppConfigTlvType::RangingRoundUsage => { + self.ranging_round_usage = Some(try_parse(value)?) + } + uci::AppConfigTlvType::StsConfig => self.sts_config = try_parse(value)?, + uci::AppConfigTlvType::MultiNodeMode => self.multi_node_mode = Some(try_parse(value)?), + uci::AppConfigTlvType::ChannelNumber => self.channel_number = try_parse(value)?, + uci::AppConfigTlvType::NumberOfControlees => { + self.number_of_controlees = try_parse_u8(value)? + } + uci::AppConfigTlvType::DeviceMacAddress => { + self.device_mac_address = Some(match self.mac_address_mode { + uci::MacAddressMode::Mode0 => MacAddress::Short(value.try_into()?), + uci::MacAddressMode::Mode1 => unimplemented!(), + uci::MacAddressMode::Mode2 => MacAddress::Extended(value.try_into()?), + }) + } + uci::AppConfigTlvType::DstMacAddress => { + let mac_address_size = match self.mac_address_mode { + uci::MacAddressMode::Mode0 => 2, + uci::MacAddressMode::Mode1 => unimplemented!(), + uci::MacAddressMode::Mode2 => 8, + }; + if value.len() != self.number_of_controlees as usize * mac_address_size { + log::error!( + "invalid dst_mac_address len: expected {}x{}, got {}", + self.number_of_controlees, + mac_address_size, + value.len() + ); + anyhow::bail!("invalid dst_mac_address len") + } + self.dst_mac_address = value + .chunks(mac_address_size) + .map(|value| match self.mac_address_mode { + uci::MacAddressMode::Mode0 => MacAddress::Short(value.try_into().unwrap()), + uci::MacAddressMode::Mode1 => unimplemented!(), + uci::MacAddressMode::Mode2 => { + MacAddress::Extended(value.try_into().unwrap()) + } + }) + .collect(); + } + uci::AppConfigTlvType::SlotDuration => self.slot_duration = try_parse_u16(value)?, + uci::AppConfigTlvType::RangingDuration => self.ranging_duration = try_parse_u32(value)?, + uci::AppConfigTlvType::StsIndex => self.sts_index = try_parse_u32(value)?, + uci::AppConfigTlvType::MacFcsType => self.mac_fcs_type = try_parse(value)?, + uci::AppConfigTlvType::RangingRoundControl => { + self.ranging_round_control = try_parse_u8(value)? + } + uci::AppConfigTlvType::AoaResultReq => self.aoa_result_req = try_parse(value)?, + uci::AppConfigTlvType::SessionInfoNtfConfig => { + self.session_info_ntf_config = try_parse(value)? + } + uci::AppConfigTlvType::NearProximityConfig => { + self.near_proximity_config = try_parse_u16(value)? + } + uci::AppConfigTlvType::FarProximityConfig => { + self.far_proximity_config = try_parse_u16(value)? + } + uci::AppConfigTlvType::DeviceRole => self.device_role = Some(try_parse(value)?), + uci::AppConfigTlvType::RframeConfig => self.rframe_config = try_parse(value)?, + uci::AppConfigTlvType::RssiReporting => self.rssi_reporting = try_parse(value)?, + uci::AppConfigTlvType::PreambleCodeIndex => { + self.preamble_code_index = try_parse_u8(value)? + } + uci::AppConfigTlvType::SfdId => self.sfd_id = try_parse_u8(value)?, + uci::AppConfigTlvType::PsduDataRate => self.psdu_data_rate = try_parse(value)?, + uci::AppConfigTlvType::PreambleDuration => self.preamble_duration = try_parse(value)?, + uci::AppConfigTlvType::LinkLayerMode => self.link_layer_mode = try_parse(value)?, + uci::AppConfigTlvType::DataRepetitionCount => { + self.data_repetition_count = try_parse_u8(value)? + } + uci::AppConfigTlvType::RangingTimeStruct => { + self.ranging_time_struct = try_parse(value)? + } + uci::AppConfigTlvType::SlotsPerRr => self.slots_per_rr = try_parse_u8(value)?, + uci::AppConfigTlvType::AoaBoundConfig => { + if value.len() != 8 { + log::error!( + "invalid aoa_bound_config len: expected 8, got {}", + value.len() + ); + anyhow::bail!("invalid aoa_bound_config len") + } + self.aoa_bound_config = [ + u16::from_le_bytes([value[0], value[1]]), + u16::from_le_bytes([value[2], value[3]]), + u16::from_le_bytes([value[4], value[5]]), + u16::from_le_bytes([value[6], value[7]]), + ] + } + uci::AppConfigTlvType::PrfMode => self.prf_mode = try_parse(value)?, + uci::AppConfigTlvType::CapSizeRange => self.cap_size_range = value.try_into()?, + uci::AppConfigTlvType::TxJitterWindowSize => { + self.tx_jitter_window_size = try_parse_u8(value)? + } + uci::AppConfigTlvType::ScheduleMode => self.schedule_mode = Some(try_parse(value)?), + uci::AppConfigTlvType::KeyRotation => self.key_rotation = try_parse(value)?, + uci::AppConfigTlvType::KeyRotationRate => self.key_rotation_rate = try_parse_u8(value)?, + uci::AppConfigTlvType::SessionPriority => self.session_priority = try_parse_u8(value)?, + uci::AppConfigTlvType::MacAddressMode => self.mac_address_mode = try_parse(value)?, + uci::AppConfigTlvType::VendorId => self.vendor_id = try_parse_u16(value)?, + uci::AppConfigTlvType::StaticStsIv => self.static_sts_iv = value.try_into()?, + uci::AppConfigTlvType::NumberOfStsSegments => { + self.number_of_sts_segments = try_parse_u8(value)? + } + uci::AppConfigTlvType::MaxRrRetry => self.max_rr_retry = try_parse_u16(value)?, + uci::AppConfigTlvType::UwbInitiationTime => { + // Implement backward compatiblity for UCI 1.0 + // where the value is 4 bytes instead of 8. + self.uwb_initiation_time = match value.len() { + 4 => try_parse_u32(value)? as u64, + _ => try_parse_u64(value)?, + } + } + uci::AppConfigTlvType::HoppingMode => self.hopping_mode = try_parse(value)?, + uci::AppConfigTlvType::BlockStrideLength => { + self.block_stride_length = try_parse_u8(value)? + } + uci::AppConfigTlvType::ResultReportConfig => { + self.result_report_config = try_parse_u8(value)? + } + uci::AppConfigTlvType::InBandTerminationAttemptCount => { + self.in_band_termination_attempt_count = try_parse_u8(value)? + } + uci::AppConfigTlvType::SubSessionId => self.sub_session_id = try_parse_u32(value)?, + uci::AppConfigTlvType::BprfPhrDataRate => self.bprf_phr_data_rate = try_parse(value)?, + uci::AppConfigTlvType::MaxNumberOfMeasurements => { + self.max_number_of_measurements = try_parse_u16(value)? + } + uci::AppConfigTlvType::StsLength => self.sts_length = try_parse(value)?, + uci::AppConfigTlvType::MinFramesPerRr => self.min_frames_per_rr = try_parse_u8(value)?, + uci::AppConfigTlvType::MtuSize => self.mtu_size = try_parse_u16(value)?, + uci::AppConfigTlvType::InterFrameInterval => { + self.inter_frame_interval = try_parse_u8(value)? + } + uci::AppConfigTlvType::SessionKey => self.session_key = value.to_vec(), + uci::AppConfigTlvType::SubSessionKey => self.sub_session_key = value.to_vec(), + uci::AppConfigTlvType::SessionDataTransferStatusNtfConfig => { + self.session_data_transfer_status_ntf_config = try_parse(value)? + } + uci::AppConfigTlvType::SessionTimeBase => self.session_time_base = value.try_into()?, + uci::AppConfigTlvType::ApplicationDataEndpoint => { + self.application_data_endpoint = try_parse_u8(value)? + } + + uci::AppConfigTlvType::CccHopModeKey + | uci::AppConfigTlvType::CccUwbTime0 + | uci::AppConfigTlvType::CccRangingProtocolVer + | uci::AppConfigTlvType::CccUwbConfigId + | uci::AppConfigTlvType::CccPulseshapeCombo + | uci::AppConfigTlvType::CccUrskTtl + | uci::AppConfigTlvType::CccLastIndexUsed + | uci::AppConfigTlvType::NbOfRangeMeasurements + | uci::AppConfigTlvType::NbOfAzimuthMeasurements + | uci::AppConfigTlvType::NbOfElevationMeasurements + | uci::AppConfigTlvType::EnableDiagnostics + | uci::AppConfigTlvType::DiagramsFrameReportsFields => { + log::error!("unsupported vendor config type {:?}", id); + anyhow::bail!("unsupported vendor config type {:?}", id) + } + _ => { + log::error!("unsupported app config type {:?}", id); + anyhow::bail!("unsupported app config type {:?}", id) + } + } + Ok(()) + } + + /// Retrieve the APP configuration value with the selected identifier + /// Returns `Ok` if the identifier is known, `Err` otherwise. + pub fn get(&self, id: uci::AppConfigTlvType) -> anyhow::Result> { + match id { + uci::AppConfigTlvType::DeviceType => Ok(vec![self + .device_type + .ok_or(anyhow::anyhow!("optional app config not set"))? + .into()]), + uci::AppConfigTlvType::RangingRoundUsage => Ok(vec![self + .ranging_round_usage + .ok_or(anyhow::anyhow!("optional app config not set"))? + .into()]), + uci::AppConfigTlvType::StsConfig => Ok(vec![self.sts_config.into()]), + uci::AppConfigTlvType::MultiNodeMode => Ok(vec![self + .multi_node_mode + .ok_or(anyhow::anyhow!("optional app config not set"))? + .into()]), + uci::AppConfigTlvType::ChannelNumber => Ok(vec![self.channel_number.into()]), + uci::AppConfigTlvType::NumberOfControlees => Ok(vec![self.number_of_controlees]), + uci::AppConfigTlvType::DeviceMacAddress => Ok(self + .device_mac_address + .ok_or(anyhow::anyhow!("optional app config not set"))? + .into()), + uci::AppConfigTlvType::DstMacAddress => Ok(self + .dst_mac_address + .iter() + .flat_map(Vec::::from) + .collect()), + uci::AppConfigTlvType::SlotDuration => Ok(self.slot_duration.to_le_bytes().to_vec()), + uci::AppConfigTlvType::RangingDuration => { + Ok(self.ranging_duration.to_le_bytes().to_vec()) + } + uci::AppConfigTlvType::StsIndex => Ok(self.sts_index.to_le_bytes().to_vec()), + uci::AppConfigTlvType::MacFcsType => Ok(vec![self.mac_fcs_type.into()]), + uci::AppConfigTlvType::RangingRoundControl => Ok(vec![self.ranging_round_control]), + uci::AppConfigTlvType::AoaResultReq => Ok(vec![self.aoa_result_req.into()]), + uci::AppConfigTlvType::SessionInfoNtfConfig => { + Ok(vec![self.session_info_ntf_config.into()]) + } + uci::AppConfigTlvType::NearProximityConfig => { + Ok(self.near_proximity_config.to_le_bytes().to_vec()) + } + uci::AppConfigTlvType::FarProximityConfig => { + Ok(self.far_proximity_config.to_le_bytes().to_vec()) + } + uci::AppConfigTlvType::DeviceRole => Ok(vec![self + .device_role + .ok_or(anyhow::anyhow!("optional app config not set"))? + .into()]), + uci::AppConfigTlvType::RframeConfig => Ok(vec![self.rframe_config.into()]), + uci::AppConfigTlvType::RssiReporting => Ok(vec![self.rssi_reporting.into()]), + uci::AppConfigTlvType::PreambleCodeIndex => Ok(vec![self.preamble_code_index]), + uci::AppConfigTlvType::SfdId => Ok(vec![self.sfd_id]), + uci::AppConfigTlvType::PsduDataRate => Ok(vec![self.psdu_data_rate.into()]), + uci::AppConfigTlvType::PreambleDuration => Ok(vec![self.preamble_duration.into()]), + uci::AppConfigTlvType::LinkLayerMode => Ok(vec![self.link_layer_mode.into()]), + uci::AppConfigTlvType::DataRepetitionCount => Ok(vec![self.data_repetition_count]), + uci::AppConfigTlvType::RangingTimeStruct => Ok(vec![self.ranging_time_struct.into()]), + uci::AppConfigTlvType::SlotsPerRr => Ok(vec![self.slots_per_rr]), + uci::AppConfigTlvType::AoaBoundConfig => Ok(self + .aoa_bound_config + .iter() + .copied() + .flat_map(u16::to_le_bytes) + .collect()), + uci::AppConfigTlvType::PrfMode => Ok(vec![self.prf_mode.into()]), + uci::AppConfigTlvType::CapSizeRange => Ok(self.cap_size_range.to_vec()), + uci::AppConfigTlvType::TxJitterWindowSize => Ok(vec![self.tx_jitter_window_size]), + uci::AppConfigTlvType::ScheduleMode => Ok(vec![self + .schedule_mode + .ok_or(anyhow::anyhow!("optional app config not set"))? + .into()]), + uci::AppConfigTlvType::KeyRotation => Ok(vec![self.key_rotation.into()]), + uci::AppConfigTlvType::KeyRotationRate => Ok(vec![self.key_rotation_rate]), + uci::AppConfigTlvType::SessionPriority => Ok(vec![self.session_priority]), + uci::AppConfigTlvType::MacAddressMode => Ok(vec![self.mac_address_mode.into()]), + uci::AppConfigTlvType::VendorId => Ok(self.vendor_id.to_le_bytes().to_vec()), + uci::AppConfigTlvType::StaticStsIv => Ok(self.static_sts_iv.to_vec()), + uci::AppConfigTlvType::NumberOfStsSegments => Ok(vec![self.number_of_sts_segments]), + uci::AppConfigTlvType::MaxRrRetry => Ok(self.max_rr_retry.to_le_bytes().to_vec()), + uci::AppConfigTlvType::UwbInitiationTime => { + Ok(self.uwb_initiation_time.to_le_bytes().to_vec()) + } + uci::AppConfigTlvType::HoppingMode => Ok(vec![self.hopping_mode.into()]), + uci::AppConfigTlvType::BlockStrideLength => Ok(vec![self.block_stride_length]), + uci::AppConfigTlvType::ResultReportConfig => Ok(vec![self.result_report_config]), + uci::AppConfigTlvType::InBandTerminationAttemptCount => { + Ok(vec![self.in_band_termination_attempt_count]) + } + uci::AppConfigTlvType::SubSessionId => Ok(self.sub_session_id.to_le_bytes().to_vec()), + uci::AppConfigTlvType::BprfPhrDataRate => Ok(vec![self.bprf_phr_data_rate.into()]), + uci::AppConfigTlvType::MaxNumberOfMeasurements => { + Ok(self.max_number_of_measurements.to_le_bytes().to_vec()) + } + uci::AppConfigTlvType::StsLength => Ok(vec![self.sts_length.into()]), + uci::AppConfigTlvType::MinFramesPerRr => Ok(vec![self.min_frames_per_rr]), + uci::AppConfigTlvType::MtuSize => Ok(self.mtu_size.to_le_bytes().to_vec()), + uci::AppConfigTlvType::InterFrameInterval => Ok(vec![self.inter_frame_interval]), + uci::AppConfigTlvType::SessionKey => Ok(self.session_key.clone()), + uci::AppConfigTlvType::SubSessionKey => Ok(self.sub_session_key.clone()), + uci::AppConfigTlvType::SessionDataTransferStatusNtfConfig => { + Ok(vec![self.session_data_transfer_status_ntf_config.into()]) + } + uci::AppConfigTlvType::SessionTimeBase => Ok(self.session_time_base.to_vec()), + uci::AppConfigTlvType::ApplicationDataEndpoint => { + Ok(vec![self.application_data_endpoint]) + } + + uci::AppConfigTlvType::CccHopModeKey + | uci::AppConfigTlvType::CccUwbTime0 + | uci::AppConfigTlvType::CccRangingProtocolVer + | uci::AppConfigTlvType::CccUwbConfigId + | uci::AppConfigTlvType::CccPulseshapeCombo + | uci::AppConfigTlvType::CccUrskTtl + | uci::AppConfigTlvType::CccLastIndexUsed + | uci::AppConfigTlvType::NbOfRangeMeasurements + | uci::AppConfigTlvType::NbOfAzimuthMeasurements + | uci::AppConfigTlvType::NbOfElevationMeasurements + | uci::AppConfigTlvType::EnableDiagnostics + | uci::AppConfigTlvType::DiagramsFrameReportsFields => { + log::error!("unsupported vendor config type {:?}", id); + anyhow::bail!("unsupported vendor config type {:?}", id) + } + _ => { + log::error!("unsupported app config type {:?}", id); + anyhow::bail!("unsupported app config type {:?}", id) + } + } + } + + pub fn is_compatible_for_ranging(&self, peer_config: &Self) -> bool { + self == peer_config + && self.device_role != peer_config.device_role + && self.device_type != peer_config.device_type + && peer_config + .dst_mac_address + .contains(&self.device_mac_address.unwrap()) + && self + .dst_mac_address + .contains(&peer_config.device_mac_address.unwrap()) + } + + pub fn can_start_data_transfer(&self) -> bool { + self.device_role == Some(uci::DeviceRole::Initiator) + } + + pub fn can_receive_data_transfer(&self) -> bool { + self.device_role == Some(uci::DeviceRole::Responder) + } +} diff --git a/src/lib.rs b/src/lib.rs index 5b82c93..870f57f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,7 +36,8 @@ use session::MAX_SESSION; mod mac_address; pub use mac_address::MacAddress; -use crate::session::RangeDataNtfConfig; +mod app_config; +pub use app_config::AppConfig; pub type UciPacket = Vec; @@ -285,7 +286,7 @@ impl Pica { let mut uci_reader = packets::uci::Reader::new(uci_rx); let mut uci_writer = packets::uci::Writer::new(uci_tx); - tokio::try_join!( + let _ = tokio::try_join!( async { loop { // Read UCI packets sent from connected UWB host. @@ -311,8 +312,7 @@ impl Pica { } } } - ) - .unwrap(); + ); pica_tx .send(PicaCommand::Disconnect(device_handle)) @@ -343,7 +343,7 @@ impl Pica { let mut measurements = Vec::new(); let mut peer_device_data_transfer = Vec::new(); session - .get_dst_mac_addresses() + .get_dst_mac_address() .iter() .for_each(|mac_address| { if let Some(other) = self.anchors.get(mac_address) { @@ -397,7 +397,7 @@ impl Pica { ) .unwrap(); } - if session.is_ranging_data_ntf_enabled() != RangeDataNtfConfig::Disable { + if session.is_session_info_ntf_enabled() { device .tx .send( diff --git a/src/mac_address.rs b/src/mac_address.rs index 79cfe94..6b67e7e 100644 --- a/src/mac_address.rs +++ b/src/mac_address.rs @@ -52,6 +52,21 @@ impl From for u64 { } } +impl From<&MacAddress> for Vec { + fn from(mac_address: &MacAddress) -> Self { + match mac_address { + MacAddress::Short(addr) => addr.to_vec(), + MacAddress::Extended(addr) => addr.to_vec(), + } + } +} + +impl From for Vec { + fn from(mac_address: MacAddress) -> Self { + Vec::::from(&mac_address) + } +} + impl TryFrom for MacAddress { type Error = Error; fn try_from(mac_address: String) -> std::result::Result { diff --git a/src/session.rs b/src/session.rs index bbd23bb..dc0ed30 100644 --- a/src/session.rs +++ b/src/session.rs @@ -17,674 +17,25 @@ //! - [UCI] FiRa Consortium UWB Command Interface Generic Technical specification use crate::packets::uci::{self, *}; -use crate::{MacAddress, PicaCommand}; +use crate::{AppConfig, MacAddress, PicaCommand}; use bytes::BytesMut; -use std::collections::HashMap; use std::time::Duration; use tokio::sync::mpsc; use tokio::task::JoinHandle; use tokio::time; -use num_derive::{FromPrimitive, ToPrimitive}; -use num_traits::FromPrimitive; - use super::UciPacket; pub const MAX_SESSION: usize = 255; -pub const DEFAULT_RANGING_INTERVAL: Duration = time::Duration::from_millis(200); -pub const DEFAULT_SLOT_DURATION: u16 = 2400; // RTSU unit /// cf. [UCI] 8.3 Table 29 pub const MAX_NUMBER_OF_CONTROLEES: usize = 8; -pub const FIRA_1_1_INITIATION_TIME_SIZE: usize = 4; -pub const FIRA_2_0_INITIATION_TIME_SIZE: usize = 8; - -#[derive(Copy, Clone, FromPrimitive, PartialEq, Eq)] -pub enum DeviceType { - /// [MAC] 5.1.2 Device utilizing the ranging features set through Control Messages - Controlee = 0x00, - /// [MAC] 5.1.1 Device controlling the ranging features through Control Messages - Controller = 0x01, -} - -#[derive(Copy, Clone, FromPrimitive, PartialEq, Eq)] -pub enum DeviceRole { - /// [MAC] 5.1.3 Device initiating a ranging exchange with a ranging initiation message - Initiator, - /// [MAC] 5.1.4 Device responding to ranging initiation messages - Responder, -} - -/// cf. [UCI] 8.4 Table 29 -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq, Eq)] -#[repr(u8)] -pub enum MacAddressMode { - /// MAC address is 2 bytes and 2 bytes to be used in MAC header - AddressMode0 = 0x00, - /// Not Supported: MAC address is 8 bytes and 2 bytes to be used in MAC header - AddressMode1 = 0x01, - /// MAC address is 8 bytes and 8 bytes to be used in MAC header - AddressMode2 = 0x02, -} - -/// cf. [UCI] 8.3 Table 29 -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq, Eq)] -#[repr(u8)] -pub enum ChannelNumber { - ChannelNumber5 = 0x05, - ChannelNumber6 = 0x06, - ChannelNumber8 = 0x08, - ChannelNumber9 = 0x09, - ChannelNumber10 = 0x0a, - ChannelNumber12 = 0x0c, - ChannelNumber13 = 0x0d, - ChannelNumber14 = 0x0e, -} - -const DEFAULT_CHANNEL_NUMBER: ChannelNumber = ChannelNumber::ChannelNumber9; - -/// cf. [UCI] 8.3 Table 29 -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum MultiNodeMode { - /// Single device to single device - Unicast = 0x00, - OneToMany = 0x01, - ManyToMany = 0x02, -} - -/// cf. [UCI] 7.7 -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum UpdateMulticastListAction { - Add = 0x00, - Delete = 0x01, - AddWithShortSubSessionKey = 0x02, - AddwithExtendedSubSessionKey = 0x03, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum RangingRoundUsage { - UlTdoa = 0x00, - SsTwrDeferredMode = 0x01, - DsTwrDeferredMode = 0x02, - SsTwrNonDeferredMode = 0x03, - DsTwrNonDeferredMode = 0x04, - DlTdoa = 0x05, - OwrAoaMeasurement = 0x06, - DataTransferMode = 0x09, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum StsConfig { - Static = 0x00, - Dynamic = 0x01, - DynamicForControleeIndividualKey = 0x02, - Provisioned = 0x03, - ProvisionedForControleeIndividualKey = 0x04, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum MacFcsType { - MacFcsTypeCrc16 = 0x00, - MacFcsTypeCrc32 = 0x01, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum AoaResultReq { - NoAoaResult = 0x00, - ReqAoaResults = 0x01, - ReqAoaResultsAzimuthOnly = 0x02, - ReqAoaResultsElevationOnly = 0x03, - ReqAoaResultsInterleaved = 0x04, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum RframeConfig { - Sp0 = 0x00, - Sp1 = 0x01, - Sp3 = 0x03, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum PsduDataRate { - Rate6M81 = 0x00, - Rate7M80 = 0x01, - Rate27M2 = 0x02, - Rate31M2 = 0x03, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum PreambleDuration { - PreambleDurationT32Symbols = 0x00, - PreambleDurationT64Symbols = 0x01, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum RangingTimeStruct { - IntervalBasedScheduling = 0x00, - BlockBasedScheduling = 0x01, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum PrfMode { - PrfModeBprf = 0x00, - PrfModeHprf = 0x01, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum SchedulingMode { - ContentionBased = 0x00, - TimeScheduled = 0x01, - HybridScheduled = 0x02, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum HoppingMode { - Disable = 0x00, - FiraEnable = 0x01, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum StsLength { - StsLength32 = 0x00, - StsLength64 = 0x01, - StsLength128 = 0x02, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum BprfPhrDataRate { - BprfPhrDataRate850K = 0x00, - BprfPhrDataRate6M81 = 0x01, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum SfdIdValue { - SfdIdValue0 = 0x00, - SfdIdValue1 = 0x01, - SfdIdValue2 = 0x02, - SfdIdValue3 = 0x03, - SfdIdValue4 = 0x04, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -enum StsSegmentCountValue { - StsSegmentCountValue0 = 0x00, - StsSegmentCountValue1 = 0x01, - StsSegmentCountValue2 = 0x02, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -pub enum RangeDataNtfConfig { - Disable = 0x00, - Enable = 0x01, - EnableProximityLevelTrig = 0x02, - EnableAoaLevelTrig = 0x03, - EnableProximityAoaLevelTrig = 0x04, - EnableProximityEdgeTrig = 0x05, - EnableAoaEdgeTrig = 0x06, - EnableProximityAoaEdgeTrig = 0x07, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -pub enum LinkLayerMode { - Bypass = 0x00, - Assigned = 0x01, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -pub enum DataRepetitionCount { - NoRepetition = 0x00, - Infinite = 0xFF, -} - -#[derive(Copy, Clone, FromPrimitive, ToPrimitive, PartialEq)] -#[repr(u8)] -pub enum SessionDataTransferStatusNtfConfig { - Disable = 0x00, - Enable = 0x01, -} -/// cf. [UCI] 8.3 Table 29 -#[derive(Clone)] -pub struct AppConfig { - /// Copy of the valid App Configuration parameters provided by host - raw: HashMap>, - - device_type: DeviceType, - device_role: DeviceRole, - mac_address_mode: MacAddressMode, - pub device_mac_address: MacAddress, - number_of_controlees: usize, - dst_mac_addresses: Vec, - ranging_interval: time::Duration, - slot_duration: u16, - channel_number: ChannelNumber, - multi_node_mode: MultiNodeMode, - ranging_round_usage: RangingRoundUsage, - sts_config: StsConfig, - mac_fcs_type: MacFcsType, - ranging_round_control: u8, - aoa_result_req: AoaResultReq, - rng_data_ntf: RangeDataNtfConfig, - rng_data_ntf_proximity_near: u16, - rng_data_ntf_proximity_far: u16, - r_frame_config: RframeConfig, - rssi_reporting: bool, - preamble_code_index: u8, - sfd_id: SfdIdValue, - psdu_data_rate: PsduDataRate, - preamble_duration: PreambleDuration, - ranging_time_struct: RangingTimeStruct, - slots_per_rr: u8, - tx_adaptive_payload_power: bool, - prf_mode: PrfMode, - schedule_mode: SchedulingMode, - key_rotation: bool, - key_rotation_rate: u8, - session_priority: u8, - number_of_sts_segments: StsSegmentCountValue, - max_rr_retry: u16, - hopping_mode: HoppingMode, - block_stride_length: u8, - result_report_config: bool, - in_band_termination_attempt_count: u8, - bprf_phr_data_rate: BprfPhrDataRate, - max_number_of_measurements: u8, - sts_length: StsLength, - uwb_initiation_time: u64, - vendor_id: Option>, - static_sts_iv: Option>, - session_key: Option>, - sub_session_key: Option>, - sub_session_id: u32, - link_layer_mode: LinkLayerMode, - data_repetition_count: DataRepetitionCount, - session_data_transfer_status_ntf_config: SessionDataTransferStatusNtfConfig, - application_data_endpoint: u8, -} - -impl Default for AppConfig { - fn default() -> Self { - AppConfig { - raw: HashMap::new(), - mac_address_mode: MacAddressMode::AddressMode0, - device_role: DeviceRole::Responder, - device_type: DeviceType::Controlee, - ranging_interval: DEFAULT_RANGING_INTERVAL, - slot_duration: DEFAULT_SLOT_DURATION, - channel_number: DEFAULT_CHANNEL_NUMBER, - device_mac_address: MacAddress::Short([0x00, 0x00]), - number_of_controlees: 0, - dst_mac_addresses: Vec::new(), - multi_node_mode: MultiNodeMode::Unicast, - ranging_round_usage: RangingRoundUsage::DsTwrDeferredMode, - sts_config: StsConfig::Static, - mac_fcs_type: MacFcsType::MacFcsTypeCrc16, - ranging_round_control: 6_u8, - aoa_result_req: AoaResultReq::ReqAoaResults, - rng_data_ntf: RangeDataNtfConfig::Enable, - rng_data_ntf_proximity_near: 0, - rng_data_ntf_proximity_far: 0, - r_frame_config: RframeConfig::Sp3, - rssi_reporting: false, - preamble_code_index: 10, - sfd_id: SfdIdValue::SfdIdValue2, - psdu_data_rate: PsduDataRate::Rate6M81, - preamble_duration: PreambleDuration::PreambleDurationT64Symbols, - ranging_time_struct: RangingTimeStruct::IntervalBasedScheduling, - slots_per_rr: 25, - tx_adaptive_payload_power: false, - prf_mode: PrfMode::PrfModeBprf, - schedule_mode: SchedulingMode::TimeScheduled, - key_rotation: false, - key_rotation_rate: 0, - session_priority: 50, - number_of_sts_segments: StsSegmentCountValue::StsSegmentCountValue1, - max_rr_retry: 0, - hopping_mode: HoppingMode::Disable, - block_stride_length: 0, - result_report_config: true, - in_band_termination_attempt_count: 1, - bprf_phr_data_rate: BprfPhrDataRate::BprfPhrDataRate850K, - max_number_of_measurements: 0, - sts_length: StsLength::StsLength64, - uwb_initiation_time: 0, - vendor_id: None, - static_sts_iv: None, - session_key: None, - sub_session_key: None, - sub_session_id: 0, - link_layer_mode: LinkLayerMode::Bypass, - data_repetition_count: DataRepetitionCount::NoRepetition, - session_data_transfer_status_ntf_config: SessionDataTransferStatusNtfConfig::Disable, - application_data_endpoint: 0, - } - } -} - -impl PartialEq for AppConfig { - fn eq(&self, other: &Self) -> bool { - self.mac_address_mode == other.mac_address_mode - && self.ranging_interval == other.ranging_interval - && self.slot_duration == other.slot_duration - && self.channel_number == other.channel_number - && self.multi_node_mode == other.multi_node_mode - && self.ranging_round_usage == other.ranging_round_usage - && self.sts_config == other.sts_config - && self.mac_fcs_type == other.mac_fcs_type - && self.ranging_round_control == other.ranging_round_control - && self.aoa_result_req == other.aoa_result_req - && self.rng_data_ntf == other.rng_data_ntf - && self.rng_data_ntf_proximity_near == other.rng_data_ntf_proximity_near - && self.rng_data_ntf_proximity_far == other.rng_data_ntf_proximity_far - && self.r_frame_config == other.r_frame_config - && self.rssi_reporting == other.rssi_reporting - && self.preamble_code_index == other.preamble_code_index - && self.sfd_id == other.sfd_id - && self.psdu_data_rate == other.psdu_data_rate - && self.preamble_duration == other.preamble_duration - && self.ranging_time_struct == other.ranging_time_struct - && self.slots_per_rr == other.slots_per_rr - && self.tx_adaptive_payload_power == other.tx_adaptive_payload_power - && self.prf_mode == other.prf_mode - && self.schedule_mode == other.schedule_mode - && self.key_rotation == other.key_rotation - && self.key_rotation_rate == other.key_rotation_rate - && self.session_priority == other.session_priority - && self.number_of_sts_segments == other.number_of_sts_segments - && self.max_rr_retry == other.max_rr_retry - && self.result_report_config == other.result_report_config - && self.bprf_phr_data_rate == other.bprf_phr_data_rate - && self.max_number_of_measurements == other.max_number_of_measurements - && self.sts_length == other.sts_length - && self.uwb_initiation_time == other.uwb_initiation_time - && self.vendor_id == other.vendor_id - && self.static_sts_iv == other.static_sts_iv - } -} - -fn app_config_has_mandatory_parameters(configs: &[AppConfigTlv]) -> bool { - const MANDATORY_PARAMETERS: [AppConfigTlvType; 6] = [ - AppConfigTlvType::DeviceRole, - AppConfigTlvType::MultiNodeMode, - AppConfigTlvType::NoOfControlee, - AppConfigTlvType::DeviceMacAddress, - AppConfigTlvType::DstMacAddress, - AppConfigTlvType::DeviceType, - ]; - - MANDATORY_PARAMETERS - .iter() - .all(|&mparam| configs.iter().any(|param| mparam == param.cfg_id)) -} - -impl AppConfig { - fn set_config( - &mut self, - id: AppConfigTlvType, - value: &[u8], - ) -> std::result::Result<(), StatusCode> { - match id { - AppConfigTlvType::MacAddressMode => { - let mode = MacAddressMode::from_u8(value[0]).unwrap(); - if mode == MacAddressMode::AddressMode1 { - return Err(StatusCode::UciStatusInvalidParam); - } - self.mac_address_mode = mode; - } - AppConfigTlvType::RangingDuration => { - let interval = u32::from_le_bytes(value[..].try_into().unwrap()); - self.ranging_interval = time::Duration::from_millis(interval as u64) - } - AppConfigTlvType::SlotDuration => { - self.slot_duration = u16::from_le_bytes(value[..].try_into().unwrap()) - } - AppConfigTlvType::ChannelNumber => { - self.channel_number = ChannelNumber::from_u8(value[0]).unwrap() - } - AppConfigTlvType::DeviceMacAddress => { - self.device_mac_address = match self.mac_address_mode { - MacAddressMode::AddressMode0 => { - MacAddress::Short(value[..].try_into().unwrap()) - } - MacAddressMode::AddressMode2 => { - MacAddress::Extended(value[..].try_into().unwrap()) - } - _ => panic!("Unexpected MAC Address Mode"), - }; - } - AppConfigTlvType::NoOfControlee => { - assert!(value[0] as usize <= MAX_NUMBER_OF_CONTROLEES); - self.number_of_controlees = value[0] as usize; - } - AppConfigTlvType::DstMacAddress => { - let mac_address_size = match self.mac_address_mode { - MacAddressMode::AddressMode0 => 2, - MacAddressMode::AddressMode2 => 8, - _ => panic!("Unexpected MAC Address Mode"), - }; - if value.len() % mac_address_size != 0 - || (value.len() / mac_address_size) != self.number_of_controlees - { - return Err(StatusCode::UciStatusInvalidParam); - } - self.dst_mac_addresses = value - .chunks(mac_address_size) - .map(|c| match self.mac_address_mode { - MacAddressMode::AddressMode0 => MacAddress::Short(c.try_into().unwrap()), - MacAddressMode::AddressMode2 => MacAddress::Extended(c.try_into().unwrap()), - _ => panic!("Unexpected MAC Address Mode"), - }) - .collect(); - assert_eq!(self.dst_mac_addresses.len(), self.number_of_controlees); - } - AppConfigTlvType::MultiNodeMode => { - self.multi_node_mode = MultiNodeMode::from_u8(value[0]).unwrap() - } - AppConfigTlvType::DeviceType => { - self.device_type = DeviceType::from_u8(value[0]).unwrap(); - } - AppConfigTlvType::RangingRoundUsage => { - self.ranging_round_usage = RangingRoundUsage::from_u8(value[0]).unwrap() - } - AppConfigTlvType::StsConfig => self.sts_config = StsConfig::from_u8(value[0]).unwrap(), - AppConfigTlvType::MacFcsType => { - self.mac_fcs_type = MacFcsType::from_u8(value[0]).unwrap() - } - AppConfigTlvType::RangingRoundControl => self.ranging_round_control = value[0], - AppConfigTlvType::AoaResultReq => { - self.aoa_result_req = AoaResultReq::from_u8(value[0]).unwrap() - } - AppConfigTlvType::RngDataNtf => { - self.rng_data_ntf = RangeDataNtfConfig::from_u8(value[0]).unwrap() - } - AppConfigTlvType::RngDataNtfProximityNear => { - self.rng_data_ntf_proximity_near = u16::from_le_bytes(value[..].try_into().unwrap()) - } - AppConfigTlvType::RngDataNtfProximityFar => { - self.rng_data_ntf_proximity_far = u16::from_le_bytes(value[..].try_into().unwrap()) - } - AppConfigTlvType::DeviceRole => { - self.device_role = DeviceRole::from_u8(value[0]).unwrap(); - } - AppConfigTlvType::RframeConfig => { - self.r_frame_config = RframeConfig::from_u8(value[0]).unwrap() - } - AppConfigTlvType::RssiReporting => { - self.rssi_reporting = match value[0] { - 0 => false, - 1 => true, - _ => panic!("Invalid rssi reporting value!"), - } - } - AppConfigTlvType::PreambleCodeIndex => self.preamble_code_index = value[0], - AppConfigTlvType::SfdId => self.sfd_id = SfdIdValue::from_u8(value[0]).unwrap(), - AppConfigTlvType::PsduDataRate => { - self.psdu_data_rate = PsduDataRate::from_u8(value[0]).unwrap() - } - AppConfigTlvType::PreambleDuration => { - self.preamble_duration = PreambleDuration::from_u8(value[0]).unwrap() - } - AppConfigTlvType::RangingTimeStruct => { - self.ranging_time_struct = RangingTimeStruct::from_u8(value[0]).unwrap() - } - AppConfigTlvType::SlotsPerRr => self.slots_per_rr = value[0], - AppConfigTlvType::TxAdaptivePayloadPower => { - self.tx_adaptive_payload_power = match value[0] { - 0 => false, - 1 => true, - _ => panic!("Invalid tx adaptive payload power value!"), - } - } - AppConfigTlvType::PrfMode => self.prf_mode = PrfMode::from_u8(value[0]).unwrap(), - AppConfigTlvType::ScheduledMode => { - self.schedule_mode = SchedulingMode::from_u8(value[0]).unwrap() - } - AppConfigTlvType::KeyRotation => { - self.key_rotation = match value[0] { - 0 => false, - 1 => true, - _ => panic!("Invalid key rotation value!"), - } - } - AppConfigTlvType::KeyRotationRate => self.key_rotation_rate = value[0], - AppConfigTlvType::SessionPriority => self.session_priority = value[0], - AppConfigTlvType::VendorId => { - self.vendor_id = Some(value.to_vec()); - } - AppConfigTlvType::StaticStsIv => { - self.static_sts_iv = Some(value.to_vec()); - } - AppConfigTlvType::NumberOfStsSegments => { - self.number_of_sts_segments = StsSegmentCountValue::from_u8(value[0]).unwrap() - } - AppConfigTlvType::MaxRrRetry => { - self.max_rr_retry = u16::from_le_bytes(value[..].try_into().unwrap()) - } - AppConfigTlvType::UwbInitiationTime => { - self.uwb_initiation_time = match value.len() { - // Backward compatible with Fira 1.1 Version UCI host. - FIRA_1_1_INITIATION_TIME_SIZE => { - u32::from_le_bytes(value[..].try_into().unwrap()) as u64 - } - FIRA_2_0_INITIATION_TIME_SIZE => { - u64::from_le_bytes(value[..].try_into().unwrap()) - } - _ => panic!("Invalid initiation time!"), - } - } - AppConfigTlvType::HoppingMode => { - self.hopping_mode = HoppingMode::from_u8(value[0]).unwrap() - } - AppConfigTlvType::BlockStrideLength => self.block_stride_length = value[0], - AppConfigTlvType::ResultReportConfig => { - self.result_report_config = match value[0] { - 0 => false, - 1 => true, - _ => panic!("Invalid result report config value!"), - } - } - AppConfigTlvType::BprfPhrDataRate => { - self.bprf_phr_data_rate = BprfPhrDataRate::from_u8(value[0]).unwrap() - } - AppConfigTlvType::MaxNumberOfMeasurements => self.max_number_of_measurements = value[0], - AppConfigTlvType::StsLength => self.sts_length = StsLength::from_u8(value[0]).unwrap(), - AppConfigTlvType::InBandTerminationAttemptCount => { - self.in_band_termination_attempt_count = value[0] - } - AppConfigTlvType::SessionKey => self.session_key = Some(value.to_vec()), - AppConfigTlvType::SubSessionId => { - self.sub_session_id = u32::from_le_bytes(value[..].try_into().unwrap()) - } - AppConfigTlvType::SubsessionKey => self.sub_session_key = Some(value.to_vec()), - AppConfigTlvType::LinkLayerMode => { - self.link_layer_mode = LinkLayerMode::from_u8(value[0]).unwrap() - } - AppConfigTlvType::DataRepetitionCount => { - self.data_repetition_count = DataRepetitionCount::from_u8(value[0]).unwrap() - } - AppConfigTlvType::SessionDataTransferStatusNtfConfig => { - self.session_data_transfer_status_ntf_config = - SessionDataTransferStatusNtfConfig::from_u8(value[0]).unwrap() - } - AppConfigTlvType::ApplicationDataEndpoint => self.application_data_endpoint = value[0], - id => { - log::error!("Ignored AppConfig parameter {:?}", id); - return Err(StatusCode::UciStatusInvalidParam); - } - }; - - self.raw.insert(id, value.to_vec()); - - Ok(()) - } - - fn get_config(&self, id: AppConfigTlvType) -> Option> { - self.raw.get(&id).cloned() - } - - pub fn is_compatible_for_ranging(&self, peer_config: &Self) -> bool { - self == peer_config - && self.device_role != peer_config.device_role - && self.device_type != peer_config.device_type - && peer_config - .dst_mac_addresses - .contains(&self.device_mac_address) - && self - .dst_mac_addresses - .contains(&peer_config.device_mac_address) - } - - pub fn can_start_data_transfer(&self) -> bool { - self.device_role == DeviceRole::Initiator - } - - pub fn can_receive_data_transfer(&self) -> bool { - self.device_role == DeviceRole::Responder - } - - fn extend(&mut self, configs: &[AppConfigTlv]) -> Vec { - if !app_config_has_mandatory_parameters(configs) { - // TODO: What shall we do in this situation? - } - - configs - .iter() - .fold(Vec::new(), |mut invalid_parameters, config| { - match self.set_config(config.cfg_id, &config.v) { - Ok(_) => (), - Err(status) => invalid_parameters.push(AppConfigStatus { - cfg_id: config.cfg_id, - status, - }), - }; - invalid_parameters - }) - } -} enum SubSessionKey { None, Short([u8; 16]), Extended([u8; 32]), } + struct Controlee { short_address: MacAddress, sub_session_id: u32, @@ -785,12 +136,18 @@ impl Session { }); } - pub fn get_dst_mac_addresses(&self) -> &Vec { - &self.app_config.dst_mac_addresses + pub fn get_dst_mac_address(&self) -> &[MacAddress] { + &self.app_config.dst_mac_address + } + + pub fn is_session_info_ntf_enabled(&self) -> bool { + self.app_config.session_info_ntf_config != uci::SessionInfoNtfConfig::Disable } - pub fn is_ranging_data_ntf_enabled(&self) -> RangeDataNtfConfig { - self.app_config.rng_data_ntf + #[allow(unused)] + pub fn is_session_data_transfer_status_ntf_enabled(&self) -> bool { + self.app_config.session_data_transfer_status_ntf_config + != uci::SessionDataTransferStatusNtfConfig::Disable } pub fn data(&self) -> &BytesMut { @@ -852,7 +209,47 @@ impl Session { (StatusCode::UciStatusRejected, Vec::new()) } else { let mut app_config = self.app_config.clone(); - let invalid_parameters = app_config.extend(cmd.get_tlvs()); + let mut invalid_parameters = vec![]; + for cfg in cmd.get_tlvs() { + match app_config.set(cfg.cfg_id, &cfg.v) { + Ok(_) => (), + Err(_) => invalid_parameters.push(AppConfigStatus { + cfg_id: cfg.cfg_id, + status: uci::StatusCode::UciStatusInvalidParam, + }), + } + } + + // [UCI] 7.5.1 Configuration of a Session + // This section defines the mandatory APP Configuration Parameters to be applied + // by the Host for FiRa defined UWB Session types. The Host shall apply these + // mandatory configurations to move the Session State from SESSION_STATE_INIT + // to SESSION_STATE_IDLE. + // + // - DEVICE_ROLE + // - MULTI_NODE_MODE + // - RANGING_ROUND_USAGE + // - DEVICE_MAC_ADDRESS + // - DEVICE_TYPE (see Note1) + // - SCHEDULE_MODE + if app_config.device_role.is_none() + || app_config.multi_node_mode.is_none() + || app_config.ranging_round_usage.is_none() + || app_config.device_mac_address.is_none() + || app_config.schedule_mode.is_none() + { + log::error!( + "[{}:0x{:x}] missing mandatory APP config parameters", + self.device_handle, + self.id + ); + return SessionSetAppConfigRspBuilder { + status: uci::StatusCode::UciStatusRejected, + cfg_status: vec![], + } + .build(); + } + if invalid_parameters.is_empty() { self.app_config = app_config; if self.state == SessionState::SessionStateInit { @@ -883,25 +280,21 @@ impl Session { assert_eq!(self.id, cmd.get_session_token()); let (status, valid_parameters) = { - let (valid_parameters, invalid_parameters) = cmd.get_app_cfg().iter().fold( - (Vec::new(), Vec::new()), - |(mut valid_parameters, mut invalid_parameters), config_id| { - match AppConfigTlvType::try_from(*config_id) { - Ok(id) => match self.app_config.get_config(id) { - Some(value) => valid_parameters.push(AppConfigTlv { - cfg_id: id, - v: value, - }), - None => invalid_parameters.push(AppConfigTlv { - cfg_id: id, - v: Vec::new(), - }), - }, - Err(_) => log::error!("Failed to parse AppConfigTlv: {:?}", *config_id), - } - (valid_parameters, invalid_parameters) - }, - ); + let mut valid_parameters = vec![]; + let mut invalid_parameters = vec![]; + for id in cmd.get_app_cfg() { + match self.app_config.get(*id) { + Ok(value) => valid_parameters.push(AppConfigTlv { + cfg_id: *id, + v: value, + }), + Err(_) => invalid_parameters.push(AppConfigTlv { + cfg_id: *id, + v: vec![], + }), + } + } + if invalid_parameters.is_empty() { (StatusCode::UciStatusOk, valid_parameters) } else { @@ -937,19 +330,19 @@ impl Session { assert_eq!(self.id, cmd.get_session_token()); if (self.state != SessionState::SessionStateActive && self.state != SessionState::SessionStateIdle) - || self.app_config.device_type != DeviceType::Controller - || (self.app_config.multi_node_mode != MultiNodeMode::OneToMany - && self.app_config.multi_node_mode != MultiNodeMode::ManyToMany) + || self.app_config.device_type != Some(DeviceType::Controller) + || self.app_config.multi_node_mode != Some(MultiNodeMode::OneToMany) { return SessionUpdateControllerMulticastListRspBuilder { status: StatusCode::UciStatusRejected, } .build(); } - let action = UpdateMulticastListAction::from_u8(cmd.get_action().into()).unwrap(); - let mut dst_addresses = self.app_config.dst_mac_addresses.clone(); + let action = cmd.get_action(); + let mut dst_addresses = self.app_config.dst_mac_address.clone(); let new_controlees: Vec = match action { - UpdateMulticastListAction::Add | UpdateMulticastListAction::Delete => { + UpdateMulticastListAction::AddControlee + | UpdateMulticastListAction::RemoveControlee => { if let Ok(packet) = SessionUpdateControllerMulticastListCmdPayload::parse(cmd.get_payload()) { @@ -965,7 +358,7 @@ impl Session { .build(); } } - UpdateMulticastListAction::AddWithShortSubSessionKey => { + UpdateMulticastListAction::AddControleeWithShortSubSessionKey => { if let Ok(packet) = SessionUpdateControllerMulticastListCmd_2_0_16_Byte_Payload::parse( cmd.get_payload(), @@ -983,7 +376,7 @@ impl Session { .build(); } } - UpdateMulticastListAction::AddwithExtendedSubSessionKey => { + UpdateMulticastListAction::AddControleeWithExtendedSubSessionKey => { if let Ok(packet) = SessionUpdateControllerMulticastListCmd_2_0_32_Byte_Payload::parse( cmd.get_payload(), @@ -1008,19 +401,21 @@ impl Session { let mut status = StatusCode::UciStatusOk; match action { - UpdateMulticastListAction::Add - | UpdateMulticastListAction::AddWithShortSubSessionKey - | UpdateMulticastListAction::AddwithExtendedSubSessionKey => { + UpdateMulticastListAction::AddControlee + | UpdateMulticastListAction::AddControleeWithShortSubSessionKey + | UpdateMulticastListAction::AddControleeWithExtendedSubSessionKey => { new_controlees.iter().for_each(|controlee| { let mut update_status = MulticastUpdateStatusCode::StatusOkMulticastListUpdate; if !dst_addresses.contains(&controlee.short_address) { if dst_addresses.len() == MAX_NUMBER_OF_CONTROLEES { status = StatusCode::UciStatusMulticastListFull; update_status = MulticastUpdateStatusCode::StatusErrorMulticastListFull; - } else if (action == UpdateMulticastListAction::AddWithShortSubSessionKey - || action == UpdateMulticastListAction::AddwithExtendedSubSessionKey) + } else if (action + == UpdateMulticastListAction::AddControleeWithShortSubSessionKey + || action + == UpdateMulticastListAction::AddControleeWithExtendedSubSessionKey) && self.app_config.sts_config - != StsConfig::ProvisionedForControleeIndividualKey + != uci::StsConfig::ProvisionedForResponderSubSessionKey { // If Action is 0x02 or 0x03 for STS_CONFIG values other than // 0x04, the UWBS shall return SESSION_UPDATE_CONTROLLER_MULTICAST_LIST_NTF @@ -1043,7 +438,7 @@ impl Session { }); }); } - UpdateMulticastListAction::Delete => { + UpdateMulticastListAction::RemoveControlee => { new_controlees.iter().for_each(|controlee: &Controlee| { let pica_tx = self.pica_tx.clone(); let address = controlee.short_address; @@ -1080,12 +475,12 @@ impl Session { }); } } - self.app_config.number_of_controlees = dst_addresses.len(); - self.app_config.dst_mac_addresses = dst_addresses.clone(); + self.app_config.number_of_controlees = dst_addresses.len() as u8; + self.app_config.dst_mac_address = dst_addresses.clone(); // If the multicast list becomes empty, the UWBS shall move the session to // SESSION_STATE_IDLE by sending the SESSION_STATUS_NTF with Reason Code // set to ERROR_INVALID_NUM_OF_CONTROLEES. - if self.app_config.dst_mac_addresses.is_empty() { + if self.app_config.dst_mac_address.is_empty() { self.set_state( SessionState::SessionStateIdle, ReasonCode::ErrorInvalidNumOfControlees, @@ -1118,7 +513,8 @@ impl Session { assert_eq!(self.state, SessionState::SessionStateIdle); let session_id = self.id; - let ranging_interval = self.app_config.ranging_interval; + let ranging_interval = + time::Duration::from_millis(self.app_config.ranging_duration as u64); let device_handle = self.device_handle; let tx = self.pica_tx.clone(); self.ranging_task = Some(tokio::spawn(async move { diff --git a/src/uci_packets.pdl b/src/uci_packets.pdl index 2213e02..ebb8db2 100644 --- a/src/uci_packets.pdl +++ b/src/uci_packets.pdl @@ -199,13 +199,14 @@ enum DeviceConfigId : 8 { LOW_POWER_MODE = 0x01, } +// [UCI] Table 45: APP Configuration Parameters IDs enum AppConfigTlvType : 8 { DEVICE_TYPE = 0x00, RANGING_ROUND_USAGE = 0x01, STS_CONFIG = 0x02, MULTI_NODE_MODE = 0x03, CHANNEL_NUMBER = 0x04, - NO_OF_CONTROLEE = 0x05, + NUMBER_OF_CONTROLEES = 0x05, DEVICE_MAC_ADDRESS = 0x06, DST_MAC_ADDRESS = 0x07, SLOT_DURATION = 0x08, @@ -214,9 +215,9 @@ enum AppConfigTlvType : 8 { MAC_FCS_TYPE = 0x0B, RANGING_ROUND_CONTROL = 0x0C, AOA_RESULT_REQ = 0x0D, - RNG_DATA_NTF = 0x0E, - RNG_DATA_NTF_PROXIMITY_NEAR = 0x0F, - RNG_DATA_NTF_PROXIMITY_FAR = 0x10, + SESSION_INFO_NTF_CONFIG = 0x0E, + NEAR_PROXIMITY_CONFIG = 0x0F, + FAR_PROXIMITY_CONFIG = 0x10, DEVICE_ROLE = 0x11, RFRAME_CONFIG = 0x12, RSSI_REPORTING = 0x13, @@ -228,14 +229,11 @@ enum AppConfigTlvType : 8 { DATA_REPETITION_COUNT = 0x19, RANGING_TIME_STRUCT = 0x1A, SLOTS_PER_RR = 0x1B, - TX_ADAPTIVE_PAYLOAD_POWER = 0x1C, - // TODO: Ensure this value is correct in the final 2.0 specification. - RNG_DATA_NTF_AOA_BOUND = 0x1D, - RESPONDER_SLOT_INDEX = 0x1E, + AOA_BOUND_CONFIG = 0x1D, PRF_MODE = 0x1F, CAP_SIZE_RANGE = 0x20, TX_JITTER_WINDOW_SIZE = 0x21, - SCHEDULED_MODE = 0x22, + SCHEDULE_MODE = 0x22, KEY_ROTATION = 0x23, KEY_ROTATION_RATE = 0x24, SESSION_PRIORITY = 0x25, @@ -252,13 +250,7 @@ enum AppConfigTlvType : 8 { SUB_SESSION_ID = 0x30, BPRF_PHR_DATA_RATE = 0x31, MAX_NUMBER_OF_MEASUREMENTS = 0x32, - UL_TDOA_TX_INTERVAL = 0x33, - UL_TDOA_RANDOM_WINDOW = 0x34, STS_LENGTH = 0x35, - SUSPEND_RANGING_ROUNDS = 0x36, - UL_TDOA_NTF_REPORT_CONFIG = 0x37, - UL_TDOA_DEVICE_ID = 0x38, - UL_TDOA_TX_TIMESTAMP = 0x39, MIN_FRAMES_PER_RR = 0x3A, MTU_SIZE = 0x3B, INTER_FRAME_INTERVAL = 0x3C, @@ -268,10 +260,10 @@ enum AppConfigTlvType : 8 { DL_TDOA_ANCHOR_CFO = 0x40, DL_TDOA_ANCHOR_LOCATION = 0x41, DL_TDOA_TX_ACTIVE_RANGING_ROUNDS = 0x42, - DL_TDOA_BLOCK_STRIDING = 0x43, + DL_TDOA_BLOCK_SKIPPING = 0x43, DL_TDOA_TIME_REFERENCE_ANCHOR = 0x44, SESSION_KEY = 0x45, - SUBSESSION_KEY = 0x46, + SUB_SESSION_KEY = 0x46, SESSION_DATA_TRANSFER_STATUS_NTF_CONFIG = 0x47, SESSION_TIME_BASE = 0x48, DL_TDOA_RESPONDER_TOF = 0x49, @@ -279,9 +271,9 @@ enum AppConfigTlvType : 8 { SECURE_RANGING_CSW_LENGTH = 0x4B, APPLICATION_DATA_ENDPOINT = 0x4C, OWR_AOA_MEASUREMENT_NTF_PERIOD = 0x4D, - RFU_APP_CFG_TLV_TYPE_RANGE = 0x4E..0x9F, + RFU = 0x4E..0x9F, - VENDOR_SPECIFIC_APP_CFG_TLV_TYPE_RANGE_1 = 0xA0..0xDF { + VENDOR_SPECIFIC_1 = 0xA0..0xDF { // CCC specific CCC_HOP_MODE_KEY = 0xA0, CCC_UWB_TIME0 = 0xA1, @@ -293,9 +285,10 @@ enum AppConfigTlvType : 8 { }, // Reserved for extension IDs. - RFU_APP_CFG_TLV_TYPE_RANGE_3 = 0xE0..0xE2, + // ID is 2 Octets in length. Refer section 8.1 for details + ID_EXTENSION = 0xE0..0xE2, - VENDOR_SPECIFIC_APP_CFG_TLV_TYPE_RANGE_2 = 0xE3..0xFF { + VENDOR_SPECIFIC_2 = 0xE3..0xFF { // Interleaving ratio if AOA_RESULT_REQ is set to 0xF0. NB_OF_RANGE_MEASUREMENTS = 0xE3, NB_OF_AZIMUTH_MEASUREMENTS = 0xE4, @@ -305,10 +298,189 @@ enum AppConfigTlvType : 8 { }, } -enum FrameReportTlvType : 8 { - RSSI = 0x0, - AOA = 0x1, - CIR = 0x2, +enum DeviceType : 8 { + // [MAC] 5.1.2 + // Device utilizing the ranging features set through Control Messages + CONTROLEE = 0x00, + // [MAC] 5.1.1 + // Device controlling the ranging features through Control Messages + CONTROLLER = 0x01, +} + +enum RangingRoundUsage : 8 { + SS_TWR_DEFERRED_MODE = 0x01, + DS_TWR_DEFERRED_MODE = 0x02, + SS_TWR_NON_DEFERRED_MODE = 0x03, + DS_TWR_NON_DEFERRED_MODE = 0x04, + ON_WAY_RANGING_DL_TDOA = 0x05, + OWR_AOA_MEASUREMENT = 0x06, + ESS_TWR_NON_DEFERRED = 0x07, + ADS_TWR_NON_DEFERRED = 0x08, +} + +enum StsConfig : 8 { + STATIC = 0x00, // Default + DYNAMIC = 0x01, + DYNAMIC_FOR_RESPONDER_SUB_SESSION_KEY = 0x02, + PROVISIONED = 0x03, + PROVISIONED_FOR_RESPONDER_SUB_SESSION_KEY = 0x04, +} + +enum MultiNodeMode : 8 { + ONE_TO_ONE = 0x00, + ONE_TO_MANY = 0x01, +} + +enum ChannelNumber : 8 { + CHANNEL_NUMBER_5 = 0x05, + CHANNEL_NUMBER_6 = 0x06, + CHANNEL_NUMBER_8 = 0x08, + CHANNEL_NUMBER_9 = 0x09, // Default + CHANNEL_NUMBER_10 = 0x0a, + CHANNEL_NUMBER_12 = 0x0c, + CHANNEL_NUMBER_13 = 0x0d, + CHANNEL_NUMBER_14 = 0x0e, +} + +enum MacFcsType : 8 { + CRC_16 = 0x00, // Default + CRC_32 = 0x01, +} + +struct RangingRoundControl { + rrrm : 1, // Default 1 + _fixed_ = 1 : 1, + rcp : 1, // Default 0 + _reserved_ : 3, + mrp : 1, // Default 0 + mrm : 1, // Default 0 +} + +enum AoaResultReq : 8 { + AOA_DISABLED = 0x00, + AOA_ENABLED = 0x01, // Default + AOA_ENABLED_AZIMUTH_ONLY = 0x02, + AOA_ENABLED_ELEVATION_ONLY = 0x03, +} + +enum SessionInfoNtfConfig : 8 { + DISABLE = 0x00, + ENABLE = 0x01, // Default + ENABLE_PROXIMITY_TRIGGER = 0x02, + ENABLE_AOA_TRIGGER = 0x03, + ENABLE_PROXIMITY_AOA_TRIGGER = 0x04, + ENABLE_PROXIMITY_EDGE_TRIGGER = 0x05, + ENABLE_AOA_EDGE_TRIGGER = 0x06, + ENABLE_PROXIMITY_AOA_EDGE_TRIGGER = 0x07, +} + +enum DeviceRole : 8 { + // [MAC] 5.1.3 + // Device initiating a ranging exchange with a ranging initiation message + RESPONDER = 0x00, + // [MAC] 5.1.4 + // Device responding to ranging initiation messages + INITIATOR = 0x01, + ADVERTISER = 0x05, + OBSERVER = 0x06, + DT_ANCHOR = 0x07, + DT_TAG = 0x08, +} + +enum RframeConfig : 8 { + SP0 = 0x00, + SP1 = 0x01, + SP3 = 0x03, // Default +} + +enum RssiReporting : 8 { + DISABLE = 0x00, // Default + ENABLE = 0x01, +} + +enum PsduDataRate : 8 { + DATA_RATE_6M81 = 0x00, + DATA_RATE_7M80 = 0x01, + DATA_RATE_27M2 = 0x02, + DATA_RATE_31M2 = 0x03, +} + +enum PreambleDuration : 8 { + DURATION_32_SYMBOLS = 0x00, + DURATION_64_SYMBOLS = 0x01, // Default +} + +enum LinkLayerMode : 8 { + BYPASS_MODE = 0x00, // Default +} + +enum RangingTimeStruct : 8 { + BLOCK_BASED_SCHEDULING = 0x01, // Default +} + +enum PrfMode : 8 { + BPRF_MODE = 0x00, // Default + HPRF_MODE_124M8 = 0x01, + HPRF_MODE_249M6 = 0x02, +} + +enum ScheduleMode : 8 { + CONTENTION_BASED = 0x00, + TIME_SCHEDULED = 0x01, +} + +enum KeyRotation : 8 { + DISABLE = 0x00, // Default + ENABLE = 0x01, +} + +enum MacAddressMode : 8 { + // MAC address is 2 bytes and 2 bytes to be used in MAC header + MODE_0 = 0x00, // Default + // MAC address is 8 bytes and 2 bytes to be used in MAC header + // (Not supported). + MODE_1 = 0x01, + // MAC address is 8 bytes and 8 bytes to be used in MAC header + MODE_2 = 0x02, +} + +enum HoppingMode : 8 { + DISABLE = 0x00, // Default + ENABLE = 0x01, +} + +struct ResultReportConfig { + tof : 1, // Default 1 + aoa_azimuth : 1, + aoa_elevation : 1, + aoa_fom : 1, + _reserved_ : 4, +} + +enum BprfPhrDataRate : 8 { + DATA_RATE_850K = 0x00, // Default + DATA_RATE_6M81 = 0x01 +} + +enum StsLength : 8 { + LENGTH_32_SYMBOLS = 0x00, // Default + LENGTH_64_SYMBOLS = 0x01, + LENGTH_128_SYMBOLS = 0x02, +} + +enum DlTdoaRangingMethod : 8 { + SS_TWR = 0x00, + DS_TWR = 0x01, // Default +} + +enum DlTdoaAnchorCfo : 8 { + ANCHOR_CFO_NOT_INCLUDED = 0x00, + ANCHOR_CFO_INCLUDED = 0x01, // Default +} + +enum SessionDataTransferStatusNtfConfig : 8 { + DISABLE = 0x00, // Default + ENABLE = 0x01, } enum CapTlvType : 8 { @@ -706,7 +878,7 @@ test SetConfigRsp { packet GetConfigCmd : CoreCommand (opcode = 0x5) { //CORE_GET_CONFIG _count_(cfg_id): 8, - cfg_id: 8[], // DeviceConfigId (Infra does not allow array of enums) + cfg_id: 8[], // DeviceConfigId } test GetConfigCmd { @@ -839,7 +1011,7 @@ test SessionSetAppConfigRsp { packet SessionGetAppConfigCmd : SessionConfigCommand (opcode = 0x4) { //SESSION_GET_APP_CONFIG session_token: 32, // Session ID or Session Handle (based on UWBS version) _count_(app_cfg): 8, - app_cfg: 8[], // AppConfigTlvType (Infra does not allow array of enums) + app_cfg: AppConfigTlvType[], } test SessionGetAppConfigCmd { @@ -926,11 +1098,12 @@ struct Controlee_V2_0_32_Byte_Version { subsession_key: 8[32], } +/// cf. [UCI] 7.7 enum UpdateMulticastListAction: 8 { ADD_CONTROLEE = 0x00, REMOVE_CONTROLEE = 0x01, ADD_CONTROLEE_WITH_SHORT_SUB_SESSION_KEY = 0x02, - ADD_CONTROLEE_WITH_LONG_SUB_SESSION_KEY = 0x03, + ADD_CONTROLEE_WITH_EXTENDED_SUB_SESSION_KEY = 0x03, } packet SessionUpdateControllerMulticastListCmd : SessionConfigCommand (opcode = 0x7) { //SESSION_UPDATE_CONTROLLER_MULTICAST_LIST @@ -1262,6 +1435,12 @@ test AndroidSetCountryCodeRsp { "\x4c\x01\x00\x01\x00\x00\x00\x00", } +enum FrameReportTlvType : 8 { + RSSI = 0x0, + AOA = 0x1, + CIR = 0x2, +} + struct FrameReportTlv { t: FrameReportTlvType, _size_(v): 16, diff --git a/tests/data_transfer.py b/tests/data_transfer.py index 241a6b9..1ef3e23 100755 --- a/tests/data_transfer.py +++ b/tests/data_transfer.py @@ -19,7 +19,7 @@ import logging from pica import Host from pica.packets import uci -from .helper import init +from helper import init from pathlib import Path MAX_DATA_PACKET_PAYLOAD_SIZE = 1024 @@ -45,32 +45,45 @@ async def controller(host: Host, peer: Host, file: Path): ) ) - mac_address_mode = 0x0 + ranging_round_usage = 0x06 ranging_duration = int(1000).to_bytes(4, byteorder="little") - device_role_initiator = bytes([0]) - device_type_controller = bytes([1]) + host.send_control( uci.SessionSetAppConfigCmd( session_token=0, tlvs=[ uci.AppConfigTlv( - cfg_id=uci.AppConfigTlvType.DEVICE_ROLE, v=device_role_initiator + cfg_id=uci.AppConfigTlvType.DEVICE_ROLE, + v=bytes([uci.DeviceRole.INITIATOR]), ), uci.AppConfigTlv( - cfg_id=uci.AppConfigTlvType.DEVICE_TYPE, v=device_type_controller + cfg_id=uci.AppConfigTlvType.DEVICE_TYPE, + v=bytes([uci.DeviceType.CONTROLLER]), ), uci.AppConfigTlv( cfg_id=uci.AppConfigTlvType.DEVICE_MAC_ADDRESS, v=host.mac_address ), uci.AppConfigTlv( cfg_id=uci.AppConfigTlvType.MAC_ADDRESS_MODE, - v=bytes([mac_address_mode]), + v=bytes([uci.MacAddressMode.MODE_0]), + ), + uci.AppConfigTlv( + cfg_id=uci.AppConfigTlvType.MULTI_NODE_MODE, + v=bytes([uci.MultiNodeMode.ONE_TO_ONE]), + ), + uci.AppConfigTlv( + cfg_id=uci.AppConfigTlvType.SCHEDULE_MODE, + v=bytes([uci.ScheduleMode.CONTENTION_BASED]), + ), + uci.AppConfigTlv( + cfg_id=uci.AppConfigTlvType.RANGING_ROUND_USAGE, + v=bytes([ranging_round_usage]), ), uci.AppConfigTlv( cfg_id=uci.AppConfigTlvType.RANGING_DURATION, v=ranging_duration ), uci.AppConfigTlv( - cfg_id=uci.AppConfigTlvType.NO_OF_CONTROLEE, v=bytes([1]) + cfg_id=uci.AppConfigTlvType.NUMBER_OF_CONTROLEES, v=bytes([1]) ), uci.AppConfigTlv( cfg_id=uci.AppConfigTlvType.DST_MAC_ADDRESS, v=peer.mac_address @@ -159,32 +172,45 @@ async def controlee(host: Host, peer: Host, file: Path): ) ) - mac_address_mode = 0x0 + ranging_round_usage = 0x06 ranging_duration = int(1000).to_bytes(4, byteorder="little") - device_role_responder = bytes([1]) - device_type_controllee = bytes([0]) + host.send_control( uci.SessionSetAppConfigCmd( session_token=0, tlvs=[ uci.AppConfigTlv( - cfg_id=uci.AppConfigTlvType.DEVICE_ROLE, v=device_role_responder + cfg_id=uci.AppConfigTlvType.DEVICE_ROLE, + v=bytes([uci.DeviceRole.RESPONDER]), ), uci.AppConfigTlv( - cfg_id=uci.AppConfigTlvType.DEVICE_TYPE, v=device_type_controllee + cfg_id=uci.AppConfigTlvType.DEVICE_TYPE, + v=bytes([uci.DeviceType.CONTROLEE]), ), uci.AppConfigTlv( cfg_id=uci.AppConfigTlvType.DEVICE_MAC_ADDRESS, v=host.mac_address ), uci.AppConfigTlv( cfg_id=uci.AppConfigTlvType.MAC_ADDRESS_MODE, - v=bytes([mac_address_mode]), + v=bytes([uci.MacAddressMode.MODE_0]), + ), + uci.AppConfigTlv( + cfg_id=uci.AppConfigTlvType.MULTI_NODE_MODE, + v=bytes([uci.MultiNodeMode.ONE_TO_ONE]), + ), + uci.AppConfigTlv( + cfg_id=uci.AppConfigTlvType.SCHEDULE_MODE, + v=bytes([uci.ScheduleMode.CONTENTION_BASED]), + ), + uci.AppConfigTlv( + cfg_id=uci.AppConfigTlvType.RANGING_ROUND_USAGE, + v=bytes([ranging_round_usage]), ), uci.AppConfigTlv( cfg_id=uci.AppConfigTlvType.RANGING_DURATION, v=ranging_duration ), uci.AppConfigTlv( - cfg_id=uci.AppConfigTlvType.NO_OF_CONTROLEE, v=bytes([1]) + cfg_id=uci.AppConfigTlvType.NUMBER_OF_CONTROLEES, v=bytes([1]) ), uci.AppConfigTlv( cfg_id=uci.AppConfigTlvType.DST_MAC_ADDRESS, v=peer.mac_address diff --git a/tests/ranging.py b/tests/ranging.py index 7af8224..9d6477b 100755 --- a/tests/ranging.py +++ b/tests/ranging.py @@ -20,7 +20,7 @@ from pica import Host from pica.packets import uci -from .helper import init +from helper import init async def controller(host: Host, peer: Host): @@ -42,32 +42,45 @@ async def controller(host: Host, peer: Host): ) ) - mac_address_mode = 0x0 + ranging_round_usage = 0x06 ranging_duration = int(1000).to_bytes(4, byteorder="little") - device_role_initiator = bytes([0]) - device_type_controller = bytes([1]) + host.send_control( uci.SessionSetAppConfigCmd( session_token=0, tlvs=[ uci.AppConfigTlv( - cfg_id=uci.AppConfigTlvType.DEVICE_ROLE, v=device_role_initiator + cfg_id=uci.AppConfigTlvType.DEVICE_ROLE, + v=bytes([uci.DeviceRole.INITIATOR]), ), uci.AppConfigTlv( - cfg_id=uci.AppConfigTlvType.DEVICE_TYPE, v=device_type_controller + cfg_id=uci.AppConfigTlvType.DEVICE_TYPE, + v=bytes([uci.DeviceType.CONTROLLER]), ), uci.AppConfigTlv( cfg_id=uci.AppConfigTlvType.DEVICE_MAC_ADDRESS, v=host.mac_address ), uci.AppConfigTlv( cfg_id=uci.AppConfigTlvType.MAC_ADDRESS_MODE, - v=bytes([mac_address_mode]), + v=bytes([uci.MacAddressMode.MODE_0]), + ), + uci.AppConfigTlv( + cfg_id=uci.AppConfigTlvType.MULTI_NODE_MODE, + v=bytes([uci.MultiNodeMode.ONE_TO_ONE]), + ), + uci.AppConfigTlv( + cfg_id=uci.AppConfigTlvType.SCHEDULE_MODE, + v=bytes([uci.ScheduleMode.CONTENTION_BASED]), + ), + uci.AppConfigTlv( + cfg_id=uci.AppConfigTlvType.RANGING_ROUND_USAGE, + v=bytes([ranging_round_usage]), ), uci.AppConfigTlv( cfg_id=uci.AppConfigTlvType.RANGING_DURATION, v=ranging_duration ), uci.AppConfigTlv( - cfg_id=uci.AppConfigTlvType.NO_OF_CONTROLEE, v=bytes([1]) + cfg_id=uci.AppConfigTlvType.NUMBER_OF_CONTROLEES, v=bytes([1]) ), uci.AppConfigTlv( cfg_id=uci.AppConfigTlvType.DST_MAC_ADDRESS, v=peer.mac_address @@ -148,32 +161,45 @@ async def controlee(host: Host, peer: Host): ) ) - mac_address_mode = 0x0 + ranging_round_usage = 0x06 ranging_duration = int(1000).to_bytes(4, byteorder="little") - device_role_responder = bytes([1]) - device_type_controlee = bytes([0]) + host.send_control( uci.SessionSetAppConfigCmd( session_token=0, tlvs=[ uci.AppConfigTlv( - cfg_id=uci.AppConfigTlvType.DEVICE_ROLE, v=device_role_responder + cfg_id=uci.AppConfigTlvType.DEVICE_ROLE, + v=bytes([uci.DeviceRole.RESPONDER]), ), uci.AppConfigTlv( - cfg_id=uci.AppConfigTlvType.DEVICE_TYPE, v=device_type_controlee + cfg_id=uci.AppConfigTlvType.DEVICE_TYPE, + v=bytes([uci.DeviceType.CONTROLEE]), ), uci.AppConfigTlv( cfg_id=uci.AppConfigTlvType.DEVICE_MAC_ADDRESS, v=host.mac_address ), uci.AppConfigTlv( cfg_id=uci.AppConfigTlvType.MAC_ADDRESS_MODE, - v=bytes([mac_address_mode]), + v=bytes([uci.MacAddressMode.MODE_0]), + ), + uci.AppConfigTlv( + cfg_id=uci.AppConfigTlvType.MULTI_NODE_MODE, + v=bytes([uci.MultiNodeMode.ONE_TO_ONE]), + ), + uci.AppConfigTlv( + cfg_id=uci.AppConfigTlvType.SCHEDULE_MODE, + v=bytes([uci.ScheduleMode.CONTENTION_BASED]), + ), + uci.AppConfigTlv( + cfg_id=uci.AppConfigTlvType.RANGING_ROUND_USAGE, + v=bytes([ranging_round_usage]), ), uci.AppConfigTlv( cfg_id=uci.AppConfigTlvType.RANGING_DURATION, v=ranging_duration ), uci.AppConfigTlv( - cfg_id=uci.AppConfigTlvType.NO_OF_CONTROLEE, v=bytes([1]) + cfg_id=uci.AppConfigTlvType.NUMBER_OF_CONTROLEES, v=bytes([1]) ), uci.AppConfigTlv( cfg_id=uci.AppConfigTlvType.DST_MAC_ADDRESS, v=peer.mac_address