diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-04-09 23:13:57 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-04-09 23:13:57 +0000 |
commit | 17299d80b68284b4909142eee704063dcab84b5a (patch) | |
tree | 948bad0adf585b1939c9c8612add3921c708fe9b | |
parent | 8c6c855de61adb34326e9f58626b1a380c23f937 (diff) | |
parent | 2fa040978d9004a07be97597cf4b3a758f1b7b6b (diff) | |
download | pica-sdk-release.tar.gz |
Snap for 11692525 from 2fa040978d9004a07be97597cf4b3a758f1b7b6b to sdk-releasesdk-release
Change-Id: I18e86b97a5f59a28d611ad99bbd78d32c88684c6
-rw-r--r-- | Cargo.lock | 2 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | METADATA | 4 | ||||
-rw-r--r-- | py/pica/pica/packets/uci.py | 3127 | ||||
-rw-r--r-- | src/app_config.rs | 517 | ||||
-rw-r--r-- | src/device.rs | 975 | ||||
-rw-r--r-- | src/lib.rs | 24 | ||||
-rw-r--r-- | src/mac_address.rs | 25 | ||||
-rw-r--r-- | src/session.rs | 1112 | ||||
-rw-r--r-- | src/uci_packets.pdl | 904 | ||||
-rwxr-xr-x | tests/data_transfer.py | 94 | ||||
-rw-r--r-- | tests/helper.py | 8 | ||||
-rwxr-xr-x | tests/ranging.py | 82 |
13 files changed, 3064 insertions, 3812 deletions
@@ -648,7 +648,7 @@ dependencies = [ [[package]] name = "pica" -version = "0.1.7" +version = "0.1.9" dependencies = [ "anyhow", "bytes", @@ -1,6 +1,6 @@ [package] name = "pica" -version = "0.1.8" +version = "0.1.9" edition = "2021" description = "Pica is a virtual UWB Controller implementing the FiRa UCI specification." repository = "https://github.com/google/pica" @@ -12,7 +12,7 @@ third_party { type: GIT value: "https://github.com/google/pica.git" } - version: "v0.1.8" + version: "v0.1.9" license_type: NOTICE - last_upgrade_date { year: 2024 month: 3 day: 13 } + last_upgrade_date { year: 2024 month: 4 day: 5 } } diff --git a/py/pica/pica/packets/uci.py b/py/pica/pica/packets/uci.py index 32ffbdc..574feab 100644 --- a/py/pica/pica/packets/uci.py +++ b/py/pica/pica/packets/uci.py @@ -94,16 +94,30 @@ class PacketBoundaryFlag(enum.IntEnum): raise exn +class MessageType(enum.IntEnum): + DATA = 0x0 + COMMAND = 0x1 + RESPONSE = 0x2 + NOTIFICATION = 0x3 + + @staticmethod + def from_int(v: int) -> Union[int, 'MessageType']: + try: + return MessageType(v) + except ValueError as exn: + raise exn + + class GroupId(enum.IntEnum): CORE = 0x0 SESSION_CONFIG = 0x1 SESSION_CONTROL = 0x2 DATA_CONTROL = 0x3 - TEST = 0xd VENDOR_RESERVED_9 = 0x9 VENDOR_RESERVED_A = 0xa VENDOR_RESERVED_B = 0xb VENDOR_ANDROID = 0xc + TEST = 0xd VENDOR_RESERVED_E = 0xe VENDOR_RESERVED_F = 0xf @@ -127,154 +141,112 @@ class DataPacketFormat(enum.IntEnum): raise exn -class GroupIdOrDataPacketFormat(enum.IntEnum): - CORE = 0x0 - SESSION_CONFIG_OR_DATA_SND = 0x1 - SESSION_CONTROL_OR_DATA_RCV = 0x2 - DATA_CONTROL = 0x3 - TEST = 0xd - VENDOR_RESERVED_9 = 0x9 - VENDOR_RESERVED_A = 0xa - VENDOR_RESERVED_B = 0xb - VENDOR_ANDROID = 0xc - VENDOR_RESERVED_E = 0xe - VENDOR_RESERVED_F = 0xf +class CoreOpcodeId(enum.IntEnum): + DEVICE_RESET = 0x0 + DEVICE_STATUS = 0x1 + GET_DEVICE_INFO = 0x2 + GET_CAPS_INFO = 0x3 + SET_CONFIG = 0x4 + GET_CONFIG = 0x5 + GENERIC_ERROR = 0x7 + QUERY_UWBS_TIMESTAMP = 0x8 @staticmethod - def from_int(v: int) -> Union[int, 'GroupIdOrDataPacketFormat']: + def from_int(v: int) -> Union[int, 'CoreOpcodeId']: try: - return GroupIdOrDataPacketFormat(v) + return CoreOpcodeId(v) except ValueError as exn: raise exn -class CoreOpCode(enum.IntEnum): - CORE_DEVICE_RESET = 0x0 - CORE_DEVICE_STATUS_NTF = 0x1 - CORE_DEVICE_INFO = 0x2 - CORE_GET_CAPS_INFO = 0x3 - CORE_SET_CONFIG = 0x4 - CORE_GET_CONFIG = 0x5 - CORE_DEVICE_SUSPEND = 0x6 - CORE_GENERIC_ERROR_NTF = 0x7 - CORE_QUERY_UWBS_TIMESTAMP = 0x8 +class SessionConfigOpcodeId(enum.IntEnum): + INIT = 0x0 + DEINIT = 0x1 + STATUS = 0x2 + SET_APP_CONFIG = 0x3 + GET_APP_CONFIG = 0x4 + GET_COUNT = 0x5 + GET_STATE = 0x6 + UPDATE_CONTROLLER_MULTICAST_LIST = 0x7 + UPDATE_DT_ANCHOR_RANGING_ROUNDS = 0x8 + UPDATE_DT_TAG_RANGING_ROUNDS = 0x9 + QUERY_DATA_SIZE_IN_RANGING = 0xb @staticmethod - def from_int(v: int) -> Union[int, 'CoreOpCode']: + def from_int(v: int) -> Union[int, 'SessionConfigOpcodeId']: try: - return CoreOpCode(v) + return SessionConfigOpcodeId(v) except ValueError as exn: raise exn -class SessionConfigOpCode(enum.IntEnum): - SESSION_INIT = 0x0 - SESSION_DEINIT = 0x1 - SESSION_STATUS_NTF = 0x2 - SESSION_SET_APP_CONFIG = 0x3 - SESSION_GET_APP_CONFIG = 0x4 - SESSION_GET_COUNT = 0x5 - SESSION_GET_STATE = 0x6 - SESSION_UPDATE_CONTROLLER_MULTICAST_LIST = 0x7 - SESSION_UPDATE_ACTIVE_ROUNDS_ANCHOR = 0x8 - SESSION_UPDATE_ACTIVE_ROUNDS_DT_TAG = 0x9 - SESSION_SET_INITIATOR_DT_ANCHOR_RR_RDM_LIST = 0xa - SESSION_QUERY_DATA_SIZE_IN_RANGING = 0xb - SESSION_SET_HUS_CONFIG = 0xc +class SessionControlOpcodeId(enum.IntEnum): + START = 0x0 + STOP = 0x1 + GET_RANGING_COUNT = 0x3 + DATA_CREDIT = 0x4 + DATA_TRANSFER_STATUS = 0x5 @staticmethod - def from_int(v: int) -> Union[int, 'SessionConfigOpCode']: + def from_int(v: int) -> Union[int, 'SessionControlOpcodeId']: try: - return SessionConfigOpCode(v) + return SessionControlOpcodeId(v) except ValueError as exn: raise exn -class SessionControlOpCode(enum.IntEnum): - SESSION_START = 0x0 - SESSION_STOP = 0x1 - SESSION_RESERVED = 0x2 - SESSION_GET_RANGING_COUNT = 0x3 - SESSION_DATA_CREDIT_NTF = 0x4 - SESSION_DATA_TRANSFER_STATUS_NTF = 0x5 +class AndroidOpcodeId(enum.IntEnum): + GET_POWER_STATS = 0x0 + SET_COUNTRY_CODE = 0x1 + FIRA_RANGE_DIAGNOSTICS = 0x2 @staticmethod - def from_int(v: int) -> Union[int, 'SessionControlOpCode']: + def from_int(v: int) -> Union[int, 'AndroidOpcodeId']: try: - return SessionControlOpCode(v) + return AndroidOpcodeId(v) except ValueError as exn: raise exn -class AppDataOpCode(enum.IntEnum): - APP_DATA_TX = 0x0 - APP_DATA_RX = 0x1 - - @staticmethod - def from_int(v: int) -> Union[int, 'AppDataOpCode']: - try: - return AppDataOpCode(v) - except ValueError as exn: - raise exn - - -class AndroidOpCode(enum.IntEnum): - ANDROID_GET_POWER_STATS = 0x0 - ANDROID_SET_COUNTRY_CODE = 0x1 - ANDROID_FIRA_RANGE_DIAGNOSTICS = 0x2 - - @staticmethod - def from_int(v: int) -> Union[int, 'AndroidOpCode']: - try: - return AndroidOpCode(v) - except ValueError as exn: - raise exn - - -class StatusCode(enum.IntEnum): - UCI_STATUS_OK = 0x0 - UCI_STATUS_REJECTED = 0x1 - UCI_STATUS_FAILED = 0x2 - UCI_STATUS_SYNTAX_ERROR = 0x3 - UCI_STATUS_INVALID_PARAM = 0x4 - UCI_STATUS_INVALID_RANGE = 0x5 - UCI_STATUS_INVALID_MSG_SIZE = 0x6 - UCI_STATUS_UNKNOWN_GID = 0x7 - UCI_STATUS_UNKNOWN_OID = 0x8 - UCI_STATUS_READ_ONLY = 0x9 - UCI_STATUS_COMMAND_RETRY = 0xa - UCI_STATUS_UNKNOWN = 0xb - UCI_STATUS_NOT_APPLICABLE = 0xc - UCI_STATUS_SESSION_NOT_EXIST = 0x11 - UCI_STATUS_SESSION_DUPLICATE = 0x12 - UCI_STATUS_SESSION_ACTIVE = 0x13 - UCI_STATUS_MAX_SESSIONS_EXCEEDED = 0x14 - UCI_STATUS_SESSION_NOT_CONFIGURED = 0x15 - UCI_STATUS_ACTIVE_SESSIONS_ONGOING = 0x16 - UCI_STATUS_MULTICAST_LIST_FULL = 0x17 - UCI_STATUS_ADDRESS_NOT_FOUND = 0x18 - UCI_STATUS_ADDRESS_ALREADY_PRESENT = 0x19 - UCI_STATUS_ERROR_UWB_INITIATION_TIME_TOO_OLD = 0x1a - UCI_STATUS_OK_NEGATIVE_DISTANCE_REPORT = 0x1b - UCI_STATUS_RANGING_TX_FAILED = 0x20 - UCI_STATUS_RANGING_RX_TIMEOUT = 0x21 - UCI_STATUS_RANGING_RX_PHY_DEC_FAILED = 0x22 - UCI_STATUS_RANGING_RX_PHY_TOA_FAILED = 0x23 - UCI_STATUS_RANGING_RX_PHY_STS_FAILED = 0x24 - UCI_STATUS_RANGING_RX_MAC_DEC_FAILED = 0x25 - UCI_STATUS_RANGING_RX_MAC_IE_DEC_FAILED = 0x26 - UCI_STATUS_RANGING_RX_MAC_IE_MISSING = 0x27 - UCI_STATUS_ERROR_ROUND_INDEX_NOT_ACTIVATED = 0x28 - UCI_STATUS_ERROR_NUMBER_OF_ACTIVE_RANGING_ROUNDS_EXCEEDED = 0x29 - UCI_STATUS_ERROR_DL_TDOA_DEVICE_ADDRESS_NOT_MATCHING_IN_REPLY_TIME_LIST = 0x2a - UCI_STATUS_DATA_MAX_TX_PSDU_SIZE_EXCEEDED = 0x30 - UCI_STATUS_DATA_RX_CRC_ERROR = 0x31 - VENDOR_SPECIFIC_STATUS_CODE_2 = 0xff - - @staticmethod - def from_int(v: int) -> Union[int, 'StatusCode']: - try: - return StatusCode(v) +class Status(enum.IntEnum): + OK = 0x0 + REJECTED = 0x1 + FAILED = 0x2 + SYNTAX_ERROR = 0x3 + INVALID_PARAM = 0x4 + INVALID_RANGE = 0x5 + INVALID_MESSAGE_SIZE = 0x6 + UNKNOWN_GID = 0x7 + UNKNOWN_OID = 0x8 + READ_ONLY = 0x9 + UCI_MESSAGE_RETRY = 0xa + UNKNOWN = 0xb + NOT_APPLICABLE = 0xc + ERROR_SESSION_NOT_EXIST = 0x11 + ERROR_SESSION_DUPLICATE = 0x12 + ERROR_SESSION_ACTIVE = 0x13 + ERROR_MAX_SESSIONS_EXCEEDED = 0x14 + ERROR_SESSION_NOT_CONFIGURED = 0x15 + ERROR_ACTIVE_SESSIONS_ONGOING = 0x16 + ERROR_MULTICAST_LIST_FULL = 0x17 + ERROR_UWB_INITIATION_TIME_TOO_OLD = 0x1a + OK_NEGATIVE_DISTANCE_REPORT = 0x1b + RANGING_TX_FAILED = 0x20 + RANGING_RX_TIMEOUT = 0x21 + RANGING_RX_PHY_DEC_FAILED = 0x22 + RANGING_RX_PHY_TOA_FAILED = 0x23 + RANGING_RX_PHY_STS_FAILED = 0x24 + RANGING_RX_MAC_DEC_FAILED = 0x25 + RANGING_RX_MAC_IE_DEC_FAILED = 0x26 + RANGING_RX_MAC_IE_MISSING = 0x27 + ERROR_ROUND_INDEX_NOT_ACTIVATED = 0x28 + ERROR_NUMBER_OF_ACTIVE_RANGING_ROUNDS_EXCEEDED = 0x29 + ERROR_DL_TDOA_DEVICE_ADDRESS_NOT_MATCHING_IN_REPLY_TIME_LIST = 0x2a + + @staticmethod + def from_int(v: int) -> Union[int, 'Status']: + try: + return Status(v) except ValueError as exn: return v @@ -333,25 +305,13 @@ class ResetConfig(enum.IntEnum): raise exn -class DeviceConfigId(enum.IntEnum): - DEVICE_STATE = 0x0 - LOW_POWER_MODE = 0x1 - - @staticmethod - def from_int(v: int) -> Union[int, 'DeviceConfigId']: - try: - return DeviceConfigId(v) - except ValueError as exn: - raise exn - - class AppConfigTlvType(enum.IntEnum): DEVICE_TYPE = 0x0 RANGING_ROUND_USAGE = 0x1 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 +320,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 +334,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 +355,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 +365,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 +385,434 @@ class AppConfigTlvType(enum.IntEnum): 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 @@ -571,20 +942,21 @@ class ReasonCode(enum.IntEnum): return v -class MulticastUpdateStatusCode(enum.IntEnum): - STATUS_OK_MULTICAST_LIST_UPDATE = 0x0 - STATUS_ERROR_MULTICAST_LIST_FULL = 0x1 - STATUS_ERROR_KEY_FETCH_FAIL = 0x2 - STATUS_ERROR_SUB_SESSION_ID_NOT_FOUND = 0x3 - STATUS_ERROR_SUB_SESSION_KEY_NOT_FOUND = 0x5 - STATUS_ERROR_SUB_SESSION_KEY_NOT_APPLICABLE = 0x6 - STATUS_ERROR_SESSION_KEY_NOT_FOUND = 0x7 - STATUS_ERROR_ADDRESS_ALREADY_PRESENT = 0x8 +class MulticastUpdateStatus(enum.IntEnum): + OK_MULTICAST_LIST_UPDATE = 0x0 + ERROR_MULTICAST_LIST_FULL = 0x1 + ERROR_KEY_FETCH_FAIL = 0x2 + ERROR_SUB_SESSION_ID_NOT_FOUND = 0x3 + ERROR_SUB_SESSION_KEY_NOT_FOUND = 0x4 + ERROR_SUB_SESSION_KEY_NOT_APPLICABLE = 0x5 + ERROR_SESSION_KEY_NOT_FOUND = 0x6 + ERROR_ADDRESS_NOT_FOUND = 0x7 + ERROR_ADDRESS_ALREADY_PRESENT = 0x8 @staticmethod - def from_int(v: int) -> Union[int, 'MulticastUpdateStatusCode']: + def from_int(v: int) -> Union[int, 'MulticastUpdateStatus']: try: - return MulticastUpdateStatusCode(v) + return MulticastUpdateStatus(v) except ValueError as exn: raise exn @@ -619,22 +991,6 @@ class SessionType(enum.IntEnum): raise exn -class MessageType(enum.IntEnum): - DATA = 0x0 - COMMAND = 0x1 - RESPONSE = 0x2 - NOTIFICATION = 0x3 - RESERVED_FOR_TESTING_1 = 0x4 - RESERVED_FOR_TESTING_2 = 0x5 - - @staticmethod - def from_int(v: int) -> Union[int, 'MessageType']: - try: - return MessageType(v) - except ValueError as exn: - raise exn - - @dataclass class CommonPacketHeader(Packet): pbf: PacketBoundaryFlag = field(kw_only=True, default=PacketBoundaryFlag.COMPLETE) @@ -748,7 +1104,6 @@ class DataPacketHeader(Packet): class ControlPacket(Packet): gid: GroupId = field(kw_only=True, default=GroupId.CORE) mt: MessageType = field(kw_only=True, default=MessageType.DATA) - opcode: int = field(kw_only=True, default=0) def __post_init__(self): pass @@ -756,210 +1111,28 @@ class ControlPacket(Packet): @staticmethod def parse(span: bytes) -> Tuple['ControlPacket', bytes]: fields = {'payload': None} - if len(span) < 4: + if len(span) < 1: raise Exception('Invalid packet size') fields['gid'] = GroupId.from_int((span[0] >> 0) & 0xf) fields['mt'] = MessageType.from_int((span[0] >> 5) & 0x7) - fields['opcode'] = (span[1] >> 0) & 0x3f - value_ = int.from_bytes(span[2:4], byteorder='little') - span = span[4:] + span = span[1:] payload = span span = bytes([]) fields['payload'] = payload try: - return DeviceResetCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GetDeviceInfoCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GetCapsInfoCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SetConfigCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GetConfigCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return CoreQueryTimeStampCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionInitCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionDeinitCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionSetAppConfigCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionGetAppConfigCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionGetCountCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionGetStateCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionUpdateDtTagRangingRoundsCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionUpdateControllerMulticastListCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionSetHybridConfigCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionQueryMaxDataSizeCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionControlCommand.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return AndroidGetPowerStatsCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return AndroidSetCountryCodeCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return DeviceResetRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GetDeviceInfoRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GetCapsInfoRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SetConfigRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GetConfigRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return CoreQueryTimeStampRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionInitRsp_V2.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionInitRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionDeinitRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionSetAppConfigRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionGetAppConfigRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionGetCountRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionGetStateRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionUpdateDtTagRangingRoundsRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionSetHybridConfigRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionUpdateControllerMulticastListRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionQueryMaxDataSizeRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionStartRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionStopRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionGetRangingCountRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return AndroidGetPowerStatsRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return AndroidSetCountryCodeRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return DeviceStatusNtf.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GenericError.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionStatusNtf.parse(fields.copy(), payload) + return CorePacket.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionUpdateControllerMulticastListNtf.parse(fields.copy(), payload) + return SessionConfigPacket.parse(fields.copy(), payload) except Exception as exn: pass try: - return DataCreditNtf.parse(fields.copy(), payload) + return SessionControlPacket.parse(fields.copy(), payload) except Exception as exn: pass try: - return DataTransferStatusNtf.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionInfoNtf.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return AndroidRangeDiagnosticsNtf.parse(fields.copy(), payload) + return AndroidPacket.parse(fields.copy(), payload) except Exception as exn: pass return ControlPacket(**fields), span @@ -971,17 +1144,12 @@ class ControlPacket(Packet): (self.mt << 5) ) _span.append(_value) - if self.opcode > 63: - print(f"Invalid value for field ControlPacket::opcode: {self.opcode} > 63; the value will be truncated") - self.opcode &= 63 - _span.append((self.opcode << 0)) - _span.extend([0] * 2) _span.extend(payload or self.payload or []) return bytes(_span) @property def size(self) -> int: - return len(self.payload) + 4 + return len(self.payload) + 1 @dataclass class DataPacket(Packet): @@ -1089,7 +1257,7 @@ class DataMessageSnd(DataPacket): @dataclass class DataMessageRcv(DataPacket): session_handle: int = field(kw_only=True, default=0) - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) + status: Status = field(kw_only=True, default=Status.OK) source_address: int = field(kw_only=True, default=0) data_sequence_number: int = field(kw_only=True, default=0) application_data: bytearray = field(kw_only=True, default_factory=bytearray) @@ -1106,7 +1274,7 @@ class DataMessageRcv(DataPacket): raise Exception('Invalid packet size') value_ = int.from_bytes(span[0:4], byteorder='little') fields['session_handle'] = value_ - fields['status'] = StatusCode.from_int(span[4]) + fields['status'] = Status.from_int(span[4]) value_ = int.from_bytes(span[5:13], byteorder='little') fields['source_address'] = value_ value_ = int.from_bytes(span[13:15], byteorder='little') @@ -1144,142 +1312,114 @@ class DataMessageRcv(DataPacket): return len(self.application_data) * 1 + 17 @dataclass -class UciCommand(ControlPacket): - +class CorePacket(ControlPacket): + oid: CoreOpcodeId = field(kw_only=True, default=CoreOpcodeId.DEVICE_RESET) def __post_init__(self): - self.mt = MessageType.COMMAND + self.gid = GroupId.CORE @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciCommand', bytes]: - if fields['mt'] != MessageType.COMMAND: + def parse(fields: dict, span: bytes) -> Tuple['CorePacket', bytes]: + if fields['gid'] != GroupId.CORE: raise Exception("Invalid constraint field values") + if len(span) < 3: + raise Exception('Invalid packet size') + fields['oid'] = CoreOpcodeId.from_int((span[0] >> 0) & 0x3f) + value_ = int.from_bytes(span[1:3], byteorder='little') + span = span[3:] payload = span span = bytes([]) fields['payload'] = payload try: - return DeviceResetCmd.parse(fields.copy(), payload) + return CoreDeviceResetCmd.parse(fields.copy(), payload) except Exception as exn: pass try: - return GetDeviceInfoCmd.parse(fields.copy(), payload) + return CoreDeviceResetRsp.parse(fields.copy(), payload) except Exception as exn: pass try: - return GetCapsInfoCmd.parse(fields.copy(), payload) + return CoreDeviceStatusNtf.parse(fields.copy(), payload) except Exception as exn: pass try: - return SetConfigCmd.parse(fields.copy(), payload) + return CoreGetDeviceInfoCmd.parse(fields.copy(), payload) except Exception as exn: pass try: - return GetConfigCmd.parse(fields.copy(), payload) + return CoreGetDeviceInfoRsp.parse(fields.copy(), payload) except Exception as exn: pass try: - return CoreQueryTimeStampCmd.parse(fields.copy(), payload) + return CoreGetCapsInfoCmd.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionInitCmd.parse(fields.copy(), payload) + return CoreGetCapsInfoRsp.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionDeinitCmd.parse(fields.copy(), payload) + return CoreSetConfigCmd.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionSetAppConfigCmd.parse(fields.copy(), payload) + return CoreSetConfigRsp.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionGetAppConfigCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionGetCountCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionGetStateCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionUpdateDtTagRangingRoundsCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionUpdateControllerMulticastListCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionSetHybridConfigCmd.parse(fields.copy(), payload) + return CoreGetConfigCmd.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionQueryMaxDataSizeCmd.parse(fields.copy(), payload) + return CoreGetConfigRsp.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionControlCommand.parse(fields.copy(), payload) + return CoreGenericErrorNtf.parse(fields.copy(), payload) except Exception as exn: pass try: - return AndroidGetPowerStatsCmd.parse(fields.copy(), payload) + return CoreQueryTimeStampCmd.parse(fields.copy(), payload) except Exception as exn: pass try: - return AndroidSetCountryCodeCmd.parse(fields.copy(), payload) + return CoreQueryTimeStampRsp.parse(fields.copy(), payload) except Exception as exn: pass - return UciCommand(**fields), span + return CorePacket(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() + _span.append((self.oid << 0)) + _span.extend([0] * 2) _span.extend(payload or self.payload or []) return ControlPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: - return len(self.payload) + return len(self.payload) + 3 @dataclass -class UciResponse(ControlPacket): - +class SessionConfigPacket(ControlPacket): + oid: SessionConfigOpcodeId = field(kw_only=True, default=SessionConfigOpcodeId.INIT) def __post_init__(self): - self.mt = MessageType.RESPONSE + self.gid = GroupId.SESSION_CONFIG @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciResponse', bytes]: - if fields['mt'] != MessageType.RESPONSE: + def parse(fields: dict, span: bytes) -> Tuple['SessionConfigPacket', bytes]: + if fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") + if len(span) < 3: + raise Exception('Invalid packet size') + fields['oid'] = SessionConfigOpcodeId.from_int((span[0] >> 0) & 0x3f) + value_ = int.from_bytes(span[1:3], byteorder='little') + span = span[3:] payload = span span = bytes([]) fields['payload'] = payload try: - return DeviceResetRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GetDeviceInfoRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GetCapsInfoRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SetConfigRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GetConfigRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return CoreQueryTimeStampRsp.parse(fields.copy(), payload) + return SessionInitCmd.parse(fields.copy(), payload) except Exception as exn: pass try: @@ -1291,92 +1431,11 @@ class UciResponse(ControlPacket): except Exception as exn: pass try: - return SessionDeinitRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionSetAppConfigRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionGetAppConfigRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionGetCountRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionGetStateRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionUpdateDtTagRangingRoundsRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionSetHybridConfigRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionUpdateControllerMulticastListRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionQueryMaxDataSizeRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionStartRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionStopRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionGetRangingCountRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return AndroidGetPowerStatsRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return AndroidSetCountryCodeRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - return UciResponse(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return ControlPacket.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class UciNotification(ControlPacket): - - - def __post_init__(self): - self.mt = MessageType.NOTIFICATION - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciNotification', bytes]: - if fields['mt'] != MessageType.NOTIFICATION: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - try: - return DeviceStatusNtf.parse(fields.copy(), payload) + return SessionDeinitCmd.parse(fields.copy(), payload) except Exception as exn: pass try: - return GenericError.parse(fields.copy(), payload) + return SessionDeinitRsp.parse(fields.copy(), payload) except Exception as exn: pass try: @@ -1384,277 +1443,47 @@ class UciNotification(ControlPacket): except Exception as exn: pass try: - return SessionUpdateControllerMulticastListNtf.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return DataCreditNtf.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return DataTransferStatusNtf.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionInfoNtf.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return AndroidRangeDiagnosticsNtf.parse(fields.copy(), payload) - except Exception as exn: - pass - return UciNotification(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return ControlPacket.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class CoreCommand(UciCommand): - - - def __post_init__(self): - self.gid = GroupId.CORE - self.mt = MessageType.COMMAND - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['CoreCommand', bytes]: - if fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.COMMAND: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - try: - return DeviceResetCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GetDeviceInfoCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GetCapsInfoCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SetConfigCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GetConfigCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return CoreQueryTimeStampCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - return CoreCommand(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciCommand.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class CoreResponse(UciResponse): - - - def __post_init__(self): - self.gid = GroupId.CORE - self.mt = MessageType.RESPONSE - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['CoreResponse', bytes]: - if fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.RESPONSE: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - try: - return DeviceResetRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GetDeviceInfoRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GetCapsInfoRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SetConfigRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GetConfigRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return CoreQueryTimeStampRsp.parse(fields.copy(), payload) - except Exception as exn: - pass - return CoreResponse(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciResponse.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class CoreNotification(UciNotification): - - - def __post_init__(self): - self.gid = GroupId.CORE - self.mt = MessageType.NOTIFICATION - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['CoreNotification', bytes]: - if fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.NOTIFICATION: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - try: - return DeviceStatusNtf.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return GenericError.parse(fields.copy(), payload) - except Exception as exn: - pass - return CoreNotification(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciNotification.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class SessionConfigCommand(UciCommand): - - - def __post_init__(self): - self.gid = GroupId.SESSION_CONFIG - self.mt = MessageType.COMMAND - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['SessionConfigCommand', bytes]: - if fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.COMMAND: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - try: - return SessionInitCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionDeinitCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: return SessionSetAppConfigCmd.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionGetAppConfigCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionGetCountCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionGetStateCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionUpdateDtTagRangingRoundsCmd.parse(fields.copy(), payload) - except Exception as exn: - pass - try: - return SessionUpdateControllerMulticastListCmd.parse(fields.copy(), payload) + return SessionSetAppConfigRsp.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionSetHybridConfigCmd.parse(fields.copy(), payload) + return SessionGetAppConfigCmd.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionQueryMaxDataSizeCmd.parse(fields.copy(), payload) + return SessionGetAppConfigRsp.parse(fields.copy(), payload) except Exception as exn: pass - return SessionConfigCommand(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciCommand.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class SessionConfigResponse(UciResponse): - - - def __post_init__(self): - self.gid = GroupId.SESSION_CONFIG - self.mt = MessageType.RESPONSE - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['SessionConfigResponse', bytes]: - if fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.RESPONSE: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload try: - return SessionInitRsp_V2.parse(fields.copy(), payload) + return SessionGetCountCmd.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionInitRsp.parse(fields.copy(), payload) + return SessionGetCountRsp.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionDeinitRsp.parse(fields.copy(), payload) + return SessionGetStateCmd.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionSetAppConfigRsp.parse(fields.copy(), payload) + return SessionGetStateRsp.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionGetAppConfigRsp.parse(fields.copy(), payload) + return SessionUpdateDtAnchorRangingRoundsCmd.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionGetCountRsp.parse(fields.copy(), payload) + return SessionUpdateDtAnchorRangingRoundsRsp.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionGetStateRsp.parse(fields.copy(), payload) + return SessionUpdateDtTagRangingRoundsCmd.parse(fields.copy(), payload) except Exception as exn: pass try: @@ -1662,7 +1491,7 @@ class SessionConfigResponse(UciResponse): except Exception as exn: pass try: - return SessionSetHybridConfigRsp.parse(fields.copy(), payload) + return SessionUpdateControllerMulticastListCmd.parse(fields.copy(), payload) except Exception as exn: pass try: @@ -1670,189 +1499,114 @@ class SessionConfigResponse(UciResponse): except Exception as exn: pass try: - return SessionQueryMaxDataSizeRsp.parse(fields.copy(), payload) + return SessionUpdateControllerMulticastListNtf.parse(fields.copy(), payload) except Exception as exn: pass - return SessionConfigResponse(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciResponse.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class SessionConfigNotification(UciNotification): - - - def __post_init__(self): - self.gid = GroupId.SESSION_CONFIG - self.mt = MessageType.NOTIFICATION - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['SessionConfigNotification', bytes]: - if fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.NOTIFICATION: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload try: - return SessionStatusNtf.parse(fields.copy(), payload) + return SessionQueryMaxDataSizeInRangingCmd.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionUpdateControllerMulticastListNtf.parse(fields.copy(), payload) + return SessionQueryMaxDataSizeInRangingRsp.parse(fields.copy(), payload) except Exception as exn: pass - return SessionConfigNotification(**fields), span + return SessionConfigPacket(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() + _span.append((self.oid << 0)) + _span.extend([0] * 2) _span.extend(payload or self.payload or []) - return UciNotification.serialize(self, payload = bytes(_span)) + return ControlPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: - return len(self.payload) + return len(self.payload) + 3 @dataclass -class SessionControlCommand(UciCommand): - session_id: int = field(kw_only=True, default=0) +class SessionControlPacket(ControlPacket): + oid: SessionControlOpcodeId = field(kw_only=True, default=SessionControlOpcodeId.START) def __post_init__(self): self.gid = GroupId.SESSION_CONTROL - self.mt = MessageType.COMMAND @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['SessionControlCommand', bytes]: - if fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.COMMAND: + def parse(fields: dict, span: bytes) -> Tuple['SessionControlPacket', bytes]: + if fields['gid'] != GroupId.SESSION_CONTROL: raise Exception("Invalid constraint field values") - if len(span) < 4: + if len(span) < 3: raise Exception('Invalid packet size') - value_ = int.from_bytes(span[0:4], byteorder='little') - fields['session_id'] = value_ - span = span[4:] + fields['oid'] = SessionControlOpcodeId.from_int((span[0] >> 0) & 0x3f) + value_ = int.from_bytes(span[1:3], byteorder='little') + span = span[3:] payload = span span = bytes([]) fields['payload'] = payload try: - return SessionStartCmd.parse(fields.copy(), payload) + return SessionDataCreditNtf.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionStopCmd.parse(fields.copy(), payload) + return SessionDataTransferStatusNtf.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionGetRangingCountCmd.parse(fields.copy(), payload) + return SessionStartCmd.parse(fields.copy(), payload) except Exception as exn: pass - return SessionControlCommand(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - if self.session_id > 4294967295: - print(f"Invalid value for field SessionControlCommand::session_id: {self.session_id} > 4294967295; the value will be truncated") - self.session_id &= 4294967295 - _span.extend(int.to_bytes((self.session_id << 0), length=4, byteorder='little')) - _span.extend(payload or self.payload or []) - return UciCommand.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) + 4 - -@dataclass -class SessionControlResponse(UciResponse): - - - def __post_init__(self): - self.gid = GroupId.SESSION_CONTROL - self.mt = MessageType.RESPONSE - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['SessionControlResponse', bytes]: - if fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.RESPONSE: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload try: return SessionStartRsp.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionStopRsp.parse(fields.copy(), payload) + return SessionInfoNtf.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionGetRangingCountRsp.parse(fields.copy(), payload) + return SessionStopCmd.parse(fields.copy(), payload) except Exception as exn: pass - return SessionControlResponse(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciResponse.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class SessionControlNotification(UciNotification): - - - def __post_init__(self): - self.gid = GroupId.SESSION_CONTROL - self.mt = MessageType.NOTIFICATION - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['SessionControlNotification', bytes]: - if fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.NOTIFICATION: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload try: - return DataCreditNtf.parse(fields.copy(), payload) + return SessionStopRsp.parse(fields.copy(), payload) except Exception as exn: pass try: - return DataTransferStatusNtf.parse(fields.copy(), payload) + return SessionGetRangingCountCmd.parse(fields.copy(), payload) except Exception as exn: pass try: - return SessionInfoNtf.parse(fields.copy(), payload) + return SessionGetRangingCountRsp.parse(fields.copy(), payload) except Exception as exn: pass - return SessionControlNotification(**fields), span + return SessionControlPacket(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() + _span.append((self.oid << 0)) + _span.extend([0] * 2) _span.extend(payload or self.payload or []) - return UciNotification.serialize(self, payload = bytes(_span)) + return ControlPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: - return len(self.payload) + return len(self.payload) + 3 @dataclass -class AndroidCommand(UciCommand): - +class AndroidPacket(ControlPacket): + oid: AndroidOpcodeId = field(kw_only=True, default=AndroidOpcodeId.GET_POWER_STATS) def __post_init__(self): self.gid = GroupId.VENDOR_ANDROID - self.mt = MessageType.COMMAND @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['AndroidCommand', bytes]: - if fields['gid'] != GroupId.VENDOR_ANDROID or fields['mt'] != MessageType.COMMAND: + def parse(fields: dict, span: bytes) -> Tuple['AndroidPacket', bytes]: + if fields['gid'] != GroupId.VENDOR_ANDROID: raise Exception("Invalid constraint field values") + if len(span) < 3: + raise Exception('Invalid packet size') + fields['oid'] = AndroidOpcodeId.from_int((span[0] >> 0) & 0x3f) + value_ = int.from_bytes(span[1:3], byteorder='little') + span = span[3:] payload = span span = bytes([]) fields['payload'] = payload @@ -1861,194 +1615,144 @@ class AndroidCommand(UciCommand): except Exception as exn: pass try: - return AndroidSetCountryCodeCmd.parse(fields.copy(), payload) + return AndroidGetPowerStatsRsp.parse(fields.copy(), payload) except Exception as exn: pass - return AndroidCommand(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciCommand.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class AndroidResponse(UciResponse): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_ANDROID - self.mt = MessageType.RESPONSE - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['AndroidResponse', bytes]: - if fields['gid'] != GroupId.VENDOR_ANDROID or fields['mt'] != MessageType.RESPONSE: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload try: - return AndroidGetPowerStatsRsp.parse(fields.copy(), payload) + return AndroidSetCountryCodeCmd.parse(fields.copy(), payload) except Exception as exn: pass try: return AndroidSetCountryCodeRsp.parse(fields.copy(), payload) except Exception as exn: pass - return AndroidResponse(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciResponse.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class AndroidNotification(UciNotification): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_ANDROID - self.mt = MessageType.NOTIFICATION - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['AndroidNotification', bytes]: - if fields['gid'] != GroupId.VENDOR_ANDROID or fields['mt'] != MessageType.NOTIFICATION: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload try: return AndroidRangeDiagnosticsNtf.parse(fields.copy(), payload) except Exception as exn: pass - return AndroidNotification(**fields), span + return AndroidPacket(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() + _span.append((self.oid << 0)) + _span.extend([0] * 2) _span.extend(payload or self.payload or []) - return UciNotification.serialize(self, payload = bytes(_span)) + return ControlPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: - return len(self.payload) + return len(self.payload) + 3 @dataclass -class DeviceResetCmd(CoreCommand): +class CoreDeviceResetCmd(CorePacket): reset_config: ResetConfig = field(kw_only=True, default=ResetConfig.UWBS_RESET) def __post_init__(self): - self.opcode = 0 - self.gid = GroupId.CORE self.mt = MessageType.COMMAND + self.oid = CoreOpcodeId.DEVICE_RESET + self.gid = GroupId.CORE @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['DeviceResetCmd', bytes]: - if fields['opcode'] != 0x0 or fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.COMMAND: + def parse(fields: dict, span: bytes) -> Tuple['CoreDeviceResetCmd', bytes]: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != CoreOpcodeId.DEVICE_RESET or fields['gid'] != GroupId.CORE: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') fields['reset_config'] = ResetConfig.from_int(span[0]) span = span[1:] - return DeviceResetCmd(**fields), span + return CoreDeviceResetCmd(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() _span.append((self.reset_config << 0)) - return CoreCommand.serialize(self, payload = bytes(_span)) + return CorePacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 1 @dataclass -class DeviceResetRsp(CoreResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class CoreDeviceResetRsp(CorePacket): + status: Status = field(kw_only=True, default=Status.OK) def __post_init__(self): - self.opcode = 0 - self.gid = GroupId.CORE self.mt = MessageType.RESPONSE + self.oid = CoreOpcodeId.DEVICE_RESET + self.gid = GroupId.CORE @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['DeviceResetRsp', bytes]: - if fields['opcode'] != 0x0 or fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.RESPONSE: + def parse(fields: dict, span: bytes) -> Tuple['CoreDeviceResetRsp', bytes]: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != CoreOpcodeId.DEVICE_RESET or fields['gid'] != GroupId.CORE: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) span = span[1:] - return DeviceResetRsp(**fields), span + return CoreDeviceResetRsp(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() _span.append((self.status << 0)) - return CoreResponse.serialize(self, payload = bytes(_span)) + return CorePacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 1 @dataclass -class DeviceStatusNtf(CoreNotification): +class CoreDeviceStatusNtf(CorePacket): device_state: DeviceState = field(kw_only=True, default=DeviceState.DEVICE_STATE_READY) def __post_init__(self): - self.opcode = 1 - self.gid = GroupId.CORE self.mt = MessageType.NOTIFICATION + self.oid = CoreOpcodeId.DEVICE_STATUS + self.gid = GroupId.CORE @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['DeviceStatusNtf', bytes]: - if fields['opcode'] != 0x1 or fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.NOTIFICATION: + def parse(fields: dict, span: bytes) -> Tuple['CoreDeviceStatusNtf', bytes]: + if fields['mt'] != MessageType.NOTIFICATION or fields['oid'] != CoreOpcodeId.DEVICE_STATUS or fields['gid'] != GroupId.CORE: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') fields['device_state'] = DeviceState.from_int(span[0]) span = span[1:] - return DeviceStatusNtf(**fields), span + return CoreDeviceStatusNtf(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() _span.append((self.device_state << 0)) - return CoreNotification.serialize(self, payload = bytes(_span)) + return CorePacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 1 @dataclass -class GetDeviceInfoCmd(CoreCommand): +class CoreGetDeviceInfoCmd(CorePacket): def __post_init__(self): - self.opcode = 2 - self.gid = GroupId.CORE self.mt = MessageType.COMMAND + self.oid = CoreOpcodeId.GET_DEVICE_INFO + self.gid = GroupId.CORE @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['GetDeviceInfoCmd', bytes]: - if fields['opcode'] != 0x2 or fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.COMMAND: + def parse(fields: dict, span: bytes) -> Tuple['CoreGetDeviceInfoCmd', bytes]: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != CoreOpcodeId.GET_DEVICE_INFO or fields['gid'] != GroupId.CORE: raise Exception("Invalid constraint field values") - return GetDeviceInfoCmd(**fields), span + return CoreGetDeviceInfoCmd(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() - return CoreCommand.serialize(self, payload = bytes(_span)) + return CorePacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 0 @dataclass -class GetDeviceInfoRsp(CoreResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class CoreGetDeviceInfoRsp(CorePacket): + status: Status = field(kw_only=True, default=Status.OK) uci_version: int = field(kw_only=True, default=0) mac_version: int = field(kw_only=True, default=0) phy_version: int = field(kw_only=True, default=0) @@ -2056,17 +1760,17 @@ class GetDeviceInfoRsp(CoreResponse): vendor_spec_info: bytearray = field(kw_only=True, default_factory=bytearray) def __post_init__(self): - self.opcode = 2 - self.gid = GroupId.CORE self.mt = MessageType.RESPONSE + self.oid = CoreOpcodeId.GET_DEVICE_INFO + self.gid = GroupId.CORE @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['GetDeviceInfoRsp', bytes]: - if fields['opcode'] != 0x2 or fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.RESPONSE: + def parse(fields: dict, span: bytes) -> Tuple['CoreGetDeviceInfoRsp', bytes]: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != CoreOpcodeId.GET_DEVICE_INFO or fields['gid'] != GroupId.CORE: raise Exception("Invalid constraint field values") if len(span) < 10: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) value_ = int.from_bytes(span[1:3], byteorder='little') fields['uci_version'] = value_ value_ = int.from_bytes(span[3:5], byteorder='little') @@ -2081,56 +1785,56 @@ class GetDeviceInfoRsp(CoreResponse): raise Exception('Invalid packet size') fields['vendor_spec_info'] = list(span[:vendor_spec_info_count]) span = span[vendor_spec_info_count:] - return GetDeviceInfoRsp(**fields), span + return CoreGetDeviceInfoRsp(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() _span.append((self.status << 0)) if self.uci_version > 65535: - print(f"Invalid value for field GetDeviceInfoRsp::uci_version: {self.uci_version} > 65535; the value will be truncated") + print(f"Invalid value for field CoreGetDeviceInfoRsp::uci_version: {self.uci_version} > 65535; the value will be truncated") self.uci_version &= 65535 _span.extend(int.to_bytes((self.uci_version << 0), length=2, byteorder='little')) if self.mac_version > 65535: - print(f"Invalid value for field GetDeviceInfoRsp::mac_version: {self.mac_version} > 65535; the value will be truncated") + print(f"Invalid value for field CoreGetDeviceInfoRsp::mac_version: {self.mac_version} > 65535; the value will be truncated") self.mac_version &= 65535 _span.extend(int.to_bytes((self.mac_version << 0), length=2, byteorder='little')) if self.phy_version > 65535: - print(f"Invalid value for field GetDeviceInfoRsp::phy_version: {self.phy_version} > 65535; the value will be truncated") + print(f"Invalid value for field CoreGetDeviceInfoRsp::phy_version: {self.phy_version} > 65535; the value will be truncated") self.phy_version &= 65535 _span.extend(int.to_bytes((self.phy_version << 0), length=2, byteorder='little')) if self.uci_test_version > 65535: - print(f"Invalid value for field GetDeviceInfoRsp::uci_test_version: {self.uci_test_version} > 65535; the value will be truncated") + print(f"Invalid value for field CoreGetDeviceInfoRsp::uci_test_version: {self.uci_test_version} > 65535; the value will be truncated") self.uci_test_version &= 65535 _span.extend(int.to_bytes((self.uci_test_version << 0), length=2, byteorder='little')) if len(self.vendor_spec_info) > 255: - print(f"Invalid length for field GetDeviceInfoRsp::vendor_spec_info: {len(self.vendor_spec_info)} > 255; the array will be truncated") + print(f"Invalid length for field CoreGetDeviceInfoRsp::vendor_spec_info: {len(self.vendor_spec_info)} > 255; the array will be truncated") del self.vendor_spec_info[255:] _span.append((len(self.vendor_spec_info) << 0)) _span.extend(self.vendor_spec_info) - return CoreResponse.serialize(self, payload = bytes(_span)) + return CorePacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return len(self.vendor_spec_info) * 1 + 10 @dataclass -class GetCapsInfoCmd(CoreCommand): +class CoreGetCapsInfoCmd(CorePacket): def __post_init__(self): - self.opcode = 3 - self.gid = GroupId.CORE self.mt = MessageType.COMMAND + self.oid = CoreOpcodeId.GET_CAPS_INFO + self.gid = GroupId.CORE @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['GetCapsInfoCmd', bytes]: - if fields['opcode'] != 0x3 or fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.COMMAND: + def parse(fields: dict, span: bytes) -> Tuple['CoreGetCapsInfoCmd', bytes]: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != CoreOpcodeId.GET_CAPS_INFO or fields['gid'] != GroupId.CORE: raise Exception("Invalid constraint field values") - return GetCapsInfoCmd(**fields), span + return CoreGetCapsInfoCmd(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() - return CoreCommand.serialize(self, payload = bytes(_span)) + return CorePacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: @@ -2173,22 +1877,22 @@ class CapTlv(Packet): return len(self.v) * 1 + 2 @dataclass -class GetCapsInfoRsp(CoreResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class CoreGetCapsInfoRsp(CorePacket): + status: Status = field(kw_only=True, default=Status.OK) tlvs: List[CapTlv] = field(kw_only=True, default_factory=list) def __post_init__(self): - self.opcode = 3 - self.gid = GroupId.CORE self.mt = MessageType.RESPONSE + self.oid = CoreOpcodeId.GET_CAPS_INFO + self.gid = GroupId.CORE @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['GetCapsInfoRsp', bytes]: - if fields['opcode'] != 0x3 or fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.RESPONSE: + def parse(fields: dict, span: bytes) -> Tuple['CoreGetCapsInfoRsp', bytes]: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != CoreOpcodeId.GET_CAPS_INFO or fields['gid'] != GroupId.CORE: raise Exception("Invalid constraint field values") if len(span) < 2: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) tlvs_count = span[1] span = span[2:] tlvs = [] @@ -2196,118 +1900,127 @@ class GetCapsInfoRsp(CoreResponse): element, span = CapTlv.parse(span) tlvs.append(element) fields['tlvs'] = tlvs - return GetCapsInfoRsp(**fields), span + return CoreGetCapsInfoRsp(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() _span.append((self.status << 0)) if len(self.tlvs) > 255: - print(f"Invalid length for field GetCapsInfoRsp::tlvs: {len(self.tlvs)} > 255; the array will be truncated") + print(f"Invalid length for field CoreGetCapsInfoRsp::tlvs: {len(self.tlvs)} > 255; the array will be truncated") del self.tlvs[255:] _span.append((len(self.tlvs) << 0)) for _elt in self.tlvs: _span.extend(_elt.serialize()) - return CoreResponse.serialize(self, payload = bytes(_span)) + return CorePacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return sum([elt.size for elt in self.tlvs]) + 2 +class ConfigParameterId(enum.IntEnum): + DEVICE_STATE = 0x0 + LOW_POWER_MODE = 0x1 + + @staticmethod + def from_int(v: int) -> Union[int, 'ConfigParameterId']: + try: + return ConfigParameterId(v) + except ValueError as exn: + return v + + @dataclass -class DeviceConfigTlv(Packet): - cfg_id: DeviceConfigId = field(kw_only=True, default=DeviceConfigId.DEVICE_STATE) - v: bytearray = field(kw_only=True, default_factory=bytearray) +class ConfigParameter(Packet): + id: ConfigParameterId = field(kw_only=True, default=ConfigParameterId.DEVICE_STATE) + value: bytearray = field(kw_only=True, default_factory=bytearray) def __post_init__(self): pass @staticmethod - def parse(span: bytes) -> Tuple['DeviceConfigTlv', bytes]: + def parse(span: bytes) -> Tuple['ConfigParameter', bytes]: fields = {'payload': None} if len(span) < 2: raise Exception('Invalid packet size') - fields['cfg_id'] = DeviceConfigId.from_int(span[0]) - v_count = span[1] + fields['id'] = ConfigParameterId.from_int(span[0]) + value_size = span[1] span = span[2:] - if len(span) < v_count: + if len(span) < value_size: raise Exception('Invalid packet size') - fields['v'] = list(span[:v_count]) - span = span[v_count:] - return DeviceConfigTlv(**fields), span + fields['value'] = list(span[:value_size]) + span = span[value_size:] + return ConfigParameter(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() - _span.append((self.cfg_id << 0)) - if len(self.v) > 255: - print(f"Invalid length for field DeviceConfigTlv::v: {len(self.v)} > 255; the array will be truncated") - del self.v[255:] - _span.append((len(self.v) << 0)) - _span.extend(self.v) + _span.append((self.id << 0)) + _span.append(((len(self.value) * 1) << 0)) + _span.extend(self.value) return bytes(_span) @property def size(self) -> int: - return len(self.v) * 1 + 2 + return len(self.value) * 1 + 2 @dataclass -class SetConfigCmd(CoreCommand): - tlvs: List[DeviceConfigTlv] = field(kw_only=True, default_factory=list) +class CoreSetConfigCmd(CorePacket): + parameters: List[ConfigParameter] = field(kw_only=True, default_factory=list) def __post_init__(self): - self.opcode = 4 - self.gid = GroupId.CORE self.mt = MessageType.COMMAND + self.oid = CoreOpcodeId.SET_CONFIG + self.gid = GroupId.CORE @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['SetConfigCmd', bytes]: - if fields['opcode'] != 0x4 or fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.COMMAND: + def parse(fields: dict, span: bytes) -> Tuple['CoreSetConfigCmd', bytes]: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != CoreOpcodeId.SET_CONFIG or fields['gid'] != GroupId.CORE: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') - tlvs_count = span[0] + parameters_count = span[0] span = span[1:] - tlvs = [] - for n in range(tlvs_count): - element, span = DeviceConfigTlv.parse(span) - tlvs.append(element) - fields['tlvs'] = tlvs - return SetConfigCmd(**fields), span + parameters = [] + for n in range(parameters_count): + element, span = ConfigParameter.parse(span) + parameters.append(element) + fields['parameters'] = parameters + return CoreSetConfigCmd(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() - if len(self.tlvs) > 255: - print(f"Invalid length for field SetConfigCmd::tlvs: {len(self.tlvs)} > 255; the array will be truncated") - del self.tlvs[255:] - _span.append((len(self.tlvs) << 0)) - for _elt in self.tlvs: + if len(self.parameters) > 255: + print(f"Invalid length for field CoreSetConfigCmd::parameters: {len(self.parameters)} > 255; the array will be truncated") + del self.parameters[255:] + _span.append((len(self.parameters) << 0)) + for _elt in self.parameters: _span.extend(_elt.serialize()) - return CoreCommand.serialize(self, payload = bytes(_span)) + return CorePacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: - return sum([elt.size for elt in self.tlvs]) + 1 + return sum([elt.size for elt in self.parameters]) + 1 @dataclass -class DeviceConfigStatus(Packet): - cfg_id: DeviceConfigId = field(kw_only=True, default=DeviceConfigId.DEVICE_STATE) - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class ConfigParameterStatus(Packet): + id: ConfigParameterId = field(kw_only=True, default=ConfigParameterId.DEVICE_STATE) + status: Status = field(kw_only=True, default=Status.OK) def __post_init__(self): pass @staticmethod - def parse(span: bytes) -> Tuple['DeviceConfigStatus', bytes]: + def parse(span: bytes) -> Tuple['ConfigParameterStatus', bytes]: fields = {'payload': None} if len(span) < 2: raise Exception('Invalid packet size') - fields['cfg_id'] = DeviceConfigId.from_int(span[0]) - fields['status'] = StatusCode.from_int(span[1]) + fields['id'] = ConfigParameterId.from_int(span[0]) + fields['status'] = Status.from_int(span[1]) span = span[2:] - return DeviceConfigStatus(**fields), span + return ConfigParameterStatus(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() - _span.append((self.cfg_id << 0)) + _span.append((self.id << 0)) _span.append((self.status << 0)) return bytes(_span) @@ -2316,193 +2029,197 @@ class DeviceConfigStatus(Packet): return 2 @dataclass -class SetConfigRsp(CoreResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) - cfg_status: List[DeviceConfigStatus] = field(kw_only=True, default_factory=list) +class CoreSetConfigRsp(CorePacket): + status: Status = field(kw_only=True, default=Status.OK) + parameters: List[ConfigParameterStatus] = field(kw_only=True, default_factory=list) def __post_init__(self): - self.opcode = 4 - self.gid = GroupId.CORE self.mt = MessageType.RESPONSE + self.oid = CoreOpcodeId.SET_CONFIG + self.gid = GroupId.CORE @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['SetConfigRsp', bytes]: - if fields['opcode'] != 0x4 or fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.RESPONSE: + def parse(fields: dict, span: bytes) -> Tuple['CoreSetConfigRsp', bytes]: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != CoreOpcodeId.SET_CONFIG or fields['gid'] != GroupId.CORE: raise Exception("Invalid constraint field values") if len(span) < 2: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) - cfg_status_count = span[1] + fields['status'] = Status.from_int(span[0]) + parameters_count = span[1] span = span[2:] - if len(span) < cfg_status_count * 2: + if len(span) < parameters_count * 2: raise Exception('Invalid packet size') - cfg_status = [] - for n in range(cfg_status_count): - cfg_status.append(DeviceConfigStatus.parse_all(span[n * 2:(n + 1) * 2])) - fields['cfg_status'] = cfg_status - span = span[cfg_status_count * 2:] - return SetConfigRsp(**fields), span + parameters = [] + for n in range(parameters_count): + parameters.append(ConfigParameterStatus.parse_all(span[n * 2:(n + 1) * 2])) + fields['parameters'] = parameters + span = span[parameters_count * 2:] + return CoreSetConfigRsp(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() _span.append((self.status << 0)) - if len(self.cfg_status) > 255: - print(f"Invalid length for field SetConfigRsp::cfg_status: {len(self.cfg_status)} > 255; the array will be truncated") - del self.cfg_status[255:] - _span.append((len(self.cfg_status) << 0)) - for _elt in self.cfg_status: + if len(self.parameters) > 255: + print(f"Invalid length for field CoreSetConfigRsp::parameters: {len(self.parameters)} > 255; the array will be truncated") + del self.parameters[255:] + _span.append((len(self.parameters) << 0)) + for _elt in self.parameters: _span.extend(_elt.serialize()) - return CoreResponse.serialize(self, payload = bytes(_span)) + return CorePacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: - return sum([elt.size for elt in self.cfg_status]) + 2 + return sum([elt.size for elt in self.parameters]) + 2 @dataclass -class GetConfigCmd(CoreCommand): - cfg_id: bytearray = field(kw_only=True, default_factory=bytearray) +class CoreGetConfigCmd(CorePacket): + parameter_ids: List[ConfigParameterId] = field(kw_only=True, default_factory=list) def __post_init__(self): - self.opcode = 5 - self.gid = GroupId.CORE self.mt = MessageType.COMMAND + self.oid = CoreOpcodeId.GET_CONFIG + self.gid = GroupId.CORE @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['GetConfigCmd', bytes]: - if fields['opcode'] != 0x5 or fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.COMMAND: + def parse(fields: dict, span: bytes) -> Tuple['CoreGetConfigCmd', bytes]: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != CoreOpcodeId.GET_CONFIG or fields['gid'] != GroupId.CORE: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') - cfg_id_count = span[0] + parameter_ids_count = span[0] span = span[1:] - if len(span) < cfg_id_count: + if len(span) < parameter_ids_count: raise Exception('Invalid packet size') - fields['cfg_id'] = list(span[:cfg_id_count]) - span = span[cfg_id_count:] - return GetConfigCmd(**fields), span + parameter_ids = [] + for n in range(parameter_ids_count): + parameter_ids.append(ConfigParameterId(int.from_bytes(span[n:n + 1], byteorder='little'))) + fields['parameter_ids'] = parameter_ids + span = span[parameter_ids_count:] + return CoreGetConfigCmd(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() - if len(self.cfg_id) > 255: - print(f"Invalid length for field GetConfigCmd::cfg_id: {len(self.cfg_id)} > 255; the array will be truncated") - del self.cfg_id[255:] - _span.append((len(self.cfg_id) << 0)) - _span.extend(self.cfg_id) - return CoreCommand.serialize(self, payload = bytes(_span)) + if len(self.parameter_ids) > 255: + print(f"Invalid length for field CoreGetConfigCmd::parameter_ids: {len(self.parameter_ids)} > 255; the array will be truncated") + del self.parameter_ids[255:] + _span.append((len(self.parameter_ids) << 0)) + for _elt in self.parameter_ids: + _span.append(_elt) + return CorePacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: - return len(self.cfg_id) * 1 + 1 + return len(self.parameter_ids) * 8 + 1 @dataclass -class GetConfigRsp(CoreResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) - tlvs: List[DeviceConfigTlv] = field(kw_only=True, default_factory=list) +class CoreGetConfigRsp(CorePacket): + status: Status = field(kw_only=True, default=Status.OK) + parameters: List[ConfigParameter] = field(kw_only=True, default_factory=list) def __post_init__(self): - self.opcode = 5 - self.gid = GroupId.CORE self.mt = MessageType.RESPONSE + self.oid = CoreOpcodeId.GET_CONFIG + self.gid = GroupId.CORE @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['GetConfigRsp', bytes]: - if fields['opcode'] != 0x5 or fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.RESPONSE: + def parse(fields: dict, span: bytes) -> Tuple['CoreGetConfigRsp', bytes]: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != CoreOpcodeId.GET_CONFIG or fields['gid'] != GroupId.CORE: raise Exception("Invalid constraint field values") if len(span) < 2: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) - tlvs_count = span[1] + fields['status'] = Status.from_int(span[0]) + parameters_count = span[1] span = span[2:] - tlvs = [] - for n in range(tlvs_count): - element, span = DeviceConfigTlv.parse(span) - tlvs.append(element) - fields['tlvs'] = tlvs - return GetConfigRsp(**fields), span + parameters = [] + for n in range(parameters_count): + element, span = ConfigParameter.parse(span) + parameters.append(element) + fields['parameters'] = parameters + return CoreGetConfigRsp(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() _span.append((self.status << 0)) - if len(self.tlvs) > 255: - print(f"Invalid length for field GetConfigRsp::tlvs: {len(self.tlvs)} > 255; the array will be truncated") - del self.tlvs[255:] - _span.append((len(self.tlvs) << 0)) - for _elt in self.tlvs: + if len(self.parameters) > 255: + print(f"Invalid length for field CoreGetConfigRsp::parameters: {len(self.parameters)} > 255; the array will be truncated") + del self.parameters[255:] + _span.append((len(self.parameters) << 0)) + for _elt in self.parameters: _span.extend(_elt.serialize()) - return CoreResponse.serialize(self, payload = bytes(_span)) + return CorePacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: - return sum([elt.size for elt in self.tlvs]) + 2 + return sum([elt.size for elt in self.parameters]) + 2 @dataclass -class GenericError(CoreNotification): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class CoreGenericErrorNtf(CorePacket): + status: Status = field(kw_only=True, default=Status.OK) def __post_init__(self): - self.opcode = 7 - self.gid = GroupId.CORE self.mt = MessageType.NOTIFICATION + self.oid = CoreOpcodeId.GENERIC_ERROR + self.gid = GroupId.CORE @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['GenericError', bytes]: - if fields['opcode'] != 0x7 or fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.NOTIFICATION: + def parse(fields: dict, span: bytes) -> Tuple['CoreGenericErrorNtf', bytes]: + if fields['mt'] != MessageType.NOTIFICATION or fields['oid'] != CoreOpcodeId.GENERIC_ERROR or fields['gid'] != GroupId.CORE: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) span = span[1:] - return GenericError(**fields), span + return CoreGenericErrorNtf(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() _span.append((self.status << 0)) - return CoreNotification.serialize(self, payload = bytes(_span)) + return CorePacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 1 @dataclass -class CoreQueryTimeStampCmd(CoreCommand): +class CoreQueryTimeStampCmd(CorePacket): def __post_init__(self): - self.opcode = 8 - self.gid = GroupId.CORE self.mt = MessageType.COMMAND + self.oid = CoreOpcodeId.QUERY_UWBS_TIMESTAMP + self.gid = GroupId.CORE @staticmethod def parse(fields: dict, span: bytes) -> Tuple['CoreQueryTimeStampCmd', bytes]: - if fields['opcode'] != 0x8 or fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.COMMAND: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != CoreOpcodeId.QUERY_UWBS_TIMESTAMP or fields['gid'] != GroupId.CORE: raise Exception("Invalid constraint field values") return CoreQueryTimeStampCmd(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() - return CoreCommand.serialize(self, payload = bytes(_span)) + return CorePacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 0 @dataclass -class CoreQueryTimeStampRsp(CoreResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class CoreQueryTimeStampRsp(CorePacket): + status: Status = field(kw_only=True, default=Status.OK) timeStamp: int = field(kw_only=True, default=0) def __post_init__(self): - self.opcode = 8 - self.gid = GroupId.CORE self.mt = MessageType.RESPONSE + self.oid = CoreOpcodeId.QUERY_UWBS_TIMESTAMP + self.gid = GroupId.CORE @staticmethod def parse(fields: dict, span: bytes) -> Tuple['CoreQueryTimeStampRsp', bytes]: - if fields['opcode'] != 0x8 or fields['gid'] != GroupId.CORE or fields['mt'] != MessageType.RESPONSE: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != CoreOpcodeId.QUERY_UWBS_TIMESTAMP or fields['gid'] != GroupId.CORE: raise Exception("Invalid constraint field values") if len(span) < 9: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) value_ = int.from_bytes(span[1:9], byteorder='little') fields['timeStamp'] = value_ span = span[9:] @@ -2515,25 +2232,25 @@ class CoreQueryTimeStampRsp(CoreResponse): print(f"Invalid value for field CoreQueryTimeStampRsp::timeStamp: {self.timeStamp} > 18446744073709551615; the value will be truncated") self.timeStamp &= 18446744073709551615 _span.extend(int.to_bytes((self.timeStamp << 0), length=8, byteorder='little')) - return CoreResponse.serialize(self, payload = bytes(_span)) + return CorePacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 9 @dataclass -class SessionInitCmd(SessionConfigCommand): +class SessionInitCmd(SessionConfigPacket): session_id: int = field(kw_only=True, default=0) session_type: SessionType = field(kw_only=True, default=SessionType.FIRA_RANGING_SESSION) def __post_init__(self): - self.opcode = 0 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.COMMAND + self.oid = SessionConfigOpcodeId.INIT + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionInitCmd', bytes]: - if fields['opcode'] != 0x0 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.COMMAND: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != SessionConfigOpcodeId.INIT or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 5: raise Exception('Invalid packet size') @@ -2550,29 +2267,29 @@ class SessionInitCmd(SessionConfigCommand): self.session_id &= 4294967295 _span.extend(int.to_bytes((self.session_id << 0), length=4, byteorder='little')) _span.append((self.session_type << 0)) - return SessionConfigCommand.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 5 @dataclass -class SessionInitRsp_V2(SessionConfigResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class SessionInitRsp_V2(SessionConfigPacket): + status: Status = field(kw_only=True, default=Status.OK) session_handle: int = field(kw_only=True, default=0) def __post_init__(self): - self.opcode = 0 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.RESPONSE + self.oid = SessionConfigOpcodeId.INIT + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionInitRsp_V2', bytes]: - if fields['opcode'] != 0x0 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.RESPONSE: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != SessionConfigOpcodeId.INIT or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 5: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) value_ = int.from_bytes(span[1:5], byteorder='little') fields['session_handle'] = value_ span = span[5:] @@ -2585,52 +2302,52 @@ class SessionInitRsp_V2(SessionConfigResponse): print(f"Invalid value for field SessionInitRsp_V2::session_handle: {self.session_handle} > 4294967295; the value will be truncated") self.session_handle &= 4294967295 _span.extend(int.to_bytes((self.session_handle << 0), length=4, byteorder='little')) - return SessionConfigResponse.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 5 @dataclass -class SessionInitRsp(SessionConfigResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class SessionInitRsp(SessionConfigPacket): + status: Status = field(kw_only=True, default=Status.OK) def __post_init__(self): - self.opcode = 0 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.RESPONSE + self.oid = SessionConfigOpcodeId.INIT + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionInitRsp', bytes]: - if fields['opcode'] != 0x0 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.RESPONSE: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != SessionConfigOpcodeId.INIT or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) span = span[1:] return SessionInitRsp(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() _span.append((self.status << 0)) - return SessionConfigResponse.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 1 @dataclass -class SessionDeinitCmd(SessionConfigCommand): +class SessionDeinitCmd(SessionConfigPacket): session_token: int = field(kw_only=True, default=0) def __post_init__(self): - self.opcode = 1 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.COMMAND + self.oid = SessionConfigOpcodeId.DEINIT + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionDeinitCmd', bytes]: - if fields['opcode'] != 0x1 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.COMMAND: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != SessionConfigOpcodeId.DEINIT or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 4: raise Exception('Invalid packet size') @@ -2645,54 +2362,54 @@ class SessionDeinitCmd(SessionConfigCommand): print(f"Invalid value for field SessionDeinitCmd::session_token: {self.session_token} > 4294967295; the value will be truncated") self.session_token &= 4294967295 _span.extend(int.to_bytes((self.session_token << 0), length=4, byteorder='little')) - return SessionConfigCommand.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 4 @dataclass -class SessionDeinitRsp(SessionConfigResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class SessionDeinitRsp(SessionConfigPacket): + status: Status = field(kw_only=True, default=Status.OK) def __post_init__(self): - self.opcode = 1 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.RESPONSE + self.oid = SessionConfigOpcodeId.DEINIT + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionDeinitRsp', bytes]: - if fields['opcode'] != 0x1 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.RESPONSE: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != SessionConfigOpcodeId.DEINIT or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) span = span[1:] return SessionDeinitRsp(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() _span.append((self.status << 0)) - return SessionConfigResponse.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 1 @dataclass -class SessionStatusNtf(SessionConfigNotification): +class SessionStatusNtf(SessionConfigPacket): session_token: int = field(kw_only=True, default=0) session_state: SessionState = field(kw_only=True, default=SessionState.SESSION_STATE_INIT) reason_code: int = field(kw_only=True, default=0) def __post_init__(self): - self.opcode = 2 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.NOTIFICATION + self.oid = SessionConfigOpcodeId.STATUS + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionStatusNtf', bytes]: - if fields['opcode'] != 0x2 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.NOTIFICATION: + if fields['mt'] != MessageType.NOTIFICATION or fields['oid'] != SessionConfigOpcodeId.STATUS or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 6: raise Exception('Invalid packet size') @@ -2714,7 +2431,7 @@ class SessionStatusNtf(SessionConfigNotification): print(f"Invalid value for field SessionStatusNtf::reason_code: {self.reason_code} > 255; the value will be truncated") self.reason_code &= 255 _span.append((self.reason_code << 0)) - return SessionConfigNotification.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: @@ -2757,18 +2474,18 @@ class AppConfigTlv(Packet): return len(self.v) * 1 + 2 @dataclass -class SessionSetAppConfigCmd(SessionConfigCommand): +class SessionSetAppConfigCmd(SessionConfigPacket): session_token: int = field(kw_only=True, default=0) tlvs: List[AppConfigTlv] = field(kw_only=True, default_factory=list) def __post_init__(self): - self.opcode = 3 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.COMMAND + self.oid = SessionConfigOpcodeId.SET_APP_CONFIG + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionSetAppConfigCmd', bytes]: - if fields['opcode'] != 0x3 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.COMMAND: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != SessionConfigOpcodeId.SET_APP_CONFIG or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 5: raise Exception('Invalid packet size') @@ -2795,7 +2512,7 @@ class SessionSetAppConfigCmd(SessionConfigCommand): _span.append((len(self.tlvs) << 0)) for _elt in self.tlvs: _span.extend(_elt.serialize()) - return SessionConfigCommand.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: @@ -2804,7 +2521,7 @@ class SessionSetAppConfigCmd(SessionConfigCommand): @dataclass class AppConfigStatus(Packet): cfg_id: AppConfigTlvType = field(kw_only=True, default=AppConfigTlvType.DEVICE_TYPE) - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) + status: Status = field(kw_only=True, default=Status.OK) def __post_init__(self): pass @@ -2815,7 +2532,7 @@ class AppConfigStatus(Packet): if len(span) < 2: raise Exception('Invalid packet size') fields['cfg_id'] = AppConfigTlvType.from_int(span[0]) - fields['status'] = StatusCode.from_int(span[1]) + fields['status'] = Status.from_int(span[1]) span = span[2:] return AppConfigStatus(**fields), span @@ -2830,22 +2547,22 @@ class AppConfigStatus(Packet): return 2 @dataclass -class SessionSetAppConfigRsp(SessionConfigResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class SessionSetAppConfigRsp(SessionConfigPacket): + status: Status = field(kw_only=True, default=Status.OK) cfg_status: List[AppConfigStatus] = field(kw_only=True, default_factory=list) def __post_init__(self): - self.opcode = 3 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.RESPONSE + self.oid = SessionConfigOpcodeId.SET_APP_CONFIG + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionSetAppConfigRsp', bytes]: - if fields['opcode'] != 0x3 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.RESPONSE: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != SessionConfigOpcodeId.SET_APP_CONFIG or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 2: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) cfg_status_count = span[1] span = span[2:] if len(span) < cfg_status_count * 2: @@ -2866,25 +2583,25 @@ class SessionSetAppConfigRsp(SessionConfigResponse): _span.append((len(self.cfg_status) << 0)) for _elt in self.cfg_status: _span.extend(_elt.serialize()) - return SessionConfigResponse.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return sum([elt.size for elt in self.cfg_status]) + 2 @dataclass -class SessionGetAppConfigCmd(SessionConfigCommand): +class SessionGetAppConfigCmd(SessionConfigPacket): 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 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.COMMAND + self.oid = SessionConfigOpcodeId.GET_APP_CONFIG + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionGetAppConfigCmd', bytes]: - if fields['opcode'] != 0x4 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.COMMAND: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != SessionConfigOpcodeId.GET_APP_CONFIG or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 5: raise Exception('Invalid packet size') @@ -2894,7 +2611,10 @@ class SessionGetAppConfigCmd(SessionConfigCommand): 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,30 +2628,31 @@ class SessionGetAppConfigCmd(SessionConfigCommand): 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) - return SessionConfigCommand.serialize(self, payload = bytes(_span)) + for _elt in self.app_cfg: + _span.append(_elt) + return SessionConfigPacket.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): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class SessionGetAppConfigRsp(SessionConfigPacket): + status: Status = field(kw_only=True, default=Status.OK) tlvs: List[AppConfigTlv] = field(kw_only=True, default_factory=list) def __post_init__(self): - self.opcode = 4 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.RESPONSE + self.oid = SessionConfigOpcodeId.GET_APP_CONFIG + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionGetAppConfigRsp', bytes]: - if fields['opcode'] != 0x4 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.RESPONSE: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != SessionConfigOpcodeId.GET_APP_CONFIG or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 2: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) tlvs_count = span[1] span = span[2:] tlvs = [] @@ -2950,52 +2671,52 @@ class SessionGetAppConfigRsp(SessionConfigResponse): _span.append((len(self.tlvs) << 0)) for _elt in self.tlvs: _span.extend(_elt.serialize()) - return SessionConfigResponse.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return sum([elt.size for elt in self.tlvs]) + 2 @dataclass -class SessionGetCountCmd(SessionConfigCommand): +class SessionGetCountCmd(SessionConfigPacket): def __post_init__(self): - self.opcode = 5 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.COMMAND + self.oid = SessionConfigOpcodeId.GET_COUNT + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionGetCountCmd', bytes]: - if fields['opcode'] != 0x5 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.COMMAND: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != SessionConfigOpcodeId.GET_COUNT or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") return SessionGetCountCmd(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() - return SessionConfigCommand.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 0 @dataclass -class SessionGetCountRsp(SessionConfigResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class SessionGetCountRsp(SessionConfigPacket): + status: Status = field(kw_only=True, default=Status.OK) session_count: int = field(kw_only=True, default=0) def __post_init__(self): - self.opcode = 5 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.RESPONSE + self.oid = SessionConfigOpcodeId.GET_COUNT + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionGetCountRsp', bytes]: - if fields['opcode'] != 0x5 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.RESPONSE: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != SessionConfigOpcodeId.GET_COUNT or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 2: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) fields['session_count'] = span[1] span = span[2:] return SessionGetCountRsp(**fields), span @@ -3007,24 +2728,24 @@ class SessionGetCountRsp(SessionConfigResponse): print(f"Invalid value for field SessionGetCountRsp::session_count: {self.session_count} > 255; the value will be truncated") self.session_count &= 255 _span.append((self.session_count << 0)) - return SessionConfigResponse.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 2 @dataclass -class SessionGetStateCmd(SessionConfigCommand): +class SessionGetStateCmd(SessionConfigPacket): session_token: int = field(kw_only=True, default=0) def __post_init__(self): - self.opcode = 6 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.COMMAND + self.oid = SessionConfigOpcodeId.GET_STATE + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionGetStateCmd', bytes]: - if fields['opcode'] != 0x6 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.COMMAND: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != SessionConfigOpcodeId.GET_STATE or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 4: raise Exception('Invalid packet size') @@ -3039,29 +2760,29 @@ class SessionGetStateCmd(SessionConfigCommand): print(f"Invalid value for field SessionGetStateCmd::session_token: {self.session_token} > 4294967295; the value will be truncated") self.session_token &= 4294967295 _span.extend(int.to_bytes((self.session_token << 0), length=4, byteorder='little')) - return SessionConfigCommand.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 4 @dataclass -class SessionGetStateRsp(SessionConfigResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class SessionGetStateRsp(SessionConfigPacket): + status: Status = field(kw_only=True, default=Status.OK) session_state: SessionState = field(kw_only=True, default=SessionState.SESSION_STATE_INIT) def __post_init__(self): - self.opcode = 6 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.RESPONSE + self.oid = SessionConfigOpcodeId.GET_STATE + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionGetStateRsp', bytes]: - if fields['opcode'] != 0x6 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.RESPONSE: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != SessionConfigOpcodeId.GET_STATE or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 2: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) fields['session_state'] = SessionState.from_int(span[1]) span = span[2:] return SessionGetStateRsp(**fields), span @@ -3070,25 +2791,71 @@ class SessionGetStateRsp(SessionConfigResponse): _span = bytearray() _span.append((self.status << 0)) _span.append((self.session_state << 0)) - return SessionConfigResponse.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 2 @dataclass -class SessionUpdateDtTagRangingRoundsCmd(SessionConfigCommand): +class SessionUpdateDtAnchorRangingRoundsCmd(SessionConfigPacket): + + + def __post_init__(self): + self.mt = MessageType.COMMAND + self.oid = SessionConfigOpcodeId.UPDATE_DT_ANCHOR_RANGING_ROUNDS + self.gid = GroupId.SESSION_CONFIG + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple['SessionUpdateDtAnchorRangingRoundsCmd', bytes]: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != SessionConfigOpcodeId.UPDATE_DT_ANCHOR_RANGING_ROUNDS or fields['gid'] != GroupId.SESSION_CONFIG: + raise Exception("Invalid constraint field values") + return SessionUpdateDtAnchorRangingRoundsCmd(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + return SessionConfigPacket.serialize(self, payload = bytes(_span)) + + @property + def size(self) -> int: + return 0 + +@dataclass +class SessionUpdateDtAnchorRangingRoundsRsp(SessionConfigPacket): + + + def __post_init__(self): + self.mt = MessageType.RESPONSE + self.oid = SessionConfigOpcodeId.UPDATE_DT_ANCHOR_RANGING_ROUNDS + self.gid = GroupId.SESSION_CONFIG + + @staticmethod + def parse(fields: dict, span: bytes) -> Tuple['SessionUpdateDtAnchorRangingRoundsRsp', bytes]: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != SessionConfigOpcodeId.UPDATE_DT_ANCHOR_RANGING_ROUNDS or fields['gid'] != GroupId.SESSION_CONFIG: + raise Exception("Invalid constraint field values") + return SessionUpdateDtAnchorRangingRoundsRsp(**fields), span + + def serialize(self, payload: bytes = None) -> bytes: + _span = bytearray() + return SessionConfigPacket.serialize(self, payload = bytes(_span)) + + @property + def size(self) -> int: + return 0 + +@dataclass +class SessionUpdateDtTagRangingRoundsCmd(SessionConfigPacket): session_token: int = field(kw_only=True, default=0) ranging_round_indexes: bytearray = field(kw_only=True, default_factory=bytearray) def __post_init__(self): - self.opcode = 9 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.COMMAND + self.oid = SessionConfigOpcodeId.UPDATE_DT_TAG_RANGING_ROUNDS + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionUpdateDtTagRangingRoundsCmd', bytes]: - if fields['opcode'] != 0x9 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.COMMAND: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != SessionConfigOpcodeId.UPDATE_DT_TAG_RANGING_ROUNDS or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 5: raise Exception('Invalid packet size') @@ -3113,29 +2880,29 @@ class SessionUpdateDtTagRangingRoundsCmd(SessionConfigCommand): del self.ranging_round_indexes[255:] _span.append((len(self.ranging_round_indexes) << 0)) _span.extend(self.ranging_round_indexes) - return SessionConfigCommand.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return len(self.ranging_round_indexes) * 1 + 5 @dataclass -class SessionUpdateDtTagRangingRoundsRsp(SessionConfigResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class SessionUpdateDtTagRangingRoundsRsp(SessionConfigPacket): + status: Status = field(kw_only=True, default=Status.OK) ranging_round_indexes: bytearray = field(kw_only=True, default_factory=bytearray) def __post_init__(self): - self.opcode = 9 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.RESPONSE + self.oid = SessionConfigOpcodeId.UPDATE_DT_TAG_RANGING_ROUNDS + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionUpdateDtTagRangingRoundsRsp', bytes]: - if fields['opcode'] != 0x9 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.RESPONSE: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != SessionConfigOpcodeId.UPDATE_DT_TAG_RANGING_ROUNDS or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 2: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) ranging_round_indexes_count = span[1] span = span[2:] if len(span) < ranging_round_indexes_count: @@ -3152,7 +2919,7 @@ class SessionUpdateDtTagRangingRoundsRsp(SessionConfigResponse): del self.ranging_round_indexes[255:] _span.append((len(self.ranging_round_indexes) << 0)) _span.extend(self.ranging_round_indexes) - return SessionConfigResponse.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: @@ -3279,7 +3046,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']: @@ -3290,18 +3057,18 @@ class UpdateMulticastListAction(enum.IntEnum): @dataclass -class SessionUpdateControllerMulticastListCmd(SessionConfigCommand): +class SessionUpdateControllerMulticastListCmd(SessionConfigPacket): session_token: int = field(kw_only=True, default=0) action: UpdateMulticastListAction = field(kw_only=True, default=UpdateMulticastListAction.ADD_CONTROLEE) def __post_init__(self): - self.opcode = 7 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.COMMAND + self.oid = SessionConfigOpcodeId.UPDATE_CONTROLLER_MULTICAST_LIST + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionUpdateControllerMulticastListCmd', bytes]: - if fields['opcode'] != 0x7 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.COMMAND: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != SessionConfigOpcodeId.UPDATE_CONTROLLER_MULTICAST_LIST or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 5: raise Exception('Invalid packet size') @@ -3322,139 +3089,13 @@ class SessionUpdateControllerMulticastListCmd(SessionConfigCommand): _span.extend(int.to_bytes((self.session_token << 0), length=4, byteorder='little')) _span.append((self.action << 0)) _span.extend(payload or self.payload or []) - return SessionConfigCommand.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return len(self.payload) + 5 @dataclass -class PhaseList(Packet): - session_token: int = field(kw_only=True, default=0) - start_slot_index: int = field(kw_only=True, default=0) - end_slot_index: int = field(kw_only=True, default=0) - - def __post_init__(self): - pass - - @staticmethod - def parse(span: bytes) -> Tuple['PhaseList', bytes]: - fields = {'payload': None} - if len(span) < 8: - raise Exception('Invalid packet size') - value_ = int.from_bytes(span[0:4], byteorder='little') - fields['session_token'] = value_ - value_ = int.from_bytes(span[4:6], byteorder='little') - fields['start_slot_index'] = value_ - value_ = int.from_bytes(span[6:8], byteorder='little') - fields['end_slot_index'] = value_ - span = span[8:] - return PhaseList(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - if self.session_token > 4294967295: - print(f"Invalid value for field PhaseList::session_token: {self.session_token} > 4294967295; the value will be truncated") - self.session_token &= 4294967295 - _span.extend(int.to_bytes((self.session_token << 0), length=4, byteorder='little')) - if self.start_slot_index > 65535: - print(f"Invalid value for field PhaseList::start_slot_index: {self.start_slot_index} > 65535; the value will be truncated") - self.start_slot_index &= 65535 - _span.extend(int.to_bytes((self.start_slot_index << 0), length=2, byteorder='little')) - if self.end_slot_index > 65535: - print(f"Invalid value for field PhaseList::end_slot_index: {self.end_slot_index} > 65535; the value will be truncated") - self.end_slot_index &= 65535 - _span.extend(int.to_bytes((self.end_slot_index << 0), length=2, byteorder='little')) - return bytes(_span) - - @property - def size(self) -> int: - return 8 - -@dataclass -class SessionSetHybridConfigCmd(SessionConfigCommand): - session_token: int = field(kw_only=True, default=0) - number_of_phases: int = field(kw_only=True, default=0) - update_time: bytearray = field(kw_only=True, default_factory=bytearray) - phase_list: List[PhaseList] = field(kw_only=True, default_factory=list) - - def __post_init__(self): - self.opcode = 12 - self.gid = GroupId.SESSION_CONFIG - self.mt = MessageType.COMMAND - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['SessionSetHybridConfigCmd', bytes]: - if fields['opcode'] != 0xc or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.COMMAND: - raise Exception("Invalid constraint field values") - if len(span) < 5: - raise Exception('Invalid packet size') - value_ = int.from_bytes(span[0:4], byteorder='little') - fields['session_token'] = value_ - fields['number_of_phases'] = span[4] - span = span[5:] - if len(span) < 8: - raise Exception('Invalid packet size') - fields['update_time'] = list(span[:8]) - span = span[8:] - if len(span) % 8 != 0: - raise Exception('Array size is not a multiple of the element size') - phase_list_count = int(len(span) / 8) - phase_list = [] - for n in range(phase_list_count): - phase_list.append(PhaseList.parse_all(span[n * 8:(n + 1) * 8])) - fields['phase_list'] = phase_list - span = bytes() - return SessionSetHybridConfigCmd(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - if self.session_token > 4294967295: - print(f"Invalid value for field SessionSetHybridConfigCmd::session_token: {self.session_token} > 4294967295; the value will be truncated") - self.session_token &= 4294967295 - _span.extend(int.to_bytes((self.session_token << 0), length=4, byteorder='little')) - if self.number_of_phases > 255: - print(f"Invalid value for field SessionSetHybridConfigCmd::number_of_phases: {self.number_of_phases} > 255; the value will be truncated") - self.number_of_phases &= 255 - _span.append((self.number_of_phases << 0)) - _span.extend(self.update_time) - for _elt in self.phase_list: - _span.extend(_elt.serialize()) - return SessionConfigCommand.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return sum([elt.size for elt in self.phase_list]) + 13 - -@dataclass -class SessionSetHybridConfigRsp(SessionConfigResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) - - def __post_init__(self): - self.opcode = 12 - self.gid = GroupId.SESSION_CONFIG - self.mt = MessageType.RESPONSE - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['SessionSetHybridConfigRsp', bytes]: - if fields['opcode'] != 0xc or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.RESPONSE: - raise Exception("Invalid constraint field values") - if len(span) < 1: - raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) - span = span[1:] - return SessionSetHybridConfigRsp(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.append((self.status << 0)) - return SessionConfigResponse.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return 1 - -@dataclass class SessionUpdateControllerMulticastListCmdPayload(Packet): controlees: List[Controlee] = field(kw_only=True, default_factory=list) @@ -3566,28 +3207,28 @@ class SessionUpdateControllerMulticastListCmd_2_0_32_Byte_Payload(Packet): return sum([elt.size for elt in self.controlees]) + 1 @dataclass -class SessionUpdateControllerMulticastListRsp(SessionConfigResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class SessionUpdateControllerMulticastListRsp(SessionConfigPacket): + status: Status = field(kw_only=True, default=Status.OK) def __post_init__(self): - self.opcode = 7 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.RESPONSE + self.oid = SessionConfigOpcodeId.UPDATE_CONTROLLER_MULTICAST_LIST + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionUpdateControllerMulticastListRsp', bytes]: - if fields['opcode'] != 0x7 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.RESPONSE: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != SessionConfigOpcodeId.UPDATE_CONTROLLER_MULTICAST_LIST or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) span = span[1:] return SessionUpdateControllerMulticastListRsp(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() _span.append((self.status << 0)) - return SessionConfigResponse.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: @@ -3597,7 +3238,7 @@ class SessionUpdateControllerMulticastListRsp(SessionConfigResponse): class ControleeStatus(Packet): mac_address: bytearray = field(kw_only=True, default_factory=bytearray) subsession_id: int = field(kw_only=True, default=0) - status: MulticastUpdateStatusCode = field(kw_only=True, default=MulticastUpdateStatusCode.STATUS_OK_MULTICAST_LIST_UPDATE) + status: MulticastUpdateStatus = field(kw_only=True, default=MulticastUpdateStatus.OK_MULTICAST_LIST_UPDATE) def __post_init__(self): pass @@ -3613,7 +3254,7 @@ class ControleeStatus(Packet): raise Exception('Invalid packet size') value_ = int.from_bytes(span[0:4], byteorder='little') fields['subsession_id'] = value_ - fields['status'] = MulticastUpdateStatusCode.from_int(span[4]) + fields['status'] = MulticastUpdateStatus.from_int(span[4]) span = span[5:] return ControleeStatus(**fields), span @@ -3632,19 +3273,19 @@ class ControleeStatus(Packet): return 7 @dataclass -class SessionUpdateControllerMulticastListNtf(SessionConfigNotification): +class SessionUpdateControllerMulticastListNtf(SessionConfigPacket): session_token: int = field(kw_only=True, default=0) remaining_multicast_list_size: int = field(kw_only=True, default=0) controlee_status: List[ControleeStatus] = field(kw_only=True, default_factory=list) def __post_init__(self): - self.opcode = 7 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.NOTIFICATION + self.oid = SessionConfigOpcodeId.UPDATE_CONTROLLER_MULTICAST_LIST + self.gid = GroupId.SESSION_CONFIG @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionUpdateControllerMulticastListNtf', bytes]: - if fields['opcode'] != 0x7 or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.NOTIFICATION: + if fields['mt'] != MessageType.NOTIFICATION or fields['oid'] != SessionConfigOpcodeId.UPDATE_CONTROLLER_MULTICAST_LIST or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 6: raise Exception('Invalid packet size') @@ -3678,25 +3319,25 @@ class SessionUpdateControllerMulticastListNtf(SessionConfigNotification): _span.append((len(self.controlee_status) << 0)) for _elt in self.controlee_status: _span.extend(_elt.serialize()) - return SessionConfigNotification.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return sum([elt.size for elt in self.controlee_status]) + 6 @dataclass -class DataCreditNtf(SessionControlNotification): +class SessionDataCreditNtf(SessionControlPacket): session_token: int = field(kw_only=True, default=0) credit_availability: CreditAvailability = field(kw_only=True, default=CreditAvailability.CREDIT_NOT_AVAILABLE) def __post_init__(self): - self.opcode = 4 - self.gid = GroupId.SESSION_CONTROL self.mt = MessageType.NOTIFICATION + self.oid = SessionControlOpcodeId.DATA_CREDIT + self.gid = GroupId.SESSION_CONTROL @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['DataCreditNtf', bytes]: - if fields['opcode'] != 0x4 or fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.NOTIFICATION: + def parse(fields: dict, span: bytes) -> Tuple['SessionDataCreditNtf', bytes]: + if fields['mt'] != MessageType.NOTIFICATION or fields['oid'] != SessionControlOpcodeId.DATA_CREDIT or fields['gid'] != GroupId.SESSION_CONTROL: raise Exception("Invalid constraint field values") if len(span) < 5: raise Exception('Invalid packet size') @@ -3704,36 +3345,36 @@ class DataCreditNtf(SessionControlNotification): fields['session_token'] = value_ fields['credit_availability'] = CreditAvailability.from_int(span[4]) span = span[5:] - return DataCreditNtf(**fields), span + return SessionDataCreditNtf(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() if self.session_token > 4294967295: - print(f"Invalid value for field DataCreditNtf::session_token: {self.session_token} > 4294967295; the value will be truncated") + print(f"Invalid value for field SessionDataCreditNtf::session_token: {self.session_token} > 4294967295; the value will be truncated") self.session_token &= 4294967295 _span.extend(int.to_bytes((self.session_token << 0), length=4, byteorder='little')) _span.append((self.credit_availability << 0)) - return SessionControlNotification.serialize(self, payload = bytes(_span)) + return SessionControlPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 5 @dataclass -class DataTransferStatusNtf(SessionControlNotification): +class SessionDataTransferStatusNtf(SessionControlPacket): session_token: int = field(kw_only=True, default=0) uci_sequence_number: int = field(kw_only=True, default=0) status: DataTransferNtfStatusCode = field(kw_only=True, default=DataTransferNtfStatusCode.UCI_DATA_TRANSFER_STATUS_REPETITION_OK) tx_count: int = field(kw_only=True, default=0) def __post_init__(self): - self.opcode = 5 - self.gid = GroupId.SESSION_CONTROL self.mt = MessageType.NOTIFICATION + self.oid = SessionControlOpcodeId.DATA_TRANSFER_STATUS + self.gid = GroupId.SESSION_CONTROL @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['DataTransferStatusNtf', bytes]: - if fields['opcode'] != 0x5 or fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.NOTIFICATION: + def parse(fields: dict, span: bytes) -> Tuple['SessionDataTransferStatusNtf', bytes]: + if fields['mt'] != MessageType.NOTIFICATION or fields['oid'] != SessionControlOpcodeId.DATA_TRANSFER_STATUS or fields['gid'] != GroupId.SESSION_CONTROL: raise Exception("Invalid constraint field values") if len(span) < 7: raise Exception('Invalid packet size') @@ -3743,74 +3384,74 @@ class DataTransferStatusNtf(SessionControlNotification): fields['status'] = DataTransferNtfStatusCode.from_int(span[5]) fields['tx_count'] = span[6] span = span[7:] - return DataTransferStatusNtf(**fields), span + return SessionDataTransferStatusNtf(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() if self.session_token > 4294967295: - print(f"Invalid value for field DataTransferStatusNtf::session_token: {self.session_token} > 4294967295; the value will be truncated") + print(f"Invalid value for field SessionDataTransferStatusNtf::session_token: {self.session_token} > 4294967295; the value will be truncated") self.session_token &= 4294967295 _span.extend(int.to_bytes((self.session_token << 0), length=4, byteorder='little')) if self.uci_sequence_number > 255: - print(f"Invalid value for field DataTransferStatusNtf::uci_sequence_number: {self.uci_sequence_number} > 255; the value will be truncated") + print(f"Invalid value for field SessionDataTransferStatusNtf::uci_sequence_number: {self.uci_sequence_number} > 255; the value will be truncated") self.uci_sequence_number &= 255 _span.append((self.uci_sequence_number << 0)) _span.append((self.status << 0)) if self.tx_count > 255: - print(f"Invalid value for field DataTransferStatusNtf::tx_count: {self.tx_count} > 255; the value will be truncated") + print(f"Invalid value for field SessionDataTransferStatusNtf::tx_count: {self.tx_count} > 255; the value will be truncated") self.tx_count &= 255 _span.append((self.tx_count << 0)) - return SessionControlNotification.serialize(self, payload = bytes(_span)) + return SessionControlPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 7 @dataclass -class SessionQueryMaxDataSizeCmd(SessionConfigCommand): +class SessionQueryMaxDataSizeInRangingCmd(SessionConfigPacket): session_token: int = field(kw_only=True, default=0) def __post_init__(self): - self.opcode = 11 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.COMMAND + self.oid = SessionConfigOpcodeId.QUERY_DATA_SIZE_IN_RANGING + self.gid = GroupId.SESSION_CONFIG @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['SessionQueryMaxDataSizeCmd', bytes]: - if fields['opcode'] != 0xb or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.COMMAND: + def parse(fields: dict, span: bytes) -> Tuple['SessionQueryMaxDataSizeInRangingCmd', bytes]: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != SessionConfigOpcodeId.QUERY_DATA_SIZE_IN_RANGING or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 4: raise Exception('Invalid packet size') value_ = int.from_bytes(span[0:4], byteorder='little') fields['session_token'] = value_ span = span[4:] - return SessionQueryMaxDataSizeCmd(**fields), span + return SessionQueryMaxDataSizeInRangingCmd(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() if self.session_token > 4294967295: - print(f"Invalid value for field SessionQueryMaxDataSizeCmd::session_token: {self.session_token} > 4294967295; the value will be truncated") + print(f"Invalid value for field SessionQueryMaxDataSizeInRangingCmd::session_token: {self.session_token} > 4294967295; the value will be truncated") self.session_token &= 4294967295 _span.extend(int.to_bytes((self.session_token << 0), length=4, byteorder='little')) - return SessionConfigCommand.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 4 @dataclass -class SessionQueryMaxDataSizeRsp(SessionConfigResponse): +class SessionQueryMaxDataSizeInRangingRsp(SessionConfigPacket): session_token: int = field(kw_only=True, default=0) max_data_size: int = field(kw_only=True, default=0) def __post_init__(self): - self.opcode = 11 - self.gid = GroupId.SESSION_CONFIG self.mt = MessageType.RESPONSE + self.oid = SessionConfigOpcodeId.QUERY_DATA_SIZE_IN_RANGING + self.gid = GroupId.SESSION_CONFIG @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['SessionQueryMaxDataSizeRsp', bytes]: - if fields['opcode'] != 0xb or fields['gid'] != GroupId.SESSION_CONFIG or fields['mt'] != MessageType.RESPONSE: + def parse(fields: dict, span: bytes) -> Tuple['SessionQueryMaxDataSizeInRangingRsp', bytes]: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != SessionConfigOpcodeId.QUERY_DATA_SIZE_IN_RANGING or fields['gid'] != GroupId.SESSION_CONFIG: raise Exception("Invalid constraint field values") if len(span) < 6: raise Exception('Invalid packet size') @@ -3819,70 +3460,79 @@ class SessionQueryMaxDataSizeRsp(SessionConfigResponse): value_ = int.from_bytes(span[4:6], byteorder='little') fields['max_data_size'] = value_ span = span[6:] - return SessionQueryMaxDataSizeRsp(**fields), span + return SessionQueryMaxDataSizeInRangingRsp(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() if self.session_token > 4294967295: - print(f"Invalid value for field SessionQueryMaxDataSizeRsp::session_token: {self.session_token} > 4294967295; the value will be truncated") + print(f"Invalid value for field SessionQueryMaxDataSizeInRangingRsp::session_token: {self.session_token} > 4294967295; the value will be truncated") self.session_token &= 4294967295 _span.extend(int.to_bytes((self.session_token << 0), length=4, byteorder='little')) if self.max_data_size > 65535: - print(f"Invalid value for field SessionQueryMaxDataSizeRsp::max_data_size: {self.max_data_size} > 65535; the value will be truncated") + print(f"Invalid value for field SessionQueryMaxDataSizeInRangingRsp::max_data_size: {self.max_data_size} > 65535; the value will be truncated") self.max_data_size &= 65535 _span.extend(int.to_bytes((self.max_data_size << 0), length=2, byteorder='little')) - return SessionConfigResponse.serialize(self, payload = bytes(_span)) + return SessionConfigPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 6 @dataclass -class SessionStartCmd(SessionControlCommand): - +class SessionStartCmd(SessionControlPacket): + session_id: int = field(kw_only=True, default=0) def __post_init__(self): - self.opcode = 0 - self.gid = GroupId.SESSION_CONTROL self.mt = MessageType.COMMAND + self.oid = SessionControlOpcodeId.START + self.gid = GroupId.SESSION_CONTROL @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionStartCmd', bytes]: - if fields['opcode'] != 0x0 or fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.COMMAND: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != SessionControlOpcodeId.START or fields['gid'] != GroupId.SESSION_CONTROL: raise Exception("Invalid constraint field values") + if len(span) < 4: + raise Exception('Invalid packet size') + value_ = int.from_bytes(span[0:4], byteorder='little') + fields['session_id'] = value_ + span = span[4:] return SessionStartCmd(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() - return SessionControlCommand.serialize(self, payload = bytes(_span)) + if self.session_id > 4294967295: + print(f"Invalid value for field SessionStartCmd::session_id: {self.session_id} > 4294967295; the value will be truncated") + self.session_id &= 4294967295 + _span.extend(int.to_bytes((self.session_id << 0), length=4, byteorder='little')) + return SessionControlPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: - return 0 + return 4 @dataclass -class SessionStartRsp(SessionControlResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class SessionStartRsp(SessionControlPacket): + status: Status = field(kw_only=True, default=Status.OK) def __post_init__(self): - self.opcode = 0 - self.gid = GroupId.SESSION_CONTROL self.mt = MessageType.RESPONSE + self.oid = SessionControlOpcodeId.START + self.gid = GroupId.SESSION_CONTROL @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionStartRsp', bytes]: - if fields['opcode'] != 0x0 or fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.RESPONSE: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != SessionControlOpcodeId.START or fields['gid'] != GroupId.SESSION_CONTROL: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) span = span[1:] return SessionStartRsp(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() _span.append((self.status << 0)) - return SessionControlResponse.serialize(self, payload = bytes(_span)) + return SessionControlPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: @@ -3891,7 +3541,7 @@ class SessionStartRsp(SessionControlResponse): @dataclass class ShortAddressTwoWayRangingMeasurement(Packet): mac_address: int = field(kw_only=True, default=0) - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) + status: Status = field(kw_only=True, default=Status.OK) nlos: int = field(kw_only=True, default=0) distance: int = field(kw_only=True, default=0) aoa_azimuth: int = field(kw_only=True, default=0) @@ -3915,7 +3565,7 @@ class ShortAddressTwoWayRangingMeasurement(Packet): raise Exception('Invalid packet size') value_ = int.from_bytes(span[0:2], byteorder='little') fields['mac_address'] = value_ - fields['status'] = StatusCode.from_int(span[2]) + fields['status'] = Status.from_int(span[2]) fields['nlos'] = span[3] value_ = int.from_bytes(span[4:6], byteorder='little') fields['distance'] = value_ @@ -4004,7 +3654,7 @@ class ShortAddressTwoWayRangingMeasurement(Packet): @dataclass class ExtendedAddressTwoWayRangingMeasurement(Packet): mac_address: int = field(kw_only=True, default=0) - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) + status: Status = field(kw_only=True, default=Status.OK) nlos: int = field(kw_only=True, default=0) distance: int = field(kw_only=True, default=0) aoa_azimuth: int = field(kw_only=True, default=0) @@ -4028,7 +3678,7 @@ class ExtendedAddressTwoWayRangingMeasurement(Packet): raise Exception('Invalid packet size') value_ = int.from_bytes(span[0:8], byteorder='little') fields['mac_address'] = value_ - fields['status'] = StatusCode.from_int(span[8]) + fields['status'] = Status.from_int(span[8]) fields['nlos'] = span[9] value_ = int.from_bytes(span[10:12], byteorder='little') fields['distance'] = value_ @@ -4115,7 +3765,7 @@ class ExtendedAddressTwoWayRangingMeasurement(Packet): @dataclass class ShortAddressOwrAoaRangingMeasurement(Packet): mac_address: int = field(kw_only=True, default=0) - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) + status: Status = field(kw_only=True, default=Status.OK) nlos: int = field(kw_only=True, default=0) frame_sequence_number: int = field(kw_only=True, default=0) block_index: int = field(kw_only=True, default=0) @@ -4134,7 +3784,7 @@ class ShortAddressOwrAoaRangingMeasurement(Packet): raise Exception('Invalid packet size') value_ = int.from_bytes(span[0:2], byteorder='little') fields['mac_address'] = value_ - fields['status'] = StatusCode.from_int(span[2]) + fields['status'] = Status.from_int(span[2]) fields['nlos'] = span[3] fields['frame_sequence_number'] = span[4] value_ = int.from_bytes(span[5:7], byteorder='little') @@ -4192,7 +3842,7 @@ class ShortAddressOwrAoaRangingMeasurement(Packet): @dataclass class ExtendedAddressOwrAoaRangingMeasurement(Packet): mac_address: int = field(kw_only=True, default=0) - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) + status: Status = field(kw_only=True, default=Status.OK) nlos: int = field(kw_only=True, default=0) frame_sequence_number: int = field(kw_only=True, default=0) block_index: int = field(kw_only=True, default=0) @@ -4211,7 +3861,7 @@ class ExtendedAddressOwrAoaRangingMeasurement(Packet): raise Exception('Invalid packet size') value_ = int.from_bytes(span[0:8], byteorder='little') fields['mac_address'] = value_ - fields['status'] = StatusCode.from_int(span[8]) + fields['status'] = Status.from_int(span[8]) fields['nlos'] = span[9] fields['frame_sequence_number'] = span[10] value_ = int.from_bytes(span[11:13], byteorder='little') @@ -4281,7 +3931,7 @@ class RangingMeasurementType(enum.IntEnum): @dataclass -class SessionInfoNtf(SessionControlNotification): +class SessionInfoNtf(SessionControlPacket): sequence_number: int = field(kw_only=True, default=0) session_token: int = field(kw_only=True, default=0) rcr_indicator: int = field(kw_only=True, default=0) @@ -4290,13 +3940,13 @@ class SessionInfoNtf(SessionControlNotification): mac_address_indicator: MacAddressIndicator = field(kw_only=True, default=MacAddressIndicator.SHORT_ADDRESS) def __post_init__(self): - self.opcode = 0 - self.gid = GroupId.SESSION_CONTROL self.mt = MessageType.NOTIFICATION + self.oid = SessionControlOpcodeId.START + self.gid = GroupId.SESSION_CONTROL @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionInfoNtf', bytes]: - if fields['opcode'] != 0x0 or fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.NOTIFICATION: + if fields['mt'] != MessageType.NOTIFICATION or fields['oid'] != SessionControlOpcodeId.START or fields['gid'] != GroupId.SESSION_CONTROL: raise Exception("Invalid constraint field values") if len(span) < 24: raise Exception('Invalid packet size') @@ -4363,7 +4013,7 @@ class SessionInfoNtf(SessionControlNotification): _span.append((self.mac_address_indicator << 0)) _span.extend([0] * 8) _span.extend(payload or self.payload or []) - return SessionControlNotification.serialize(self, payload = bytes(_span)) + return SessionControlPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: @@ -4377,13 +4027,13 @@ class ShortMacTwoWaySessionInfoNtf(SessionInfoNtf): def __post_init__(self): self.ranging_measurement_type = RangingMeasurementType.TWO_WAY self.mac_address_indicator = MacAddressIndicator.SHORT_ADDRESS - self.opcode = 0 - self.gid = GroupId.SESSION_CONTROL self.mt = MessageType.NOTIFICATION + self.oid = SessionControlOpcodeId.START + self.gid = GroupId.SESSION_CONTROL @staticmethod def parse(fields: dict, span: bytes) -> Tuple['ShortMacTwoWaySessionInfoNtf', bytes]: - if fields['ranging_measurement_type'] != RangingMeasurementType.TWO_WAY or fields['mac_address_indicator'] != MacAddressIndicator.SHORT_ADDRESS or fields['opcode'] != 0x0 or fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.NOTIFICATION: + if fields['ranging_measurement_type'] != RangingMeasurementType.TWO_WAY or fields['mac_address_indicator'] != MacAddressIndicator.SHORT_ADDRESS or fields['mt'] != MessageType.NOTIFICATION or fields['oid'] != SessionControlOpcodeId.START or fields['gid'] != GroupId.SESSION_CONTROL: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') @@ -4426,13 +4076,13 @@ class ExtendedMacTwoWaySessionInfoNtf(SessionInfoNtf): def __post_init__(self): self.ranging_measurement_type = RangingMeasurementType.TWO_WAY self.mac_address_indicator = MacAddressIndicator.EXTENDED_ADDRESS - self.opcode = 0 - self.gid = GroupId.SESSION_CONTROL self.mt = MessageType.NOTIFICATION + self.oid = SessionControlOpcodeId.START + self.gid = GroupId.SESSION_CONTROL @staticmethod def parse(fields: dict, span: bytes) -> Tuple['ExtendedMacTwoWaySessionInfoNtf', bytes]: - if fields['ranging_measurement_type'] != RangingMeasurementType.TWO_WAY or fields['mac_address_indicator'] != MacAddressIndicator.EXTENDED_ADDRESS or fields['opcode'] != 0x0 or fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.NOTIFICATION: + if fields['ranging_measurement_type'] != RangingMeasurementType.TWO_WAY or fields['mac_address_indicator'] != MacAddressIndicator.EXTENDED_ADDRESS or fields['mt'] != MessageType.NOTIFICATION or fields['oid'] != SessionControlOpcodeId.START or fields['gid'] != GroupId.SESSION_CONTROL: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') @@ -4475,13 +4125,13 @@ class ShortMacDlTDoASessionInfoNtf(SessionInfoNtf): def __post_init__(self): self.ranging_measurement_type = RangingMeasurementType.DL_TDOA self.mac_address_indicator = MacAddressIndicator.SHORT_ADDRESS - self.opcode = 0 - self.gid = GroupId.SESSION_CONTROL self.mt = MessageType.NOTIFICATION + self.oid = SessionControlOpcodeId.START + self.gid = GroupId.SESSION_CONTROL @staticmethod def parse(fields: dict, span: bytes) -> Tuple['ShortMacDlTDoASessionInfoNtf', bytes]: - if fields['ranging_measurement_type'] != RangingMeasurementType.DL_TDOA or fields['mac_address_indicator'] != MacAddressIndicator.SHORT_ADDRESS or fields['opcode'] != 0x0 or fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.NOTIFICATION: + if fields['ranging_measurement_type'] != RangingMeasurementType.DL_TDOA or fields['mac_address_indicator'] != MacAddressIndicator.SHORT_ADDRESS or fields['mt'] != MessageType.NOTIFICATION or fields['oid'] != SessionControlOpcodeId.START or fields['gid'] != GroupId.SESSION_CONTROL: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') @@ -4512,13 +4162,13 @@ class ExtendedMacDlTDoASessionInfoNtf(SessionInfoNtf): def __post_init__(self): self.ranging_measurement_type = RangingMeasurementType.DL_TDOA self.mac_address_indicator = MacAddressIndicator.EXTENDED_ADDRESS - self.opcode = 0 - self.gid = GroupId.SESSION_CONTROL self.mt = MessageType.NOTIFICATION + self.oid = SessionControlOpcodeId.START + self.gid = GroupId.SESSION_CONTROL @staticmethod def parse(fields: dict, span: bytes) -> Tuple['ExtendedMacDlTDoASessionInfoNtf', bytes]: - if fields['ranging_measurement_type'] != RangingMeasurementType.DL_TDOA or fields['mac_address_indicator'] != MacAddressIndicator.EXTENDED_ADDRESS or fields['opcode'] != 0x0 or fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.NOTIFICATION: + if fields['ranging_measurement_type'] != RangingMeasurementType.DL_TDOA or fields['mac_address_indicator'] != MacAddressIndicator.EXTENDED_ADDRESS or fields['mt'] != MessageType.NOTIFICATION or fields['oid'] != SessionControlOpcodeId.START or fields['gid'] != GroupId.SESSION_CONTROL: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') @@ -4549,13 +4199,13 @@ class ShortMacOwrAoaSessionInfoNtf(SessionInfoNtf): def __post_init__(self): self.ranging_measurement_type = RangingMeasurementType.OWR_AOA self.mac_address_indicator = MacAddressIndicator.SHORT_ADDRESS - self.opcode = 0 - self.gid = GroupId.SESSION_CONTROL self.mt = MessageType.NOTIFICATION + self.oid = SessionControlOpcodeId.START + self.gid = GroupId.SESSION_CONTROL @staticmethod def parse(fields: dict, span: bytes) -> Tuple['ShortMacOwrAoaSessionInfoNtf', bytes]: - if fields['ranging_measurement_type'] != RangingMeasurementType.OWR_AOA or fields['mac_address_indicator'] != MacAddressIndicator.SHORT_ADDRESS or fields['opcode'] != 0x0 or fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.NOTIFICATION: + if fields['ranging_measurement_type'] != RangingMeasurementType.OWR_AOA or fields['mac_address_indicator'] != MacAddressIndicator.SHORT_ADDRESS or fields['mt'] != MessageType.NOTIFICATION or fields['oid'] != SessionControlOpcodeId.START or fields['gid'] != GroupId.SESSION_CONTROL: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') @@ -4598,13 +4248,13 @@ class ExtendedMacOwrAoaSessionInfoNtf(SessionInfoNtf): def __post_init__(self): self.ranging_measurement_type = RangingMeasurementType.OWR_AOA self.mac_address_indicator = MacAddressIndicator.EXTENDED_ADDRESS - self.opcode = 0 - self.gid = GroupId.SESSION_CONTROL self.mt = MessageType.NOTIFICATION + self.oid = SessionControlOpcodeId.START + self.gid = GroupId.SESSION_CONTROL @staticmethod def parse(fields: dict, span: bytes) -> Tuple['ExtendedMacOwrAoaSessionInfoNtf', bytes]: - if fields['ranging_measurement_type'] != RangingMeasurementType.OWR_AOA or fields['mac_address_indicator'] != MacAddressIndicator.EXTENDED_ADDRESS or fields['opcode'] != 0x0 or fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.NOTIFICATION: + if fields['ranging_measurement_type'] != RangingMeasurementType.OWR_AOA or fields['mac_address_indicator'] != MacAddressIndicator.EXTENDED_ADDRESS or fields['mt'] != MessageType.NOTIFICATION or fields['oid'] != SessionControlOpcodeId.START or fields['gid'] != GroupId.SESSION_CONTROL: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') @@ -4640,96 +4290,114 @@ class ExtendedMacOwrAoaSessionInfoNtf(SessionInfoNtf): ) @dataclass -class SessionStopCmd(SessionControlCommand): - +class SessionStopCmd(SessionControlPacket): + session_id: int = field(kw_only=True, default=0) def __post_init__(self): - self.opcode = 1 - self.gid = GroupId.SESSION_CONTROL self.mt = MessageType.COMMAND + self.oid = SessionControlOpcodeId.STOP + self.gid = GroupId.SESSION_CONTROL @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionStopCmd', bytes]: - if fields['opcode'] != 0x1 or fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.COMMAND: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != SessionControlOpcodeId.STOP or fields['gid'] != GroupId.SESSION_CONTROL: raise Exception("Invalid constraint field values") + if len(span) < 4: + raise Exception('Invalid packet size') + value_ = int.from_bytes(span[0:4], byteorder='little') + fields['session_id'] = value_ + span = span[4:] return SessionStopCmd(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() - return SessionControlCommand.serialize(self, payload = bytes(_span)) + if self.session_id > 4294967295: + print(f"Invalid value for field SessionStopCmd::session_id: {self.session_id} > 4294967295; the value will be truncated") + self.session_id &= 4294967295 + _span.extend(int.to_bytes((self.session_id << 0), length=4, byteorder='little')) + return SessionControlPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: - return 0 + return 4 @dataclass -class SessionStopRsp(SessionControlResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class SessionStopRsp(SessionControlPacket): + status: Status = field(kw_only=True, default=Status.OK) def __post_init__(self): - self.opcode = 1 - self.gid = GroupId.SESSION_CONTROL self.mt = MessageType.RESPONSE + self.oid = SessionControlOpcodeId.STOP + self.gid = GroupId.SESSION_CONTROL @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionStopRsp', bytes]: - if fields['opcode'] != 0x1 or fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.RESPONSE: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != SessionControlOpcodeId.STOP or fields['gid'] != GroupId.SESSION_CONTROL: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) span = span[1:] return SessionStopRsp(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() _span.append((self.status << 0)) - return SessionControlResponse.serialize(self, payload = bytes(_span)) + return SessionControlPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 1 @dataclass -class SessionGetRangingCountCmd(SessionControlCommand): - +class SessionGetRangingCountCmd(SessionControlPacket): + session_id: int = field(kw_only=True, default=0) def __post_init__(self): - self.opcode = 3 - self.gid = GroupId.SESSION_CONTROL self.mt = MessageType.COMMAND + self.oid = SessionControlOpcodeId.GET_RANGING_COUNT + self.gid = GroupId.SESSION_CONTROL @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionGetRangingCountCmd', bytes]: - if fields['opcode'] != 0x3 or fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.COMMAND: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != SessionControlOpcodeId.GET_RANGING_COUNT or fields['gid'] != GroupId.SESSION_CONTROL: raise Exception("Invalid constraint field values") + if len(span) < 4: + raise Exception('Invalid packet size') + value_ = int.from_bytes(span[0:4], byteorder='little') + fields['session_id'] = value_ + span = span[4:] return SessionGetRangingCountCmd(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() - return SessionControlCommand.serialize(self, payload = bytes(_span)) + if self.session_id > 4294967295: + print(f"Invalid value for field SessionGetRangingCountCmd::session_id: {self.session_id} > 4294967295; the value will be truncated") + self.session_id &= 4294967295 + _span.extend(int.to_bytes((self.session_id << 0), length=4, byteorder='little')) + return SessionControlPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: - return 0 + return 4 @dataclass -class SessionGetRangingCountRsp(SessionControlResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class SessionGetRangingCountRsp(SessionControlPacket): + status: Status = field(kw_only=True, default=Status.OK) count: int = field(kw_only=True, default=0) def __post_init__(self): - self.opcode = 3 - self.gid = GroupId.SESSION_CONTROL self.mt = MessageType.RESPONSE + self.oid = SessionControlOpcodeId.GET_RANGING_COUNT + self.gid = GroupId.SESSION_CONTROL @staticmethod def parse(fields: dict, span: bytes) -> Tuple['SessionGetRangingCountRsp', bytes]: - if fields['opcode'] != 0x3 or fields['gid'] != GroupId.SESSION_CONTROL or fields['mt'] != MessageType.RESPONSE: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != SessionControlOpcodeId.GET_RANGING_COUNT or fields['gid'] != GroupId.SESSION_CONTROL: raise Exception("Invalid constraint field values") if len(span) < 5: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) value_ = int.from_bytes(span[1:5], byteorder='little') fields['count'] = value_ span = span[5:] @@ -4742,30 +4410,30 @@ class SessionGetRangingCountRsp(SessionControlResponse): print(f"Invalid value for field SessionGetRangingCountRsp::count: {self.count} > 4294967295; the value will be truncated") self.count &= 4294967295 _span.extend(int.to_bytes((self.count << 0), length=4, byteorder='little')) - return SessionControlResponse.serialize(self, payload = bytes(_span)) + return SessionControlPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 5 @dataclass -class AndroidGetPowerStatsCmd(AndroidCommand): +class AndroidGetPowerStatsCmd(AndroidPacket): def __post_init__(self): - self.opcode = 0 - self.gid = GroupId.VENDOR_ANDROID self.mt = MessageType.COMMAND + self.oid = AndroidOpcodeId.GET_POWER_STATS + self.gid = GroupId.VENDOR_ANDROID @staticmethod def parse(fields: dict, span: bytes) -> Tuple['AndroidGetPowerStatsCmd', bytes]: - if fields['opcode'] != 0x0 or fields['gid'] != GroupId.VENDOR_ANDROID or fields['mt'] != MessageType.COMMAND: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != AndroidOpcodeId.GET_POWER_STATS or fields['gid'] != GroupId.VENDOR_ANDROID: raise Exception("Invalid constraint field values") return AndroidGetPowerStatsCmd(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() - return AndroidCommand.serialize(self, payload = bytes(_span)) + return AndroidPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: @@ -4773,7 +4441,7 @@ class AndroidGetPowerStatsCmd(AndroidCommand): @dataclass class PowerStats(Packet): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) + status: Status = field(kw_only=True, default=Status.OK) idle_time_ms: int = field(kw_only=True, default=0) tx_time_ms: int = field(kw_only=True, default=0) rx_time_ms: int = field(kw_only=True, default=0) @@ -4787,7 +4455,7 @@ class PowerStats(Packet): fields = {'payload': None} if len(span) < 17: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) value_ = int.from_bytes(span[1:5], byteorder='little') fields['idle_time_ms'] = value_ value_ = int.from_bytes(span[5:9], byteorder='little') @@ -4825,17 +4493,17 @@ class PowerStats(Packet): return 17 @dataclass -class AndroidGetPowerStatsRsp(AndroidResponse): +class AndroidGetPowerStatsRsp(AndroidPacket): stats: PowerStats = field(kw_only=True, default_factory=PowerStats) def __post_init__(self): - self.opcode = 0 - self.gid = GroupId.VENDOR_ANDROID self.mt = MessageType.RESPONSE + self.oid = AndroidOpcodeId.GET_POWER_STATS + self.gid = GroupId.VENDOR_ANDROID @staticmethod def parse(fields: dict, span: bytes) -> Tuple['AndroidGetPowerStatsRsp', bytes]: - if fields['opcode'] != 0x0 or fields['gid'] != GroupId.VENDOR_ANDROID or fields['mt'] != MessageType.RESPONSE: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != AndroidOpcodeId.GET_POWER_STATS or fields['gid'] != GroupId.VENDOR_ANDROID: raise Exception("Invalid constraint field values") if len(span) < 17: raise Exception('Invalid packet size') @@ -4846,24 +4514,24 @@ class AndroidGetPowerStatsRsp(AndroidResponse): def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() _span.extend(self.stats.serialize()) - return AndroidResponse.serialize(self, payload = bytes(_span)) + return AndroidPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 17 @dataclass -class AndroidSetCountryCodeCmd(AndroidCommand): +class AndroidSetCountryCodeCmd(AndroidPacket): country_code: bytearray = field(kw_only=True, default_factory=bytearray) def __post_init__(self): - self.opcode = 1 - self.gid = GroupId.VENDOR_ANDROID self.mt = MessageType.COMMAND + self.oid = AndroidOpcodeId.SET_COUNTRY_CODE + self.gid = GroupId.VENDOR_ANDROID @staticmethod def parse(fields: dict, span: bytes) -> Tuple['AndroidSetCountryCodeCmd', bytes]: - if fields['opcode'] != 0x1 or fields['gid'] != GroupId.VENDOR_ANDROID or fields['mt'] != MessageType.COMMAND: + if fields['mt'] != MessageType.COMMAND or fields['oid'] != AndroidOpcodeId.SET_COUNTRY_CODE or fields['gid'] != GroupId.VENDOR_ANDROID: raise Exception("Invalid constraint field values") if len(span) < 2: raise Exception('Invalid packet size') @@ -4874,40 +4542,53 @@ class AndroidSetCountryCodeCmd(AndroidCommand): def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() _span.extend(self.country_code) - return AndroidCommand.serialize(self, payload = bytes(_span)) + return AndroidPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return 2 @dataclass -class AndroidSetCountryCodeRsp(AndroidResponse): - status: StatusCode = field(kw_only=True, default=StatusCode.UCI_STATUS_OK) +class AndroidSetCountryCodeRsp(AndroidPacket): + status: Status = field(kw_only=True, default=Status.OK) def __post_init__(self): - self.opcode = 1 - self.gid = GroupId.VENDOR_ANDROID self.mt = MessageType.RESPONSE + self.oid = AndroidOpcodeId.SET_COUNTRY_CODE + self.gid = GroupId.VENDOR_ANDROID @staticmethod def parse(fields: dict, span: bytes) -> Tuple['AndroidSetCountryCodeRsp', bytes]: - if fields['opcode'] != 0x1 or fields['gid'] != GroupId.VENDOR_ANDROID or fields['mt'] != MessageType.RESPONSE: + if fields['mt'] != MessageType.RESPONSE or fields['oid'] != AndroidOpcodeId.SET_COUNTRY_CODE or fields['gid'] != GroupId.VENDOR_ANDROID: raise Exception("Invalid constraint field values") if len(span) < 1: raise Exception('Invalid packet size') - fields['status'] = StatusCode.from_int(span[0]) + fields['status'] = Status.from_int(span[0]) span = span[1:] return AndroidSetCountryCodeRsp(**fields), span def serialize(self, payload: bytes = None) -> bytes: _span = bytearray() _span.append((self.status << 0)) - return AndroidResponse.serialize(self, payload = bytes(_span)) + return AndroidPacket.serialize(self, payload = bytes(_span)) @property 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) @@ -5277,19 +4958,19 @@ class FrameReport(Packet): return sum([elt.size for elt in self.frame_report_tlvs]) + 4 @dataclass -class AndroidRangeDiagnosticsNtf(AndroidNotification): +class AndroidRangeDiagnosticsNtf(AndroidPacket): session_token: int = field(kw_only=True, default=0) sequence_number: int = field(kw_only=True, default=0) frame_reports: List[FrameReport] = field(kw_only=True, default_factory=list) def __post_init__(self): - self.opcode = 2 - self.gid = GroupId.VENDOR_ANDROID self.mt = MessageType.NOTIFICATION + self.oid = AndroidOpcodeId.FIRA_RANGE_DIAGNOSTICS + self.gid = GroupId.VENDOR_ANDROID @staticmethod def parse(fields: dict, span: bytes) -> Tuple['AndroidRangeDiagnosticsNtf', bytes]: - if fields['opcode'] != 0x2 or fields['gid'] != GroupId.VENDOR_ANDROID or fields['mt'] != MessageType.NOTIFICATION: + if fields['mt'] != MessageType.NOTIFICATION or fields['oid'] != AndroidOpcodeId.FIRA_RANGE_DIAGNOSTICS or fields['gid'] != GroupId.VENDOR_ANDROID: raise Exception("Invalid constraint field values") if len(span) < 9: raise Exception('Invalid packet size') @@ -5322,424 +5003,8 @@ class AndroidRangeDiagnosticsNtf(AndroidNotification): _span.append((len(self.frame_reports) << 0)) for _elt in self.frame_reports: _span.extend(_elt.serialize()) - return AndroidNotification.serialize(self, payload = bytes(_span)) + return AndroidPacket.serialize(self, payload = bytes(_span)) @property def size(self) -> int: return sum([elt.size for elt in self.frame_reports]) + 9 - -@dataclass -class UciVendor_9_Command(UciCommand): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_RESERVED_9 - self.mt = MessageType.COMMAND - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciVendor_9_Command', bytes]: - if fields['gid'] != GroupId.VENDOR_RESERVED_9 or fields['mt'] != MessageType.COMMAND: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - return UciVendor_9_Command(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciCommand.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class UciVendor_A_Command(UciCommand): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_RESERVED_A - self.mt = MessageType.COMMAND - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciVendor_A_Command', bytes]: - if fields['gid'] != GroupId.VENDOR_RESERVED_A or fields['mt'] != MessageType.COMMAND: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - return UciVendor_A_Command(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciCommand.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class UciVendor_B_Command(UciCommand): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_RESERVED_B - self.mt = MessageType.COMMAND - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciVendor_B_Command', bytes]: - if fields['gid'] != GroupId.VENDOR_RESERVED_B or fields['mt'] != MessageType.COMMAND: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - return UciVendor_B_Command(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciCommand.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class UciVendor_E_Command(UciCommand): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_RESERVED_E - self.mt = MessageType.COMMAND - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciVendor_E_Command', bytes]: - if fields['gid'] != GroupId.VENDOR_RESERVED_E or fields['mt'] != MessageType.COMMAND: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - return UciVendor_E_Command(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciCommand.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class UciVendor_F_Command(UciCommand): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_RESERVED_F - self.mt = MessageType.COMMAND - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciVendor_F_Command', bytes]: - if fields['gid'] != GroupId.VENDOR_RESERVED_F or fields['mt'] != MessageType.COMMAND: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - return UciVendor_F_Command(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciCommand.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class UciVendor_9_Response(UciResponse): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_RESERVED_9 - self.mt = MessageType.RESPONSE - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciVendor_9_Response', bytes]: - if fields['gid'] != GroupId.VENDOR_RESERVED_9 or fields['mt'] != MessageType.RESPONSE: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - return UciVendor_9_Response(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciResponse.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class UciVendor_A_Response(UciResponse): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_RESERVED_A - self.mt = MessageType.RESPONSE - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciVendor_A_Response', bytes]: - if fields['gid'] != GroupId.VENDOR_RESERVED_A or fields['mt'] != MessageType.RESPONSE: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - return UciVendor_A_Response(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciResponse.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class UciVendor_B_Response(UciResponse): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_RESERVED_B - self.mt = MessageType.RESPONSE - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciVendor_B_Response', bytes]: - if fields['gid'] != GroupId.VENDOR_RESERVED_B or fields['mt'] != MessageType.RESPONSE: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - return UciVendor_B_Response(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciResponse.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class UciVendor_E_Response(UciResponse): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_RESERVED_E - self.mt = MessageType.RESPONSE - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciVendor_E_Response', bytes]: - if fields['gid'] != GroupId.VENDOR_RESERVED_E or fields['mt'] != MessageType.RESPONSE: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - return UciVendor_E_Response(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciResponse.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class UciVendor_F_Response(UciResponse): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_RESERVED_F - self.mt = MessageType.RESPONSE - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciVendor_F_Response', bytes]: - if fields['gid'] != GroupId.VENDOR_RESERVED_F or fields['mt'] != MessageType.RESPONSE: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - return UciVendor_F_Response(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciResponse.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class UciVendor_9_Notification(UciNotification): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_RESERVED_9 - self.mt = MessageType.NOTIFICATION - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciVendor_9_Notification', bytes]: - if fields['gid'] != GroupId.VENDOR_RESERVED_9 or fields['mt'] != MessageType.NOTIFICATION: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - return UciVendor_9_Notification(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciNotification.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class UciVendor_A_Notification(UciNotification): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_RESERVED_A - self.mt = MessageType.NOTIFICATION - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciVendor_A_Notification', bytes]: - if fields['gid'] != GroupId.VENDOR_RESERVED_A or fields['mt'] != MessageType.NOTIFICATION: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - return UciVendor_A_Notification(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciNotification.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class UciVendor_B_Notification(UciNotification): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_RESERVED_B - self.mt = MessageType.NOTIFICATION - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciVendor_B_Notification', bytes]: - if fields['gid'] != GroupId.VENDOR_RESERVED_B or fields['mt'] != MessageType.NOTIFICATION: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - return UciVendor_B_Notification(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciNotification.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class UciVendor_E_Notification(UciNotification): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_RESERVED_E - self.mt = MessageType.NOTIFICATION - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciVendor_E_Notification', bytes]: - if fields['gid'] != GroupId.VENDOR_RESERVED_E or fields['mt'] != MessageType.NOTIFICATION: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - return UciVendor_E_Notification(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciNotification.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class UciVendor_F_Notification(UciNotification): - - - def __post_init__(self): - self.gid = GroupId.VENDOR_RESERVED_F - self.mt = MessageType.NOTIFICATION - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['UciVendor_F_Notification', bytes]: - if fields['gid'] != GroupId.VENDOR_RESERVED_F or fields['mt'] != MessageType.NOTIFICATION: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - return UciVendor_F_Notification(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciNotification.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) - -@dataclass -class TestNotification(UciNotification): - - - def __post_init__(self): - self.gid = GroupId.TEST - self.mt = MessageType.NOTIFICATION - - @staticmethod - def parse(fields: dict, span: bytes) -> Tuple['TestNotification', bytes]: - if fields['gid'] != GroupId.TEST or fields['mt'] != MessageType.NOTIFICATION: - raise Exception("Invalid constraint field values") - payload = span - span = bytes([]) - fields['payload'] = payload - return TestNotification(**fields), span - - def serialize(self, payload: bytes = None) -> bytes: - _span = bytearray() - _span.extend(payload or self.payload or []) - return UciNotification.serialize(self, payload = bytes(_span)) - - @property - def size(self) -> int: - return len(self.payload) diff --git a/src/app_config.rs b/src/app_config.rs new file mode 100644 index 0000000..40ac595 --- /dev/null +++ b/src/app_config.rs @@ -0,0 +1,517 @@ +use crate::packets::uci; +use crate::MacAddress; + +/// [UCI] 8.3 Application Configuration Parameters. +/// Sub-session Key provided for Provisioned STS for Responder specific Key mode +/// (STS_CONFIG equal to 0x04). +#[derive(Clone, PartialEq, Eq)] +pub enum SubSessionKey { + None, + Short([u8; 16]), + Extended([u8; 32]), +} + +/// [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<uci::DeviceType>, + pub ranging_round_usage: Option<uci::RangingRoundUsage>, + pub sts_config: uci::StsConfig, + pub multi_node_mode: Option<uci::MultiNodeMode>, + 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<MacAddress>, + /// 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<MacAddress>, + 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<uci::DeviceRole>, + 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<uci::ScheduleMode>, + 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<u8>, + sub_session_key: SubSessionKey, + 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: SubSessionKey::None, + 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<T: TryFrom<u8, Error = u8>>(value: &[u8]) -> anyhow::Result<T> { + T::try_from(u8::from_le_bytes(value.try_into()?)).map_err(anyhow::Error::msg) + } + + fn try_parse_u8(value: &[u8]) -> anyhow::Result<u8> { + Ok(u8::from_le_bytes(value.try_into()?)) + } + + fn try_parse_u16(value: &[u8]) -> anyhow::Result<u16> { + Ok(u16::from_le_bytes(value.try_into()?)) + } + + fn try_parse_u32(value: &[u8]) -> anyhow::Result<u32> { + Ok(u32::from_le_bytes(value.try_into()?)) + } + + fn try_parse_u64(value: &[u8]) -> anyhow::Result<u64> { + 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 = match value.len() { + 16 => SubSessionKey::Short(value.try_into().unwrap()), + 32 => SubSessionKey::Extended(value.try_into().unwrap()), + _ => anyhow::bail!("invalid sub-session key size {}", value.len()), + } + } + 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<Vec<u8>> { + 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::<u8>::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(match self.sub_session_key { + SubSessionKey::None => vec![], + SubSessionKey::Short(key) => key.to_vec(), + SubSessionKey::Extended(key) => key.to_vec(), + }), + 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.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/device.rs b/src/device.rs index 48d2bd2..eec1c7f 100644 --- a/src/device.rs +++ b/src/device.rs @@ -12,27 +12,31 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::packets::uci::*; +use crate::packets::uci::{self, *}; use crate::MacAddress; use crate::PicaCommand; use std::collections::HashMap; -use std::iter::Extend; use std::time::Duration; use tokio::sync::mpsc; use tokio::time; -use super::session::{Session, MAX_SESSION}; +use super::app_config::SubSessionKey; +use super::session::Session; use super::UciPacket; pub const MAX_DEVICE: usize = 4; +pub const MAX_SESSION: usize = 255; const UCI_VERSION: u16 = 0x0002; // Version 2.0 const MAC_VERSION: u16 = 0x3001; // Version 1.3.0 const PHY_VERSION: u16 = 0x3001; // Version 1.3.0 const TEST_VERSION: u16 = 0x1001; // Version 1.1 +/// cf. [UCI] 8.3 Table 29 +pub const MAX_NUMBER_OF_CONTROLEES: usize = 8; + // Capabilities are vendor defined // Android compliant: FIRA-287 UCI_Generic_Specification controlee capabilities_r4 // Android parses capabilities, according to these definitions: @@ -72,15 +76,36 @@ pub const DEFAULT_CAPS_INFO: &[(CapTlvType, &[u8])] = &[ ), ]; +/// [UCI] 8.2 Device Configuration Parameters +pub struct DeviceConfig { + device_state: DeviceState, + // This config is used to enable/disable the low power mode. + // 0x00 = Disable low power mode + // 0x01 = Enable low power mode (default) + low_power_mode: bool, +} + +// [UCI] 6.3.1 Setting the Configuration +// All device configuration parameters within the UWBS are set to +// default values at Table 44 [..]. +impl Default for DeviceConfig { + fn default() -> Self { + DeviceConfig { + device_state: DeviceState::DeviceStateError, + low_power_mode: true, + } + } +} + pub struct Device { pub handle: usize, pub mac_address: MacAddress, + config: DeviceConfig, /// [UCI] 5. UWBS Device State Machine state: DeviceState, sessions: HashMap<u32, Session>, pub tx: mpsc::UnboundedSender<UciPacket>, pica_tx: mpsc::Sender<PicaCommand>, - config: HashMap<DeviceConfigId, Vec<u8>>, country_code: [u8; 2], pub n_active_sessions: usize, @@ -96,11 +121,11 @@ impl Device { Device { handle, mac_address, + config: Default::default(), state: DeviceState::DeviceStateError, // Will be overwitten sessions: Default::default(), tx, pica_tx, - config: HashMap::new(), country_code: Default::default(), n_active_sessions: 0, } @@ -117,7 +142,7 @@ impl Device { let tx = self.tx.clone(); tokio::spawn(async move { time::sleep(Duration::from_millis(5)).await; - tx.send(DeviceStatusNtfBuilder { device_state }.build().into()) + tx.send(CoreDeviceStatusNtfBuilder { device_state }.build().into()) .unwrap() }); } @@ -175,13 +200,13 @@ impl Device { // The fira norm specify to send a response, then reset, then // send a notification once the reset is done - fn command_device_reset(&mut self, cmd: DeviceResetCmd) -> DeviceResetRsp { + fn core_device_reset(&mut self, cmd: CoreDeviceResetCmd) -> CoreDeviceResetRsp { let reset_config = cmd.get_reset_config(); log::debug!("[{}] DeviceReset", self.handle); log::debug!(" reset_config={:?}", reset_config); let status = match reset_config { - ResetConfig::UwbsReset => StatusCode::UciStatusOk, + ResetConfig::UwbsReset => uci::Status::Ok, }; *self = Device::new( self.handle, @@ -191,15 +216,15 @@ impl Device { ); self.init(); - DeviceResetRspBuilder { status }.build() + CoreDeviceResetRspBuilder { status }.build() } - fn command_get_device_info(&self, _cmd: GetDeviceInfoCmd) -> GetDeviceInfoRsp { + fn core_get_device_info(&self, _cmd: CoreGetDeviceInfoCmd) -> CoreGetDeviceInfoRsp { // TODO: Implement a fancy build time state machine instead of crash at runtime log::debug!("[{}] GetDeviceInfo", self.handle); assert_eq!(self.state, DeviceState::DeviceStateReady); - GetDeviceInfoRspBuilder { - status: StatusCode::UciStatusOk, + CoreGetDeviceInfoRspBuilder { + status: uci::Status::Ok, uci_version: UCI_VERSION, mac_version: MAC_VERSION, phy_version: PHY_VERSION, @@ -209,7 +234,7 @@ impl Device { .build() } - pub fn command_get_caps_info(&self, _cmd: GetCapsInfoCmd) -> GetCapsInfoRsp { + pub fn core_get_caps_info(&self, _cmd: CoreGetCapsInfoCmd) -> CoreGetCapsInfoRsp { log::debug!("[{}] GetCapsInfo", self.handle); let caps = DEFAULT_CAPS_INFO @@ -220,86 +245,101 @@ impl Device { }) .collect(); - GetCapsInfoRspBuilder { - status: StatusCode::UciStatusOk, + CoreGetCapsInfoRspBuilder { + status: uci::Status::Ok, tlvs: caps, } .build() } - pub fn command_set_config(&mut self, cmd: SetConfigCmd) -> SetConfigRsp { + pub fn core_set_config(&mut self, cmd: CoreSetConfigCmd) -> CoreSetConfigRsp { log::debug!("[{}] SetConfig", self.handle); assert_eq!(self.state, DeviceState::DeviceStateReady); // UCI 6.3 - let (valid_parameters, invalid_config_status) = cmd.get_tlvs().iter().fold( - (HashMap::new(), Vec::new()), - |(mut valid_parameters, invalid_config_status), param| { - // TODO: DeviceState is a read only parameter - valid_parameters.insert(param.cfg_id, param.v.clone()); + // [UCI] 6.3.1 Setting the Configuration + // The UWBS shall respond with CORE_SET_CONFIG_RSP setting the Status + // field of STATUS_INVALID_PARAM and including one or more invalid + // Parameter ID(s) If the Host tries to set a parameter that is not + // available in the UWBS. All other configuration parameters should + // have been set to the new values within the UWBS. + let mut invalid_parameters = vec![]; + for parameter in cmd.get_parameters() { + match parameter.id { + uci::ConfigParameterId::DeviceState => { + invalid_parameters.push(uci::ConfigParameterStatus { + id: parameter.id, + status: uci::Status::ReadOnly, + }) + } + uci::ConfigParameterId::LowPowerMode => { + self.config.low_power_mode = parameter.value.first().copied().unwrap_or(1) != 0; + } + uci::ConfigParameterId::Rfu(id) => { + log::warn!("unknown config parameter id 0x{:02x}", *id); + invalid_parameters.push(uci::ConfigParameterStatus { + id: parameter.id, + status: uci::Status::InvalidParam, + }) + } + } + } - (valid_parameters, invalid_config_status) + CoreSetConfigRspBuilder { + status: if invalid_parameters.is_empty() { + uci::Status::Ok + } else { + uci::Status::InvalidParam }, - ); - - let (status, parameters) = if invalid_config_status.is_empty() { - self.config.extend(valid_parameters); - (StatusCode::UciStatusOk, Vec::new()) - } else { - (StatusCode::UciStatusInvalidParam, invalid_config_status) - }; - - SetConfigRspBuilder { - cfg_status: parameters, - status, + parameters: invalid_parameters, } .build() } - pub fn command_get_config(&self, cmd: GetConfigCmd) -> GetConfigRsp { + pub fn core_get_config(&self, cmd: CoreGetConfigCmd) -> CoreGetConfigRsp { log::debug!("[{}] GetConfig", self.handle); - // TODO: do this config shall be set on device reset - let ids = cmd.get_cfg_id(); - let (valid_parameters, invalid_parameters) = ids.iter().fold( - (Vec::new(), Vec::new()), - |(mut valid_parameters, mut invalid_parameters), id| { - // UCI Core Section 6.3.2 Table 8 - // UCI Core Section 6.3.2 - Return the Configuration - // If the status code is ok, return the params - // If there is at least one invalid param, return the list of invalid params - // If the ID is not present in our config, return the Type with length = 0 - match DeviceConfigId::try_from(*id) { - Ok(cfg_id) => match self.config.get(&cfg_id) { - Some(value) => valid_parameters.push(DeviceConfigTlv { - cfg_id, - v: value.clone(), - }), - None => invalid_parameters.push(DeviceConfigTlv { - cfg_id, - v: Vec::new(), - }), - }, - Err(_) => log::error!("Failed to parse config id: {:?}", id), - } - - (valid_parameters, invalid_parameters) - }, - ); + // [UCI] 6.3.2 Retrieve the Configuration + // If the Host tries to retrieve any Parameter(s) that are not available + // in the UWBS, the UWBS shall respond with a CORE_GET_CONFIG_RSP with + // a Status field of STATUS_INVALID_PARAM, containing each unavailable + // Device Configuration Parameter Type with Length field is zero. In + // this case, the CORE_GET_CONFIG_RSP shall not include any parameter(s) + // that are available in the UWBS. + let mut valid_parameters = vec![]; + let mut invalid_parameters = vec![]; + for id in cmd.get_parameter_ids() { + match id { + ConfigParameterId::DeviceState => valid_parameters.push(ConfigParameter { + id: *id, + value: vec![self.config.device_state.into()], + }), + ConfigParameterId::LowPowerMode => valid_parameters.push(ConfigParameter { + id: *id, + value: vec![self.config.low_power_mode.into()], + }), + ConfigParameterId::Rfu(_) => invalid_parameters.push(ConfigParameter { + id: *id, + value: vec![], + }), + } + } - let (status, parameters) = if invalid_parameters.is_empty() { - (StatusCode::UciStatusOk, valid_parameters) + if invalid_parameters.is_empty() { + CoreGetConfigRspBuilder { + status: uci::Status::Ok, + parameters: valid_parameters, + } + .build() } else { - (StatusCode::UciStatusInvalidParam, invalid_parameters) - }; - - GetConfigRspBuilder { - status, - tlvs: parameters, + CoreGetConfigRspBuilder { + status: uci::Status::InvalidParam, + parameters: invalid_parameters, + } + .build() } - .build() } - fn command_session_init(&mut self, cmd: SessionInitCmd) -> SessionInitRsp { + fn session_init(&mut self, cmd: SessionInitCmd) -> SessionInitRsp { let session_id = cmd.get_session_id(); let session_type = cmd.get_session_type(); @@ -308,23 +348,17 @@ impl Device { log::debug!(" session_type={:?}", session_type); let status = if self.sessions.len() >= MAX_SESSION { - StatusCode::UciStatusMaxSessionsExceeded + uci::Status::ErrorMaxSessionsExceeded } else { match self.sessions.insert( session_id, - Session::new( - session_id, - session_type, - self.handle, - self.tx.clone(), - self.pica_tx.clone(), - ), + Session::new(session_id, session_type, self.handle, self.tx.clone()), ) { - Some(_) => StatusCode::UciStatusSessionDuplicate, + Some(_) => uci::Status::ErrorSessionDuplicate, None => { // Should not fail self.session_mut(session_id).unwrap().init(); - StatusCode::UciStatusOk + uci::Status::Ok } } }; @@ -332,7 +366,7 @@ impl Device { SessionInitRspBuilder { status }.build() } - fn command_session_deinit(&mut self, cmd: SessionDeinitCmd) -> SessionDeinitRsp { + fn session_deinit(&mut self, cmd: SessionDeinitCmd) -> SessionDeinitRsp { let session_id = cmd.get_session_token(); log::debug!("[{}] Session deinit", self.handle); log::debug!(" session_id=0x{:x}", session_id); @@ -346,24 +380,499 @@ impl Device { } } self.sessions.remove(&session_id); - StatusCode::UciStatusOk + uci::Status::Ok } - None => StatusCode::UciStatusSessionNotExist, + None => uci::Status::ErrorSessionNotExist, }; SessionDeinitRspBuilder { status }.build() } - fn command_session_get_count(&self, _cmd: SessionGetCountCmd) -> SessionGetCountRsp { + fn session_get_count(&self, _cmd: SessionGetCountCmd) -> SessionGetCountRsp { log::debug!("[{}] Session get count", self.handle); SessionGetCountRspBuilder { - status: StatusCode::UciStatusOk, + status: uci::Status::Ok, session_count: self.sessions.len() as u8, } .build() } - fn command_set_country_code( + fn session_set_app_config(&mut self, cmd: SessionSetAppConfigCmd) -> SessionSetAppConfigRsp { + let session_handle = cmd.get_session_token(); + + log::debug!( + "[{}:0x{:x}] Session Set App Config", + self.handle, + session_handle + ); + + let Some(session) = self.sessions.get_mut(&session_handle) else { + return SessionSetAppConfigRspBuilder { + cfg_status: Vec::new(), + status: uci::Status::ErrorSessionNotExist, + } + .build(); + }; + + assert!( + session.session_type == SessionType::FiraRangingSession + || session.session_type == SessionType::FiraRangingAndInBandDataSession + ); + + if session.state == SessionState::SessionStateActive { + const IMMUTABLE_PARAMETERS: &[AppConfigTlvType] = &[AppConfigTlvType::AoaResultReq]; + if cmd + .get_tlvs() + .iter() + .any(|cfg| IMMUTABLE_PARAMETERS.contains(&cfg.cfg_id)) + { + return SessionSetAppConfigRspBuilder { + status: uci::Status::ErrorSessionActive, + cfg_status: vec![], + } + .build(); + } + } + + let (status, invalid_parameters) = if session.state != SessionState::SessionStateInit + && session.state != SessionState::SessionStateActive + { + (uci::Status::Rejected, Vec::new()) + } else { + let mut app_config = session.app_config.clone(); + 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::Status::InvalidParam, + }), + } + } + + // [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.handle, + session_handle + ); + return SessionSetAppConfigRspBuilder { + status: uci::Status::Rejected, + cfg_status: vec![], + } + .build(); + } + + if invalid_parameters.is_empty() { + session.app_config = app_config; + if session.state == SessionState::SessionStateInit { + session.set_state( + SessionState::SessionStateIdle, + ReasonCode::StateChangeWithSessionManagementCommands, + ); + } + (uci::Status::Ok, invalid_parameters) + } else { + (uci::Status::InvalidParam, invalid_parameters) + } + }; + + SessionSetAppConfigRspBuilder { + status, + cfg_status: invalid_parameters, + } + .build() + } + + fn session_get_app_config(&self, cmd: SessionGetAppConfigCmd) -> SessionGetAppConfigRsp { + let session_handle = cmd.get_session_token(); + + log::debug!( + "[{}:0x{:x}] Session Get App Config", + self.handle, + session_handle + ); + + let Some(session) = self.sessions.get(&session_handle) else { + return SessionGetAppConfigRspBuilder { + tlvs: vec![], + status: uci::Status::ErrorSessionNotExist, + } + .build(); + }; + + let (status, valid_parameters) = { + let mut valid_parameters = vec![]; + let mut invalid_parameters = vec![]; + for id in cmd.get_app_cfg() { + match session.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() { + (uci::Status::Ok, valid_parameters) + } else { + (uci::Status::Failed, Vec::new()) + } + }; + + SessionGetAppConfigRspBuilder { + status, + tlvs: valid_parameters, + } + .build() + } + + fn session_get_state(&self, cmd: SessionGetStateCmd) -> SessionGetStateRsp { + let session_handle = cmd.get_session_token(); + + log::debug!("[{}:0x{:x}] Session Get State", self.handle, session_handle); + + let Some(session) = self.sessions.get(&session_handle) else { + return SessionGetStateRspBuilder { + session_state: SessionState::SessionStateInit, + status: uci::Status::ErrorSessionNotExist, + } + .build(); + }; + + SessionGetStateRspBuilder { + status: uci::Status::Ok, + session_state: session.state, + } + .build() + } + + fn session_update_controller_multicast_list( + &mut self, + cmd: SessionUpdateControllerMulticastListCmd, + ) -> SessionUpdateControllerMulticastListRsp { + let session_handle = cmd.get_session_token(); + + log::debug!( + "[{}:0x{:x}] Session Update Controller Multicast List", + self.handle, + session_handle + ); + + let Some(session) = self.sessions.get_mut(&session_handle) else { + return SessionUpdateControllerMulticastListRspBuilder { + status: uci::Status::ErrorSessionNotExist, + } + .build(); + }; + + if (session.state != SessionState::SessionStateActive + && session.state != SessionState::SessionStateIdle) + || session.app_config.device_type != Some(DeviceType::Controller) + || session.app_config.multi_node_mode != Some(MultiNodeMode::OneToMany) + { + return SessionUpdateControllerMulticastListRspBuilder { + status: uci::Status::Rejected, + } + .build(); + } + let action = cmd.get_action(); + let mut dst_addresses = session.app_config.dst_mac_address.clone(); + let new_controlees: Vec<Controlee> = match action { + UpdateMulticastListAction::AddControlee + | UpdateMulticastListAction::RemoveControlee => { + if let Ok(packet) = + SessionUpdateControllerMulticastListCmdPayload::parse(cmd.get_payload()) + { + packet + .controlees + .iter() + .map(|controlee| controlee.into()) + .collect() + } else { + return SessionUpdateControllerMulticastListRspBuilder { + status: uci::Status::SyntaxError, + } + .build(); + } + } + UpdateMulticastListAction::AddControleeWithShortSubSessionKey => { + if let Ok(packet) = + SessionUpdateControllerMulticastListCmd_2_0_16_Byte_Payload::parse( + cmd.get_payload(), + ) + { + packet + .controlees + .iter() + .map(|controlee| controlee.into()) + .collect() + } else { + return SessionUpdateControllerMulticastListRspBuilder { + status: uci::Status::SyntaxError, + } + .build(); + } + } + UpdateMulticastListAction::AddControleeWithExtendedSubSessionKey => { + if let Ok(packet) = + SessionUpdateControllerMulticastListCmd_2_0_32_Byte_Payload::parse( + cmd.get_payload(), + ) + { + packet + .controlees + .iter() + .map(|controlee| controlee.into()) + .collect() + } else { + return SessionUpdateControllerMulticastListRspBuilder { + status: uci::Status::SyntaxError, + } + .build(); + } + } + }; + let mut controlee_status = Vec::new(); + let mut status = uci::Status::Ok; + + match action { + UpdateMulticastListAction::AddControlee + | UpdateMulticastListAction::AddControleeWithShortSubSessionKey + | UpdateMulticastListAction::AddControleeWithExtendedSubSessionKey => { + new_controlees.iter().for_each(|controlee| { + let mut update_status = MulticastUpdateStatus::OkMulticastListUpdate; + if !dst_addresses.contains(&controlee.short_address) { + if dst_addresses.len() == MAX_NUMBER_OF_CONTROLEES { + status = uci::Status::ErrorMulticastListFull; + update_status = MulticastUpdateStatus::ErrorMulticastListFull; + } else if (action + == UpdateMulticastListAction::AddControleeWithShortSubSessionKey + || action + == UpdateMulticastListAction::AddControleeWithExtendedSubSessionKey) + && session.app_config.sts_config + != 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 + // with Status set to STATUS_ERROR_SUB_SESSION_KEY_NOT_APPLICABLE for each + // Controlee in the Controlee List. + status = uci::Status::Failed; + update_status = MulticastUpdateStatus::ErrorSubSessionKeyNotApplicable; + } else { + dst_addresses.push(controlee.short_address); + }; + } + controlee_status.push(ControleeStatus { + mac_address: match controlee.short_address { + MacAddress::Short(address) => address, + MacAddress::Extended(_) => panic!("Extended address is not supported!"), + }, + status: update_status, + }); + }); + } + UpdateMulticastListAction::RemoveControlee => { + new_controlees.iter().for_each(|controlee: &Controlee| { + let pica_tx = self.pica_tx.clone(); + let address = controlee.short_address; + let attempt_count = session.app_config.in_band_termination_attempt_count; + let mut update_status = MulticastUpdateStatus::OkMulticastListUpdate; + if !dst_addresses.contains(&address) { + status = uci::Status::Failed; + update_status = MulticastUpdateStatus::ErrorKeyFetchFail; + } else { + dst_addresses.retain(|value| *value != address); + // If IN_BAND_TERMINATION_ATTEMPT_COUNT is not equal to 0x00, then the + // UWBS shall transmit the RCM with the “Stop Ranging” bit set to ‘1’ + // for IN_BAND_TERMINATION_ATTEMPT_COUNT times to the corresponding + // Controlee. + if attempt_count != 0 { + tokio::spawn(async move { + for _ in 0..attempt_count { + pica_tx + .send(PicaCommand::StopRanging(address, session_handle)) + .await + .unwrap() + } + }); + } + } + controlee_status.push(ControleeStatus { + mac_address: match address { + MacAddress::Short(addr) => addr, + MacAddress::Extended(_) => panic!("Extended address is not supported!"), + }, + status: update_status, + }); + }); + } + } + session.app_config.number_of_controlees = dst_addresses.len() as u8; + session.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 session.app_config.dst_mac_address.is_empty() { + session.set_state( + SessionState::SessionStateIdle, + ReasonCode::ErrorInvalidNumOfControlees, + ) + } + let tx = self.tx.clone(); + tokio::spawn(async move { + // Sleep for 5ms to make sure the notification is not being + // sent before the response. + // TODO(#84) remove the sleep. + time::sleep(Duration::from_millis(5)).await; + tx.send( + SessionUpdateControllerMulticastListNtfBuilder { + controlee_status, + session_token: session_handle, + } + .build() + .into(), + ) + .unwrap() + }); + SessionUpdateControllerMulticastListRspBuilder { status }.build() + } + + fn session_start(&mut self, cmd: SessionStartCmd) -> SessionStartRsp { + let session_id = cmd.get_session_id(); + + log::debug!("[{}:0x{:x}] Session Start", self.handle, session_id); + + let Some(session) = self.sessions.get_mut(&session_id) else { + return SessionStartRspBuilder { + status: uci::Status::ErrorSessionNotExist, + } + .build(); + }; + + if session.state != SessionState::SessionStateIdle { + return SessionStartRspBuilder { + status: uci::Status::ErrorSessionNotConfigured, + } + .build(); + } + + assert!(session.ranging_task.is_none()); + + let ranging_interval = + time::Duration::from_millis(session.app_config.ranging_duration as u64); + + let tx = self.pica_tx.clone(); + let handle = self.handle; + session.ranging_task = Some(tokio::spawn(async move { + loop { + time::sleep(ranging_interval).await; + tx.send(PicaCommand::Ranging(handle, session_id)) + .await + .unwrap(); + } + })); + + session.set_state( + SessionState::SessionStateActive, + ReasonCode::StateChangeWithSessionManagementCommands, + ); + + self.n_active_sessions += 1; + self.set_state(DeviceState::DeviceStateActive); + + SessionStartRspBuilder { + status: uci::Status::Ok, + } + .build() + } + + fn session_stop(&mut self, cmd: SessionStopCmd) -> SessionStopRsp { + let session_id = cmd.get_session_id(); + + log::debug!("[{}:0x{:x}] Session Stop", self.handle, session_id); + + let Some(session) = self.sessions.get_mut(&session_id) else { + return SessionStopRspBuilder { + status: uci::Status::ErrorSessionNotExist, + } + .build(); + }; + + if session.state != SessionState::SessionStateActive { + return SessionStopRspBuilder { + status: uci::Status::ErrorSessionActive, + } + .build(); + } + + session.stop_ranging_task(); + session.set_state( + SessionState::SessionStateIdle, + ReasonCode::StateChangeWithSessionManagementCommands, + ); + + self.n_active_sessions -= 1; + if self.n_active_sessions == 0 { + self.set_state(DeviceState::DeviceStateReady); + } + + SessionStopRspBuilder { + status: uci::Status::Ok, + } + .build() + } + + fn session_get_ranging_count( + &self, + cmd: SessionGetRangingCountCmd, + ) -> SessionGetRangingCountRsp { + let session_id = cmd.get_session_id(); + + log::debug!( + "[{}:0x{:x}] Session Get Ranging Count", + self.handle, + session_id + ); + + let Some(session) = self.sessions.get(&session_id) else { + return SessionGetRangingCountRspBuilder { + status: uci::Status::ErrorSessionNotExist, + count: 0, + } + .build(); + }; + + SessionGetRangingCountRspBuilder { + status: uci::Status::Ok, + count: session.sequence_number, + } + .build() + } + + fn android_set_country_code( &mut self, cmd: AndroidSetCountryCodeCmd, ) -> AndroidSetCountryCodeRsp { @@ -373,12 +882,12 @@ impl Device { self.country_code = country_code; AndroidSetCountryCodeRspBuilder { - status: StatusCode::UciStatusOk, + status: uci::Status::Ok, } .build() } - fn command_get_power_stats( + fn android_get_power_stats( &mut self, _cmd: AndroidGetPowerStatsCmd, ) -> AndroidGetPowerStatsRsp { @@ -387,7 +896,7 @@ impl Device { // TODO AndroidGetPowerStatsRspBuilder { stats: PowerStats { - status: StatusCode::UciStatusOk, + status: uci::Status::Ok, idle_time_ms: 0, tx_time_ms: 0, rx_time_ms: 0, @@ -397,7 +906,7 @@ impl Device { .build() } - pub fn data_message_snd(&mut self, data: DataPacket) -> SessionControlNotification { + pub fn data_message_snd(&mut self, data: DataPacket) -> ControlPacket { log::debug!("[{}] data_message_send", self.handle); match data.specialize() { DataPacketChild::DataMessageSnd(data_msg_snd) => { @@ -405,7 +914,7 @@ impl Device { if let Some(session) = self.session_mut(session_token) { session.data_message_snd(data_msg_snd) } else { - DataTransferStatusNtfBuilder { + SessionDataTransferStatusNtfBuilder { session_token, status: DataTransferNtfStatusCode::UciDataTransferStatusErrorRejected, tx_count: 1, // TODO: support for retries? @@ -418,7 +927,7 @@ impl Device { DataPacketChild::DataMessageRcv(data_msg_rcv) => { // This function should not be passed anything besides DataMessageSnd let session_token = data_msg_rcv.get_session_handle(); - DataTransferStatusNtfBuilder { + SessionDataTransferStatusNtfBuilder { session_token, status: DataTransferNtfStatusCode::UciDataTransferStatusInvalidFormat, tx_count: 1, // TODO: support for retries? @@ -433,187 +942,73 @@ impl Device { } } - fn receive_command(&mut self, cmd: UciCommand) -> UciResponse { + fn receive_command(&mut self, cmd: ControlPacket) -> ControlPacket { + use AndroidPacketChild::*; + use ControlPacketChild::*; + use CorePacketChild::*; + use SessionConfigPacketChild::*; + use SessionControlPacketChild::*; + match cmd.specialize() { - // Handle commands for this device - UciCommandChild::CoreCommand(core_command) => match core_command.specialize() { - CoreCommandChild::DeviceResetCmd(cmd) => self.command_device_reset(cmd).into(), - CoreCommandChild::GetDeviceInfoCmd(cmd) => self.command_get_device_info(cmd).into(), - CoreCommandChild::GetCapsInfoCmd(cmd) => self.command_get_caps_info(cmd).into(), - CoreCommandChild::SetConfigCmd(cmd) => self.command_set_config(cmd).into(), - CoreCommandChild::GetConfigCmd(cmd) => self.command_get_config(cmd).into(), - _ => panic!("Unsupported core command"), + CorePacket(cmd) => match cmd.specialize() { + CoreDeviceResetCmd(cmd) => self.core_device_reset(cmd).into(), + CoreGetDeviceInfoCmd(cmd) => self.core_get_device_info(cmd).into(), + CoreGetCapsInfoCmd(cmd) => self.core_get_caps_info(cmd).into(), + CoreSetConfigCmd(cmd) => self.core_set_config(cmd).into(), + CoreGetConfigCmd(cmd) => self.core_get_config(cmd).into(), + _ => unimplemented!("Unsupported Core oid {:?}", cmd.get_oid()), }, - // Handle commands for session management - UciCommandChild::SessionConfigCommand(session_command) => { - match session_command.specialize() { - SessionConfigCommandChild::SessionInitCmd(cmd) => { - return self.command_session_init(cmd).into(); - } - SessionConfigCommandChild::SessionDeinitCmd(cmd) => { - return self.command_session_deinit(cmd).into(); - } - SessionConfigCommandChild::SessionGetCountCmd(cmd) => { - return self.command_session_get_count(cmd).into(); - } - _ => {} - } - - // Common code for retrieving the session_id in the command - let session_id = match session_command.specialize() { - SessionConfigCommandChild::SessionSetAppConfigCmd(cmd) => { - cmd.get_session_token() - } - SessionConfigCommandChild::SessionGetAppConfigCmd(cmd) => { - cmd.get_session_token() - } - SessionConfigCommandChild::SessionGetStateCmd(cmd) => cmd.get_session_token(), - SessionConfigCommandChild::SessionUpdateControllerMulticastListCmd(cmd) => { - cmd.get_session_token() - } - _ => panic!("Unsupported session command type"), - }; - - if let Some(session) = self.session_mut(session_id) { - // There is a session matching the session_id in the command - // Pass the command through - match session_command.specialize() { - SessionConfigCommandChild::SessionSetAppConfigCmd(_) - | SessionConfigCommandChild::SessionGetAppConfigCmd(_) - | SessionConfigCommandChild::SessionGetStateCmd(_) - | SessionConfigCommandChild::SessionUpdateControllerMulticastListCmd(_) => { - session.session_command(session_command).into() - } - _ => panic!("Unsupported session command"), - } - } else { - // There is no session matching the session_id in the command - let status = StatusCode::UciStatusSessionNotExist; - match session_command.specialize() { - SessionConfigCommandChild::SessionSetAppConfigCmd(_) => { - SessionSetAppConfigRspBuilder { - cfg_status: Vec::new(), - status, - } - .build() - .into() - } - SessionConfigCommandChild::SessionGetAppConfigCmd(_) => { - SessionGetAppConfigRspBuilder { - status, - tlvs: Vec::new(), - } - .build() - .into() - } - SessionConfigCommandChild::SessionGetStateCmd(_) => { - SessionGetStateRspBuilder { - status, - session_state: SessionState::SessionStateDeinit, - } - .build() - .into() - } - SessionConfigCommandChild::SessionUpdateControllerMulticastListCmd(_) => { - SessionUpdateControllerMulticastListRspBuilder { status } - .build() - .into() - } - _ => panic!("Unsupported session command"), - } - } - } - UciCommandChild::SessionControlCommand(ranging_command) => { - let session_id = ranging_command.get_session_id(); - if let Some(session) = self.session_mut(session_id) { - // Forward to the proper session - let response = session.ranging_command(ranging_command); - match response.specialize() { - SessionControlResponseChild::SessionStartRsp(rsp) - if rsp.get_status() == StatusCode::UciStatusOk => - { - self.n_active_sessions += 1; - self.set_state(DeviceState::DeviceStateActive); - } - SessionControlResponseChild::SessionStopRsp(rsp) - if rsp.get_status() == StatusCode::UciStatusOk => - { - assert!(self.n_active_sessions > 0); - self.n_active_sessions -= 1; - if self.n_active_sessions == 0 { - self.set_state(DeviceState::DeviceStateReady); - } - } - _ => {} - } - response.into() - } else { - let status = StatusCode::UciStatusSessionNotExist; - match ranging_command.specialize() { - SessionControlCommandChild::SessionStartCmd(_) => { - SessionStartRspBuilder { status }.build().into() - } - SessionControlCommandChild::SessionStopCmd(_) => { - SessionStopRspBuilder { status }.build().into() - } - SessionControlCommandChild::SessionGetRangingCountCmd(_) => { - SessionGetRangingCountRspBuilder { status, count: 0 } - .build() - .into() - } - _ => panic!("Unsupported ranging command"), - } + SessionConfigPacket(cmd) => match cmd.specialize() { + SessionInitCmd(cmd) => self.session_init(cmd).into(), + SessionDeinitCmd(cmd) => self.session_deinit(cmd).into(), + SessionGetCountCmd(cmd) => self.session_get_count(cmd).into(), + SessionSetAppConfigCmd(cmd) => self.session_set_app_config(cmd).into(), + SessionGetAppConfigCmd(cmd) => self.session_get_app_config(cmd).into(), + SessionGetStateCmd(cmd) => self.session_get_state(cmd).into(), + SessionUpdateControllerMulticastListCmd(cmd) => { + self.session_update_controller_multicast_list(cmd).into() } + _ => unimplemented!("Unsupported Session Config oid {:?}", cmd.get_oid()), + }, + SessionControlPacket(cmd) => match cmd.specialize() { + SessionStartCmd(cmd) => self.session_start(cmd).into(), + SessionStopCmd(cmd) => self.session_stop(cmd).into(), + SessionGetRangingCountCmd(cmd) => self.session_get_ranging_count(cmd).into(), + _ => unimplemented!("Unsupported Session Control oid {:?}", cmd.get_oid()), + }, + AndroidPacket(cmd) => match cmd.specialize() { + AndroidSetCountryCodeCmd(cmd) => self.android_set_country_code(cmd).into(), + AndroidGetPowerStatsCmd(cmd) => self.android_get_power_stats(cmd).into(), + _ => unimplemented!("Unsupported Android oid {:?}", cmd.get_oid()), + }, + ControlPacketChild::Payload(_) + if matches!( + cmd.get_mt(), + uci::MessageType::Response | uci::MessageType::Notification + ) => + { + unreachable!("Unhandled control messsage with type {:?}", cmd.get_mt()); } - - UciCommandChild::AndroidCommand(android_command) => { - match android_command.specialize() { - AndroidCommandChild::AndroidSetCountryCodeCmd(cmd) => { - self.command_set_country_code(cmd).into() - } - AndroidCommandChild::AndroidGetPowerStatsCmd(cmd) => { - self.command_get_power_stats(cmd).into() - } - _ => panic!("Unsupported Android command"), + ControlPacketChild::Payload(payload) => { + // [UCI] 4.3.2 Exception Handling for Control Messages + // The UWBS shall respond to an unknown Command (unknown GID + // or OID) with a Response having the same GID and OID field + // values as the Command, followed by a Status field with the + // value of STATUS_UNKNOWN_GID/STATUS_UNKNOWN_OID respectively + // and no additional fields. + log::error!("Unsupported gid {:?}", cmd.get_gid()); + ControlPacketBuilder { + mt: uci::MessageType::Response, + gid: cmd.get_gid(), + payload: Some( + vec![payload[0], payload[1], 0x1, uci::Status::UnknownGid.into()].into(), + ), } + .build() } - UciCommandChild::UciVendor_9_Command(vendor_command) => UciVendor_9_ResponseBuilder { - opcode: vendor_command.get_opcode(), - payload: Some(vec![u8::from(StatusCode::UciStatusRejected)].into()), - } - .build() - .into(), - UciCommandChild::UciVendor_A_Command(vendor_command) => UciVendor_A_ResponseBuilder { - opcode: vendor_command.get_opcode(), - payload: Some(vec![u8::from(StatusCode::UciStatusRejected)].into()), - } - .build() - .into(), - UciCommandChild::UciVendor_B_Command(vendor_command) => UciVendor_B_ResponseBuilder { - opcode: vendor_command.get_opcode(), - payload: Some(vec![u8::from(StatusCode::UciStatusRejected)].into()), - } - .build() - .into(), - UciCommandChild::UciVendor_E_Command(vendor_command) => UciVendor_E_ResponseBuilder { - opcode: vendor_command.get_opcode(), - payload: Some(vec![u8::from(StatusCode::UciStatusRejected)].into()), - } - .build() - .into(), - UciCommandChild::UciVendor_F_Command(vendor_command) => UciVendor_F_ResponseBuilder { - opcode: vendor_command.get_opcode(), - payload: Some(vec![u8::from(StatusCode::UciStatusRejected)].into()), - } - .build() - .into(), - // TODO: Handle properly without panic - _ => UciResponseBuilder { - gid: GroupId::Core, - opcode: 0, - payload: None, + ControlPacketChild::None => { + unreachable!() } - .build(), } } @@ -642,9 +1037,9 @@ impl Device { let opcode_id = packet[1] & 0x3f; let status = if GroupId::try_from(group_id).is_ok() { - StatusCode::UciStatusUnknownOid + uci::Status::UnknownOid } else { - StatusCode::UciStatusUnknownGid + uci::Status::UnknownGid }; // The PDL generated code cannot be used to generate // responses with invalid group identifiers. @@ -660,7 +1055,7 @@ impl Device { // Parsing success, ignore non command packets. Ok(cmd) => { - let response = self.receive_command(cmd.try_into().unwrap()); + let response = self.receive_command(cmd); self.send_control(response) } } @@ -672,3 +1067,41 @@ impl Device { } } } + +struct Controlee { + short_address: MacAddress, + #[allow(dead_code)] + sub_session_id: u32, + #[allow(dead_code)] + session_key: SubSessionKey, +} + +impl From<&uci::Controlee> for Controlee { + fn from(value: &uci::Controlee) -> Self { + Controlee { + short_address: MacAddress::Short(value.short_address), + sub_session_id: value.subsession_id, + session_key: SubSessionKey::None, + } + } +} + +impl From<&uci::Controlee_V2_0_16_Byte_Version> for Controlee { + fn from(value: &uci::Controlee_V2_0_16_Byte_Version) -> Self { + Controlee { + short_address: MacAddress::Short(value.short_address), + sub_session_id: value.subsession_id, + session_key: SubSessionKey::Short(value.subsession_key), + } + } +} + +impl From<&uci::Controlee_V2_0_32_Byte_Version> for Controlee { + fn from(value: &uci::Controlee_V2_0_32_Byte_Version) -> Self { + Controlee { + short_address: MacAddress::Short(value.short_address), + sub_session_id: value.subsession_id, + session_key: SubSessionKey::Extended(value.subsession_key), + } + } +} @@ -24,19 +24,18 @@ use tokio::sync::{broadcast, mpsc, oneshot}; pub mod packets; mod pcapng; -use packets::uci::StatusCode as UciStatusCode; -use packets::uci::*; +use packets::uci::{self, *}; mod device; -use device::{Device, MAX_DEVICE}; +use device::{Device, MAX_DEVICE, MAX_SESSION}; mod session; -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<u8>; pub type UciStream = Pin<Box<dyn futures::stream::Stream<Item = Vec<u8>> + Send>>; @@ -162,7 +161,7 @@ fn make_measurement( if let MacAddress::Short(address) = mac_address { ShortAddressTwoWayRangingMeasurement { mac_address: u16::from_le_bytes(*address), - status: UciStatusCode::UciStatusOk, + status: uci::Status::Ok, nlos: 0, // in Line Of Sight distance: local.range, aoa_azimuth: local.azimuth as u16, @@ -412,7 +411,7 @@ impl Pica { let mut measurements = Vec::new(); // Look for compatible anchors. - for mac_address in session.get_dst_mac_addresses() { + for mac_address in session.get_dst_mac_address() { if let Some(other) = self.anchors.get(mac_address) { let local = self .ranging_estimator @@ -437,7 +436,8 @@ impl Pica { .session(session_id) .unwrap() .app_config - .device_mac_address; + .device_mac_address + .unwrap(); let local = self .ranging_estimator .estimate(&device.handle, &peer_device.handle) @@ -467,15 +467,15 @@ impl Pica { data_sequence_number: 0x01, pbf: PacketBoundaryFlag::Complete, session_handle: session_id, - source_address: device.mac_address.into(), - status: UciStatusCode::UciStatusOk, + source_address: session.app_config.device_mac_address.unwrap().into(), + status: uci::Status::Ok, } .build() .into(), ) .unwrap(); } - if session.is_ranging_data_ntf_enabled() != RangeDataNtfConfig::Disable { + if session.is_session_info_ntf_enabled() { device .tx .send( @@ -554,7 +554,7 @@ impl Pica { continue; }; - if &session.app_config.device_mac_address != mac_address { + if session.app_config.device_mac_address != Some(*mac_address) { continue; } diff --git a/src/mac_address.rs b/src/mac_address.rs index 79cfe94..6c93981 100644 --- a/src/mac_address.rs +++ b/src/mac_address.rs @@ -43,15 +43,36 @@ impl MacAddress { } } +impl From<&MacAddress> for u64 { + fn from(mac_address: &MacAddress) -> Self { + match mac_address { + MacAddress::Short(addr) => u16::from_le_bytes(*addr) as u64, + MacAddress::Extended(addr) => u64::from_le_bytes(*addr), + } + } +} + impl From<MacAddress> for u64 { fn from(mac_address: MacAddress) -> Self { + u64::from(&mac_address) + } +} + +impl From<&MacAddress> for Vec<u8> { + fn from(mac_address: &MacAddress) -> Self { match mac_address { - MacAddress::Short(addr) => u16::from_le_bytes(addr) as u64, - MacAddress::Extended(addr) => u64::from_le_bytes(addr), + MacAddress::Short(addr) => addr.to_vec(), + MacAddress::Extended(addr) => addr.to_vec(), } } } +impl From<MacAddress> for Vec<u8> { + fn from(mac_address: MacAddress) -> Self { + Vec::<u8>::from(&mac_address) + } +} + impl TryFrom<String> for MacAddress { type Error = Error; fn try_from(mac_address: String) -> std::result::Result<Self, Error> { diff --git a/src/session.rs b/src/session.rs index eb4e063..2ee656a 100644 --- a/src/session.rs +++ b/src/session.rs @@ -17,711 +17,15 @@ //! - [UCI] FiRa Consortium UWB Command Interface Generic Technical specification use crate::packets::uci::{self, *}; -use crate::{MacAddress, PicaCommand}; +use crate::{AppConfig, MacAddress}; 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<AppConfigTlvType, Vec<u8>>, - - device_type: DeviceType, - device_role: DeviceRole, - mac_address_mode: MacAddressMode, - pub device_mac_address: MacAddress, - number_of_controlees: usize, - dst_mac_addresses: Vec<MacAddress>, - 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<Vec<u8>>, - static_sts_iv: Option<Vec<u8>>, - session_key: Option<Vec<u8>>, - sub_session_key: Option<Vec<u8>>, - 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<Vec<u8>> { - 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<AppConfigStatus> { - 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, - #[allow(dead_code)] - session_key: SubSessionKey, -} - -impl From<&uci::Controlee> for Controlee { - fn from(value: &uci::Controlee) -> Self { - Controlee { - short_address: MacAddress::Short(value.short_address), - sub_session_id: value.subsession_id, - session_key: SubSessionKey::None, - } - } -} - -impl From<&uci::Controlee_V2_0_16_Byte_Version> for Controlee { - fn from(value: &uci::Controlee_V2_0_16_Byte_Version) -> Self { - Controlee { - short_address: MacAddress::Short(value.short_address), - sub_session_id: value.subsession_id, - session_key: SubSessionKey::Short(value.subsession_key), - } - } -} - -impl From<&uci::Controlee_V2_0_32_Byte_Version> for Controlee { - fn from(value: &uci::Controlee_V2_0_32_Byte_Version) -> Self { - Controlee { - short_address: MacAddress::Short(value.short_address), - sub_session_id: value.subsession_id, - session_key: SubSessionKey::Extended(value.subsession_key), - } - } -} - pub struct Session { /// cf. [UCI] 7.1 pub state: SessionState, @@ -730,12 +34,11 @@ pub struct Session { device_handle: usize, data: BytesMut, - session_type: SessionType, + pub session_type: SessionType, pub sequence_number: u32, pub app_config: AppConfig, - ranging_task: Option<JoinHandle<()>>, + pub ranging_task: Option<JoinHandle<()>>, tx: mpsc::UnboundedSender<UciPacket>, - pica_tx: mpsc::Sender<PicaCommand>, } impl Session { @@ -744,7 +47,6 @@ impl Session { session_type: SessionType, device_handle: usize, tx: mpsc::UnboundedSender<UciPacket>, - pica_tx: mpsc::Sender<PicaCommand>, ) -> Self { Self { state: SessionState::SessionStateDeinit, @@ -756,7 +58,6 @@ impl Session { app_config: AppConfig::default(), ranging_task: None, tx, - pica_tx, } } @@ -785,12 +86,18 @@ impl Session { }); } - pub fn get_dst_mac_addresses(&self) -> &Vec<MacAddress> { - &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 { @@ -816,407 +123,20 @@ impl Session { ); } - fn command_set_app_config(&mut self, cmd: SessionSetAppConfigCmd) -> SessionSetAppConfigRsp { - // TODO properly handle these asserts - log::debug!( - "[{}:0x{:x}] Session Set App Config", - self.device_handle, - self.id - ); - assert_eq!(self.id, cmd.get_session_token()); - assert!( - self.session_type.eq(&SessionType::FiraRangingSession) - || self - .session_type - .eq(&SessionType::FiraRangingAndInBandDataSession) - ); - - if self.state == SessionState::SessionStateActive { - const IMMUTABLE_PARAMETERS: &[AppConfigTlvType] = &[AppConfigTlvType::AoaResultReq]; - if cmd - .get_tlvs() - .iter() - .any(|cfg| IMMUTABLE_PARAMETERS.contains(&cfg.cfg_id)) - { - return SessionSetAppConfigRspBuilder { - status: StatusCode::UciStatusSessionActive, - cfg_status: vec![], - } - .build(); - } - } - - let (status, invalid_parameters) = if self.state != SessionState::SessionStateInit - && self.state != SessionState::SessionStateActive - { - (StatusCode::UciStatusRejected, Vec::new()) - } else { - let mut app_config = self.app_config.clone(); - let invalid_parameters = app_config.extend(cmd.get_tlvs()); - if invalid_parameters.is_empty() { - self.app_config = app_config; - if self.state == SessionState::SessionStateInit { - self.set_state( - SessionState::SessionStateIdle, - ReasonCode::StateChangeWithSessionManagementCommands, - ); - } - (StatusCode::UciStatusOk, invalid_parameters) - } else { - (StatusCode::UciStatusInvalidParam, invalid_parameters) - } - }; - - SessionSetAppConfigRspBuilder { - status, - cfg_status: invalid_parameters, - } - .build() - } - - fn command_get_app_config(&self, cmd: SessionGetAppConfigCmd) -> SessionGetAppConfigRsp { - log::debug!( - "[{}:0x{:x}] Session Get App Config", - self.device_handle, - self.id - ); - 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) - }, - ); - if invalid_parameters.is_empty() { - (StatusCode::UciStatusOk, valid_parameters) - } else { - (StatusCode::UciStatusFailed, Vec::new()) - } - }; - SessionGetAppConfigRspBuilder { - status, - tlvs: valid_parameters, - } - .build() - } - - fn command_get_state(&self, cmd: SessionGetStateCmd) -> SessionGetStateRsp { - log::debug!("[{}:0x{:x}] Session Get State", self.device_handle, self.id); - assert_eq!(self.id, cmd.get_session_token()); - SessionGetStateRspBuilder { - status: StatusCode::UciStatusOk, - session_state: self.state, - } - .build() - } - - fn command_update_controller_multicast_list( - &mut self, - cmd: SessionUpdateControllerMulticastListCmd, - ) -> SessionUpdateControllerMulticastListRsp { - log::debug!( - "[{}:0x{:x}] Session Update Controller Multicast List", - self.device_handle, - self.id - ); - 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) - { - 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 new_controlees: Vec<Controlee> = match action { - UpdateMulticastListAction::Add | UpdateMulticastListAction::Delete => { - if let Ok(packet) = - SessionUpdateControllerMulticastListCmdPayload::parse(cmd.get_payload()) - { - packet - .controlees - .iter() - .map(|controlee| controlee.into()) - .collect() - } else { - return SessionUpdateControllerMulticastListRspBuilder { - status: StatusCode::UciStatusSyntaxError, - } - .build(); - } - } - UpdateMulticastListAction::AddWithShortSubSessionKey => { - if let Ok(packet) = - SessionUpdateControllerMulticastListCmd_2_0_16_Byte_Payload::parse( - cmd.get_payload(), - ) - { - packet - .controlees - .iter() - .map(|controlee| controlee.into()) - .collect() - } else { - return SessionUpdateControllerMulticastListRspBuilder { - status: StatusCode::UciStatusSyntaxError, - } - .build(); - } - } - UpdateMulticastListAction::AddwithExtendedSubSessionKey => { - if let Ok(packet) = - SessionUpdateControllerMulticastListCmd_2_0_32_Byte_Payload::parse( - cmd.get_payload(), - ) - { - packet - .controlees - .iter() - .map(|controlee| controlee.into()) - .collect() - } else { - return SessionUpdateControllerMulticastListRspBuilder { - status: StatusCode::UciStatusSyntaxError, - } - .build(); - } - } - }; - let mut controlee_status = Vec::new(); - - let session_id = self.id; - let mut status = StatusCode::UciStatusOk; - - match action { - UpdateMulticastListAction::Add - | UpdateMulticastListAction::AddWithShortSubSessionKey - | UpdateMulticastListAction::AddwithExtendedSubSessionKey => { - 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) - && self.app_config.sts_config - != StsConfig::ProvisionedForControleeIndividualKey - { - // If Action is 0x02 or 0x03 for STS_CONFIG values other than - // 0x04, the UWBS shall return SESSION_UPDATE_CONTROLLER_MULTICAST_LIST_NTF - // with Status set to STATUS_ERROR_SUB_SESSION_KEY_NOT_APPLICABLE for each - // Controlee in the Controlee List. - status = StatusCode::UciStatusFailed; - update_status = - MulticastUpdateStatusCode::StatusErrorSubSessionKeyNotApplicable; - } else { - dst_addresses.push(controlee.short_address); - }; - } - controlee_status.push(ControleeStatus { - mac_address: match controlee.short_address { - MacAddress::Short(address) => address, - MacAddress::Extended(_) => panic!("Extended address is not supported!"), - }, - subsession_id: controlee.sub_session_id, - status: update_status, - }); - }); - } - UpdateMulticastListAction::Delete => { - new_controlees.iter().for_each(|controlee: &Controlee| { - let pica_tx = self.pica_tx.clone(); - let address = controlee.short_address; - let attempt_count = self.app_config.in_band_termination_attempt_count; - let mut update_status = MulticastUpdateStatusCode::StatusOkMulticastListUpdate; - if !dst_addresses.contains(&address) { - status = StatusCode::UciStatusAddressNotFound; - update_status = MulticastUpdateStatusCode::StatusErrorKeyFetchFail; - } else { - dst_addresses.retain(|value| *value != address); - // If IN_BAND_TERMINATION_ATTEMPT_COUNT is not equal to 0x00, then the - // UWBS shall transmit the RCM with the “Stop Ranging” bit set to ‘1’ - // for IN_BAND_TERMINATION_ATTEMPT_COUNT times to the corresponding - // Controlee. - if attempt_count != 0 { - tokio::spawn(async move { - for _ in 0..attempt_count { - pica_tx - .send(PicaCommand::StopRanging(address, session_id)) - .await - .unwrap() - } - }); - } - } - controlee_status.push(ControleeStatus { - mac_address: match address { - MacAddress::Short(addr) => addr, - MacAddress::Extended(_) => panic!("Extended address is not supported!"), - }, - subsession_id: controlee.sub_session_id, - status: update_status, - }); - }); - } - } - self.app_config.number_of_controlees = dst_addresses.len(); - self.app_config.dst_mac_addresses = 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() { - self.set_state( - SessionState::SessionStateIdle, - ReasonCode::ErrorInvalidNumOfControlees, - ) - } - let tx = self.tx.clone(); - tokio::spawn(async move { - tx.send( - SessionUpdateControllerMulticastListNtfBuilder { - controlee_status, - remaining_multicast_list_size: dst_addresses.len() as u8, - session_token: session_id, - } - .build() - .into(), - ) - .unwrap() - }); - SessionUpdateControllerMulticastListRspBuilder { status }.build() - } - - fn command_range_start(&mut self, cmd: SessionStartCmd) -> SessionStartRsp { - log::debug!("[{}:0x{:x}] Range Start", self.device_handle, self.id); - assert_eq!(self.id, cmd.get_session_id()); - - let status = if self.state != SessionState::SessionStateIdle { - StatusCode::UciStatusSessionNotConfigured - } else { - assert!(self.ranging_task.is_none()); - assert_eq!(self.state, SessionState::SessionStateIdle); - - let session_id = self.id; - let ranging_interval = self.app_config.ranging_interval; - let device_handle = self.device_handle; - let tx = self.pica_tx.clone(); - self.ranging_task = Some(tokio::spawn(async move { - loop { - time::sleep(ranging_interval).await; - tx.send(PicaCommand::Ranging(device_handle, session_id)) - .await - .unwrap(); - } - })); - self.set_state( - SessionState::SessionStateActive, - ReasonCode::StateChangeWithSessionManagementCommands, - ); - StatusCode::UciStatusOk - }; - SessionStartRspBuilder { status }.build() - } - pub fn stop_ranging_task(&mut self) { if let Some(handle) = &self.ranging_task { handle.abort(); self.ranging_task = None; } } - fn command_range_stop(&mut self, cmd: SessionStopCmd) -> SessionStopRsp { - log::debug!("[{}:0x{:x}] Range Stop", self.device_handle, self.id); - assert_eq!(self.id, cmd.get_session_id()); - - let status = if self.state != SessionState::SessionStateActive { - StatusCode::UciStatusSessionActive - } else { - self.stop_ranging_task(); - self.set_state( - SessionState::SessionStateIdle, - ReasonCode::StateChangeWithSessionManagementCommands, - ); - StatusCode::UciStatusOk - }; - SessionStopRspBuilder { status }.build() - } - - fn command_get_ranging_count( - &self, - cmd: SessionGetRangingCountCmd, - ) -> SessionGetRangingCountRsp { - log::debug!( - "[{}:0x{:x}] Range Get Ranging Count", - self.device_handle, - self.id - ); - assert_eq!(self.id, cmd.get_session_id()); - - SessionGetRangingCountRspBuilder { - status: StatusCode::UciStatusOk, - count: self.sequence_number, - } - .build() - } - - pub fn session_command(&mut self, cmd: SessionConfigCommand) -> SessionConfigResponse { - match cmd.specialize() { - SessionConfigCommandChild::SessionSetAppConfigCmd(cmd) => { - self.command_set_app_config(cmd).into() - } - SessionConfigCommandChild::SessionGetAppConfigCmd(cmd) => { - self.command_get_app_config(cmd).into() - } - SessionConfigCommandChild::SessionGetStateCmd(cmd) => { - self.command_get_state(cmd).into() - } - SessionConfigCommandChild::SessionUpdateControllerMulticastListCmd(cmd) => { - self.command_update_controller_multicast_list(cmd).into() - } - _ => panic!("Unsupported session command"), - } - } - - pub fn ranging_command(&mut self, cmd: SessionControlCommand) -> SessionControlResponse { - match cmd.specialize() { - SessionControlCommandChild::SessionStartCmd(cmd) => { - self.command_range_start(cmd).into() - } - SessionControlCommandChild::SessionStopCmd(cmd) => self.command_range_stop(cmd).into(), - SessionControlCommandChild::SessionGetRangingCountCmd(cmd) => { - self.command_get_ranging_count(cmd).into() - } - _ => panic!("Unsupported ranging command"), - } - } - pub fn data_message_snd(&mut self, data: DataMessageSnd) -> SessionControlNotification { + pub fn data_message_snd(&mut self, data: DataMessageSnd) -> ControlPacket { log::debug!("[{}] data_message_snd", self.device_handle); let session_token = data.get_session_handle(); let uci_sequence_number = data.get_data_sequence_number() as u8; if self.session_type != SessionType::FiraRangingAndInBandDataSession { - return DataTransferStatusNtfBuilder { + return SessionDataTransferStatusNtfBuilder { session_token, status: DataTransferNtfStatusCode::UciDataTransferStatusSessionTypeNotSupported, tx_count: 1, // TODO: support for retries? @@ -1230,7 +150,7 @@ impl Session { self.data.extend_from_slice(data.get_application_data()); - DataCreditNtfBuilder { + SessionDataCreditNtfBuilder { credit_availability: CreditAvailability::CreditAvailable, session_token, } diff --git a/src/uci_packets.pdl b/src/uci_packets.pdl index 07b19b6..05ba3f3 100644 --- a/src/uci_packets.pdl +++ b/src/uci_packets.pdl @@ -15,154 +15,126 @@ little_endian_packets enum PacketBoundaryFlag : 1 { - COMPLETE = 0x00, - NOT_COMPLETE = 0x01, + COMPLETE = 0, + NOT_COMPLETE = 1, +} + +enum MessageType : 3 { + DATA = 0x0, + COMMAND = 0x1, + RESPONSE = 0x2, + NOTIFICATION = 0x3, } enum GroupId : 4 { - CORE = 0x00, - SESSION_CONFIG = 0x01, - SESSION_CONTROL = 0x02, - DATA_CONTROL = 0x03, - TEST = 0x0d, - VENDOR_RESERVED_9 = 0x09, - VENDOR_RESERVED_A = 0x0a, - VENDOR_RESERVED_B = 0x0b, - VENDOR_ANDROID = 0x0c, - VENDOR_RESERVED_E = 0x0e, - VENDOR_RESERVED_F = 0x0f, -} - -enum DataPacketFormat: 4 { + CORE = 0x0, + SESSION_CONFIG = 0x1, + SESSION_CONTROL = 0x2, + DATA_CONTROL = 0x3, + VENDOR_RESERVED_9 = 0x9, + VENDOR_RESERVED_A = 0xa, + VENDOR_RESERVED_B = 0xb, + VENDOR_ANDROID = 0xc, + TEST = 0xd, + VENDOR_RESERVED_E = 0xe, + VENDOR_RESERVED_F = 0xf, +} + +enum DataPacketFormat : 4 { DATA_SND = 0x01, DATA_RCV = 0x02, } -// Define a merged enum across GroupId & DataPacketFormat as they are at the same bits in -// |UciPacketHal|. -enum GroupIdOrDataPacketFormat : 4 { - CORE = 0x00, - SESSION_CONFIG_OR_DATA_SND = 0x01, - SESSION_CONTROL_OR_DATA_RCV = 0x02, - DATA_CONTROL = 0x03, - TEST = 0x0d, - VENDOR_RESERVED_9 = 0x09, - VENDOR_RESERVED_A = 0x0a, - VENDOR_RESERVED_B = 0x0b, - VENDOR_ANDROID = 0x0c, - VENDOR_RESERVED_E = 0x0e, - VENDOR_RESERVED_F = 0x0f, -} - -enum CoreOpCode : 6 { - CORE_DEVICE_RESET = 0x00, - CORE_DEVICE_STATUS_NTF = 0x01, - CORE_DEVICE_INFO = 0x02, - CORE_GET_CAPS_INFO = 0x03, - CORE_SET_CONFIG = 0x04, - CORE_GET_CONFIG = 0x05, - CORE_DEVICE_SUSPEND = 0x06, - CORE_GENERIC_ERROR_NTF = 0x07, - CORE_QUERY_UWBS_TIMESTAMP = 0x08, -} - -enum SessionConfigOpCode : 6 { - SESSION_INIT = 0x00, - SESSION_DEINIT = 0x01, - SESSION_STATUS_NTF = 0x02, - SESSION_SET_APP_CONFIG = 0x03, - SESSION_GET_APP_CONFIG = 0x04, - SESSION_GET_COUNT = 0x05, - SESSION_GET_STATE = 0x06, - SESSION_UPDATE_CONTROLLER_MULTICAST_LIST = 0x07, - SESSION_UPDATE_ACTIVE_ROUNDS_ANCHOR = 0x08, - SESSION_UPDATE_ACTIVE_ROUNDS_DT_TAG = 0x09, - SESSION_SET_INITIATOR_DT_ANCHOR_RR_RDM_LIST = 0x0a, - SESSION_QUERY_DATA_SIZE_IN_RANGING = 0x0b, - SESSION_SET_HUS_CONFIG = 0x0c, -} - -enum SessionControlOpCode : 6 { - SESSION_START = 0x00, - SESSION_STOP = 0x01, - SESSION_RESERVED = 0x02, - SESSION_GET_RANGING_COUNT = 0x03, - SESSION_DATA_CREDIT_NTF = 0x04, - SESSION_DATA_TRANSFER_STATUS_NTF = 0x05, -} - -enum AppDataOpCode : 6 { - APP_DATA_TX = 0x00, - APP_DATA_RX = 0x01, -} - -// Android vendor commands -enum AndroidOpCode : 6 { - ANDROID_GET_POWER_STATS = 0x0, - ANDROID_SET_COUNTRY_CODE = 0x1, - ANDROID_FIRA_RANGE_DIAGNOSTICS = 0x2, -} - -enum StatusCode : 8 { +enum CoreOpcodeId : 6 { + DEVICE_RESET = 0x00, + DEVICE_STATUS = 0x01, + GET_DEVICE_INFO = 0x02, + GET_CAPS_INFO = 0x03, + SET_CONFIG = 0x04, + GET_CONFIG = 0x05, + GENERIC_ERROR = 0x07, + QUERY_UWBS_TIMESTAMP = 0x08, +} + +enum SessionConfigOpcodeId : 6 { + INIT = 0x00, + DEINIT = 0x01, + STATUS = 0x02, + SET_APP_CONFIG = 0x03, + GET_APP_CONFIG = 0x04, + GET_COUNT = 0x05, + GET_STATE = 0x06, + UPDATE_CONTROLLER_MULTICAST_LIST = 0x07, + UPDATE_DT_ANCHOR_RANGING_ROUNDS = 0x08, + UPDATE_DT_TAG_RANGING_ROUNDS = 0x09, + QUERY_DATA_SIZE_IN_RANGING = 0x0b, +} + +enum SessionControlOpcodeId : 6 { + START = 0x00, // INFO_NTF + STOP = 0x01, + GET_RANGING_COUNT = 0x03, + DATA_CREDIT = 0x04, + DATA_TRANSFER_STATUS = 0x05, +} + +enum AndroidOpcodeId : 6 { + GET_POWER_STATS = 0x00, + SET_COUNTRY_CODE = 0x01, + FIRA_RANGE_DIAGNOSTICS = 0x02, +} + +/// [UCI] 8.5 Status Codes +enum Status : 8 { // Generic Status Codes - UCI_STATUS_OK = 0x00, - UCI_STATUS_REJECTED = 0x01, - UCI_STATUS_FAILED = 0x02, - UCI_STATUS_SYNTAX_ERROR = 0x03, - UCI_STATUS_INVALID_PARAM = 0x04, - UCI_STATUS_INVALID_RANGE = 0x05, - UCI_STATUS_INVALID_MSG_SIZE = 0x06, - UCI_STATUS_UNKNOWN_GID = 0x07, - UCI_STATUS_UNKNOWN_OID = 0x08, - UCI_STATUS_READ_ONLY = 0x09, - UCI_STATUS_COMMAND_RETRY = 0x0A, - UCI_STATUS_UNKNOWN = 0x0B, - UCI_STATUS_NOT_APPLICABLE = 0x0C, - RFU_STATUS_CODE_RANGE_1 = 0x0D..0x10, + OK = 0x00, + REJECTED = 0x01, + FAILED = 0x02, + SYNTAX_ERROR = 0x03, + INVALID_PARAM = 0x04, + INVALID_RANGE = 0x05, + INVALID_MESSAGE_SIZE = 0x06, + UNKNOWN_GID = 0x07, + UNKNOWN_OID = 0x08, + READ_ONLY = 0x09, + UCI_MESSAGE_RETRY = 0x0A, + UNKNOWN = 0x0B, + NOT_APPLICABLE = 0x0C, // UWB Session Specific Status Codes - UCI_STATUS_SESSION_NOT_EXIST = 0x11, - UCI_STATUS_SESSION_DUPLICATE = 0x12, - UCI_STATUS_SESSION_ACTIVE = 0x13, - UCI_STATUS_MAX_SESSIONS_EXCEEDED = 0x14, - UCI_STATUS_SESSION_NOT_CONFIGURED = 0x15, - UCI_STATUS_ACTIVE_SESSIONS_ONGOING = 0x16, - UCI_STATUS_MULTICAST_LIST_FULL = 0x17, - UCI_STATUS_ADDRESS_NOT_FOUND = 0x18, - UCI_STATUS_ADDRESS_ALREADY_PRESENT = 0x19, - UCI_STATUS_ERROR_UWB_INITIATION_TIME_TOO_OLD = 0x1A, - UCI_STATUS_OK_NEGATIVE_DISTANCE_REPORT = 0x1B, - RFU_STATUS_CODE_RANGE_2 = 0x1C..0x1F, + ERROR_SESSION_NOT_EXIST = 0x11, + ERROR_SESSION_DUPLICATE = 0x12, + ERROR_SESSION_ACTIVE = 0x13, + ERROR_MAX_SESSIONS_EXCEEDED = 0x14, + ERROR_SESSION_NOT_CONFIGURED = 0x15, + ERROR_ACTIVE_SESSIONS_ONGOING = 0x16, + ERROR_MULTICAST_LIST_FULL = 0x17, + ERROR_UWB_INITIATION_TIME_TOO_OLD = 0x1A, + OK_NEGATIVE_DISTANCE_REPORT = 0x1B, // UWB Ranging Session Specific Status Codes - UCI_STATUS_RANGING_TX_FAILED = 0x20, - UCI_STATUS_RANGING_RX_TIMEOUT = 0x21, - UCI_STATUS_RANGING_RX_PHY_DEC_FAILED = 0x22, - UCI_STATUS_RANGING_RX_PHY_TOA_FAILED = 0x23, - UCI_STATUS_RANGING_RX_PHY_STS_FAILED = 0x24, - UCI_STATUS_RANGING_RX_MAC_DEC_FAILED = 0x25, - UCI_STATUS_RANGING_RX_MAC_IE_DEC_FAILED = 0x26, - UCI_STATUS_RANGING_RX_MAC_IE_MISSING = 0x27, - UCI_STATUS_ERROR_ROUND_INDEX_NOT_ACTIVATED = 0x28, - UCI_STATUS_ERROR_NUMBER_OF_ACTIVE_RANGING_ROUNDS_EXCEEDED = 0x29, - UCI_STATUS_ERROR_DL_TDOA_DEVICE_ADDRESS_NOT_MATCHING_IN_REPLY_TIME_LIST = 0x2A, - RFU_STATUS_CODE_RANGE_3 = 0x2B..0x2F, - - // UWB Data Session Specific Status Codes - UCI_STATUS_DATA_MAX_TX_PSDU_SIZE_EXCEEDED = 0x30, - UCI_STATUS_DATA_RX_CRC_ERROR = 0x31, - RFU_STATUS_CODE_RANGE_4 = 0x32..0x4F, + RANGING_TX_FAILED = 0x20, + RANGING_RX_TIMEOUT = 0x21, + RANGING_RX_PHY_DEC_FAILED = 0x22, + RANGING_RX_PHY_TOA_FAILED = 0x23, + RANGING_RX_PHY_STS_FAILED = 0x24, + RANGING_RX_MAC_DEC_FAILED = 0x25, + RANGING_RX_MAC_IE_DEC_FAILED = 0x26, + RANGING_RX_MAC_IE_MISSING = 0x27, + ERROR_ROUND_INDEX_NOT_ACTIVATED = 0x28, + ERROR_NUMBER_OF_ACTIVE_RANGING_ROUNDS_EXCEEDED = 0x29, + ERROR_DL_TDOA_DEVICE_ADDRESS_NOT_MATCHING_IN_REPLY_TIME_LIST = 0x2A, // Vendor Specific Status Codes - VENDOR_SPECIFIC_STATUS_CODE_RANGE_1 = 0x50..0xFE { - UCI_STATUS_ERROR_CCC_SE_BUSY = 0x50, - UCI_STATUS_ERROR_CCC_LIFECYCLE = 0x51, - UCI_STATUS_ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT = 0x52, - UCI_STATUS_REGULATION_UWB_OFF = 0x53, + VENDOR_SPECIFIC = 0x50..0xFF { + ERROR_CCC_SE_BUSY = 0x50, + ERROR_CCC_LIFECYCLE = 0x51, + ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT = 0x52, + REGULATION_UWB_OFF = 0x53, }, - // For internal usage, we will use 0xFF as default. - VENDOR_SPECIFIC_STATUS_CODE_2 = 0xFF, + // All others reserved for future use + RFU = .., } // This needs a separate StatusCode as the Status code values in the DATA_RCV packet have @@ -194,18 +166,14 @@ enum ResetConfig : 8 { UWBS_RESET = 0x00, } -enum DeviceConfigId : 8 { - DEVICE_STATE = 0x00, - 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 +182,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 +196,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 +217,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 +227,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 +238,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 +252,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 +265,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 { @@ -441,15 +580,17 @@ enum ReasonCode : 8 { VENDOR_SPECIFIC_REASON_CODE_2 = 0xFF, } -enum MulticastUpdateStatusCode : 8 { - STATUS_OK_MULTICAST_LIST_UPDATE = 0x00, - STATUS_ERROR_MULTICAST_LIST_FULL = 0x01, - STATUS_ERROR_KEY_FETCH_FAIL = 0x02, - STATUS_ERROR_SUB_SESSION_ID_NOT_FOUND = 0x03, - STATUS_ERROR_SUB_SESSION_KEY_NOT_FOUND = 0x05, - STATUS_ERROR_SUB_SESSION_KEY_NOT_APPLICABLE = 0x06, - STATUS_ERROR_SESSION_KEY_NOT_FOUND = 0x07, - STATUS_ERROR_ADDRESS_ALREADY_PRESENT = 0x08, +/// [UCI] Table 40: Multicast list update status codes +enum MulticastUpdateStatus : 8 { + OK_MULTICAST_LIST_UPDATE = 0x00, + ERROR_MULTICAST_LIST_FULL = 0x01, + ERROR_KEY_FETCH_FAIL = 0x02, + ERROR_SUB_SESSION_ID_NOT_FOUND = 0x03, + ERROR_SUB_SESSION_KEY_NOT_FOUND = 0x04, + ERROR_SUB_SESSION_KEY_NOT_APPLICABLE = 0x05, + ERROR_SESSION_KEY_NOT_FOUND = 0x06, + ERROR_ADDRESS_NOT_FOUND = 0x07, + ERROR_ADDRESS_ALREADY_PRESENT = 0x08, } enum MacAddressIndicator : 8 { @@ -468,15 +609,6 @@ enum SessionType: 8 { DEVICE_TEST_MODE = 0xD0, } -enum MessageType: 3 { - DATA = 0x00, - COMMAND = 0x01, - RESPONSE = 0x02, - NOTIFICATION = 0x03, - RESERVED_FOR_TESTING_1 = 0x04, - RESERVED_FOR_TESTING_2 = 0x05, -} - // Used to parse message type packet CommonPacketHeader { _reserved_ : 4, @@ -509,9 +641,6 @@ packet ControlPacket { gid : GroupId, _reserved_ : 1, mt : MessageType, - opcode: 6, - _reserved_: 2, - _reserved_: 16, _payload_, } @@ -534,109 +663,76 @@ packet DataMessageSnd : DataPacket (dpf = DATA_SND, mt = DATA) { packet DataMessageRcv : DataPacket (dpf = DATA_RCV, mt = DATA) { session_handle: 32, - status : StatusCode, + status: Status, source_address: 64, data_sequence_number: 16, _size_(application_data): 16, application_data: 8[] } -// TODO(b/202760099): Handle fragmentation of packets if the size exceed max allowed. -packet UciCommand : ControlPacket (mt = COMMAND) { - _payload_, -} - -packet UciResponse : ControlPacket (mt = RESPONSE) { - _payload_, -} - -packet UciNotification : ControlPacket (mt = NOTIFICATION) { - _payload_, -} - -packet CoreCommand : UciCommand (gid = CORE) { - _body_, -} - -packet CoreResponse : UciResponse (gid = CORE) { - _body_, -} - -packet CoreNotification : UciNotification (gid = CORE) { - _body_, -} - -packet SessionConfigCommand : UciCommand (gid = SESSION_CONFIG) { - _body_, -} - -packet SessionConfigResponse : UciResponse (gid = SESSION_CONFIG) { - _body_, -} - -packet SessionConfigNotification : UciNotification (gid = SESSION_CONFIG) { - _body_, -} - -packet SessionControlCommand : UciCommand (gid = SESSION_CONTROL) { - session_id: 32, - _body_, -} - -packet SessionControlResponse : UciResponse (gid = SESSION_CONTROL) { - _body_, +packet CorePacket : ControlPacket (gid = CORE) { + oid : CoreOpcodeId, + _reserved_ : 2, + _reserved_ : 16, + _payload_, } -packet SessionControlNotification : UciNotification (gid = SESSION_CONTROL) { - _body_, +packet SessionConfigPacket : ControlPacket (gid = SESSION_CONFIG) { + oid : SessionConfigOpcodeId, + _reserved_ : 2, + _reserved_ : 16, + _payload_, } -packet AndroidCommand : UciCommand (gid = VENDOR_ANDROID) { - _body_, +packet SessionControlPacket : ControlPacket (gid = SESSION_CONTROL) { + oid : SessionControlOpcodeId, + _reserved_ : 2, + _reserved_ : 16, + _payload_, } -packet AndroidResponse : UciResponse (gid = VENDOR_ANDROID) { - _body_, +packet AndroidPacket : ControlPacket (gid = VENDOR_ANDROID) { + oid : AndroidOpcodeId, + _reserved_ : 2, + _reserved_ : 16, + _payload_, } -packet AndroidNotification : UciNotification (gid = VENDOR_ANDROID) { - _body_, -} +// ---------------------------- Core group ---------------------------------- // -// TODO: b/202760099: Use the correspnding opcode enum instead of the raw value in the |opcode| field. -packet DeviceResetCmd : CoreCommand (opcode = 0x0) { //CORE_DEVICE_RESET +packet CoreDeviceResetCmd : CorePacket (mt = COMMAND, oid = DEVICE_RESET) { reset_config: ResetConfig, } -test DeviceResetCmd { +test CoreDeviceResetCmd { "\x20\x00\x00\x01\x00\x00\x00\x00", } -packet DeviceResetRsp : CoreResponse (opcode = 0x0) { //CORE_DEVICE_RESET - status: StatusCode, +packet CoreDeviceResetRsp : CorePacket (mt = RESPONSE, oid = DEVICE_RESET) { + status: Status, } -test DeviceResetRsp { +test CoreDeviceResetRsp { "\x40\x00\x00\x01\x00\x00\x00\x00", } -packet DeviceStatusNtf : CoreNotification (opcode = 0x1) { //CORE_DEVICE_STATUS_NTF +packet CoreDeviceStatusNtf : CorePacket (mt = NOTIFICATION, oid = DEVICE_STATUS) { device_state: DeviceState, } -test DeviceStatusNtf { +test CoreDeviceStatusNtf { "\x60\x01\x00\x01\x00\x00\x00\x01", } -packet GetDeviceInfoCmd : CoreCommand (opcode = 0x2) { //CORE_DEVICE_INFO +packet CoreGetDeviceInfoCmd : CorePacket (mt = COMMAND, oid = GET_DEVICE_INFO) { } -test GetDeviceInfoCmd { +test CoreGetDeviceInfoCmd { "\x20\x02\x00\x00\x00\x00\x00", } -packet GetDeviceInfoRsp : CoreResponse (opcode = 0x2) { //CORE_DEVICE_INFO - status: StatusCode, +packet CoreGetDeviceInfoRsp : CorePacket (mt = RESPONSE, oid = GET_DEVICE_INFO) { + status: Status, uci_version: 16, mac_version: 16, phy_version: 16, @@ -645,14 +741,14 @@ packet GetDeviceInfoRsp : CoreResponse (opcode = 0x2) { //CORE_DEVICE_INFO vendor_spec_info: 8[], } -test GetDeviceInfoRsp { +test CoreGetDeviceInfoRsp { "\x40\x02\x00\x0b\x00\x00\x00\x01\x01\x00\x02\x00\x03\x00\x04\x00\x01\x0a", } -packet GetCapsInfoCmd : CoreCommand (opcode = 0x3) { //CORE_GET_CAPS_INFO +packet CoreGetCapsInfoCmd : CorePacket (mt = COMMAND, oid = GET_CAPS_INFO) { } -test GetCapsInfoCmd { +test CoreGetCapsInfoCmd { "\x20\x03\x00\x00\x00\x00\x00", } @@ -663,84 +759,91 @@ struct CapTlv { } -packet GetCapsInfoRsp : CoreResponse (opcode = 0x3) { //CORE_GET_CAPS_INFO - status: StatusCode, +packet CoreGetCapsInfoRsp : CorePacket (mt = RESPONSE, oid = GET_CAPS_INFO) { + status: Status, _count_(tlvs): 8, tlvs: CapTlv[], } -test GetCapsInfoRsp { +test CoreGetCapsInfoRsp { "\x40\x03\x00\x05\x00\x00\x00\x00\x01\x00\x01\x01", } -struct DeviceConfigTlv { - cfg_id: DeviceConfigId, - _count_(v): 8, - v: 8[], +// [UCI] Table 44: Device Configuration Parameters +enum ConfigParameterId : 8 { + DEVICE_STATE = 0x00, + LOW_POWER_MODE = 0x01, + RFU = .., } -packet SetConfigCmd : CoreCommand (opcode = 0x4) { //CORE_SET_CONFIG - _count_(tlvs): 8, - tlvs: DeviceConfigTlv[], +struct ConfigParameter { + id: ConfigParameterId, + _size_(value): 8, + value: 8[], } -test SetConfigCmd { +packet CoreSetConfigCmd : CorePacket (mt = COMMAND, oid = SET_CONFIG) { + _count_(parameters): 8, + parameters: ConfigParameter[], +} + +test CoreSetConfigCmd { "\x20\x04\x00\x03\x00\x00\x00\x01\x01\x00", } -struct DeviceConfigStatus { - cfg_id: DeviceConfigId, - status: StatusCode, +struct ConfigParameterStatus { + id: ConfigParameterId, + status: Status, } -packet SetConfigRsp : CoreResponse (opcode = 0x4) { //CORE_SET_CONFIG - status: StatusCode, - _count_(cfg_status): 8, - cfg_status: DeviceConfigStatus[], +packet CoreSetConfigRsp : CorePacket (mt = RESPONSE, oid = SET_CONFIG) { + status: Status, + _count_(parameters): 8, + parameters: ConfigParameterStatus[], } -test SetConfigRsp { +test CoreSetConfigRsp { "\x40\x04\x00\x04\x00\x00\x00\x01\x01\x01\x01", "\x40\x04\x00\x04\x00\x00\x00\x01\x01\x01\x0B", } -packet GetConfigCmd : CoreCommand (opcode = 0x5) { //CORE_GET_CONFIG - _count_(cfg_id): 8, - cfg_id: 8[], // DeviceConfigId (Infra does not allow array of enums) +packet CoreGetConfigCmd : CorePacket (mt = COMMAND, oid = GET_CONFIG) { + _count_(parameter_ids): 8, + parameter_ids: ConfigParameterId[], } -test GetConfigCmd { +test CoreGetConfigCmd { "\x20\x05\x00\x02\x00\x00\x00\x01\x01", } -packet GetConfigRsp : CoreResponse (opcode = 0x5) { //CORE_GET_CONFIG - status: StatusCode, - _count_(tlvs): 8, - tlvs: DeviceConfigTlv[] +packet CoreGetConfigRsp : CorePacket (mt = RESPONSE, oid = GET_CONFIG) { + status: Status, + _count_(parameters): 8, + parameters: ConfigParameter[] } -test GetConfigRsp { +test CoreGetConfigRsp { "\x40\x05\x00\x05\x00\x00\x00\x01\x01\x00\x01\x01", } -packet GenericError : CoreNotification (opcode = 0x7) { //CORE_GENERIC_ERROR_NTF - status: StatusCode, +packet CoreGenericErrorNtf : CorePacket (mt = NOTIFICATION, oid = GENERIC_ERROR) { + status: Status, } -test GenericError { +test CoreGenericErrorNtf { "\x60\x07\x00\x01\x00\x00\x00\x01", } -packet CoreQueryTimeStampCmd : CoreCommand (opcode = 0x8) { //CORE_QUERY_UWBS_TIMESTAMP +packet CoreQueryTimeStampCmd : CorePacket (mt = COMMAND, oid = QUERY_UWBS_TIMESTAMP) { } test CoreQueryTimeStampCmd { "\x20\x08\x00\\x00", } -packet CoreQueryTimeStampRsp : CoreResponse (opcode = 0x8) { //CORE_QUERY_UWBS_TIMESTAMP - status: StatusCode, +packet CoreQueryTimeStampRsp : CorePacket (mt = RESPONSE, oid = QUERY_UWBS_TIMESTAMP) { + status: Status, timeStamp: 64, } @@ -748,7 +851,9 @@ test CoreQueryTimeStampRsp { "\x40\x08\x00\x09\x00\x00\x00\x01\x01\x00\x01\x01\x01", } -packet SessionInitCmd : SessionConfigCommand (opcode = 0x0) { //SESSION_INIT +// ---------------------- Session Config group ------------------------------ // + +packet SessionInitCmd : SessionConfigPacket (mt = COMMAND, oid = INIT) { session_id: 32, session_type: SessionType, } @@ -759,8 +864,8 @@ test SessionInitCmd { // FIRA version 2 introduces a new version of SESSION_INIT_RSP which // includes UWBS generated session handle. -packet SessionInitRsp_V2 : SessionConfigResponse (opcode = 0x0) { //SESSION_INIT - status: StatusCode, +packet SessionInitRsp_V2 : SessionConfigPacket (mt = RESPONSE, oid = INIT) { + status: Status, session_handle: 32, } @@ -768,15 +873,15 @@ test SessionInitRsp_V2 { "\x41\x00\x00\x01\x00\x00\x00\x11\x00\x00\x00\x01", } -packet SessionInitRsp : SessionConfigResponse (opcode = 0x0) { //SESSION_INIT - status: StatusCode, +packet SessionInitRsp : SessionConfigPacket (mt = RESPONSE, oid = INIT) { + status: Status, } test SessionInitRsp { "\x41\x00\x00\x01\x00\x00\x00\x11", } -packet SessionDeinitCmd : SessionConfigCommand (opcode = 0x1) { //SESSION_DEINIT +packet SessionDeinitCmd : SessionConfigPacket (mt = COMMAND, oid = DEINIT) { session_token: 32, // Session ID or Session Handle (based on UWBS version) } @@ -784,15 +889,15 @@ test SessionDeinitCmd { "\x21\x01\x00\x04\x00\x00\x00\x01\x02\x03\x04", } -packet SessionDeinitRsp : SessionConfigResponse (opcode = 0x1) { //SESSION_DEINIT - status: StatusCode, +packet SessionDeinitRsp : SessionConfigPacket (mt = RESPONSE, oid = DEINIT) { + status: Status, } test SessionDeinitRsp { "\x41\x01\x00\x01\x00\x00\x00\x00", } -packet SessionStatusNtf : SessionConfigNotification (opcode = 0x2) { //SESSION_STATUS_NTF +packet SessionStatusNtf : SessionConfigPacket (mt = NOTIFICATION, oid = STATUS) { session_token: 32, // Session ID or Session Handle (based on UWBS version) session_state: SessionState, // TODO(b/272775225): Switch back to the enum type ReasonCode, once PDL supports defining a @@ -811,7 +916,7 @@ struct AppConfigTlv { v: 8[], } -packet SessionSetAppConfigCmd : SessionConfigCommand (opcode = 0x3) { //SESSION_SET_APP_CONFIG +packet SessionSetAppConfigCmd : SessionConfigPacket (mt = COMMAND, oid = SET_APP_CONFIG) { session_token: 32, // Session ID or Session Handle (based on UWBS version) _count_(tlvs): 8, tlvs: AppConfigTlv[] @@ -823,11 +928,11 @@ test SessionSetAppConfigCmd { struct AppConfigStatus { cfg_id: AppConfigTlvType, - status: StatusCode, + status: Status, } -packet SessionSetAppConfigRsp : SessionConfigResponse (opcode = 0x3) { //SESSION_SET_APP_CONFIG - status: StatusCode, +packet SessionSetAppConfigRsp : SessionConfigPacket (mt = RESPONSE, oid = SET_APP_CONFIG) { + status: Status, _count_(cfg_status): 8, cfg_status: AppConfigStatus[], } @@ -836,18 +941,18 @@ test SessionSetAppConfigRsp { "\x41\x03\x00\x04\x00\x00\x00\x01\x01\x01\x00", } -packet SessionGetAppConfigCmd : SessionConfigCommand (opcode = 0x4) { //SESSION_GET_APP_CONFIG +packet SessionGetAppConfigCmd : SessionConfigPacket (mt = COMMAND, oid = 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 { "\x21\x04\x00\x05\x00\x00\x00\x01\x02\x03\x04\x00", } -packet SessionGetAppConfigRsp : SessionConfigResponse (opcode = 0x4) { //SESSION_GET_APP_CONFIG - status: StatusCode, +packet SessionGetAppConfigRsp : SessionConfigPacket (mt = RESPONSE, oid = GET_APP_CONFIG) { + status: Status, _count_(tlvs): 8, tlvs: AppConfigTlv[], } @@ -856,15 +961,15 @@ test SessionGetAppConfigRsp { "\x41\x04\x00\x02\x00\x00\x00\x01\x00", } -packet SessionGetCountCmd : SessionConfigCommand (opcode = 0x5) { //SESSION_GET_COUNT +packet SessionGetCountCmd : SessionConfigPacket (mt = COMMAND, oid = GET_COUNT) { } test SessionGetCountCmd { "\x21\x05\x00\x00\x00\x00\x00", } -packet SessionGetCountRsp : SessionConfigResponse (opcode = 0x5) { //SESSION_GET_COUNT - status: StatusCode, +packet SessionGetCountRsp : SessionConfigPacket (mt = RESPONSE, oid = GET_COUNT) { + status: Status, session_count: 8, } @@ -872,7 +977,7 @@ test SessionGetCountRsp { "\x41\x05\x00\x02\x00\x00\x00\x00\x01", } -packet SessionGetStateCmd : SessionConfigCommand (opcode = 0x6) { //SESSION_GET_STATE +packet SessionGetStateCmd : SessionConfigPacket (mt = COMMAND, oid = GET_STATE) { session_token: 32, // Session ID or Session Handle (based on UWBS version) } @@ -880,8 +985,8 @@ test SessionGetStateCmd { "\x21\x06\x00\x04\x00\x00\x00\x00\x01\x02\x03", } -packet SessionGetStateRsp : SessionConfigResponse (opcode = 0x6) { //SESSION_GET_STATE - status: StatusCode, +packet SessionGetStateRsp : SessionConfigPacket (mt = RESPONSE, oid = GET_STATE) { + status: Status, session_state: SessionState, } @@ -889,7 +994,15 @@ test SessionGetStateRsp { "\x41\x06\x00\x02\x00\x00\x00\x00\x01", } -packet SessionUpdateDtTagRangingRoundsCmd : SessionConfigCommand (opcode = 0x9) { //SESSION_UPDATE_ACTIVE_ROUNDS_DT_TAG +packet SessionUpdateDtAnchorRangingRoundsCmd : SessionConfigPacket (mt = COMMAND, oid = UPDATE_DT_ANCHOR_RANGING_ROUNDS) { + // TODO +} + +packet SessionUpdateDtAnchorRangingRoundsRsp : SessionConfigPacket (mt = RESPONSE, oid = UPDATE_DT_ANCHOR_RANGING_ROUNDS) { + // TODO +} + +packet SessionUpdateDtTagRangingRoundsCmd : SessionConfigPacket (mt = COMMAND, oid = UPDATE_DT_TAG_RANGING_ROUNDS) { session_token: 32, // Session ID or Session Handle (based on UWBS version) _count_(ranging_round_indexes): 8, ranging_round_indexes: 8[], @@ -899,8 +1012,8 @@ test SessionUpdateDtTagRangingRoundsCmd { "\x21\x09\x00\x0a\x00\x00\x00\x03\x03\x0f\x0c\x05\x08\x00\x00\x00\x00", } -packet SessionUpdateDtTagRangingRoundsRsp : SessionConfigResponse (opcode = 0x9) { //SESSION_UPDATE_ACTIVE_ROUNDS_DT_TAG - status: StatusCode, +packet SessionUpdateDtTagRangingRoundsRsp : SessionConfigPacket (mt = RESPONSE, oid = UPDATE_DT_TAG_RANGING_ROUNDS) { + status: Status, _count_(ranging_round_indexes): 8, ranging_round_indexes: 8[], } @@ -926,36 +1039,20 @@ 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 +packet SessionUpdateControllerMulticastListCmd : SessionConfigPacket (mt = COMMAND, oid = UPDATE_CONTROLLER_MULTICAST_LIST) { session_token: 32, // Session ID or Session Handle (based on UWBS version) action: UpdateMulticastListAction, _payload_, } -struct PhaseList { - session_token: 32, - start_slot_index: 16, - end_slot_index: 16, -} - -packet SessionSetHybridConfigCmd : SessionConfigCommand (opcode = 0x0c) { //SESSION_SET_HUS_CONFIG - session_token: 32, - number_of_phases: 8, - update_time: 8[8], - phase_list: PhaseList[], -} - -packet SessionSetHybridConfigRsp : SessionConfigResponse (opcode = 0x0c) { //SESSION_SET_HUS_CONFIG - status: StatusCode, -} - struct SessionUpdateControllerMulticastListCmdPayload { _count_(controlees): 8, controlees: Controlee[], @@ -971,8 +1068,8 @@ struct SessionUpdateControllerMulticastListCmd_2_0_32_Byte_Payload { controlees: Controlee_V2_0_32_Byte_Version[], } -packet SessionUpdateControllerMulticastListRsp : SessionConfigResponse (opcode = 0x7) { //SESSION_UPDATE_CONTROLLER_MULTICAST_LIST - status: StatusCode, +packet SessionUpdateControllerMulticastListRsp : SessionConfigPacket (mt = RESPONSE, oid = UPDATE_CONTROLLER_MULTICAST_LIST) { + status: Status, } test SessionUpdateControllerMulticastListRsp { @@ -981,13 +1078,11 @@ test SessionUpdateControllerMulticastListRsp { struct ControleeStatus { mac_address: 8[2], - subsession_id: 32, - status: MulticastUpdateStatusCode, + status: MulticastUpdateStatus, } -packet SessionUpdateControllerMulticastListNtf : SessionConfigNotification (opcode = 0x7) { //SESSION_UPDATE_CONTROLLER_MULTICAST_LIST +packet SessionUpdateControllerMulticastListNtf : SessionConfigPacket (mt = NOTIFICATION, oid = UPDATE_CONTROLLER_MULTICAST_LIST) { session_token: 32, // Session ID or Session Handle (based on UWBS version) - remaining_multicast_list_size: 8, _count_(controlee_status): 8, controlee_status: ControleeStatus[], } @@ -996,52 +1091,55 @@ test SessionUpdateControllerMulticastListNtf { "\x61\x07\x00\x06\x00\x00\x00\x00\x01\x02\x03\x04\x00", } -packet DataCreditNtf : SessionControlNotification (opcode = 0x04) { // SESSION_DATA_CREDIT_NTF +// ---------------------- Session Control group ----------------------------- // + +packet SessionDataCreditNtf : SessionControlPacket (mt = NOTIFICATION, oid = DATA_CREDIT) { session_token: 32, // Session ID or Session Handle (based on UWBS version) credit_availability: CreditAvailability, } -test DataCreditNtf { +test SessionDataCreditNtf { "\x62\x04\x00\x05\x00\x00\x00\x00\x00\x00\x01\x01", } -packet DataTransferStatusNtf : SessionControlNotification (opcode = 0x05) { // SESSION_DATA_TRANSFER_STATUS_NTF +packet SessionDataTransferStatusNtf : SessionControlPacket (mt = NOTIFICATION, oid = DATA_TRANSFER_STATUS) { session_token: 32, // Session ID or Session Handle (based on UWBS version) uci_sequence_number: 8, status: DataTransferNtfStatusCode, tx_count: 8, } -test DataTransferStatusNtf { +test SessionDataTransferStatusNtf { "\x62\x05\x00\x06\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00", } -packet SessionQueryMaxDataSizeCmd : SessionConfigCommand (opcode = 0xB) { //QUERY_MAX_DATA_SIZE +packet SessionQueryMaxDataSizeInRangingCmd : SessionConfigPacket (mt = COMMAND, oid = QUERY_DATA_SIZE_IN_RANGING) { session_token: 32, // Session ID or Session Handle (based on UWBS version) } -test SessionQueryMaxDataSizeCmd { +test SessionQueryMaxDataSizeInRangingCmd { "\x21\x0B\x00\x04\x00\x00\x00\x00", } -packet SessionQueryMaxDataSizeRsp : SessionConfigResponse (opcode = 0xB) { //QUER_MAX_DATA_SIZE +packet SessionQueryMaxDataSizeInRangingRsp : SessionConfigPacket (mt = RESPONSE, oid = QUERY_DATA_SIZE_IN_RANGING) { session_token: 32, // Session ID or Session Handle (based on UWBS version) max_data_size: 16, } -test SessionQueryMaxDataSizeRsp { +test SessionQueryMaxDataSizeInRangingRsp { "\x41\x0B\x00\x06\x00\x00\x00\x00\x0E7\0x07", } -packet SessionStartCmd : SessionControlCommand (opcode = 0x0) { //RANGE_START +packet SessionStartCmd : SessionControlPacket (mt = COMMAND, oid = START) { + session_id: 32, } test SessionStartCmd { "\x22\x00\x00\x04\x00\x00\x00\x00\x01\x02\x03", } -packet SessionStartRsp : SessionControlResponse (opcode = 0x0) { //RANGE_START - status: StatusCode, +packet SessionStartRsp : SessionControlPacket (mt = RESPONSE, oid = START) { + status: Status, } test SessionStartRsp { @@ -1050,7 +1148,7 @@ test SessionStartRsp { struct ShortAddressTwoWayRangingMeasurement { mac_address: 16, - status: StatusCode, + status: Status, nlos: 8, distance: 16, aoa_azimuth: 16, @@ -1072,7 +1170,7 @@ struct ShortAddressTwoWayRangingMeasurement { struct ExtendedAddressTwoWayRangingMeasurement { mac_address: 64, - status: StatusCode, + status: Status, nlos: 8, distance: 16, aoa_azimuth: 16, @@ -1090,7 +1188,7 @@ struct ExtendedAddressTwoWayRangingMeasurement { struct ShortAddressOwrAoaRangingMeasurement { mac_address: 16, - status: StatusCode, + status: Status, nlos: 8, frame_sequence_number: 8, block_index: 16, @@ -1102,7 +1200,7 @@ struct ShortAddressOwrAoaRangingMeasurement { struct ExtendedAddressOwrAoaRangingMeasurement { mac_address: 64, - status: StatusCode, + status: Status, nlos: 8, frame_sequence_number: 8, block_index: 16, @@ -1119,7 +1217,7 @@ enum RangingMeasurementType : 8 { OWR_AOA = 0x03, } -packet SessionInfoNtf : SessionControlNotification (opcode = 0x0) { // SESSION_INFO +packet SessionInfoNtf : SessionControlPacket (mt = NOTIFICATION, oid = START) { sequence_number: 32, session_token: 32, // Session ID or Session Handle (based on UWBS version) rcr_indicator: 8, @@ -1191,30 +1289,32 @@ test ExtendedMacOwrAoaSessionInfoNtf { "\x62\x00\x00\x2c\x00\x00\x00\x00\x02\x03\x04\x05\x06\x07\x08\x00\x0a\x01\x01\x01\x03\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xbb\xcc\xdd\x01\x02\x03\x04\x00\x00\x01\x01\x00\x03\x04\x60\x05\x06\x50", } -packet SessionStopCmd : SessionControlCommand (opcode = 0x1) { // SESSION_STOP +packet SessionStopCmd : SessionControlPacket (mt = COMMAND, oid = STOP) { + session_id: 32, } test SessionStopCmd { "\x22\x01\x00\x04\x00\x00\x00\x00\x02\x03\x04", } -packet SessionStopRsp : SessionControlResponse (opcode = 0x1) { // SESSION_STOP - status: StatusCode, +packet SessionStopRsp : SessionControlPacket (mt = RESPONSE, oid = STOP) { + status: Status, } test SessionStopRsp { "\x42\x01\x00\x01\x00\x00\x00\x00", } -packet SessionGetRangingCountCmd : SessionControlCommand (opcode = 0x3) { // SESSION_GET_RANGING_COUNT +packet SessionGetRangingCountCmd : SessionControlPacket (mt = COMMAND, oid = GET_RANGING_COUNT) { + session_id: 32, } test SessionGetRangingCountCmd { "\x22\x03\x00\x04\x00\x00\x00\x00\x02\x03\x04", } -packet SessionGetRangingCountRsp : SessionControlResponse (opcode = 0x3) { // SESSION_GET_RANGING_COUNT - status: StatusCode, +packet SessionGetRangingCountRsp : SessionControlPacket (mt = RESPONSE, oid = GET_RANGING_COUNT) { + status: Status, count: 32, } @@ -1222,7 +1322,9 @@ test SessionGetRangingCountRsp { "\x42\x03\x00\x05\x00\x00\x00\x00\x02\x03\x04\x05", } -packet AndroidGetPowerStatsCmd: AndroidCommand (opcode = 0x0) { //ANDROID_GET_POWER_STATS +// -------------------------- Android group --------------------------------- // + +packet AndroidGetPowerStatsCmd : AndroidPacket (mt = COMMAND, oid = GET_POWER_STATS) { } test AndroidGetPowerStatsCmd { @@ -1230,14 +1332,14 @@ test AndroidGetPowerStatsCmd { } struct PowerStats { - status: StatusCode, + status: Status, idle_time_ms: 32, tx_time_ms: 32, rx_time_ms: 32, total_wake_count:32, } -packet AndroidGetPowerStatsRsp : AndroidResponse (opcode = 0x0) { //ANDROID_GET_POWER_STATS +packet AndroidGetPowerStatsRsp : AndroidPacket (mt = RESPONSE, oid = GET_POWER_STATS) { stats: PowerStats, } @@ -1245,7 +1347,7 @@ test AndroidGetPowerStatsRsp { "\x4c\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", } -packet AndroidSetCountryCodeCmd: AndroidCommand (opcode = 0x1) { //ANDROID_SET_COUNTRY_CODE +packet AndroidSetCountryCodeCmd: AndroidPacket (mt = COMMAND, oid = SET_COUNTRY_CODE) { country_code : 8[2], } @@ -1254,14 +1356,20 @@ test AndroidSetCountryCodeCmd { "\x2c\x01\x00\x02\x00\x00\x00\x55\x53", } -packet AndroidSetCountryCodeRsp : AndroidResponse (opcode = 0x1) { //ANDROID_SET_COUNTRY_CODE - status: StatusCode, +packet AndroidSetCountryCodeRsp : AndroidPacket (mt = RESPONSE, oid = SET_COUNTRY_CODE) { + status: Status, } 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, @@ -1324,7 +1432,7 @@ struct FrameReport { frame_report_tlvs: FrameReportTlv[], } -packet AndroidRangeDiagnosticsNtf : AndroidNotification (opcode = 0x2) { //FIRA_RANGE_DIAGNOSTICS +packet AndroidRangeDiagnosticsNtf : AndroidPacket (mt = NOTIFICATION, oid = FIRA_RANGE_DIAGNOSTICS) { session_token: 32, // Session ID or Session Handle (based on UWBS version) sequence_number: 32, _count_(frame_reports): 8, @@ -1335,67 +1443,3 @@ test AndroidRangeDiagnosticsNtf { "\x6c\x02\x00\x11\x00\x00\x00\x01\x01\x01\x01\x02\x02\x02\x02\x01\x00\x01\x02\x01\x00\x01\x00\x00", "\x6c\x02\x00\x34\x00\x00\x00\x01\x01\x01\x01\x02\x02\x02\x02\x01\x00\x01\x02\x03\x01\x08\x00\x01\x02\x01\x02\x01\x02\x01\x01\x02\x15\x00\x01\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x01\x02\x00\x02\x04\x00\x01\x02\x03\x04\x00\x01\x00\x00", } - -packet UciVendor_9_Command : UciCommand (gid = VENDOR_RESERVED_9) { - _payload_, -} - -packet UciVendor_A_Command : UciCommand (gid = VENDOR_RESERVED_A) { - _payload_, -} - -packet UciVendor_B_Command : UciCommand (gid = VENDOR_RESERVED_B) { - _payload_, -} - -packet UciVendor_E_Command : UciCommand (gid = VENDOR_RESERVED_E) { - _payload_, -} - -packet UciVendor_F_Command : UciCommand (gid = VENDOR_RESERVED_F) { - _payload_, -} - -packet UciVendor_9_Response : UciResponse (gid = VENDOR_RESERVED_9) { - _payload_, -} - -packet UciVendor_A_Response : UciResponse (gid = VENDOR_RESERVED_A) { - _payload_, -} - -packet UciVendor_B_Response : UciResponse (gid = VENDOR_RESERVED_B) { - _payload_, -} - -packet UciVendor_E_Response : UciResponse (gid = VENDOR_RESERVED_E) { - _payload_, -} - -packet UciVendor_F_Response : UciResponse (gid = VENDOR_RESERVED_F) { - _payload_, -} - -packet UciVendor_9_Notification : UciNotification (gid = VENDOR_RESERVED_9) { - _payload_, -} - -packet UciVendor_A_Notification : UciNotification (gid = VENDOR_RESERVED_A) { - _payload_, -} - -packet UciVendor_B_Notification : UciNotification (gid = VENDOR_RESERVED_B) { - _payload_, -} - -packet UciVendor_E_Notification : UciNotification (gid = VENDOR_RESERVED_E) { - _payload_, -} - -packet UciVendor_F_Notification : UciNotification (gid = VENDOR_RESERVED_F) { - _payload_, -} - -packet TestNotification : UciNotification (gid = TEST) { - _payload_, -}
\ No newline at end of file diff --git a/tests/data_transfer.py b/tests/data_transfer.py index 241a6b9..6d4fb8c 100755 --- a/tests/data_transfer.py +++ b/tests/data_transfer.py @@ -35,7 +35,7 @@ async def controller(host: Host, peer: Host, file: Path): ) ) - await host.expect_control(uci.SessionInitRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.SessionInitRsp(status=uci.Status.OK)) await host.expect_control( uci.SessionStatusNtf( @@ -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 @@ -80,7 +93,7 @@ async def controller(host: Host, peer: Host, file: Path): ) await host.expect_control( - uci.SessionSetAppConfigRsp(status=uci.StatusCode.UCI_STATUS_OK, cfg_status=[]) + uci.SessionSetAppConfigRsp(status=uci.Status.OK, cfg_status=[]) ) await host.expect_control( @@ -96,7 +109,7 @@ async def controller(host: Host, peer: Host, file: Path): # START SESSION CMD host.send_control(uci.SessionStartCmd(session_id=0)) - await host.expect_control(uci.SessionStartRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.SessionStartRsp(status=uci.Status.OK)) await host.expect_control( uci.SessionStatusNtf( @@ -107,7 +120,7 @@ async def controller(host: Host, peer: Host, file: Path): ) await host.expect_control( - uci.DeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_ACTIVE) + uci.CoreDeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_ACTIVE) ) event = await host.expect_control(uci.ShortMacTwoWaySessionInfoNtf, timeout=2.0) @@ -119,7 +132,7 @@ async def controller(host: Host, peer: Host, file: Path): # STOP SESSION host.send_control(uci.SessionStopCmd(session_id=0)) - await host.expect_control(uci.SessionStopRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.SessionStopRsp(status=uci.Status.OK)) await host.expect_control( uci.SessionStatusNtf( @@ -130,13 +143,13 @@ async def controller(host: Host, peer: Host, file: Path): ) await host.expect_control( - uci.DeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_READY) + uci.CoreDeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_READY) ) # DEINIT host.send_control(uci.SessionDeinitCmd(session_token=0)) - await host.expect_control(uci.SessionDeinitRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.SessionDeinitRsp(status=uci.Status.OK)) async def controlee(host: Host, peer: Host, file: Path): @@ -149,7 +162,7 @@ async def controlee(host: Host, peer: Host, file: Path): ) ) - await host.expect_control(uci.SessionInitRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.SessionInitRsp(status=uci.Status.OK)) await host.expect_control( uci.SessionStatusNtf( @@ -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 @@ -194,7 +220,7 @@ async def controlee(host: Host, peer: Host, file: Path): ) await host.expect_control( - uci.SessionSetAppConfigRsp(status=uci.StatusCode.UCI_STATUS_OK, cfg_status=[]) + uci.SessionSetAppConfigRsp(status=uci.Status.OK, cfg_status=[]) ) await host.expect_control( @@ -207,7 +233,7 @@ async def controlee(host: Host, peer: Host, file: Path): host.send_control(uci.SessionStartCmd(session_id=0)) - await host.expect_control(uci.SessionStartRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.SessionStartRsp(status=uci.Status.OK)) await host.expect_control( uci.SessionStatusNtf( @@ -218,7 +244,7 @@ async def controlee(host: Host, peer: Host, file: Path): ) await host.expect_control( - uci.DeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_ACTIVE) + uci.CoreDeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_ACTIVE) ) with file.open("rb") as f: @@ -226,8 +252,8 @@ async def controlee(host: Host, peer: Host, file: Path): event = await host.expect_data( uci.DataMessageRcv( session_handle=0, - status=uci.StatusCode.UCI_STATUS_OK, - source_address=int.from_bytes(peer.mac_address, "big"), + status=uci.Status.OK, + source_address=int.from_bytes(peer.mac_address, "little"), data_sequence_number=0x01, application_data=application_data, ), @@ -240,7 +266,7 @@ async def controlee(host: Host, peer: Host, file: Path): host.send_control(uci.SessionStopCmd(session_id=0)) - await host.expect_control(uci.SessionStopRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.SessionStopRsp(status=uci.Status.OK)) await host.expect_control( uci.SessionStatusNtf( @@ -251,12 +277,12 @@ async def controlee(host: Host, peer: Host, file: Path): ) await host.expect_control( - uci.DeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_READY) + uci.CoreDeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_READY) ) host.send_control(uci.SessionDeinitCmd(session_token=0)) - await host.expect_control(uci.SessionDeinitRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.SessionDeinitRsp(status=uci.Status.OK)) async def data_transfer( @@ -296,7 +322,7 @@ async def data_transfer( seq_num = 0 event = await host.expect_control( - uci.DataCreditNtf( + uci.SessionDataCreditNtf( session_token=int(session_id), credit_availability=uci.CreditAvailability.CREDIT_AVAILABLE, ) @@ -312,7 +338,7 @@ async def data_transfer( ) ) event = await host.expect_control( - uci.DataCreditNtf( + uci.SessionDataCreditNtf( session_token=int(session_id), credit_availability=uci.CreditAvailability.CREDIT_AVAILABLE, ) @@ -325,8 +351,8 @@ async def data_transfer( async def run(address: str, uci_port: int, file: Path): try: - host0 = await Host.connect(address, uci_port, bytes([0, 0])) - host1 = await Host.connect(address, uci_port, bytes([0, 1])) + host0 = await Host.connect(address, uci_port, bytes([0x34, 0x12])) + host1 = await Host.connect(address, uci_port, bytes([0x78, 0x56])) except Exception as e: raise Exception( f"Failed to connect to Pica server at address {address}:{uci_port}\n" diff --git a/tests/helper.py b/tests/helper.py index bbce7ad..f5d9db5 100644 --- a/tests/helper.py +++ b/tests/helper.py @@ -18,13 +18,13 @@ from pica.packets import uci async def init(host: Host): await host.expect_control( - uci.DeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_READY) + uci.CoreDeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_READY) ) - host.send_control(uci.DeviceResetCmd(reset_config=uci.ResetConfig.UWBS_RESET)) + host.send_control(uci.CoreDeviceResetCmd(reset_config=uci.ResetConfig.UWBS_RESET)) - await host.expect_control(uci.DeviceResetRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.CoreDeviceResetRsp(status=uci.Status.OK)) await host.expect_control( - uci.DeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_READY) + uci.CoreDeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_READY) ) diff --git a/tests/ranging.py b/tests/ranging.py index 7af8224..165c5e3 100755 --- a/tests/ranging.py +++ b/tests/ranging.py @@ -32,7 +32,7 @@ async def controller(host: Host, peer: Host): ) ) - await host.expect_control(uci.SessionInitRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.SessionInitRsp(status=uci.Status.OK)) await host.expect_control( uci.SessionStatusNtf( @@ -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 @@ -77,7 +90,7 @@ async def controller(host: Host, peer: Host): ) await host.expect_control( - uci.SessionSetAppConfigRsp(status=uci.StatusCode.UCI_STATUS_OK, cfg_status=[]) + uci.SessionSetAppConfigRsp(status=uci.Status.OK, cfg_status=[]) ) await host.expect_control( @@ -90,7 +103,7 @@ async def controller(host: Host, peer: Host): host.send_control(uci.SessionStartCmd(session_id=0)) - await host.expect_control(uci.SessionStartRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.SessionStartRsp(status=uci.Status.OK)) await host.expect_control( uci.SessionStatusNtf( @@ -101,7 +114,7 @@ async def controller(host: Host, peer: Host): ) await host.expect_control( - uci.DeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_ACTIVE) + uci.CoreDeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_ACTIVE) ) for _ in range(1, 3): @@ -110,7 +123,7 @@ async def controller(host: Host, peer: Host): host.send_control(uci.SessionStopCmd(session_id=0)) - await host.expect_control(uci.SessionStopRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.SessionStopRsp(status=uci.Status.OK)) await host.expect_control( uci.SessionStatusNtf( @@ -121,12 +134,12 @@ async def controller(host: Host, peer: Host): ) await host.expect_control( - uci.DeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_READY) + uci.CoreDeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_READY) ) host.send_control(uci.SessionDeinitCmd(session_token=0)) - await host.expect_control(uci.SessionDeinitRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.SessionDeinitRsp(status=uci.Status.OK)) async def controlee(host: Host, peer: Host): @@ -138,7 +151,7 @@ async def controlee(host: Host, peer: Host): ) ) - await host.expect_control(uci.SessionInitRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.SessionInitRsp(status=uci.Status.OK)) await host.expect_control( uci.SessionStatusNtf( @@ -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 @@ -183,7 +209,7 @@ async def controlee(host: Host, peer: Host): ) await host.expect_control( - uci.SessionSetAppConfigRsp(status=uci.StatusCode.UCI_STATUS_OK, cfg_status=[]) + uci.SessionSetAppConfigRsp(status=uci.Status.OK, cfg_status=[]) ) await host.expect_control( @@ -196,7 +222,7 @@ async def controlee(host: Host, peer: Host): host.send_control(uci.SessionStartCmd(session_id=0)) - await host.expect_control(uci.SessionStartRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.SessionStartRsp(status=uci.Status.OK)) await host.expect_control( uci.SessionStatusNtf( @@ -207,7 +233,7 @@ async def controlee(host: Host, peer: Host): ) await host.expect_control( - uci.DeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_ACTIVE) + uci.CoreDeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_ACTIVE) ) for _ in range(1, 3): @@ -216,7 +242,7 @@ async def controlee(host: Host, peer: Host): host.send_control(uci.SessionStopCmd(session_id=0)) - await host.expect_control(uci.SessionStopRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.SessionStopRsp(status=uci.Status.OK)) await host.expect_control( uci.SessionStatusNtf( @@ -227,12 +253,12 @@ async def controlee(host: Host, peer: Host): ) await host.expect_control( - uci.DeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_READY) + uci.CoreDeviceStatusNtf(device_state=uci.DeviceState.DEVICE_STATE_READY) ) host.send_control(uci.SessionDeinitCmd(session_token=0)) - await host.expect_control(uci.SessionDeinitRsp(status=uci.StatusCode.UCI_STATUS_OK)) + await host.expect_control(uci.SessionDeinitRsp(status=uci.Status.OK)) async def run(address: str, uci_port: int): |