diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-05-10 16:24:34 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-05-10 16:24:34 +0000 |
commit | 8bdc95edf6ec07c35b8c8c46cb54fc215652260e (patch) | |
tree | ddd7c6ac6629c209a0e02f18a213cc0b0753b0be | |
parent | 6a0b33a9662237b8982002992196642bc603c2d4 (diff) | |
parent | 1a86b7006f9919c1bdeeb128ee773f5b8e21c59d (diff) | |
download | uwb-android13-mainline-tzdata4-release.tar.gz |
Snap for 10102166 from 1a86b7006f9919c1bdeeb128ee773f5b8e21c59d to mainline-tzdata4-releaseaml_tz4_332714070aml_tz4_332714050aml_tz4_332714010aml_tz4_331910000android13-mainline-tzdata4-releaseaml_tz4_332714010
Change-Id: Iced10414419407f626e6976505cb349aa43630d7
77 files changed, 8727 insertions, 6963 deletions
diff --git a/src/Android.bp b/src/Android.bp index d1db35b..55f04cf 100755 --- a/src/Android.bp +++ b/src/Android.bp @@ -3,96 +3,6 @@ package { } rust_defaults { - name: "libuwb_uci_defaults", - srcs: [ - "rust/lib.rs", - ], - crate_name: "uwb_uci_rust", - lints: "android", - clippy_lints: "android", - rustlibs: [ - "android.hardware.uwb-V1-rust", - "libanyhow", - "libbinder_ndk_sys", - "libbinder_rs", - "libbinder_tokio_rs", - "libbytes", - "libjni", - "liblazy_static", - "liblog_rust", - "liblogger", - "libnum_traits", - "libthiserror", - "libtokio", - "libuwb_uci_packets", - ], - target: { - android: { - rustlibs: [ - "librustutils", - ], - }, - }, - rlibs: [ - "libarbitrary", - ], - proc_macros: [ - "libasync_trait", - ], - apex_available: [ - "com.android.uwb", - ], - min_sdk_version: "Tiramisu", - host_supported: true, -} - -rust_library { - name: "libuwb_uci_rust", - defaults: ["libuwb_uci_defaults"], -} - -rust_test { - name: "libuwb_uci_rust_tests", - defaults: ["libuwb_uci_defaults"], - target: { - android: { - test_suites: [ - "general-tests", - "mts-uwb" - ], - test_config_template: "uwb_rust_test_config_template.xml", - }, - host: { - test_suites: [ - "general-tests", - ], - data_libs: [ - "libandroid_runtime_lazy", - "libbase", - "libbinder", - "libbinder_ndk", - "libcutils", - "liblog", - "libutils", - ], - }, - }, - // Support multilib variants (using different suffix per sub-architecture), which is needed on - // build targets with secondary architectures, as the MTS test suite packaging logic flattens - // all test artifacts into a single `testcases` directory. - compile_multilib: "both", - multilib: { - lib32: { - suffix: "32", - }, - lib64: { - suffix: "64", - }, - }, - auto_gen_config: true, -} - -rust_defaults { name: "libuwb_uci_packet_defaults", srcs: [ "rust/uwb_uci_packets/src/lib.rs", @@ -125,6 +35,8 @@ rust_test { test_suites: [ "general-tests", ], + // See b/268061150 + stem: "libuwb_uci_packet_tests_host", }, }, // Support multilib variants (using different suffix per sub-architecture), which is needed on @@ -140,6 +52,7 @@ rust_test { }, }, auto_gen_config: true, + min_sdk_version: "33", } rust_library { @@ -156,10 +69,7 @@ rust_library { genrule { name: "UwbGeneratedPackets_rust", - tools: [ - "bluetooth_packetgen", - ], - cmd: "$(location bluetooth_packetgen) --include=external/uwb/src --out=$(genDir) $(in) --rust", + defaults: ["pdl_rust_generator_defaults"], srcs: [ "rust/uwb_uci_packets/uci_packets.pdl", ], @@ -200,6 +110,16 @@ rust_library { ], } +// Builds uwb_core library with "mock-utils" enabled. +// This enables mock methods to be used for testing external crates. +rust_library { + name: "libuwb_core_with_mock", + defaults: ["libuwb_core_defaults"], + crate_name: "uwb_core", + features: ["mock-utils"], + host_supported: true, +} + rust_test { name: "libuwb_core_tests", defaults: ["libuwb_core_defaults"], @@ -207,9 +127,43 @@ rust_test { "libenv_logger", "libtempfile", ], - test_suites: [ - "general-tests", - ], + target: { + android: { + test_suites: [ + "general-tests", + "mts-uwb" + ], + test_config_template: "uwb_rust_test_config_template.xml", + }, + host: { + test_suites: [ + "general-tests", + ], + data_libs: [ + "libandroid_runtime_lazy", + "libbase", + "libcutils", + "liblog", + "libutils", + ], + // See b/268061150 + stem: "libuwb_core_tests_host", + }, + }, + // Support multilib variants (using different suffix per sub-architecture), which is needed on + // build targets with secondary architectures, as the MTS test suite packaging logic flattens + // all test artifacts into a single `testcases` directory. + compile_multilib: "both", + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + auto_gen_config: true, + min_sdk_version: "33", } rust_binary { @@ -231,19 +185,59 @@ rust_binary { host_supported: true, } +// Build the uwb_core library with "proto" feature enabled. +// It's used for fuzzer test. +rust_library { + name: "libuwb_core_with_proto", + defaults: ["libuwb_core_defaults"], + crate_name: "uwb_core", + srcs: [ + // Generate the protobuf bindings. + // These genrule follows the logic of rust/uwb_core/build.rs. + ":gen_uwb_core_proto", + ":include_uwb_core_proto", + ], + rustlibs: [ + "libprotobuf", + ], + features: ["proto"], + host_supported: true, +} + +genrule { + name: "gen_uwb_core_proto", + tools: ["aprotoc", "protoc-gen-rust"], + cmd: "$(location aprotoc)" + + " --proto_path=`dirname $(in)`" + + " --dependency_out=$(depfile)" + + " --plugin=$(location protoc-gen-rust)" + + " --rust_out=$(genDir) $(in)", + srcs: [ + "rust/uwb_core/protos/uwb_service.proto", + ], + out: [ + "uwb_service.rs", + ], + depfile: true, +} + +genrule { + name: "include_uwb_core_proto", + cmd: "echo '#[path = \"uwb_service.rs\"]' > $(out);" + + "echo 'pub mod bindings;' >> $(out);", + out: [ + "proto_bindings.rs", + ], +} + rust_fuzz { - name: "uwb_uci_rust_fuzzer", + name: "uwb_core_fuzzer", srcs: [ - "fuzz/fuzzer.rs", + "rust/uwb_core/fuzz/proto_uwb_service_fuzzer.rs", ], rustlibs: [ - "android.hardware.uwb-V1-rust", "libarbitrary", - "liblog_rust", - "libnum_traits", - "libtokio", - "libuwb_uci_packets", - "libuwb_uci_rust", + "libuwb_core_with_proto", ], fuzz_config: { cc: [ @@ -255,15 +249,14 @@ rust_fuzz { }, } -rust_library { - name: "libuci_hal_android", +rust_defaults { + name: "libuci_hal_android_defaults", crate_name: "uci_hal_android", lints: "android", clippy_lints: "android", rustlibs: [ "android.hardware.uwb-V1-rust", "libanyhow", - "libbinder_ndk_sys", "libbinder_rs", "libbinder_tokio_rs", "libbytes", @@ -271,7 +264,6 @@ rust_library { "liblog_rust", "libthiserror", "libtokio", - "libuwb_core", "libuwb_uci_packets", ], target: { @@ -294,11 +286,77 @@ rust_library { ], } +rust_library { + name: "libuci_hal_android", + defaults: ["libuci_hal_android_defaults"], + rustlibs: [ + "libuwb_core", + ], +} + +// uci_hal_android built with uwb_core_with_mock. +// Used to replace uci_hal_android in place where mock version of uwb_core is +// used. +rust_library { + name: "libuci_hal_android_with_mock", + defaults: ["libuci_hal_android_defaults"], + rustlibs: [ + "libuwb_core_with_mock", + ], +} + +rust_test { + name: "libuci_hal_android_tests", + defaults: ["libuci_hal_android_defaults"], + rustlibs: [ + "libenv_logger", + "libtempfile", + "libuwb_core", + ], + target: { + android: { + test_suites: [ + "general-tests", + "mts-uwb" + ], + test_config_template: "uwb_rust_test_config_template.xml", + }, + host: { + test_suites: [ + "general-tests", + ], + data_libs: [ + "libandroid_runtime_lazy", + "libbase", + "libcutils", + "liblog", + "libutils", + ], + // See b/268061150 + stem: "libuci_hal_android_tests_host", + }, + }, + // Support multilib variants (using different suffix per sub-architecture), which is needed on + // build targets with secondary architectures, as the MTS test suite packaging logic flattens + // all test artifacts into a single `testcases` directory. + compile_multilib: "both", + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + auto_gen_config: true, + min_sdk_version: "33", +} + // Generate the artifacts zip for uwb_core library and its dependencies. genrule { name: "uwb_core_artifacts", tools: [ - "bluetooth_packetgen", + "pdl", "soong_zip", ], cmd: @@ -310,10 +368,9 @@ genrule { " $(genDir)/artifacts;" + // Generate uci_packets.rs at $(genDir)/artifacts/uwb_uci_packets/. - "$(location bluetooth_packetgen) --rust " + - " --include=external/uwb/src/rust" + - " --out=$(genDir)/artifacts " + - " external/uwb/src/rust/uwb_uci_packets/uci_packets.pdl;" + + "$(location pdl) --output-format rust " + + " external/uwb/src/rust/uwb_uci_packets/uci_packets.pdl " + + " > $(genDir)/artifacts/uwb_uci_packets/uci_packets.rs;" + // Pack the artifacts directory and clean up the directory. "$(location soong_zip) -o $(out) " + diff --git a/src/TEST_MAPPING b/src/TEST_MAPPING index f098f13..94263af 100644 --- a/src/TEST_MAPPING +++ b/src/TEST_MAPPING @@ -6,9 +6,6 @@ // "host": true // }, { - "name": "libuwb_uci_rust_tests" - }, - { "name": "libuwb_uci_packet_tests" }, { diff --git a/src/fuzz/fuzzer.rs b/src/fuzz/fuzzer.rs deleted file mode 100644 index 7386a38..0000000 --- a/src/fuzz/fuzzer.rs +++ /dev/null @@ -1,618 +0,0 @@ -#![no_main] -#![allow(missing_docs)] - -use android_hardware_uwb::aidl::android::hardware::uwb::{ - IUwbChip::IUwbChipAsync, UwbEvent::UwbEvent, UwbStatus::UwbStatus, -}; -use android_hardware_uwb::binder::{DeathRecipient, Strong}; -use libfuzzer_sys::{arbitrary::Arbitrary, fuzz_target}; -use log::{error, info}; -use num_traits::cast::FromPrimitive; -use std::collections::HashMap; -use std::sync::Arc; -use tokio::runtime::Builder; -use tokio::sync::{mpsc, Mutex}; -use uwb_uci_packets::{ - AndroidGetPowerStatsCmdBuilder, AndroidGetPowerStatsRspBuilder, - AndroidSetCountryCodeCmdBuilder, AndroidSetCountryCodeRspBuilder, ControleeStatus, - DeviceResetRspBuilder, DeviceState, DeviceStatusNtfBuilder, - ExtendedAddressTwoWayRangingMeasurement, ExtendedMacTwoWayRangeDataNtfBuilder, - GenericErrorBuilder, GetCapsInfoCmdBuilder, GetCapsInfoRspBuilder, GetDeviceInfoCmdBuilder, - GetDeviceInfoRspBuilder, MessageControl, MulticastUpdateStatusCode, Packet, PowerStats, - RangeStartCmdBuilder, RangeStartRspBuilder, RangeStopCmdBuilder, RangeStopRspBuilder, - ReasonCode, SessionDeinitCmdBuilder, SessionDeinitRspBuilder, SessionGetAppConfigCmdBuilder, - SessionGetAppConfigRspBuilder, SessionGetCountCmdBuilder, SessionGetCountRspBuilder, - SessionGetStateCmdBuilder, SessionGetStateRspBuilder, SessionInitCmdBuilder, - SessionInitRspBuilder, SessionSetAppConfigRspBuilder, SessionState, SessionStatusNtfBuilder, - SessionType, SessionUpdateControllerMulticastListNtfBuilder, - SessionUpdateControllerMulticastListRspBuilder, ShortAddressTwoWayRangingMeasurement, - ShortMacTwoWayRangeDataNtfBuilder, StatusCode, UciCommandPacket, UciPacketChild, - UciPacketHalPacket, UciPacketPacket, UciVendor_9_ResponseBuilder, -}; -use uwb_uci_rust::adaptation::{mock_hal::MockHal, UwbAdaptationImpl}; -use uwb_uci_rust::error::UwbErr; -use uwb_uci_rust::event_manager::mock_event_manager::MockEventManager; -use uwb_uci_rust::uci::{ - uci_hmsgs, uci_hrcv, Dispatcher, DispatcherImpl, HalCallback, JNICommand, SyncUwbAdaptation, -}; - -#[derive(Debug, Clone, Arbitrary)] -enum UciNotification { - GenericError { - status: u8, - }, - DeviceStatusNtf { - device_state: u8, - }, - SessionStatusNtf { - session_id: u32, - session_state: u8, - reason_code: u8, - }, - ShortMacTwoWayRangeDataNtf { - sequence_number: u32, - session_id: u32, - rcr_indicator: u8, - current_ranging_interval: u32, - two_way_ranging_measurements: Vec<ShortAddressMeasurement>, - }, - ExtendedMacTwoWayRangeDataNtf { - sequence_number: u32, - session_id: u32, - rcr_indicator: u8, - current_ranging_interval: u32, - two_way_ranging_measurements: Vec<ExtendedAddressMeasurement>, - }, - SessionUpdateControllerMulticastListNtf { - session_id: u32, - remaining_multicast_list_size: u8, - controlee_status: Vec<MockControleeStatus>, - }, -} - -#[derive(Debug, Clone, Arbitrary)] -struct ShortAddressMeasurement { - mac_address: u16, - status: u8, - nlos: u8, - distance: u16, - aoa_azimuth: u16, - aoa_azimuth_fom: u8, - aoa_elevation: u16, - aoa_elevation_fom: u8, - aoa_destination_azimuth: u16, - aoa_destination_azimuth_fom: u8, - aoa_destination_elevation: u16, - aoa_destination_elevation_fom: u8, - slot_index: u8, - rssi: u8, -} - -impl ShortAddressMeasurement { - fn convert(&self) -> Result<ShortAddressTwoWayRangingMeasurement, UwbErr> { - Ok(ShortAddressTwoWayRangingMeasurement { - mac_address: self.mac_address, - status: StatusCode::from_u8(self.status).ok_or(UwbErr::InvalidArgs)?, - nlos: self.nlos, - distance: self.distance, - aoa_azimuth: self.aoa_azimuth, - aoa_azimuth_fom: self.aoa_azimuth_fom, - aoa_elevation: self.aoa_elevation, - aoa_elevation_fom: self.aoa_elevation_fom, - aoa_destination_azimuth: self.aoa_destination_azimuth, - aoa_destination_azimuth_fom: self.aoa_destination_azimuth_fom, - aoa_destination_elevation: self.aoa_destination_elevation, - aoa_destination_elevation_fom: self.aoa_destination_elevation_fom, - slot_index: self.slot_index, - rssi: self.rssi, - }) - } -} - -#[derive(Debug, Clone, Arbitrary)] -struct ExtendedAddressMeasurement { - mac_address: u64, - status: u8, - nlos: u8, - distance: u16, - aoa_azimuth: u16, - aoa_azimuth_fom: u8, - aoa_elevation: u16, - aoa_elevation_fom: u8, - aoa_destination_azimuth: u16, - aoa_destination_azimuth_fom: u8, - aoa_destination_elevation: u16, - aoa_destination_elevation_fom: u8, - slot_index: u8, - rssi: u8, -} - -impl ExtendedAddressMeasurement { - fn convert(&self) -> Result<ExtendedAddressTwoWayRangingMeasurement, UwbErr> { - Ok(ExtendedAddressTwoWayRangingMeasurement { - mac_address: self.mac_address, - status: StatusCode::from_u8(self.status).ok_or(UwbErr::InvalidArgs)?, - nlos: self.nlos, - distance: self.distance, - aoa_azimuth: self.aoa_azimuth, - aoa_azimuth_fom: self.aoa_azimuth_fom, - aoa_elevation: self.aoa_elevation, - aoa_elevation_fom: self.aoa_elevation_fom, - aoa_destination_azimuth: self.aoa_destination_azimuth, - aoa_destination_azimuth_fom: self.aoa_destination_azimuth_fom, - aoa_destination_elevation: self.aoa_destination_elevation, - aoa_destination_elevation_fom: self.aoa_destination_elevation_fom, - slot_index: self.slot_index, - rssi: self.rssi, - }) - } -} - -#[derive(Debug, Clone, Arbitrary)] -struct MockControleeStatus { - mac_address: u16, - subsession_id: u32, - status: u8, -} - -impl MockControleeStatus { - fn convert(&self) -> Result<ControleeStatus, UwbErr> { - Ok(ControleeStatus { - mac_address: self.mac_address, - subsession_id: self.subsession_id, - status: MulticastUpdateStatusCode::from_u8(self.status).ok_or(UwbErr::InvalidArgs)?, - }) - } -} - -#[derive(Debug, Clone, Arbitrary)] -enum Command { - JNICmd(JNICommand), - UciNtf(UciNotification), -} - -async fn create_dispatcher_with_mock_adaptation( - msgs: Vec<Command>, -) -> Result<(DispatcherImpl, mpsc::UnboundedSender<(HalCallback, String)>), UwbErr> { - let (rsp_sender, rsp_receiver) = mpsc::unbounded_channel::<(HalCallback, String)>(); - let mut mock_hal = MockHal::new(Some(rsp_sender.clone())); - let mut mock_event_manager = MockEventManager::new(); - for msg in &msgs { - match msg { - Command::JNICmd(cmd) => match cmd { - JNICommand::Enable => { - mock_hal.expect_open(Ok(())); - mock_hal.expect_core_init(Ok(())); - } - JNICommand::Disable(_graceful) => { - break; - } - _ => { - let (cmd, rsp) = match generate_fake_cmd_rsp(cmd) { - Ok((command, response)) => (command, response), - Err(e) => { - mock_hal.clear_expected_calls(); - mock_event_manager.clear_expected_calls(); - return Err(e); - } - }; - let cmd_packet: UciPacketPacket = cmd.clone().into(); - let mut cmd_frag_packets: Vec<UciPacketHalPacket> = cmd_packet.into(); - let cmd_frag_data = cmd_frag_packets.pop().unwrap().to_vec(); - let cmd_frag_data_len = cmd_frag_data.len(); - mock_hal.expect_send_uci_message( - cmd_frag_data, - Some(rsp), - Ok(cmd_frag_data_len.try_into().unwrap()), - ) - } - }, - Command::UciNtf(ntf) => match ntf { - UciNotification::GenericError { status } => { - if StatusCode::from_u8(*status).is_none() { - mock_hal.clear_expected_calls(); - mock_event_manager.clear_expected_calls(); - return Err(UwbErr::InvalidArgs); - } - mock_event_manager.expect_core_generic_error_notification_received(Ok(())) - } - UciNotification::DeviceStatusNtf { device_state } => { - if DeviceState::from_u8(*device_state).is_none() { - mock_hal.clear_expected_calls(); - mock_event_manager.clear_expected_calls(); - return Err(UwbErr::InvalidArgs); - } - mock_event_manager.expect_device_status_notification_received(Ok(())) - } - UciNotification::SessionStatusNtf { - session_id: _session_id, - session_state, - reason_code, - } => { - if SessionState::from_u8(*session_state).is_none() - || ReasonCode::from_u8(*reason_code).is_none() - || *session_state == 0 - { - mock_hal.clear_expected_calls(); - mock_event_manager.clear_expected_calls(); - return Err(UwbErr::InvalidArgs); - } - mock_event_manager.expect_session_status_notification_received(Ok(())) - } - UciNotification::ShortMacTwoWayRangeDataNtf { - sequence_number: _sequence_number, - session_id: _session_id, - rcr_indicator: _rcr_indicator, - current_ranging_interval: _current_ranging_interval, - two_way_ranging_measurements, - } => { - for measurement in two_way_ranging_measurements.iter() { - if StatusCode::from_u8(measurement.status).is_none() { - mock_hal.clear_expected_calls(); - mock_event_manager.clear_expected_calls(); - return Err(UwbErr::InvalidArgs); - } - } - mock_event_manager.expect_short_range_data_notification_received(Ok(())) - } - UciNotification::ExtendedMacTwoWayRangeDataNtf { - sequence_number: _sequence_number, - session_id: _session_id, - rcr_indicator: _rcr_indicator, - current_ranging_interval: _current_ranging_interval, - two_way_ranging_measurements, - } => { - for measurement in two_way_ranging_measurements.iter() { - if StatusCode::from_u8(measurement.status).is_none() { - mock_hal.clear_expected_calls(); - mock_event_manager.clear_expected_calls(); - return Err(UwbErr::InvalidArgs); - } - } - mock_event_manager.expect_extended_range_data_notification_received(Ok(())) - } - UciNotification::SessionUpdateControllerMulticastListNtf { - session_id: _session_id, - remaining_multicast_list_size: _remaining_multicast_list_size, - controlee_status, - } => { - for status in controlee_status.iter() { - if MulticastUpdateStatusCode::from_u8(status.status).is_none() { - mock_hal.clear_expected_calls(); - mock_event_manager.clear_expected_calls(); - return Err(UwbErr::InvalidArgs); - } - } - mock_event_manager - .expect_session_update_controller_multicast_list_notification_received(Ok( - (), - )) - } - }, - } - } - mock_hal.expect_close(Ok(())); - let mut adaptation = Arc::new( - UwbAdaptationImpl::new_with_args( - rsp_sender.clone(), - HashMap::from([( - String::from("chip_id"), - Strong::new(Box::new(mock_hal) as Box<dyn IUwbChipAsync<_> + 'static>), - )]), - Arc::new(Mutex::new(Vec::from([DeathRecipient::new(|| {})]))), - ) - .await?, - ); - Ok(( - DispatcherImpl::new_for_testing( - mock_event_manager, - adaptation as SyncUwbAdaptation, - rsp_receiver, - )?, - rsp_sender, - )) -} - -fn generate_fake_cmd_rsp( - msg: &JNICommand, -) -> Result<(UciCommandPacket, uci_hrcv::UciResponse), UwbErr> { - match msg { - JNICommand::UciSessionInit(session_id, session_type) => Ok(( - uci_hmsgs::build_session_init_cmd(*session_id, *session_type)?.build().into(), - uci_hrcv::UciResponse::SessionInitRsp( - SessionInitRspBuilder { status: StatusCode::UciStatusOk }.build(), - ), - )), - JNICommand::UciGetCapsInfo => Ok(( - GetCapsInfoCmdBuilder {}.build().into(), - uci_hrcv::UciResponse::GetCapsInfoRsp( - GetCapsInfoRspBuilder { status: StatusCode::UciStatusOk, tlvs: vec![] }.build(), - ), - )), - JNICommand::UciGetDeviceInfo => Ok(( - GetDeviceInfoCmdBuilder {}.build().into(), - uci_hrcv::UciResponse::GetDeviceInfoRsp( - GetDeviceInfoRspBuilder { - status: StatusCode::UciStatusOk, - uci_version: 0, - mac_version: 0, - phy_version: 0, - uci_test_version: 0, - vendor_spec_info: vec![], - } - .build(), - ), - )), - JNICommand::UciSessionDeinit(session_id) => Ok(( - SessionDeinitCmdBuilder { session_id: *session_id }.build().into(), - uci_hrcv::UciResponse::SessionDeinitRsp( - SessionDeinitRspBuilder { status: StatusCode::UciStatusOk }.build(), - ), - )), - JNICommand::UciSessionGetCount => Ok(( - SessionGetCountCmdBuilder {}.build().into(), - uci_hrcv::UciResponse::SessionGetCountRsp( - SessionGetCountRspBuilder { status: StatusCode::UciStatusOk, session_count: 1 } - .build(), - ), - )), - JNICommand::UciStartRange(session_id) => Ok(( - RangeStartCmdBuilder { session_id: *session_id }.build().into(), - uci_hrcv::UciResponse::RangeStartRsp( - RangeStartRspBuilder { status: StatusCode::UciStatusOk }.build(), - ), - )), - JNICommand::UciStopRange(session_id) => Ok(( - RangeStopCmdBuilder { session_id: *session_id }.build().into(), - uci_hrcv::UciResponse::RangeStopRsp( - RangeStopRspBuilder { status: StatusCode::UciStatusOk }.build(), - ), - )), - JNICommand::UciGetSessionState(session_id) => Ok(( - SessionGetStateCmdBuilder { session_id: *session_id }.build().into(), - uci_hrcv::UciResponse::SessionGetStateRsp( - SessionGetStateRspBuilder { - status: StatusCode::UciStatusOk, - session_state: SessionState::SessionStateInit, - } - .build(), - ), - )), - JNICommand::UciSessionUpdateMulticastList { - session_id, - action, - no_of_controlee, - address_list, - sub_session_id_list, - message_control, - sub_session_key_list, - } => Ok(( - uci_hmsgs::build_multicast_list_update_cmd( - *session_id, - *action, - *no_of_controlee, - address_list, - sub_session_id_list, - match *message_control { - -1 => None, - _ => Some( - MessageControl::from_i32(*message_control).ok_or(UwbErr::InvalidArgs)?, - ), - }, - sub_session_key_list, - )? - .build() - .into(), - uci_hrcv::UciResponse::SessionUpdateControllerMulticastListRsp( - SessionUpdateControllerMulticastListRspBuilder { status: StatusCode::UciStatusOk } - .build(), - ), - )), - JNICommand::UciSetCountryCode { code } => Ok(( - uci_hmsgs::build_set_country_code_cmd(&code)?.build().into(), - uci_hrcv::UciResponse::AndroidSetCountryCodeRsp( - AndroidSetCountryCodeRspBuilder { status: StatusCode::UciStatusOk }.build(), - ), - )), - JNICommand::UciSetAppConfig { - session_id, - no_of_params, - app_config_param_len, - app_configs, - } => Ok(( - uci_hmsgs::build_set_app_config_cmd(*session_id, *no_of_params, app_configs)? - .build() - .into(), - uci_hrcv::UciResponse::SessionSetAppConfigRsp( - SessionSetAppConfigRspBuilder { - status: StatusCode::UciStatusOk, - cfg_status: vec![], - } - .build(), - ), - )), - JNICommand::UciGetAppConfig { - session_id, - no_of_params, - app_config_param_len, - app_configs, - } => Ok(( - SessionGetAppConfigCmdBuilder { - session_id: *session_id, - app_cfg: app_configs.to_vec(), - } - .build() - .into(), - uci_hrcv::UciResponse::SessionGetAppConfigRsp( - SessionGetAppConfigRspBuilder { status: StatusCode::UciStatusOk, tlvs: vec![] } - .build(), - ), - )), - JNICommand::UciRawVendorCmd { gid, oid, payload } => Ok(( - uci_hmsgs::build_uci_vendor_cmd_packet(*gid, *oid, payload.to_vec())?.into(), - uci_hrcv::UciResponse::RawVendorRsp( - UciVendor_9_ResponseBuilder { opcode: 0, payload: None }.build().into(), - ), - )), - JNICommand::UciDeviceReset { reset_config } => Ok(( - uci_hmsgs::build_device_reset_cmd(*reset_config)?.build().into(), - uci_hrcv::UciResponse::DeviceResetRsp( - DeviceResetRspBuilder { status: StatusCode::UciStatusOk }.build(), - ), - )), - JNICommand::UciGetPowerStats => Ok(( - AndroidGetPowerStatsCmdBuilder {}.build().into(), - uci_hrcv::UciResponse::AndroidGetPowerStatsRsp( - AndroidGetPowerStatsRspBuilder { - stats: PowerStats { - status: StatusCode::UciStatusOk, - idle_time_ms: 0, - tx_time_ms: 0, - rx_time_ms: 0, - total_wake_count: 0, - }, - } - .build(), - ), - )), - _ => Err(UwbErr::Exit), - } -} - -fn consume_command(msgs: Vec<Command>) -> Result<(), UwbErr> { - let rt = Builder::new_current_thread().enable_all().build()?; - let (mut mock_dispatcher, mut rsp_sender) = - rt.block_on(create_dispatcher_with_mock_adaptation(msgs.clone()))?; - let chip_id = String::from("chip_id"); - for msg in msgs { - match msg { - Command::JNICmd(cmd) => match cmd { - JNICommand::Enable => { - mock_dispatcher - .send_jni_command(JNICommand::Enable, chip_id.clone()) - .expect(format!("Failed to send {:?}", cmd).as_str()); - } - JNICommand::Disable(_graceful) => { - break; - } - _ => { - mock_dispatcher - .block_on_jni_command(cmd.clone(), chip_id.clone()) - .expect(format!("Failed to send {:?}", cmd).as_str()); - } - }, - Command::UciNtf(ntf) => { - let evt = match ntf { - UciNotification::GenericError { status } => { - uci_hrcv::UciNotification::GenericError( - GenericErrorBuilder { - status: StatusCode::from_u8(status).ok_or(UwbErr::InvalidArgs)?, - } - .build(), - ) - } - UciNotification::DeviceStatusNtf { device_state } => { - uci_hrcv::UciNotification::DeviceStatusNtf( - DeviceStatusNtfBuilder { - device_state: DeviceState::from_u8(device_state) - .ok_or(UwbErr::InvalidArgs)?, - } - .build(), - ) - } - UciNotification::SessionStatusNtf { - session_id, - session_state, - reason_code, - } => uci_hrcv::UciNotification::SessionStatusNtf( - SessionStatusNtfBuilder { - session_id, - session_state: SessionState::from_u8(session_state) - .ok_or(UwbErr::InvalidArgs)?, - reason_code: ReasonCode::from_u8(reason_code) - .ok_or(UwbErr::InvalidArgs)?, - } - .build(), - ), - UciNotification::ShortMacTwoWayRangeDataNtf { - sequence_number, - session_id, - rcr_indicator, - current_ranging_interval, - two_way_ranging_measurements, - } => { - let mut measurements = Vec::new(); - for measurement in two_way_ranging_measurements.iter() { - measurements.push(measurement.convert()?); - } - uci_hrcv::UciNotification::ShortMacTwoWayRangeDataNtf( - ShortMacTwoWayRangeDataNtfBuilder { - sequence_number, - session_id, - rcr_indicator, - current_ranging_interval, - two_way_ranging_measurements: measurements, - } - .build(), - ) - } - UciNotification::ExtendedMacTwoWayRangeDataNtf { - sequence_number, - session_id, - rcr_indicator, - current_ranging_interval, - two_way_ranging_measurements, - } => { - let mut measurements = Vec::new(); - for measurement in two_way_ranging_measurements.iter() { - measurements.push(measurement.convert()?); - } - uci_hrcv::UciNotification::ExtendedMacTwoWayRangeDataNtf( - ExtendedMacTwoWayRangeDataNtfBuilder { - sequence_number, - session_id, - rcr_indicator, - current_ranging_interval, - two_way_ranging_measurements: measurements, - } - .build(), - ) - } - UciNotification::SessionUpdateControllerMulticastListNtf { - session_id, - remaining_multicast_list_size, - controlee_status, - } => { - let mut status_vec = Vec::new(); - for status in controlee_status.iter() { - status_vec.push(status.convert()?); - } - uci_hrcv::UciNotification::SessionUpdateControllerMulticastListNtf( - SessionUpdateControllerMulticastListNtfBuilder { - session_id, - remaining_multicast_list_size, - controlee_status: status_vec, - } - .build(), - ) - } - }; - rsp_sender - .send((HalCallback::UciNtf(evt.clone()), chip_id.clone())) - .expect(format!("Error sending uci notification: {:?}", evt).as_str()); - } - } - } - mock_dispatcher - .send_jni_command(JNICommand::Disable(true), chip_id) - .expect("Failed to send JNICommand::Disable cmd."); - mock_dispatcher.wait_for_exit()?; - Ok(()) -} - -fuzz_target!(|msgs: Vec<Command>| { - match consume_command(msgs) { - Ok(()) => info!("fuzzing success"), - Err(e) => error!("fuzzing failed: {:?}", e), - }; -}); diff --git a/src/gki/common/uwb_gki.h b/src/gki/common/uwb_gki.h index 4cae456..3451093 100755 --- a/src/gki/common/uwb_gki.h +++ b/src/gki/common/uwb_gki.h @@ -122,6 +122,12 @@ typedef struct { uint16_t count; } BUFFER_Q; +typedef struct { + BUFFER_Q tx_data_pkt_q; + uint32_t session_id; + uint8_t credit_available; +} DATA_BUFFER_Q; + /* Task constants */ #ifndef TASKPTR diff --git a/src/gki/common/uwb_gki_buffer.cc b/src/gki/common/uwb_gki_buffer.cc index 82858f7..566b31d 100755 --- a/src/gki/common/uwb_gki_buffer.cc +++ b/src/gki/common/uwb_gki_buffer.cc @@ -93,7 +93,7 @@ static bool phUwb_gki_alloc_free_queue(uint8_t id) { if (Q->p_first == 0) { uint32_t requested_size; bool overflow = __builtin_mul_overflow(Q->size + BUFFER_PADDING_SIZE, - Q->total, &requested_size) + Q->total, &requested_size); if (overflow) { phUwb_GKI_exception(GKI_ERROR_BUF_SIZE_TOOBIG, "gki_alloc_free_queue: Buffer overflow"); diff --git a/src/gki/ulinux/uwb_gki_ulinux.cc b/src/gki/ulinux/uwb_gki_ulinux.cc index 40af99d..084ca07 100755 --- a/src/gki/ulinux/uwb_gki_ulinux.cc +++ b/src/gki/ulinux/uwb_gki_ulinux.cc @@ -1,7 +1,7 @@ /****************************************************************************** * * Copyright (C) 1999-2012 Broadcom Corporation - * Copyright 2019 NXP + * Copyright 2019,2022 NXP * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -647,9 +647,7 @@ void phUwb_GKI_enable(void) { *******************************************************************************/ void phUwb_GKI_disable(void) { - UCI_TRACE_I("GKI_disable"); pthread_mutex_lock(&gki_cb.os.GKI_mutex); - UCI_TRACE_I("Leaving GKI_disable"); return; } diff --git a/src/include/uwb_hal_int.h b/src/include/uwb_hal_int.h index b5fced1..f328b9e 100755 --- a/src/include/uwb_hal_int.h +++ b/src/include/uwb_hal_int.h @@ -32,7 +32,9 @@ enum { HAL_UWB_OPEN_CPLT_EVT = 0x00, HAL_UWB_CLOSE_CPLT_EVT = 0x01, - HAL_UWB_ERROR_EVT = 0x02 + HAL_UWB_INIT_CPLT_EVT = 0x02, + HAL_UWB_ERROR_EVT = 0x03, + HAL_UWB_HW_RESET = 0x04 }; typedef uint8_t uwb_event_t; diff --git a/src/include/uwb_types.h b/src/include/uwb_types.h index 410f072..5e141b2 100755 --- a/src/include/uwb_types.h +++ b/src/include/uwb_types.h @@ -165,6 +165,14 @@ typedef struct { ((((uint32_t)(*((p) + 3)))) << 24)); \ (p) += 4; \ } +#define STREAM_TO_UINT40(u40, p) \ + { \ + u40 = (((uint64_t)(*(p))) + ((((uint64_t)(*((p) + 1)))) << 8) + \ + ((((uint64_t)(*((p) + 2)))) << 16) + \ + ((((uint64_t)(*((p) + 3)))) << 24) + \ + ((((uint64_t)(*((p) + 4)))) << 32)); \ + (p) += 5; \ + } #define STREAM_TO_UINT64(u64, p) \ { \ u64 = (((uint64_t)(*(p))) + ((((uint64_t)(*((p) + 1)))) << 8) + \ diff --git a/src/rust/adaptation/mock_adaptation.rs b/src/rust/adaptation/mock_adaptation.rs deleted file mode 100644 index 078aa5d..0000000 --- a/src/rust/adaptation/mock_adaptation.rs +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! MockUwbAdaptation - -use crate::adaptation::UwbAdaptation; -use crate::error::UwbErr; -use crate::uci::uci_hrcv; -use crate::uci::HalCallback; -use android_hardware_uwb::aidl::android::hardware::uwb::{ - UwbEvent::UwbEvent, UwbStatus::UwbStatus, -}; -use async_trait::async_trait; -use log::warn; -use std::collections::VecDeque; -use std::sync::Mutex as StdMutex; -use tokio::sync::mpsc; -use uwb_uci_packets::{Packet, UciCommandPacket}; - -type Result<T> = std::result::Result<T, UwbErr>; - -#[cfg(any(test, fuzzing))] -enum ExpectedCall { - Finalize { - expected_exit_status: bool, - }, - HalOpen { - out: Result<()>, - }, - HalClose { - out: Result<()>, - }, - CoreInitialization { - out: Result<()>, - }, - SessionInitialization { - expected_session_id: i32, - out: Result<()>, - }, - SendUciMessage { - expected_cmd: UciCommandPacket, - rsp: Option<uci_hrcv::UciResponse>, - notf: Option<uci_hrcv::UciNotification>, - out: Result<()>, - }, -} - -#[cfg(any(test, fuzzing))] -pub struct MockUwbAdaptation { - rsp_sender: mpsc::UnboundedSender<(HalCallback, String)>, - expected_calls: StdMutex<VecDeque<ExpectedCall>>, -} - -#[cfg(any(test, fuzzing))] -impl MockUwbAdaptation { - pub fn new(rsp_sender: mpsc::UnboundedSender<(HalCallback, String)>) -> Self { - Self { rsp_sender, expected_calls: StdMutex::new(VecDeque::new()) } - } - - pub fn expect_finalize(&self, expected_exit_status: bool) { - self.expected_calls - .lock() - .unwrap() - .push_back(ExpectedCall::Finalize { expected_exit_status }); - } - pub fn expect_hal_open(&self, out: Result<()>) { - self.expected_calls.lock().unwrap().push_back(ExpectedCall::HalOpen { out }); - } - pub fn expect_hal_close(&self, out: Result<()>) { - self.expected_calls.lock().unwrap().push_back(ExpectedCall::HalClose { out }); - } - pub fn expect_core_initialization(&self, out: Result<()>) { - self.expected_calls.lock().unwrap().push_back(ExpectedCall::CoreInitialization { out }); - } - pub fn expect_session_initialization(&self, expected_session_id: i32, out: Result<()>) { - self.expected_calls - .lock() - .unwrap() - .push_back(ExpectedCall::SessionInitialization { expected_session_id, out }); - } - pub fn expect_send_uci_message( - &self, - expected_cmd: UciCommandPacket, - rsp: Option<uci_hrcv::UciResponse>, - notf: Option<uci_hrcv::UciNotification>, - out: Result<()>, - ) { - self.expected_calls.lock().unwrap().push_back(ExpectedCall::SendUciMessage { - expected_cmd, - rsp, - notf, - out, - }); - } - - pub fn clear_expected_calls(&self) { - self.expected_calls.lock().unwrap().clear(); - } - - async fn send_hal_event(&self, event: UwbEvent, event_status: UwbStatus) { - self.rsp_sender - .send((HalCallback::Event { event, event_status }, String::from("default"))) - .unwrap(); - } - - async fn send_uci_response(&self, rsp: uci_hrcv::UciResponse) { - self.rsp_sender.send((HalCallback::UciRsp(rsp), String::from("default"))).unwrap(); - } - - async fn send_uci_notification(&self, ntf: uci_hrcv::UciNotification) { - self.rsp_sender.send((HalCallback::UciNtf(ntf), String::from("default"))).unwrap(); - } -} - -#[cfg(any(test, fuzzing))] -impl Drop for MockUwbAdaptation { - fn drop(&mut self) { - assert!(self.expected_calls.lock().unwrap().is_empty()); - } -} - -#[cfg(any(test, fuzzing))] -#[async_trait] -impl UwbAdaptation for MockUwbAdaptation { - async fn finalize(&mut self, exit_status: bool) { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::Finalize { expected_exit_status }) - if expected_exit_status == exit_status => - { - return; - } - Some(call) => { - expected_calls.push_front(call); - } - None => {} - } - warn!("unpected finalize() called"); - } - - async fn hal_open(&self, _chip_id: &str) -> Result<()> { - let expected_out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::HalOpen { out }) => Some(out), - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - - match expected_out { - Some(out) => { - let status = if out.is_ok() { UwbStatus::OK } else { UwbStatus::FAILED }; - self.send_hal_event(UwbEvent::OPEN_CPLT, status).await; - out - } - None => { - warn!("unpected hal_open() called"); - Err(UwbErr::Undefined) - } - } - } - - async fn hal_close(&self, _chip_id: &str) -> Result<()> { - let expected_out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::HalClose { out }) => Some(out), - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - - match expected_out { - Some(out) => { - let status = if out.is_ok() { UwbStatus::OK } else { UwbStatus::FAILED }; - self.send_hal_event(UwbEvent::CLOSE_CPLT, status).await; - out - } - None => { - warn!("unpected hal_close() called"); - Err(UwbErr::Undefined) - } - } - } - - async fn core_initialization(&self, _chip_id: &str) -> Result<()> { - let expected_out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::CoreInitialization { out }) => Some(out), - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - - match expected_out { - Some(out) => { - let status = if out.is_ok() { UwbStatus::OK } else { UwbStatus::FAILED }; - self.send_hal_event(UwbEvent::POST_INIT_CPLT, status).await; - out - } - None => { - warn!("unpected core_initialization() called"); - Err(UwbErr::Undefined) - } - } - } - - async fn session_initialization(&self, session_id: i32, _chip_id: &str) -> Result<()> { - let expected_out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::SessionInitialization { expected_session_id, out }) - if expected_session_id == session_id => - { - Some(out) - } - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - - match expected_out { - Some(out) => out, - None => { - warn!("unpected session_initialization() called"); - Err(UwbErr::Undefined) - } - } - } - - async fn send_uci_message(&self, cmd: UciCommandPacket, _chip_id: &str) -> Result<()> { - let expected_out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::SendUciMessage { - expected_cmd, - rsp, - notf, - out, - // PDL generated packets do not implement PartialEq, so use the raw bytes for comparison. - }) if expected_cmd.clone().to_bytes() == cmd.to_bytes() => Some((rsp, notf, out)), - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - - match expected_out { - Some((rsp, notf, out)) => { - if let Some(notf) = notf { - self.send_uci_notification(notf).await; - } - if let Some(rsp) = rsp { - self.send_uci_response(rsp).await; - } - out - } - None => { - warn!("unpected send_uci_message() called"); - Err(UwbErr::Undefined) - } - } - } -} diff --git a/src/rust/adaptation/mock_hal.rs b/src/rust/adaptation/mock_hal.rs deleted file mode 100644 index db43edb..0000000 --- a/src/rust/adaptation/mock_hal.rs +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! MockHal - -use crate::uci::{uci_hrcv, HalCallback}; -use android_hardware_uwb::aidl::android::hardware::uwb::{ - IUwbChip::IUwbChipAsync, IUwbClientCallback::IUwbClientCallback, UwbEvent::UwbEvent, - UwbStatus::UwbStatus, -}; -use android_hardware_uwb::binder::{Result as BinderResult, Strong}; -use async_trait::async_trait; -use binder::{SpIBinder, StatusCode}; -use log::info; -use std::collections::VecDeque; -use std::sync::Mutex as StdMutex; -use tokio::sync::mpsc; - -#[cfg(any(test, fuzzing))] -enum ExpectedHalCall { - Open { - out: BinderResult<()>, - }, - Close { - out: BinderResult<()>, - }, - CoreInit { - out: BinderResult<()>, - }, - SessionInit { - expected_session_id: i32, - out: BinderResult<()>, - }, - SendUciMessage { - expected_data: Vec<u8>, - expected_rsp: Option<uci_hrcv::UciResponse>, - out: BinderResult<i32>, - }, -} - -#[cfg(any(test, fuzzing))] -pub struct MockHal { - rsp_sender: Option<mpsc::UnboundedSender<(HalCallback, String)>>, - expected_calls: StdMutex<VecDeque<ExpectedHalCall>>, -} - -#[cfg(any(test, fuzzing))] -impl MockHal { - pub fn new(rsp_sender: Option<mpsc::UnboundedSender<(HalCallback, String)>>) -> Self { - logger::init( - logger::Config::default().with_tag_on_device("uwb").with_min_level(log::Level::Debug), - ); - info!("created mock hal."); - Self { rsp_sender, expected_calls: StdMutex::new(VecDeque::new()) } - } - #[allow(dead_code)] - pub fn expect_open(&self, out: BinderResult<()>) { - self.expected_calls.lock().unwrap().push_back(ExpectedHalCall::Open { out }); - } - #[allow(dead_code)] - pub fn expect_close(&self, out: BinderResult<()>) { - self.expected_calls.lock().unwrap().push_back(ExpectedHalCall::Close { out }); - } - #[allow(dead_code)] - pub fn expect_core_init(&self, out: BinderResult<()>) { - self.expected_calls.lock().unwrap().push_back(ExpectedHalCall::CoreInit { out }); - } - #[allow(dead_code)] - pub fn expect_session_init(&self, expected_session_id: i32, out: BinderResult<()>) { - self.expected_calls - .lock() - .unwrap() - .push_back(ExpectedHalCall::SessionInit { expected_session_id, out }); - } - pub fn expect_send_uci_message( - &self, - expected_data: Vec<u8>, - expected_rsp: Option<uci_hrcv::UciResponse>, - out: BinderResult<i32>, - ) { - self.expected_calls.lock().unwrap().push_back(ExpectedHalCall::SendUciMessage { - expected_data, - expected_rsp, - out, - }); - } - pub fn clear_expected_calls(&self) { - self.expected_calls.lock().unwrap().clear(); - } -} - -#[cfg(any(test, fuzzing))] -impl Drop for MockHal { - fn drop(&mut self) { - assert!(self.expected_calls.lock().unwrap().is_empty()); - } -} - -#[cfg(any(test, fuzzing))] -impl Default for MockHal { - fn default() -> Self { - Self::new(None) - } -} - -#[cfg(any(test, fuzzing))] -impl binder::Interface for MockHal {} - -#[cfg(any(test, fuzzing))] -impl binder::FromIBinder for MockHal { - fn try_from(_ibinder: SpIBinder) -> std::result::Result<Strong<Self>, binder::StatusCode> { - Err(binder::StatusCode::OK) - } -} - -#[cfg(any(test, fuzzing))] -#[async_trait] -impl<P: binder::BinderAsyncPool> IUwbChipAsync<P> for MockHal { - fn getName(&self) -> binder::BoxFuture<BinderResult<String>> { - Box::pin(std::future::ready(Ok("default".into()))) - } - - fn open<'a>( - &'a self, - _cb: &'a binder::Strong<dyn IUwbClientCallback>, - ) -> binder::BoxFuture<'a, BinderResult<()>> { - let expected_out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedHalCall::Open { out }) => Some(out), - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - - match expected_out { - Some(out) => Box::pin(std::future::ready(out)), - None => Box::pin(std::future::ready(Err(StatusCode::UNKNOWN_ERROR.into()))), - } - } - - fn close(&self) -> binder::BoxFuture<BinderResult<()>> { - let expected_out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedHalCall::Close { out }) => { - if let Some(sender) = self.rsp_sender.as_ref() { - sender - .send(( - HalCallback::Event { - event: UwbEvent::CLOSE_CPLT, - event_status: UwbStatus::OK, - }, - String::from("default"), - )) - .unwrap(); - } - Some(out) - } - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - - match expected_out { - Some(out) => Box::pin(std::future::ready(out)), - None => Box::pin(std::future::ready(Err(StatusCode::UNKNOWN_ERROR.into()))), - } - } - - fn coreInit(&self) -> binder::BoxFuture<BinderResult<()>> { - let expected_out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedHalCall::CoreInit { out }) => Some(out), - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - - match expected_out { - Some(out) => Box::pin(std::future::ready(out)), - None => Box::pin(std::future::ready(Err(StatusCode::UNKNOWN_ERROR.into()))), - } - } - - fn sessionInit(&self, session_id: i32) -> binder::BoxFuture<BinderResult<()>> { - let expected_out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedHalCall::SessionInit { expected_session_id, out }) - if expected_session_id == session_id => - { - Some(out) - } - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - - match expected_out { - Some(out) => Box::pin(std::future::ready(out)), - None => Box::pin(std::future::ready(Err(StatusCode::UNKNOWN_ERROR.into()))), - } - } - - fn getSupportedAndroidUciVersion(&self) -> binder::BoxFuture<BinderResult<i32>> { - Box::pin(std::future::ready(Ok(0))) - } - - fn sendUciMessage(&self, cmd: &[u8]) -> binder::BoxFuture<BinderResult<i32>> { - let expected_out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedHalCall::SendUciMessage { expected_data, expected_rsp, out }) - if expected_data == cmd => - { - if let (Some(rsp), Some(sender)) = (expected_rsp, self.rsp_sender.as_ref()) { - sender.send((HalCallback::UciRsp(rsp), String::from("default"))).unwrap(); - } - Some(out) - } - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - match expected_out { - Some(out) => Box::pin(std::future::ready(out)), - None => Box::pin(std::future::ready(Err(StatusCode::UNKNOWN_ERROR.into()))), - } - } -} diff --git a/src/rust/adaptation/mod.rs b/src/rust/adaptation/mod.rs deleted file mode 100644 index d16bae4..0000000 --- a/src/rust/adaptation/mod.rs +++ /dev/null @@ -1,886 +0,0 @@ -//! HAL interface. - -use crate::error::UwbErr; -use crate::uci::uci_hrcv; -use crate::uci::uci_logger::{RealFileFactory, UciLogMode, UciLogger, UciLoggerImpl}; -use crate::uci::HalCallback; -use android_hardware_uwb::aidl::android::hardware::uwb::{ - IUwb::IUwbAsync, - IUwbChip::IUwbChipAsync, - IUwbClientCallback::{BnUwbClientCallback, IUwbClientCallbackAsyncServer}, - UwbEvent::UwbEvent, - UwbStatus::UwbStatus, -}; -use android_hardware_uwb::binder::{ - BinderFeatures, DeathRecipient, Interface, Result as BinderResult, Strong, -}; -use async_trait::async_trait; -use binder::IBinder; -use binder_tokio::{Tokio, TokioRuntime}; -use log::error; -#[cfg(target_os = "android")] -use rustutils::system_properties; -use std::collections::hash_map::HashMap; -use std::sync::Arc; -use tokio::runtime::Handle; -use tokio::sync::{mpsc, Mutex}; -use uwb_uci_packets::{ - Packet, PacketDefrager, UciCommandPacket, UciPacketChild, UciPacketHalPacket, UciPacketPacket, -}; - -type Result<T> = std::result::Result<T, UwbErr>; -type SyncUciLogger = Arc<dyn UciLogger + Send + Sync>; - -const UCI_LOG_DEFAULT: UciLogMode = UciLogMode::Disabled; - -pub struct UwbClientCallback { - rsp_sender: mpsc::UnboundedSender<(HalCallback, String)>, - logger: SyncUciLogger, - defrager: Mutex<PacketDefrager>, - chip_id: String, -} - -impl UwbClientCallback { - fn new( - rsp_sender: mpsc::UnboundedSender<(HalCallback, String)>, - logger: SyncUciLogger, - chip_id: String, - ) -> Self { - UwbClientCallback { rsp_sender, logger, defrager: Default::default(), chip_id } - } - - async fn log_uci_packet(&self, packet: UciPacketPacket) { - match packet.specialize() { - UciPacketChild::UciResponse(pkt) => self.logger.log_uci_response(pkt).await, - UciPacketChild::UciNotification(pkt) => self.logger.log_uci_notification(pkt).await, - _ => {} - } - } -} - -impl Interface for UwbClientCallback {} - -#[async_trait] -impl IUwbClientCallbackAsyncServer for UwbClientCallback { - async fn onHalEvent(&self, event: UwbEvent, event_status: UwbStatus) -> BinderResult<()> { - self.rsp_sender - .send((HalCallback::Event { event, event_status }, self.chip_id.clone())) - .unwrap_or_else(|e| error!("Error sending evt callback: {:?}", e)); - Ok(()) - } - - async fn onUciMessage(&self, data: &[u8]) -> BinderResult<()> { - if let Some(packet) = self.defrager.lock().await.defragment_packet(data) { - // all fragments for the packet received. - self.log_uci_packet(packet.clone()).await; - let packet_msg = uci_hrcv::uci_message(packet); - match packet_msg { - Ok(uci_hrcv::UciMessage::Response(evt)) => self - .rsp_sender - .send((HalCallback::UciRsp(evt), self.chip_id.clone())) - .unwrap_or_else(|e| error!("Error sending uci response: {:?}", e)), - Ok(uci_hrcv::UciMessage::Notification(evt)) => self - .rsp_sender - .send((HalCallback::UciNtf(evt), self.chip_id.clone())) - .unwrap_or_else(|e| error!("Error sending uci notification: {:?}", e)), - _ => error!("UCI message which is neither a UCI RSP or NTF: {:?}", data), - } - } - Ok(()) - } -} - -async fn get_hal_service( - chip_ids: &[String], -) -> Result<HashMap<String, Strong<dyn IUwbChipAsync<Tokio>>>> { - let service_name: &str = "android.hardware.uwb.IUwb/default"; - let i_uwb: Strong<dyn IUwbAsync<Tokio>> = - binder_tokio::wait_for_interface(service_name).await?; - let mut hal_map = HashMap::new(); - let chips = i_uwb.getChips().await?; - - // If this is a single-chip system with chip_id == "default", then the name of the chip - // doesn't matter. In that case, just grab the first IUwbChip from the list returned by getChips - if *chip_ids == vec![String::from("default")] { - if chips.is_empty() { - error!("No chips are provided by vendor"); - return Err(UwbErr::UwbStatus(UwbStatus::FAILED)); - } - let i_uwb_chip = i_uwb.getChip(chips.get(0).unwrap()).await?.into_async(); - hal_map.insert(String::from("default"), i_uwb_chip); - return Ok(hal_map); - } - - for chip_id in chip_ids { - if !chips.contains(chip_id) { - error!("Chip {} is not present in list of chip ids provided by vendor", chip_id); - return Err(UwbErr::UwbStatus(UwbStatus::FAILED)); - } - let i_uwb_chip = i_uwb.getChip(chip_id).await?.into_async(); - hal_map.insert(chip_id.clone(), i_uwb_chip); - } - - Ok(hal_map) -} - -#[async_trait] -pub trait UwbAdaptation { - async fn finalize(&mut self, exit_status: bool); - async fn hal_open(&self, chip_id: &str) -> Result<()>; - async fn hal_close(&self, chip_id: &str) -> Result<()>; - async fn core_initialization(&self, chip_id: &str) -> Result<()>; - async fn session_initialization(&self, session_id: i32, chip_id: &str) -> Result<()>; - async fn send_uci_message(&self, cmd: UciCommandPacket, chip_id: &str) -> Result<()>; -} - -#[derive(Clone)] -pub struct UwbAdaptationImpl { - hal_map: HashMap<String, Strong<dyn IUwbChipAsync<Tokio>>>, - #[allow(dead_code)] - // Need to store the death recipient since link_to_death stores a weak pointer. - hal_death_recipients: Arc<Mutex<Vec<DeathRecipient>>>, - rsp_sender: mpsc::UnboundedSender<(HalCallback, String)>, - logger: SyncUciLogger, -} - -impl UwbAdaptationImpl { - #[cfg(target_os = "android")] - fn get_uci_log_mode() -> UciLogMode { - match system_properties::read("persist.uwb.uci_logger_mode") { - Ok(Some(logger_mode)) => match logger_mode.as_str() { - "disabled" => UciLogMode::Disabled, - "filtered" => UciLogMode::Filtered, - "enabled" => UciLogMode::Enabled, - str => { - error!("Logger mode not recognized! Value: {:?}", str); - UCI_LOG_DEFAULT - } - }, - Ok(None) => UCI_LOG_DEFAULT, - Err(e) => { - error!("Failed to get uci_logger_mode {:?}", e); - UCI_LOG_DEFAULT - } - } - } - - #[cfg(not(target_os = "android"))] - fn get_uci_log_mode() -> UciLogMode { - // system_properties is not supported on host builds. - UCI_LOG_DEFAULT - } - - pub async fn new_with_args( - rsp_sender: mpsc::UnboundedSender<(HalCallback, String)>, - hal_map: HashMap<String, Strong<dyn IUwbChipAsync<Tokio>>>, - hal_death_recipients: Arc<Mutex<Vec<DeathRecipient>>>, - ) -> Result<Self> { - let logger = UciLoggerImpl::new( - UwbAdaptationImpl::get_uci_log_mode(), - Arc::new(Mutex::new(RealFileFactory::default())), - ) - .await; - Ok(UwbAdaptationImpl { - hal_map, - rsp_sender, - logger: Arc::new(logger), - hal_death_recipients, - }) - } - - pub async fn new( - rsp_sender: mpsc::UnboundedSender<(HalCallback, String)>, - chip_ids: &[String], - ) -> Result<Self> { - let hal_map = get_hal_service(chip_ids).await?; - - let mut hal_death_recipients = Vec::new(); - // Register for death notification. - for chip_id in chip_ids { - let rsp_sender_clone = rsp_sender.clone(); - let mut hal_death_recipient = DeathRecipient::new({ - let chip_id_clone = chip_id.clone(); - - move || { - error!("UWB HAL died. Resetting stack..."); - // Send error HAL event to trigger stack recovery. - rsp_sender_clone - .send(( - HalCallback::Event { - event: UwbEvent::ERROR, - event_status: UwbStatus::FAILED, - }, - chip_id_clone.clone(), - )) - .unwrap_or_else(|e| error!("Error sending error evt callback: {:?}", e)); - } - }); - hal_map.get(chip_id).unwrap().as_binder().link_to_death(&mut hal_death_recipient)?; - hal_death_recipients.push(hal_death_recipient); - } - Self::new_with_args(rsp_sender, hal_map, Arc::new(Mutex::new(hal_death_recipients))).await - } -} - -#[async_trait] -impl UwbAdaptation for UwbAdaptationImpl { - async fn finalize(&mut self, _exit_status: bool) {} - - async fn hal_open(&self, chip_id: &str) -> Result<()> { - let m_cback = BnUwbClientCallback::new_async_binder( - UwbClientCallback::new( - self.rsp_sender.clone(), - self.logger.clone(), - chip_id.to_string(), - ), - TokioRuntime(Handle::current()), - BinderFeatures::default(), - ); - match self.hal_map.get(chip_id) { - Some(chip) => Ok(chip.open(&m_cback).await?), - _ => Err(UwbErr::UwbStatus(UwbStatus::FAILED)), - } - } - - async fn hal_close(&self, chip_id: &str) -> Result<()> { - self.logger.close_file().await; - Ok(self.hal_map.get(chip_id).unwrap().close().await?) - } - - async fn core_initialization(&self, chip_id: &str) -> Result<()> { - Ok(self.hal_map.get(chip_id).unwrap().coreInit().await?) - } - - async fn session_initialization(&self, session_id: i32, chip_id: &str) -> Result<()> { - Ok(self.hal_map.get(chip_id).unwrap().sessionInit(session_id).await?) - } - - async fn send_uci_message(&self, cmd: UciCommandPacket, chip_id: &str) -> Result<()> { - self.logger.log_uci_command(cmd.clone()).await; - let packet: UciPacketPacket = cmd.into(); - // fragment packet. - let fragmented_packets: Vec<UciPacketHalPacket> = packet.into(); - for packet in fragmented_packets { - self.hal_map.get(chip_id).unwrap().sendUciMessage(&packet.to_vec()).await?; - } - // TODO should we be validating the returned number? - Ok(()) - } -} - -#[cfg(any(test, fuzzing))] -pub mod mock_adaptation; -#[cfg(any(test, fuzzing))] -pub mod mock_hal; - -#[cfg(test)] -pub mod tests { - use super::*; - use crate::adaptation::mock_hal::MockHal; - use crate::uci::mock_uci_logger::MockUciLogger; - use bytes::Bytes; - use uwb_uci_packets::*; - - fn create_uwb_client_callback( - rsp_sender: mpsc::UnboundedSender<(HalCallback, String)>, - ) -> UwbClientCallback { - // Add tests for the mock logger. - UwbClientCallback::new(rsp_sender, Arc::new(MockUciLogger::new()), String::from("default")) - } - - fn setup_client_callback() -> (mpsc::UnboundedReceiver<(HalCallback, String)>, UwbClientCallback) - { - // TODO: Remove this once we call it somewhere real. - logger::init( - logger::Config::default() - .with_tag_on_device("uwb_test") - .with_min_level(log::Level::Debug), - ); - let (rsp_sender, rsp_receiver) = mpsc::unbounded_channel::<(HalCallback, String)>(); - let uwb_client_callback = create_uwb_client_callback(rsp_sender); - (rsp_receiver, uwb_client_callback) - } - - #[tokio::test] - async fn test_on_hal_event() { - let event = UwbEvent(0); - let event_status = UwbStatus(1); - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onHalEvent(event, event_status).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!(response, Some((HalCallback::Event { event: _, event_status: _ }, _)))); - } - - #[tokio::test] - async fn test_get_device_info_rsp() { - let data = [ - 0x40, 0x02, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x01, - 0x0a, - ]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciRsp(uci_hrcv::UciResponse::GetDeviceInfoRsp(_)), _)) - )); - } - - #[tokio::test] - async fn test_get_caps_info_rsp() { - let data = [0x40, 0x03, 0x00, 0x05, 0x00, 0x01, 0x00, 0x01, 0x01]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciRsp(uci_hrcv::UciResponse::GetCapsInfoRsp(_)), _)) - )); - } - - #[tokio::test] - async fn test_set_config_rsp() { - let data = [0x40, 0x04, 0x00, 0x04, 0x01, 0x01, 0x01, 0x01]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciRsp(uci_hrcv::UciResponse::SetConfigRsp(_)), _)) - )); - } - - #[tokio::test] - async fn test_get_config_rsp() { - let data = [0x40, 0x05, 0x00, 0x05, 0x01, 0x01, 0x00, 0x01, 0x01]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciRsp(uci_hrcv::UciResponse::GetConfigRsp(_)), _)) - )); - } - - #[tokio::test] - async fn test_device_reset_rsp() { - let data = [0x40, 0x00, 0x00, 0x01, 0x00]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciRsp(uci_hrcv::UciResponse::DeviceResetRsp(_)), _)) - )); - } - - #[tokio::test] - async fn test_session_init_rsp() { - let data = [0x41, 0x00, 0x00, 0x01, 0x11]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciRsp(uci_hrcv::UciResponse::SessionInitRsp(_)), _)) - )); - } - - #[tokio::test] - async fn test_session_deinit_rsp() { - let data = [0x41, 0x01, 0x00, 0x01, 0x00]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciRsp(uci_hrcv::UciResponse::SessionDeinitRsp(_)), _)) - )); - } - - #[tokio::test] - async fn test_session_get_app_config_rsp() { - let data = [0x41, 0x04, 0x00, 0x02, 0x01, 0x00]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciRsp(uci_hrcv::UciResponse::SessionGetAppConfigRsp(_)), _)) - )); - } - - #[tokio::test] - async fn test_session_set_app_config_rsp() { - let data = [0x41, 0x03, 0x00, 0x04, 0x01, 0x01, 0x01, 0x00]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciRsp(uci_hrcv::UciResponse::SessionSetAppConfigRsp(_)), _)) - )); - } - - #[tokio::test] - async fn test_session_get_state_rsp() { - let data = [0x41, 0x06, 0x00, 0x02, 0x00, 0x01]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciRsp(uci_hrcv::UciResponse::SessionGetStateRsp(_)), _)) - )); - } - - #[tokio::test] - async fn test_session_get_count_rsp() { - let data = [0x41, 0x05, 0x00, 0x02, 0x00, 0x01]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciRsp(uci_hrcv::UciResponse::SessionGetCountRsp(_)), _)) - )); - } - - #[tokio::test] - async fn test_session_update_controller_multicast_list_rsp() { - let data = [0x41, 0x07, 0x00, 0x01, 0x00]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some(( - HalCallback::UciRsp( - uci_hrcv::UciResponse::SessionUpdateControllerMulticastListRsp(_) - ), - _ - )) - )); - } - - #[tokio::test] - async fn test_range_start_rsp() { - let data = [0x42, 0x00, 0x00, 0x01, 0x00]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciRsp(uci_hrcv::UciResponse::RangeStartRsp(_)), _)) - )); - } - - #[tokio::test] - async fn test_range_stop_rsp() { - let data = [0x42, 0x01, 0x00, 0x01, 0x00]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciRsp(uci_hrcv::UciResponse::RangeStopRsp(_)), _)) - )); - } - - #[tokio::test] - async fn test_android_set_country_code_rsp() { - let data = [0x4c, 0x01, 0x00, 0x01, 0x00]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciRsp(uci_hrcv::UciResponse::AndroidSetCountryCodeRsp(_)), _)) - )); - } - - #[tokio::test] - async fn test_android_get_power_stats_rsp() { - let data = [ - 0x4c, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciRsp(uci_hrcv::UciResponse::AndroidGetPowerStatsRsp(_)), _)) - )); - } - - #[tokio::test] - async fn test_raw_vendor_rsp_fragmented_packet() { - let fragment_1 = [ - 0x59, 0x01, 0x00, 0xff, 0x81, 0x93, 0xf8, 0x56, 0x53, 0x74, 0x5d, 0xcf, 0x45, 0xfa, - 0x34, 0xbd, 0xf1, 0x56, 0x53, 0x8f, 0x13, 0xff, 0x9b, 0xdd, 0xee, 0xaf, 0x0e, 0xff, - 0x1e, 0x63, 0xb6, 0xd7, 0xd4, 0x7b, 0xb7, 0x78, 0x30, 0xc7, 0x92, 0xd0, 0x8a, 0x5e, - 0xf0, 0x00, 0x1d, 0x05, 0xea, 0xf9, 0x56, 0xce, 0x8b, 0xbc, 0x8b, 0x1b, 0xc2, 0xd4, - 0x2a, 0xb8, 0x14, 0x82, 0x8b, 0xed, 0x12, 0xe5, 0x83, 0xe6, 0xb0, 0xb8, 0xa0, 0xb9, - 0xd0, 0x90, 0x6e, 0x09, 0x4e, 0x2e, 0x22, 0x38, 0x39, 0x03, 0x66, 0xf5, 0x95, 0x14, - 0x1c, 0xd7, 0x60, 0xbf, 0x28, 0x58, 0x9d, 0x47, 0x18, 0x1a, 0x93, 0x59, 0xbb, 0x0d, - 0x88, 0xf7, 0x7c, 0xce, 0x13, 0xa8, 0x2f, 0x3d, 0x0e, 0xd9, 0x5c, 0x19, 0x45, 0x5d, - 0xe8, 0xc3, 0xe0, 0x3a, 0xf3, 0x71, 0x09, 0x6e, 0x73, 0x07, 0x96, 0xa9, 0x1f, 0xf4, - 0x57, 0x84, 0x2e, 0x59, 0x6a, 0xf6, 0x90, 0x28, 0x47, 0xc1, 0x51, 0x7c, 0x59, 0x7e, - 0x95, 0xfc, 0xa6, 0x4d, 0x1b, 0xe6, 0xfe, 0x97, 0xa0, 0x39, 0x91, 0xa8, 0x28, 0xc9, - 0x1d, 0x7e, 0xfc, 0xec, 0x71, 0x1d, 0x43, 0x38, 0xcb, 0xbd, 0x50, 0xea, 0x02, 0xfd, - 0x2c, 0x7a, 0xde, 0x06, 0xdd, 0x77, 0x69, 0x4d, 0x2f, 0x57, 0xf5, 0x4b, 0x97, 0x51, - 0x58, 0x66, 0x7a, 0x8a, 0xcb, 0x7b, 0x91, 0x18, 0xbe, 0x4e, 0x94, 0xe4, 0xf1, 0xed, - 0x52, 0x06, 0xa7, 0xe8, 0x6b, 0xe1, 0x8f, 0x4a, 0x06, 0xe8, 0x2c, 0x9f, 0xc7, 0xcb, - 0xd2, 0x10, 0xb0, 0x0b, 0x71, 0x80, 0x2c, 0xd1, 0xf1, 0x03, 0xc2, 0x79, 0x7e, 0x7f, - 0x70, 0xf4, 0x8c, 0xc9, 0xcf, 0x9f, 0xcf, 0xa2, 0x8e, 0x6a, 0xe4, 0x1a, 0x28, 0x05, - 0xa8, 0xfe, 0x7d, 0xec, 0xd9, 0x5f, 0xa7, 0xd0, 0x29, 0x63, 0x1a, 0xba, 0x39, 0xf7, - 0xfa, 0x5e, 0xff, 0xb8, 0x5a, 0xbd, 0x35, - ]; - let fragment_2 = [ - 0x49, 0x01, 0x00, 0x91, 0xe7, 0x26, 0xfb, 0xc4, 0x48, 0x68, 0x42, 0x93, 0x23, 0x1f, - 0x87, 0xf6, 0x12, 0x5e, 0x60, 0xc8, 0x6a, 0x9d, 0x98, 0xbb, 0xb2, 0xb0, 0x47, 0x2f, - 0xaa, 0xa5, 0xce, 0xdb, 0x32, 0x88, 0x86, 0x0d, 0x6a, 0x5a, 0xfe, 0xc8, 0xda, 0xa1, - 0xc0, 0x06, 0x37, 0x08, 0xda, 0x67, 0x49, 0x6a, 0xa7, 0x04, 0x62, 0x95, 0xf3, 0x1e, - 0xcd, 0x71, 0x00, 0x99, 0x68, 0xb4, 0x03, 0xb3, 0x15, 0x64, 0x8b, 0xde, 0xbc, 0x8f, - 0x41, 0x64, 0xdf, 0x34, 0x6e, 0xff, 0x48, 0xc8, 0xe2, 0xbf, 0x02, 0x15, 0xc5, 0xbc, - 0x0f, 0xf8, 0xa1, 0x49, 0x91, 0x71, 0xdd, 0xb4, 0x37, 0x1c, 0xfa, 0x60, 0xcb, 0x0f, - 0xce, 0x6a, 0x0e, 0x90, 0xaf, 0x14, 0x30, 0xf2, 0x5b, 0x21, 0x6f, 0x85, 0xd3, 0x1b, - 0x89, 0xc9, 0xba, 0x3f, 0x07, 0x11, 0xbd, 0x56, 0xda, 0xdc, 0x88, 0xb4, 0xb0, 0x57, - 0x0b, 0x0c, 0x44, 0xd9, 0xb9, 0xd2, 0x38, 0x4c, 0xb6, 0xff, 0x83, 0xfe, 0xc8, 0x65, - 0xbc, 0x2a, 0x10, 0xed, 0x18, 0x62, 0xd2, 0x1b, 0x87, - ]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result1 = uwb_client_callback.onUciMessage(&fragment_1).await; - assert_eq!(result1, Ok(())); - let result2 = uwb_client_callback.onUciMessage(&fragment_2).await; - assert_eq!(result2, Ok(())); - // One defragmented packet sent as response - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciRsp(uci_hrcv::UciResponse::RawVendorRsp(_)), _)) - )); - } - - #[tokio::test] - async fn test_generic_error_ntf() { - let data = [0x60, 0x07, 0x00, 0x01, 0x01]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciNtf(uci_hrcv::UciNotification::GenericError(_)), _)) - )); - } - - #[tokio::test] - async fn test_device_status_ntf() { - let data = [0x60, 0x01, 0x00, 0x01, 0x01]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciNtf(uci_hrcv::UciNotification::DeviceStatusNtf(_)), _)) - )); - } - - #[tokio::test] - async fn test_session_status_ntf() { - let data = [0x61, 0x02, 0x00, 0x06, 0x01, 0x02, 0x03, 0x04, 0x02, 0x21]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciNtf(uci_hrcv::UciNotification::SessionStatusNtf(_)), _)) - )); - } - - #[tokio::test] - async fn test_session_update_controller_multicast_list_ntf() { - let data = [0x61, 0x07, 0x00, 0x06, 0x00, 0x01, 0x02, 0x03, 0x04, 0x00]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some(( - HalCallback::UciNtf( - uci_hrcv::UciNotification::SessionUpdateControllerMulticastListNtf(_) - ), - _ - )) - )); - } - - #[tokio::test] - async fn test_short_mac_two_way_range_data_ntf() { - let data = [ - 0x62, 0x00, 0x00, 0x19, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, 0x0a, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some(( - HalCallback::UciNtf(uci_hrcv::UciNotification::ShortMacTwoWayRangeDataNtf(_)), - _ - )) - )); - } - - #[tokio::test] - async fn test_extended_mac_two_way_range_data_ntf() { - let data = [ - 0x62, 0x00, 0x00, 0x19, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, 0x0a, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some(( - HalCallback::UciNtf(uci_hrcv::UciNotification::ExtendedMacTwoWayRangeDataNtf(_)), - _ - )) - )); - } - - #[tokio::test] - async fn test_raw_vendor_ntf_fragmented_packet() { - let fragment_1 = [ - 0x79, 0x01, 0x00, 0xff, 0x81, 0x93, 0xf8, 0x56, 0x53, 0x74, 0x5d, 0xcf, 0x45, 0xfa, - 0x34, 0xbd, 0xf1, 0x56, 0x53, 0x8f, 0x13, 0xff, 0x9b, 0xdd, 0xee, 0xaf, 0x0e, 0xff, - 0x1e, 0x63, 0xb6, 0xd7, 0xd4, 0x7b, 0xb7, 0x78, 0x30, 0xc7, 0x92, 0xd0, 0x8a, 0x5e, - 0xf0, 0x00, 0x1d, 0x05, 0xea, 0xf9, 0x56, 0xce, 0x8b, 0xbc, 0x8b, 0x1b, 0xc2, 0xd4, - 0x2a, 0xb8, 0x14, 0x82, 0x8b, 0xed, 0x12, 0xe5, 0x83, 0xe6, 0xb0, 0xb8, 0xa0, 0xb9, - 0xd0, 0x90, 0x6e, 0x09, 0x4e, 0x2e, 0x22, 0x38, 0x39, 0x03, 0x66, 0xf5, 0x95, 0x14, - 0x1c, 0xd7, 0x60, 0xbf, 0x28, 0x58, 0x9d, 0x47, 0x18, 0x1a, 0x93, 0x59, 0xbb, 0x0d, - 0x88, 0xf7, 0x7c, 0xce, 0x13, 0xa8, 0x2f, 0x3d, 0x0e, 0xd9, 0x5c, 0x19, 0x45, 0x5d, - 0xe8, 0xc3, 0xe0, 0x3a, 0xf3, 0x71, 0x09, 0x6e, 0x73, 0x07, 0x96, 0xa9, 0x1f, 0xf4, - 0x57, 0x84, 0x2e, 0x59, 0x6a, 0xf6, 0x90, 0x28, 0x47, 0xc1, 0x51, 0x7c, 0x59, 0x7e, - 0x95, 0xfc, 0xa6, 0x4d, 0x1b, 0xe6, 0xfe, 0x97, 0xa0, 0x39, 0x91, 0xa8, 0x28, 0xc9, - 0x1d, 0x7e, 0xfc, 0xec, 0x71, 0x1d, 0x43, 0x38, 0xcb, 0xbd, 0x50, 0xea, 0x02, 0xfd, - 0x2c, 0x7a, 0xde, 0x06, 0xdd, 0x77, 0x69, 0x4d, 0x2f, 0x57, 0xf5, 0x4b, 0x97, 0x51, - 0x58, 0x66, 0x7a, 0x8a, 0xcb, 0x7b, 0x91, 0x18, 0xbe, 0x4e, 0x94, 0xe4, 0xf1, 0xed, - 0x52, 0x06, 0xa7, 0xe8, 0x6b, 0xe1, 0x8f, 0x4a, 0x06, 0xe8, 0x2c, 0x9f, 0xc7, 0xcb, - 0xd2, 0x10, 0xb0, 0x0b, 0x71, 0x80, 0x2c, 0xd1, 0xf1, 0x03, 0xc2, 0x79, 0x7e, 0x7f, - 0x70, 0xf4, 0x8c, 0xc9, 0xcf, 0x9f, 0xcf, 0xa2, 0x8e, 0x6a, 0xe4, 0x1a, 0x28, 0x05, - 0xa8, 0xfe, 0x7d, 0xec, 0xd9, 0x5f, 0xa7, 0xd0, 0x29, 0x63, 0x1a, 0xba, 0x39, 0xf7, - 0xfa, 0x5e, 0xff, 0xb8, 0x5a, 0xbd, 0x35, - ]; - let fragment_2 = [ - 0x69, 0x01, 0x00, 0x91, 0xe7, 0x26, 0xfb, 0xc4, 0x48, 0x68, 0x42, 0x93, 0x23, 0x1f, - 0x87, 0xf6, 0x12, 0x5e, 0x60, 0xc8, 0x6a, 0x9d, 0x98, 0xbb, 0xb2, 0xb0, 0x47, 0x2f, - 0xaa, 0xa5, 0xce, 0xdb, 0x32, 0x88, 0x86, 0x0d, 0x6a, 0x5a, 0xfe, 0xc8, 0xda, 0xa1, - 0xc0, 0x06, 0x37, 0x08, 0xda, 0x67, 0x49, 0x6a, 0xa7, 0x04, 0x62, 0x95, 0xf3, 0x1e, - 0xcd, 0x71, 0x00, 0x99, 0x68, 0xb4, 0x03, 0xb3, 0x15, 0x64, 0x8b, 0xde, 0xbc, 0x8f, - 0x41, 0x64, 0xdf, 0x34, 0x6e, 0xff, 0x48, 0xc8, 0xe2, 0xbf, 0x02, 0x15, 0xc5, 0xbc, - 0x0f, 0xf8, 0xa1, 0x49, 0x91, 0x71, 0xdd, 0xb4, 0x37, 0x1c, 0xfa, 0x60, 0xcb, 0x0f, - 0xce, 0x6a, 0x0e, 0x90, 0xaf, 0x14, 0x30, 0xf2, 0x5b, 0x21, 0x6f, 0x85, 0xd3, 0x1b, - 0x89, 0xc9, 0xba, 0x3f, 0x07, 0x11, 0xbd, 0x56, 0xda, 0xdc, 0x88, 0xb4, 0xb0, 0x57, - 0x0b, 0x0c, 0x44, 0xd9, 0xb9, 0xd2, 0x38, 0x4c, 0xb6, 0xff, 0x83, 0xfe, 0xc8, 0x65, - 0xbc, 0x2a, 0x10, 0xed, 0x18, 0x62, 0xd2, 0x1b, 0x87, - ]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result1 = uwb_client_callback.onUciMessage(&fragment_1).await; - assert_eq!(result1, Ok(())); - let result2 = uwb_client_callback.onUciMessage(&fragment_2).await; - assert_eq!(result2, Ok(())); - // One defragmented packet sent as response - let response = rsp_receiver.recv().await; - assert!(matches!( - response, - Some((HalCallback::UciNtf(uci_hrcv::UciNotification::RawVendorNtf(_)), _)) - )); - } - - #[tokio::test] - async fn test_on_uci_message_bad() { - let data = [ - 0x42, 0x02, 0x00, 0x0b, 0x01, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x01, - 0x0a, - ]; - let (mut rsp_receiver, uwb_client_callback) = setup_client_callback(); - let result = uwb_client_callback.onUciMessage(&data).await; - assert_eq!(result, Ok(())); - let response = rsp_receiver.try_recv(); - assert!(response.is_err()); - } - - async fn setup_adaptation_impl(config_fn: impl Fn(&MockHal)) -> Result<UwbAdaptationImpl> { - // TODO: Remove this once we call it somewhere real. - logger::init( - logger::Config::default() - .with_tag_on_device("uwb_test") - .with_min_level(log::Level::Debug), - ); - let (rsp_sender, _) = mpsc::unbounded_channel::<(HalCallback, _)>(); - let mock_hal = MockHal::new(None); - config_fn(&mock_hal); - - UwbAdaptationImpl::new_with_args( - rsp_sender, - HashMap::from([( - String::from("default"), - binder::Strong::new(Box::new(mock_hal) as Box<dyn IUwbChipAsync<_> + 'static>), - )]), - Arc::new(Mutex::new(Vec::from([DeathRecipient::new(|| {})]))), - ) - .await - } - - #[tokio::test] - async fn test_send_uci_message() { - let cmd: UciCommandPacket = GetDeviceInfoCmdBuilder {}.build().into(); - let adaptation_impl = setup_adaptation_impl(|mock_hal| { - let cmd_packet: UciPacketPacket = cmd.clone().into(); - let mut cmd_frag_packets: Vec<UciPacketHalPacket> = cmd_packet.into(); - let cmd_frag_data = cmd_frag_packets.pop().unwrap().to_vec(); - let cmd_frag_data_len = cmd_frag_data.len(); - mock_hal.expect_send_uci_message( - cmd_frag_data, - None, - Ok(cmd_frag_data_len.try_into().unwrap()), - ); - }) - .await - .unwrap(); - adaptation_impl.send_uci_message(cmd, "default").await.unwrap(); - } - - #[tokio::test] - async fn test_send_uci_message_fragmented_packet() { - let (rsp_sender, _) = mpsc::unbounded_channel::<(HalCallback, _)>(); - let mock_hal = MockHal::new(None); - - let cmd_payload: [u8; 400] = [ - 0x81, 0x93, 0xf8, 0x56, 0x53, 0x74, 0x5d, 0xcf, 0x45, 0xfa, 0x34, 0xbd, 0xf1, 0x56, - 0x53, 0x8f, 0x13, 0xff, 0x9b, 0xdd, 0xee, 0xaf, 0x0e, 0xff, 0x1e, 0x63, 0xb6, 0xd7, - 0xd4, 0x7b, 0xb7, 0x78, 0x30, 0xc7, 0x92, 0xd0, 0x8a, 0x5e, 0xf0, 0x00, 0x1d, 0x05, - 0xea, 0xf9, 0x56, 0xce, 0x8b, 0xbc, 0x8b, 0x1b, 0xc2, 0xd4, 0x2a, 0xb8, 0x14, 0x82, - 0x8b, 0xed, 0x12, 0xe5, 0x83, 0xe6, 0xb0, 0xb8, 0xa0, 0xb9, 0xd0, 0x90, 0x6e, 0x09, - 0x4e, 0x2e, 0x22, 0x38, 0x39, 0x03, 0x66, 0xf5, 0x95, 0x14, 0x1c, 0xd7, 0x60, 0xbf, - 0x28, 0x58, 0x9d, 0x47, 0x18, 0x1a, 0x93, 0x59, 0xbb, 0x0d, 0x88, 0xf7, 0x7c, 0xce, - 0x13, 0xa8, 0x2f, 0x3d, 0x0e, 0xd9, 0x5c, 0x19, 0x45, 0x5d, 0xe8, 0xc3, 0xe0, 0x3a, - 0xf3, 0x71, 0x09, 0x6e, 0x73, 0x07, 0x96, 0xa9, 0x1f, 0xf4, 0x57, 0x84, 0x2e, 0x59, - 0x6a, 0xf6, 0x90, 0x28, 0x47, 0xc1, 0x51, 0x7c, 0x59, 0x7e, 0x95, 0xfc, 0xa6, 0x4d, - 0x1b, 0xe6, 0xfe, 0x97, 0xa0, 0x39, 0x91, 0xa8, 0x28, 0xc9, 0x1d, 0x7e, 0xfc, 0xec, - 0x71, 0x1d, 0x43, 0x38, 0xcb, 0xbd, 0x50, 0xea, 0x02, 0xfd, 0x2c, 0x7a, 0xde, 0x06, - 0xdd, 0x77, 0x69, 0x4d, 0x2f, 0x57, 0xf5, 0x4b, 0x97, 0x51, 0x58, 0x66, 0x7a, 0x8a, - 0xcb, 0x7b, 0x91, 0x18, 0xbe, 0x4e, 0x94, 0xe4, 0xf1, 0xed, 0x52, 0x06, 0xa7, 0xe8, - 0x6b, 0xe1, 0x8f, 0x4a, 0x06, 0xe8, 0x2c, 0x9f, 0xc7, 0xcb, 0xd2, 0x10, 0xb0, 0x0b, - 0x71, 0x80, 0x2c, 0xd1, 0xf1, 0x03, 0xc2, 0x79, 0x7e, 0x7f, 0x70, 0xf4, 0x8c, 0xc9, - 0xcf, 0x9f, 0xcf, 0xa2, 0x8e, 0x6a, 0xe4, 0x1a, 0x28, 0x05, 0xa8, 0xfe, 0x7d, 0xec, - 0xd9, 0x5f, 0xa7, 0xd0, 0x29, 0x63, 0x1a, 0xba, 0x39, 0xf7, 0xfa, 0x5e, 0xff, 0xb8, - 0x5a, 0xbd, 0x35, 0xe7, 0x26, 0xfb, 0xc4, 0x48, 0x68, 0x42, 0x93, 0x23, 0x1f, 0x87, - 0xf6, 0x12, 0x5e, 0x60, 0xc8, 0x6a, 0x9d, 0x98, 0xbb, 0xb2, 0xb0, 0x47, 0x2f, 0xaa, - 0xa5, 0xce, 0xdb, 0x32, 0x88, 0x86, 0x0d, 0x6a, 0x5a, 0xfe, 0xc8, 0xda, 0xa1, 0xc0, - 0x06, 0x37, 0x08, 0xda, 0x67, 0x49, 0x6a, 0xa7, 0x04, 0x62, 0x95, 0xf3, 0x1e, 0xcd, - 0x71, 0x00, 0x99, 0x68, 0xb4, 0x03, 0xb3, 0x15, 0x64, 0x8b, 0xde, 0xbc, 0x8f, 0x41, - 0x64, 0xdf, 0x34, 0x6e, 0xff, 0x48, 0xc8, 0xe2, 0xbf, 0x02, 0x15, 0xc5, 0xbc, 0x0f, - 0xf8, 0xa1, 0x49, 0x91, 0x71, 0xdd, 0xb4, 0x37, 0x1c, 0xfa, 0x60, 0xcb, 0x0f, 0xce, - 0x6a, 0x0e, 0x90, 0xaf, 0x14, 0x30, 0xf2, 0x5b, 0x21, 0x6f, 0x85, 0xd3, 0x1b, 0x89, - 0xc9, 0xba, 0x3f, 0x07, 0x11, 0xbd, 0x56, 0xda, 0xdc, 0x88, 0xb4, 0xb0, 0x57, 0x0b, - 0x0c, 0x44, 0xd9, 0xb9, 0xd2, 0x38, 0x4c, 0xb6, 0xff, 0x83, 0xfe, 0xc8, 0x65, 0xbc, - 0x2a, 0x10, 0xed, 0x18, 0x62, 0xd2, 0x1b, 0x87, - ]; - let cmd: UciCommandPacket = UciVendor_9_CommandBuilder { - opcode: 1, - payload: Some(Bytes::from(cmd_payload.to_vec())), - } - .build() - .into(); - - let cmd_frag_data_1 = [ - 0x39, 0x01, 0x00, 0xff, 0x81, 0x93, 0xf8, 0x56, 0x53, 0x74, 0x5d, 0xcf, 0x45, 0xfa, - 0x34, 0xbd, 0xf1, 0x56, 0x53, 0x8f, 0x13, 0xff, 0x9b, 0xdd, 0xee, 0xaf, 0x0e, 0xff, - 0x1e, 0x63, 0xb6, 0xd7, 0xd4, 0x7b, 0xb7, 0x78, 0x30, 0xc7, 0x92, 0xd0, 0x8a, 0x5e, - 0xf0, 0x00, 0x1d, 0x05, 0xea, 0xf9, 0x56, 0xce, 0x8b, 0xbc, 0x8b, 0x1b, 0xc2, 0xd4, - 0x2a, 0xb8, 0x14, 0x82, 0x8b, 0xed, 0x12, 0xe5, 0x83, 0xe6, 0xb0, 0xb8, 0xa0, 0xb9, - 0xd0, 0x90, 0x6e, 0x09, 0x4e, 0x2e, 0x22, 0x38, 0x39, 0x03, 0x66, 0xf5, 0x95, 0x14, - 0x1c, 0xd7, 0x60, 0xbf, 0x28, 0x58, 0x9d, 0x47, 0x18, 0x1a, 0x93, 0x59, 0xbb, 0x0d, - 0x88, 0xf7, 0x7c, 0xce, 0x13, 0xa8, 0x2f, 0x3d, 0x0e, 0xd9, 0x5c, 0x19, 0x45, 0x5d, - 0xe8, 0xc3, 0xe0, 0x3a, 0xf3, 0x71, 0x09, 0x6e, 0x73, 0x07, 0x96, 0xa9, 0x1f, 0xf4, - 0x57, 0x84, 0x2e, 0x59, 0x6a, 0xf6, 0x90, 0x28, 0x47, 0xc1, 0x51, 0x7c, 0x59, 0x7e, - 0x95, 0xfc, 0xa6, 0x4d, 0x1b, 0xe6, 0xfe, 0x97, 0xa0, 0x39, 0x91, 0xa8, 0x28, 0xc9, - 0x1d, 0x7e, 0xfc, 0xec, 0x71, 0x1d, 0x43, 0x38, 0xcb, 0xbd, 0x50, 0xea, 0x02, 0xfd, - 0x2c, 0x7a, 0xde, 0x06, 0xdd, 0x77, 0x69, 0x4d, 0x2f, 0x57, 0xf5, 0x4b, 0x97, 0x51, - 0x58, 0x66, 0x7a, 0x8a, 0xcb, 0x7b, 0x91, 0x18, 0xbe, 0x4e, 0x94, 0xe4, 0xf1, 0xed, - 0x52, 0x06, 0xa7, 0xe8, 0x6b, 0xe1, 0x8f, 0x4a, 0x06, 0xe8, 0x2c, 0x9f, 0xc7, 0xcb, - 0xd2, 0x10, 0xb0, 0x0b, 0x71, 0x80, 0x2c, 0xd1, 0xf1, 0x03, 0xc2, 0x79, 0x7e, 0x7f, - 0x70, 0xf4, 0x8c, 0xc9, 0xcf, 0x9f, 0xcf, 0xa2, 0x8e, 0x6a, 0xe4, 0x1a, 0x28, 0x05, - 0xa8, 0xfe, 0x7d, 0xec, 0xd9, 0x5f, 0xa7, 0xd0, 0x29, 0x63, 0x1a, 0xba, 0x39, 0xf7, - 0xfa, 0x5e, 0xff, 0xb8, 0x5a, 0xbd, 0x35, - ]; - let cmd_frag_data_len_1 = cmd_frag_data_1.len(); - - let cmd_frag_data_2 = [ - 0x29, 0x01, 0x00, 0x91, 0xe7, 0x26, 0xfb, 0xc4, 0x48, 0x68, 0x42, 0x93, 0x23, 0x1f, - 0x87, 0xf6, 0x12, 0x5e, 0x60, 0xc8, 0x6a, 0x9d, 0x98, 0xbb, 0xb2, 0xb0, 0x47, 0x2f, - 0xaa, 0xa5, 0xce, 0xdb, 0x32, 0x88, 0x86, 0x0d, 0x6a, 0x5a, 0xfe, 0xc8, 0xda, 0xa1, - 0xc0, 0x06, 0x37, 0x08, 0xda, 0x67, 0x49, 0x6a, 0xa7, 0x04, 0x62, 0x95, 0xf3, 0x1e, - 0xcd, 0x71, 0x00, 0x99, 0x68, 0xb4, 0x03, 0xb3, 0x15, 0x64, 0x8b, 0xde, 0xbc, 0x8f, - 0x41, 0x64, 0xdf, 0x34, 0x6e, 0xff, 0x48, 0xc8, 0xe2, 0xbf, 0x02, 0x15, 0xc5, 0xbc, - 0x0f, 0xf8, 0xa1, 0x49, 0x91, 0x71, 0xdd, 0xb4, 0x37, 0x1c, 0xfa, 0x60, 0xcb, 0x0f, - 0xce, 0x6a, 0x0e, 0x90, 0xaf, 0x14, 0x30, 0xf2, 0x5b, 0x21, 0x6f, 0x85, 0xd3, 0x1b, - 0x89, 0xc9, 0xba, 0x3f, 0x07, 0x11, 0xbd, 0x56, 0xda, 0xdc, 0x88, 0xb4, 0xb0, 0x57, - 0x0b, 0x0c, 0x44, 0xd9, 0xb9, 0xd2, 0x38, 0x4c, 0xb6, 0xff, 0x83, 0xfe, 0xc8, 0x65, - 0xbc, 0x2a, 0x10, 0xed, 0x18, 0x62, 0xd2, 0x1b, 0x87, - ]; - let cmd_frag_data_len_2 = cmd_frag_data_2.len(); - - mock_hal.expect_send_uci_message( - cmd_frag_data_1.to_vec(), - None, - Ok(cmd_frag_data_len_1.try_into().unwrap()), - ); - mock_hal.expect_send_uci_message( - cmd_frag_data_2.to_vec(), - None, - Ok(cmd_frag_data_len_2.try_into().unwrap()), - ); - let adaptation_impl = UwbAdaptationImpl::new_with_args( - rsp_sender, - HashMap::from([( - String::from("default"), - binder::Strong::new(Box::new(mock_hal) as Box<dyn IUwbChipAsync<_> + 'static>), - )]), - Arc::new(Mutex::new(Vec::from([DeathRecipient::new(|| {})]))), - ) - .await - .unwrap(); - adaptation_impl.send_uci_message(cmd, "default").await.unwrap(); - } -} diff --git a/src/rust/error.rs b/src/rust/error.rs deleted file mode 100644 index cc031f9..0000000 --- a/src/rust/error.rs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use crate::uci::uci_hrcv::UciResponse; -use crate::uci::{HalCallback, JNICommand}; -use android_hardware_uwb::aidl::android::hardware::uwb::UwbStatus::UwbStatus; -use std::array::TryFromSliceError; -use std::option::Option; -use tokio::sync::{mpsc, oneshot}; -use uwb_uci_packets::StatusCode; - -#[derive(Debug, thiserror::Error)] -pub enum UwbErr { - #[error("StatusCoode error: {0:?}")] - StatusCode(StatusCode), - #[error("UWBStatus error: {0:?}")] - UwbStatus(UwbStatus), - #[error("Binder error: {0}")] - Binder(#[from] binder::Status), - #[error("JNI error: {0}")] - Jni(#[from] jni::errors::Error), - #[error("IO error: {0}")] - Io(#[from] std::io::Error), - #[error("SendError for JNICommand: {0}")] - SendJNICommand( - #[from] mpsc::error::SendError<(JNICommand, Option<oneshot::Sender<UciResponse>>, String)>, - ), - #[error("SendError for HalCallback: {0}")] - SendHalCallback(#[from] mpsc::error::SendError<HalCallback>), - #[error("RecvError: {0}")] - RecvError(#[from] oneshot::error::RecvError), - #[error("Could not parse: {0}")] - Parse(#[from] uwb_uci_packets::Error), - #[error("Could not specialize: {0:?}")] - Specialize(Vec<u8>), - #[error("Could not convert: {0:?}")] - ConvertToArray(#[from] TryFromSliceError), - #[error("The dispatcher does not exist")] - NoneDispatcher, - #[error("Invalid args")] - InvalidArgs, - #[error("Exit")] - Exit, - #[error("Could not connect to HAL")] - HalUnavailable(#[from] binder::StatusCode), - #[error("Could not convert")] - ConvertToEnum(#[from] std::num::TryFromIntError), - #[error("Unknown error")] - Undefined, -} - -impl UwbErr { - pub fn failed() -> Self { - UwbErr::UwbStatus(UwbStatus::FAILED) - } - - pub fn refused() -> Self { - UwbErr::UwbStatus(UwbStatus::REFUSED) - } -} diff --git a/src/rust/event_manager/mock_event_manager.rs b/src/rust/event_manager/mock_event_manager.rs deleted file mode 100644 index 03d940a..0000000 --- a/src/rust/event_manager/mock_event_manager.rs +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//! MockEventManager - -use crate::event_manager::EventManager; -use jni::errors::{Error, JniError, Result}; -use log::warn; -use std::collections::VecDeque; -use std::sync::Mutex; -use uwb_uci_packets::{ - DeviceStatusNtfPacket, ExtendedMacTwoWayRangeDataNtfPacket, GenericErrorPacket, - SessionStatusNtfPacket, SessionUpdateControllerMulticastListNtfPacket, - ShortMacTwoWayRangeDataNtfPacket, UciNotificationPacket, -}; - -#[cfg(any(test, fuzzing))] -enum ExpectedCall { - DeviceStatus { out: Result<()> }, - CoreGenericError { out: Result<()> }, - SessionStatus { out: Result<()> }, - ShortRangeData { out: Result<()> }, - ExtendedRangeData { out: Result<()> }, - SessionUpdateControllerMulticastList { out: Result<()> }, - VendorUci { out: Result<()> }, -} - -#[cfg(any(test, fuzzing))] -#[derive(Default)] -pub struct MockEventManager { - expected_calls: Mutex<VecDeque<ExpectedCall>>, -} - -#[cfg(any(test, fuzzing))] -impl MockEventManager { - pub fn new() -> Self { - Default::default() - } - - pub fn expect_device_status_notification_received(&mut self, out: Result<()>) { - self.add_expected_call(ExpectedCall::DeviceStatus { out }); - } - - pub fn expect_core_generic_error_notification_received(&mut self, out: Result<()>) { - self.add_expected_call(ExpectedCall::CoreGenericError { out }); - } - - pub fn expect_session_status_notification_received(&mut self, out: Result<()>) { - self.add_expected_call(ExpectedCall::SessionStatus { out }); - } - - pub fn expect_short_range_data_notification_received(&mut self, out: Result<()>) { - self.add_expected_call(ExpectedCall::ShortRangeData { out }); - } - - pub fn expect_extended_range_data_notification_received(&mut self, out: Result<()>) { - self.add_expected_call(ExpectedCall::ExtendedRangeData { out }); - } - - pub fn expect_session_update_controller_multicast_list_notification_received( - &mut self, - out: Result<()>, - ) { - self.add_expected_call(ExpectedCall::SessionUpdateControllerMulticastList { out }); - } - - pub fn expect_vendor_uci_notification_received(&mut self, out: Result<()>) { - self.add_expected_call(ExpectedCall::VendorUci { out }); - } - - fn add_expected_call(&mut self, call: ExpectedCall) { - self.expected_calls.lock().unwrap().push_back(call); - } - - fn unwrap_out(&self, out: Option<Result<()>>, method_name: &str) -> Result<()> { - out.unwrap_or_else(move || { - warn!("unpected {:?}() called", method_name); - Err(Error::JniCall(JniError::Unknown)) - }) - } - - pub fn clear_expected_calls(&self) { - self.expected_calls.lock().unwrap().clear(); - } -} - -#[cfg(any(test, fuzzing))] -impl Drop for MockEventManager { - fn drop(&mut self) { - assert!(self.expected_calls.lock().unwrap().is_empty()); - } -} -#[cfg(any(test, fuzzing))] -impl EventManager for MockEventManager { - fn device_status_notification_received( - &self, - _data: DeviceStatusNtfPacket, - _chip_id: &str, - ) -> Result<()> { - let out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::DeviceStatus { out }) => Some(out), - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - - self.unwrap_out(out, "device_status_notification_received") - } - - fn core_generic_error_notification_received( - &self, - _data: GenericErrorPacket, - _chip_id: &str, - ) -> Result<()> { - let out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::CoreGenericError { out }) => Some(out), - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - - self.unwrap_out(out, "core_generic_error_notification_received") - } - - fn session_status_notification_received(&self, _data: SessionStatusNtfPacket) -> Result<()> { - let out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::SessionStatus { out }) => Some(out), - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - - self.unwrap_out(out, "session_status_notification_received") - } - - fn short_range_data_notification_received( - &self, - _data: ShortMacTwoWayRangeDataNtfPacket, - ) -> Result<()> { - let out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::ShortRangeData { out }) => Some(out), - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - - self.unwrap_out(out, "short_range_data_notification_received") - } - fn extended_range_data_notification_received( - &self, - _data: ExtendedMacTwoWayRangeDataNtfPacket, - ) -> Result<()> { - let out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::ExtendedRangeData { out }) => Some(out), - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - - self.unwrap_out(out, "extended_range_data_notification_received") - } - - fn session_update_controller_multicast_list_notification_received( - &self, - _data: SessionUpdateControllerMulticastListNtfPacket, - ) -> Result<()> { - let out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::SessionUpdateControllerMulticastList { out }) => Some(out), - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - - self.unwrap_out(out, "session_update_controller_multicast_list_notification_received") - } - - fn vendor_uci_notification_received( - &self, - _data: UciNotificationPacket, - _chip_id: &str, - ) -> Result<()> { - let out = { - let mut expected_calls = self.expected_calls.lock().unwrap(); - match expected_calls.pop_front() { - Some(ExpectedCall::VendorUci { out }) => Some(out), - Some(call) => { - expected_calls.push_front(call); - None - } - None => None, - } - }; - - self.unwrap_out(out, "vendor_uci_notification_received") - } -} diff --git a/src/rust/event_manager/mod.rs b/src/rust/event_manager/mod.rs deleted file mode 100644 index 226083a..0000000 --- a/src/rust/event_manager/mod.rs +++ /dev/null @@ -1,756 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use jni::errors::{Error, JniError, Result}; -use jni::objects::{GlobalRef, JClass, JObject, JValue, JValue::Void}; -use jni::signature::JavaType; -use jni::sys::jobjectArray; -use jni::{AttachGuard, JNIEnv, JavaVM}; -use log::error; -use num_traits::ToPrimitive; -use std::convert::TryInto; -use std::vec::Vec; -use uwb_uci_packets::{ - DeviceStatusNtfPacket, ExtendedAddressTwoWayRangingMeasurement, - ExtendedMacTwoWayRangeDataNtfPacket, GenericErrorPacket, Packet, RangeDataNtfPacket, - SessionStatusNtfPacket, SessionUpdateControllerMulticastListNtfPacket, - ShortAddressTwoWayRangingMeasurement, ShortMacTwoWayRangeDataNtfPacket, UciNotificationChild, - UciNotificationPacket, UciVendor_9_NotificationChild, UciVendor_A_NotificationChild, - UciVendor_B_NotificationChild, UciVendor_E_NotificationChild, UciVendor_F_NotificationChild, -}; - -const UWB_RANGING_DATA_CLASS: &str = "com/android/server/uwb/data/UwbRangingData"; -const UWB_TWO_WAY_MEASUREMENT_CLASS: &str = "com/android/server/uwb/data/UwbTwoWayMeasurement"; -const MULTICAST_LIST_UPDATE_STATUS_CLASS: &str = - "com/android/server/uwb/data/UwbMulticastListUpdateStatus"; -const SHORT_MAC_ADDRESS_LEN: usize = 2; -const EXTENDED_MAC_ADDRESS_LEN: usize = 8; - -// TODO: Reconsider the best way to cache the JNIEnv. We currently attach and detach for every -// call, which the documentation warns could be expensive. We could attach the thread permanently, -// but that would not allow us to detach when we drop this structure. We could cache the -// AttachGuard in the EventManager, but it is not Send, so we should wait to see how this is used -// and how expensive the current approach is. We can call JavaVM's get_env method if we're already -// attached. - -// TODO: We could consider caching the method ids rather than recomputing them each time at the cost -// of less safety. - -pub trait EventManager { - fn device_status_notification_received( - &self, - data: DeviceStatusNtfPacket, - chip_id: &str, - ) -> Result<()>; - fn core_generic_error_notification_received( - &self, - data: GenericErrorPacket, - chip_id: &str, - ) -> Result<()>; - fn session_status_notification_received(&self, data: SessionStatusNtfPacket) -> Result<()>; - fn short_range_data_notification_received( - &self, - data: ShortMacTwoWayRangeDataNtfPacket, - ) -> Result<()>; - fn extended_range_data_notification_received( - &self, - data: ExtendedMacTwoWayRangeDataNtfPacket, - ) -> Result<()>; - fn session_update_controller_multicast_list_notification_received( - &self, - data: SessionUpdateControllerMulticastListNtfPacket, - ) -> Result<()>; - fn vendor_uci_notification_received( - &self, - data: UciNotificationPacket, - chip_id: &str, - ) -> Result<()>; -} - -// Manages calling Java callbacks through the JNI. -pub struct EventManagerImpl { - jvm: JavaVM, - obj: GlobalRef, - // cache used to lookup uwb classes in callback. - class_loader_obj: GlobalRef, -} - -impl EventManager for EventManagerImpl { - fn device_status_notification_received( - &self, - data: DeviceStatusNtfPacket, - chip_id: &str, - ) -> Result<()> { - let env = self.jvm.attach_current_thread()?; - let result = self.handle_device_status_notification_received(&env, data, chip_id); - self.clear_exception(env); - result - } - - fn core_generic_error_notification_received( - &self, - data: GenericErrorPacket, - chip_id: &str, - ) -> Result<()> { - let env = self.jvm.attach_current_thread()?; - let result = self.handle_core_generic_error_notification_received(&env, data, chip_id); - self.clear_exception(env); - result - } - - fn session_status_notification_received(&self, data: SessionStatusNtfPacket) -> Result<()> { - let env = self.jvm.attach_current_thread()?; - let result = self.handle_session_status_notification_received(&env, data); - self.clear_exception(env); - result - } - - fn short_range_data_notification_received( - &self, - data: ShortMacTwoWayRangeDataNtfPacket, - ) -> Result<()> { - let env = self.jvm.attach_current_thread()?; - let result = self.handle_short_range_data_notification_received(&env, data); - self.clear_exception(env); - result - } - - fn extended_range_data_notification_received( - &self, - data: ExtendedMacTwoWayRangeDataNtfPacket, - ) -> Result<()> { - let env = self.jvm.attach_current_thread()?; - let result = self.handle_extended_range_data_notification_received(&env, data); - self.clear_exception(env); - result - } - - fn session_update_controller_multicast_list_notification_received( - &self, - data: SessionUpdateControllerMulticastListNtfPacket, - ) -> Result<()> { - let env = self.jvm.attach_current_thread()?; - let result = - self.handle_session_update_controller_multicast_list_notification_received(&env, data); - self.clear_exception(env); - result - } - fn vendor_uci_notification_received( - &self, - data: UciNotificationPacket, - chip_id: &str, - ) -> Result<()> { - let env = self.jvm.attach_current_thread()?; - let result = self.handle_vendor_uci_notification_received(&env, data, chip_id); - self.clear_exception(env); - result - } -} - -impl EventManagerImpl { - /// Creates a new EventManagerImpl. - pub fn new(env: JNIEnv, obj: JObject) -> Result<Self> { - let jvm = env.get_java_vm()?; - let obj = env.new_global_ref(obj)?; - let class_loader_obj = EventManagerImpl::get_classloader_obj(&env)?; - let class_loader_obj = env.new_global_ref(class_loader_obj)?; - Ok(EventManagerImpl { jvm, obj, class_loader_obj }) - } - - fn get_classloader_obj<'a>(env: &'a JNIEnv) -> Result<JObject<'a>> { - // Use UwbRangingData class to find the classloader used by the java service. - let ranging_data_class = env.find_class(&UWB_RANGING_DATA_CLASS)?; - let ranging_data_class_class = env.get_object_class(ranging_data_class)?; - let get_class_loader_method = env.get_method_id( - ranging_data_class_class, - "getClassLoader", - "()Ljava/lang/ClassLoader;", - )?; - let class_loader = env.call_method_unchecked( - ranging_data_class, - get_class_loader_method, - JavaType::Object("java/lang/ClassLoader".into()), - &[Void], - )?; - class_loader.l() - } - - fn find_class<'a>(&'a self, env: &'a JNIEnv, class_name: &'a str) -> Result<JClass<'a>> { - let class_value = env.call_method( - self.class_loader_obj.as_obj(), - "findClass", - "(Ljava/lang/String;)Ljava/lang/Class;", - &[JValue::Object(JObject::from(env.new_string(class_name)?))], - )?; - class_value.l().map(JClass::from) - } - - fn handle_device_status_notification_received( - &self, - env: &JNIEnv, - data: DeviceStatusNtfPacket, - chip_id: &str, - ) -> Result<()> { - let state = data.get_device_state().to_i32().ok_or_else(|| { - error!("Failed converting device_state to i32"); - Error::JniCall(JniError::Unknown) - })?; - env.call_method( - self.obj.as_obj(), - "onDeviceStatusNotificationReceived", - "(ILjava/lang/String;)V", - &[JValue::Int(state), JValue::Object(JObject::from(env.new_string(chip_id)?))], - ) - .map(|_| ()) // drop void method return - } - - fn handle_session_status_notification_received( - &self, - env: &JNIEnv, - data: SessionStatusNtfPacket, - ) -> Result<()> { - let session_id = data.get_session_id().to_i64().ok_or_else(|| { - error!("Failed converting session_id to i64"); - Error::JniCall(JniError::Unknown) - })?; - let state = data.get_session_state().to_i32().ok_or_else(|| { - error!("Failed converting session_state to i32"); - Error::JniCall(JniError::Unknown) - })?; - let reason_code = data.get_reason_code().to_i32().ok_or_else(|| { - error!("Failed converting reason_code to i32"); - Error::JniCall(JniError::Unknown) - })?; - env.call_method( - self.obj.as_obj(), - "onSessionStatusNotificationReceived", - "(JII)V", - &[JValue::Long(session_id), JValue::Int(state), JValue::Int(reason_code)], - ) - .map(|_| ()) // drop void method return - } - - fn handle_core_generic_error_notification_received( - &self, - env: &JNIEnv, - data: GenericErrorPacket, - chip_id: &str, - ) -> Result<()> { - let status = data.get_status().to_i32().ok_or_else(|| { - error!("Failed converting status to i32"); - Error::JniCall(JniError::Unknown) - })?; - env.call_method( - self.obj.as_obj(), - "onCoreGenericErrorNotificationReceived", - "(ILjava/lang/String;)V", - &[JValue::Int(status), JValue::Object(JObject::from(env.new_string(chip_id)?))], - ) - .map(|_| ()) // drop void method return - } - - fn create_zeroed_two_way_measurement_java<'a>( - env: &'a JNIEnv, - two_way_measurement_class: JClass, - mac_address_java: jobjectArray, - ) -> Result<JObject<'a>> { - env.new_object( - two_way_measurement_class, - "([BIIIIIIIIIIIII)V", - &[ - JValue::Object(JObject::from(mac_address_java)), - JValue::Int(0), - JValue::Int(0), - JValue::Int(0), - JValue::Int(0), - JValue::Int(0), - JValue::Int(0), - JValue::Int(0), - JValue::Int(0), - JValue::Int(0), - JValue::Int(0), - JValue::Int(0), - JValue::Int(0), - JValue::Int(0), - ], - ) - } - - fn create_short_mac_two_way_measurement_java<'a>( - env: &'a JNIEnv, - two_way_measurement_class: JClass, - two_way_measurement: &'a ShortAddressTwoWayRangingMeasurement, - ) -> Result<JObject<'a>> { - let mac_address_arr = two_way_measurement.mac_address.to_ne_bytes(); - let mac_address_java = - env.new_byte_array(SHORT_MAC_ADDRESS_LEN.to_i32().ok_or_else(|| { - error!("Failed converting mac address len to i32"); - Error::JniCall(JniError::Unknown) - })?)?; - // Convert from [u8] to [i8] since java does not support unsigned byte. - let mac_address_arr_i8 = mac_address_arr.map(|x| x as i8); - env.set_byte_array_region(mac_address_java, 0, &mac_address_arr_i8)?; - env.new_object( - two_way_measurement_class, - "([BIIIIIIIIIIIII)V", - &[ - JValue::Object(JObject::from(mac_address_java)), - JValue::Int(two_way_measurement.status.to_i32().ok_or_else(|| { - error!("Failed converting status to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(two_way_measurement.nlos.to_i32().ok_or_else(|| { - error!("Failed converting nlos to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(two_way_measurement.distance.to_i32().ok_or_else(|| { - error!("Failed converting distance to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(two_way_measurement.aoa_azimuth.to_i32().ok_or_else(|| { - error!("Failed converting aoa azimuth to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(two_way_measurement.aoa_azimuth_fom.to_i32().ok_or_else(|| { - error!("Failed converting aoa azimuth fom to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(two_way_measurement.aoa_elevation.to_i32().ok_or_else(|| { - error!("Failed converting aoa elevation to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(two_way_measurement.aoa_elevation_fom.to_i32().ok_or_else(|| { - error!("Failed converting aoa elevation fom to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(two_way_measurement.aoa_destination_azimuth.to_i32().ok_or_else( - || { - error!("Failed converting dest aoa azimuth to i32"); - Error::JniCall(JniError::Unknown) - }, - )?), - JValue::Int(two_way_measurement.aoa_destination_azimuth_fom.to_i32().ok_or_else( - || { - error!("Failed converting dest aoa azimuth fom to i32"); - Error::JniCall(JniError::Unknown) - }, - )?), - JValue::Int(two_way_measurement.aoa_destination_elevation.to_i32().ok_or_else( - || { - error!("Failed converting dest aoa elevation to i32"); - Error::JniCall(JniError::Unknown) - }, - )?), - JValue::Int( - two_way_measurement.aoa_destination_elevation_fom.to_i32().ok_or_else( - || { - error!("Failed converting dest aoa elevation azimuth to i32"); - Error::JniCall(JniError::Unknown) - }, - )?, - ), - JValue::Int(two_way_measurement.slot_index.to_i32().ok_or_else(|| { - error!("Failed converting slot index to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(two_way_measurement.rssi.to_i32().ok_or_else(|| { - error!("Failed converting rssi to i32"); - Error::JniCall(JniError::Unknown) - })?), - ], - ) - } - - fn create_extended_mac_two_way_measurement_java<'a>( - env: &'a JNIEnv, - two_way_measurement_class: JClass, - two_way_measurement: &'a ExtendedAddressTwoWayRangingMeasurement, - ) -> Result<JObject<'a>> { - let mac_address_arr = two_way_measurement.mac_address.to_ne_bytes(); - let mac_address_java = - env.new_byte_array(EXTENDED_MAC_ADDRESS_LEN.to_i32().ok_or_else(|| { - error!("Failed converting mac address len to i32"); - Error::JniCall(JniError::Unknown) - })?)?; - // Convert from [u8] to [i8] since java does not support unsigned byte. - let mac_address_arr_i8 = mac_address_arr.map(|x| x as i8); - env.set_byte_array_region(mac_address_java, 0, &mac_address_arr_i8)?; - env.new_object( - two_way_measurement_class, - "([BIIIIIIIIIIIII)V", - &[ - JValue::Object(JObject::from(mac_address_java)), - JValue::Int(two_way_measurement.status.to_i32().ok_or_else(|| { - error!("Failed converting status to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(two_way_measurement.nlos.to_i32().ok_or_else(|| { - error!("Failed converting nlos to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(two_way_measurement.distance.to_i32().ok_or_else(|| { - error!("Failed converting distance to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(two_way_measurement.aoa_azimuth.to_i32().ok_or_else(|| { - error!("Failed converting aoa azimuth to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(two_way_measurement.aoa_azimuth_fom.to_i32().ok_or_else(|| { - error!("Failed converting aoa azimuth fom to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(two_way_measurement.aoa_elevation.to_i32().ok_or_else(|| { - error!("Failed converting aoa elevation to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(two_way_measurement.aoa_elevation_fom.to_i32().ok_or_else(|| { - error!("Failed converting aoa elevation fom to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(two_way_measurement.aoa_destination_azimuth.to_i32().ok_or_else( - || { - error!("Failed converting dest aoa azimuth to i32"); - Error::JniCall(JniError::Unknown) - }, - )?), - JValue::Int(two_way_measurement.aoa_destination_azimuth_fom.to_i32().ok_or_else( - || { - error!("Failed converting dest aoa azimuth fom to i32"); - Error::JniCall(JniError::Unknown) - }, - )?), - JValue::Int(two_way_measurement.aoa_destination_elevation.to_i32().ok_or_else( - || { - error!("Failed converting dest aoa elevation to i32"); - Error::JniCall(JniError::Unknown) - }, - )?), - JValue::Int( - two_way_measurement.aoa_destination_elevation_fom.to_i32().ok_or_else( - || { - error!("Failed converting dest aoa elevation azimuth to i32"); - Error::JniCall(JniError::Unknown) - }, - )?, - ), - JValue::Int(two_way_measurement.slot_index.to_i32().ok_or_else(|| { - error!("Failed converting slot index to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(two_way_measurement.rssi.to_i32().ok_or_else(|| { - error!("Failed converting rssi to i32"); - Error::JniCall(JniError::Unknown) - })?), - ], - ) - } - - fn create_range_data_java<'a>( - &'a self, - env: &'a JNIEnv, - data: RangeDataNtfPacket, - two_way_measurements_java: jobjectArray, - num_two_way_measurements: i32, - ) -> Result<JObject<'a>> { - let ranging_data_class = self.find_class(env, UWB_RANGING_DATA_CLASS)?; - let raw_ntf: Vec<u8> = data.clone().to_vec(); - let raw_ntf_jbytearray = env.byte_array_from_slice(raw_ntf.as_ref())?; - env.new_object( - ranging_data_class, - "(JJIJIII[Lcom/android/server/uwb/data/UwbTwoWayMeasurement;[B)V", - &[ - JValue::Long(data.get_sequence_number().to_i64().ok_or_else(|| { - error!("Failed converting seq num to i64"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Long(data.get_session_id().to_i64().ok_or_else(|| { - error!("Failed converting session id to i64"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(data.get_rcr_indicator().to_i32().ok_or_else(|| { - error!("Failed converting rcr indicator to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Long(data.get_current_ranging_interval().to_i64().ok_or_else(|| { - error!("Failed converting current ranging interval to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(data.get_ranging_measurement_type().to_i32().ok_or_else(|| { - error!("Failed converting ranging measurement type to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(data.get_mac_address_indicator().to_i32().ok_or_else(|| { - error!("Failed converting mac address indicator to i32"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(num_two_way_measurements), - JValue::Object(JObject::from(two_way_measurements_java)), - JValue::Object(JObject::from(raw_ntf_jbytearray)), - ], - ) - } - - fn handle_short_range_data_notification_received( - &self, - env: &JNIEnv, - data: ShortMacTwoWayRangeDataNtfPacket, - ) -> Result<()> { - let two_way_measurement_class = self.find_class(env, UWB_TWO_WAY_MEASUREMENT_CLASS)?; - let two_way_measurement_initial_java = - EventManagerImpl::create_zeroed_two_way_measurement_java( - env, - two_way_measurement_class, - env.new_byte_array(SHORT_MAC_ADDRESS_LEN.to_i32().ok_or_else(|| { - error!("Failed converting mac address len to i32"); - Error::JniCall(JniError::Unknown) - })?)?, - )?; - let num_two_way_measurements: i32 = - data.get_two_way_ranging_measurements().len().to_i32().ok_or_else(|| { - error!("Failed converting len to i32"); - Error::JniCall(JniError::Unknown) - })?; - let two_way_measurements_java = env.new_object_array( - num_two_way_measurements, - two_way_measurement_class, - two_way_measurement_initial_java, - )?; - for (i, two_way_measurement) in data.get_two_way_ranging_measurements().iter().enumerate() { - let two_way_measurement_java = - EventManagerImpl::create_short_mac_two_way_measurement_java( - env, - two_way_measurement_class, - two_way_measurement, - )?; - env.set_object_array_element( - two_way_measurements_java, - i.to_i32().ok_or_else(|| { - error!("Failed converting idx to i32"); - Error::JniCall(JniError::Unknown) - })?, - two_way_measurement_java, - )? - } - let ranging_data_java = self.create_range_data_java( - env, - data.into(), - two_way_measurements_java, - num_two_way_measurements, - )?; - env.call_method( - self.obj.as_obj(), - "onRangeDataNotificationReceived", - "(Lcom/android/server/uwb/data/UwbRangingData;)V", - &[JValue::Object(ranging_data_java)], - ) - .map(|_| ()) // drop void method return - } - - fn handle_extended_range_data_notification_received( - &self, - env: &JNIEnv, - data: ExtendedMacTwoWayRangeDataNtfPacket, - ) -> Result<()> { - let two_way_measurement_class = self.find_class(env, UWB_TWO_WAY_MEASUREMENT_CLASS)?; - let two_way_measurement_initial_java = - EventManagerImpl::create_zeroed_two_way_measurement_java( - env, - two_way_measurement_class, - env.new_byte_array(EXTENDED_MAC_ADDRESS_LEN.to_i32().ok_or_else(|| { - error!("Failed converting mac address len to i32"); - Error::JniCall(JniError::Unknown) - })?)?, - )?; - let num_two_way_measurements: i32 = - data.get_two_way_ranging_measurements().len().to_i32().ok_or_else(|| { - error!("Failed converting len to i32"); - Error::JniCall(JniError::Unknown) - })?; - let two_way_measurements_java = env.new_object_array( - num_two_way_measurements, - two_way_measurement_class, - two_way_measurement_initial_java, - )?; - for (i, two_way_measurement) in data.get_two_way_ranging_measurements().iter().enumerate() { - let two_way_measurement_java = - EventManagerImpl::create_extended_mac_two_way_measurement_java( - env, - two_way_measurement_class, - two_way_measurement, - )?; - env.set_object_array_element( - two_way_measurements_java, - i.to_i32().ok_or_else(|| { - error!("Failed converting idx to i32"); - Error::JniCall(JniError::Unknown) - })?, - two_way_measurement_java, - )?; - } - let ranging_data_java = self.create_range_data_java( - env, - data.into(), - two_way_measurements_java, - num_two_way_measurements, - )?; - env.call_method( - self.obj.as_obj(), - "onRangeDataNotificationReceived", - "(Lcom/android/server/uwb/data/UwbRangingData;)V", - &[JValue::Object(ranging_data_java)], - ) - .map(|_| ()) // drop void method return - } - - pub fn handle_session_update_controller_multicast_list_notification_received( - &self, - env: &JNIEnv, - data: SessionUpdateControllerMulticastListNtfPacket, - ) -> Result<()> { - let uwb_multicast_update_class = - self.find_class(env, MULTICAST_LIST_UPDATE_STATUS_CLASS)?; - - let controlee_status = data.get_controlee_status(); - let count: i32 = controlee_status.len().try_into().map_err(|_| { - error!("Failed to convert controlee status length"); - Error::JniCall(JniError::Unknown) - })?; - let mut mac_address_list: Vec<i32> = Vec::new(); - let mut subsession_id_list: Vec<i64> = Vec::new(); - let mut status_list: Vec<i32> = Vec::new(); - - for iter in controlee_status { - mac_address_list.push(iter.mac_address.into()); - subsession_id_list.push(iter.subsession_id.into()); - status_list.push(iter.status.to_i32().ok_or_else(|| { - error!("Failed to convert controlee_status's status field: {:?}", iter.status); - Error::JniCall(JniError::Unknown) - })?); - } - - let mac_address_jintarray = env.new_int_array(count)?; - env.set_int_array_region(mac_address_jintarray, 0, mac_address_list.as_ref())?; - let subsession_id_jlongarray = env.new_long_array(count)?; - env.set_long_array_region(subsession_id_jlongarray, 0, subsession_id_list.as_ref())?; - let status_jintarray = env.new_int_array(count)?; - env.set_int_array_region(status_jintarray, 0, status_list.as_ref())?; - - let uwb_multicast_update_object = env.new_object( - uwb_multicast_update_class, - "(JII[I[J[I)V", - &[ - JValue::Long(data.get_session_id().try_into().map_err(|_| { - error!("Could not convert session_id"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(data.get_remaining_multicast_list_size().try_into().map_err(|_| { - error!("Could not convert remaining multicast list size"); - Error::JniCall(JniError::Unknown) - })?), - JValue::Int(count), - JValue::Object(JObject::from(mac_address_jintarray)), - JValue::Object(JObject::from(subsession_id_jlongarray)), - JValue::Object(JObject::from(status_jintarray)), - ], - )?; - - env.call_method( - self.obj.as_obj(), - "onMulticastListUpdateNotificationReceived", - "(Lcom/android/server/uwb/data/UwbMulticastListUpdateStatus;)V", - &[JValue::Object(uwb_multicast_update_object)], - ) - .map(|_| ()) // drop void method return - } - - fn get_vendor_uci_payload(data: UciNotificationPacket) -> Result<Vec<u8>> { - match data.specialize() { - UciNotificationChild::UciVendor_9_Notification(evt) => match evt.specialize() { - UciVendor_9_NotificationChild::Payload(payload) => Ok(payload.to_vec()), - UciVendor_9_NotificationChild::None => Ok(Vec::new()), - }, - UciNotificationChild::UciVendor_A_Notification(evt) => match evt.specialize() { - UciVendor_A_NotificationChild::Payload(payload) => Ok(payload.to_vec()), - UciVendor_A_NotificationChild::None => Ok(Vec::new()), - }, - UciNotificationChild::UciVendor_B_Notification(evt) => match evt.specialize() { - UciVendor_B_NotificationChild::Payload(payload) => Ok(payload.to_vec()), - UciVendor_B_NotificationChild::None => Ok(Vec::new()), - }, - UciNotificationChild::UciVendor_E_Notification(evt) => match evt.specialize() { - UciVendor_E_NotificationChild::Payload(payload) => Ok(payload.to_vec()), - UciVendor_E_NotificationChild::None => Ok(Vec::new()), - }, - UciNotificationChild::UciVendor_F_Notification(evt) => match evt.specialize() { - UciVendor_F_NotificationChild::Payload(payload) => Ok(payload.to_vec()), - UciVendor_F_NotificationChild::None => Ok(Vec::new()), - }, - _ => { - error!("Invalid vendor notification with gid {:?}", data.to_vec()); - Err(Error::JniCall(JniError::Unknown)) - } - } - } - - pub fn handle_vendor_uci_notification_received( - &self, - env: &JNIEnv, - data: UciNotificationPacket, - _chip_id: &str, - ) -> Result<()> { - let gid: i32 = data.get_group_id().to_i32().ok_or_else(|| { - error!("Failed to convert gid"); - Error::JniCall(JniError::Unknown) - })?; - let oid: i32 = data.get_opcode().to_i32().ok_or_else(|| { - error!("Failed to convert gid"); - Error::JniCall(JniError::Unknown) - })?; - let payload: Vec<u8> = EventManagerImpl::get_vendor_uci_payload(data)?; - let payload_jbytearray = env.byte_array_from_slice(payload.as_ref())?; - - // TODO(b/237533396): Add chip_id parameter to onVendorUciNotificationReceived - env.call_method( - self.obj.as_obj(), - "onVendorUciNotificationReceived", - "(II[B)V", - &[ - JValue::Int(gid), - JValue::Int(oid), - JValue::Object(JObject::from(payload_jbytearray)), - ], - ) - .map(|_| ()) // drop void method return - } - - // Attempts to clear an exception. If we do not do this, the exception continues being thrown - // when the control flow returns to Java. We discard errors here (after logging them) rather - // than propagating them to the caller since there's nothing they can do with that information. - fn clear_exception(&self, env: AttachGuard) { - match env.exception_check() { - Ok(true) => match env.exception_clear() { - Ok(()) => {} // We successfully cleared the exception. - Err(e) => error!("Error clearing JNI exception: {:?}", e), - }, - Ok(false) => {} // No exception found. - Err(e) => error!("Error checking JNI exception: {:?}", e), - } - } -} - -#[cfg(any(test, fuzzing))] -pub mod mock_event_manager; diff --git a/src/rust/lib.rs b/src/rust/lib.rs deleted file mode 100644 index e9f3dbe..0000000 --- a/src/rust/lib.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -// TODO remove this and add appropriate documentation once it is decided whether this stays here or -// moves into packages/modules/Uwb -#![allow(missing_docs)] -#![feature(rustc_private)] - -pub mod adaptation; -pub mod error; -pub mod event_manager; -pub mod uci; diff --git a/src/rust/uci/mock_uci_logger.rs b/src/rust/uci/mock_uci_logger.rs deleted file mode 100644 index ebf53c7..0000000 --- a/src/rust/uci/mock_uci_logger.rs +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -extern crate libc; - -use crate::uci::uci_logger::UciLogger; -use crate::uci::UwbErr; -use async_trait::async_trait; -use std::path::Path; -use std::time::Duration; -use uwb_uci_packets::{UciCommandPacket, UciNotificationPacket, UciResponsePacket}; - -#[cfg(test)] -pub struct MockUciLogger {} - -#[cfg(test)] -impl MockUciLogger { - pub fn new() -> Self { - MockUciLogger {} - } -} - -#[cfg(test)] -impl Default for MockUciLogger { - fn default() -> Self { - Self::new() - } -} - -#[cfg(test)] -#[async_trait] -impl UciLogger for MockUciLogger { - async fn log_uci_command(&self, _cmd: UciCommandPacket) {} - async fn log_uci_response(&self, _rsp: UciResponsePacket) {} - async fn log_uci_notification(&self, _ntf: UciNotificationPacket) {} - async fn close_file(&self) {} -} - -#[cfg(test)] -pub async fn create_dir(_path: impl AsRef<Path>) -> Result<(), UwbErr> { - Ok(()) -} - -#[cfg(test)] -pub async fn remove_file(_path: impl AsRef<Path>) -> Result<(), UwbErr> { - Ok(()) -} - -#[cfg(test)] -pub async fn rename(_from: impl AsRef<Path>, _to: impl AsRef<Path>) -> Result<(), UwbErr> { - Ok(()) -} - -#[cfg(test)] -pub struct Instant {} -impl Instant { - pub fn now() -> Instant { - Instant {} - } - pub fn elapsed(&self) -> Duration { - Duration::from_micros(0) - } -} diff --git a/src/rust/uci/mod.rs b/src/rust/uci/mod.rs deleted file mode 100644 index 7cd7da1..0000000 --- a/src/rust/uci/mod.rs +++ /dev/null @@ -1,1129 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -pub mod uci_hmsgs; -pub mod uci_hrcv; -pub mod uci_logger; - -use crate::adaptation::{UwbAdaptation, UwbAdaptationImpl}; -use crate::error::UwbErr; -use crate::event_manager::EventManager; -use crate::uci::uci_hrcv::UciResponse; -use android_hardware_uwb::aidl::android::hardware::uwb::{ - UwbEvent::UwbEvent, UwbStatus::UwbStatus, -}; -use arbitrary::Arbitrary; -use log::{debug, error, info, warn}; -use num_traits::FromPrimitive; -use std::future::Future; -use std::option::Option; -use std::sync::Arc; -use tokio::runtime::{Builder, Runtime}; -use tokio::sync::{mpsc, oneshot, Notify}; -use tokio::{select, task}; -use uwb_uci_packets::{ - AndroidGetPowerStatsCmdBuilder, DeviceState, DeviceStatusNtfBuilder, GetCapsInfoCmdBuilder, - GetDeviceInfoCmdBuilder, GetDeviceInfoRspPacket, MessageControl, RangeStartCmdBuilder, - RangeStopCmdBuilder, SessionDeinitCmdBuilder, SessionGetAppConfigCmdBuilder, - SessionGetCountCmdBuilder, SessionGetStateCmdBuilder, SessionState, SessionStatusNtfPacket, - StatusCode, UciCommandPacket, -}; - -pub type Result<T> = std::result::Result<T, UwbErr>; -pub type UciResponseHandle = oneshot::Sender<UciResponse>; -pub type SyncUwbAdaptation = Arc<dyn UwbAdaptation + Send + Sync>; - -// Commands sent from JNI. -#[derive(Arbitrary, Clone, Debug, PartialEq, Eq)] -pub enum JNICommand { - // Blocking UCI commands - Enable, - UciGetCapsInfo, - UciGetDeviceInfo, - UciSessionInit(u32, u8), - UciSessionDeinit(u32), - UciSessionGetCount, - UciStartRange(u32), - UciStopRange(u32), - UciGetSessionState(u32), - UciSessionUpdateMulticastList { - session_id: u32, - action: u8, - no_of_controlee: u8, - address_list: Vec<i16>, - sub_session_id_list: Vec<i32>, - message_control: i32, - sub_session_key_list: Vec<Vec<u8>>, - }, - UciSetCountryCode { - code: Vec<u8>, - }, - UciSetAppConfig { - session_id: u32, - no_of_params: u32, - // TODO this field should be removed, in tandem with a change to the Uwb APEX - app_config_param_len: u32, - app_configs: Vec<u8>, - }, - UciGetAppConfig { - session_id: u32, - no_of_params: u32, - app_config_param_len: u32, - app_configs: Vec<u8>, - }, - UciRawVendorCmd { - gid: u32, - oid: u32, - payload: Vec<u8>, - }, - UciDeviceReset { - reset_config: u8, - }, - UciGetPowerStats, - - // Non blocking commands - Disable(bool), -} - -// Responses from the HAL. -#[derive(Debug)] -pub enum HalCallback { - Event { event: UwbEvent, event_status: UwbStatus }, - UciRsp(uci_hrcv::UciResponse), - UciNtf(uci_hrcv::UciNotification), -} - -#[derive(Debug, PartialEq)] -pub enum UwbState { - None, - W4HalOpen, - Ready, - W4UciResp, - W4HalClose, -} - -#[derive(Clone)] -struct Retryer { - received: Arc<Notify>, - failed: Arc<Notify>, - retry: Arc<Notify>, -} - -impl Retryer { - fn new() -> Self { - Self { - received: Arc::new(Notify::new()), - failed: Arc::new(Notify::new()), - retry: Arc::new(Notify::new()), - } - } - - async fn command_failed(&self) { - self.failed.notified().await - } - - async fn immediate_retry(&self) { - self.retry.notified().await - } - - async fn command_serviced(&self) { - self.received.notified().await - } - - fn received(&self) { - self.received.notify_one() - } - - fn retry(&self) { - self.retry.notify_one() - } - - fn failed(&self) { - self.failed.notify_one() - } - - fn send_with_retry( - self, - adaptation: SyncUwbAdaptation, - cmd: UciCommandPacket, - chip_id: String, - ) { - tokio::task::spawn(async move { - let mut received_response = false; - for retry in 0..MAX_RETRIES { - adaptation.send_uci_message(cmd.clone(), &chip_id).await.unwrap_or_else(|e| { - error!("Sending UCI message to chip {} failed: {:?}", chip_id, e); - }); - select! { - _ = tokio::time::sleep(tokio::time::Duration::from_millis(RETRY_DELAY_MS)) => warn!("UWB chip did not respond within {}ms deadline. Retrying (#{})...", RETRY_DELAY_MS, retry + 1), - _ = self.command_serviced() => { - received_response = true; - break; - } - _ = self.immediate_retry() => debug!("UWB chip requested immediate retry. Retrying (#{})...", retry + 1), - } - } - if !received_response { - error!("After {} retries, no response from UWB chip {}", MAX_RETRIES, chip_id); - adaptation.core_initialization(&chip_id).await.unwrap_or_else(|e| { - error!("Resetting chip {} due to no responses failed: {:?}", chip_id, e); - }); - self.failed(); - } - }); - } -} - -//TODO pull in libfutures instead of open-coding this -async fn option_future<R, T: Future<Output = R>>(mf: Option<T>) -> Option<R> { - if let Some(f) = mf { - Some(f.await) - } else { - None - } -} - -struct Driver<T: EventManager> { - adaptation: SyncUwbAdaptation, - event_manager: T, - cmd_receiver: mpsc::UnboundedReceiver<(JNICommand, Option<UciResponseHandle>, String)>, - rsp_receiver: mpsc::UnboundedReceiver<(HalCallback, String)>, - response_channel: Option<(UciResponseHandle, Retryer)>, - state: UwbState, -} - -// Creates a future that handles messages from JNI and the HAL. -async fn drive<T: EventManager + Send + Sync>( - adaptation: SyncUwbAdaptation, - event_manager: T, - cmd_receiver: mpsc::UnboundedReceiver<(JNICommand, Option<UciResponseHandle>, String)>, - rsp_receiver: mpsc::UnboundedReceiver<(HalCallback, String)>, -) -> Result<()> { - Driver::new(adaptation, event_manager, cmd_receiver, rsp_receiver).await.drive().await -} - -const MAX_RETRIES: usize = 5; -const RETRY_DELAY_MS: u64 = 800; - -impl<T: EventManager> Driver<T> { - async fn new( - adaptation: SyncUwbAdaptation, - event_manager: T, - cmd_receiver: mpsc::UnboundedReceiver<(JNICommand, Option<UciResponseHandle>, String)>, - rsp_receiver: mpsc::UnboundedReceiver<(HalCallback, String)>, - ) -> Self { - Self { - adaptation, - event_manager, - cmd_receiver, - rsp_receiver, - response_channel: None, - state: UwbState::None, - } - } - - // Continually handles messages. - async fn drive(mut self) -> Result<()> { - loop { - match self.drive_once().await { - Err(UwbErr::Exit) => return Ok(()), - Err(e) => error!("drive_once: {:?}", e), - Ok(()) => (), - } - } - } - - async fn handle_blocking_jni_cmd( - &mut self, - tx: oneshot::Sender<UciResponse>, - cmd: JNICommand, - chip_id: String, - ) -> Result<()> { - log::debug!("Received blocking cmd {:?}", cmd); - let command: UciCommandPacket = match cmd { - JNICommand::Enable => { - match ( - self.adaptation.hal_open(&chip_id).await, - self.adaptation.core_initialization(&chip_id).await, - ) { - (Ok(()), Ok(())) => { - tx.send(UciResponse::EnableRsp(true)) - .unwrap_or_else(|e| error!("Error sending Enable response: {:?}", e)); - self.set_state(UwbState::W4HalOpen); - } - (Err(e), _) | (_, Err(e)) => { - error!("Enable UWB failed with: {:?}", e); - tx.send(UciResponse::EnableRsp(false)) - .unwrap_or_else(|e| error!("Error sending Enable response: {:?}", e)); - } - } - return Ok(()); - } - JNICommand::UciGetDeviceInfo => GetDeviceInfoCmdBuilder {}.build().into(), - JNICommand::UciGetCapsInfo => GetCapsInfoCmdBuilder {}.build().into(), - JNICommand::UciSessionInit(session_id, session_type) => { - uci_hmsgs::build_session_init_cmd(session_id, session_type)?.build().into() - } - JNICommand::UciSessionDeinit(session_id) => { - SessionDeinitCmdBuilder { session_id }.build().into() - } - JNICommand::UciSessionGetCount => SessionGetCountCmdBuilder {}.build().into(), - JNICommand::UciGetPowerStats => AndroidGetPowerStatsCmdBuilder {}.build().into(), - JNICommand::UciStartRange(session_id) => { - RangeStartCmdBuilder { session_id }.build().into() - } - JNICommand::UciStopRange(session_id) => { - RangeStopCmdBuilder { session_id }.build().into() - } - JNICommand::UciGetSessionState(session_id) => { - SessionGetStateCmdBuilder { session_id }.build().into() - } - JNICommand::UciSessionUpdateMulticastList { - session_id, - action, - no_of_controlee, - ref address_list, - ref sub_session_id_list, - message_control, - ref sub_session_key_list, - } => uci_hmsgs::build_multicast_list_update_cmd( - session_id, - action, - no_of_controlee, - address_list, - sub_session_id_list, - match message_control { - -1 => None, - _ => { - Some(MessageControl::from_i32(message_control).ok_or(UwbErr::InvalidArgs)?) - } - }, - sub_session_key_list, - )? - .build() - .into(), - JNICommand::UciSetCountryCode { ref code } => { - uci_hmsgs::build_set_country_code_cmd(code)?.build().into() - } - JNICommand::UciSetAppConfig { session_id, no_of_params, ref app_configs, .. } => { - uci_hmsgs::build_set_app_config_cmd(session_id, no_of_params, app_configs)? - .build() - .into() - } - JNICommand::UciGetAppConfig { session_id, ref app_configs, .. } => { - SessionGetAppConfigCmdBuilder { session_id, app_cfg: app_configs.to_vec() } - .build() - .into() - } - JNICommand::UciRawVendorCmd { gid, oid, payload } => { - uci_hmsgs::build_uci_vendor_cmd_packet(gid, oid, payload)? - } - JNICommand::UciDeviceReset { reset_config } => { - uci_hmsgs::build_device_reset_cmd(reset_config)?.build().into() - } - _ => { - error!("Unexpected blocking cmd received {:?}", cmd); - return Ok(()); - } - }; - - log::debug!("Sending HAL UCI message {:?}", command); - - let retryer = Retryer::new(); - self.response_channel = Some((tx, retryer.clone())); - retryer.send_with_retry(self.adaptation.clone(), command, chip_id); - self.set_state(UwbState::W4UciResp); - Ok(()) - } - - async fn handle_non_blocking_jni_cmd( - &mut self, - cmd: JNICommand, - chip_id: String, - ) -> Result<()> { - log::debug!("Received non blocking cmd {:?}", cmd); - match cmd { - JNICommand::Disable(_graceful) => match self.adaptation.hal_close(&chip_id).await { - Ok(()) => self.set_state(UwbState::W4HalClose), - Err(e) => { - error!("Recevied HAL close failure: {:?}", e); - self.set_state(UwbState::None); // TODO(b/239965873): state should be per-chip - return Err(UwbErr::Exit); - } - }, - _ => error!("Unexpected non blocking cmd received {:?}", cmd), - } - Ok(()) - } - - async fn handle_hal_notification( - &self, - response: uci_hrcv::UciNotification, - chip_id: String, - ) -> Result<()> { - log::debug!("Received hal notification {:?}", response); - match response { - uci_hrcv::UciNotification::DeviceStatusNtf(response) => { - self.event_manager.device_status_notification_received(response, &chip_id)?; - } - uci_hrcv::UciNotification::GenericError(response) => { - if let (StatusCode::UciStatusCommandRetry, Some((_, retryer))) = - (response.get_status(), self.response_channel.as_ref()) - { - retryer.retry(); - } - self.event_manager.core_generic_error_notification_received(response, &chip_id)?; - } - uci_hrcv::UciNotification::SessionStatusNtf(response) => { - self.invoke_hal_session_init_if_necessary(&response, chip_id).await; - self.event_manager.session_status_notification_received(response)?; - } - uci_hrcv::UciNotification::ShortMacTwoWayRangeDataNtf(response) => { - self.event_manager.short_range_data_notification_received(response)?; - } - uci_hrcv::UciNotification::ExtendedMacTwoWayRangeDataNtf(response) => { - self.event_manager.extended_range_data_notification_received(response)?; - } - uci_hrcv::UciNotification::SessionUpdateControllerMulticastListNtf(response) => { - self.event_manager - .session_update_controller_multicast_list_notification_received(response)?; - } - uci_hrcv::UciNotification::AndroidRangeDiagnosticsNtf(_response) => {} - uci_hrcv::UciNotification::RawVendorNtf(response) => { - self.event_manager.vendor_uci_notification_received(response, &chip_id)?; - } - } - Ok(()) - } - - // Handles a single message from JNI or the HAL. - async fn drive_once(&mut self) -> Result<()> { - select! { - Some(()) = option_future(self.response_channel.as_ref() - .map(|(_, retryer)| retryer.command_failed())) => { - // TODO: Do we want to flush the incoming queue of commands when this happens? - self.set_state(UwbState::W4HalOpen); - self.response_channel = None - } - // Note: If a blocking command is awaiting a response, any non-blocking commands are not - // dequeued until the blocking cmd's response is received. - Some((cmd, tx, chip_id)) = self.cmd_receiver.recv(), if self.can_process_cmd() => { - match tx { - Some(tx) => { // Blocking JNI commands processing. - self.handle_blocking_jni_cmd(tx, cmd, chip_id).await?; - }, - None => { // Non Blocking JNI commands processing. - self.handle_non_blocking_jni_cmd(cmd, chip_id).await?; - } - } - } - Some((rsp, chip_id)) = self.rsp_receiver.recv() => { - match rsp { - HalCallback::Event{event, event_status} => { - log::info!("Received HAL event: {:?} with status: {:?}", event, event_status); - match event { - UwbEvent::POST_INIT_CPLT => { - self.set_state(UwbState::Ready); - } - UwbEvent::CLOSE_CPLT => { - self.set_state(UwbState::None); - return Err(UwbErr::Exit); - } - UwbEvent::ERROR => { - // Send device status notification with error state. - let device_status_ntf = DeviceStatusNtfBuilder { device_state: DeviceState::DeviceStateError}.build(); - self.event_manager.device_status_notification_received(device_status_ntf, &chip_id)?; - self.set_state(UwbState::None); - } - _ => () - } - }, - HalCallback::UciRsp(response) => { - log::debug!("Received HAL UCI message {:?}", response); - self.set_state(UwbState::Ready); - if let Some((channel, retryer)) = self.response_channel.take() { - retryer.received(); - channel.send(response).unwrap_or_else(|_| { - error!("Unable to send response, receiver gone"); - }); - } else { - error!("Received response packet, but no response channel available"); - } - }, - HalCallback::UciNtf(response) => { - self.handle_hal_notification(response, chip_id).await?; - } - } - } - } - Ok(()) - } - - // Triggers the session init HAL API, if a new session is initialized. - async fn invoke_hal_session_init_if_necessary( - &self, - response: &SessionStatusNtfPacket, - chip_id: String, - ) { - if let SessionState::SessionStateInit = response.get_session_state() { - info!( - "Session {:?} initialized, invoking session init HAL API", - response.get_session_id() - ); - self.adaptation - // HAL API accepts signed int, so cast received session_id as i32. - .session_initialization(response.get_session_id() as i32, &chip_id) - .await - .unwrap_or_else(|e| error!("Error invoking session init HAL API : {:?}", e)); - } - } - - fn set_state(&mut self, state: UwbState) { - info!("UWB state change from {:?} to {:?}", self.state, state); - self.state = state; - } - - fn can_process_cmd(&mut self) -> bool { - self.state == UwbState::None || self.state == UwbState::Ready - } -} - -pub trait Dispatcher { - fn send_jni_command(&self, cmd: JNICommand, chip_id: String) -> Result<()>; - fn block_on_jni_command(&self, cmd: JNICommand, chip_id: String) -> Result<UciResponse>; - fn wait_for_exit(&mut self) -> Result<()>; - fn get_device_info(&self) -> &Option<GetDeviceInfoRspPacket>; - fn set_device_info(&mut self, device_info: Option<GetDeviceInfoRspPacket>); -} - -// Controller for sending tasks for the native thread to handle. -pub struct DispatcherImpl { - cmd_sender: mpsc::UnboundedSender<(JNICommand, Option<UciResponseHandle>, String)>, - join_handle: task::JoinHandle<Result<()>>, - runtime: Runtime, - device_info: Option<GetDeviceInfoRspPacket>, -} - -impl DispatcherImpl { - pub fn new<T: 'static + EventManager + Send + Sync>( - event_manager: T, - chip_ids: Vec<String>, - ) -> Result<Self> { - let (rsp_sender, rsp_receiver) = mpsc::unbounded_channel::<(HalCallback, String)>(); - // TODO when simplifying constructors, avoid spare runtime - let adaptation: SyncUwbAdaptation = Arc::new( - Builder::new_current_thread() - .build()? - .block_on(UwbAdaptationImpl::new(rsp_sender, &chip_ids))?, - ); - - Self::new_with_args(event_manager, adaptation, rsp_receiver) - } - - pub fn new_for_testing<T: 'static + EventManager + Send + Sync>( - event_manager: T, - adaptation: SyncUwbAdaptation, - rsp_receiver: mpsc::UnboundedReceiver<(HalCallback, String)>, - ) -> Result<Self> { - Self::new_with_args(event_manager, adaptation, rsp_receiver) - } - - fn new_with_args<T: 'static + EventManager + Send + Sync>( - event_manager: T, - adaptation: SyncUwbAdaptation, - rsp_receiver: mpsc::UnboundedReceiver<(HalCallback, String)>, - ) -> Result<Self> { - info!("initializing dispatcher"); - let (cmd_sender, cmd_receiver) = - mpsc::unbounded_channel::<(JNICommand, Option<UciResponseHandle>, String)>(); - - // We create a new thread here both to avoid reusing the Java service thread and because - // binder threads will call into this. - let runtime = Builder::new_multi_thread() - .worker_threads(1) - .thread_name("uwb-uci-handler") - .enable_all() - .build()?; - - let join_handle = - runtime.spawn(drive(adaptation, event_manager, cmd_receiver, rsp_receiver)); - Ok(DispatcherImpl { cmd_sender, join_handle, runtime, device_info: None }) - } -} - -impl Dispatcher for DispatcherImpl { - fn send_jni_command(&self, cmd: JNICommand, chip_id: String) -> Result<()> { - self.cmd_sender.send((cmd, None, chip_id))?; - Ok(()) - } - - // TODO: Consider implementing these separate for different commands so we can have more - // specific return types. - fn block_on_jni_command(&self, cmd: JNICommand, chip_id: String) -> Result<UciResponse> { - let (tx, rx) = oneshot::channel(); - self.cmd_sender.send((cmd, Some(tx), chip_id))?; - let ret = self.runtime.block_on(rx)?; - log::trace!("{:?}", ret); - Ok(ret) - } - - fn wait_for_exit(&mut self) -> Result<()> { - match self.runtime.block_on(&mut self.join_handle) { - Err(err) if err.is_panic() => { - error!("Driver thread is panic!"); - Err(UwbErr::Undefined) - } - _ => Ok(()), - } - } - - fn get_device_info(&self) -> &Option<GetDeviceInfoRspPacket> { - &self.device_info - } - fn set_device_info(&mut self, device_info: Option<GetDeviceInfoRspPacket>) { - self.device_info = device_info; - } -} - -#[cfg(test)] -pub mod mock_uci_logger; - -#[cfg(test)] -mod tests { - use self::uci_hrcv::UciNotification; - use self::uci_hrcv::UciResponse; - - use super::*; - use crate::adaptation::mock_adaptation::MockUwbAdaptation; - use crate::event_manager::mock_event_manager::MockEventManager; - use android_hardware_uwb::aidl::android::hardware::uwb::{ - UwbEvent::UwbEvent, UwbStatus::UwbStatus, - }; - use bytes::Bytes; - use num_traits::ToPrimitive; - use uwb_uci_packets::*; - - fn setup_dispatcher_and_return_hal_cb_sender( - config_fn: fn(&mut Arc<MockUwbAdaptation>, &mut MockEventManager), - ) -> (DispatcherImpl, mpsc::UnboundedSender<(HalCallback, String)>) { - // TODO: Remove this once we call it somewhere real. - logger::init( - logger::Config::default().with_tag_on_device("uwb").with_min_level(log::Level::Debug), - ); - - let (rsp_sender, rsp_receiver) = mpsc::unbounded_channel::<(HalCallback, String)>(); - let mut mock_adaptation = Arc::new(MockUwbAdaptation::new(rsp_sender.clone())); - let mut mock_event_manager = MockEventManager::new(); - - config_fn(&mut mock_adaptation, &mut mock_event_manager); - let dispatcher = DispatcherImpl::new_for_testing( - mock_event_manager, - mock_adaptation as SyncUwbAdaptation, - rsp_receiver, - ) - .unwrap(); - (dispatcher, rsp_sender) - } - - fn generate_fake_get_device_cmd_rsp() -> (GetDeviceInfoCmdPacket, GetDeviceInfoRspPacket) { - let cmd = GetDeviceInfoCmdBuilder {}.build(); - let rsp = GetDeviceInfoRspBuilder { - status: StatusCode::UciStatusOk, - uci_version: 0, - mac_version: 0, - phy_version: 0, - uci_test_version: 0, - vendor_spec_info: vec![], - } - .build(); - (cmd, rsp) - } - - fn generate_fake_device_status_ntf() -> DeviceStatusNtfPacket { - DeviceStatusNtfBuilder { device_state: DeviceState::DeviceStateReady }.build() - } - - #[test] - fn test_initialize() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - mock_adaptation.expect_hal_open(Ok(())); - mock_adaptation.expect_core_initialization(Ok(())); - }); - - dispatcher.block_on_jni_command(JNICommand::Enable, String::from("chip_id")).unwrap(); - } - - #[test] - fn test_hal_error_event() { - let (mut dispatcher, hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, mock_event_manager| { - mock_adaptation.expect_hal_open(Ok(())); - mock_adaptation.expect_core_initialization(Ok(())); - mock_event_manager.expect_device_status_notification_received(Ok(())); - }); - let chip_id = String::from("chip_id"); - - dispatcher.block_on_jni_command(JNICommand::Enable, chip_id.clone()).unwrap(); - hal_sender - .send(( - HalCallback::Event { event: UwbEvent::ERROR, event_status: UwbStatus::FAILED }, - chip_id.clone(), - )) - .unwrap(); - hal_sender - .send(( - HalCallback::Event { event: UwbEvent::CLOSE_CPLT, event_status: UwbStatus::OK }, - chip_id, - )) - .unwrap(); - dispatcher.wait_for_exit().unwrap(); - } - - #[test] - fn test_deinitialize() { - let (mut dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - mock_adaptation.expect_hal_close(Ok(())); - }); - let chip_id = String::from("chip_id"); - - dispatcher.send_jni_command(JNICommand::Disable(true), chip_id).unwrap(); - dispatcher.wait_for_exit().unwrap(); - } - - #[test] - fn test_get_device_info() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let (cmd, rsp) = generate_fake_get_device_cmd_rsp(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::GetDeviceInfoRsp(rsp)), - None, - Ok(()), - ); - }); - - dispatcher - .block_on_jni_command(JNICommand::UciGetDeviceInfo, String::from("chip_id")) - .unwrap(); - } - - #[test] - fn test_get_device_info_with_uci_retry() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let (cmd, rsp) = generate_fake_get_device_cmd_rsp(); - - // Let the first 2 tries not response data, then the 3rd tries response successfully. - mock_adaptation.expect_send_uci_message(cmd.clone().into(), None, None, Ok(())); - mock_adaptation.expect_send_uci_message(cmd.clone().into(), None, None, Ok(())); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::GetDeviceInfoRsp(rsp)), - None, - Ok(()), - ); - }); - - dispatcher - .block_on_jni_command(JNICommand::UciGetDeviceInfo, String::from("chip_id")) - .unwrap(); - } - - #[test] - fn test_get_device_info_send_uci_message_failed() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let (cmd, _rsp) = generate_fake_get_device_cmd_rsp(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - None, - None, - Err(UwbErr::failed()), - ); - }); - - dispatcher - .block_on_jni_command(JNICommand::UciGetDeviceInfo, String::from("chip_id")) - .expect_err("This method should fail."); - } - - #[test] - fn test_device_status_notification() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, mock_event_manager| { - let (cmd, rsp) = generate_fake_get_device_cmd_rsp(); - let ntf = generate_fake_device_status_ntf(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::GetDeviceInfoRsp(rsp)), - Some(UciNotification::DeviceStatusNtf(ntf)), - Ok(()), - ); - mock_event_manager.expect_device_status_notification_received(Ok(())); - }); - - dispatcher - .block_on_jni_command(JNICommand::UciGetDeviceInfo, String::from("chip_id")) - .unwrap(); - } - - #[test] - fn test_get_caps_info() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let cmd = GetCapsInfoCmdBuilder {}.build(); - let rsp = - GetCapsInfoRspBuilder { status: StatusCode::UciStatusOk, tlvs: Vec::new() } - .build(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::GetCapsInfoRsp(rsp)), - None, - Ok(()), - ); - }); - - dispatcher - .block_on_jni_command(JNICommand::UciGetCapsInfo, String::from("chip_id")) - .unwrap(); - } - - #[test] - fn test_session_init() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let cmd = SessionInitCmdBuilder { - session_id: 1, - session_type: SessionType::FiraRangingSession, - } - .build(); - let rsp = SessionInitRspBuilder { status: StatusCode::UciStatusOk }.build(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::SessionInitRsp(rsp)), - None, - Ok(()), - ); - }); - - dispatcher - .block_on_jni_command( - JNICommand::UciSessionInit(1, SessionType::FiraRangingSession.to_u8().unwrap()), - String::from("chip_id"), - ) - .unwrap(); - } - - #[test] - fn test_session_deinit() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let cmd = SessionDeinitCmdBuilder { session_id: 1 }.build(); - let rsp = SessionDeinitRspBuilder { status: StatusCode::UciStatusOk }.build(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::SessionDeinitRsp(rsp)), - None, - Ok(()), - ); - }); - - dispatcher - .block_on_jni_command(JNICommand::UciSessionDeinit(1), String::from("chip_id")) - .unwrap(); - } - - #[test] - fn test_session_get_count() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let cmd = SessionGetCountCmdBuilder {}.build(); - let rsp = - SessionGetCountRspBuilder { status: StatusCode::UciStatusOk, session_count: 5 } - .build(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::SessionGetCountRsp(rsp)), - None, - Ok(()), - ); - }); - - dispatcher - .block_on_jni_command(JNICommand::UciSessionGetCount, String::from("chip_id")) - .unwrap(); - } - - #[test] - fn test_start_range() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let cmd = RangeStartCmdBuilder { session_id: 5 }.build(); - let rsp = RangeStartRspBuilder { status: StatusCode::UciStatusOk }.build(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::RangeStartRsp(rsp)), - None, - Ok(()), - ); - }); - - dispatcher - .block_on_jni_command(JNICommand::UciStartRange(5), String::from("chip_id")) - .unwrap(); - } - - #[test] - fn test_stop_range() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let cmd = RangeStopCmdBuilder { session_id: 5 }.build(); - let rsp = RangeStopRspBuilder { status: StatusCode::UciStatusOk }.build(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::RangeStopRsp(rsp)), - None, - Ok(()), - ); - }); - - dispatcher - .block_on_jni_command(JNICommand::UciStopRange(5), String::from("chip_id")) - .unwrap(); - } - - #[test] - fn test_get_session_state() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let cmd = SessionGetStateCmdBuilder { session_id: 5 }.build(); - let rsp = SessionGetStateRspBuilder { - status: StatusCode::UciStatusOk, - session_state: SessionState::SessionStateActive, - } - .build(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::SessionGetStateRsp(rsp)), - None, - Ok(()), - ); - }); - - dispatcher - .block_on_jni_command(JNICommand::UciGetSessionState(5), String::from("chip_id")) - .unwrap(); - } - - #[test] - fn test_session_update_multicast_list() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let cmd = SessionUpdateControllerMulticastListCmdBuilder { - session_id: 5, - action: UpdateMulticastListAction::AddControlee, - payload: Some(Bytes::from(vec![0])), - } - .build(); - let rsp = SessionUpdateControllerMulticastListRspBuilder { - status: StatusCode::UciStatusOk, - } - .build(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::SessionUpdateControllerMulticastListRsp(rsp)), - None, - Ok(()), - ); - }); - - dispatcher - .block_on_jni_command( - JNICommand::UciSessionUpdateMulticastList { - session_id: 5, - action: UpdateMulticastListAction::AddControlee.to_u8().unwrap(), - no_of_controlee: 0, - address_list: Vec::new(), - sub_session_id_list: Vec::new(), - message_control: 8, - sub_session_key_list: Vec::new(), - }, - String::from("chip_id"), - ) - .unwrap(); - } - - #[test] - fn test_set_country_code() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let cmd = AndroidSetCountryCodeCmdBuilder { country_code: [45, 34] }.build(); - let rsp = - AndroidSetCountryCodeRspBuilder { status: StatusCode::UciStatusOk }.build(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::AndroidSetCountryCodeRsp(rsp)), - None, - Ok(()), - ); - }); - - dispatcher - .block_on_jni_command( - JNICommand::UciSetCountryCode { code: vec![45, 34] }, - String::from("chip_id"), - ) - .unwrap(); - } - - #[test] - fn test_set_app_config() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let cmd = SessionSetAppConfigCmdBuilder { session_id: 5, tlvs: Vec::new() }.build(); - let rsp = SessionSetAppConfigRspBuilder { - status: StatusCode::UciStatusOk, - cfg_status: Vec::new(), - } - .build(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::SessionSetAppConfigRsp(rsp)), - None, - Ok(()), - ); - }); - - dispatcher - .block_on_jni_command( - JNICommand::UciSetAppConfig { - session_id: 5, - no_of_params: 0, - app_config_param_len: 0, - app_configs: Vec::new(), - }, - String::from("chip_id"), - ) - .unwrap(); - } - - #[test] - fn test_get_app_config() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let cmd = - SessionGetAppConfigCmdBuilder { session_id: 5, app_cfg: Vec::new() }.build(); - let rsp = SessionGetAppConfigRspBuilder { - status: StatusCode::UciStatusOk, - tlvs: Vec::new(), - } - .build(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::SessionGetAppConfigRsp(rsp)), - None, - Ok(()), - ); - }); - - dispatcher - .block_on_jni_command( - JNICommand::UciGetAppConfig { - session_id: 5, - no_of_params: 0, - app_config_param_len: 0, - app_configs: Vec::new(), - }, - String::from("chip_id"), - ) - .unwrap(); - } - - #[test] - fn test_raw_vendor_cmd() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let cmd = UciVendor_9_CommandBuilder { opcode: 5, payload: None }.build(); - let rsp = UciVendor_9_ResponseBuilder { opcode: 5, payload: None }.build(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::RawVendorRsp(rsp.into())), - None, - Ok(()), - ); - }); - - dispatcher - .block_on_jni_command( - JNICommand::UciRawVendorCmd { gid: 9, oid: 5, payload: Vec::new() }, - String::from("chip_id"), - ) - .unwrap(); - } - - #[test] - fn test_device_reset() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let cmd = DeviceResetCmdBuilder { reset_config: ResetConfig::UwbsReset }.build(); - let rsp = DeviceResetRspBuilder { status: StatusCode::UciStatusOk }.build(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::DeviceResetRsp(rsp)), - None, - Ok(()), - ); - }); - - dispatcher - .block_on_jni_command( - JNICommand::UciDeviceReset { reset_config: 0 }, - String::from("chip_id"), - ) - .unwrap(); - } - - #[test] - fn test_get_power_stats() { - let (dispatcher, _hal_sender) = - setup_dispatcher_and_return_hal_cb_sender(|mock_adaptation, _mock_event_manager| { - let cmd = AndroidGetPowerStatsCmdBuilder {}.build(); - let rsp = AndroidGetPowerStatsRspBuilder { - stats: PowerStats { - status: StatusCode::UciStatusOk, - idle_time_ms: 0, - tx_time_ms: 0, - rx_time_ms: 0, - total_wake_count: 0, - }, - } - .build(); - mock_adaptation.expect_send_uci_message( - cmd.into(), - Some(UciResponse::AndroidGetPowerStatsRsp(rsp)), - None, - Ok(()), - ); - }); - - dispatcher - .block_on_jni_command(JNICommand::UciGetPowerStats, String::from("chip_id")) - .unwrap(); - } -} diff --git a/src/rust/uci/uci_hmsgs.rs b/src/rust/uci/uci_hmsgs.rs deleted file mode 100644 index 3f6e2ab..0000000 --- a/src/rust/uci/uci_hmsgs.rs +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use crate::uci::UwbErr; -use bytes::{BufMut, Bytes, BytesMut}; -use log::error; -use num_traits::FromPrimitive; -use uwb_uci_packets::{ - write_controlee, write_controlee_2_0_0byte, write_controlee_2_0_16byte, - write_controlee_2_0_32byte, AndroidSetCountryCodeCmdBuilder, AppConfigTlv, Controlee, - Controlee_V2_0_0_Byte_Version, Controlee_V2_0_16_Byte_Version, Controlee_V2_0_32_Byte_Version, - DeviceResetCmdBuilder, GroupId, MessageControl, ResetConfig, SessionInitCmdBuilder, - SessionSetAppConfigCmdBuilder, SessionType, SessionUpdateControllerMulticastListCmdBuilder, - UciCommandPacket, UciVendor_9_CommandBuilder, UciVendor_A_CommandBuilder, - UciVendor_B_CommandBuilder, UciVendor_E_CommandBuilder, UciVendor_F_CommandBuilder, - UpdateMulticastListAction, -}; - -pub fn build_session_init_cmd( - session_id: u32, - session_type: u8, -) -> Result<SessionInitCmdBuilder, UwbErr> { - Ok(SessionInitCmdBuilder { - session_id, - session_type: SessionType::from_u8(session_type).ok_or(UwbErr::InvalidArgs)?, - }) -} - -pub fn build_set_country_code_cmd(code: &[u8]) -> Result<AndroidSetCountryCodeCmdBuilder, UwbErr> { - Ok(AndroidSetCountryCodeCmdBuilder { country_code: code.try_into()? }) -} - -pub fn build_multicast_list_update_cmd( - session_id: u32, - action: u8, - no_of_controlee: u8, - address_list: &[i16], - sub_session_id_list: &[i32], - message_control: Option<MessageControl>, - sub_session_key_list: &[Vec<u8>], -) -> Result<SessionUpdateControllerMulticastListCmdBuilder, UwbErr> { - if usize::from(no_of_controlee) != address_list.len() - || usize::from(no_of_controlee) != sub_session_id_list.len() - { - return Err(UwbErr::InvalidArgs); - } - let mut controlees_buf = BytesMut::new(); - controlees_buf.put_u8(no_of_controlee); - match message_control { - None => { - for i in 0..no_of_controlee { - controlees_buf.extend_from_slice(&write_controlee(&Controlee { - short_address: address_list[i as usize] as u16, - subsession_id: sub_session_id_list[i as usize] as u32, - })); - } - } - Some(MessageControl::SubSessionKeyNotConfigured) => { - for i in 0..no_of_controlee { - controlees_buf.extend_from_slice(&write_controlee_2_0_0byte( - &Controlee_V2_0_0_Byte_Version { - short_address: address_list[i as usize] as u16, - subsession_id: sub_session_id_list[i as usize] as u32, - message_control: MessageControl::SubSessionKeyNotConfigured, - }, - )); - } - } - Some(MessageControl::ShortSubSessionKeyConfigured) => { - for i in 0..no_of_controlee { - controlees_buf.extend_from_slice(&write_controlee_2_0_16byte( - &Controlee_V2_0_16_Byte_Version { - short_address: address_list[i as usize] as u16, - subsession_id: sub_session_id_list[i as usize] as u32, - message_control: MessageControl::ShortSubSessionKeyConfigured, - subsession_key: sub_session_key_list[i as usize] - .clone() - .try_into() - .map_err(|_| UwbErr::InvalidArgs)?, - }, - )); - } - } - Some(MessageControl::LongSubSessionKeyConfigured) => { - for i in 0..no_of_controlee { - controlees_buf.extend_from_slice(&write_controlee_2_0_32byte( - &Controlee_V2_0_32_Byte_Version { - short_address: address_list[i as usize] as u16, - subsession_id: sub_session_id_list[i as usize] as u32, - message_control: MessageControl::LongSubSessionKeyConfigured, - subsession_key: sub_session_key_list[i as usize] - .clone() - .try_into() - .map_err(|_| UwbErr::InvalidArgs)?, - }, - )); - } - } - } - Ok(SessionUpdateControllerMulticastListCmdBuilder { - session_id, - action: UpdateMulticastListAction::from_u8(action).ok_or(UwbErr::InvalidArgs)?, - payload: Some(controlees_buf.freeze()), - }) -} - -pub fn build_set_app_config_cmd( - session_id: u32, - no_of_params: u32, - mut app_configs: &[u8], -) -> Result<SessionSetAppConfigCmdBuilder, UwbErr> { - let mut tlvs = Vec::new(); - let received_tlvs_len = app_configs.len(); - let mut parsed_tlvs_len = 0; - for _ in 0..no_of_params { - let tlv = AppConfigTlv::parse(app_configs)?; - app_configs = app_configs.get(tlv.v.len() + 2..).ok_or(UwbErr::InvalidArgs)?; - parsed_tlvs_len += tlv.v.len() + 2; - tlvs.push(tlv); - } - if parsed_tlvs_len != received_tlvs_len { - error!("Parsed TLV len: {:?}, received len: {:?}", parsed_tlvs_len, received_tlvs_len); - return Err(UwbErr::InvalidArgs); - } - Ok(SessionSetAppConfigCmdBuilder { session_id, tlvs }) -} - -pub fn build_uci_vendor_cmd_packet( - gid: u32, - oid: u32, - payload: Vec<u8>, -) -> Result<UciCommandPacket, UwbErr> { - use GroupId::*; - let group_id: GroupId = GroupId::from_u32(gid).ok_or(UwbErr::InvalidArgs)?; - let payload = if payload.is_empty() { None } else { Some(Bytes::from(payload)) }; - let opcode: u8 = oid.try_into()?; - let packet: UciCommandPacket = match group_id { - VendorReserved9 => UciVendor_9_CommandBuilder { opcode, payload }.build().into(), - VendorReservedA => UciVendor_A_CommandBuilder { opcode, payload }.build().into(), - VendorReservedB => UciVendor_B_CommandBuilder { opcode, payload }.build().into(), - VendorReservedE => UciVendor_E_CommandBuilder { opcode, payload }.build().into(), - VendorReservedF => UciVendor_F_CommandBuilder { opcode, payload }.build().into(), - _ => { - error!("Invalid vendor gid {:?}", gid); - return Err(UwbErr::InvalidArgs); - } - }; - Ok(packet) -} - -pub fn build_device_reset_cmd(reset_config: u8) -> Result<DeviceResetCmdBuilder, UwbErr> { - Ok(DeviceResetCmdBuilder { - reset_config: ResetConfig::from_u8(reset_config).ok_or(UwbErr::InvalidArgs)?, - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use num_traits::ToPrimitive; - use uwb_uci_packets::*; - - #[test] - fn test_build_uci_vendor_cmd_packet() { - let oid: u8 = 6; - let gid = GroupId::VendorReserved9; - let payload = vec![0x5, 0x5, 0x5, 0x5]; - assert_eq!( - build_uci_vendor_cmd_packet(gid.to_u32().unwrap(), oid.into(), payload.clone()) - .unwrap() - .to_bytes(), - UciVendor_9_CommandBuilder { opcode: oid, payload: Some(Bytes::from(payload)) } - .build() - .to_bytes() - ); - } -} diff --git a/src/rust/uci/uci_hrcv.rs b/src/rust/uci/uci_hrcv.rs deleted file mode 100644 index c79b7c6..0000000 --- a/src/rust/uci/uci_hrcv.rs +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use crate::error::UwbErr; -use uwb_uci_packets::*; - -#[derive(Debug)] -pub enum UciMessage { - Response(UciResponse), - Notification(UciNotification), -} - -#[derive(Debug, Clone)] -pub enum UciResponse { - GetDeviceInfoRsp(GetDeviceInfoRspPacket), - GetCapsInfoRsp(GetCapsInfoRspPacket), - SetConfigRsp(SetConfigRspPacket), - GetConfigRsp(GetConfigRspPacket), - DeviceResetRsp(DeviceResetRspPacket), - SessionInitRsp(SessionInitRspPacket), - SessionDeinitRsp(SessionDeinitRspPacket), - SessionGetAppConfigRsp(SessionGetAppConfigRspPacket), - SessionSetAppConfigRsp(SessionSetAppConfigRspPacket), - SessionGetStateRsp(SessionGetStateRspPacket), - SessionGetCountRsp(SessionGetCountRspPacket), - SessionUpdateControllerMulticastListRsp(SessionUpdateControllerMulticastListRspPacket), - RangeStartRsp(RangeStartRspPacket), - RangeStopRsp(RangeStopRspPacket), - RangeGetRangingCountRsp(RangeGetRangingCountRspPacket), - AndroidSetCountryCodeRsp(AndroidSetCountryCodeRspPacket), - AndroidGetPowerStatsRsp(AndroidGetPowerStatsRspPacket), - RawVendorRsp(UciResponsePacket), - EnableRsp(bool), - DisableRsp, -} - -#[derive(Debug, Clone)] -pub enum UciNotification { - GenericError(GenericErrorPacket), - DeviceStatusNtf(DeviceStatusNtfPacket), - SessionStatusNtf(SessionStatusNtfPacket), - SessionUpdateControllerMulticastListNtf(SessionUpdateControllerMulticastListNtfPacket), - ShortMacTwoWayRangeDataNtf(ShortMacTwoWayRangeDataNtfPacket), - ExtendedMacTwoWayRangeDataNtf(ExtendedMacTwoWayRangeDataNtfPacket), - AndroidRangeDiagnosticsNtf(ParsedDiagnosticNtfPacket), - RawVendorNtf(UciNotificationPacket), -} - -pub fn uci_message(evt: UciPacketPacket) -> Result<UciMessage, UwbErr> { - match evt.specialize() { - UciPacketChild::UciResponse(evt) => Ok(UciMessage::Response(uci_response(evt).unwrap())), - UciPacketChild::UciNotification(evt) => { - Ok(UciMessage::Notification(uci_notification(evt).unwrap())) - } - _ => Err(UwbErr::Specialize(evt.to_vec())), - } -} - -pub fn uci_response(evt: UciResponsePacket) -> Result<UciResponse, UwbErr> { - match evt.specialize() { - UciResponseChild::CoreResponse(evt) => core_response(evt), - UciResponseChild::SessionResponse(evt) => session_response(evt), - UciResponseChild::RangingResponse(evt) => ranging_response(evt), - UciResponseChild::AndroidResponse(evt) => android_response(evt), - UciResponseChild::UciVendor_9_Response(evt) => vendor_response(evt.into()), - UciResponseChild::UciVendor_A_Response(evt) => vendor_response(evt.into()), - UciResponseChild::UciVendor_B_Response(evt) => vendor_response(evt.into()), - UciResponseChild::UciVendor_E_Response(evt) => vendor_response(evt.into()), - UciResponseChild::UciVendor_F_Response(evt) => vendor_response(evt.into()), - _ => Err(UwbErr::Specialize(evt.to_vec())), - } -} - -pub fn uci_notification(evt: UciNotificationPacket) -> Result<UciNotification, UwbErr> { - match evt.specialize() { - UciNotificationChild::CoreNotification(evt) => core_notification(evt), - UciNotificationChild::SessionNotification(evt) => session_notification(evt), - UciNotificationChild::RangingNotification(evt) => ranging_notification(evt), - UciNotificationChild::AndroidNotification(evt) => android_notification(evt), - UciNotificationChild::UciVendor_9_Notification(evt) => vendor_notification(evt.into()), - UciNotificationChild::UciVendor_A_Notification(evt) => vendor_notification(evt.into()), - UciNotificationChild::UciVendor_B_Notification(evt) => vendor_notification(evt.into()), - UciNotificationChild::UciVendor_E_Notification(evt) => vendor_notification(evt.into()), - UciNotificationChild::UciVendor_F_Notification(evt) => vendor_notification(evt.into()), - _ => Err(UwbErr::Specialize(evt.to_vec())), - } -} - -fn core_response(evt: CoreResponsePacket) -> Result<UciResponse, UwbErr> { - match evt.specialize() { - CoreResponseChild::GetDeviceInfoRsp(evt) => Ok(UciResponse::GetDeviceInfoRsp(evt)), - CoreResponseChild::GetCapsInfoRsp(evt) => Ok(UciResponse::GetCapsInfoRsp(evt)), - CoreResponseChild::SetConfigRsp(evt) => Ok(UciResponse::SetConfigRsp(evt)), - CoreResponseChild::GetConfigRsp(evt) => Ok(UciResponse::GetConfigRsp(evt)), - CoreResponseChild::DeviceResetRsp(evt) => Ok(UciResponse::DeviceResetRsp(evt)), - _ => Err(UwbErr::Specialize(evt.to_vec())), - } -} - -fn session_response(evt: SessionResponsePacket) -> Result<UciResponse, UwbErr> { - match evt.specialize() { - SessionResponseChild::SessionInitRsp(evt) => Ok(UciResponse::SessionInitRsp(evt)), - SessionResponseChild::SessionDeinitRsp(evt) => Ok(UciResponse::SessionDeinitRsp(evt)), - SessionResponseChild::SessionSetAppConfigRsp(evt) => { - Ok(UciResponse::SessionSetAppConfigRsp(evt)) - } - SessionResponseChild::SessionGetAppConfigRsp(evt) => { - Ok(UciResponse::SessionGetAppConfigRsp(evt)) - } - SessionResponseChild::SessionGetStateRsp(evt) => Ok(UciResponse::SessionGetStateRsp(evt)), - SessionResponseChild::SessionGetCountRsp(evt) => Ok(UciResponse::SessionGetCountRsp(evt)), - SessionResponseChild::SessionUpdateControllerMulticastListRsp(evt) => { - Ok(UciResponse::SessionUpdateControllerMulticastListRsp(evt)) - } - _ => Err(UwbErr::Specialize(evt.to_vec())), - } -} - -fn ranging_response(evt: RangingResponsePacket) -> Result<UciResponse, UwbErr> { - match evt.specialize() { - RangingResponseChild::RangeStartRsp(evt) => Ok(UciResponse::RangeStartRsp(evt)), - RangingResponseChild::RangeStopRsp(evt) => Ok(UciResponse::RangeStopRsp(evt)), - RangingResponseChild::RangeGetRangingCountRsp(evt) => { - Ok(UciResponse::RangeGetRangingCountRsp(evt)) - } - _ => Err(UwbErr::Specialize(evt.to_vec())), - } -} - -fn android_response(evt: AndroidResponsePacket) -> Result<UciResponse, UwbErr> { - match evt.specialize() { - AndroidResponseChild::AndroidSetCountryCodeRsp(evt) => { - Ok(UciResponse::AndroidSetCountryCodeRsp(evt)) - } - AndroidResponseChild::AndroidGetPowerStatsRsp(evt) => { - Ok(UciResponse::AndroidGetPowerStatsRsp(evt)) - } - _ => Err(UwbErr::Specialize(evt.to_vec())), - } -} - -fn vendor_response(evt: UciResponsePacket) -> Result<UciResponse, UwbErr> { - Ok(UciResponse::RawVendorRsp(evt)) -} - -fn core_notification(evt: CoreNotificationPacket) -> Result<UciNotification, UwbErr> { - match evt.specialize() { - CoreNotificationChild::DeviceStatusNtf(evt) => Ok(UciNotification::DeviceStatusNtf(evt)), - CoreNotificationChild::GenericError(evt) => Ok(UciNotification::GenericError(evt)), - _ => Err(UwbErr::Specialize(evt.to_vec())), - } -} - -fn session_notification(evt: SessionNotificationPacket) -> Result<UciNotification, UwbErr> { - match evt.specialize() { - SessionNotificationChild::SessionStatusNtf(evt) => { - Ok(UciNotification::SessionStatusNtf(evt)) - } - SessionNotificationChild::SessionUpdateControllerMulticastListNtf(evt) => { - Ok(UciNotification::SessionUpdateControllerMulticastListNtf(evt)) - } - _ => Err(UwbErr::Specialize(evt.to_vec())), - } -} - -fn ranging_notification(evt: RangingNotificationPacket) -> Result<UciNotification, UwbErr> { - match evt.specialize() { - RangingNotificationChild::RangeDataNtf(evt) => range_data_notification(evt), - _ => Err(UwbErr::Specialize(evt.to_vec())), - } -} - -fn range_data_notification(evt: RangeDataNtfPacket) -> Result<UciNotification, UwbErr> { - match evt.specialize() { - RangeDataNtfChild::ShortMacTwoWayRangeDataNtf(evt) => { - Ok(UciNotification::ShortMacTwoWayRangeDataNtf(evt)) - } - RangeDataNtfChild::ExtendedMacTwoWayRangeDataNtf(evt) => { - Ok(UciNotification::ExtendedMacTwoWayRangeDataNtf(evt)) - } - _ => Err(UwbErr::Specialize(evt.to_vec())), - } -} - -fn android_notification(evt: AndroidNotificationPacket) -> Result<UciNotification, UwbErr> { - match evt.specialize() { - AndroidNotificationChild::AndroidRangeDiagnosticsNtf(evt) => { - Ok(UciNotification::AndroidRangeDiagnosticsNtf(parse_diagnostics_ntf(evt)?)) - } - _ => Err(UwbErr::Specialize(evt.to_vec())), - } -} - -fn vendor_notification(evt: UciNotificationPacket) -> Result<UciNotification, UwbErr> { - Ok(UciNotification::RawVendorNtf(evt)) -} diff --git a/src/rust/uci/uci_logger.rs b/src/rust/uci/uci_logger.rs deleted file mode 100644 index dce8789..0000000 --- a/src/rust/uci/uci_logger.rs +++ /dev/null @@ -1,693 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -extern crate libc; - -#[cfg(test)] -use crate::uci::mock_uci_logger::{create_dir, remove_file, rename, Instant}; -use crate::uci::UwbErr; -use async_trait::async_trait; -use log::{error, info}; -use std::marker::Unpin; -use std::sync::Arc; -#[cfg(not(test))] -use std::time::Instant; -use std::time::SystemTime; -use tokio::fs::OpenOptions; -#[cfg(not(test))] -use tokio::fs::{create_dir, remove_file, rename}; -use tokio::io::{AsyncWrite, AsyncWriteExt}; -use tokio::sync::Mutex; -use tokio::{task, time}; -use uwb_uci_packets::{ - AppConfigTlv, AppConfigTlvType, Packet, SessionCommandChild, SessionGetAppConfigRspBuilder, - SessionResponseChild, SessionSetAppConfigCmdBuilder, UciCommandChild, UciCommandPacket, - UciNotificationPacket, UciPacketPacket, UciResponseChild, UciResponsePacket, -}; - -// micros since 0000-01-01 -const UCI_LOG_LAST_FILE_STORE_TIME_SEC: u64 = 86400; // 24 hours -const MAX_FILE_SIZE: usize = 102400; // 100 KB -const MAX_BUFFER_SIZE: usize = 10240; // 10 KB -const VENDOR_ID: u64 = AppConfigTlvType::VendorId as u64; -const STATIC_STS_IV: u64 = AppConfigTlvType::StaticStsIv as u64; -const LOG_DIR: &str = "/data/misc/apexdata/com.android.uwb/log"; -const FILE_NAME: &str = "uwb_uci.pcapng"; - -type SyncFile = Arc<Mutex<dyn AsyncWrite + Send + Sync + Unpin>>; -type SyncFactory = Arc<Mutex<dyn FileFactory + Send + Sync>>; - -#[derive(Clone, PartialEq, Eq)] -pub enum UciLogMode { - Disabled, - Filtered, - Enabled, -} - -#[derive(Clone)] -pub struct UciLogConfig { - path: String, - mode: UciLogMode, -} - -impl UciLogConfig { - pub fn new(mode: UciLogMode) -> Self { - Self { path: format!("{}/{}", LOG_DIR, FILE_NAME), mode } - } -} - -#[async_trait] -pub trait UciLogger { - async fn log_uci_command(&self, cmd: UciCommandPacket); - async fn log_uci_response(&self, rsp: UciResponsePacket); - async fn log_uci_notification(&self, ntf: UciNotificationPacket); - async fn close_file(&self); -} - -struct BufferedFile { - file: Option<SyncFile>, - written_size: usize, - buffer: Vec<u8>, - deleter_handle: Option<task::JoinHandle<()>>, -} - -impl BufferedFile { - async fn open_next_file(&mut self, factory: SyncFactory, path: &str) -> Result<(), UwbErr> { - info!("Open next file"); - self.close_file().await; - if create_dir(LOG_DIR).await.is_err() { - error!("Failed to create dir"); - } - if rename(path, path.to_owned() + ".last").await.is_err() { - error!("Failed to rename the file"); - } - if let Some(deleter_handle) = self.deleter_handle.take() { - deleter_handle.abort(); - } - let last_file_path = path.to_owned() + ".last"; - self.deleter_handle = Some(task::spawn(async { - time::sleep(time::Duration::from_secs(UCI_LOG_LAST_FILE_STORE_TIME_SEC)).await; - if remove_file(last_file_path).await.is_err() { - error!("Failed to remove file!"); - }; - })); - let file = factory.lock().await.create_file_using_open_options(path).await?; - self.file = Some(file); - let header = get_pcapng_header(); - self.buffered_write(header).await; - Ok(()) - } - - fn file_size(&self) -> usize { - self.written_size + self.buffer.len() - } - - async fn buffered_write(&mut self, mut data: Vec<u8>) { - if self.buffer.len() + data.len() >= MAX_BUFFER_SIZE { - self.flush_buffer().await; - } - self.buffer.append(&mut data); - } - - async fn close_file(&mut self) { - if self.file.is_some() { - info!("UCI log file closing"); - self.flush_buffer().await; - self.file = None; - } - self.written_size = 0; - } - - async fn flush_buffer(&mut self) { - let mut locked_file = match &self.file { - Some(file) => file.lock().await, - None => { - return; - } - }; - if locked_file.write_all(&self.buffer).await.is_err() { - error!("Failed to write"); - return; - } - if let Err(e) = locked_file.flush().await { - error!("Failed to flush: {:?}", e); - return; - } - self.written_size += self.buffer.len(); - self.buffer.clear(); - } -} - -pub struct UciLoggerImpl { - config: UciLogConfig, - buf_file: Mutex<BufferedFile>, - file_factory: SyncFactory, - start_time: Instant, -} - -impl UciLoggerImpl { - pub async fn new(mode: UciLogMode, file_factory: SyncFactory) -> Self { - let config = UciLogConfig::new(mode); - let mut factory = file_factory.lock().await; - factory.set_config(config.clone()).await; - let (file, size) = factory.new_file().await; - let buf_file = BufferedFile { - written_size: size, - file, - buffer: Vec::with_capacity(MAX_BUFFER_SIZE), - deleter_handle: None, - }; - let ret = Self { - config, - buf_file: Mutex::new(buf_file), - file_factory: file_factory.clone(), - start_time: Instant::now(), - }; - info!("UCI logger created"); - ret - } - - async fn log_uci_packet(&self, packet: UciPacketPacket) { - const HEADER_SIZE: usize = 48; - let bytes = packet.to_vec(); - let timestamp = self.start_time.elapsed().as_micros() as u64; - let enhanced_block = EnhancedBlockBuilder::new() - .interface_id(0) - .timestamp(timestamp) - .packet(bytes) - .max_block_size(MAX_FILE_SIZE - HEADER_SIZE) - .build(); - // Checks whether the enhanced_block fits inside the file: - let mut buf_file = self.buf_file.lock().await; - if buf_file.file_size() + enhanced_block.len() > MAX_FILE_SIZE { - match buf_file.open_next_file(self.file_factory.clone(), &self.config.path).await { - Ok(()) => info!("Created new pcagng log file"), - Err(e) => { - error!("Failed to open new pcapng log file: {:?}", e); - return; - } - } - } - buf_file.buffered_write(enhanced_block).await; - } -} - -/// Constructs Enhanced Packet Block from raw packet and additional fields. -struct EnhancedBlockBuilder { - interface_id: u32, - timestamp: u64, - packet: Vec<u8>, - max_block_size: Option<usize>, -} -impl EnhancedBlockBuilder { - /// Constructor. - fn new() -> Self { - Self { interface_id: 0, timestamp: 0, packet: vec![], max_block_size: None } - } - - /// Sets interface_id. - fn interface_id(mut self, interface_id: u32) -> Self { - self.interface_id = interface_id; - self - } - - /// Sets timestamp. - fn timestamp(mut self, timestamp: u64) -> Self { - self.timestamp = timestamp; - self - } - - /// Sets packet. - fn packet(mut self, packet: Vec<u8>) -> Self { - self.packet = packet; - self - } - - /// Sets maximum block size permitted (optional). Truncated down to nearest multiple of 4. - fn max_block_size(mut self, max_size: usize) -> Self { - self.max_block_size = Some(max_size / 4 * 4); - self - } - - /// Builds the packet. - fn build(mut self) -> Vec<u8> { - const ENHANCED_BLOCK_SIZE: usize = 32; - let packet_length = self.packet.len() as u32; - let padded_data_length = (self.packet.len() + 3) / 4 * 4; // padded to multiple of 4. - let mut pad_length = padded_data_length - self.packet.len(); - let mut block_total_length = padded_data_length + ENHANCED_BLOCK_SIZE; - if let Some(max_block_size) = self.max_block_size { - if block_total_length > max_block_size { - self.packet.truncate(max_block_size - ENHANCED_BLOCK_SIZE); - pad_length = 0; - block_total_length = max_block_size; - } - } - let mut block_data = Vec::<u8>::new(); - block_data.extend_from_slice(&u32::to_le_bytes(0x00000006)); // Block Type - block_data.extend_from_slice(&u32::to_le_bytes(block_total_length.try_into().unwrap())); - block_data.extend_from_slice(&u32::to_le_bytes(self.interface_id)); // Interface ID - // High timestamp - block_data.extend_from_slice(&u32::to_le_bytes((self.timestamp >> 32) as u32)); - // Low timestamp - block_data.extend_from_slice(&u32::to_le_bytes(self.timestamp as u32)); - // Captured Packet Length - block_data.extend_from_slice(&u32::to_le_bytes(self.packet.len() as u32)); - block_data.extend_from_slice(&u32::to_le_bytes(packet_length)); // Original Packet Length - block_data.append(&mut self.packet); - block_data.extend_from_slice(&vec![0; pad_length]); - block_data.extend_from_slice(&u32::to_le_bytes(block_total_length.try_into().unwrap())); - block_data - } -} - -fn get_pcapng_header() -> Vec<u8> { - let mut bytes = vec![]; - // PCAPng files must start with a Section Header Block. - bytes.extend_from_slice(&u32::to_le_bytes(0x0A0D0D0A)); // Block Type - bytes.extend_from_slice(&u32::to_le_bytes(28)); // Block Total Length - bytes.extend_from_slice(&u32::to_le_bytes(0x1A2B3C4D)); // Byte-Order Magic - bytes.extend_from_slice(&u16::to_le_bytes(1)); // Major Version - bytes.extend_from_slice(&u16::to_le_bytes(0)); // Minor Version - bytes.extend_from_slice(&u64::to_le_bytes(0xFFFFFFFFFFFFFFFF)); // Section Length (not specified) - bytes.extend_from_slice(&u32::to_le_bytes(28)); // Block Total Length - - // Write the Interface Description Block used for all - // UCI records. - bytes.extend_from_slice(&u32::to_le_bytes(0x00000001)); // Block Type - bytes.extend_from_slice(&u32::to_le_bytes(20)); // Block Total Length - bytes.extend_from_slice(&u16::to_le_bytes(293)); // LinkType - bytes.extend_from_slice(&u16::to_le_bytes(0)); // Reserved - bytes.extend_from_slice(&u32::to_le_bytes(0)); // SnapLen (no limit) - bytes.extend_from_slice(&u32::to_le_bytes(20)); // Block Total Length - bytes -} - -#[async_trait] -impl UciLogger for UciLoggerImpl { - async fn log_uci_command(&self, cmd: UciCommandPacket) { - match self.config.mode { - UciLogMode::Disabled => return, - UciLogMode::Enabled => self.log_uci_packet(cmd.into()).await, - UciLogMode::Filtered => { - let filtered_cmd: UciCommandPacket = match cmd.specialize() { - UciCommandChild::SessionCommand(session_cmd) => { - match session_cmd.specialize() { - SessionCommandChild::SessionSetAppConfigCmd(set_config_cmd) => { - let session_id = set_config_cmd.get_session_id(); - let tlvs = set_config_cmd.get_tlvs(); - let mut filtered_tlvs = Vec::new(); - for tlv in tlvs { - if VENDOR_ID == tlv.cfg_id as u64 - || STATIC_STS_IV == tlv.cfg_id as u64 - { - filtered_tlvs.push(AppConfigTlv { - cfg_id: tlv.cfg_id, - v: vec![0; tlv.v.len()], - }); - } else { - filtered_tlvs.push(tlv.clone()); - } - } - SessionSetAppConfigCmdBuilder { session_id, tlvs: filtered_tlvs } - .build() - .into() - } - _ => session_cmd.into(), - } - } - _ => cmd, - }; - self.log_uci_packet(filtered_cmd.into()).await; - } - } - } - - async fn log_uci_response(&self, rsp: UciResponsePacket) { - match self.config.mode { - UciLogMode::Disabled => return, - UciLogMode::Enabled => self.log_uci_packet(rsp.into()).await, - UciLogMode::Filtered => { - let filtered_rsp: UciResponsePacket = match rsp.specialize() { - UciResponseChild::SessionResponse(session_rsp) => { - match session_rsp.specialize() { - SessionResponseChild::SessionGetAppConfigRsp(rsp) => { - let status = rsp.get_status(); - let tlvs = rsp.get_tlvs(); - let mut filtered_tlvs = Vec::new(); - for tlv in tlvs { - if VENDOR_ID == tlv.cfg_id as u64 - || STATIC_STS_IV == tlv.cfg_id as u64 - { - filtered_tlvs.push(AppConfigTlv { - cfg_id: tlv.cfg_id, - v: vec![0; tlv.v.len()], - }); - } else { - filtered_tlvs.push(tlv.clone()); - } - } - SessionGetAppConfigRspBuilder { status, tlvs: filtered_tlvs } - .build() - .into() - } - _ => session_rsp.into(), - } - } - _ => rsp, - }; - self.log_uci_packet(filtered_rsp.into()).await; - } - } - } - - async fn log_uci_notification(&self, ntf: UciNotificationPacket) { - if self.config.mode == UciLogMode::Disabled { - return; - } - // No notifications to be filtered. - self.log_uci_packet(ntf.into()).await; - } - - async fn close_file(&self) { - if self.config.mode == UciLogMode::Disabled { - return; - } - self.buf_file.lock().await.close_file().await; - } -} - -#[async_trait] -pub trait FileFactory { - async fn new_file(&self) -> (Option<SyncFile>, usize); - async fn create_file_using_open_options(&self, path: &str) -> Result<SyncFile, UwbErr>; - async fn create_file_at_path(&self, path: &str) -> Option<SyncFile>; - async fn set_config(&mut self, config: UciLogConfig); -} - -#[derive(Default)] -pub struct RealFileFactory { - config: Option<UciLogConfig>, -} - -#[async_trait] -impl FileFactory for RealFileFactory { - async fn new_file(&self) -> (Option<SyncFile>, usize) { - match OpenOptions::new() - .append(true) - .custom_flags(libc::O_NOFOLLOW) - .open(&self.config.as_ref().unwrap().path) - .await - .ok() - { - Some(f) => { - let size = match f.metadata().await { - Ok(md) => { - let duration = match md.modified() { - Ok(modified_date) => { - match SystemTime::now().duration_since(modified_date) { - Ok(duration) => duration.as_secs(), - Err(e) => { - error!("Failed to convert to duration {:?}", e); - 0 - } - } - } - Err(e) => { - error!("Failed to convert to duration {:?}", e); - 0 - } - }; - if duration > UCI_LOG_LAST_FILE_STORE_TIME_SEC { - 0 - } else { - md.len().try_into().unwrap() - } - } - Err(e) => { - error!("Failed to get metadata {:?}", e); - 0 - } - }; - match size { - 0 => { - (self.create_file_at_path(&self.config.as_ref().unwrap().path).await, size) - } - _ => (Some(Arc::new(Mutex::new(f))), size), - } - } - None => (self.create_file_at_path(&self.config.as_ref().unwrap().path).await, 0), - } - } - - async fn set_config(&mut self, config: UciLogConfig) { - self.config = Some(config); - } - - async fn create_file_using_open_options(&self, path: &str) -> Result<SyncFile, UwbErr> { - Ok(Arc::new(Mutex::new(OpenOptions::new().write(true).create_new(true).open(path).await?))) - } - - async fn create_file_at_path(&self, path: &str) -> Option<SyncFile> { - if create_dir(LOG_DIR).await.is_err() { - error!("Failed to create dir"); - } - if remove_file(path).await.is_err() { - error!("Failed to remove file!"); - } - match self.create_file_using_open_options(path).await { - Ok(f) => Some(f), - Err(e) => { - error!("Failed to create file {:?}", e); - None - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use core::pin::Pin; - use core::task::{Context, Poll}; - use log::debug; - use std::io::Error; - use uwb_uci_packets::{ - AppConfigTlvType, DeviceState, DeviceStatusNtfBuilder, GetDeviceInfoCmdBuilder, - GetDeviceInfoRspBuilder, StatusCode, - }; - - struct MockLogFile; - - impl MockLogFile { - #[allow(dead_code)] - async fn write_all(&mut self, _data: &[u8]) -> Result<(), UwbErr> { - debug!("Write to fake file"); - Ok(()) - } - #[allow(dead_code)] - async fn flush(&self) -> Result<(), UwbErr> { - debug!("Fake file flush success"); - Ok(()) - } - } - - impl AsyncWrite for MockLogFile { - fn poll_write( - self: Pin<&mut Self>, - _cx: &mut Context<'_>, - _buf: &[u8], - ) -> Poll<Result<usize, Error>> { - Poll::Ready(Ok(0)) - } - - fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Error>> { - Poll::Ready(Ok(())) - } - - fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), Error>> { - Poll::Ready(Ok(())) - } - } - - struct MockFileFactory; - - #[async_trait] - impl FileFactory for MockFileFactory { - async fn new_file(&self) -> (Option<SyncFile>, usize) { - (Some(Arc::new(Mutex::new(MockLogFile {}))), 0) - } - async fn set_config(&mut self, _config: UciLogConfig) {} - async fn create_file_using_open_options(&self, _path: &str) -> Result<SyncFile, UwbErr> { - Ok(Arc::new(Mutex::new(MockLogFile {}))) - } - async fn create_file_at_path(&self, _path: &str) -> Option<SyncFile> { - Some(Arc::new(Mutex::new(MockLogFile {}))) - } - } - - #[test] - fn test_enhanced_packet_build() { - let uci_packet: Vec<u8> = vec![0x41, 0x03, 0x00, 0x02, 0x00, 0x00]; - let timestamp: u64 = 0x0102_0304_0506_0708; - let interface_id: u32 = 0; - let enhanced_block = EnhancedBlockBuilder::new() - .timestamp(timestamp) - .interface_id(interface_id) - .packet(uci_packet) - .build(); - - let expected_block: Vec<u8> = vec![ - 0x06, 0x00, 0x00, 0x00, // block type - // packet is of length 6, padded to 8, with total length 40=0x28 - 0x28, 0x00, 0x00, 0x00, // block length - 0x00, 0x00, 0x00, 0x00, // interface id - 0x04, 0x03, 0x02, 0x01, // timestamp high - 0x08, 0x07, 0x06, 0x05, // timestemp low - 0x06, 0x00, 0x00, 0x00, // captured length - 0x06, 0x00, 0x00, 0x00, // original length - 0x41, 0x03, 0x00, 0x02, // packet (padded) - 0x00, 0x00, 0x00, 0x00, // packet (padded) - 0x28, 0x00, 0x00, 0x00, // block length - ]; - assert_eq!(&enhanced_block, &expected_block); - } - - #[test] - fn test_enhanced_packet_truncate_build() { - let uci_packet: Vec<u8> = vec![0x41, 0x03, 0x00, 0x02, 0x00, 0x00]; - let timestamp: u64 = 0x0102_0304_0506_0708; - let interface_id: u32 = 0; - let enhanced_block = EnhancedBlockBuilder::new() - .timestamp(timestamp) - .interface_id(interface_id) - .packet(uci_packet) - .max_block_size(0x24) // packet need truncation - .build(); - - let expected_block: Vec<u8> = vec![ - 0x06, 0x00, 0x00, 0x00, // block type - 0x24, 0x00, 0x00, 0x00, // block length - 0x00, 0x00, 0x00, 0x00, // interface id - 0x04, 0x03, 0x02, 0x01, // timestamp high - 0x08, 0x07, 0x06, 0x05, // timestemp low - 0x04, 0x00, 0x00, 0x00, // captured length - 0x06, 0x00, 0x00, 0x00, // original length - 0x41, 0x03, 0x00, 0x02, // packet (truncated) - 0x24, 0x00, 0x00, 0x00, // block length - ]; - assert_eq!(&enhanced_block, &expected_block); - } - - #[tokio::test] - async fn test_log_command() -> Result<(), UwbErr> { - let logger = - UciLoggerImpl::new(UciLogMode::Filtered, Arc::new(Mutex::new(MockFileFactory {}))) - .await; - let cmd: UciCommandPacket = GetDeviceInfoCmdBuilder {}.build().into(); - logger.log_uci_command(cmd).await; - let expected_buffer = [ - 6, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, - 32, 2, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, - ]; - let buf_file = logger.buf_file.lock().await; - assert_eq!(&buf_file.buffer, &expected_buffer); - Ok(()) - } - - #[tokio::test] - async fn test_log_response() -> Result<(), UwbErr> { - let logger = - UciLoggerImpl::new(UciLogMode::Filtered, Arc::new(Mutex::new(MockFileFactory {}))) - .await; - let rsp = GetDeviceInfoRspBuilder { - status: StatusCode::UciStatusOk, - uci_version: 0, - mac_version: 0, - phy_version: 0, - uci_test_version: 0, - vendor_spec_info: vec![], - } - .build() - .into(); - logger.log_uci_response(rsp).await; - let expected_buffer = [ - 6, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 17, 0, 0, 0, - 64, 2, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, - ]; - let buf_file = logger.buf_file.lock().await; - assert_eq!(&buf_file.buffer, &expected_buffer); - Ok(()) - } - - #[tokio::test] - async fn test_log_notification() -> Result<(), UwbErr> { - let logger = - UciLoggerImpl::new(UciLogMode::Filtered, Arc::new(Mutex::new(MockFileFactory {}))) - .await; - let ntf = - DeviceStatusNtfBuilder { device_state: DeviceState::DeviceStateReady }.build().into(); - logger.log_uci_notification(ntf).await; - let expected_buffer = [ - 6, 0, 0, 0, // block type - 40, 0, 0, 0, // block length - 0, 0, 0, 0, // interface id - 0, 0, 0, 0, // timestamp high - 0, 0, 0, 0, // timestamp low - 8, 0, 0, 0, // captured length - 8, 0, 0, 0, // original length - 96, 1, 0, 1, // packet - 0, 0, 0, 1, // packet - 40, 0, 0, 0, // block length - ]; - let buf_file = logger.buf_file.lock().await; - assert_eq!(&buf_file.buffer, &expected_buffer); - Ok(()) - } - - #[tokio::test] - async fn test_disabled_log() -> Result<(), UwbErr> { - let logger = - UciLoggerImpl::new(UciLogMode::Disabled, Arc::new(Mutex::new(MockFileFactory {}))) - .await; - let cmd: UciCommandPacket = GetDeviceInfoCmdBuilder {}.build().into(); - logger.log_uci_command(cmd).await; - let buf_file = logger.buf_file.lock().await; - assert!(buf_file.buffer.is_empty()); - Ok(()) - } - - #[tokio::test] - async fn test_filter_log() -> Result<(), UwbErr> { - let logger = - UciLoggerImpl::new(UciLogMode::Filtered, Arc::new(Mutex::new(MockFileFactory {}))) - .await; - let rsp = SessionGetAppConfigRspBuilder { - status: StatusCode::UciStatusOk, - tlvs: vec![AppConfigTlv { cfg_id: AppConfigTlvType::VendorId, v: vec![0x02, 0x02] }], - } - .build() - .into(); - logger.log_uci_response(rsp).await; - let expected_buffer = [ - 6, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 13, 0, 0, 0, - 65, 4, 0, 6, 0, 0, 0, 0, 1, 39, 2, 0, 0, 0, 0, 0, 48, 0, 0, 0, - ]; - let buf_file = logger.buf_file.lock().await; - assert_eq!(&buf_file.buffer, &expected_buffer); - Ok(()) - } -} diff --git a/src/rust/uci_hal_android/error.rs b/src/rust/uci_hal_android/error.rs index ede1b99..60fa755 100644 --- a/src/rust/uci_hal_android/error.rs +++ b/src/rust/uci_hal_android/error.rs @@ -126,3 +126,146 @@ fn exception_code_to_uwb_error(exception_code: ExceptionCode) -> UwbCoreError { } /// Result type associated with Error: pub type Result<T> = std::result::Result<T, Error>; + +#[cfg(test)] +mod tests { + use super::*; + + use android_hardware_uwb::binder::ExceptionCode; + use uwb_core::error::Error as UwbCoreError; + + #[test] + fn test_uwb_core_error_to_exception_code() { + let mut exception = uwb_core_error_to_exception_code(UwbCoreError::BadParameters); + assert_eq!(exception, ExceptionCode::ILLEGAL_ARGUMENT); + + exception = uwb_core_error_to_exception_code(UwbCoreError::ForeignFunctionInterface); + assert_eq!(exception, ExceptionCode::TRANSACTION_FAILED); + } + + #[test] + fn test_exception_code_to_uwb_error() { + let mut error = exception_code_to_uwb_error(ExceptionCode::ILLEGAL_ARGUMENT); + assert_eq!(error, UwbCoreError::BadParameters); + + error = exception_code_to_uwb_error(ExceptionCode::ILLEGAL_STATE); + assert_eq!(error, UwbCoreError::BadParameters); + + error = exception_code_to_uwb_error(ExceptionCode::UNSUPPORTED_OPERATION); + assert_eq!(error, UwbCoreError::BadParameters); + + error = exception_code_to_uwb_error(ExceptionCode::NULL_POINTER); + assert_eq!(error, UwbCoreError::BadParameters); + } + + #[test] + fn test_status_code_to_exception_code() { + let mut exception = status_code_to_exception_code(StatusCode::OK); + assert_eq!(exception, ExceptionCode::NONE); + + exception = status_code_to_exception_code(StatusCode::NO_MEMORY); + assert_eq!(exception, ExceptionCode::TRANSACTION_FAILED); + + exception = status_code_to_exception_code(StatusCode::INVALID_OPERATION); + assert_eq!(exception, ExceptionCode::ILLEGAL_ARGUMENT); + + exception = status_code_to_exception_code(StatusCode::BAD_VALUE); + assert_eq!(exception, ExceptionCode::ILLEGAL_ARGUMENT); + + exception = status_code_to_exception_code(StatusCode::BAD_TYPE); + assert_eq!(exception, ExceptionCode::ILLEGAL_ARGUMENT); + + exception = status_code_to_exception_code(StatusCode::NAME_NOT_FOUND); + assert_eq!(exception, ExceptionCode::ILLEGAL_ARGUMENT); + + exception = status_code_to_exception_code(StatusCode::PERMISSION_DENIED); + assert_eq!(exception, ExceptionCode::SECURITY); + + exception = status_code_to_exception_code(StatusCode::NO_INIT); + assert_eq!(exception, ExceptionCode::ILLEGAL_ARGUMENT); + + exception = status_code_to_exception_code(StatusCode::ALREADY_EXISTS); + assert_eq!(exception, ExceptionCode::ILLEGAL_ARGUMENT); + + exception = status_code_to_exception_code(StatusCode::DEAD_OBJECT); + assert_eq!(exception, ExceptionCode::ILLEGAL_ARGUMENT); + + exception = status_code_to_exception_code(StatusCode::FAILED_TRANSACTION); + assert_eq!(exception, ExceptionCode::TRANSACTION_FAILED); + + exception = status_code_to_exception_code(StatusCode::BAD_INDEX); + assert_eq!(exception, ExceptionCode::ILLEGAL_ARGUMENT); + + exception = status_code_to_exception_code(StatusCode::NOT_ENOUGH_DATA); + assert_eq!(exception, ExceptionCode::ILLEGAL_ARGUMENT); + + exception = status_code_to_exception_code(StatusCode::WOULD_BLOCK); + assert_eq!(exception, ExceptionCode::TRANSACTION_FAILED); + + exception = status_code_to_exception_code(StatusCode::TIMED_OUT); + assert_eq!(exception, ExceptionCode::TRANSACTION_FAILED); + + exception = status_code_to_exception_code(StatusCode::UNKNOWN_TRANSACTION); + assert_eq!(exception, ExceptionCode::ILLEGAL_ARGUMENT); + + exception = status_code_to_exception_code(StatusCode::FDS_NOT_ALLOWED); + assert_eq!(exception, ExceptionCode::ILLEGAL_ARGUMENT); + + exception = status_code_to_exception_code(StatusCode::UNEXPECTED_NULL); + assert_eq!(exception, ExceptionCode::ILLEGAL_ARGUMENT); + } + + #[test] + fn test_status_code_to_uwb_core_error() { + let mut error = status_code_to_uwb_core_error(StatusCode::OK); + assert_eq!(error, UwbCoreError::Unknown); + + error = status_code_to_uwb_core_error(StatusCode::NO_MEMORY); + assert_eq!(error, UwbCoreError::Unknown); + + error = status_code_to_uwb_core_error(StatusCode::BAD_VALUE); + assert_eq!(error, UwbCoreError::BadParameters); + + error = status_code_to_uwb_core_error(StatusCode::BAD_TYPE); + assert_eq!(error, UwbCoreError::BadParameters); + + error = status_code_to_uwb_core_error(StatusCode::NAME_NOT_FOUND); + assert_eq!(error, UwbCoreError::BadParameters); + + error = status_code_to_uwb_core_error(StatusCode::PERMISSION_DENIED); + assert_eq!(error, UwbCoreError::BadParameters); + + error = status_code_to_uwb_core_error(StatusCode::NO_INIT); + assert_eq!(error, UwbCoreError::BadParameters); + + error = status_code_to_uwb_core_error(StatusCode::ALREADY_EXISTS); + assert_eq!(error, UwbCoreError::Unknown); + + error = status_code_to_uwb_core_error(StatusCode::DEAD_OBJECT); + assert_eq!(error, UwbCoreError::Unknown); + + error = status_code_to_uwb_core_error(StatusCode::FAILED_TRANSACTION); + assert_eq!(error, UwbCoreError::Unknown); + + error = status_code_to_uwb_core_error(StatusCode::BAD_INDEX); + assert_eq!(error, UwbCoreError::BadParameters); + + error = status_code_to_uwb_core_error(StatusCode::NOT_ENOUGH_DATA); + assert_eq!(error, UwbCoreError::BadParameters); + + error = status_code_to_uwb_core_error(StatusCode::WOULD_BLOCK); + assert_eq!(error, UwbCoreError::Unknown); + + error = status_code_to_uwb_core_error(StatusCode::TIMED_OUT); + assert_eq!(error, UwbCoreError::Timeout); + + error = status_code_to_uwb_core_error(StatusCode::UNKNOWN_TRANSACTION); + assert_eq!(error, UwbCoreError::BadParameters); + + error = status_code_to_uwb_core_error(StatusCode::FDS_NOT_ALLOWED); + assert_eq!(error, UwbCoreError::Unknown); + + error = status_code_to_uwb_core_error(StatusCode::UNEXPECTED_NULL); + assert_eq!(error, UwbCoreError::Unknown); + } +} diff --git a/src/rust/uci_hal_android/uci_hal_android.rs b/src/rust/uci_hal_android/uci_hal_android.rs index a9b3048..0dd4c0b 100644 --- a/src/rust/uci_hal_android/uci_hal_android.rs +++ b/src/rust/uci_hal_android/uci_hal_android.rs @@ -39,10 +39,10 @@ use uwb_uci_packets::{DeviceState, DeviceStatusNtfBuilder}; use crate::error::{Error, Result}; -fn input_uci_hal_packet<T: Into<uwb_uci_packets::UciPacketPacket>>( +fn input_uci_hal_packet<T: Into<uwb_uci_packets::UciControlPacket>>( builder: T, ) -> Vec<UciHalPacket> { - let packets: Vec<uwb_uci_packets::UciPacketHalPacket> = builder.into().into(); + let packets: Vec<uwb_uci_packets::UciControlPacketHal> = builder.into().into(); packets.into_iter().map(|packet| packet.into()).collect() } @@ -286,3 +286,49 @@ impl UciHal for UciHalAndroid { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_send_device_state_error_notification() { + let (uci_sender, _) = mpsc::unbounded_channel(); + let res = send_device_state_error_notification(&uci_sender); + assert_eq!(res, Err(UwbCoreError::BadParameters)); + } + + #[tokio::test] + async fn test_new() { + let chip_id = "test_chip_id"; + let hal = UciHalAndroid::new(chip_id); + assert_eq!(hal.chip_id, chip_id); + assert!(hal.hal_close_result_receiver.is_none()); + assert!(hal.hal_death_recipient.is_none()); + assert!(hal.hal_uci_recipient.is_none()); + } + + #[tokio::test] + async fn test_open_error() { + let chip_id = "test_chip_id"; + let mut hal = UciHalAndroid::new(chip_id); + let packet_sender = mpsc::unbounded_channel().0; + let res = hal.open(packet_sender).await; + assert_eq!(res, Err(UwbCoreError::BadParameters)); + } + + #[tokio::test] + async fn test_close() { + let chip_id = "test_chip_id"; + let mut hal = UciHalAndroid::new(chip_id); + let (_, receiver) = mpsc::channel::<Result<()>>(1); + let death_recipient = Arc::new(Mutex::new(DeathRecipient::new(|| {}))); + hal.hal_close_result_receiver = Some(receiver); + hal.hal_death_recipient = Some(death_recipient.clone()); + let res = hal.close().await; + assert_eq!(res, Err(UwbCoreError::BadParameters)); + assert!(hal.hal_close_result_receiver.is_none()); + assert!(hal.hal_death_recipient.is_none()); + assert!(hal.hal_uci_recipient.is_none()); + } +} diff --git a/src/rust/uwb_core/Cargo.toml b/src/rust/uwb_core/Cargo.toml index 8f0e03c..9814721 100644 --- a/src/rust/uwb_core/Cargo.toml +++ b/src/rust/uwb_core/Cargo.toml @@ -3,10 +3,14 @@ name = "uwb_core" version = "0.0.1" edition = "2021" +[build-dependencies] +protoc-rust = "2.24.1" + [dependencies] async-trait = "0.1.32" bytes = "1.1.0" log = "0.4.14" +protobuf = { version = "2.24.1", optional = true } num-traits = "0.2.12" num-derive = "0.3.3" thiserror = "1.0.30" @@ -17,4 +21,8 @@ uwb_uci_packets = { path = "../uwb_uci_packets" } # provided by ebuild [dev-dependencies] env_logger = "0.9.0" -tempfile = "3"
\ No newline at end of file +tempfile = "3" + +[features] +proto = ["dep:protobuf"] +mock-util = []
\ No newline at end of file diff --git a/src/rust/uwb_core/README.md b/src/rust/uwb_core/README.md index ea22064..0601af1 100644 --- a/src/rust/uwb_core/README.md +++ b/src/rust/uwb_core/README.md @@ -5,13 +5,13 @@ build and test the library by cargo. ## Building `uwb_uci_packets` package -The `uwb_uci_packets` package depends on `bluetooth_packetgen` and thus simply -using `cargo build` will fail. Follow the steps below before using cargo. +The `uwb_uci_packets` package depends on `pdl` and thus simply using `cargo +build` will fail. Follow the steps below before using cargo. 1. Enter Android environment by `source build/make/rbesetup.sh; lunch <target>` -2. Run `m -j32 bluetooth_packetgen` to compile `bluetooth_packetgen` c++ binary. +2. Run `m pdl` to compile the `pdl` Rust binary. -After that, we could build or test the package by cargo. +After that, we could build or test the package by `cargo test --features proto`. ## Enable logging for a certain test case of uwb\_core library @@ -23,3 +23,68 @@ single test case. ``` RUST_LOG=debug cargo test -p uwb_core <test_case_name> -- --nocapture ``` + +# Code Architecture + +This section describes the main modules of this library. The modules below are +listed in reversed topological order. + +## The uwb\_uci\_packets crate + +The `uwb_uci_packets` crate is aimed for encoding and decoding the UCI packets. +All the details of the UCI packet format should be encapsulated here. That +means, the client of this crate should not be aware of how the UCI messages are +constructed to or parsed from raw byte buffers. + +The crate is mainly generated from the PDL file. However, in the case where a +UCI feature cannot be achieved using PDL alone, a workaround should be created +inside this crate to complete this feature (i.e. define structs and implement +the parsing methods manually) to encapsulate the details of UCI packet format. + +Note that the interface of the workaround should be as close to PDL-generated +code as possible. + + +## params + +The params modules defines the parameters types, including UCI, FiRa, and CCC +specification. + +This module depends on the `uwb_uci_packets` crate. To prevent the client of +this module directly depending on the `uwb_uci_packets` crate, we re-expose all +the needed enums and structs at `params/uci_packets.rs`. + +## UCI + +The `uci` module is aimed to provide a rust-idiomatic way that implements the +UCI interface, such as: +- Declare meaningful arguments types +- Create a public method for each UCI command, and wait for its corresponding + response +- Create a callback method for each UCI notification + +According to the asynchronous nature of the UCI interface, the `UciManager` +struct provides asynchronous methods using the actor model. For easier usage, +the `UciManagerSync` struct works as a thin synchronous wrapper. + +This module depends on the `params` module. + +## Session + +The `session` module implements the ranging session-related logic. We support +the FiRa and CCC specification here. + +This module depends on the `params` and `UCI` modules. + +## service + +The `service` module is aimed to provide a "top-shim", the main entry of this +library. Similar to the `UciManagerSync`, the `UwbService` struct provides a +simple synchronous interface to the client of the library. `examples/main.rs` is +a simple example for using the `UwbService` struct. + +If we want to provide the UWB across the process or language boundary, then +`ProtoUwbService` provices a simple wrapper that converts all the arguments and +responses to protobuf-encoded byte buffers. + +The `service` module depends on `params`, `uci`, and `session` modules. diff --git a/src/rust/uwb_core/build.rs b/src/rust/uwb_core/build.rs new file mode 100644 index 0000000..f29a5e1 --- /dev/null +++ b/src/rust/uwb_core/build.rs @@ -0,0 +1,59 @@ +// Copyright 2022, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +/// Generate the protobuf bindings inside the uwb_core library. +/// +/// The protobuf are mainly used to represent the elements of uwb_uci_packets. If we use +/// Android's rust_protobuf build target to split the protobuf bindings to a dedicated crate, then +/// we cannot implement the conversion trait (i.e. std::convert::From) between the protobuf +/// bindings and the uwb_uci_packets's elements due to Rust's orphan rule. +fn generate_proto_bindings() { + let out_dir = std::env::var_os("OUT_DIR").unwrap(); + + // Generate the protobuf bindings to "${OUT_DIR}/uwb_service.rs". + protoc_rust::Codegen::new() + .out_dir(&out_dir) + .input("./protos/uwb_service.proto") + .run() + .expect("Running protoc failed."); + + // Including the protobuf bindings directly hits the issue: + // "error: an inner attribute is not permitted in this context". + // + // To workaround this, first we create the file "${OUT_DIR}/proto_bindings.rs" that contains + // ``` + // #[path = "${OUT_DIR}/uwb_service.rs"] + // pub mod bindings; + // ``` + // + // Then include the generated file at proto.rs by: + // ``` + // include!(concat!(env!("OUT_DIR"), "/proto_bindings.rs")); + // ``` + let file_path = PathBuf::from(&out_dir).join("proto_bindings.rs"); + let file = File::create(file_path).expect("Failed to create the generated file"); + writeln!(&file, "#[path = \"{}/uwb_service.rs\"]", out_dir.to_str().unwrap()) + .expect("Failed to write to the generated file"); + writeln!(&file, "pub mod bindings;").expect("Failed to write to the generated file"); +} + +fn main() { + if std::env::var("CARGO_FEATURE_PROTO") == Ok("1".to_string()) { + generate_proto_bindings(); + } +} diff --git a/src/rust/uwb_core/examples/main.rs b/src/rust/uwb_core/examples/main.rs index 42ad748..63317b1 100644 --- a/src/rust/uwb_core/examples/main.rs +++ b/src/rust/uwb_core/examples/main.rs @@ -14,81 +14,25 @@ //! A simple example for the usage of the uwb_core library. -use async_trait::async_trait; use log::debug; -use tokio::sync::mpsc; use uwb_core::error::{Error as UwbError, Result as UwbResult}; -use uwb_core::params::uci_packets::{DeviceState, ReasonCode, SessionId, SessionState}; use uwb_core::service::{ - default_runtime, UwbServiceBuilder, UwbServiceCallback, UwbServiceCallbackSendBuilder, + default_runtime, NopUwbServiceCallback, UwbServiceBuilder, UwbServiceCallbackSendBuilder, }; -use uwb_core::uci::uci_logger_factory::UciLoggerFactoryNull; -use uwb_core::uci::{SessionRangeData, UciHal, UciHalPacket}; - -/// A placeholder implementation for UciHal. -struct UciHalImpl {} -#[async_trait] -impl UciHal for UciHalImpl { - async fn open(&mut self, _packet_sender: mpsc::UnboundedSender<UciHalPacket>) -> UwbResult<()> { - debug!("UciHalImpl::open() is called"); - Ok(()) - } - async fn close(&mut self) -> UwbResult<()> { - debug!("UciHalImpl::close() is called"); - Ok(()) - } - async fn send_packet(&mut self, packet: UciHalPacket) -> UwbResult<()> { - debug!("UciHalImpl::send_packet({:?}) is called", packet); - Ok(()) - } -} - -/// A placeholder implementation for UwbServiceCallback. -struct UwbServiceCallbackImpl {} -impl UwbServiceCallback for UwbServiceCallbackImpl { - fn on_service_reset(&mut self, success: bool) { - debug!("UwbService is reset, success: {}", success); - } - - fn on_uci_device_status_changed(&mut self, state: DeviceState) { - debug!("UCI device status: {:?}", state); - } - - fn on_session_state_changed( - &mut self, - session_id: SessionId, - session_state: SessionState, - reason_code: ReasonCode, - ) { - debug!( - "Session {:?}'s state is changed to {:?}, reason: {:?}", - session_id, session_state, reason_code - ); - } - - fn on_range_data_received(&mut self, session_id: SessionId, range_data: SessionRangeData) { - debug!("Received range data {:?} from Session {:?}", range_data, session_id); - } - - fn on_vendor_notification_received(&mut self, gid: u32, oid: u32, payload: Vec<u8>) { - debug!("Received vendor notification: gid={}, oid={}, payload={:?}", gid, oid, payload); - } -} +use uwb_core::uci::{NopUciHal, NopUciLoggerFactory}; fn main() { env_logger::init(); // The UwbService needs an outlived Tokio Runtime. let runtime = default_runtime().unwrap(); - // Initialize callback object. - let callback = UwbServiceCallbackImpl {}; // Initialize the UWB service. - let mut service = UwbServiceBuilder::new() + let service = UwbServiceBuilder::new() .runtime_handle(runtime.handle().to_owned()) - .callback_builder(UwbServiceCallbackSendBuilder::new(callback)) - .uci_hal(UciHalImpl {}) - .uci_logger_factory(UciLoggerFactoryNull::default()) + .callback_builder(UwbServiceCallbackSendBuilder::new(NopUwbServiceCallback {})) + .uci_hal(NopUciHal {}) + .uci_logger_factory(NopUciLoggerFactory {}) .build() .unwrap(); diff --git a/src/rust/uwb_core/fuzz/.gitignore b/src/rust/uwb_core/fuzz/.gitignore new file mode 100644 index 0000000..1a45eee --- /dev/null +++ b/src/rust/uwb_core/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/src/rust/uwb_core/fuzz/Cargo.toml b/src/rust/uwb_core/fuzz/Cargo.toml new file mode 100644 index 0000000..e6ea9f4 --- /dev/null +++ b/src/rust/uwb_core/fuzz/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "uwb_core-fuzz" +version = "0.0.1" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +arbitrary = {version = "1", features = ["derive"] } +libfuzzer-sys = "0.4" +uwb_core = { path = "..", features = ["proto"] } + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "proto_uwb_service_fuzzer" +path = "proto_uwb_service_fuzzer.rs" +test = false +doc = false diff --git a/src/rust/uwb_core/fuzz/proto_uwb_service_fuzzer.rs b/src/rust/uwb_core/fuzz/proto_uwb_service_fuzzer.rs new file mode 100644 index 0000000..850d8a1 --- /dev/null +++ b/src/rust/uwb_core/fuzz/proto_uwb_service_fuzzer.rs @@ -0,0 +1,87 @@ +// Copyright 2022, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_main] + +use libfuzzer_sys::{arbitrary::Arbitrary, fuzz_target}; +use uwb_core::service::{ + default_runtime, NopUwbServiceCallback, ProtoUwbService, UwbServiceBuilder, + UwbServiceCallbackSendBuilder, +}; +use uwb_core::uci::{NopUciHal, NopUciLoggerFactory}; + +/// The list of the ProtoUwbService's methods that take the argument. +#[derive(Arbitrary, Debug)] +enum Command { + SetLoggerMode, + InitSession, + DeinitSession, + StartRanging, + StopRanging, + Reconfigure, + UpdateControllerMulticastList, + AndroidSetCountryCode, + SendVendorCmd, + SessionParams, +} + +fuzz_target!(|methods: Vec<(Command, &[u8])>| { + // Setup the ProtoUwbService. + let runtime = default_runtime().unwrap(); + let service = UwbServiceBuilder::new() + .runtime_handle(runtime.handle().to_owned()) + .callback_builder(UwbServiceCallbackSendBuilder::new(NopUwbServiceCallback {})) + .uci_hal(NopUciHal {}) + .uci_logger_factory(NopUciLoggerFactory {}) + .build() + .unwrap(); + let mut proto_service = ProtoUwbService::new(service); + let _ = proto_service.enable(); + + // Call the methods of ProtoUwbService that takes the argument. + for (command, bytes) in methods.into_iter() { + match command { + Command::SetLoggerMode => { + let _ = proto_service.set_logger_mode(bytes); + } + Command::InitSession => { + let _ = proto_service.init_session(bytes); + } + Command::DeinitSession => { + let _ = proto_service.deinit_session(bytes); + } + Command::StartRanging => { + let _ = proto_service.start_ranging(bytes); + } + Command::StopRanging => { + let _ = proto_service.stop_ranging(bytes); + } + Command::Reconfigure => { + let _ = proto_service.reconfigure(bytes); + } + Command::UpdateControllerMulticastList => { + let _ = proto_service.update_controller_multicast_list(bytes); + } + Command::AndroidSetCountryCode => { + let _ = proto_service.android_set_country_code(bytes); + } + Command::SendVendorCmd => { + let _ = proto_service.raw_uci_cmd(bytes); + } + Command::SessionParams => { + let _ = proto_service.session_params(bytes); + } + } + } +}); diff --git a/src/rust/uwb_core/protos/uwb_service.proto b/src/rust/uwb_core/protos/uwb_service.proto new file mode 100644 index 0000000..3d51c4d --- /dev/null +++ b/src/rust/uwb_core/protos/uwb_service.proto @@ -0,0 +1,665 @@ +// Copyright 2022, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file defines the requests and responses of the UwbService's methods. +// Most of the elements are referred to the FiRa Consortium UWB Command +// Interface Generic Techinal Specification Version 1.1.0. +// +// Note: Due to the protobuf's restriction: the first field of enum must be +// zero, the value of some enum fields are not the same as the UCI +// specification. Also, the default value of some fields defined at UCI +// specification is not zero. The client should set all the fields when creating +// protobuf structure, instead of relying the default value of protobuf. + +syntax = "proto3"; +package uwb_core; + +// The status code of the method response, containing variants of error::Error +// and OK. +enum Status { + // The method is executed successfully. + OK = 0; + + // The provided parameters are invalid, or the method is not allowed to be + // called in the current state. + BAD_PARAMETERS = 1; + + // The maximum number of sessions has been reached. + MAX_SESSIONS_EXCEEDED = 2; + + // Max ranging round retries reached. + MAX_RR_RETRY_REACHED = 3; + + // Fails due to a protocol specific reason. + PROTOCOL_SPECIFIC = 4; + + // The remote device has requested to change the session. + REMOTE_REQUEST = 5; + + // The response or notification is not received in timeout. + TIMEOUT = 6; + + // The command should be retried. + COMMAND_RETRY = 7; + + // Duplicated SessionId. + DUPLICATED_SESSION_ID = 8; + + // The unknown error. + UNKNOWN = 9; +} + +// Represent uwb_uci_packets::StatusCode. +enum StatusCode { + UCI_STATUS_OK = 0; + UCI_STATUS_REJECTED = 1; + UCI_STATUS_FAILED = 2; + UCI_STATUS_SYNTAX_ERROR = 3; + UCI_STATUS_INVALID_PARAM = 4; + UCI_STATUS_INVALID_RANGE = 5; + UCI_STATUS_INVALID_MSG_SIZE = 6; + UCI_STATUS_UNKNOWN_GID = 7; + UCI_STATUS_UNKNOWN_OID = 8; + UCI_STATUS_READ_ONLY = 9; + UCI_STATUS_COMMAND_RETRY = 10; + + UCI_STATUS_SESSION_NOT_EXIST = 17; + UCI_STATUS_SESSION_DUPLICATE = 18; + UCI_STATUS_SESSION_ACTIVE = 19; + UCI_STATUS_MAX_SESSIONS_EXCEEDED = 20; + UCI_STATUS_SESSION_NOT_CONFIGURED = 21; + UCI_STATUS_ACTIVE_SESSIONS_ONGOING = 22; + UCI_STATUS_MULTICAST_LIST_FULL = 23; + UCI_STATUS_ADDRESS_NOT_FOUND = 24; + UCI_STATUS_ADDRESS_ALREADY_PRESENT = 25; + UCI_STATUS_OK_NEGATIVE_DISTANCE_REPORT = 27; + + UCI_STATUS_RANGING_TX_FAILED = 32; + UCI_STATUS_RANGING_RX_TIMEOUT = 33; + UCI_STATUS_RANGING_RX_PHY_DEC_FAILED = 34; + UCI_STATUS_RANGING_RX_PHY_TOA_FAILED = 35; + UCI_STATUS_RANGING_RX_PHY_STS_FAILED = 36; + UCI_STATUS_RANGING_RX_MAC_DEC_FAILED = 37; + UCI_STATUS_RANGING_RX_MAC_IE_DEC_FAILED = 38; + UCI_STATUS_RANGING_RX_MAC_IE_MISSING = 39; + UCI_STATUS_ERROR_ROUND_INDEX_NOT_ACTIVATED = 40; + UCI_STATUS_ERROR_NUMBER_OF_ACTIVE_RANGING_ROUNDS_EXCEEDED = 41; + UCI_STATUS_ERROR_ROUND_INDEX_NOT_SET_AS_INITIATOR = 42; + UCI_STATUS_ERROR_DL_TDOA_DEVICE_ADDRESS_NOT_MATCHING_IN_REPLY_TIME_LIST = 43; + + UCI_STATUS_DATA_MAX_TX_PSDU_SIZE_EXCEEDED = 48; + UCI_STATUS_DATA_RX_CRC_ERROR = 49; + + UCI_STATUS_ERROR_CCC_SE_BUSY = 80; + UCI_STATUS_ERROR_CCC_LIFECYCLE = 81; + UCI_STATUS_ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT = 82; + // All vendor specific status code will be mapped to UCI_STATUS_VENDOR_SPECIFIC. + UCI_STATUS_RFU_OR_VENDOR_SPECIFIC = 255; +} + +// Represents uwb_uci_packets::OwrAoaStatusCode +enum OwrAoaStatusCode { + UCI_STATUS_SUCCESS = 0; + UCI_STATUS_INTER_FRAME_INTERVAL_TIMEOUT = 1; +} + +// Represent uwb_uci_packets::DeviceState. +enum DeviceState { + DEVICE_STATE_READY = 0; + DEVICE_STATE_ACTIVE = 1; + DEVICE_STATE_ERROR = 2; +} + +// Represent uwb_uci_packets::SessionState. +enum SessionState { + INIT = 0; + DEINIT = 1; + ACTIVE = 2; + IDLE = 3; +} + +// Represent uwb_uci_packets::ReasonCode. +enum ReasonCode { + STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS = 0; + MAX_RANGING_ROUND_RETRY_COUNT_REACHED = 1; + MAX_NUMBER_OF_MEASUREMENTS_REACHED = 2; + SESSION_SUSPENDED_DUE_TO_INBAND_SIGNAL = 3; + SESSION_RESUMED_DUE_TO_INBAND_SIGNAL = 4; + SESSION_STOPPED_DUE_TO_INBAND_SIGNAL = 5; + ERROR_INVALID_UL_TDOA_RANDOM_WINDOW = 29; + ERROR_MIN_RFRAMES_PER_RR_NOT_SUPPORTED = 30; + ERROR_TX_DELAY_NOT_SUPPORTED = 31; + ERROR_SLOT_LENGTH_NOT_SUPPORTED = 32; + ERROR_INSUFFICIENT_SLOTS_PER_RR = 33; + ERROR_MAC_ADDRESS_MODE_NOT_SUPPORTED = 34; + ERROR_INVALID_RANGING_DURATION = 35; + ERROR_INVALID_STS_CONFIG = 36; + ERROR_INVALID_RFRAME_CONFIG = 37; + ERROR_HUS_NOT_ENOUGH_SLOTS = 38; + ERROR_HUS_CFP_PHASE_TOO_SHORT = 39; + ERROR_HUS_CAP_PHASE_TOO_SHORT = 40; + ERROR_HUS_OTHERS = 41; + ERROR_STATUS_SESSION_KEY_NOT_FOUND = 42; + ERROR_STATUS_SUB_SESSION_KEY_NOT_FOUND = 43; + ERROR_INVALID_PREAMBLE_CODE_INDEX = 44; + ERROR_INVALID_SFD_ID = 45; + ERROR_INVALID_PSDU_DATA_RATE = 46; + ERROR_INVALID_PHR_DATA_RATE = 47; + ERROR_INVALID_PREAMBLE_DURATION = 48; + ERROR_INVALID_STS_LENGTH = 49; + ERROR_INVALID_NUM_OF_STS_SEGMENTS = 50; + ERROR_INVALID_NUM_OF_CONTROLEES = 51; + ERROR_MAX_RANGING_REPLY_TIME_EXCEEDED = 52; + ERROR_INVALID_DST_ADDRESS_LIST = 53; + ERROR_INVALID_OR_NOT_FOUND_SUB_SESSION_ID = 54; + ERROR_INVALID_RESULT_REPORT_CONFIG = 55; + ERROR_INVALID_RANGING_ROUND_CONTROL_CONFIG = 56; + ERROR_INVALID_RANGING_ROUND_USAGE = 57; + ERROR_INVALID_MULTI_NODE_MODE = 58; + ERROR_RDS_FETCH_FAILURE = 59; + ERROR_REF_UWB_SESSION_DOES_NOT_EXIST = 60; + ERROR_REF_UWB_SESSION_RANGING_DURATION_MISMATCH = 61; + ERROR_REF_UWB_SESSION_INVALID_OFFSET_TIME = 62; + ERROR_REF_UWB_SESSION_LOST = 63; + ERROR_INVALID_CHANNEL_WITH_AOA = 128; + ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT = 129; + ERROR_DT_ANCHOR_RANGING_ROUNDS_NOT_CONFIGURED = 130; + ERROR_DT_TAG_RANGING_ROUNDS_NOT_CONFIGURED = 131; + // All vendor reason code will be mapped to ERROR_VENDOR_SPECIFIC. + ERROR_RFU_OR_VENDOR_SPECIFIC = 255; +} + +// Represent uwb_uci_packets::RangingMeasurementType. +enum RangingMeasurementType { + ONE_WAY = 0; + TWO_WAY = 1; + DL_TDOA = 2; + OWR_AOA = 3; +} + +// Represent uwb_uci_packets::SessionType. +enum SessionType { + FIRA_RANGING_SESSION = 0; + FIRA_DATA_TRANSFER = 1; + CCC = 2; +} + +// Represent uwb_uci_packets::UpdateMulticastListAction. +enum UpdateMulticastListAction { + ADD_CONTROLEE = 0; + REMOVE_CONTROLEE = 1; + ADD_CONTROLEE_WITH_SHORT_SUB_SESSION_KEY = 2; + ADD_CONTROLEE_WITH_LONG_SUB_SESSION_KEY = 3; +} + +// Represent uwb_core::params::fira_app_config_params::DeviceType. +enum DeviceType { + CONTROLEE = 0; + CONTROLLER = 1; +} + +// Represent uwb_core::params::fira_app_config_params::RangingRoundUsage. +enum RangingRoundUsage { + SS_TWR = 0; + DS_TWR = 1; + SS_TWR_NON = 2; + DS_TWR_NON = 3; +} + +// Represent uwb_core::params::fira_app_config_params::StsConfig. +enum StsConfig { + STATIC = 0; + DYNAMIC = 1; + DYNAMIC_FOR_CONTROLEE_INDIVIDUAL_KEY = 2; +} + +// Represent uwb_core::params::fira_app_config_params::MultiNodeMode. +enum MultiNodeMode { + UNICAST = 0; + ONE_TO_MANY = 1; + MANY_TO_MANY = 2; +} + +// Represent uwb_core::params::fira_app_config_params::UwbChannel. +enum UwbChannel { + CHANNEL_5 = 0; + CHANNEL_6 = 1; + CHANNEL_8 = 2; + CHANNEL_9 = 3; + CHANNEL_10 = 4; + CHANNEL_12 = 5; + CHANNEL_13 = 6; + CHANNEL_14 = 7; +} + +// Represent uwb_core::params::fira_app_config_params::MacFcsType. +enum MacFcsType { + CRC_16 = 0; + CRC_32 = 1; +} + +// Represent uwb_core::params::fira_app_config_params::AoaResultRequest. +enum AoaResultRequest { + NO_AOA_REPORT = 0; + REQ_AOA_RESULTS = 1; + REQ_AOA_RESULTS_AZIMUTH_ONLY = 2; + REQ_AOA_RESULTS_ELEVATION_ONLY = 3; + REQ_AOA_RESULTS_INTERLEAVED = 4; +} + +// Represent uwb_core::params::fira_app_config_params::RangeDataNtfConfig. +enum RangeDataNtfConfig { + RANGE_DATA_NTF_CONFIG_DISABLE = 0; + RANGE_DATA_NTF_CONFIG_ENABLE = 1; + RANGE_DATA_NTF_CONFIG_ENABLE_PROXIMITY = 2; +} + +// Represent uwb_core::params::fira_app_config_params::DeviceRole. +enum DeviceRole { + RESPONDER = 0; + INITIATOR = 1; +} + +// Represent uwb_core::params::fira_app_config_params::RframeConfig. +enum RframeConfig { + SP0 = 0; + SP1 = 1; + SP3 = 3; +} + +// Represent uwb_core::params::fira_app_config_params::PsduDataRate. +enum PsduDataRate { + RATE_6M_81 = 0; + RATE_7M_80 = 1; + RATE_27M_2 = 2; + RATE_31M_2 = 3; + RATE_850K = 4; +} + +// Represent uwb_core::params::fira_app_config_params::PreambleDuration. +enum PreambleDuration { + T32_SYMBOLS = 0; + T64_SYMBOLS = 1; +} + +// Represent uwb_core::params::fira_app_config_params::RangingTimeStruct. +enum RangingTimeStruct { + INTERVAL_BASED_SCHEDULING = 0; + BLOCK_BASED_SCHEDULING = 1; +} + +// Represent uwb_core::params::fira_app_config_params::TxAdaptivePayloadPower. +enum TxAdaptivePayloadPower { + TX_ADAPTIVE_PAYLOAD_POWER_DISABLE = 0; + TX_ADAPTIVE_PAYLOAD_POWER_ENABLE = 1; +} + +// Represent uwb_core::params::fira_app_config_params::PrfMode. +enum PrfMode { + BPRF = 0; + HPRF_WITH_124_8_MHZ = 1; + HPRF_WITH_249_6_MHZ = 2; +} + +// Represent uwb_core::params::fira_app_config_params::ScheduledMode. +enum ScheduledMode { + TIME_SCHEDULED_RANGING = 0; +} + +// Represent uwb_core::params::fira_app_config_params::KeyRotation. +enum KeyRotation { + KEY_ROTATION_DISABLE = 0; + KEY_ROTATION_ENABLE = 1; +} + +// Represent uwb_core::params::fira_app_config_params::MacAddressMode. +enum MacAddressMode { + MAC_ADDRESS_2_BYTES = 0; + MAC_ADDRESS_8_BYTES_2_BYTES_HEADER = 1; + MAC_ADDRESS_8_BYTES = 2; +} + +// Represent uwb_core::params::fira_app_config_params::HoppingMode. +enum HoppingMode { + HOPPING_MODE_DISABLE = 0; + FIRA_HOPPING_ENABLE = 1; +} + +// Represent uwb_core::params::fira_app_config_params::BprfPhrDataRate. +enum BprfPhrDataRate { + BPRF_PHR_DATA_RATE_850K = 0; + BPRF_PHR_DATA_RATE_6M_81 = 1; +} + +// Represent uwb_core::params::fira_app_config_params::StsLength. +enum StsLength { + LENGTH_32 = 0; + LENGTH_64 = 1; + LENGTH_128 = 2; +} + +// Represent uwb_core::uci::uci_logger::UciLoggerMode. +enum UciLoggerMode { + UCI_LOGGER_MODE_DISABLED = 0; + UCI_LOGGER_MODE_UNFILTERED = 1; + UCI_LOGGER_MODE_FILTERED = 2; +} + +// Represent uwb_core::params::fira_app_config_params::RangingRoundControl. +message RangingRoundControl { + bool ranging_result_report_message = 1; + bool control_message = 2; + bool measurement_report_message = 3; +} + +// Represent uwb_core::params::fira_app_config_params::ResultReportConfig. +message ResultReportConfig { + bool tof = 1; + bool aoa_azimuth = 2; + bool aoa_elevation = 3; + bool aoa_fom = 4; +} + +// Represent uwb_core::params::fira_app_config_params::FiraAppConfigParams. +message FiraAppConfigParams { + DeviceType device_type = 1; + RangingRoundUsage ranging_round_usage = 2; + StsConfig sts_config = 3; + MultiNodeMode multi_node_mode = 4; + UwbChannel channel_number = 5; + bytes device_mac_address = 6; + repeated bytes dst_mac_address = 7; + uint32 slot_duration_rstu = 8; + uint32 ranging_interval_ms = 9; + MacFcsType mac_fcs_type = 10; + RangingRoundControl ranging_round_control = 11; + AoaResultRequest aoa_result_request = 12; + RangeDataNtfConfig range_data_ntf_config = 13; + uint32 range_data_ntf_proximity_near_cm = 14; + uint32 range_data_ntf_proximity_far_cm = 15; + DeviceRole device_role = 16; + RframeConfig rframe_config = 17; + uint32 preamble_code_index = 18; + uint32 sfd_id = 19; + PsduDataRate psdu_data_rate = 20; + PreambleDuration preamble_duration = 21; + RangingTimeStruct ranging_time_struct = 22; + uint32 slots_per_rr = 23; + TxAdaptivePayloadPower tx_adaptive_payload_power = 24; + uint32 responder_slot_index = 25; + PrfMode prf_mode = 26; + ScheduledMode scheduled_mode = 27; + KeyRotation key_rotation = 28; + uint32 key_rotation_rate = 29; + uint32 session_priority = 30; + MacAddressMode mac_address_mode = 31; + bytes vendor_id = 32; + bytes static_sts_iv = 33; + uint32 number_of_sts_segments = 34; + uint32 max_rr_retry = 35; + uint32 uwb_initiation_time_ms = 36; + HoppingMode hopping_mode = 37; + uint32 block_stride_length = 38; + ResultReportConfig result_report_config = 39; + uint32 in_band_termination_attempt_count = 40; + uint32 sub_session_id = 41; + BprfPhrDataRate bprf_phr_data_rate = 42; + uint32 max_number_of_measurements = 43; + StsLength sts_length = 44; + uint32 number_of_range_measurements = 45; + uint32 number_of_aoa_azimuth_measurements = 46; + uint32 number_of_aoa_elevation_measurements = 47; +} + +// Represent uwb_uci_packets::Controlee. +message Controlee { + uint32 short_address = 1; + uint32 subsession_id = 2; +} + +// Represent uwb_uci_packets::ShortAddressTwoWayRangingMeasurement or +// uwb_uci_packets::ExtendedAddressTwoWayRangingMeasurement. +message TwoWayRangingMeasurement { + uint64 mac_address = 1; + StatusCode status = 2; + uint32 nlos = 3; + uint32 distance = 4; + uint32 aoa_azimuth = 5; + uint32 aoa_azimuth_fom = 6; + uint32 aoa_elevation = 7; + uint32 aoa_elevation_fom = 8; + uint32 aoa_destination_azimuth = 9; + uint32 aoa_destination_azimuth_fom = 10; + uint32 aoa_destination_elevation = 11; + uint32 aoa_destination_elevation_fom = 12; + uint32 slot_index = 13; + uint32 rssi = 14; +} + +// Represent uwb_uci_packets::ShortAddressOwrAoaRangingMeasurement or +// uwb_uci_packets::ExtendedAddressOwrAoaRangingMeasurement. +message OwrAoaRangingMeasurement { + uint64 mac_address = 1; + OwrAoaStatusCode owr_aoa_status_code = 2; + uint32 nlos = 3; + uint32 block_index = 4; + uint32 frame_sequence_number = 5; + uint32 aoa_azimuth = 6; + uint32 aoa_azimuth_fom = 7; + uint32 aoa_elevation = 8; + uint32 aoa_elevation_fom = 9; +} + +// Represent uwb_uci_packets::ShortAddressDlTdoaRangingMeasurement or +// uwb_uci_packets::ExtendedAddressDlTdoaRangingMeasurement. +message DlTDoARangingMeasurement { + uint64 mac_address = 1; + StatusCode status = 2; + uint32 message_control = 3; + uint32 block_index = 4; + uint32 round_index = 5; + uint32 nlos = 6; + uint32 aoa_azimuth = 7; + uint32 aoa_azimuth_fom = 8; + uint32 aoa_elevation = 9; + uint32 aoa_elevation_fom = 10; + uint32 rssi = 11; + uint64 tx_timestamp = 12; + uint64 rx_timestamp = 13; + uint32 anchor_cfo = 14; + uint32 cfo = 15; + uint32 initiator_reply_time = 16; + uint32 responder_reply_time = 17; + uint32 initiator_responder_tof = 18; + repeated uint32 dt_anchor_location = 19; + repeated uint32 ranging_rounds = 20; +} + +// Represent uwb_core::uci::notification::SessionRangeData; +message SessionRangeData { + uint32 sequence_number = 1; + uint32 session_id = 2; + uint32 current_ranging_interval_ms = 3; + RangingMeasurementType ranging_measurement_type = 4; + repeated TwoWayRangingMeasurement twoway_ranging_measurements = 5; + repeated DlTDoARangingMeasurement dltdoa_ranging_measurements = 6; + OwrAoaRangingMeasurement owraoa_ranging_measurement = 7; +} + +// Represent uwb_uci_packets::PowerStats; +message PowerStats { + StatusCode status = 1; + uint32 idle_time_ms = 2; + uint32 tx_time_ms = 3; + uint32 rx_time_ms = 4; + uint32 total_wake_count = 5; +} + +// Response of the UwbService::enable() method. +message EnableResponse { + Status status = 1; +} + +// Response of the UwbService::disable() method. +message DisableResponse { + Status status = 1; +} + +// Argument of the UwbService::SetLoggerMode() method. +message SetLoggerModeRequest { + UciLoggerMode logger_mode = 1; +} + +// Response of the UwbService::SetLoggerMode() method. +message SetLoggerModeResponse { + Status status = 1; +} + +// Argument of the UwbService::InitSession() method. +message InitSessionRequest { + uint32 session_id = 1; + SessionType session_type = 2; + FiraAppConfigParams params = 3; +} + +// Response of the UwbService::InitSession() method. +message InitSessionResponse { + Status status = 1; +} + +// Argument of the UwbService::DeinitSession() method. +message DeinitSessionRequest { + uint32 session_id = 1; +} + +// Response of the UwbService::DeinitSession() method. +message DeinitSessionResponse { + Status status = 1; +} + +// Argument of the UwbService::StartRanging() method. +message StartRangingRequest { + uint32 session_id = 1; +} + +// Response of the UwbService::StartRanging() method. +message StartRangingResponse { + Status status = 1; +} + +// Argument of the UwbService::StopRanging() method. +message StopRangingRequest { + uint32 session_id = 1; +} + +// Response of the UwbService::StopRanging() method. +message StopRangingResponse { + Status status = 1; +} + +// Argument of the UwbService::SessionParams() method. +message SessionParamsRequest { + uint32 session_id = 1; +} + +// Response of the UwbService::SessionParams() method. +message SessionParamsResponse { + Status status = 1; + FiraAppConfigParams params = 2; +} + +// Argument of the UwbService::Reconfigure() method. +message ReconfigureRequest { + uint32 session_id = 1; + FiraAppConfigParams params = 2; +} + +// Response of the UwbService::Reconfigure() method. +message ReconfigureResponse { + Status status = 1; +} + +// Argument of the UwbService::UpdateControllerMulticastList() method. +message UpdateControllerMulticastListRequest { + uint32 session_id = 1; + UpdateMulticastListAction action = 2; + repeated Controlee controlees = 3; +} + +// Response of the UwbService::UpdateControllerMulticastList() method. +message UpdateControllerMulticastListResponse { + Status status = 1; +} + +// Argument of the UwbService::AndroidSetCountryCode() method. +message AndroidSetCountryCodeRequest { + string country_code = 1; +} + +// Response of the UwbService::AndroidSetCountryCode() method. +message AndroidSetCountryCodeResponse { + Status status = 1; +} + +// Response of the UwbService::AndroidGetPowerStats() method. +message AndroidGetPowerStatsResponse { + Status status = 1; + PowerStats power_stats = 2; +} + +// Argument of the UwbService::SendVendorCmd() method. +message SendVendorCmdRequest { + uint32 gid = 1; + uint32 oid = 2; + bytes payload = 3; + uint32 mt = 4; +} + +// Response of the UwbService::SendVendorCmd() method. +message SendVendorCmdResponse { + Status status = 1; + uint32 gid = 2; + uint32 oid = 3; + bytes payload = 4; +} + +// Argument of the UwbServiceCallback::onServiceReset() method. +message ServiceResetSignal { + bool success = 1; +} + +// Argument of the UwbServiceCallback::onUciDeviceStatusChanged() method. +message UciDeviceStatusChangedSignal { + DeviceState state = 1; +} + +// Argument of the UwbServiceCallback::onSessionStateChanged() method. +message SessionStateChangedSignal { + uint32 session_id = 1; + SessionState session_state = 2; + ReasonCode reason_code = 3; +} + +// Argument of the UwbServiceCallback::onRangeDataReceived() method. +message RangeDataReceivedSignal { + uint32 session_id = 1; + SessionRangeData range_data = 2; +} + +// Argument of the UwbServiceCallback::onVendorNotificationReceived() method. +message VendorNotificationReceivedSignal { + uint32 gid = 1; + uint32 oid = 2; + bytes payload = 3; +} diff --git a/src/rust/uwb_core/src/error.rs b/src/rust/uwb_core/src/error.rs index da2be79..0b9c6b3 100644 --- a/src/rust/uwb_core/src/error.rs +++ b/src/rust/uwb_core/src/error.rs @@ -22,6 +22,9 @@ pub enum Error { /// current state. #[error("Bad parameters")] BadParameters, + /// Error across Foreign Function Interface. + #[error("Error across Foreign Function Interface")] + ForeignFunctionInterface, /// The maximum number of sessions has been reached. #[error("The maximum number of sessions has been reached")] MaxSessionsExceeded, @@ -43,12 +46,15 @@ pub enum Error { /// Duplicated SessionId. #[error("Duplicated SessionId")] DuplicatedSessionId, + /// Packet Tx Error + #[error("The packet send failed with an error")] + PacketTxError, /// The unknown error. #[error("The unknown error")] Unknown, /// The result of the mock method is not assigned - #[cfg(test)] + #[cfg(any(test, feature = "mock-utils"))] #[error("The result of the mock method is not assigned")] MockUndefined, } diff --git a/src/rust/uwb_core/src/lib.rs b/src/rust/uwb_core/src/lib.rs index 9049525..8f1eb1f 100644 --- a/src/rust/uwb_core/src/lib.rs +++ b/src/rust/uwb_core/src/lib.rs @@ -19,5 +19,7 @@ pub(crate) mod utils; pub mod error; pub mod params; +#[cfg(feature = "proto")] +pub mod proto; pub mod service; pub mod uci; diff --git a/src/rust/uwb_core/src/params/app_config_params.rs b/src/rust/uwb_core/src/params/app_config_params.rs index 8d58366..bb06d52 100644 --- a/src/rust/uwb_core/src/params/app_config_params.rs +++ b/src/rust/uwb_core/src/params/app_config_params.rs @@ -26,7 +26,7 @@ pub(super) type AppConfigTlvMap = HashMap<AppConfigTlvType, Vec<u8>>; /// The application configuration parameters of the UWB session. It is used to generate the /// parameters for the SESSION_SET_APP_CONFIG_CMD, or converted from the result of the /// SESSION_GET_APP_CONFIG_CMD. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum AppConfigParams { Fira(FiraAppConfigParams), Ccc(CccAppConfigParams), diff --git a/src/rust/uwb_core/src/params/fira_app_config_params.rs b/src/rust/uwb_core/src/params/fira_app_config_params.rs index dbe959d..880d197 100644 --- a/src/rust/uwb_core/src/params/fira_app_config_params.rs +++ b/src/rust/uwb_core/src/params/fira_app_config_params.rs @@ -1265,8 +1265,8 @@ mod tests { ), (AppConfigTlvType::DeviceRole, vec![device_role as u8]), (AppConfigTlvType::RframeConfig, vec![rframe_config as u8]), - (AppConfigTlvType::PreambleCodeIndex, vec![preamble_code_index as u8]), - (AppConfigTlvType::SfdId, vec![sfd_id as u8]), + (AppConfigTlvType::PreambleCodeIndex, vec![preamble_code_index]), + (AppConfigTlvType::SfdId, vec![sfd_id]), (AppConfigTlvType::PsduDataRate, vec![psdu_data_rate as u8]), (AppConfigTlvType::PreambleDuration, vec![preamble_duration as u8]), (AppConfigTlvType::RangingTimeStruct, vec![DEFAULT_RANGING_TIME_STRUCT as u8]), @@ -1289,7 +1289,7 @@ mod tests { (AppConfigTlvType::ResultReportConfig, vec![result_report_config.as_u8()]), ( AppConfigTlvType::InBandTerminationAttemptCount, - vec![in_band_termination_attempt_count as u8], + vec![in_band_termination_attempt_count], ), (AppConfigTlvType::BprfPhrDataRate, vec![DEFAULT_BPRF_PHR_DATA_RATE as u8]), ( diff --git a/src/rust/uwb_core/src/params/uci_packets.rs b/src/rust/uwb_core/src/params/uci_packets.rs index 421ed1e..2e0941e 100644 --- a/src/rust/uwb_core/src/params/uci_packets.rs +++ b/src/rust/uwb_core/src/params/uci_packets.rs @@ -21,11 +21,18 @@ use std::iter::FromIterator; // Re-export enums and structs from uwb_uci_packets. pub use uwb_uci_packets::{ AppConfigStatus, AppConfigTlv as RawAppConfigTlv, AppConfigTlvType, CapTlv, CapTlvType, - Controlee, ControleeStatus, ControleesV2, DeviceConfigId, DeviceConfigStatus, DeviceConfigTlv, - DeviceState, ExtendedAddressTwoWayRangingMeasurement, MulticastUpdateStatusCode, PowerStats, - RangingMeasurementType, ReasonCode, ResetConfig, SessionState, SessionType, - ShortAddressTwoWayRangingMeasurement, StatusCode, UpdateMulticastListAction, + Controlee, ControleeStatus, Controlees, CreditAvailability, DataRcvStatusCode, + DataTransferNtfStatusCode, DeviceConfigId, DeviceConfigStatus, DeviceConfigTlv, DeviceState, + ExtendedAddressDlTdoaRangingMeasurement, ExtendedAddressOwrAoaRangingMeasurement, + ExtendedAddressTwoWayRangingMeasurement, FiraComponent, GroupId, MessageType, + MulticastUpdateStatusCode, OwrAoaStatusCode, PowerStats, RangingMeasurementType, ReasonCode, + ResetConfig, SessionState, SessionType, ShortAddressDlTdoaRangingMeasurement, + ShortAddressOwrAoaRangingMeasurement, ShortAddressTwoWayRangingMeasurement, StatusCode, + UpdateMulticastListAction, }; +pub(crate) use uwb_uci_packets::{UciControlPacket, UciDataPacket, UciDataPacketHal}; + +use crate::error::Error; /// The type of the session identifier. pub type SessionId = u32; @@ -132,17 +139,30 @@ pub struct SetAppConfigResponse { pub config_status: Vec<AppConfigStatus>, } +/// The response from UciManager::session_update_dt_tag_ranging_rounds() method. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SessionUpdateDtTagRangingRoundsResponse { + /// The status code of the response. + pub status: StatusCode, + /// Indexes of unsuccessful ranging rounds. + pub ranging_round_indexes: Vec<u8>, +} + /// The country code struct that contains 2 uppercase ASCII characters. #[derive(Debug, Clone, PartialEq, Eq)] pub struct CountryCode([u8; 2]); impl CountryCode { + const UNKNOWN_COUNTRY_CODE: &'static [u8] = "00".as_bytes(); + /// Create a CountryCode instance. pub fn new(code: &[u8; 2]) -> Option<Self> { - if !code[0].is_ascii_uppercase() || !code[1].is_ascii_uppercase() { + if code != CountryCode::UNKNOWN_COUNTRY_CODE + && !code.iter().all(|x| (*x as char).is_ascii_alphabetic()) + { None } else { - Some(Self(*code)) + Some(Self((*code).to_ascii_uppercase().try_into().ok()?)) } } } @@ -153,6 +173,14 @@ impl From<CountryCode> for [u8; 2] { } } +impl TryFrom<String> for CountryCode { + type Error = Error; + fn try_from(item: String) -> Result<Self, Self::Error> { + let code = item.as_bytes().try_into().map_err(|_| Error::BadParameters)?; + Self::new(code).ok_or(Error::BadParameters) + } +} + /// The response of the UciManager::core_get_device_info() method. #[derive(Debug, Clone, PartialEq, Eq)] pub struct GetDeviceInfoResponse { @@ -170,7 +198,7 @@ pub struct GetDeviceInfoResponse { /// The raw UCI message for the vendor commands. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct RawVendorMessage { +pub struct RawUciMessage { /// The group id of the message. pub gid: u32, /// The opcode of the message. @@ -179,6 +207,16 @@ pub struct RawVendorMessage { pub payload: Vec<u8>, } +impl From<UciControlPacket> for RawUciMessage { + fn from(packet: UciControlPacket) -> Self { + Self { + gid: packet.get_group_id().into(), + oid: packet.get_opcode() as u32, + payload: packet.to_raw_payload(), + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -199,4 +237,14 @@ mod tests { let format_str = format!("{tlv:?}"); assert_eq!(format_str, "AppConfigTlv { cfg_id: DeviceType, v: [12, 34] }"); } + + #[test] + fn test_country_code() { + let _country_code_ascii: CountryCode = String::from("US").try_into().unwrap(); + let _country_code_unknown: CountryCode = String::from("00").try_into().unwrap(); + let country_code_invalid_1: Result<CountryCode, Error> = String::from("0S").try_into(); + country_code_invalid_1.unwrap_err(); + let country_code_invalid_2: Result<CountryCode, Error> = String::from("ÀÈ").try_into(); + country_code_invalid_2.unwrap_err(); + } } diff --git a/src/rust/uwb_core/src/proto.rs b/src/rust/uwb_core/src/proto.rs new file mode 100644 index 0000000..7cf2740 --- /dev/null +++ b/src/rust/uwb_core/src/proto.rs @@ -0,0 +1,21 @@ +// Copyright 2022, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Provide the protobuf bindings and the conversion between the elements of uwb_core and protobuf. + +// Include the protobuf bindings generated from protoc_rust to "pub mod bindings;". +include!(concat!(env!("OUT_DIR"), "/proto_bindings.rs")); + +pub(crate) mod mappings; +pub mod utils; diff --git a/src/rust/uwb_core/src/proto/mappings.rs b/src/rust/uwb_core/src/proto/mappings.rs new file mode 100644 index 0000000..0d21c09 --- /dev/null +++ b/src/rust/uwb_core/src/proto/mappings.rs @@ -0,0 +1,1208 @@ +// Copyright 2022, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Provide the conversion between the uwb_core's elements and protobuf bindings. + +use std::convert::{TryFrom, TryInto}; + +use protobuf::RepeatedField; +use zeroize::Zeroize; + +use crate::error::{Error, Result}; +use crate::params::fira_app_config_params::{ + AoaResultRequest, BprfPhrDataRate, DeviceRole, DeviceType, FiraAppConfigParams, + FiraAppConfigParamsBuilder, HoppingMode, KeyRotation, MacAddressMode, MacFcsType, + MultiNodeMode, PreambleDuration, PrfMode, PsduDataRate, RangeDataNtfConfig, + RangingRoundControl, RangingRoundUsage, RangingTimeStruct, ResultReportConfig, RframeConfig, + ScheduledMode, StsConfig, StsLength, TxAdaptivePayloadPower, UwbAddress, UwbChannel, +}; +use crate::params::uci_packets::{ + Controlee, DeviceState, ExtendedAddressDlTdoaRangingMeasurement, + ExtendedAddressOwrAoaRangingMeasurement, ExtendedAddressTwoWayRangingMeasurement, + OwrAoaStatusCode, PowerStats, RangingMeasurementType, ReasonCode, SessionState, SessionType, + ShortAddressDlTdoaRangingMeasurement, ShortAddressOwrAoaRangingMeasurement, + ShortAddressTwoWayRangingMeasurement, StatusCode, UpdateMulticastListAction, +}; +use crate::params::AppConfigParams; +use crate::proto::bindings::{ + AoaResultRequest as ProtoAoaResultRequest, BprfPhrDataRate as ProtoBprfPhrDataRate, + Controlee as ProtoControlee, DeviceRole as ProtoDeviceRole, DeviceState as ProtoDeviceState, + DeviceType as ProtoDeviceType, DlTDoARangingMeasurement as ProtoDlTDoARangingMeasurement, + FiraAppConfigParams as ProtoFiraAppConfigParams, HoppingMode as ProtoHoppingMode, + KeyRotation as ProtoKeyRotation, MacAddressMode as ProtoMacAddressMode, + MacFcsType as ProtoMacFcsType, MultiNodeMode as ProtoMultiNodeMode, + OwrAoaRangingMeasurement as ProtoOwrAoaRangingMeasurement, + OwrAoaStatusCode as ProtoOwrAoaStatusCode, PowerStats as ProtoPowerStats, + PreambleDuration as ProtoPreambleDuration, PrfMode as ProtoPrfMode, + PsduDataRate as ProtoPsduDataRate, RangeDataNtfConfig as ProtoRangeDataNtfConfig, + RangingMeasurementType as ProtoRangingMeasurementType, + RangingRoundControl as ProtoRangingRoundControl, RangingRoundUsage as ProtoRangingRoundUsage, + RangingTimeStruct as ProtoRangingTimeStruct, ReasonCode as ProtoReasonCode, + ResultReportConfig as ProtoResultReportConfig, RframeConfig as ProtoRframeConfig, + ScheduledMode as ProtoScheduledMode, SessionRangeData as ProtoSessionRangeData, + SessionState as ProtoSessionState, SessionType as ProtoSessionType, Status as ProtoStatus, + StatusCode as ProtoStatusCode, StsConfig as ProtoStsConfig, StsLength as ProtoStsLength, + TwoWayRangingMeasurement as ProtoTwoWayRangingMeasurement, + TxAdaptivePayloadPower as ProtoTxAdaptivePayloadPower, UciLoggerMode as ProtoUciLoggerMode, + UpdateMulticastListAction as ProtoUpdateMulticastListAction, UwbChannel as ProtoUwbChannel, +}; +use crate::uci::notification::{RangingMeasurements, SessionRangeData}; +use crate::uci::uci_logger::UciLoggerMode; + +/// Generate the conversion functions between 2 enum types, which field is 1-to-1 mapping. +/// +/// Example: +/// ``` +/// enum EnumA { +/// Value1, +/// Value2, +/// } +/// enum EnumB { +/// Foo, +/// Bar, +/// } +/// // This macro generates `From<EnumA> for EnumB` and `From<EnumB> for EnumA`. +/// uwb_core::enum_mapping! { +/// EnumA => EnumB, +/// Value1 => Foo, +/// Value2 => Bar, +/// } +/// ``` +#[macro_export] +macro_rules! enum_mapping { + ( $enum_a:ty => $enum_b:ty, $( $field_a:ident => $field_b:ident, )+ ) => { + impl From<$enum_a> for $enum_b { + fn from(item: $enum_a) -> $enum_b { + match item { + $( + <$enum_a>::$field_a => <$enum_b>::$field_b, + )* + } + } + } + impl From<$enum_b> for $enum_a { + fn from(item: $enum_b) -> $enum_a { + match item { + $( + <$enum_b>::$field_b => <$enum_a>::$field_a, + )* + } + } + } + }; +} + +impl From<ProtoStatusCode> for StatusCode { + fn from(item: ProtoStatusCode) -> Self { + match item { + ProtoStatusCode::UCI_STATUS_OK => StatusCode::UciStatusOk, + ProtoStatusCode::UCI_STATUS_REJECTED => StatusCode::UciStatusRejected, + ProtoStatusCode::UCI_STATUS_FAILED => StatusCode::UciStatusFailed, + ProtoStatusCode::UCI_STATUS_SYNTAX_ERROR => StatusCode::UciStatusSyntaxError, + ProtoStatusCode::UCI_STATUS_INVALID_PARAM => StatusCode::UciStatusInvalidParam, + ProtoStatusCode::UCI_STATUS_INVALID_RANGE => StatusCode::UciStatusInvalidRange, + ProtoStatusCode::UCI_STATUS_INVALID_MSG_SIZE => StatusCode::UciStatusInvalidMsgSize, + ProtoStatusCode::UCI_STATUS_UNKNOWN_GID => StatusCode::UciStatusUnknownGid, + ProtoStatusCode::UCI_STATUS_UNKNOWN_OID => StatusCode::UciStatusUnknownOid, + ProtoStatusCode::UCI_STATUS_READ_ONLY => StatusCode::UciStatusReadOnly, + ProtoStatusCode::UCI_STATUS_COMMAND_RETRY => StatusCode::UciStatusCommandRetry, + ProtoStatusCode::UCI_STATUS_SESSION_NOT_EXIST => StatusCode::UciStatusSessionNotExist, + ProtoStatusCode::UCI_STATUS_SESSION_DUPLICATE => StatusCode::UciStatusSessionDuplicate, + ProtoStatusCode::UCI_STATUS_SESSION_ACTIVE => StatusCode::UciStatusSessionActive, + ProtoStatusCode::UCI_STATUS_MAX_SESSIONS_EXCEEDED => { + StatusCode::UciStatusMaxSessionsExceeded + } + ProtoStatusCode::UCI_STATUS_SESSION_NOT_CONFIGURED => { + StatusCode::UciStatusSessionNotConfigured + } + ProtoStatusCode::UCI_STATUS_ACTIVE_SESSIONS_ONGOING => { + StatusCode::UciStatusActiveSessionsOngoing + } + ProtoStatusCode::UCI_STATUS_MULTICAST_LIST_FULL => { + StatusCode::UciStatusMulticastListFull + } + ProtoStatusCode::UCI_STATUS_ADDRESS_NOT_FOUND => StatusCode::UciStatusAddressNotFound, + ProtoStatusCode::UCI_STATUS_ADDRESS_ALREADY_PRESENT => { + StatusCode::UciStatusAddressAlreadyPresent + } + ProtoStatusCode::UCI_STATUS_OK_NEGATIVE_DISTANCE_REPORT => { + StatusCode::UciStatusOkNegativeDistanceReport + } + ProtoStatusCode::UCI_STATUS_RANGING_TX_FAILED => StatusCode::UciStatusRangingTxFailed, + ProtoStatusCode::UCI_STATUS_RANGING_RX_TIMEOUT => { + StatusCode::UciStatusRangingRxTimeout + } + ProtoStatusCode::UCI_STATUS_RANGING_RX_PHY_DEC_FAILED => { + StatusCode::UciStatusRangingRxPhyDecFailed + } + ProtoStatusCode::UCI_STATUS_RANGING_RX_PHY_TOA_FAILED => { + StatusCode::UciStatusRangingRxPhyToaFailed + } + ProtoStatusCode::UCI_STATUS_RANGING_RX_PHY_STS_FAILED => { + StatusCode::UciStatusRangingRxPhyStsFailed + } + ProtoStatusCode::UCI_STATUS_RANGING_RX_MAC_DEC_FAILED => { + StatusCode::UciStatusRangingRxMacDecFailed + } + ProtoStatusCode::UCI_STATUS_RANGING_RX_MAC_IE_DEC_FAILED => { + StatusCode::UciStatusRangingRxMacIeDecFailed + } + ProtoStatusCode::UCI_STATUS_RANGING_RX_MAC_IE_MISSING => { + StatusCode::UciStatusRangingRxMacIeMissing + } + ProtoStatusCode::UCI_STATUS_ERROR_ROUND_INDEX_NOT_ACTIVATED => { + StatusCode::UciStatusErrorRoundIndexNotActivated + } + ProtoStatusCode::UCI_STATUS_ERROR_NUMBER_OF_ACTIVE_RANGING_ROUNDS_EXCEEDED => { + StatusCode::UciStatusErrorNumberOfActiveRangingRoundsExceeded + } + ProtoStatusCode::UCI_STATUS_ERROR_ROUND_INDEX_NOT_SET_AS_INITIATOR => { + StatusCode::UciStatusErrorRoundIndexNotSetAsInitiator + } + ProtoStatusCode::UCI_STATUS_ERROR_DL_TDOA_DEVICE_ADDRESS_NOT_MATCHING_IN_REPLY_TIME_LIST => + StatusCode::UciStatusErrorDlTdoaDeviceAddressNotMatchingInReplyTimeList, + ProtoStatusCode::UCI_STATUS_DATA_MAX_TX_PSDU_SIZE_EXCEEDED => { + StatusCode::UciStatusDataMaxTxPsduSizeExceeded + } + ProtoStatusCode::UCI_STATUS_DATA_RX_CRC_ERROR => StatusCode::UciStatusDataRxCrcError, + ProtoStatusCode::UCI_STATUS_ERROR_CCC_SE_BUSY => StatusCode::UciStatusErrorCccSeBusy, + ProtoStatusCode::UCI_STATUS_ERROR_CCC_LIFECYCLE => { + StatusCode::UciStatusErrorCccLifecycle + } + ProtoStatusCode::UCI_STATUS_ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT => { + StatusCode::UciStatusErrorStoppedDueToOtherSessionConflict + } + _ => StatusCode::VendorSpecificStatusCode2, + } + } +} + +impl From<StatusCode> for ProtoStatusCode { + fn from(item: StatusCode) -> Self { + match item { + StatusCode::UciStatusOk => ProtoStatusCode::UCI_STATUS_OK, + StatusCode::UciStatusRejected => ProtoStatusCode::UCI_STATUS_REJECTED, + StatusCode::UciStatusFailed => ProtoStatusCode::UCI_STATUS_FAILED, + StatusCode::UciStatusSyntaxError => ProtoStatusCode::UCI_STATUS_SYNTAX_ERROR, + StatusCode::UciStatusInvalidParam => ProtoStatusCode::UCI_STATUS_INVALID_PARAM, + StatusCode::UciStatusInvalidRange => ProtoStatusCode::UCI_STATUS_INVALID_RANGE, + StatusCode::UciStatusInvalidMsgSize => ProtoStatusCode::UCI_STATUS_INVALID_MSG_SIZE, + StatusCode::UciStatusUnknownGid => ProtoStatusCode::UCI_STATUS_UNKNOWN_GID, + StatusCode::UciStatusUnknownOid => ProtoStatusCode::UCI_STATUS_UNKNOWN_OID, + StatusCode::UciStatusReadOnly => ProtoStatusCode::UCI_STATUS_READ_ONLY, + StatusCode::UciStatusCommandRetry => ProtoStatusCode::UCI_STATUS_COMMAND_RETRY, + StatusCode::UciStatusSessionNotExist => ProtoStatusCode::UCI_STATUS_SESSION_NOT_EXIST, + StatusCode::UciStatusSessionDuplicate => ProtoStatusCode::UCI_STATUS_SESSION_DUPLICATE, + StatusCode::UciStatusSessionActive => ProtoStatusCode::UCI_STATUS_SESSION_ACTIVE, + StatusCode::UciStatusMaxSessionsExceeded => { + ProtoStatusCode::UCI_STATUS_MAX_SESSIONS_EXCEEDED + } + StatusCode::UciStatusSessionNotConfigured => { + ProtoStatusCode::UCI_STATUS_SESSION_NOT_CONFIGURED + } + StatusCode::UciStatusActiveSessionsOngoing => { + ProtoStatusCode::UCI_STATUS_ACTIVE_SESSIONS_ONGOING + } + StatusCode::UciStatusMulticastListFull => { + ProtoStatusCode::UCI_STATUS_MULTICAST_LIST_FULL + } + StatusCode::UciStatusAddressNotFound => { + ProtoStatusCode::UCI_STATUS_ADDRESS_NOT_FOUND + } + StatusCode::UciStatusAddressAlreadyPresent => { + ProtoStatusCode::UCI_STATUS_ADDRESS_ALREADY_PRESENT + } + StatusCode::UciStatusOkNegativeDistanceReport => { + ProtoStatusCode::UCI_STATUS_OK_NEGATIVE_DISTANCE_REPORT + } + StatusCode::UciStatusRangingTxFailed => { + ProtoStatusCode::UCI_STATUS_RANGING_TX_FAILED + } + StatusCode::UciStatusRangingRxTimeout => { + ProtoStatusCode::UCI_STATUS_RANGING_RX_TIMEOUT + } + StatusCode::UciStatusRangingRxPhyDecFailed => { + ProtoStatusCode::UCI_STATUS_RANGING_RX_PHY_DEC_FAILED + } + StatusCode::UciStatusRangingRxPhyToaFailed => { + ProtoStatusCode::UCI_STATUS_RANGING_RX_PHY_TOA_FAILED + } + StatusCode::UciStatusRangingRxPhyStsFailed => { + ProtoStatusCode::UCI_STATUS_RANGING_RX_PHY_STS_FAILED + } + StatusCode::UciStatusRangingRxMacDecFailed => { + ProtoStatusCode::UCI_STATUS_RANGING_RX_MAC_DEC_FAILED + } + StatusCode::UciStatusRangingRxMacIeDecFailed => { + ProtoStatusCode::UCI_STATUS_RANGING_RX_MAC_IE_DEC_FAILED + } + StatusCode::UciStatusRangingRxMacIeMissing => { + ProtoStatusCode::UCI_STATUS_RANGING_RX_MAC_IE_MISSING + } + StatusCode::UciStatusErrorRoundIndexNotActivated => { + ProtoStatusCode::UCI_STATUS_ERROR_ROUND_INDEX_NOT_ACTIVATED + } + StatusCode::UciStatusErrorNumberOfActiveRangingRoundsExceeded => { + ProtoStatusCode::UCI_STATUS_ERROR_NUMBER_OF_ACTIVE_RANGING_ROUNDS_EXCEEDED + } + StatusCode::UciStatusErrorRoundIndexNotSetAsInitiator => { + ProtoStatusCode::UCI_STATUS_ERROR_ROUND_INDEX_NOT_SET_AS_INITIATOR + } + StatusCode::UciStatusErrorDlTdoaDeviceAddressNotMatchingInReplyTimeList => { + ProtoStatusCode::UCI_STATUS_ERROR_DL_TDOA_DEVICE_ADDRESS_NOT_MATCHING_IN_REPLY_TIME_LIST + } + StatusCode::UciStatusDataMaxTxPsduSizeExceeded => { + ProtoStatusCode::UCI_STATUS_DATA_MAX_TX_PSDU_SIZE_EXCEEDED + } + StatusCode::UciStatusDataRxCrcError => { + ProtoStatusCode::UCI_STATUS_DATA_RX_CRC_ERROR + } + StatusCode::UciStatusErrorCccSeBusy => { + ProtoStatusCode::UCI_STATUS_ERROR_CCC_SE_BUSY + } + StatusCode::UciStatusErrorCccLifecycle => { + ProtoStatusCode::UCI_STATUS_ERROR_CCC_LIFECYCLE + } + StatusCode::UciStatusErrorStoppedDueToOtherSessionConflict => { + ProtoStatusCode::UCI_STATUS_ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT + } + _ => ProtoStatusCode::UCI_STATUS_RFU_OR_VENDOR_SPECIFIC, + } + } +} + +enum_mapping! { + ProtoOwrAoaStatusCode => OwrAoaStatusCode, + UCI_STATUS_SUCCESS => UciStatusSuccess, + UCI_STATUS_INTER_FRAME_INTERVAL_TIMEOUT => UciStatusInterFrameIntervalTimeout, +} + +enum_mapping! { + ProtoDeviceState => DeviceState, + DEVICE_STATE_READY => DeviceStateReady, + DEVICE_STATE_ACTIVE => DeviceStateActive, + DEVICE_STATE_ERROR => DeviceStateError, +} + +enum_mapping! { + ProtoSessionState => SessionState, + INIT => SessionStateInit, + DEINIT => SessionStateDeinit, + ACTIVE => SessionStateActive, + IDLE => SessionStateIdle, +} + +impl From<ProtoReasonCode> for ReasonCode { + fn from(item: ProtoReasonCode) -> Self { + match item { + ProtoReasonCode::STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS => { + ReasonCode::StateChangeWithSessionManagementCommands + } + ProtoReasonCode::MAX_RANGING_ROUND_RETRY_COUNT_REACHED => { + ReasonCode::MaxRangingRoundRetryCountReached + } + ProtoReasonCode::MAX_NUMBER_OF_MEASUREMENTS_REACHED => { + ReasonCode::MaxNumberOfMeasurementsReached + } + ProtoReasonCode::SESSION_SUSPENDED_DUE_TO_INBAND_SIGNAL => { + ReasonCode::SessionSuspendedDueToInbandSignal + } + ProtoReasonCode::SESSION_RESUMED_DUE_TO_INBAND_SIGNAL => { + ReasonCode::SessionResumedDueToInbandSignal + } + ProtoReasonCode::SESSION_STOPPED_DUE_TO_INBAND_SIGNAL => { + ReasonCode::SessionStoppedDueToInbandSignal + } + ProtoReasonCode::ERROR_INVALID_UL_TDOA_RANDOM_WINDOW => { + ReasonCode::ErrorInvalidUlTdoaRandomWindow + } + ProtoReasonCode::ERROR_MIN_RFRAMES_PER_RR_NOT_SUPPORTED => { + ReasonCode::ErrorMinRframesPerRrNotSupported + } + ProtoReasonCode::ERROR_TX_DELAY_NOT_SUPPORTED => ReasonCode::ErrorTxDelayNotSupported, + ProtoReasonCode::ERROR_SLOT_LENGTH_NOT_SUPPORTED => { + ReasonCode::ErrorSlotLengthNotSupported + } + ProtoReasonCode::ERROR_INSUFFICIENT_SLOTS_PER_RR => { + ReasonCode::ErrorInsufficientSlotsPerRr + } + ProtoReasonCode::ERROR_MAC_ADDRESS_MODE_NOT_SUPPORTED => { + ReasonCode::ErrorMacAddressModeNotSupported + } + ProtoReasonCode::ERROR_INVALID_RANGING_DURATION => { + ReasonCode::ErrorInvalidRangingDuration + } + ProtoReasonCode::ERROR_INVALID_STS_CONFIG => ReasonCode::ErrorInvalidStsConfig, + ProtoReasonCode::ERROR_INVALID_RFRAME_CONFIG => ReasonCode::ErrorInvalidRframeConfig, + ProtoReasonCode::ERROR_HUS_NOT_ENOUGH_SLOTS => ReasonCode::ErrorHusNotEnoughSlots, + ProtoReasonCode::ERROR_HUS_CFP_PHASE_TOO_SHORT => ReasonCode::ErrorHusCfpPhaseTooShort, + ProtoReasonCode::ERROR_HUS_CAP_PHASE_TOO_SHORT => ReasonCode::ErrorHusCapPhaseTooShort, + ProtoReasonCode::ERROR_HUS_OTHERS => ReasonCode::ErrorHusOthers, + ProtoReasonCode::ERROR_STATUS_SESSION_KEY_NOT_FOUND => { + ReasonCode::ErrorStatusSessionKeyNotFound + } + ProtoReasonCode::ERROR_STATUS_SUB_SESSION_KEY_NOT_FOUND => { + ReasonCode::ErrorStatusSubSessionKeyNotFound + } + ProtoReasonCode::ERROR_INVALID_PREAMBLE_CODE_INDEX => { + ReasonCode::ErrorInvalidPreambleCodeIndex + } + ProtoReasonCode::ERROR_INVALID_SFD_ID => ReasonCode::ErrorInvalidSfdId, + ProtoReasonCode::ERROR_INVALID_PSDU_DATA_RATE => ReasonCode::ErrorInvalidPsduDataRate, + ProtoReasonCode::ERROR_INVALID_PHR_DATA_RATE => ReasonCode::ErrorInvalidPhrDataRate, + ProtoReasonCode::ERROR_INVALID_PREAMBLE_DURATION => { + ReasonCode::ErrorInvalidPreambleDuration + } + ProtoReasonCode::ERROR_INVALID_STS_LENGTH => ReasonCode::ErrorInvalidStsLength, + ProtoReasonCode::ERROR_INVALID_NUM_OF_STS_SEGMENTS => { + ReasonCode::ErrorInvalidNumOfStsSegments + } + ProtoReasonCode::ERROR_INVALID_NUM_OF_CONTROLEES => { + ReasonCode::ErrorInvalidNumOfControlees + } + ProtoReasonCode::ERROR_MAX_RANGING_REPLY_TIME_EXCEEDED => { + ReasonCode::ErrorMaxRangingReplyTimeExceeded + } + ProtoReasonCode::ERROR_INVALID_DST_ADDRESS_LIST => { + ReasonCode::ErrorInvalidDstAddressList + } + ProtoReasonCode::ERROR_INVALID_OR_NOT_FOUND_SUB_SESSION_ID => { + ReasonCode::ErrorInvalidOrNotFoundSubSessionId + } + ProtoReasonCode::ERROR_INVALID_RESULT_REPORT_CONFIG => { + ReasonCode::ErrorInvalidResultReportConfig + } + ProtoReasonCode::ERROR_INVALID_RANGING_ROUND_USAGE => { + ReasonCode::ErrorInvalidRangingRoundUsage + } + ProtoReasonCode::ERROR_INVALID_MULTI_NODE_MODE => ReasonCode::ErrorInvalidMultiNodeMode, + ProtoReasonCode::ERROR_RDS_FETCH_FAILURE => ReasonCode::ErrorRdsFetchFailure, + ProtoReasonCode::ERROR_REF_UWB_SESSION_DOES_NOT_EXIST => { + ReasonCode::ErrorRefUwbSessionDoesNotExist + } + ProtoReasonCode::ERROR_REF_UWB_SESSION_RANGING_DURATION_MISMATCH => { + ReasonCode::ErrorRefUwbSessionRangingDurationMismatch + } + ProtoReasonCode::ERROR_REF_UWB_SESSION_INVALID_OFFSET_TIME => { + ReasonCode::ErrorRefUwbSessionInvalidOffsetTime + } + ProtoReasonCode::ERROR_REF_UWB_SESSION_LOST => ReasonCode::ErrorRefUwbSessionLost, + ProtoReasonCode::ERROR_INVALID_CHANNEL_WITH_AOA => { + ReasonCode::ErrorInvalidChannelWithAoa + } + ProtoReasonCode::ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT => { + ReasonCode::ErrorStoppedDueToOtherSessionConflict + } + _ => ReasonCode::VendorSpecificReasonCode2, + } + } +} + +impl From<ReasonCode> for ProtoReasonCode { + fn from(item: ReasonCode) -> Self { + match item { + ReasonCode::StateChangeWithSessionManagementCommands => { + ProtoReasonCode::STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS + } + ReasonCode::MaxRangingRoundRetryCountReached => { + ProtoReasonCode::MAX_RANGING_ROUND_RETRY_COUNT_REACHED + } + ReasonCode::MaxNumberOfMeasurementsReached => { + ProtoReasonCode::MAX_NUMBER_OF_MEASUREMENTS_REACHED + } + ReasonCode::SessionSuspendedDueToInbandSignal => { + ProtoReasonCode::SESSION_SUSPENDED_DUE_TO_INBAND_SIGNAL + } + ReasonCode::SessionResumedDueToInbandSignal => { + ProtoReasonCode::SESSION_RESUMED_DUE_TO_INBAND_SIGNAL + } + ReasonCode::SessionStoppedDueToInbandSignal => { + ProtoReasonCode::SESSION_STOPPED_DUE_TO_INBAND_SIGNAL + } + ReasonCode::ErrorInvalidUlTdoaRandomWindow => { + ProtoReasonCode::ERROR_INVALID_UL_TDOA_RANDOM_WINDOW + } + ReasonCode::ErrorMinRframesPerRrNotSupported => { + ProtoReasonCode::ERROR_MIN_RFRAMES_PER_RR_NOT_SUPPORTED + } + ReasonCode::ErrorTxDelayNotSupported => ProtoReasonCode::ERROR_TX_DELAY_NOT_SUPPORTED, + ReasonCode::ErrorSlotLengthNotSupported => { + ProtoReasonCode::ERROR_SLOT_LENGTH_NOT_SUPPORTED + } + ReasonCode::ErrorInsufficientSlotsPerRr => { + ProtoReasonCode::ERROR_INSUFFICIENT_SLOTS_PER_RR + } + ReasonCode::ErrorMacAddressModeNotSupported => { + ProtoReasonCode::ERROR_MAC_ADDRESS_MODE_NOT_SUPPORTED + } + ReasonCode::ErrorInvalidRangingDuration => { + ProtoReasonCode::ERROR_INVALID_RANGING_DURATION + } + ReasonCode::ErrorInvalidStsConfig => ProtoReasonCode::ERROR_INVALID_STS_CONFIG, + ReasonCode::ErrorInvalidRframeConfig => ProtoReasonCode::ERROR_INVALID_RFRAME_CONFIG, + ReasonCode::ErrorHusNotEnoughSlots => ProtoReasonCode::ERROR_HUS_NOT_ENOUGH_SLOTS, + ReasonCode::ErrorHusCfpPhaseTooShort => ProtoReasonCode::ERROR_HUS_CFP_PHASE_TOO_SHORT, + ReasonCode::ErrorHusCapPhaseTooShort => ProtoReasonCode::ERROR_HUS_CAP_PHASE_TOO_SHORT, + ReasonCode::ErrorHusOthers => ProtoReasonCode::ERROR_HUS_OTHERS, + ReasonCode::ErrorStatusSessionKeyNotFound => { + ProtoReasonCode::ERROR_STATUS_SESSION_KEY_NOT_FOUND + } + ReasonCode::ErrorStatusSubSessionKeyNotFound => { + ProtoReasonCode::ERROR_STATUS_SUB_SESSION_KEY_NOT_FOUND + } + ReasonCode::ErrorInvalidPreambleCodeIndex => { + ProtoReasonCode::ERROR_INVALID_PREAMBLE_CODE_INDEX + } + ReasonCode::ErrorInvalidSfdId => ProtoReasonCode::ERROR_INVALID_SFD_ID, + ReasonCode::ErrorInvalidPsduDataRate => ProtoReasonCode::ERROR_INVALID_PSDU_DATA_RATE, + ReasonCode::ErrorInvalidPhrDataRate => ProtoReasonCode::ERROR_INVALID_PHR_DATA_RATE, + ReasonCode::ErrorInvalidPreambleDuration => { + ProtoReasonCode::ERROR_INVALID_PREAMBLE_DURATION + } + ReasonCode::ErrorInvalidStsLength => ProtoReasonCode::ERROR_INVALID_STS_LENGTH, + ReasonCode::ErrorInvalidNumOfStsSegments => { + ProtoReasonCode::ERROR_INVALID_NUM_OF_STS_SEGMENTS + } + ReasonCode::ErrorInvalidNumOfControlees => { + ProtoReasonCode::ERROR_INVALID_NUM_OF_CONTROLEES + } + ReasonCode::ErrorMaxRangingReplyTimeExceeded => { + ProtoReasonCode::ERROR_MAX_RANGING_REPLY_TIME_EXCEEDED + } + ReasonCode::ErrorInvalidDstAddressList => { + ProtoReasonCode::ERROR_INVALID_DST_ADDRESS_LIST + } + ReasonCode::ErrorInvalidOrNotFoundSubSessionId => { + ProtoReasonCode::ERROR_INVALID_OR_NOT_FOUND_SUB_SESSION_ID + } + ReasonCode::ErrorInvalidResultReportConfig => { + ProtoReasonCode::ERROR_INVALID_RESULT_REPORT_CONFIG + } + ReasonCode::ErrorInvalidRangingRoundUsage => { + ProtoReasonCode::ERROR_INVALID_RANGING_ROUND_USAGE + } + ReasonCode::ErrorInvalidMultiNodeMode => ProtoReasonCode::ERROR_INVALID_MULTI_NODE_MODE, + ReasonCode::ErrorRdsFetchFailure => ProtoReasonCode::ERROR_RDS_FETCH_FAILURE, + ReasonCode::ErrorRefUwbSessionDoesNotExist => { + ProtoReasonCode::ERROR_REF_UWB_SESSION_DOES_NOT_EXIST + } + ReasonCode::ErrorRefUwbSessionRangingDurationMismatch => { + ProtoReasonCode::ERROR_REF_UWB_SESSION_RANGING_DURATION_MISMATCH + } + ReasonCode::ErrorRefUwbSessionInvalidOffsetTime => { + ProtoReasonCode::ERROR_REF_UWB_SESSION_INVALID_OFFSET_TIME + } + ReasonCode::ErrorRefUwbSessionLost => ProtoReasonCode::ERROR_REF_UWB_SESSION_LOST, + ReasonCode::ErrorInvalidChannelWithAoa => { + ProtoReasonCode::ERROR_INVALID_CHANNEL_WITH_AOA + } + ReasonCode::ErrorStoppedDueToOtherSessionConflict => { + ProtoReasonCode::ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT + } + ReasonCode::ErrorDtAnchorRangingRoundsNotConfigured => { + ProtoReasonCode::ERROR_DT_ANCHOR_RANGING_ROUNDS_NOT_CONFIGURED + } + ReasonCode::ErrorDtTagRangingRoundsNotConfigured => { + ProtoReasonCode::ERROR_DT_TAG_RANGING_ROUNDS_NOT_CONFIGURED + } + _ => ProtoReasonCode::ERROR_RFU_OR_VENDOR_SPECIFIC, + } + } +} + +enum_mapping! { + ProtoUciLoggerMode => UciLoggerMode, + UCI_LOGGER_MODE_DISABLED => Disabled, + UCI_LOGGER_MODE_UNFILTERED => Unfiltered, + UCI_LOGGER_MODE_FILTERED => Filtered, +} + +enum_mapping! { + ProtoRangingMeasurementType => RangingMeasurementType, + ONE_WAY => OneWay, + TWO_WAY => TwoWay, + DL_TDOA => DlTdoa, + OWR_AOA => OwrAoa, +} + +enum_mapping! { + ProtoSessionType => SessionType, + FIRA_RANGING_SESSION => FiraRangingSession, + FIRA_DATA_TRANSFER => FiraDataTransfer, + CCC => Ccc, +} + +enum_mapping! { + ProtoDeviceType => DeviceType, + CONTROLEE => Controlee, + CONTROLLER => Controller, +} + +enum_mapping! { + ProtoRangingRoundUsage => RangingRoundUsage, + SS_TWR => SsTwr, + DS_TWR => DsTwr, + SS_TWR_NON => SsTwrNon, + DS_TWR_NON => DsTwrNon, +} + +enum_mapping! { + ProtoStsConfig => StsConfig, + STATIC => Static, + DYNAMIC => Dynamic, + DYNAMIC_FOR_CONTROLEE_INDIVIDUAL_KEY => DynamicForControleeIndividualKey, +} + +enum_mapping! { + ProtoMultiNodeMode => MultiNodeMode, + UNICAST => Unicast, + ONE_TO_MANY => OneToMany, + MANY_TO_MANY => ManyToMany, +} + +enum_mapping! { + ProtoUwbChannel => UwbChannel, + CHANNEL_5 => Channel5, + CHANNEL_6 => Channel6, + CHANNEL_8 => Channel8, + CHANNEL_9 => Channel9, + CHANNEL_10 => Channel10, + CHANNEL_12 => Channel12, + CHANNEL_13 => Channel13, + CHANNEL_14 => Channel14, +} + +enum_mapping! { + ProtoMacFcsType => MacFcsType, + CRC_16 => Crc16, + CRC_32 => Crc32, +} + +enum_mapping! { + ProtoAoaResultRequest => AoaResultRequest, + NO_AOA_REPORT => NoAoaReport, + REQ_AOA_RESULTS => ReqAoaResults, + REQ_AOA_RESULTS_AZIMUTH_ONLY => ReqAoaResultsAzimuthOnly, + REQ_AOA_RESULTS_ELEVATION_ONLY => ReqAoaResultsElevationOnly, + REQ_AOA_RESULTS_INTERLEAVED => ReqAoaResultsInterleaved, +} + +enum_mapping! { + ProtoRangeDataNtfConfig => RangeDataNtfConfig, + RANGE_DATA_NTF_CONFIG_DISABLE => Disable, + RANGE_DATA_NTF_CONFIG_ENABLE => Enable, + RANGE_DATA_NTF_CONFIG_ENABLE_PROXIMITY => EnableProximity, +} + +enum_mapping! { + ProtoDeviceRole => DeviceRole, + RESPONDER => Responder, + INITIATOR => Initiator, +} + +enum_mapping! { + ProtoRframeConfig => RframeConfig, + SP0 => SP0, + SP1 => SP1, + SP3 => SP3, +} + +enum_mapping! { + ProtoPsduDataRate => PsduDataRate, + RATE_6M_81 => Rate6m81, + RATE_7M_80 => Rate7m80, + RATE_27M_2 => Rate27m2, + RATE_31M_2 => Rate31m2, + RATE_850K => Rate850k, +} + +enum_mapping! { + ProtoPreambleDuration => PreambleDuration, + T32_SYMBOLS => T32Symbols, + T64_SYMBOLS => T64Symbols, +} + +enum_mapping! { + ProtoRangingTimeStruct => RangingTimeStruct, + INTERVAL_BASED_SCHEDULING => IntervalBasedScheduling, + BLOCK_BASED_SCHEDULING => BlockBasedScheduling, +} + +enum_mapping! { + ProtoTxAdaptivePayloadPower => TxAdaptivePayloadPower, + TX_ADAPTIVE_PAYLOAD_POWER_DISABLE => Disable, + TX_ADAPTIVE_PAYLOAD_POWER_ENABLE => Enable, +} + +enum_mapping! { + ProtoPrfMode => PrfMode, + BPRF => Bprf, + HPRF_WITH_124_8_MHZ => HprfWith124_8MHz, + HPRF_WITH_249_6_MHZ => HprfWith249_6MHz, +} + +enum_mapping! { + ProtoScheduledMode => ScheduledMode, + TIME_SCHEDULED_RANGING => TimeScheduledRanging, +} + +enum_mapping! { + ProtoKeyRotation => KeyRotation, + KEY_ROTATION_DISABLE => Disable, + KEY_ROTATION_ENABLE => Enable, +} + +enum_mapping! { + ProtoMacAddressMode => MacAddressMode, + MAC_ADDRESS_2_BYTES => MacAddress2Bytes, + MAC_ADDRESS_8_BYTES_2_BYTES_HEADER => MacAddress8Bytes2BytesHeader, + MAC_ADDRESS_8_BYTES => MacAddress8Bytes, +} + +enum_mapping! { + ProtoHoppingMode => HoppingMode, + HOPPING_MODE_DISABLE => Disable, + FIRA_HOPPING_ENABLE => FiraHoppingEnable, +} + +enum_mapping! { + ProtoBprfPhrDataRate => BprfPhrDataRate, + BPRF_PHR_DATA_RATE_850K => Rate850k, + BPRF_PHR_DATA_RATE_6M_81 => Rate6m81, +} + +enum_mapping! { + ProtoStsLength => StsLength, + LENGTH_32 => Length32, + LENGTH_64 => Length64, + LENGTH_128 => Length128, +} + +enum_mapping! { + ProtoUpdateMulticastListAction => UpdateMulticastListAction, + ADD_CONTROLEE => AddControlee, + REMOVE_CONTROLEE => RemoveControlee, + ADD_CONTROLEE_WITH_SHORT_SUB_SESSION_KEY => AddControleeWithShortSubSessionKey, + ADD_CONTROLEE_WITH_LONG_SUB_SESSION_KEY => AddControleeWithLongSubSessionKey, +} + +pub enum ProtoRangingMeasurements { + TwoWay(Vec<ProtoTwoWayRangingMeasurement>), + OwrAoa(ProtoOwrAoaRangingMeasurement), + DlTDoa(Vec<ProtoDlTDoARangingMeasurement>), +} + +impl<T> From<Result<T>> for ProtoStatus { + fn from(item: Result<T>) -> Self { + match item { + Ok(_) => Self::OK, + Err(Error::BadParameters) => Self::BAD_PARAMETERS, + Err(Error::MaxSessionsExceeded) => Self::MAX_SESSIONS_EXCEEDED, + Err(Error::MaxRrRetryReached) => Self::MAX_RR_RETRY_REACHED, + Err(Error::ProtocolSpecific) => Self::PROTOCOL_SPECIFIC, + Err(Error::RemoteRequest) => Self::REMOTE_REQUEST, + Err(Error::Timeout) => Self::TIMEOUT, + Err(Error::CommandRetry) => Self::COMMAND_RETRY, + Err(Error::DuplicatedSessionId) => Self::DUPLICATED_SESSION_ID, + Err(_) => Self::UNKNOWN, + } + } +} + +impl From<ShortAddressTwoWayRangingMeasurement> for ProtoTwoWayRangingMeasurement { + fn from(item: ShortAddressTwoWayRangingMeasurement) -> Self { + let mut result = Self::new(); + result.set_mac_address(item.mac_address.into()); + result.set_status(item.status.into()); + result.set_nlos(item.nlos.into()); + result.set_distance(item.distance.into()); + result.set_aoa_azimuth(item.aoa_azimuth.into()); + result.set_aoa_azimuth_fom(item.aoa_azimuth_fom.into()); + result.set_aoa_elevation(item.aoa_elevation.into()); + result.set_aoa_elevation_fom(item.aoa_elevation_fom.into()); + result.set_aoa_destination_azimuth(item.aoa_destination_azimuth.into()); + result.set_aoa_destination_azimuth_fom(item.aoa_destination_azimuth_fom.into()); + result.set_aoa_destination_elevation(item.aoa_destination_elevation.into()); + result.set_aoa_destination_elevation_fom(item.aoa_destination_elevation_fom.into()); + result.set_slot_index(item.slot_index.into()); + result.set_rssi(item.rssi.into()); + result + } +} + +impl From<ExtendedAddressTwoWayRangingMeasurement> for ProtoTwoWayRangingMeasurement { + fn from(item: ExtendedAddressTwoWayRangingMeasurement) -> Self { + let mut result = Self::new(); + result.set_mac_address(item.mac_address); + result.set_status(item.status.into()); + result.set_nlos(item.nlos.into()); + result.set_distance(item.distance.into()); + result.set_aoa_azimuth(item.aoa_azimuth.into()); + result.set_aoa_azimuth_fom(item.aoa_azimuth_fom.into()); + result.set_aoa_elevation(item.aoa_elevation.into()); + result.set_aoa_elevation_fom(item.aoa_elevation_fom.into()); + result.set_aoa_destination_azimuth(item.aoa_destination_azimuth.into()); + result.set_aoa_destination_azimuth_fom(item.aoa_destination_azimuth_fom.into()); + result.set_aoa_destination_elevation(item.aoa_destination_elevation.into()); + result.set_aoa_destination_elevation_fom(item.aoa_destination_elevation_fom.into()); + result.set_slot_index(item.slot_index.into()); + result.set_rssi(item.rssi.into()); + result + } +} + +impl From<ShortAddressOwrAoaRangingMeasurement> for ProtoOwrAoaRangingMeasurement { + fn from(item: ShortAddressOwrAoaRangingMeasurement) -> Self { + let mut result = Self::new(); + result.set_mac_address(item.mac_address.into()); + result.set_owr_aoa_status_code(item.status.into()); + result.set_nlos(item.nlos.into()); + result.set_block_index(item.block_index.into()); + result.set_frame_sequence_number(item.frame_sequence_number.into()); + result.set_aoa_azimuth(item.aoa_azimuth.into()); + result.set_aoa_azimuth_fom(item.aoa_azimuth_fom.into()); + result.set_aoa_elevation(item.aoa_elevation.into()); + result.set_aoa_elevation_fom(item.aoa_elevation_fom.into()); + result + } +} + +impl From<ExtendedAddressOwrAoaRangingMeasurement> for ProtoOwrAoaRangingMeasurement { + fn from(item: ExtendedAddressOwrAoaRangingMeasurement) -> Self { + let mut result = Self::new(); + result.set_mac_address(item.mac_address); + result.set_owr_aoa_status_code(item.status.into()); + result.set_nlos(item.nlos.into()); + result.set_block_index(item.block_index.into()); + result.set_frame_sequence_number(item.frame_sequence_number.into()); + result.set_aoa_azimuth(item.aoa_azimuth.into()); + result.set_aoa_azimuth_fom(item.aoa_azimuth_fom.into()); + result.set_aoa_elevation(item.aoa_elevation.into()); + result.set_aoa_elevation_fom(item.aoa_elevation_fom.into()); + result + } +} + +impl From<ShortAddressDlTdoaRangingMeasurement> for ProtoDlTDoARangingMeasurement { + fn from(item: ShortAddressDlTdoaRangingMeasurement) -> Self { + let mut result = Self::new(); + result.set_mac_address(item.mac_address.into()); + result.set_status( + StatusCode::try_from(item.measurement.status) + .unwrap_or(StatusCode::UciStatusFailed) + .into(), + ); + result.set_message_control(item.measurement.message_control.into()); + result.set_block_index(item.measurement.block_index.into()); + result.set_round_index(item.measurement.round_index.into()); + result.set_nlos(item.measurement.nlos.into()); + result.set_aoa_azimuth(item.measurement.aoa_azimuth.into()); + result.set_aoa_azimuth_fom(item.measurement.aoa_azimuth_fom.into()); + result.set_aoa_elevation(item.measurement.aoa_elevation.into()); + result.set_aoa_elevation_fom(item.measurement.aoa_elevation_fom.into()); + result.set_rssi(item.measurement.rssi.into()); + result.set_tx_timestamp(item.measurement.tx_timestamp); + result.set_rx_timestamp(item.measurement.rx_timestamp); + result.set_anchor_cfo(item.measurement.anchor_cfo.into()); + result.set_cfo(item.measurement.cfo.into()); + result.set_initiator_reply_time(item.measurement.initiator_reply_time); + result.set_responder_reply_time(item.measurement.responder_reply_time); + result.set_initiator_responder_tof(item.measurement.initiator_responder_tof.into()); + result.set_dt_anchor_location( + item.measurement + .dt_anchor_location + .into_iter() + .map(|val| val as u32) + .collect::<Vec<u32>>(), + ); + result.set_ranging_rounds( + item.measurement.ranging_rounds.into_iter().map(|val| val as u32).collect::<Vec<u32>>(), + ); + result + } +} + +impl From<ExtendedAddressDlTdoaRangingMeasurement> for ProtoDlTDoARangingMeasurement { + fn from(item: ExtendedAddressDlTdoaRangingMeasurement) -> Self { + let mut result = Self::new(); + result.set_mac_address(item.mac_address); + result.set_status( + StatusCode::try_from(item.measurement.status) + .unwrap_or(StatusCode::UciStatusFailed) + .into(), + ); + result.set_message_control(item.measurement.message_control.into()); + result.set_block_index(item.measurement.block_index.into()); + result.set_round_index(item.measurement.round_index.into()); + result.set_nlos(item.measurement.nlos.into()); + result.set_aoa_azimuth(item.measurement.aoa_azimuth.into()); + result.set_aoa_azimuth_fom(item.measurement.aoa_azimuth_fom.into()); + result.set_aoa_elevation(item.measurement.aoa_elevation.into()); + result.set_aoa_elevation_fom(item.measurement.aoa_elevation_fom.into()); + result.set_rssi(item.measurement.rssi.into()); + result.set_tx_timestamp(item.measurement.tx_timestamp); + result.set_rx_timestamp(item.measurement.rx_timestamp); + result.set_anchor_cfo(item.measurement.anchor_cfo.into()); + result.set_cfo(item.measurement.cfo.into()); + result.set_initiator_reply_time(item.measurement.initiator_reply_time); + result.set_responder_reply_time(item.measurement.responder_reply_time); + result.set_initiator_responder_tof(item.measurement.initiator_responder_tof.into()); + result.set_dt_anchor_location( + item.measurement + .dt_anchor_location + .into_iter() + .map(|val| val as u32) + .collect::<Vec<u32>>(), + ); + result.set_ranging_rounds( + item.measurement.ranging_rounds.into_iter().map(|val| val as u32).collect::<Vec<u32>>(), + ); + result + } +} + +impl From<SessionRangeData> for ProtoSessionRangeData { + fn from(item: SessionRangeData) -> Self { + let mut result = Self::new(); + result.set_sequence_number(item.sequence_number); + result.set_session_id(item.session_id); + result.set_current_ranging_interval_ms(item.current_ranging_interval_ms); + result.set_ranging_measurement_type(item.ranging_measurement_type.into()); + match to_proto_ranging_measurements(item.ranging_measurements) { + ProtoRangingMeasurements::TwoWay(twoway_measurements) => { + result.set_twoway_ranging_measurements(RepeatedField::from_vec(twoway_measurements)) + } + ProtoRangingMeasurements::OwrAoa(owraoa_measurement) => { + result.set_owraoa_ranging_measurement(owraoa_measurement) + } + ProtoRangingMeasurements::DlTDoa(dltdoa_measurements) => { + result.set_dltdoa_ranging_measurements(RepeatedField::from_vec(dltdoa_measurements)) + } + } + result + } +} + +fn to_proto_ranging_measurements(item: RangingMeasurements) -> ProtoRangingMeasurements { + match item { + RangingMeasurements::ShortAddressTwoWay(arr) => { + ProtoRangingMeasurements::TwoWay(arr.into_iter().map(|item| item.into()).collect()) + } + RangingMeasurements::ExtendedAddressTwoWay(arr) => { + ProtoRangingMeasurements::TwoWay(arr.into_iter().map(|item| item.into()).collect()) + } + RangingMeasurements::ShortAddressOwrAoa(r) => ProtoRangingMeasurements::OwrAoa(r.into()), + RangingMeasurements::ExtendedAddressOwrAoa(r) => ProtoRangingMeasurements::OwrAoa(r.into()), + RangingMeasurements::ShortAddressDltdoa(arr) => { + ProtoRangingMeasurements::DlTDoa(arr.into_iter().map(|item| item.into()).collect()) + } + RangingMeasurements::ExtendedAddressDltdoa(arr) => { + ProtoRangingMeasurements::DlTDoa(arr.into_iter().map(|item| item.into()).collect()) + } + } +} + +impl From<ProtoRangingRoundControl> for RangingRoundControl { + fn from(item: ProtoRangingRoundControl) -> Self { + Self { + ranging_result_report_message: item.ranging_result_report_message, + control_message: item.control_message, + measurement_report_message: item.measurement_report_message, + } + } +} + +impl From<RangingRoundControl> for ProtoRangingRoundControl { + fn from(item: RangingRoundControl) -> Self { + let mut res = Self::new(); + res.set_ranging_result_report_message(item.ranging_result_report_message); + res.set_control_message(item.control_message); + res.set_measurement_report_message(item.measurement_report_message); + res + } +} + +impl From<ProtoResultReportConfig> for ResultReportConfig { + fn from(item: ProtoResultReportConfig) -> Self { + Self { + tof: item.tof, + aoa_azimuth: item.aoa_azimuth, + aoa_elevation: item.aoa_elevation, + aoa_fom: item.aoa_fom, + } + } +} + +impl From<ResultReportConfig> for ProtoResultReportConfig { + fn from(item: ResultReportConfig) -> Self { + let mut res = Self::new(); + res.set_tof(item.tof); + res.set_aoa_azimuth(item.aoa_azimuth); + res.set_aoa_elevation(item.aoa_elevation); + res.set_aoa_fom(item.aoa_fom); + res + } +} + +fn to_uwb_address(bytes: Vec<u8>, mode: ProtoMacAddressMode) -> Option<UwbAddress> { + match mode { + ProtoMacAddressMode::MAC_ADDRESS_2_BYTES + | ProtoMacAddressMode::MAC_ADDRESS_8_BYTES_2_BYTES_HEADER => { + Some(UwbAddress::Short(bytes.try_into().ok()?)) + } + ProtoMacAddressMode::MAC_ADDRESS_8_BYTES => { + Some(UwbAddress::Extended(bytes.try_into().ok()?)) + } + } +} + +impl TryFrom<ProtoControlee> for Controlee { + type Error = String; + fn try_from(item: ProtoControlee) -> std::result::Result<Self, Self::Error> { + Ok(Self { + short_address: item + .short_address + .try_into() + .map_err(|_| "Failed to convert short_address")?, + subsession_id: item.subsession_id, + }) + } +} + +impl From<PowerStats> for ProtoPowerStats { + fn from(item: PowerStats) -> Self { + let mut res = Self::new(); + res.set_status(item.status.into()); + res.set_idle_time_ms(item.idle_time_ms); + res.set_tx_time_ms(item.tx_time_ms); + res.set_rx_time_ms(item.rx_time_ms); + res.set_total_wake_count(item.total_wake_count); + res + } +} + +impl From<FiraAppConfigParams> for ProtoFiraAppConfigParams { + fn from(item: FiraAppConfigParams) -> Self { + let mut res = Self::new(); + res.set_device_type((*item.device_type()).into()); + res.set_ranging_round_usage((*item.ranging_round_usage()).into()); + res.set_sts_config((*item.sts_config()).into()); + res.set_multi_node_mode((*item.multi_node_mode()).into()); + res.set_channel_number((*item.channel_number()).into()); + res.set_device_mac_address(item.device_mac_address().clone().into()); + res.set_dst_mac_address( + item.dst_mac_address() + .clone() + .into_iter() + .map(|addr| addr.into()) + .collect::<Vec<_>>() + .into(), + ); + res.set_slot_duration_rstu((*item.slot_duration_rstu()).into()); + res.set_ranging_interval_ms(*item.ranging_interval_ms()); + res.set_mac_fcs_type((*item.mac_fcs_type()).into()); + res.set_ranging_round_control(item.ranging_round_control().clone().into()); + res.set_aoa_result_request((*item.aoa_result_request()).into()); + res.set_range_data_ntf_config((*item.range_data_ntf_config()).into()); + res.set_range_data_ntf_proximity_near_cm((*item.range_data_ntf_proximity_near_cm()).into()); + res.set_range_data_ntf_proximity_far_cm((*item.range_data_ntf_proximity_far_cm()).into()); + res.set_device_role((*item.device_role()).into()); + res.set_rframe_config((*item.rframe_config()).into()); + res.set_preamble_code_index((*item.preamble_code_index()).into()); + res.set_sfd_id((*item.sfd_id()).into()); + res.set_psdu_data_rate((*item.psdu_data_rate()).into()); + res.set_preamble_duration((*item.preamble_duration()).into()); + res.set_ranging_time_struct((*item.ranging_time_struct()).into()); + res.set_slots_per_rr((*item.slots_per_rr()).into()); + res.set_tx_adaptive_payload_power((*item.tx_adaptive_payload_power()).into()); + res.set_responder_slot_index((*item.responder_slot_index()).into()); + res.set_prf_mode((*item.prf_mode()).into()); + res.set_scheduled_mode((*item.scheduled_mode()).into()); + res.set_key_rotation((*item.key_rotation()).into()); + res.set_key_rotation_rate((*item.key_rotation_rate()).into()); + res.set_session_priority((*item.session_priority()).into()); + res.set_mac_address_mode((*item.mac_address_mode()).into()); + res.set_vendor_id((*item.vendor_id()).into()); + res.set_static_sts_iv((*item.static_sts_iv()).into()); + res.set_number_of_sts_segments((*item.number_of_sts_segments()).into()); + res.set_max_rr_retry((*item.max_rr_retry()).into()); + res.set_uwb_initiation_time_ms(*item.uwb_initiation_time_ms()); + res.set_hopping_mode((*item.hopping_mode()).into()); + res.set_block_stride_length((*item.block_stride_length()).into()); + res.set_result_report_config(item.result_report_config().clone().into()); + res.set_in_band_termination_attempt_count( + (*item.in_band_termination_attempt_count()).into(), + ); + res.set_sub_session_id(*item.sub_session_id()); + res.set_bprf_phr_data_rate((*item.bprf_phr_data_rate()).into()); + res.set_max_number_of_measurements((*item.max_number_of_measurements()).into()); + res.set_sts_length((*item.sts_length()).into()); + res.set_number_of_range_measurements((*item.number_of_range_measurements()).into()); + res.set_number_of_aoa_azimuth_measurements( + (*item.number_of_aoa_azimuth_measurements()).into(), + ); + res.set_number_of_aoa_elevation_measurements( + (*item.number_of_aoa_elevation_measurements()).into(), + ); + + res + } +} + +impl TryFrom<ProtoFiraAppConfigParams> for AppConfigParams { + type Error = String; + fn try_from(mut item: ProtoFiraAppConfigParams) -> std::result::Result<Self, Self::Error> { + let device_mac_address = + to_uwb_address(item.device_mac_address.clone(), item.mac_address_mode) + .ok_or("Failed to convert device_mac_address")?; + let mut dst_mac_address = vec![]; + for addr in item.dst_mac_address.clone().into_iter() { + let addr = to_uwb_address(addr, item.mac_address_mode) + .ok_or("Failed to convert dst_mac_address")?; + dst_mac_address.push(addr); + } + + let mut builder = FiraAppConfigParamsBuilder::new(); + builder + .device_type(item.device_type.into()) + .ranging_round_usage(item.ranging_round_usage.into()) + .sts_config(item.sts_config.into()) + .multi_node_mode(item.multi_node_mode.into()) + .channel_number(item.channel_number.into()) + .device_mac_address(device_mac_address) + .dst_mac_address(dst_mac_address) + .slot_duration_rstu( + item.slot_duration_rstu + .try_into() + .map_err(|_| "Failed to convert slot_duration_rstu")?, + ) + .ranging_interval_ms(item.ranging_interval_ms) + .mac_fcs_type(item.mac_fcs_type.into()) + .ranging_round_control( + item.ranging_round_control.take().ok_or("ranging_round_control is empty")?.into(), + ) + .aoa_result_request(item.aoa_result_request.into()) + .range_data_ntf_config(item.range_data_ntf_config.into()) + .range_data_ntf_proximity_near_cm( + item.range_data_ntf_proximity_near_cm + .try_into() + .map_err(|_| "Failed to convert range_data_ntf_proximity_near_cm")?, + ) + .range_data_ntf_proximity_far_cm( + item.range_data_ntf_proximity_far_cm + .try_into() + .map_err(|_| "Failed to convert range_data_ntf_proximity_far_cm")?, + ) + .device_role(item.device_role.into()) + .rframe_config(item.rframe_config.into()) + .preamble_code_index( + item.preamble_code_index + .try_into() + .map_err(|_| "Failed to convert preamble_code_index")?, + ) + .sfd_id(item.sfd_id.try_into().map_err(|_| "Failed to convert sfd_id")?) + .psdu_data_rate(item.psdu_data_rate.into()) + .preamble_duration(item.preamble_duration.into()) + .ranging_time_struct(item.ranging_time_struct.into()) + .slots_per_rr( + item.slots_per_rr.try_into().map_err(|_| "Failed to convert slots_per_rr")?, + ) + .tx_adaptive_payload_power(item.tx_adaptive_payload_power.into()) + .responder_slot_index( + item.responder_slot_index + .try_into() + .map_err(|_| "Failed to convert responder_slot_index")?, + ) + .prf_mode(item.prf_mode.into()) + .scheduled_mode(item.scheduled_mode.into()) + .key_rotation(item.key_rotation.into()) + .key_rotation_rate( + item.key_rotation_rate + .try_into() + .map_err(|_| "Failed to convert key_rotation_rate")?, + ) + .session_priority( + item.session_priority + .try_into() + .map_err(|_| "Failed to convert session_priority")?, + ) + .mac_address_mode(item.mac_address_mode.into()) + .vendor_id( + item.vendor_id.clone().try_into().map_err(|_| "Failed to convert vendor_id")?, + ) + .static_sts_iv( + item.static_sts_iv + .clone() + .try_into() + .map_err(|_| "Failed to convert static_sts_iv")?, + ) + .number_of_sts_segments( + item.number_of_sts_segments + .try_into() + .map_err(|_| "Failed to convert number_of_sts_segments")?, + ) + .max_rr_retry( + item.max_rr_retry.try_into().map_err(|_| "Failed to convert max_rr_retry")?, + ) + .uwb_initiation_time_ms(item.uwb_initiation_time_ms) + .hopping_mode(item.hopping_mode.into()) + .block_stride_length( + item.block_stride_length + .try_into() + .map_err(|_| "Failed to convert block_stride_length")?, + ) + .result_report_config( + item.result_report_config.take().ok_or("ranging_round_control is empty")?.into(), + ) + .in_band_termination_attempt_count( + item.in_band_termination_attempt_count + .try_into() + .map_err(|_| "Failed to convert in_band_termination_attempt_count")?, + ) + .sub_session_id(item.sub_session_id) + .bprf_phr_data_rate(item.bprf_phr_data_rate.into()) + .max_number_of_measurements( + item.max_number_of_measurements + .try_into() + .map_err(|_| "Failed to convert max_number_of_measurements")?, + ) + .sts_length(item.sts_length.into()) + .number_of_range_measurements( + item.number_of_range_measurements + .try_into() + .map_err(|_| "Failed to convert number_of_range_measurements")?, + ) + .number_of_aoa_azimuth_measurements( + item.number_of_aoa_azimuth_measurements + .try_into() + .map_err(|_| "Failed to convert number_of_aoa_azimuth_measurements")?, + ) + .number_of_aoa_elevation_measurements( + item.number_of_aoa_elevation_measurements + .try_into() + .map_err(|_| "Failed to convert number_of_aoa_elevation_measurements")?, + ); + + Ok(builder.build().ok_or("Failed to build FiraAppConfigParam from builder")?) + } +} + +impl Drop for ProtoFiraAppConfigParams { + fn drop(&mut self) { + // Zero out the sensitive data before releasing memory. + self.vendor_id.zeroize(); + self.static_sts_iv.zeroize(); + self.sub_session_id.zeroize(); + } +} diff --git a/src/rust/uwb_core/src/proto/utils.rs b/src/rust/uwb_core/src/proto/utils.rs new file mode 100644 index 0000000..8f73e20 --- /dev/null +++ b/src/rust/uwb_core/src/proto/utils.rs @@ -0,0 +1,36 @@ +// Copyright 2022, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Provide the conversion between the elements of uwb_core and protobuf. + +use log::error; +use protobuf::Message; + +use crate::error::{Error, Result}; + +/// Convert the protobuf message to a byte buffers. Return dbus::MethodErr when conversion fails. +pub fn write_to_bytes<M: Message>(msg: &M) -> Result<Vec<u8>> { + msg.write_to_bytes().map_err(|e| { + error!("Failed to write protobuf {} to bytes: {:?}", msg.descriptor().name(), e); + Error::Unknown + }) +} + +/// Parse the byte buffer to the protobuf message. Return dbus::MethodErr when failed to parse. +pub fn parse_from_bytes<M: Message>(bytes: &[u8]) -> Result<M> { + M::parse_from_bytes(bytes).map_err(|e| { + error!("Failed to parse {:?}: {:?}", M::descriptor_static().name(), e); + Error::BadParameters + }) +} diff --git a/src/rust/uwb_core/src/service.rs b/src/rust/uwb_core/src/service.rs index 2371bfe..979cad2 100644 --- a/src/rust/uwb_core/src/service.rs +++ b/src/rust/uwb_core/src/service.rs @@ -14,6 +14,8 @@ //! This module provides the public interface of the UWB core library. +#[cfg(feature = "proto")] +pub mod proto_uwb_service; pub mod uwb_service; pub mod uwb_service_builder; pub mod uwb_service_callback_builder; @@ -22,6 +24,10 @@ pub mod uwb_service_callback_builder; mod mock_uwb_service_callback; // Re-export the public elements. -pub use uwb_service::{UwbService, UwbServiceCallback, UwbServiceCallbackBuilder}; +#[cfg(feature = "proto")] +pub use proto_uwb_service::{ProtoUwbService, ProtoUwbServiceCallback}; +pub use uwb_service::{ + NopUwbServiceCallback, UwbService, UwbServiceCallback, UwbServiceCallbackBuilder, +}; pub use uwb_service_builder::{default_runtime, UwbServiceBuilder}; pub use uwb_service_callback_builder::UwbServiceCallbackSendBuilder; diff --git a/src/rust/uwb_core/src/service/proto_uwb_service.rs b/src/rust/uwb_core/src/service/proto_uwb_service.rs new file mode 100644 index 0000000..747865a --- /dev/null +++ b/src/rust/uwb_core/src/service/proto_uwb_service.rs @@ -0,0 +1,325 @@ +// Copyright 2022, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This module provides a thin adapter of UwbService and UwbServiceCallback that encodes the +//! arguments to protobuf. + +use log::{debug, error}; + +use crate::error::{Error, Result}; +use crate::params::{AppConfigParams, DeviceState, ReasonCode, SessionId, SessionState}; +use crate::proto::bindings::{ + AndroidGetPowerStatsResponse, AndroidSetCountryCodeRequest, AndroidSetCountryCodeResponse, + DeinitSessionRequest, DeinitSessionResponse, DisableResponse, EnableResponse, + InitSessionRequest, InitSessionResponse, RangeDataReceivedSignal, ReconfigureRequest, + ReconfigureResponse, SendVendorCmdRequest, SendVendorCmdResponse, ServiceResetSignal, + SessionParamsRequest, SessionParamsResponse, SessionStateChangedSignal, SetLoggerModeRequest, + SetLoggerModeResponse, StartRangingRequest, StartRangingResponse, Status as ProtoStatus, + StopRangingRequest, StopRangingResponse, UciDeviceStatusChangedSignal, + UpdateControllerMulticastListRequest, UpdateControllerMulticastListResponse, + VendorNotificationReceivedSignal, +}; +use crate::proto::utils::{parse_from_bytes, write_to_bytes}; +use crate::service::uwb_service::{UwbService, UwbServiceCallback}; +use crate::uci::notification::SessionRangeData; + +/// A thin adapter of UwbService. The argument and the response of each method are protobuf-encoded +/// buffer. The definition of the protobuf is at protos/uwb_core_protos.proto. +/// +/// For the naming of the protobuf struct, the argument of a method `do_something()` will be called +/// `DoSomethingRequest`, and the result will be called `DoSomethingResponse`. +pub struct ProtoUwbService { + service: UwbService, +} + +impl ProtoUwbService { + /// Create a ProtoUwbService. + pub fn new(service: UwbService) -> Self { + Self { service } + } + + /// Set UCI log mode. + pub fn set_logger_mode(&self, request: &[u8]) -> Result<Vec<u8>> { + let request = parse_from_bytes::<SetLoggerModeRequest>(request)?; + let mut resp = SetLoggerModeResponse::new(); + resp.set_status(self.service.set_logger_mode(request.logger_mode.into()).into()); + write_to_bytes(&resp) + } + + /// Enable the UWB service. + pub fn enable(&self) -> Result<Vec<u8>> { + let mut resp = EnableResponse::new(); + resp.set_status(self.service.enable().into()); + write_to_bytes(&resp) + } + + /// Disable the UWB service. + pub fn disable(&self) -> Result<Vec<u8>> { + let mut resp = DisableResponse::new(); + resp.set_status(self.service.disable().into()); + write_to_bytes(&resp) + } + + /// Initialize a new ranging session with the given parameters. + /// + /// Note: Currently the protobuf only support Fira parameters, but not support CCC parameters. + pub fn init_session(&self, request: &[u8]) -> Result<Vec<u8>> { + let mut request = parse_from_bytes::<InitSessionRequest>(request)?; + let params = request + .params + .take() + .ok_or_else(|| { + error!("InitSessionRequest.params is empty"); + Error::BadParameters + })? + .try_into() + .map_err(|e| { + error!("Failed to convert to AppConfigParams: {}", e); + Error::BadParameters + })?; + + let mut resp = InitSessionResponse::new(); + resp.set_status( + self.service + .init_session(request.session_id, request.session_type.into(), params) + .into(), + ); + write_to_bytes(&resp) + } + + /// Destroy the session. + pub fn deinit_session(&self, request: &[u8]) -> Result<Vec<u8>> { + let request = parse_from_bytes::<DeinitSessionRequest>(request)?; + let mut resp = DeinitSessionResponse::new(); + resp.set_status(self.service.deinit_session(request.session_id).into()); + write_to_bytes(&resp) + } + + /// Start ranging of the session. + pub fn start_ranging(&self, request: &[u8]) -> Result<Vec<u8>> { + let request = parse_from_bytes::<StartRangingRequest>(request)?; + // Currently we only support FiRa session, not CCC. For FiRa session, the returned + // AppConfigParams is the same as the configured one before start_ranging(). Therefore, we + // don't reply the AppConfigParams received from uwb_core. + let mut resp = StartRangingResponse::new(); + resp.set_status(self.service.start_ranging(request.session_id).into()); + write_to_bytes(&resp) + } + + /// Stop ranging. + pub fn stop_ranging(&self, request: &[u8]) -> Result<Vec<u8>> { + let request = parse_from_bytes::<StopRangingRequest>(request)?; + let mut resp = StopRangingResponse::new(); + resp.set_status(self.service.stop_ranging(request.session_id).into()); + write_to_bytes(&resp) + } + + /// Reconfigure the parameters of the session. + pub fn reconfigure(&self, request: &[u8]) -> Result<Vec<u8>> { + let mut request = parse_from_bytes::<ReconfigureRequest>(request)?; + let params = request + .params + .take() + .ok_or_else(|| { + error!("ReconfigureRequest.params is empty"); + Error::BadParameters + })? + .try_into() + .map_err(|e| { + error!("Failed to convert to AppConfigParams: {}", e); + Error::BadParameters + })?; + + let mut resp = ReconfigureResponse::new(); + resp.set_status(self.service.reconfigure(request.session_id, params).into()); + write_to_bytes(&resp) + } + + /// Update the list of the controlees to the ongoing session. + pub fn update_controller_multicast_list(&self, request: &[u8]) -> Result<Vec<u8>> { + let request = parse_from_bytes::<UpdateControllerMulticastListRequest>(request)?; + let mut controlees = vec![]; + for controlee in request.controlees.into_iter() { + let controlee = controlee.try_into().map_err(|e| { + error!("Failed to convert Controlee: {:?}", e); + Error::BadParameters + })?; + controlees.push(controlee); + } + + let mut resp = UpdateControllerMulticastListResponse::new(); + resp.set_status( + self.service + .update_controller_multicast_list( + request.session_id, + request.action.into(), + controlees, + ) + .into(), + ); + write_to_bytes(&resp) + } + + /// Set the country code. Android-specific method. + pub fn android_set_country_code(&self, request: &[u8]) -> Result<Vec<u8>> { + let request = parse_from_bytes::<AndroidSetCountryCodeRequest>(request)?; + let country_code = request.country_code.try_into()?; + + let mut resp = AndroidSetCountryCodeResponse::new(); + resp.set_status(self.service.android_set_country_code(country_code).into()); + write_to_bytes(&resp) + } + + /// Get the power statistics. Android-specific method. + pub fn android_get_power_stats(&self) -> Result<Vec<u8>> { + let mut resp = AndroidGetPowerStatsResponse::new(); + match self.service.android_get_power_stats() { + Ok(power_stats) => { + resp.set_status(Ok(()).into()); + resp.set_power_stats(power_stats.into()); + } + Err(e) => { + resp.set_status(From::<Result<()>>::from(Err(e))); + } + } + write_to_bytes(&resp) + } + + /// Send a raw UCI message. + pub fn raw_uci_cmd(&self, request: &[u8]) -> Result<Vec<u8>> { + let request = parse_from_bytes::<SendVendorCmdRequest>(request)?; + let mut resp = SendVendorCmdResponse::new(); + match self.service.raw_uci_cmd(request.mt, request.gid, request.oid, request.payload) { + Ok(msg) => { + resp.set_status(Ok(()).into()); + resp.set_gid(msg.gid); + resp.set_oid(msg.oid); + resp.set_payload(msg.payload); + } + Err(e) => { + resp.set_status(From::<Result<()>>::from(Err(e))); + } + } + write_to_bytes(&resp) + } + + /// Get app config params for the given session id + pub fn session_params(&self, request: &[u8]) -> Result<Vec<u8>> { + let request = parse_from_bytes::<SessionParamsRequest>(request)?; + let mut resp = SessionParamsResponse::new(); + match self.service.session_params(request.session_id) { + Ok(AppConfigParams::Fira(params)) => { + resp.set_status(Ok(()).into()); + resp.set_params(params.into()); + } + Ok(params) => { + error!("Received non-Fira session parameters: {:?}", params); + resp.set_status(ProtoStatus::UNKNOWN); + } + Err(e) => { + resp.set_status(From::<Result<()>>::from(Err(e))); + } + } + write_to_bytes(&resp) + } +} + +/// The trait that provides the same callbacks of UwbServiceCallback. It has the blanket +/// implementation of UwbServiceCallback trait that converts the arguments to one protobuf-encoded +/// payload. +/// +/// For the naming of the protobuf struct, the payload of a callback `on_something_happened()` +/// will be called `SomethingHappenedSignal`. +pub trait ProtoUwbServiceCallback: 'static { + /// Notify the UWB service has been reset due to internal error. All the sessions are closed. + fn on_service_reset(&mut self, payload: Vec<u8>); + /// Notify the status of the UCI device. + fn on_uci_device_status_changed(&mut self, payload: Vec<u8>); + /// Notify the state of the session is changed. + fn on_session_state_changed(&mut self, payload: Vec<u8>); + /// Notify the ranging data of the session is received. + fn on_range_data_received(&mut self, payload: Vec<u8>); + /// Notify the vendor notification is received. + fn on_vendor_notification_received(&mut self, payload: Vec<u8>); +} + +impl<C: ProtoUwbServiceCallback> UwbServiceCallback for C { + fn on_service_reset(&mut self, success: bool) { + debug!("UwbService is reset, success: {}", success); + let mut msg = ServiceResetSignal::new(); + msg.set_success(success); + if let Ok(payload) = write_to_bytes(&msg) { + ProtoUwbServiceCallback::on_service_reset(self, payload); + } else { + error!("Failed to call on_service_reset()"); + } + } + + fn on_uci_device_status_changed(&mut self, state: DeviceState) { + debug!("UCI device status is changed: {:?}", state); + let mut msg = UciDeviceStatusChangedSignal::new(); + msg.set_state(state.into()); + if let Ok(payload) = write_to_bytes(&msg) { + ProtoUwbServiceCallback::on_uci_device_status_changed(self, payload); + } else { + error!("Failed to call on_uci_device_status_changed()"); + } + } + + fn on_session_state_changed( + &mut self, + session_id: SessionId, + session_state: SessionState, + reason_code: ReasonCode, + ) { + debug!( + "Session {:?}'s state is changed to {:?}, reason: {:?}", + session_id, session_state, reason_code + ); + let mut msg = SessionStateChangedSignal::new(); + msg.set_session_id(session_id); + msg.set_session_state(session_state.into()); + msg.set_reason_code(reason_code.into()); + if let Ok(payload) = write_to_bytes(&msg) { + ProtoUwbServiceCallback::on_session_state_changed(self, payload); + } else { + error!("Failed to call on_session_state_changed()"); + } + } + + fn on_range_data_received(&mut self, session_id: SessionId, range_data: SessionRangeData) { + debug!("Received range data {:?} from Session {:?}", range_data, session_id); + let mut msg = RangeDataReceivedSignal::new(); + msg.set_session_id(session_id); + msg.set_range_data(range_data.into()); + if let Ok(payload) = write_to_bytes(&msg) { + ProtoUwbServiceCallback::on_range_data_received(self, payload); + } else { + error!("Failed to call on_range_data_received()"); + } + } + + fn on_vendor_notification_received(&mut self, gid: u32, oid: u32, payload: Vec<u8>) { + debug!("Received vendor notification: gid={}, oid={}, payload={:?}", gid, oid, payload); + let mut msg = VendorNotificationReceivedSignal::new(); + msg.set_gid(gid); + msg.set_oid(oid); + msg.set_payload(payload); + if let Ok(payload) = write_to_bytes(&msg) { + ProtoUwbServiceCallback::on_vendor_notification_received(self, payload); + } else { + error!("Failed to call on_vendor_notification_received()"); + } + } +} diff --git a/src/rust/uwb_core/src/service/uwb_service.rs b/src/rust/uwb_core/src/service/uwb_service.rs index 46c17f6..a2ecb85 100644 --- a/src/rust/uwb_core/src/service/uwb_service.rs +++ b/src/rust/uwb_core/src/service/uwb_service.rs @@ -22,7 +22,7 @@ use tokio::task; use crate::error::{Error, Result}; use crate::params::app_config_params::AppConfigParams; use crate::params::uci_packets::{ - Controlee, CountryCode, DeviceState, PowerStats, RawVendorMessage, ReasonCode, SessionId, + Controlee, CountryCode, DeviceState, PowerStats, RawUciMessage, ReasonCode, SessionId, SessionState, SessionType, UpdateMulticastListAction, }; use crate::session::session_manager::{SessionManager, SessionNotification}; @@ -59,6 +59,24 @@ pub trait UwbServiceCallback: 'static { /// Notify the vendor notification is received. fn on_vendor_notification_received(&mut self, gid: u32, oid: u32, payload: Vec<u8>); + + // TODO(b/270443790): In the future, add a callback here to notify the Data Rx packet. +} + +/// A placeholder implementation for UwbServiceCallback that does nothing. +pub struct NopUwbServiceCallback {} +impl UwbServiceCallback for NopUwbServiceCallback { + fn on_service_reset(&mut self, _success: bool) {} + fn on_uci_device_status_changed(&mut self, _state: DeviceState) {} + fn on_session_state_changed( + &mut self, + _session_id: SessionId, + _session_state: SessionState, + _reason_code: ReasonCode, + ) { + } + fn on_range_data_received(&mut self, _session_id: SessionId, _range_data: SessionRangeData) {} + fn on_vendor_notification_received(&mut self, _gid: u32, _oid: u32, _payload: Vec<u8>) {} } /// The entry class (a.k.a top shim) of the core library. The class accepts requests from the @@ -127,26 +145,26 @@ impl UwbService { } /// Set UCI log mode. - pub fn set_logger_mode(&mut self, logger_mode: UciLoggerMode) -> Result<()> { + pub fn set_logger_mode(&self, logger_mode: UciLoggerMode) -> Result<()> { self.block_on_cmd(Command::SetLoggerMode { logger_mode })?; Ok(()) } /// Enable the UWB service. - pub fn enable(&mut self) -> Result<()> { + pub fn enable(&self) -> Result<()> { self.block_on_cmd(Command::Enable)?; Ok(()) } /// Disable the UWB service. - pub fn disable(&mut self) -> Result<()> { + pub fn disable(&self) -> Result<()> { self.block_on_cmd(Command::Disable)?; Ok(()) } /// Initialize a new ranging session with the given parameters. pub fn init_session( - &mut self, + &self, session_id: SessionId, session_type: SessionType, params: AppConfigParams, @@ -156,13 +174,13 @@ impl UwbService { } /// Destroy the session. - pub fn deinit_session(&mut self, session_id: SessionId) -> Result<()> { + pub fn deinit_session(&self, session_id: SessionId) -> Result<()> { self.block_on_cmd(Command::DeinitSession { session_id })?; Ok(()) } /// Start ranging of the session. - pub fn start_ranging(&mut self, session_id: SessionId) -> Result<AppConfigParams> { + pub fn start_ranging(&self, session_id: SessionId) -> Result<AppConfigParams> { match self.block_on_cmd(Command::StartRanging { session_id })? { Response::AppConfigParams(params) => Ok(params), _ => panic!("start_ranging() should return AppConfigParams"), @@ -170,20 +188,20 @@ impl UwbService { } /// Stop ranging. - pub fn stop_ranging(&mut self, session_id: SessionId) -> Result<()> { + pub fn stop_ranging(&self, session_id: SessionId) -> Result<()> { self.block_on_cmd(Command::StopRanging { session_id })?; Ok(()) } /// Reconfigure the parameters of the session. - pub fn reconfigure(&mut self, session_id: SessionId, params: AppConfigParams) -> Result<()> { + pub fn reconfigure(&self, session_id: SessionId, params: AppConfigParams) -> Result<()> { self.block_on_cmd(Command::Reconfigure { session_id, params })?; Ok(()) } /// Update the list of the controlees to the ongoing session. pub fn update_controller_multicast_list( - &mut self, + &self, session_id: SessionId, action: UpdateMulticastListAction, controlees: Vec<Controlee>, @@ -197,34 +215,35 @@ impl UwbService { } /// Set the country code. Android-specific method. - pub fn android_set_country_code(&mut self, country_code: CountryCode) -> Result<()> { + pub fn android_set_country_code(&self, country_code: CountryCode) -> Result<()> { self.block_on_cmd(Command::AndroidSetCountryCode { country_code })?; Ok(()) } /// Get the power statistics. Android-specific method. - pub fn android_get_power_stats(&mut self) -> Result<PowerStats> { + pub fn android_get_power_stats(&self) -> Result<PowerStats> { match self.block_on_cmd(Command::AndroidGetPowerStats)? { Response::PowerStats(stats) => Ok(stats), _ => panic!("android_get_power_stats() should return PowerStats"), } } - /// Send a vendor-specific UCI message. - pub fn send_vendor_cmd( - &mut self, + /// Send a raw UCI message. + pub fn raw_uci_cmd( + &self, + mt: u32, gid: u32, oid: u32, payload: Vec<u8>, - ) -> Result<RawVendorMessage> { - match self.block_on_cmd(Command::SendVendorCmd { gid, oid, payload })? { - Response::RawVendorMessage(msg) => Ok(msg), - _ => panic!("send_vendor_cmd() should return RawVendorMessage"), + ) -> Result<RawUciMessage> { + match self.block_on_cmd(Command::RawUciCmd { mt, gid, oid, payload })? { + Response::RawUciMessage(msg) => Ok(msg), + _ => panic!("raw_uci_cmd() should return RawUciMessage"), } } /// Get app config params for the given session id - pub fn session_params(&mut self, session_id: SessionId) -> Result<AppConfigParams> { + pub fn session_params(&self, session_id: SessionId) -> Result<AppConfigParams> { match self.block_on_cmd(Command::GetParams { session_id })? { Response::AppConfigParams(params) => Ok(params), _ => panic!("session_params() should return AppConfigParams"), @@ -261,7 +280,7 @@ struct UwbServiceActor<C: UwbServiceCallback, U: UciManager> { session_manager: Option<SessionManager>, core_notf_receiver: mpsc::UnboundedReceiver<CoreNotification>, session_notf_receiver: mpsc::UnboundedReceiver<SessionNotification>, - vendor_notf_receiver: mpsc::UnboundedReceiver<RawVendorMessage>, + vendor_notf_receiver: mpsc::UnboundedReceiver<RawUciMessage>, } impl<C: UwbServiceCallback, U: UciManager> UwbServiceActor<C, U> { @@ -396,9 +415,9 @@ impl<C: UwbServiceCallback, U: UciManager> UwbServiceActor<C, U> { let stats = self.uci_manager.android_get_power_stats().await?; Ok(Response::PowerStats(stats)) } - Command::SendVendorCmd { gid, oid, payload } => { - let msg = self.uci_manager.raw_vendor_cmd(gid, oid, payload).await?; - Ok(Response::RawVendorMessage(msg)) + Command::RawUciCmd { mt, gid, oid, payload } => { + let msg = self.uci_manager.raw_uci_cmd(mt, gid, oid, payload).await?; + Ok(Response::RawUciMessage(msg)) } Command::GetParams { session_id } => { if let Some(session_manager) = self.session_manager.as_mut() { @@ -438,7 +457,7 @@ impl<C: UwbServiceCallback, U: UciManager> UwbServiceActor<C, U> { } } - async fn handle_vendor_notification(&mut self, notf: RawVendorMessage) { + async fn handle_vendor_notification(&mut self, notf: RawUciMessage) { self.callback.on_vendor_notification_received(notf.gid, notf.oid, notf.payload); } @@ -530,7 +549,8 @@ enum Command { country_code: CountryCode, }, AndroidGetPowerStats, - SendVendorCmd { + RawUciCmd { + mt: u32, gid: u32, oid: u32, payload: Vec<u8>, @@ -545,7 +565,7 @@ enum Response { Null, AppConfigParams(AppConfigParams), PowerStats(PowerStats), - RawVendorMessage(RawVendorMessage), + RawUciMessage(RawUciMessage), } type ResponseSender = oneshot::Sender<Result<Response>>; @@ -581,7 +601,7 @@ mod tests { let mut uci_manager = MockUciManager::new(); uci_manager.expect_open_hal(vec![], Ok(())); uci_manager.expect_close_hal(false, Ok(())); - let (mut service, _, _runtime) = setup_uwb_service(uci_manager); + let (service, _, _runtime) = setup_uwb_service(uci_manager); let result = service.enable(); assert!(result.is_ok()); @@ -630,7 +650,7 @@ mod tests { Ok(()), ); - let (mut service, mut callback, _runtime) = setup_uwb_service(uci_manager.clone()); + let (service, mut callback, _runtime) = setup_uwb_service(uci_manager.clone()); service.enable().unwrap(); // Initialize a normal session. @@ -692,7 +712,7 @@ mod tests { let controlees = vec![Controlee { short_address: 0x13, subsession_id: 0x24 }]; let uci_manager = MockUciManager::new(); - let (mut service, _, _runtime) = setup_uwb_service(uci_manager); + let (service, _, _runtime) = setup_uwb_service(uci_manager); let result = service.init_session(session_id, session_type, params.clone()); assert!(result.is_err()); @@ -713,7 +733,7 @@ mod tests { let country_code = CountryCode::new(b"US").unwrap(); let mut uci_manager = MockUciManager::new(); uci_manager.expect_android_set_country_code(country_code.clone(), Ok(())); - let (mut service, _, _runtime) = setup_uwb_service(uci_manager); + let (service, _, _runtime) = setup_uwb_service(uci_manager); let result = service.android_set_country_code(country_code); assert!(result.is_ok()); @@ -730,30 +750,32 @@ mod tests { }; let mut uci_manager = MockUciManager::new(); uci_manager.expect_android_get_power_stats(Ok(stats.clone())); - let (mut service, _, _runtime) = setup_uwb_service(uci_manager); + let (service, _, _runtime) = setup_uwb_service(uci_manager); let result = service.android_get_power_stats().unwrap(); assert_eq!(result, stats); } #[test] - fn test_send_vendor_cmd() { + fn test_send_raw_cmd() { + let mt = 0x01; let gid = 0x09; let oid = 0x35; let cmd_payload = vec![0x12, 0x34]; let resp_payload = vec![0x56, 0x78]; let mut uci_manager = MockUciManager::new(); - uci_manager.expect_raw_vendor_cmd( + uci_manager.expect_raw_uci_cmd( + mt, gid, oid, cmd_payload.clone(), - Ok(RawVendorMessage { gid, oid, payload: resp_payload.clone() }), + Ok(RawUciMessage { gid, oid, payload: resp_payload.clone() }), ); - let (mut service, _, _runtime) = setup_uwb_service(uci_manager); + let (service, _, _runtime) = setup_uwb_service(uci_manager); - let result = service.send_vendor_cmd(gid, oid, cmd_payload).unwrap(); - assert_eq!(result, RawVendorMessage { gid, oid, payload: resp_payload }); + let result = service.raw_uci_cmd(mt, gid, oid, cmd_payload).unwrap(); + assert_eq!(result, RawUciMessage { gid, oid, payload: resp_payload }); } #[test] @@ -764,10 +786,10 @@ mod tests { let mut uci_manager = MockUciManager::new(); uci_manager.expect_open_hal( - vec![UciNotification::Vendor(RawVendorMessage { gid, oid, payload: payload.clone() })], + vec![UciNotification::Vendor(RawUciMessage { gid, oid, payload: payload.clone() })], Ok(()), ); - let (mut service, mut callback, _runtime) = setup_uwb_service(uci_manager); + let (service, mut callback, _runtime) = setup_uwb_service(uci_manager); callback.expect_on_vendor_notification_received(gid, oid, payload); service.enable().unwrap(); @@ -783,7 +805,7 @@ mod tests { vec![UciNotification::Core(CoreNotification::DeviceStatus(state))], Ok(()), ); - let (mut service, mut callback, _runtime) = setup_uwb_service(uci_manager); + let (service, mut callback, _runtime) = setup_uwb_service(uci_manager); callback.expect_on_uci_device_status_changed(state); service.enable().unwrap(); assert!(service.block_on_for_testing(callback.wait_expected_calls_done())); @@ -797,7 +819,7 @@ mod tests { // Then UwbService should close_hal() and open_hal() to reset the HAL. uci_manager.expect_close_hal(true, Ok(())); uci_manager.expect_open_hal(vec![], Ok(())); - let (mut service, mut callback, _runtime) = setup_uwb_service(uci_manager.clone()); + let (service, mut callback, _runtime) = setup_uwb_service(uci_manager.clone()); callback.expect_on_service_reset(true); let result = service.enable(); @@ -820,7 +842,7 @@ mod tests { // Then UwbService should close_hal() and open_hal() to reset the HAL. uci_manager.expect_close_hal(true, Ok(())); uci_manager.expect_open_hal(vec![], Ok(())); - let (mut service, mut callback, _runtime) = setup_uwb_service(uci_manager.clone()); + let (service, mut callback, _runtime) = setup_uwb_service(uci_manager.clone()); callback.expect_on_service_reset(true); let result = service.enable(); diff --git a/src/rust/uwb_core/src/service/uwb_service_builder.rs b/src/rust/uwb_core/src/service/uwb_service_builder.rs index 9075368..20a4f2b 100644 --- a/src/rust/uwb_core/src/service/uwb_service_builder.rs +++ b/src/rust/uwb_core/src/service/uwb_service_builder.rs @@ -103,7 +103,7 @@ mod tests { use crate::service::mock_uwb_service_callback::MockUwbServiceCallback; use crate::service::uwb_service_callback_builder::UwbServiceCallbackSendBuilder; use crate::uci::mock_uci_hal::MockUciHal; - use crate::uci::uci_logger_factory::UciLoggerFactoryNull; + use crate::uci::uci_logger_factory::NopUciLoggerFactory; #[test] fn test_build_fail() { @@ -111,7 +111,7 @@ mod tests { UwbServiceCallbackSendBuilder<MockUwbServiceCallback>, MockUwbServiceCallback, MockUciHal, - UciLoggerFactoryNull, + NopUciLoggerFactory, >::new() .build(); assert!(result.is_none()); @@ -125,7 +125,7 @@ mod tests { .runtime_handle(runtime.handle().to_owned()) .callback_builder(UwbServiceCallbackSendBuilder::new(callback)) .uci_hal(MockUciHal::new()) - .uci_logger_factory(UciLoggerFactoryNull::default()) + .uci_logger_factory(NopUciLoggerFactory::default()) .build(); assert!(result.is_some()); } diff --git a/src/rust/uwb_core/src/session/session_manager.rs b/src/rust/uwb_core/src/session/session_manager.rs index 667879f..e9432ba 100644 --- a/src/rust/uwb_core/src/session/session_manager.rs +++ b/src/rust/uwb_core/src/session/session_manager.rs @@ -295,6 +295,16 @@ impl<T: UciManager> SessionManagerActor<T> { fn handle_uci_notification(&mut self, notf: UciSessionNotification) { match notf { UciSessionNotification::Status { session_id, session_state, reason_code } => { + let reason_code = match ReasonCode::try_from(reason_code) { + Ok(r) => r, + Err(_) => { + error!( + "Received unknown reason_code {:?} in UciSessionNotification", + reason_code + ); + return; + } + }; if session_state == SessionState::SessionStateDeinit { debug!("Session {} is deinitialized", session_id); let _ = self.active_sessions.remove(&session_id); @@ -336,7 +346,7 @@ impl<T: UciManager> SessionManagerActor<T> { ); } }, - UciSessionNotification::RangeData(range_data) => { + UciSessionNotification::SessionInfo(range_data) => { if self.active_sessions.get(&range_data.session_id).is_some() { let _ = self.session_notf_sender.send(SessionNotification::RangeData { session_id: range_data.session_id, @@ -346,6 +356,42 @@ impl<T: UciManager> SessionManagerActor<T> { warn!("Received range data of the unknown Session: {:?}", range_data); } } + UciSessionNotification::DataCredit { session_id, credit_availability: _ } => { + match self.active_sessions.get(&session_id) { + Some(_) => { + /* + * TODO(b/270443790): Handle the DataCredit notification in the new + * code flow. + */ + } + None => { + warn!( + "Received the Data Credit notification for an unknown Session {}", + session_id + ); + } + } + } + UciSessionNotification::DataTransferStatus { + session_id, + uci_sequence_number: _, + status: _, + } => { + match self.active_sessions.get(&session_id) { + Some(_) => { + /* + * TODO(b/270443790): Handle the DataTransferStatus notification in the + * new code flow. + */ + } + None => { + warn!( + "Received a Data Transfer Status notification for unknown Session {}", + session_id + ); + } + } + } } } } @@ -438,7 +484,7 @@ pub(crate) mod test_utils { session_id, current_ranging_interval_ms: 3, ranging_measurement_type: RangingMeasurementType::TwoWay, - ranging_measurements: RangingMeasurements::Short(vec![ + ranging_measurements: RangingMeasurements::ShortAddressTwoWay(vec![ ShortAddressTwoWayRangingMeasurement { mac_address: 0x123, status: StatusCode::UciStatusOk, @@ -468,12 +514,12 @@ pub(crate) mod test_utils { UciNotification::Session(UciSessionNotification::Status { session_id, session_state, - reason_code: ReasonCode::StateChangeWithSessionManagementCommands, + reason_code: ReasonCode::StateChangeWithSessionManagementCommands.into(), }) } pub(crate) fn range_data_notf(range_data: SessionRangeData) -> UciNotification { - UciNotification::Session(UciSessionNotification::RangeData(range_data)) + UciNotification::Session(UciSessionNotification::SessionInfo(range_data)) } pub(super) async fn setup_session_manager<F>( @@ -508,8 +554,8 @@ mod tests { use crate::params::ccc_started_app_config_params::CccStartedAppConfigParams; use crate::params::uci_packets::{ - AppConfigTlv, AppConfigTlvType, ControleeStatus, MulticastUpdateStatusCode, - SetAppConfigResponse, StatusCode, + AppConfigTlv, AppConfigTlvType, ControleeStatus, Controlees, MulticastUpdateStatusCode, + ReasonCode, SetAppConfigResponse, StatusCode, }; use crate::params::utils::{u32_to_bytes, u64_to_bytes, u8_to_bytes}; use crate::params::{FiraAppConfigParamsBuilder, KeyRotation}; @@ -761,7 +807,7 @@ mod tests { uci_manager.expect_session_update_controller_multicast_list( session_id, action, - controlees_clone, + Controlees::NoSessionKey(controlees_clone), multicast_list_notf, Ok(()), ); @@ -846,7 +892,7 @@ mod tests { uci_manager.expect_session_update_controller_multicast_list( session_id, action, - controlees_clone, + uwb_uci_packets::Controlees::NoSessionKey(controlees_clone), vec![], // Not sending notification. Ok(()), ); diff --git a/src/rust/uwb_core/src/session/uwb_session.rs b/src/rust/uwb_core/src/session/uwb_session.rs index 6f1de24..91bd6ce 100644 --- a/src/rust/uwb_core/src/session/uwb_session.rs +++ b/src/rust/uwb_core/src/session/uwb_session.rs @@ -24,8 +24,8 @@ use crate::error::{Error, Result}; use crate::params::app_config_params::AppConfigParams; use crate::params::ccc_started_app_config_params::CccStartedAppConfigParams; use crate::params::uci_packets::{ - Controlee, ControleeStatus, MulticastUpdateStatusCode, SessionId, SessionState, SessionType, - UpdateMulticastListAction, + Controlee, ControleeStatus, Controlees, MulticastUpdateStatusCode, SessionId, SessionState, + SessionType, UpdateMulticastListAction, }; use crate::uci::error::status_code_to_result; use crate::uci::uci_manager::UciManager; @@ -306,7 +306,11 @@ impl<T: UciManager> UwbSessionActor<T> { } self.uci_manager - .session_update_controller_multicast_list(self.session_id, action, controlees) + .session_update_controller_multicast_list( + self.session_id, + action, + Controlees::NoSessionKey(controlees), + ) .await?; // Wait for the notification of the update status. diff --git a/src/rust/uwb_core/src/uci.rs b/src/rust/uwb_core/src/uci.rs index bcf4586..cac1be2 100644 --- a/src/rust/uwb_core/src/uci.rs +++ b/src/rust/uwb_core/src/uci.rs @@ -35,12 +35,15 @@ pub mod uci_manager_sync; pub(crate) mod mock_uci_hal; #[cfg(test)] pub(crate) mod mock_uci_logger; -#[cfg(test)] -pub(crate) mod mock_uci_manager; +#[cfg(any(test, feature = "mock-utils"))] +pub mod mock_uci_manager; // Re-export the public elements. pub use command::UciCommand; pub use notification::{ - CoreNotification, RangingMeasurements, SessionNotification, SessionRangeData, UciNotification, + CoreNotification, DataRcvNotification, RangingMeasurements, SessionNotification, + SessionRangeData, UciNotification, }; -pub use uci_hal::{UciHal, UciHalPacket}; +pub use uci_hal::{NopUciHal, UciHal, UciHalPacket}; +pub use uci_logger_factory::{NopUciLoggerFactory, UciLoggerFactory}; +pub use uci_manager::UciManagerImpl; diff --git a/src/rust/uwb_core/src/uci/command.rs b/src/rust/uwb_core/src/uci/command.rs index 76edd72..dc06dab 100644 --- a/src/rust/uwb_core/src/uci/command.rs +++ b/src/rust/uwb_core/src/uci/command.rs @@ -12,21 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; use bytes::Bytes; use log::error; -use num_traits::FromPrimitive; use crate::error::{Error, Result}; use crate::params::uci_packets::{ - AppConfigTlv, AppConfigTlvType, Controlee, CountryCode, DeviceConfigId, DeviceConfigTlv, + AppConfigTlv, AppConfigTlvType, Controlees, CountryCode, DeviceConfigId, DeviceConfigTlv, ResetConfig, SessionId, SessionType, UpdateMulticastListAction, }; -use uwb_uci_packets::{ - build_session_update_controller_multicast_list_cmd_v1, - build_session_update_controller_multicast_list_cmd_v2, ControleesV2, -}; +use uwb_uci_packets::{build_session_update_controller_multicast_list_cmd, GroupId, MessageType}; /// The enum to represent the UCI commands. The definition of each field should follow UCI spec. #[allow(missing_docs)] @@ -65,49 +61,47 @@ pub enum UciCommand { SessionUpdateControllerMulticastList { session_id: SessionId, action: UpdateMulticastListAction, - controlees: Vec<Controlee>, + controlees: Controlees, + }, + SessionUpdateDtTagRangingRounds { + session_id: u32, + ranging_round_indexes: Vec<u8>, }, - SessionUpdateControllerMulticastListV2 { + SessionQueryMaxDataSize { session_id: SessionId, - action: UpdateMulticastListAction, - controlees: ControleesV2, }, - RangeStart { + SessionStart { session_id: SessionId, }, - RangeStop { + SessionStop { session_id: SessionId, }, - RangeGetRangingCount { + SessionGetRangingCount { session_id: SessionId, }, AndroidSetCountryCode { country_code: CountryCode, }, AndroidGetPowerStats, - RawVendorCmd { + RawUciCmd { + mt: u32, gid: u32, oid: u32, payload: Vec<u8>, }, } -impl TryFrom<UciCommand> for uwb_uci_packets::UciCommandPacket { +impl TryFrom<UciCommand> for uwb_uci_packets::UciControlPacket { type Error = Error; fn try_from(cmd: UciCommand) -> std::result::Result<Self, Self::Error> { let packet = match cmd { + // UCI Session Config Commands UciCommand::SessionInit { session_id, session_type } => { uwb_uci_packets::SessionInitCmdBuilder { session_id, session_type }.build().into() } UciCommand::SessionDeinit { session_id } => { uwb_uci_packets::SessionDeinitCmdBuilder { session_id }.build().into() } - UciCommand::RangeStart { session_id } => { - uwb_uci_packets::RangeStartCmdBuilder { session_id }.build().into() - } - UciCommand::RangeStop { session_id } => { - uwb_uci_packets::RangeStopCmdBuilder { session_id }.build().into() - } UciCommand::CoreGetDeviceInfo => { uwb_uci_packets::GetDeviceInfoCmdBuilder {}.build().into() } @@ -116,24 +110,15 @@ impl TryFrom<UciCommand> for uwb_uci_packets::UciCommandPacket { uwb_uci_packets::SessionGetStateCmdBuilder { session_id }.build().into() } UciCommand::SessionUpdateControllerMulticastList { session_id, action, controlees } => { - build_session_update_controller_multicast_list_cmd_v1( - session_id, action, controlees, - ) - .into() + build_session_update_controller_multicast_list_cmd(session_id, action, controlees) + .map_err(|_| Error::BadParameters)? + .into() } - UciCommand::SessionUpdateControllerMulticastListV2 { - session_id, - action, - controlees, - } => build_session_update_controller_multicast_list_cmd_v2( - session_id, action, controlees, - ) - .into(), UciCommand::CoreSetConfig { config_tlvs } => { uwb_uci_packets::SetConfigCmdBuilder { tlvs: config_tlvs }.build().into() } UciCommand::CoreGetConfig { cfg_id } => uwb_uci_packets::GetConfigCmdBuilder { - cfg_id: cfg_id.into_iter().map(|item| item as u8).collect(), + cfg_id: cfg_id.into_iter().map(u8::from).collect(), } .build() .into(), @@ -148,7 +133,15 @@ impl TryFrom<UciCommand> for uwb_uci_packets::UciCommandPacket { UciCommand::SessionGetAppConfig { session_id, app_cfg } => { uwb_uci_packets::SessionGetAppConfigCmdBuilder { session_id, - app_cfg: app_cfg.into_iter().map(|item| item as u8).collect(), + app_cfg: app_cfg.into_iter().map(u8::from).collect(), + } + .build() + .into() + } + UciCommand::SessionUpdateDtTagRangingRounds { session_id, ranging_round_indexes } => { + uwb_uci_packets::SessionUpdateDtTagRangingRoundsCmdBuilder { + session_id, + ranging_round_indexes, } .build() .into() @@ -156,8 +149,8 @@ impl TryFrom<UciCommand> for uwb_uci_packets::UciCommandPacket { UciCommand::AndroidGetPowerStats => { uwb_uci_packets::AndroidGetPowerStatsCmdBuilder {}.build().into() } - UciCommand::RawVendorCmd { gid, oid, payload } => { - build_uci_vendor_cmd_packet(gid, oid, payload)? + UciCommand::RawUciCmd { mt, gid, oid, payload } => { + build_raw_uci_cmd_packet(mt, gid, oid, payload)? } UciCommand::SessionGetCount => { uwb_uci_packets::SessionGetCountCmdBuilder {}.build().into() @@ -172,49 +165,213 @@ impl TryFrom<UciCommand> for uwb_uci_packets::UciCommandPacket { UciCommand::DeviceReset { reset_config } => { uwb_uci_packets::DeviceResetCmdBuilder { reset_config }.build().into() } - UciCommand::RangeGetRangingCount { session_id } => { - uwb_uci_packets::RangeGetRangingCountCmdBuilder { session_id }.build().into() + // UCI Session Control Commands + UciCommand::SessionStart { session_id } => { + uwb_uci_packets::SessionStartCmdBuilder { session_id }.build().into() + } + UciCommand::SessionStop { session_id } => { + uwb_uci_packets::SessionStopCmdBuilder { session_id }.build().into() + } + UciCommand::SessionGetRangingCount { session_id } => { + uwb_uci_packets::SessionGetRangingCountCmdBuilder { session_id }.build().into() + } + UciCommand::SessionQueryMaxDataSize { session_id } => { + uwb_uci_packets::SessionQueryMaxDataSizeCmdBuilder { session_id }.build().into() } }; Ok(packet) } } -fn build_uci_vendor_cmd_packet( +fn build_raw_uci_cmd_packet( + mt: u32, gid: u32, oid: u32, payload: Vec<u8>, -) -> Result<uwb_uci_packets::UciCommandPacket> { - use uwb_uci_packets::GroupId; - let group_id = GroupId::from_u32(gid).ok_or_else(|| { +) -> Result<uwb_uci_packets::UciControlPacket> { + let group_id = u8::try_from(gid).or(Err(0)).and_then(GroupId::try_from).map_err(|_| { error!("Invalid GroupId: {}", gid); Error::BadParameters })?; let payload = if payload.is_empty() { None } else { Some(Bytes::from(payload)) }; - let opcode = oid.try_into().map_err(|_| { + let opcode = u8::try_from(oid).map_err(|_| { error!("Invalid opcod: {}", oid); Error::BadParameters })?; - let packet = match group_id { - GroupId::VendorReserved9 => { - uwb_uci_packets::UciVendor_9_CommandBuilder { opcode, payload }.build().into() - } - GroupId::VendorReservedA => { - uwb_uci_packets::UciVendor_A_CommandBuilder { opcode, payload }.build().into() - } - GroupId::VendorReservedB => { - uwb_uci_packets::UciVendor_B_CommandBuilder { opcode, payload }.build().into() - } - GroupId::VendorReservedE => { - uwb_uci_packets::UciVendor_E_CommandBuilder { opcode, payload }.build().into() - } - GroupId::VendorReservedF => { - uwb_uci_packets::UciVendor_F_CommandBuilder { opcode, payload }.build().into() - } - _ => { - error!("Invalid vendor gid {:?}", gid); - return Err(Error::BadParameters); - } - }; - Ok(packet) + let message_type = + u8::try_from(mt).or(Err(0)).and_then(MessageType::try_from).map_err(|_| { + error!("Invalid MessageType: {}", mt); + Error::BadParameters + })?; + match uwb_uci_packets::build_uci_control_packet(message_type, group_id, opcode, payload) { + Some(cmd) => Ok(cmd), + None => Err(Error::BadParameters), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_build_raw_uci_cmd() { + let payload = vec![0x01, 0x02]; + let cmd_packet = build_raw_uci_cmd_packet(1, 9, 0, payload.clone()).unwrap(); + assert_eq!(payload, cmd_packet.to_raw_payload()); + } + + #[test] + fn test_convert_uci_cmd_to_packets() { + let mut cmd = UciCommand::DeviceReset { reset_config: ResetConfig::UwbsReset }; + let mut packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!( + packet, + uwb_uci_packets::DeviceResetCmdBuilder { reset_config: ResetConfig::UwbsReset } + .build() + .into() + ); + + cmd = UciCommand::CoreGetDeviceInfo {}; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!(packet, uwb_uci_packets::GetDeviceInfoCmdBuilder {}.build().into()); + + cmd = UciCommand::CoreGetCapsInfo {}; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!(packet, uwb_uci_packets::GetCapsInfoCmdBuilder {}.build().into()); + + let device_cfg_tlv = DeviceConfigTlv { cfg_id: DeviceConfigId::DeviceState, v: vec![0] }; + cmd = UciCommand::CoreSetConfig { config_tlvs: vec![device_cfg_tlv.clone()] }; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!( + packet, + uwb_uci_packets::SetConfigCmdBuilder { tlvs: vec![device_cfg_tlv] }.build().into() + ); + + cmd = UciCommand::CoreGetConfig { cfg_id: vec![DeviceConfigId::DeviceState] }; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!(packet, uwb_uci_packets::GetConfigCmdBuilder { cfg_id: vec![0] }.build().into()); + + cmd = UciCommand::SessionInit { + session_id: 1, + session_type: SessionType::FiraRangingSession, + }; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!( + packet, + uwb_uci_packets::SessionInitCmdBuilder { + session_id: 1, + session_type: SessionType::FiraRangingSession + } + .build() + .into() + ); + + cmd = UciCommand::SessionDeinit { session_id: 1 }; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!( + packet, + uwb_uci_packets::SessionDeinitCmdBuilder { session_id: 1 }.build().into() + ); + + cmd = UciCommand::SessionSetAppConfig { session_id: 1, config_tlvs: vec![] }; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!( + packet, + uwb_uci_packets::SessionSetAppConfigCmdBuilder { session_id: 1, tlvs: vec![] } + .build() + .into() + ); + + cmd = UciCommand::SessionGetAppConfig { session_id: 1, app_cfg: vec![] }; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!( + packet, + uwb_uci_packets::SessionGetAppConfigCmdBuilder { session_id: 1, app_cfg: vec![] } + .build() + .into() + ); + + cmd = UciCommand::SessionGetCount {}; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!(packet, uwb_uci_packets::SessionGetCountCmdBuilder {}.build().into()); + + cmd = UciCommand::SessionGetState { session_id: 1 }; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!( + packet, + uwb_uci_packets::SessionGetStateCmdBuilder { session_id: 1 }.build().into() + ); + + cmd = UciCommand::SessionUpdateControllerMulticastList { + session_id: 1, + action: UpdateMulticastListAction::AddControlee, + controlees: Controlees::NoSessionKey(vec![]), + }; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!( + packet, + build_session_update_controller_multicast_list_cmd( + 1, + UpdateMulticastListAction::AddControlee, + Controlees::NoSessionKey(vec![]) + ) + .map_err(|_| Error::BadParameters) + .unwrap() + .into() + ); + + cmd = UciCommand::SessionUpdateDtTagRangingRounds { + session_id: 1, + ranging_round_indexes: vec![0], + }; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!( + packet, + uwb_uci_packets::SessionUpdateDtTagRangingRoundsCmdBuilder { + session_id: 1, + ranging_round_indexes: vec![0] + } + .build() + .into() + ); + + cmd = UciCommand::SessionQueryMaxDataSize { session_id: 1 }; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!( + packet, + uwb_uci_packets::SessionQueryMaxDataSizeCmdBuilder { session_id: 1 }.build().into() + ); + + cmd = UciCommand::SessionStart { session_id: 1 }; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!( + packet, + uwb_uci_packets::SessionStartCmdBuilder { session_id: 1 }.build().into() + ); + + cmd = UciCommand::SessionStop { session_id: 1 }; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!(packet, uwb_uci_packets::SessionStopCmdBuilder { session_id: 1 }.build().into()); + + cmd = UciCommand::SessionGetRangingCount { session_id: 1 }; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!( + packet, + uwb_uci_packets::SessionGetRangingCountCmdBuilder { session_id: 1 }.build().into() + ); + + let country_code: [u8; 2] = [85, 83]; + cmd = UciCommand::AndroidSetCountryCode { + country_code: CountryCode::new(&country_code).unwrap(), + }; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!( + packet, + uwb_uci_packets::AndroidSetCountryCodeCmdBuilder { country_code }.build().into() + ); + + cmd = UciCommand::AndroidGetPowerStats {}; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd).unwrap(); + assert_eq!(packet, uwb_uci_packets::AndroidGetPowerStatsCmdBuilder {}.build().into()); + } } diff --git a/src/rust/uwb_core/src/uci/message.rs b/src/rust/uwb_core/src/uci/message.rs index e386ca8..fcbc65d 100644 --- a/src/rust/uwb_core/src/uci/message.rs +++ b/src/rust/uwb_core/src/uci/message.rs @@ -26,14 +26,14 @@ pub(super) enum UciMessage { Notification(UciNotification), } -impl TryFrom<uwb_uci_packets::UciPacketPacket> for UciMessage { +impl TryFrom<uwb_uci_packets::UciControlPacket> for UciMessage { type Error = Error; - fn try_from(packet: uwb_uci_packets::UciPacketPacket) -> Result<Self, Self::Error> { + fn try_from(packet: uwb_uci_packets::UciControlPacket) -> Result<Self, Self::Error> { match packet.specialize() { - uwb_uci_packets::UciPacketChild::UciResponse(evt) => { + uwb_uci_packets::UciControlPacketChild::UciResponse(evt) => { Ok(UciMessage::Response(evt.try_into()?)) } - uwb_uci_packets::UciPacketChild::UciNotification(evt) => { + uwb_uci_packets::UciControlPacketChild::UciNotification(evt) => { Ok(UciMessage::Notification(evt.try_into()?)) } _ => { diff --git a/src/rust/uwb_core/src/uci/mock_uci_hal.rs b/src/rust/uwb_core/src/uci/mock_uci_hal.rs index eb27e00..7e7cfc0 100644 --- a/src/rust/uwb_core/src/uci/mock_uci_hal.rs +++ b/src/rust/uwb_core/src/uci/mock_uci_hal.rs @@ -59,6 +59,19 @@ impl MockUciHal { }); } + pub fn expected_send_packet( + &mut self, + expected_packet_tx: UciHalPacket, + inject_packets_rx: Vec<UciHalPacket>, + out: Result<()>, + ) { + self.expected_calls.lock().unwrap().push_back(ExpectedCall::SendPacket { + expected_packet_tx, + inject_packets_rx, + out, + }); + } + pub fn expected_notify_session_initialized( &mut self, expected_session_id: SessionId, @@ -145,9 +158,26 @@ impl UciHal for MockUciHal { } } - async fn send_packet(&mut self, _packet: UciHalPacket) -> Result<()> { - // We mock the send_command(), so send_packet() would not be called. - Err(Error::MockUndefined) + async fn send_packet(&mut self, packet_tx: UciHalPacket) -> Result<()> { + // send_packet() will be directly called for sending UCI Data packets. + let mut expected_calls = self.expected_calls.lock().unwrap(); + match expected_calls.pop_front() { + Some(ExpectedCall::SendPacket { expected_packet_tx, inject_packets_rx, out }) + if expected_packet_tx == packet_tx => + { + self.expect_call_consumed.notify_one(); + let packet_sender = self.packet_sender.as_mut().unwrap(); + for msg in inject_packets_rx.into_iter() { + let _ = packet_sender.send(msg); + } + out + } + Some(call) => { + expected_calls.push_front(call); + Err(Error::MockUndefined) + } + None => Err(Error::MockUndefined), + } } async fn notify_session_initialized(&mut self, session_id: SessionId) -> Result<()> { @@ -169,8 +199,25 @@ impl UciHal for MockUciHal { } enum ExpectedCall { - Open { packets: Option<Vec<UciHalPacket>>, out: Result<()> }, - Close { out: Result<()> }, - SendCommand { expected_cmd: UciCommand, packets: Vec<UciHalPacket>, out: Result<()> }, - NotifySessionInitialized { expected_session_id: SessionId, out: Result<()> }, + Open { + packets: Option<Vec<UciHalPacket>>, + out: Result<()>, + }, + Close { + out: Result<()>, + }, + SendCommand { + expected_cmd: UciCommand, + packets: Vec<UciHalPacket>, + out: Result<()>, + }, + SendPacket { + expected_packet_tx: UciHalPacket, + inject_packets_rx: Vec<UciHalPacket>, + out: Result<()>, + }, + NotifySessionInitialized { + expected_session_id: SessionId, + out: Result<()>, + }, } diff --git a/src/rust/uwb_core/src/uci/mock_uci_logger.rs b/src/rust/uwb_core/src/uci/mock_uci_logger.rs index 64f173d..e21fb71 100644 --- a/src/rust/uwb_core/src/uci/mock_uci_logger.rs +++ b/src/rust/uwb_core/src/uci/mock_uci_logger.rs @@ -15,7 +15,7 @@ use std::convert::TryFrom; use tokio::sync::mpsc; -use uwb_uci_packets::UciPacketPacket; +use uwb_uci_packets::{UciControlPacket, UciDataPacket}; use crate::error::{Error, Result}; use crate::uci::uci_logger::UciLogger; @@ -57,7 +57,11 @@ impl UciLogger for MockUciLogger { let _ = self.log_sender.send(UciLogEvent::HalOpen(result)); } - fn log_uci_packet(&mut self, packet: UciPacketPacket) { + fn log_uci_control_packet(&mut self, packet: UciControlPacket) { let _ = self.log_sender.send(UciLogEvent::Packet(packet.into())); } + + fn log_uci_data_packet(&mut self, packet: &UciDataPacket) { + let _ = self.log_sender.send(UciLogEvent::Packet(packet.clone().into())); + } } diff --git a/src/rust/uwb_core/src/uci/mock_uci_manager.rs b/src/rust/uwb_core/src/uci/mock_uci_manager.rs index e8215d4..a206392 100644 --- a/src/rust/uwb_core/src/uci/mock_uci_manager.rs +++ b/src/rust/uwb_core/src/uci/mock_uci_manager.rs @@ -12,8 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! This module offers a mocked version of UciManager for testing. +//! +//! The mocked version of UciManager mimics the behavior of the UCI manager and +//! stacks below it, such that tests can be run on a target without the UWB +//! hardware. use std::collections::VecDeque; -use std::iter::zip; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -23,27 +27,32 @@ use tokio::time::timeout; use crate::error::{Error, Result}; use crate::params::uci_packets::{ - app_config_tlvs_eq, device_config_tlvs_eq, AppConfigTlv, AppConfigTlvType, CapTlv, Controlee, - CoreSetConfigResponse, CountryCode, DeviceConfigId, DeviceConfigTlv, GetDeviceInfoResponse, - PowerStats, RawVendorMessage, ResetConfig, SessionId, SessionState, SessionType, - SetAppConfigResponse, UpdateMulticastListAction, + app_config_tlvs_eq, device_config_tlvs_eq, AppConfigTlv, AppConfigTlvType, CapTlv, Controlees, + CoreSetConfigResponse, CountryCode, DeviceConfigId, DeviceConfigTlv, FiraComponent, + GetDeviceInfoResponse, PowerStats, RawUciMessage, ResetConfig, SessionId, SessionState, + SessionType, SessionUpdateDtTagRangingRoundsResponse, SetAppConfigResponse, + UpdateMulticastListAction, +}; +use crate::uci::notification::{ + CoreNotification, DataRcvNotification, SessionNotification, UciNotification, }; -use crate::uci::notification::{CoreNotification, SessionNotification, UciNotification}; use crate::uci::uci_logger::UciLoggerMode; use crate::uci::uci_manager::UciManager; -use uwb_uci_packets::ControleesV2; #[derive(Clone)] -pub(crate) struct MockUciManager { +/// Mock version of UciManager for testing. +pub struct MockUciManager { expected_calls: Arc<Mutex<VecDeque<ExpectedCall>>>, expect_call_consumed: Arc<Notify>, core_notf_sender: mpsc::UnboundedSender<CoreNotification>, session_notf_sender: mpsc::UnboundedSender<SessionNotification>, - vendor_notf_sender: mpsc::UnboundedSender<RawVendorMessage>, + vendor_notf_sender: mpsc::UnboundedSender<RawUciMessage>, + data_rcv_notf_sender: mpsc::UnboundedSender<DataRcvNotification>, } #[allow(dead_code)] impl MockUciManager { + /// Constructor. pub fn new() -> Self { Self { expected_calls: Default::default(), @@ -51,9 +60,13 @@ impl MockUciManager { core_notf_sender: mpsc::unbounded_channel().0, session_notf_sender: mpsc::unbounded_channel().0, vendor_notf_sender: mpsc::unbounded_channel().0, + data_rcv_notf_sender: mpsc::unbounded_channel().0, } } + /// Wait until expected calls are done. + /// + /// Returns false if calls are pending after 1 second. pub async fn wait_expected_calls_done(&mut self) -> bool { while !self.expected_calls.lock().unwrap().is_empty() { if timeout(Duration::from_secs(1), self.expect_call_consumed.notified()).await.is_err() @@ -64,10 +77,16 @@ impl MockUciManager { true } + /// Prepare Mock to expect for open_hal. + /// + /// MockUciManager expects call, returns out as response, followed by notfs sent. pub fn expect_open_hal(&mut self, notfs: Vec<UciNotification>, out: Result<()>) { self.expected_calls.lock().unwrap().push_back(ExpectedCall::OpenHal { notfs, out }); } + /// Prepare Mock to expect for close_call. + /// + /// MockUciManager expects call with parameters, returns out as response. pub fn expect_close_hal(&mut self, expected_force: bool, out: Result<()>) { self.expected_calls .lock() @@ -75,6 +94,9 @@ impl MockUciManager { .push_back(ExpectedCall::CloseHal { expected_force, out }); } + /// Prepare Mock to expect device_reset. + /// + /// MockUciManager expects call with parameters, returns out as response. pub fn expect_device_reset(&mut self, expected_reset_config: ResetConfig, out: Result<()>) { self.expected_calls .lock() @@ -82,14 +104,23 @@ impl MockUciManager { .push_back(ExpectedCall::DeviceReset { expected_reset_config, out }); } + /// Prepare Mock to expect core_get_device_info. + /// + /// MockUciManager expects call, returns out as response. pub fn expect_core_get_device_info(&mut self, out: Result<GetDeviceInfoResponse>) { self.expected_calls.lock().unwrap().push_back(ExpectedCall::CoreGetDeviceInfo { out }); } + /// Prepare Mock to expect core_get_caps_info. + /// + /// MockUciManager expects call, returns out as response. pub fn expect_core_get_caps_info(&mut self, out: Result<Vec<CapTlv>>) { self.expected_calls.lock().unwrap().push_back(ExpectedCall::CoreGetCapsInfo { out }); } + /// Prepare Mock to expect core_set_config. + /// + /// MockUciManager expects call with parameters, returns out as response. pub fn expect_core_set_config( &mut self, expected_config_tlvs: Vec<DeviceConfigTlv>, @@ -101,6 +132,9 @@ impl MockUciManager { .push_back(ExpectedCall::CoreSetConfig { expected_config_tlvs, out }); } + /// Prepare Mock to expect core_get_config. + /// + /// MockUciManager expects call with parameters, returns out as response. pub fn expect_core_get_config( &mut self, expected_config_ids: Vec<DeviceConfigId>, @@ -112,6 +146,10 @@ impl MockUciManager { .push_back(ExpectedCall::CoreGetConfig { expected_config_ids, out }); } + /// Prepare Mock to expect session_init. + /// + /// MockUciManager expects call with parameters, returns out as response, followed by notfs + /// sent. pub fn expect_session_init( &mut self, expected_session_id: SessionId, @@ -127,6 +165,10 @@ impl MockUciManager { }); } + /// Prepare Mock to expect session_deinit. + /// + /// MockUciManager expects call with parameters, returns out as response, followed by notfs + /// sent. pub fn expect_session_deinit( &mut self, expected_session_id: SessionId, @@ -140,6 +182,10 @@ impl MockUciManager { }); } + /// Prepare Mock to expect session_set_app_config. + /// + /// MockUciManager expects call with parameters, returns out as response, followed by notfs + /// sent. pub fn expect_session_set_app_config( &mut self, expected_session_id: SessionId, @@ -155,6 +201,9 @@ impl MockUciManager { }); } + /// Prepare Mock to expect session_get_app_config. + /// + /// MockUciManager expects call with parameters, returns out as response. pub fn expect_session_get_app_config( &mut self, expected_session_id: SessionId, @@ -168,10 +217,16 @@ impl MockUciManager { }); } + /// Prepare Mock to expect session_get_count. + /// + /// MockUciManager expects call with parameters, returns out as response. pub fn expect_session_get_count(&mut self, out: Result<u8>) { self.expected_calls.lock().unwrap().push_back(ExpectedCall::SessionGetCount { out }); } + /// Prepare Mock to expect session_get_state. + /// + /// MockUciManager expects call with parameters, returns out as response. pub fn expect_session_get_state( &mut self, expected_session_id: SessionId, @@ -183,11 +238,15 @@ impl MockUciManager { .push_back(ExpectedCall::SessionGetState { expected_session_id, out }); } + /// Prepare Mock to expect update_controller_multicast_list. + /// + /// MockUciManager expects call with parameters, returns out as response, followed by notfs + /// sent. pub fn expect_session_update_controller_multicast_list( &mut self, expected_session_id: SessionId, expected_action: UpdateMulticastListAction, - expected_controlees: Vec<Controlee>, + expected_controlees: Controlees, notfs: Vec<UciNotification>, out: Result<()>, ) { @@ -202,25 +261,42 @@ impl MockUciManager { ); } - pub fn expect_session_update_controller_multicast_list_v2( + /// Prepare Mock to expect session_update_active_rounds_dt_tag. + /// + /// MockUciManager expects call with parameters, returns out as response. + pub fn expect_session_update_dt_tag_ranging_rounds( &mut self, - expected_session_id: SessionId, - expected_action: UpdateMulticastListAction, - expected_controlees: ControleesV2, - notfs: Vec<UciNotification>, - out: Result<()>, + expected_session_id: u32, + expected_ranging_round_indexes: Vec<u8>, + out: Result<SessionUpdateDtTagRangingRoundsResponse>, ) { self.expected_calls.lock().unwrap().push_back( - ExpectedCall::SessionUpdateControllerMulticastListV2 { + ExpectedCall::SessionUpdateDtTagRangingRounds { expected_session_id, - expected_action, - expected_controlees, - notfs, + expected_ranging_round_indexes, out, }, ); } + /// Prepare Mock to expect for session_query_max_data_size. + /// + /// MockUciManager expects call, returns out as response. + pub fn expect_session_query_max_data_size( + &mut self, + expected_session_id: SessionId, + out: Result<u16>, + ) { + self.expected_calls + .lock() + .unwrap() + .push_back(ExpectedCall::SessionQueryMaxDataSize { expected_session_id, out }); + } + + /// Prepare Mock to expect range_start. + /// + /// MockUciManager expects call with parameters, returns out as response, followed by notfs + /// sent. pub fn expect_range_start( &mut self, expected_session_id: SessionId, @@ -234,6 +310,10 @@ impl MockUciManager { }); } + /// Prepare Mock to expect range_stop. + /// + /// MockUciManager expects call with parameters, returns out as response, followed by notfs + /// sent. pub fn expect_range_stop( &mut self, expected_session_id: SessionId, @@ -247,6 +327,9 @@ impl MockUciManager { }); } + /// Prepare Mock to expect range_get_ranging_count. + /// + /// MockUciManager expects call with parameters, returns out as response. pub fn expect_range_get_ranging_count( &mut self, expected_session_id: SessionId, @@ -258,6 +341,9 @@ impl MockUciManager { .push_back(ExpectedCall::RangeGetRangingCount { expected_session_id, out }); } + /// Prepare Mock to expect android_set_country_code. + /// + /// MockUciManager expects call with parameters, returns out as response. pub fn expect_android_set_country_code( &mut self, expected_country_code: CountryCode, @@ -269,18 +355,26 @@ impl MockUciManager { .push_back(ExpectedCall::AndroidSetCountryCode { expected_country_code, out }); } + /// Prepare Mock to expect android_set_country_code. + /// + /// MockUciManager expects call with parameters, returns out as response. pub fn expect_android_get_power_stats(&mut self, out: Result<PowerStats>) { self.expected_calls.lock().unwrap().push_back(ExpectedCall::AndroidGetPowerStats { out }); } - pub fn expect_raw_vendor_cmd( + /// Prepare Mock to expect raw_uci_cmd. + /// + /// MockUciManager expects call with parameters, returns out as response. + pub fn expect_raw_uci_cmd( &mut self, + expected_mt: u32, expected_gid: u32, expected_oid: u32, expected_payload: Vec<u8>, - out: Result<RawVendorMessage>, + out: Result<RawUciMessage>, ) { - self.expected_calls.lock().unwrap().push_back(ExpectedCall::RawVendorCmd { + self.expected_calls.lock().unwrap().push_back(ExpectedCall::RawUciCmd { + expected_mt, expected_gid, expected_oid, expected_payload, @@ -288,6 +382,29 @@ impl MockUciManager { }); } + /// Prepare Mock to expect send_data_packet. + /// + /// MockUciManager expects call with parameters, returns out as response. + pub fn expect_send_data_packet( + &mut self, + expected_session_id: SessionId, + expected_address: Vec<u8>, + expected_dest_end_point: FiraComponent, + expected_uci_sequence_num: u8, + expected_app_payload_data: Vec<u8>, + out: Result<()>, + ) { + self.expected_calls.lock().unwrap().push_back(ExpectedCall::SendDataPacket { + expected_session_id, + expected_address, + expected_dest_end_point, + expected_uci_sequence_num, + expected_app_payload_data, + out, + }); + } + + /// Call Mock to send notifications. fn send_notifications(&self, notfs: Vec<UciNotification>) { for notf in notfs.into_iter() { match notf { @@ -305,9 +422,15 @@ impl MockUciManager { } } +impl Default for MockUciManager { + fn default() -> Self { + Self::new() + } +} + #[async_trait] impl UciManager for MockUciManager { - async fn set_logger_mode(&mut self, _logger_mode: UciLoggerMode) -> Result<()> { + async fn set_logger_mode(&self, _logger_mode: UciLoggerMode) -> Result<()> { Ok(()) } async fn set_core_notification_sender( @@ -324,12 +447,18 @@ impl UciManager for MockUciManager { } async fn set_vendor_notification_sender( &mut self, - vendor_notf_sender: mpsc::UnboundedSender<RawVendorMessage>, + vendor_notf_sender: mpsc::UnboundedSender<RawUciMessage>, ) { self.vendor_notf_sender = vendor_notf_sender; } + async fn set_data_rcv_notification_sender( + &mut self, + data_rcv_notf_sender: mpsc::UnboundedSender<DataRcvNotification>, + ) { + self.data_rcv_notf_sender = data_rcv_notf_sender; + } - async fn open_hal(&mut self) -> Result<()> { + async fn open_hal(&self) -> Result<()> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { Some(ExpectedCall::OpenHal { notfs, out }) => { @@ -345,7 +474,7 @@ impl UciManager for MockUciManager { } } - async fn close_hal(&mut self, force: bool) -> Result<()> { + async fn close_hal(&self, force: bool) -> Result<()> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { Some(ExpectedCall::CloseHal { expected_force, out }) if expected_force == force => { @@ -360,7 +489,7 @@ impl UciManager for MockUciManager { } } - async fn device_reset(&mut self, reset_config: ResetConfig) -> Result<()> { + async fn device_reset(&self, reset_config: ResetConfig) -> Result<()> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { Some(ExpectedCall::DeviceReset { expected_reset_config, out }) @@ -377,7 +506,7 @@ impl UciManager for MockUciManager { } } - async fn core_get_device_info(&mut self) -> Result<GetDeviceInfoResponse> { + async fn core_get_device_info(&self) -> Result<GetDeviceInfoResponse> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { Some(ExpectedCall::CoreGetDeviceInfo { out }) => { @@ -392,7 +521,7 @@ impl UciManager for MockUciManager { } } - async fn core_get_caps_info(&mut self) -> Result<Vec<CapTlv>> { + async fn core_get_caps_info(&self) -> Result<Vec<CapTlv>> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { Some(ExpectedCall::CoreGetCapsInfo { out }) => { @@ -408,7 +537,7 @@ impl UciManager for MockUciManager { } async fn core_set_config( - &mut self, + &self, config_tlvs: Vec<DeviceConfigTlv>, ) -> Result<CoreSetConfigResponse> { let mut expected_calls = self.expected_calls.lock().unwrap(); @@ -428,7 +557,7 @@ impl UciManager for MockUciManager { } async fn core_get_config( - &mut self, + &self, config_ids: Vec<DeviceConfigId>, ) -> Result<Vec<DeviceConfigTlv>> { let mut expected_calls = self.expected_calls.lock().unwrap(); @@ -447,11 +576,7 @@ impl UciManager for MockUciManager { } } - async fn session_init( - &mut self, - session_id: SessionId, - session_type: SessionType, - ) -> Result<()> { + async fn session_init(&self, session_id: SessionId, session_type: SessionType) -> Result<()> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { Some(ExpectedCall::SessionInit { @@ -472,7 +597,7 @@ impl UciManager for MockUciManager { } } - async fn session_deinit(&mut self, session_id: SessionId) -> Result<()> { + async fn session_deinit(&self, session_id: SessionId) -> Result<()> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { Some(ExpectedCall::SessionDeinit { expected_session_id, notfs, out }) @@ -491,7 +616,7 @@ impl UciManager for MockUciManager { } async fn session_set_app_config( - &mut self, + &self, session_id: SessionId, config_tlvs: Vec<AppConfigTlv>, ) -> Result<SetAppConfigResponse> { @@ -518,7 +643,7 @@ impl UciManager for MockUciManager { } async fn session_get_app_config( - &mut self, + &self, session_id: SessionId, config_ids: Vec<AppConfigTlvType>, ) -> Result<Vec<AppConfigTlv>> { @@ -540,7 +665,7 @@ impl UciManager for MockUciManager { } } - async fn session_get_count(&mut self) -> Result<u8> { + async fn session_get_count(&self) -> Result<u8> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { Some(ExpectedCall::SessionGetCount { out }) => { @@ -555,7 +680,7 @@ impl UciManager for MockUciManager { } } - async fn session_get_state(&mut self, session_id: SessionId) -> Result<SessionState> { + async fn session_get_state(&self, session_id: SessionId) -> Result<SessionState> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { Some(ExpectedCall::SessionGetState { expected_session_id, out }) @@ -573,10 +698,10 @@ impl UciManager for MockUciManager { } async fn session_update_controller_multicast_list( - &mut self, + &self, session_id: SessionId, action: UpdateMulticastListAction, - controlees: Vec<Controlee>, + controlees: Controlees, ) -> Result<()> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { @@ -588,9 +713,7 @@ impl UciManager for MockUciManager { out, }) if expected_session_id == session_id && expected_action == action - && zip(&expected_controlees, &controlees).all(|(a, b)| { - a.short_address == b.short_address && a.subsession_id == b.subsession_id - }) => + && expected_controlees == controlees => { self.expect_call_consumed.notify_one(); self.send_notifications(notfs); @@ -604,26 +727,21 @@ impl UciManager for MockUciManager { } } - async fn session_update_controller_multicast_list_v2( - &mut self, - session_id: SessionId, - action: UpdateMulticastListAction, - controlees: ControleesV2, - ) -> Result<()> { + async fn session_update_dt_tag_ranging_rounds( + &self, + session_id: u32, + ranging_round_indexes: Vec<u8>, + ) -> Result<SessionUpdateDtTagRangingRoundsResponse> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { - Some(ExpectedCall::SessionUpdateControllerMulticastListV2 { + Some(ExpectedCall::SessionUpdateDtTagRangingRounds { expected_session_id, - expected_action, - expected_controlees, - notfs, + expected_ranging_round_indexes, out, }) if expected_session_id == session_id - && expected_action == action - && expected_controlees == controlees => + && expected_ranging_round_indexes == ranging_round_indexes => { self.expect_call_consumed.notify_one(); - self.send_notifications(notfs); out } Some(call) => { @@ -634,7 +752,24 @@ impl UciManager for MockUciManager { } } - async fn range_start(&mut self, session_id: SessionId) -> Result<()> { + async fn session_query_max_data_size(&self, session_id: SessionId) -> Result<u16> { + let mut expected_calls = self.expected_calls.lock().unwrap(); + match expected_calls.pop_front() { + Some(ExpectedCall::SessionQueryMaxDataSize { expected_session_id, out }) + if expected_session_id == session_id => + { + self.expect_call_consumed.notify_one(); + out + } + Some(call) => { + expected_calls.push_front(call); + Err(Error::MockUndefined) + } + None => Err(Error::MockUndefined), + } + } + + async fn range_start(&self, session_id: SessionId) -> Result<()> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { Some(ExpectedCall::RangeStart { expected_session_id, notfs, out }) @@ -652,7 +787,7 @@ impl UciManager for MockUciManager { } } - async fn range_stop(&mut self, session_id: SessionId) -> Result<()> { + async fn range_stop(&self, session_id: SessionId) -> Result<()> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { Some(ExpectedCall::RangeStop { expected_session_id, notfs, out }) @@ -670,7 +805,7 @@ impl UciManager for MockUciManager { } } - async fn range_get_ranging_count(&mut self, session_id: SessionId) -> Result<usize> { + async fn range_get_ranging_count(&self, session_id: SessionId) -> Result<usize> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { Some(ExpectedCall::RangeGetRangingCount { expected_session_id, out }) @@ -687,7 +822,7 @@ impl UciManager for MockUciManager { } } - async fn android_set_country_code(&mut self, country_code: CountryCode) -> Result<()> { + async fn android_set_country_code(&self, country_code: CountryCode) -> Result<()> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { Some(ExpectedCall::AndroidSetCountryCode { expected_country_code, out }) @@ -704,7 +839,7 @@ impl UciManager for MockUciManager { } } - async fn android_get_power_stats(&mut self) -> Result<PowerStats> { + async fn android_get_power_stats(&self) -> Result<PowerStats> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { Some(ExpectedCall::AndroidGetPowerStats { out }) => { @@ -719,20 +854,60 @@ impl UciManager for MockUciManager { } } - async fn raw_vendor_cmd( - &mut self, + async fn raw_uci_cmd( + &self, + mt: u32, gid: u32, oid: u32, payload: Vec<u8>, - ) -> Result<RawVendorMessage> { + ) -> Result<RawUciMessage> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { - Some(ExpectedCall::RawVendorCmd { + Some(ExpectedCall::RawUciCmd { + expected_mt, expected_gid, expected_oid, expected_payload, out, - }) if expected_gid == gid && expected_oid == oid && expected_payload == payload => { + }) if expected_mt == mt + && expected_gid == gid + && expected_oid == oid + && expected_payload == payload => + { + self.expect_call_consumed.notify_one(); + out + } + Some(call) => { + expected_calls.push_front(call); + Err(Error::MockUndefined) + } + None => Err(Error::MockUndefined), + } + } + + async fn send_data_packet( + &self, + session_id: SessionId, + address: Vec<u8>, + dest_end_point: FiraComponent, + uci_sequence_num: u8, + app_payload_data: Vec<u8>, + ) -> Result<()> { + let mut expected_calls = self.expected_calls.lock().unwrap(); + match expected_calls.pop_front() { + Some(ExpectedCall::SendDataPacket { + expected_session_id, + expected_address, + expected_dest_end_point, + expected_uci_sequence_num, + expected_app_payload_data, + out, + }) if expected_session_id == session_id + && expected_address == address + && expected_dest_end_point == dest_end_point + && expected_uci_sequence_num == uci_sequence_num + && expected_app_payload_data == app_payload_data => + { self.expect_call_consumed.notify_one(); out } @@ -805,16 +980,18 @@ enum ExpectedCall { SessionUpdateControllerMulticastList { expected_session_id: SessionId, expected_action: UpdateMulticastListAction, - expected_controlees: Vec<Controlee>, + expected_controlees: Controlees, notfs: Vec<UciNotification>, out: Result<()>, }, - SessionUpdateControllerMulticastListV2 { + SessionUpdateDtTagRangingRounds { + expected_session_id: u32, + expected_ranging_round_indexes: Vec<u8>, + out: Result<SessionUpdateDtTagRangingRoundsResponse>, + }, + SessionQueryMaxDataSize { expected_session_id: SessionId, - expected_action: UpdateMulticastListAction, - expected_controlees: ControleesV2, - notfs: Vec<UciNotification>, - out: Result<()>, + out: Result<u16>, }, RangeStart { expected_session_id: SessionId, @@ -837,10 +1014,19 @@ enum ExpectedCall { AndroidGetPowerStats { out: Result<PowerStats>, }, - RawVendorCmd { + RawUciCmd { + expected_mt: u32, expected_gid: u32, expected_oid: u32, expected_payload: Vec<u8>, - out: Result<RawVendorMessage>, + out: Result<RawUciMessage>, + }, + SendDataPacket { + expected_session_id: SessionId, + expected_address: Vec<u8>, + expected_dest_end_point: FiraComponent, + expected_uci_sequence_num: u8, + expected_app_payload_data: Vec<u8>, + out: Result<()>, }, } diff --git a/src/rust/uwb_core/src/uci/notification.rs b/src/rust/uwb_core/src/uci/notification.rs index f3507ba..8007158 100644 --- a/src/rust/uwb_core/src/uci/notification.rs +++ b/src/rust/uwb_core/src/uci/notification.rs @@ -15,29 +15,31 @@ use std::convert::{TryFrom, TryInto}; use log::{debug, error}; -use num_traits::ToPrimitive; -use uwb_uci_packets::{parse_diagnostics_ntf, Packet}; +use uwb_uci_packets::{parse_diagnostics_ntf, Packet, UCI_PACKET_HEADER_LEN}; use crate::error::{Error, Result}; +use crate::params::fira_app_config_params::UwbAddress; use crate::params::uci_packets::{ - ControleeStatus, DeviceState, ExtendedAddressTwoWayRangingMeasurement, RangingMeasurementType, - RawVendorMessage, ReasonCode, SessionId, SessionState, ShortAddressTwoWayRangingMeasurement, - StatusCode, + ControleeStatus, CreditAvailability, DataRcvStatusCode, DataTransferNtfStatusCode, DeviceState, + ExtendedAddressDlTdoaRangingMeasurement, ExtendedAddressOwrAoaRangingMeasurement, + ExtendedAddressTwoWayRangingMeasurement, FiraComponent, RangingMeasurementType, RawUciMessage, + SessionId, SessionState, ShortAddressDlTdoaRangingMeasurement, + ShortAddressOwrAoaRangingMeasurement, ShortAddressTwoWayRangingMeasurement, StatusCode, }; /// enum of all UCI notifications with structured fields. #[derive(Debug, Clone, PartialEq)] pub enum UciNotification { - /// CoreNotificationPacket equivalent. + /// CoreNotification equivalent. Core(CoreNotification), - /// SessionNotificationPacket equivalent. + /// SessionNotification equivalent. Session(SessionNotification), /// UciVendor_X_Notification equivalent. - Vendor(RawVendorMessage), + Vendor(RawUciMessage), } /// UCI CoreNotification. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum CoreNotification { /// DeviceStatusNtf equivalent. DeviceStatus(DeviceState), @@ -55,7 +57,7 @@ pub enum SessionNotification { /// uwb_uci_packets::SessionState. session_state: SessionState, /// uwb_uci_packets::Reasoncode. - reason_code: ReasonCode, + reason_code: u8, }, /// SessionUpdateControllerMulticastListNtf equivalent. UpdateControllerMulticastList { @@ -66,8 +68,24 @@ pub enum SessionNotification { /// list of controlees. status_list: Vec<ControleeStatus>, }, - /// (Short/Extended)Mac()RangeDataNtf equivalent - RangeData(SessionRangeData), + /// (Short/Extended)Mac()SessionInfoNtf equivalent + SessionInfo(SessionRangeData), + /// DataCreditNtf equivalent. + DataCredit { + /// SessionId : u32 + session_id: SessionId, + /// Credit Availability (for sending Data packets on UWB Session) + credit_availability: CreditAvailability, + }, + /// DataTransferStatusNtf equivalent. + DataTransferStatus { + /// SessionId : u32 + session_id: SessionId, + /// Sequence Number: u8 + uci_sequence_number: u8, + /// Data Transfer Status Code + status: DataTransferNtfStatusCode, + }, } /// The session range data. @@ -99,11 +117,69 @@ pub struct SessionRangeData { /// The ranging measurements. #[derive(Debug, Clone, PartialEq)] pub enum RangingMeasurements { - /// The measurement with short address. - Short(Vec<ShortAddressTwoWayRangingMeasurement>), + /// A Two-Way measurement with short address. + ShortAddressTwoWay(Vec<ShortAddressTwoWayRangingMeasurement>), - /// The measurement with extended address. - Extended(Vec<ExtendedAddressTwoWayRangingMeasurement>), + /// A Two-Way measurement with extended address. + ExtendedAddressTwoWay(Vec<ExtendedAddressTwoWayRangingMeasurement>), + + /// Dl-TDoA measurement with short address. + ShortAddressDltdoa(Vec<ShortAddressDlTdoaRangingMeasurement>), + + /// Dl-TDoA measurement with extended address. + ExtendedAddressDltdoa(Vec<ExtendedAddressDlTdoaRangingMeasurement>), + + /// OWR for AoA measurement with short address. + ShortAddressOwrAoa(ShortAddressOwrAoaRangingMeasurement), + + /// OWR for AoA measurement with extended address. + ExtendedAddressOwrAoa(ExtendedAddressOwrAoaRangingMeasurement), +} + +/// The DATA_RCV packet +#[derive(Debug, Clone)] +pub struct DataRcvNotification { + /// The identifier of the session on which data transfer is happening. + pub session_id: SessionId, + + /// The status of the data rx. + pub status: DataRcvStatusCode, + + /// The sequence number of the data packet. + pub uci_sequence_num: u32, + + /// MacAddress of the sender of the application data. + pub source_address: UwbAddress, + + /// Identifier for the source FiraComponent. + pub source_fira_component: FiraComponent, + + /// Identifier for the destination FiraComponent. + pub dest_fira_component: FiraComponent, + + /// Application Payload Data + pub payload: Vec<u8>, +} + +impl TryFrom<uwb_uci_packets::UciDataPacket> for DataRcvNotification { + type Error = Error; + fn try_from(evt: uwb_uci_packets::UciDataPacket) -> std::result::Result<Self, Self::Error> { + match evt.specialize() { + uwb_uci_packets::UciDataPacketChild::UciDataRcv(evt) => Ok(DataRcvNotification { + session_id: evt.get_session_id(), + status: evt.get_status(), + uci_sequence_num: evt.get_uci_sequence_number(), + source_address: UwbAddress::Extended(evt.get_source_mac_address().to_le_bytes()), + source_fira_component: evt.get_source_fira_component(), + dest_fira_component: evt.get_dest_fira_component(), + payload: evt.get_data().to_vec(), + }), + _ => { + error!("Unknown UciData packet: {:?}", evt); + Err(Error::Unknown) + } + } + } } impl UciNotification { @@ -115,16 +191,18 @@ impl UciNotification { } } -impl TryFrom<uwb_uci_packets::UciNotificationPacket> for UciNotification { +impl TryFrom<uwb_uci_packets::UciNotification> for UciNotification { type Error = Error; - fn try_from( - evt: uwb_uci_packets::UciNotificationPacket, - ) -> std::result::Result<Self, Self::Error> { + fn try_from(evt: uwb_uci_packets::UciNotification) -> std::result::Result<Self, Self::Error> { use uwb_uci_packets::UciNotificationChild; match evt.specialize() { UciNotificationChild::CoreNotification(evt) => Ok(Self::Core(evt.try_into()?)), - UciNotificationChild::SessionNotification(evt) => Ok(Self::Session(evt.try_into()?)), - UciNotificationChild::RangingNotification(evt) => Ok(Self::Session(evt.try_into()?)), + UciNotificationChild::SessionConfigNotification(evt) => { + Ok(Self::Session(evt.try_into()?)) + } + UciNotificationChild::SessionControlNotification(evt) => { + Ok(Self::Session(evt.try_into()?)) + } UciNotificationChild::AndroidNotification(evt) => evt.try_into(), UciNotificationChild::UciVendor_9_Notification(evt) => vendor_notification(evt.into()), UciNotificationChild::UciVendor_A_Notification(evt) => vendor_notification(evt.into()), @@ -132,18 +210,16 @@ impl TryFrom<uwb_uci_packets::UciNotificationPacket> for UciNotification { UciNotificationChild::UciVendor_E_Notification(evt) => vendor_notification(evt.into()), UciNotificationChild::UciVendor_F_Notification(evt) => vendor_notification(evt.into()), _ => { - error!("Unknown UciNotificationPacket: {:?}", evt); + error!("Unknown UciNotification: {:?}", evt); Err(Error::Unknown) } } } } -impl TryFrom<uwb_uci_packets::CoreNotificationPacket> for CoreNotification { +impl TryFrom<uwb_uci_packets::CoreNotification> for CoreNotification { type Error = Error; - fn try_from( - evt: uwb_uci_packets::CoreNotificationPacket, - ) -> std::result::Result<Self, Self::Error> { + fn try_from(evt: uwb_uci_packets::CoreNotification) -> std::result::Result<Self, Self::Error> { use uwb_uci_packets::CoreNotificationChild; match evt.specialize() { CoreNotificationChild::DeviceStatusNtf(evt) => { @@ -151,26 +227,26 @@ impl TryFrom<uwb_uci_packets::CoreNotificationPacket> for CoreNotification { } CoreNotificationChild::GenericError(evt) => Ok(Self::GenericError(evt.get_status())), _ => { - error!("Unknown CoreNotificationPacket: {:?}", evt); + error!("Unknown CoreNotification: {:?}", evt); Err(Error::Unknown) } } } } -impl TryFrom<uwb_uci_packets::SessionNotificationPacket> for SessionNotification { +impl TryFrom<uwb_uci_packets::SessionConfigNotification> for SessionNotification { type Error = Error; fn try_from( - evt: uwb_uci_packets::SessionNotificationPacket, + evt: uwb_uci_packets::SessionConfigNotification, ) -> std::result::Result<Self, Self::Error> { - use uwb_uci_packets::SessionNotificationChild; + use uwb_uci_packets::SessionConfigNotificationChild; match evt.specialize() { - SessionNotificationChild::SessionStatusNtf(evt) => Ok(Self::Status { + SessionConfigNotificationChild::SessionStatusNtf(evt) => Ok(Self::Status { session_id: evt.get_session_id(), session_state: evt.get_session_state(), reason_code: evt.get_reason_code(), }), - SessionNotificationChild::SessionUpdateControllerMulticastListNtf(evt) => { + SessionConfigNotificationChild::SessionUpdateControllerMulticastListNtf(evt) => { Ok(Self::UpdateControllerMulticastList { session_id: evt.get_session_id(), remaining_multicast_list_size: evt.get_remaining_multicast_list_size() as usize, @@ -178,49 +254,114 @@ impl TryFrom<uwb_uci_packets::SessionNotificationPacket> for SessionNotification }) } _ => { - error!("Unknown SessionNotificationPacket: {:?}", evt); + error!("Unknown SessionConfigNotification: {:?}", evt); Err(Error::Unknown) } } } } -impl TryFrom<uwb_uci_packets::RangingNotificationPacket> for SessionNotification { +impl TryFrom<uwb_uci_packets::SessionControlNotification> for SessionNotification { type Error = Error; fn try_from( - evt: uwb_uci_packets::RangingNotificationPacket, + evt: uwb_uci_packets::SessionControlNotification, ) -> std::result::Result<Self, Self::Error> { - use uwb_uci_packets::RangingNotificationChild; + use uwb_uci_packets::SessionControlNotificationChild; match evt.specialize() { - RangingNotificationChild::RangeDataNtf(evt) => evt.try_into(), + SessionControlNotificationChild::SessionInfoNtf(evt) => evt.try_into(), + SessionControlNotificationChild::DataCreditNtf(evt) => Ok(Self::DataCredit { + session_id: evt.get_session_id(), + credit_availability: evt.get_credit_availability(), + }), + SessionControlNotificationChild::DataTransferStatusNtf(evt) => { + Ok(Self::DataTransferStatus { + session_id: evt.get_session_id(), + uci_sequence_number: evt.get_uci_sequence_number(), + status: evt.get_status(), + }) + } _ => { - error!("Unknown RangingNotificationPacket: {:?}", evt); + error!("Unknown SessionControlNotification: {:?}", evt); Err(Error::Unknown) } } } } -impl TryFrom<uwb_uci_packets::RangeDataNtfPacket> for SessionNotification { +impl TryFrom<uwb_uci_packets::SessionInfoNtf> for SessionNotification { type Error = Error; - fn try_from( - evt: uwb_uci_packets::RangeDataNtfPacket, - ) -> std::result::Result<Self, Self::Error> { - let raw_ranging_data = evt.clone().to_vec(); - use uwb_uci_packets::RangeDataNtfChild; + fn try_from(evt: uwb_uci_packets::SessionInfoNtf) -> std::result::Result<Self, Self::Error> { + let raw_ranging_data = evt.clone().to_bytes()[UCI_PACKET_HEADER_LEN..].to_vec(); + use uwb_uci_packets::SessionInfoNtfChild; let ranging_measurements = match evt.specialize() { - RangeDataNtfChild::ShortMacTwoWayRangeDataNtf(evt) => { - RangingMeasurements::Short(evt.get_two_way_ranging_measurements().clone()) + SessionInfoNtfChild::ShortMacTwoWaySessionInfoNtf(evt) => { + RangingMeasurements::ShortAddressTwoWay( + evt.get_two_way_ranging_measurements().clone(), + ) + } + SessionInfoNtfChild::ExtendedMacTwoWaySessionInfoNtf(evt) => { + RangingMeasurements::ExtendedAddressTwoWay( + evt.get_two_way_ranging_measurements().clone(), + ) } - RangeDataNtfChild::ExtendedMacTwoWayRangeDataNtf(evt) => { - RangingMeasurements::Extended(evt.get_two_way_ranging_measurements().clone()) + SessionInfoNtfChild::ShortMacOwrAoaSessionInfoNtf(evt) => { + if evt.get_owr_aoa_ranging_measurements().clone().len() == 1 { + RangingMeasurements::ShortAddressOwrAoa( + evt.get_owr_aoa_ranging_measurements().clone().pop().unwrap(), + ) + } else { + error!("Wrong count of OwrAoA ranging measurements {:?}", evt); + return Err(Error::BadParameters); + } + } + SessionInfoNtfChild::ExtendedMacOwrAoaSessionInfoNtf(evt) => { + if evt.get_owr_aoa_ranging_measurements().clone().len() == 1 { + RangingMeasurements::ExtendedAddressOwrAoa( + evt.get_owr_aoa_ranging_measurements().clone().pop().unwrap(), + ) + } else { + error!("Wrong count of OwrAoA ranging measurements {:?}", evt); + return Err(Error::BadParameters); + } + } + SessionInfoNtfChild::ShortMacDlTDoASessionInfoNtf(evt) => { + match ShortAddressDlTdoaRangingMeasurement::parse( + evt.get_dl_tdoa_measurements(), + evt.get_no_of_ranging_measurements(), + ) { + Some(v) => { + if v.len() == evt.get_no_of_ranging_measurements().into() { + RangingMeasurements::ShortAddressDltdoa(v) + } else { + error!("Wrong count of ranging measurements {:?}", evt); + return Err(Error::BadParameters); + } + } + None => return Err(Error::BadParameters), + } + } + SessionInfoNtfChild::ExtendedMacDlTDoASessionInfoNtf(evt) => { + match ExtendedAddressDlTdoaRangingMeasurement::parse( + evt.get_dl_tdoa_measurements(), + evt.get_no_of_ranging_measurements(), + ) { + Some(v) => { + if v.len() == evt.get_no_of_ranging_measurements().into() { + RangingMeasurements::ExtendedAddressDltdoa(v) + } else { + error!("Wrong count of ranging measurements {:?}", evt); + return Err(Error::BadParameters); + } + } + None => return Err(Error::BadParameters), + } } _ => { - error!("Unknown RangeDataNtfPacket: {:?}", evt); + error!("Unknown SessionInfoNtf: {:?}", evt); return Err(Error::Unknown); } }; - Ok(Self::RangeData(SessionRangeData { + Ok(Self::SessionInfo(SessionRangeData { sequence_number: evt.get_sequence_number(), session_id: evt.get_session_id(), current_ranging_interval_ms: evt.get_current_ranging_interval(), @@ -232,10 +373,10 @@ impl TryFrom<uwb_uci_packets::RangeDataNtfPacket> for SessionNotification { } } -impl TryFrom<uwb_uci_packets::AndroidNotificationPacket> for UciNotification { +impl TryFrom<uwb_uci_packets::AndroidNotification> for UciNotification { type Error = Error; fn try_from( - evt: uwb_uci_packets::AndroidNotificationPacket, + evt: uwb_uci_packets::AndroidNotification, ) -> std::result::Result<Self, Self::Error> { use uwb_uci_packets::AndroidNotificationChild; @@ -243,27 +384,21 @@ impl TryFrom<uwb_uci_packets::AndroidNotificationPacket> for UciNotification { if let AndroidNotificationChild::AndroidRangeDiagnosticsNtf(ntf) = evt.specialize() { debug!("Received diagnostic packet: {:?}", parse_diagnostics_ntf(ntf)); } else { - error!("Received unknown AndroidNotificationPacket: {:?}", evt); + error!("Received unknown AndroidNotification: {:?}", evt); } Err(Error::Unknown) } } -fn vendor_notification(evt: uwb_uci_packets::UciNotificationPacket) -> Result<UciNotification> { - Ok(UciNotification::Vendor(RawVendorMessage { - gid: evt.get_group_id().to_u32().ok_or_else(|| { - error!("Failed to get gid from packet: {:?}", evt); - Error::Unknown - })?, - oid: evt.get_opcode().to_u32().ok_or_else(|| { - error!("Failed to get opcode from packet: {:?}", evt); - Error::Unknown - })?, +fn vendor_notification(evt: uwb_uci_packets::UciNotification) -> Result<UciNotification> { + Ok(UciNotification::Vendor(RawUciMessage { + gid: evt.get_group_id().into(), + oid: evt.get_opcode().into(), payload: get_vendor_uci_payload(evt)?, })) } -fn get_vendor_uci_payload(evt: uwb_uci_packets::UciNotificationPacket) -> Result<Vec<u8>> { +fn get_vendor_uci_payload(evt: uwb_uci_packets::UciNotification) -> Result<Vec<u8>> { match evt.specialize() { uwb_uci_packets::UciNotificationChild::UciVendor_9_Notification(evt) => { match evt.specialize() { @@ -315,12 +450,14 @@ fn get_vendor_uci_payload(evt: uwb_uci_packets::UciNotificationPacket) -> Result #[cfg(test)] mod tests { use super::*; + use crate::params::uci_packets::OwrAoaStatusCode; + #[test] fn test_ranging_measurements_trait() { - let empty_short_ranging_measurements = RangingMeasurements::Short(vec![]); + let empty_short_ranging_measurements = RangingMeasurements::ShortAddressTwoWay(vec![]); assert_eq!(empty_short_ranging_measurements, empty_short_ranging_measurements); - let extended_ranging_measurements = - RangingMeasurements::Extended(vec![ExtendedAddressTwoWayRangingMeasurement { + let extended_ranging_measurements = RangingMeasurements::ExtendedAddressTwoWay(vec![ + ExtendedAddressTwoWayRangingMeasurement { mac_address: 0x1234_5678_90ab, status: StatusCode::UciStatusOk, nlos: 0, @@ -335,10 +472,12 @@ mod tests { aoa_destination_elevation_fom: 12, slot_index: 0, rssi: u8::MAX, - }]); + }, + ]); let extended_ranging_measurements_copy = extended_ranging_measurements.clone(); assert_eq!(extended_ranging_measurements, extended_ranging_measurements_copy); - let empty_extended_ranging_measurements = RangingMeasurements::Extended(vec![]); + let empty_extended_ranging_measurements = + RangingMeasurements::ExtendedAddressTwoWay(vec![]); assert_eq!(empty_short_ranging_measurements, empty_short_ranging_measurements); //short and extended measurements are unequal even if both are empty: assert_ne!(empty_short_ranging_measurements, empty_extended_ranging_measurements); @@ -350,7 +489,7 @@ mod tests { } .build(); let core_notification = - uwb_uci_packets::CoreNotificationPacket::try_from(generic_error_packet).unwrap(); + uwb_uci_packets::CoreNotification::try_from(generic_error_packet).unwrap(); let core_notification = CoreNotification::try_from(core_notification).unwrap(); let uci_notification_from_generic_error = UciNotification::Core(core_notification); assert_eq!( @@ -367,7 +506,7 @@ mod tests { } .build(); let core_notification = - uwb_uci_packets::CoreNotificationPacket::try_from(device_status_ntf_packet).unwrap(); + uwb_uci_packets::CoreNotification::try_from(device_status_ntf_packet).unwrap(); let uci_notification = CoreNotification::try_from(core_notification).unwrap(); let uci_notification_from_device_status_ntf = UciNotification::Core(uci_notification); assert_eq!( @@ -377,8 +516,9 @@ mod tests { )) ); } + #[test] - fn test_session_notification_casting_from_extended_mac_two_way_range_data_ntf() { + fn test_session_notification_casting_from_extended_mac_two_way_session_info_ntf() { let extended_measurement = uwb_uci_packets::ExtendedAddressTwoWayRangingMeasurement { mac_address: 0x1234_5678_90ab, status: StatusCode::UciStatusOk, @@ -395,30 +535,33 @@ mod tests { slot_index: 0, rssi: u8::MAX, }; - let extended_two_way_range_data_ntf = - uwb_uci_packets::ExtendedMacTwoWayRangeDataNtfBuilder { + let extended_two_way_session_info_ntf = + uwb_uci_packets::ExtendedMacTwoWaySessionInfoNtfBuilder { sequence_number: 0x10, session_id: 0x11, rcr_indicator: 0x12, current_ranging_interval: 0x13, two_way_ranging_measurements: vec![extended_measurement.clone()], + vendor_data: vec![], } .build(); - let raw_ranging_data = extended_two_way_range_data_ntf.clone().to_vec(); + let raw_ranging_data = + extended_two_way_session_info_ntf.clone().to_bytes()[UCI_PACKET_HEADER_LEN..].to_vec(); let range_notification = - uwb_uci_packets::RangingNotificationPacket::try_from(extended_two_way_range_data_ntf) - .unwrap(); + uwb_uci_packets::SessionInfoNtf::try_from(extended_two_way_session_info_ntf).unwrap(); let session_notification = SessionNotification::try_from(range_notification).unwrap(); - let uci_notification_from_extended_two_way_range_data_ntf = + let uci_notification_from_extended_two_way_session_info_ntf = UciNotification::Session(session_notification); assert_eq!( - uci_notification_from_extended_two_way_range_data_ntf, - UciNotification::Session(SessionNotification::RangeData(SessionRangeData { + uci_notification_from_extended_two_way_session_info_ntf, + UciNotification::Session(SessionNotification::SessionInfo(SessionRangeData { sequence_number: 0x10, session_id: 0x11, ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::TwoWay, current_ranging_interval_ms: 0x13, - ranging_measurements: RangingMeasurements::Extended(vec![extended_measurement]), + ranging_measurements: RangingMeasurements::ExtendedAddressTwoWay(vec![ + extended_measurement + ]), rcr_indicator: 0x12, raw_ranging_data, })) @@ -426,7 +569,7 @@ mod tests { } #[test] - fn test_session_notification_casting_from_short_mac_two_way_range_data_ntf() { + fn test_session_notification_casting_from_short_mac_two_way_session_info_ntf() { let short_measurement = uwb_uci_packets::ShortAddressTwoWayRangingMeasurement { mac_address: 0x1234, status: StatusCode::UciStatusOk, @@ -443,29 +586,121 @@ mod tests { slot_index: 0, rssi: u8::MAX, }; - let short_two_way_range_data_ntf = uwb_uci_packets::ShortMacTwoWayRangeDataNtfBuilder { + let short_two_way_session_info_ntf = uwb_uci_packets::ShortMacTwoWaySessionInfoNtfBuilder { sequence_number: 0x10, session_id: 0x11, rcr_indicator: 0x12, current_ranging_interval: 0x13, two_way_ranging_measurements: vec![short_measurement.clone()], + vendor_data: vec![0x02, 0x01], } .build(); - let raw_ranging_data = short_two_way_range_data_ntf.clone().to_vec(); + let raw_ranging_data = + short_two_way_session_info_ntf.clone().to_bytes()[UCI_PACKET_HEADER_LEN..].to_vec(); let range_notification = - uwb_uci_packets::RangingNotificationPacket::try_from(short_two_way_range_data_ntf) - .unwrap(); + uwb_uci_packets::SessionInfoNtf::try_from(short_two_way_session_info_ntf).unwrap(); let session_notification = SessionNotification::try_from(range_notification).unwrap(); - let uci_notification_from_short_two_way_range_data_ntf = + let uci_notification_from_short_two_way_session_info_ntf = UciNotification::Session(session_notification); assert_eq!( - uci_notification_from_short_two_way_range_data_ntf, - UciNotification::Session(SessionNotification::RangeData(SessionRangeData { + uci_notification_from_short_two_way_session_info_ntf, + UciNotification::Session(SessionNotification::SessionInfo(SessionRangeData { sequence_number: 0x10, session_id: 0x11, ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::TwoWay, current_ranging_interval_ms: 0x13, - ranging_measurements: RangingMeasurements::Short(vec![short_measurement]), + ranging_measurements: RangingMeasurements::ShortAddressTwoWay(vec![ + short_measurement + ]), + rcr_indicator: 0x12, + raw_ranging_data, + })) + ); + } + + #[test] + fn test_session_notification_casting_from_extended_mac_owr_aoa_session_info_ntf() { + let extended_measurement = uwb_uci_packets::ExtendedAddressOwrAoaRangingMeasurement { + mac_address: 0x1234_5678_90ab, + status: OwrAoaStatusCode::UciStatusSuccess, + nlos: 0, + frame_sequence_number: 1, + block_index: 1, + aoa_azimuth: 5, + aoa_azimuth_fom: 6, + aoa_elevation: 7, + aoa_elevation_fom: 8, + }; + let extended_owr_aoa_session_info_ntf = + uwb_uci_packets::ExtendedMacOwrAoaSessionInfoNtfBuilder { + sequence_number: 0x10, + session_id: 0x11, + rcr_indicator: 0x12, + current_ranging_interval: 0x13, + owr_aoa_ranging_measurements: vec![extended_measurement.clone()], + vendor_data: vec![], + } + .build(); + let raw_ranging_data = + extended_owr_aoa_session_info_ntf.clone().to_bytes()[UCI_PACKET_HEADER_LEN..].to_vec(); + let range_notification = + uwb_uci_packets::SessionInfoNtf::try_from(extended_owr_aoa_session_info_ntf).unwrap(); + let session_notification = SessionNotification::try_from(range_notification).unwrap(); + let uci_notification_from_extended_owr_aoa_session_info_ntf = + UciNotification::Session(session_notification); + assert_eq!( + uci_notification_from_extended_owr_aoa_session_info_ntf, + UciNotification::Session(SessionNotification::SessionInfo(SessionRangeData { + sequence_number: 0x10, + session_id: 0x11, + ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::OwrAoa, + current_ranging_interval_ms: 0x13, + ranging_measurements: RangingMeasurements::ExtendedAddressOwrAoa( + extended_measurement + ), + rcr_indicator: 0x12, + raw_ranging_data, + })) + ); + } + + #[test] + fn test_session_notification_casting_from_short_mac_owr_aoa_session_info_ntf() { + let short_measurement = uwb_uci_packets::ShortAddressOwrAoaRangingMeasurement { + mac_address: 0x1234, + status: OwrAoaStatusCode::UciStatusSuccess, + nlos: 0, + frame_sequence_number: 1, + block_index: 1, + aoa_azimuth: 5, + aoa_azimuth_fom: 6, + aoa_elevation: 7, + aoa_elevation_fom: 8, + }; + let short_owr_aoa_session_info_ntf = uwb_uci_packets::ShortMacOwrAoaSessionInfoNtfBuilder { + sequence_number: 0x10, + session_id: 0x11, + rcr_indicator: 0x12, + current_ranging_interval: 0x13, + owr_aoa_ranging_measurements: vec![short_measurement.clone()], + vendor_data: vec![], + } + .build(); + let raw_ranging_data = + short_owr_aoa_session_info_ntf.clone().to_bytes()[UCI_PACKET_HEADER_LEN..].to_vec(); + let range_notification = + uwb_uci_packets::SessionInfoNtf::try_from(short_owr_aoa_session_info_ntf).unwrap(); + let session_notification = SessionNotification::try_from(range_notification).unwrap(); + let uci_notification_from_short_owr_aoa_session_info_ntf = + UciNotification::Session(session_notification); + assert_eq!( + uci_notification_from_short_owr_aoa_session_info_ntf, + UciNotification::Session(SessionNotification::SessionInfo(SessionRangeData { + sequence_number: 0x10, + session_id: 0x11, + ranging_measurement_type: uwb_uci_packets::RangingMeasurementType::OwrAoa, + current_ranging_interval_ms: 0x13, + ranging_measurements: RangingMeasurements::ShortAddressOwrAoa(short_measurement), rcr_indicator: 0x12, raw_ranging_data, })) @@ -477,11 +712,12 @@ mod tests { let session_status_ntf = uwb_uci_packets::SessionStatusNtfBuilder { session_id: 0x20, session_state: uwb_uci_packets::SessionState::SessionStateActive, - reason_code: uwb_uci_packets::ReasonCode::StateChangeWithSessionManagementCommands, + reason_code: uwb_uci_packets::ReasonCode::StateChangeWithSessionManagementCommands + .into(), } .build(); let session_notification_packet = - uwb_uci_packets::SessionNotificationPacket::try_from(session_status_ntf).unwrap(); + uwb_uci_packets::SessionConfigNotification::try_from(session_status_ntf).unwrap(); let session_notification = SessionNotification::try_from(session_notification_packet).unwrap(); let uci_notification_from_session_status_ntf = @@ -491,7 +727,8 @@ mod tests { UciNotification::Session(SessionNotification::Status { session_id: 0x20, session_state: uwb_uci_packets::SessionState::SessionStateActive, - reason_code: uwb_uci_packets::ReasonCode::StateChangeWithSessionManagementCommands, + reason_code: uwb_uci_packets::ReasonCode::StateChangeWithSessionManagementCommands + .into(), }) ); } @@ -516,7 +753,7 @@ mod tests { controlee_status: vec![controlee_status.clone(), another_controlee_status.clone()], } .build(); - let session_notification_packet = uwb_uci_packets::SessionNotificationPacket::try_from( + let session_notification_packet = uwb_uci_packets::SessionConfigNotification::try_from( session_update_controller_multicast_list_ntf, ) .unwrap(); @@ -537,11 +774,11 @@ mod tests { #[test] #[allow(non_snake_case)] //override snake case for vendor_A fn test_vendor_notification_casting() { - let vendor_9_empty_notification: uwb_uci_packets::UciNotificationPacket = + let vendor_9_empty_notification: uwb_uci_packets::UciNotification = uwb_uci_packets::UciVendor_9_NotificationBuilder { opcode: 0x40, payload: None } .build() .into(); - let vendor_A_nonempty_notification: uwb_uci_packets::UciNotificationPacket = + let vendor_A_nonempty_notification: uwb_uci_packets::UciNotification = uwb_uci_packets::UciVendor_A_NotificationBuilder { opcode: 0x41, payload: Some(bytes::Bytes::from_static(b"Placeholder notification.")), @@ -554,7 +791,7 @@ mod tests { UciNotification::try_from(vendor_A_nonempty_notification).unwrap(); assert_eq!( uci_notification_from_vendor_9, - UciNotification::Vendor(RawVendorMessage { + UciNotification::Vendor(RawUciMessage { gid: 0x9, // per enum GroupId in uci_packets.pdl oid: 0x40, payload: vec![], @@ -562,7 +799,7 @@ mod tests { ); assert_eq!( uci_notification_from_vendor_A, - UciNotification::Vendor(RawVendorMessage { + UciNotification::Vendor(RawUciMessage { gid: 0xa, oid: 0x41, payload: b"Placeholder notification.".to_owned().into(), diff --git a/src/rust/uwb_core/src/uci/pcapng_block.rs b/src/rust/uwb_core/src/uci/pcapng_block.rs index c94ddac..6fcc670 100644 --- a/src/rust/uwb_core/src/uci/pcapng_block.rs +++ b/src/rust/uwb_core/src/uci/pcapng_block.rs @@ -143,7 +143,7 @@ pub struct InterfaceDescriptionBlockBuilder { impl Default for InterfaceDescriptionBlockBuilder { fn default() -> Self { Self { - link_type: 293, // USB 2.0 Low Speed + link_type: 299, // FiRa UCI snap_len: 0, // unlimited block_options: vec![], } @@ -422,7 +422,7 @@ mod tests { #[test] fn test_interface_description_block_with_options_build() { let comment_opt = BlockOption::new(0x1, "ABCDEF".to_owned().into_bytes()); - let link_type: u16 = 293; // 0x125 + let link_type: u16 = 299; // 0x12b let snap_len: u32 = 0; let interface_description_block = InterfaceDescriptionBlockBuilder::new() .link_type(link_type) @@ -433,7 +433,7 @@ mod tests { let expected_block: Vec<u8> = vec![ 0x01, 0x00, 0x00, 0x00, // block type 0x24, 0x00, 0x00, 0x00, // block length - 0x25, 0x01, 0x00, 0x00, // link type, reserved + 0x2b, 0x01, 0x00, 0x00, // link type, reserved 0x00, 0x00, 0x00, 0x00, // SnapLen 0x01, 0x00, 0x06, 0x00, // option code, padded length 0x41, 0x42, 0x43, 0x44, // option (ABCD) diff --git a/src/rust/uwb_core/src/uci/pcapng_uci_logger_factory.rs b/src/rust/uwb_core/src/uci/pcapng_uci_logger_factory.rs index a00018b..04e1ef4 100644 --- a/src/rust/uwb_core/src/uci/pcapng_uci_logger_factory.rs +++ b/src/rust/uwb_core/src/uci/pcapng_uci_logger_factory.rs @@ -331,7 +331,7 @@ impl FileFactory { fn build_empty_file(&mut self) -> Option<BufferedFile> { self.rotate_file()?; let file_path = self.get_file_path(0); - BufferedFile::new(&file_path, self.buffer_size) + BufferedFile::new(&self.log_directory, &file_path, self.buffer_size) } /// get file path for log files of given index. @@ -376,12 +376,21 @@ struct BufferedFile { impl BufferedFile { /// Constructor. - pub fn new(file_path: &Path, buffer_size: usize) -> Option<Self> { + pub fn new(log_dir: &Path, file_path: &Path, buffer_size: usize) -> Option<Self> { if file_path.is_file() { if let Err(e) = fs::remove_file(file_path) { error!("UCI Log: failed to remove {}: {:?}", file_path.display(), e); }; } + if !log_dir.is_dir() { + if let Err(e) = fs::create_dir_all(log_dir) { + error!( + "UCI Log: failed to create log directory {}. Error: {:?}", + log_dir.display(), + e + ); + } + } let file = match fs::OpenOptions::new().write(true).create_new(true).open(file_path) { Ok(f) => f, Err(e) => { @@ -486,7 +495,35 @@ mod tests { } // Expect no log file created as no packet is received. let log_path = dir.as_ref().to_owned().join("log.pcapng"); - assert!(fs::read(&log_path).is_err()); + assert!(fs::read(log_path).is_err()); + } + + #[test] + fn test_no_preexisting_dir_created() { + let dir_root = Path::new("./uwb_test_dir_123"); + let dir = dir_root.join("this/path/doesnt/exist"); + { + let runtime = Builder::new_multi_thread().enable_all().build().unwrap(); + let mut file_manager = PcapngUciLoggerFactoryBuilder::new() + .buffer_size(1024) + .filename_prefix("log".to_owned()) + .log_path(dir.clone()) + .runtime_handle(runtime.handle().to_owned()) + .build() + .unwrap(); + let mut logger_0 = file_manager.build_logger("logger 0").unwrap(); + let packet_0 = UciVendor_A_NotificationBuilder { opcode: 0, payload: None }.build(); + logger_0.log_uci_control_packet(packet_0.into()); + // Sleep needed to guarantee handling pending logs before runtime goes out of scope. + thread::sleep(time::Duration::from_millis(10)); + } + // Expect the dir was created. + assert!(dir.is_dir()); + // Expect the log file exists. + let log_path = dir.join("log.pcapng"); + assert!(log_path.is_file()); + // Clear test dir + let _ = fs::remove_dir_all(dir_root); } #[test] @@ -503,19 +540,19 @@ mod tests { .unwrap(); let mut logger_0 = file_manager.build_logger("logger 0").unwrap(); let packet_0 = UciVendor_A_NotificationBuilder { opcode: 0, payload: None }.build(); - logger_0.log_uci_packet(packet_0.into()); + logger_0.log_uci_control_packet(packet_0.into()); let mut logger_1 = file_manager.build_logger("logger 1").unwrap(); let packet_1 = UciVendor_A_NotificationBuilder { opcode: 1, payload: None }.build(); - logger_1.log_uci_packet(packet_1.into()); + logger_1.log_uci_control_packet(packet_1.into()); let packet_2 = UciVendor_A_NotificationBuilder { opcode: 2, payload: None }.build(); - logger_0.log_uci_packet(packet_2.into()); + logger_0.log_uci_control_packet(packet_2.into()); // Sleep needed to guarantee handling pending logs before runtime goes out of scope. thread::sleep(time::Duration::from_millis(10)); } // Expect file log.pcapng consist of SHB->IDB(logger 0)->EPB(packet 0)->IDB(logger 1) // ->EPB(packet 1)->EPB(packet 2) let log_path = dir.as_ref().to_owned().join("log.pcapng"); - let log_content = fs::read(&log_path).unwrap(); + let log_content = fs::read(log_path).unwrap(); let block_info = get_block_info(log_content).unwrap(); assert_eq!(block_info.len(), 6); assert_eq!(block_info[0].0, 0x0A0D_0D0A); // SHB @@ -541,12 +578,12 @@ mod tests { .unwrap(); let mut logger_0 = file_manager_140.build_logger("logger 0").unwrap(); let packet_0 = UciVendor_A_NotificationBuilder { opcode: 0, payload: None }.build(); - logger_0.log_uci_packet(packet_0.into()); + logger_0.log_uci_control_packet(packet_0.into()); let mut logger_1 = file_manager_140.build_logger("logger 1").unwrap(); let packet_1 = UciVendor_A_NotificationBuilder { opcode: 1, payload: None }.build(); - logger_1.log_uci_packet(packet_1.into()); + logger_1.log_uci_control_packet(packet_1.into()); let packet_2 = UciVendor_A_NotificationBuilder { opcode: 2, payload: None }.build(); - logger_0.log_uci_packet(packet_2.into()); + logger_0.log_uci_control_packet(packet_2.into()); // Sleep needed to guarantee handling pending logs before runtime goes out of scope. thread::sleep(time::Duration::from_millis(10)); } @@ -555,7 +592,7 @@ mod tests { // File 1: SHB->IDB->IDB->EPB (cannot fit next) // File 0: SHB->IDB->IDB->EPB let log_path = dir.as_ref().to_owned().join("log_2.pcapng"); - let log_content = fs::read(&log_path).unwrap(); + let log_content = fs::read(log_path).unwrap(); let block_info = get_block_info(log_content).unwrap(); assert_eq!(block_info.len(), 4); assert_eq!(block_info[0].0, 0x0A0D_0D0A); // SHB @@ -563,7 +600,7 @@ mod tests { assert_eq!(block_info[2].0, 0x6); // EPB assert_eq!(block_info[3].0, 0x1); // IDB let log_path = dir.as_ref().to_owned().join("log_1.pcapng"); - let log_content = fs::read(&log_path).unwrap(); + let log_content = fs::read(log_path).unwrap(); let block_info = get_block_info(log_content).unwrap(); assert_eq!(block_info.len(), 4); assert_eq!(block_info[0].0, 0x0A0D_0D0A); // SHB @@ -571,7 +608,7 @@ mod tests { assert_eq!(block_info[2].0, 0x1); // IDB assert_eq!(block_info[3].0, 0x6); // EPB let log_path = dir.as_ref().to_owned().join("log.pcapng"); - let log_content = fs::read(&log_path).unwrap(); + let log_content = fs::read(log_path).unwrap(); let block_info = get_block_info(log_content).unwrap(); assert_eq!(block_info.len(), 4); assert_eq!(block_info[0].0, 0x0A0D_0D0A); // SHB @@ -595,12 +632,12 @@ mod tests { .unwrap(); let mut logger_0 = file_manager_144.build_logger("logger 0").unwrap(); let packet_0 = UciVendor_A_NotificationBuilder { opcode: 0, payload: None }.build(); - logger_0.log_uci_packet(packet_0.into()); + logger_0.log_uci_control_packet(packet_0.into()); let packet_2 = UciVendor_A_NotificationBuilder { opcode: 2, payload: None }.build(); - logger_0.log_uci_packet(packet_2.into()); + logger_0.log_uci_control_packet(packet_2.into()); let mut logger_1 = file_manager_144.build_logger("logger 1").unwrap(); let packet_1 = UciVendor_A_NotificationBuilder { opcode: 1, payload: None }.build(); - logger_1.log_uci_packet(packet_1.into()); + logger_1.log_uci_control_packet(packet_1.into()); // Sleep needed to guarantee handling pending logs before runtime goes out of scope. thread::sleep(time::Duration::from_millis(10)); } @@ -608,7 +645,7 @@ mod tests { // File 1: SHB->IDB->EPB->EPB (cannot fit next) // File 0: SHB->IDB->IDB->EPB let log_path = dir.as_ref().to_owned().join("log_1.pcapng"); - let log_content = fs::read(&log_path).unwrap(); + let log_content = fs::read(log_path).unwrap(); let block_info = get_block_info(log_content).unwrap(); assert_eq!(block_info.len(), 4); assert_eq!(block_info[0].0, 0x0A0D_0D0A); // SHB @@ -616,7 +653,7 @@ mod tests { assert_eq!(block_info[2].0, 0x6); // EPB assert_eq!(block_info[3].0, 0x6); // EPB let log_path = dir.as_ref().to_owned().join("log.pcapng"); - let log_content = fs::read(&log_path).unwrap(); + let log_content = fs::read(log_path).unwrap(); let block_info = get_block_info(log_content).unwrap(); assert_eq!(block_info.len(), 4); assert_eq!(block_info[0].0, 0x0A0D_0D0A); // SHB @@ -641,12 +678,12 @@ mod tests { .unwrap(); let mut logger_0 = file_manager_96.build_logger("logger 0").unwrap(); let packet_0 = UciVendor_A_NotificationBuilder { opcode: 0, payload: None }.build(); - logger_0.log_uci_packet(packet_0.into()); + logger_0.log_uci_control_packet(packet_0.into()); let packet_2 = UciVendor_A_NotificationBuilder { opcode: 2, payload: None }.build(); - logger_0.log_uci_packet(packet_2.into()); + logger_0.log_uci_control_packet(packet_2.into()); let mut logger_1 = file_manager_96.build_logger("logger 1").unwrap(); let packet_1 = UciVendor_A_NotificationBuilder { opcode: 1, payload: None }.build(); - logger_1.log_uci_packet(packet_1.into()); + logger_1.log_uci_control_packet(packet_1.into()); } } } diff --git a/src/rust/uwb_core/src/uci/response.rs b/src/rust/uwb_core/src/uci/response.rs index 50ba610..b0d5b2b 100644 --- a/src/rust/uwb_core/src/uci/response.rs +++ b/src/rust/uwb_core/src/uci/response.rs @@ -14,13 +14,11 @@ use std::convert::{TryFrom, TryInto}; -use log::error; -use num_traits::ToPrimitive; - use crate::error::{Error, Result}; use crate::params::uci_packets::{ AppConfigTlv, CapTlv, CoreSetConfigResponse, DeviceConfigTlv, GetDeviceInfoResponse, - PowerStats, RawVendorMessage, SessionState, SetAppConfigResponse, StatusCode, + PowerStats, RawUciMessage, SessionState, SessionUpdateDtTagRangingRoundsResponse, + SetAppConfigResponse, StatusCode, UciControlPacket, }; use crate::uci::error::status_code_to_result; @@ -42,12 +40,15 @@ pub(super) enum UciResponse { SessionGetCount(Result<u8>), SessionGetState(Result<SessionState>), SessionUpdateControllerMulticastList(Result<()>), - RangeStart(Result<()>), - RangeStop(Result<()>), - RangeGetRangingCount(Result<usize>), + SessionUpdateDtTagRangingRounds(Result<SessionUpdateDtTagRangingRoundsResponse>), + SessionQueryMaxDataSize(Result<u16>), + SessionStart(Result<()>), + SessionStop(Result<()>), + SessionGetRangingCount(Result<usize>), AndroidSetCountryCode(Result<()>), AndroidGetPowerStats(Result<PowerStats>), - RawVendorCmd(Result<RawVendorMessage>), + RawUciCmd(Result<RawUciMessage>), + SendUciData(Result<()>), } impl UciResponse { @@ -66,15 +67,20 @@ impl UciResponse { Self::SessionUpdateControllerMulticastList(result) => { Self::matches_result_retry(result) } - Self::RangeStart(result) => Self::matches_result_retry(result), - Self::RangeStop(result) => Self::matches_result_retry(result), - Self::RangeGetRangingCount(result) => Self::matches_result_retry(result), + Self::SessionUpdateDtTagRangingRounds(result) => Self::matches_result_retry(result), + Self::SessionStart(result) => Self::matches_result_retry(result), + Self::SessionStop(result) => Self::matches_result_retry(result), + Self::SessionGetRangingCount(result) => Self::matches_result_retry(result), Self::AndroidSetCountryCode(result) => Self::matches_result_retry(result), Self::AndroidGetPowerStats(result) => Self::matches_result_retry(result), - Self::RawVendorCmd(result) => Self::matches_result_retry(result), + Self::RawUciCmd(result) => Self::matches_result_retry(result), Self::CoreSetConfig(resp) => Self::matches_status_retry(&resp.status), Self::SessionSetAppConfig(resp) => Self::matches_status_retry(&resp.status), + + Self::SessionQueryMaxDataSize(result) => Self::matches_result_retry(result), + // TODO(b/273376343): Implement retry logic for Data packet send. + Self::SendUciData(_result) => false, } } @@ -86,30 +92,28 @@ impl UciResponse { } } -impl TryFrom<uwb_uci_packets::UciResponsePacket> for UciResponse { +impl TryFrom<uwb_uci_packets::UciResponse> for UciResponse { type Error = Error; - fn try_from(evt: uwb_uci_packets::UciResponsePacket) -> std::result::Result<Self, Self::Error> { + fn try_from(evt: uwb_uci_packets::UciResponse) -> std::result::Result<Self, Self::Error> { use uwb_uci_packets::UciResponseChild; match evt.specialize() { UciResponseChild::CoreResponse(evt) => evt.try_into(), - UciResponseChild::SessionResponse(evt) => evt.try_into(), - UciResponseChild::RangingResponse(evt) => evt.try_into(), + UciResponseChild::SessionConfigResponse(evt) => evt.try_into(), + UciResponseChild::SessionControlResponse(evt) => evt.try_into(), UciResponseChild::AndroidResponse(evt) => evt.try_into(), - UciResponseChild::UciVendor_9_Response(evt) => vendor_response(evt.into()), - UciResponseChild::UciVendor_A_Response(evt) => vendor_response(evt.into()), - UciResponseChild::UciVendor_B_Response(evt) => vendor_response(evt.into()), - UciResponseChild::UciVendor_E_Response(evt) => vendor_response(evt.into()), - UciResponseChild::UciVendor_F_Response(evt) => vendor_response(evt.into()), + UciResponseChild::UciVendor_9_Response(evt) => raw_response(evt.into()), + UciResponseChild::UciVendor_A_Response(evt) => raw_response(evt.into()), + UciResponseChild::UciVendor_B_Response(evt) => raw_response(evt.into()), + UciResponseChild::UciVendor_E_Response(evt) => raw_response(evt.into()), + UciResponseChild::UciVendor_F_Response(evt) => raw_response(evt.into()), _ => Err(Error::Unknown), } } } -impl TryFrom<uwb_uci_packets::CoreResponsePacket> for UciResponse { +impl TryFrom<uwb_uci_packets::CoreResponse> for UciResponse { type Error = Error; - fn try_from( - evt: uwb_uci_packets::CoreResponsePacket, - ) -> std::result::Result<Self, Self::Error> { + fn try_from(evt: uwb_uci_packets::CoreResponse) -> std::result::Result<Self, Self::Error> { use uwb_uci_packets::CoreResponseChild; match evt.specialize() { CoreResponseChild::GetDeviceInfoRsp(evt) => Ok(UciResponse::CoreGetDeviceInfo( @@ -142,63 +146,78 @@ impl TryFrom<uwb_uci_packets::CoreResponsePacket> for UciResponse { } } -impl TryFrom<uwb_uci_packets::SessionResponsePacket> for UciResponse { +impl TryFrom<uwb_uci_packets::SessionConfigResponse> for UciResponse { type Error = Error; fn try_from( - evt: uwb_uci_packets::SessionResponsePacket, + evt: uwb_uci_packets::SessionConfigResponse, ) -> std::result::Result<Self, Self::Error> { - use uwb_uci_packets::SessionResponseChild; + use uwb_uci_packets::SessionConfigResponseChild; match evt.specialize() { - SessionResponseChild::SessionInitRsp(evt) => { + SessionConfigResponseChild::SessionInitRsp(evt) => { Ok(UciResponse::SessionInit(status_code_to_result(evt.get_status()))) } - SessionResponseChild::SessionDeinitRsp(evt) => { + SessionConfigResponseChild::SessionDeinitRsp(evt) => { Ok(UciResponse::SessionDeinit(status_code_to_result(evt.get_status()))) } - SessionResponseChild::SessionGetCountRsp(evt) => Ok(UciResponse::SessionGetCount( - status_code_to_result(evt.get_status()).map(|_| evt.get_session_count()), - )), - SessionResponseChild::SessionGetStateRsp(evt) => Ok(UciResponse::SessionGetState( - status_code_to_result(evt.get_status()).map(|_| evt.get_session_state()), - )), - SessionResponseChild::SessionUpdateControllerMulticastListRsp(evt) => { + SessionConfigResponseChild::SessionGetCountRsp(evt) => { + Ok(UciResponse::SessionGetCount( + status_code_to_result(evt.get_status()).map(|_| evt.get_session_count()), + )) + } + SessionConfigResponseChild::SessionGetStateRsp(evt) => { + Ok(UciResponse::SessionGetState( + status_code_to_result(evt.get_status()).map(|_| evt.get_session_state()), + )) + } + SessionConfigResponseChild::SessionUpdateControllerMulticastListRsp(evt) => { Ok(UciResponse::SessionUpdateControllerMulticastList(status_code_to_result( evt.get_status(), ))) } - SessionResponseChild::SessionSetAppConfigRsp(evt) => { + SessionConfigResponseChild::SessionUpdateDtTagRangingRoundsRsp(evt) => { + Ok(UciResponse::SessionUpdateDtTagRangingRounds(Ok( + SessionUpdateDtTagRangingRoundsResponse { + status: evt.get_status(), + ranging_round_indexes: evt.get_ranging_round_indexes().to_vec(), + }, + ))) + } + SessionConfigResponseChild::SessionSetAppConfigRsp(evt) => { Ok(UciResponse::SessionSetAppConfig(SetAppConfigResponse { status: evt.get_status(), config_status: evt.get_cfg_status().clone(), })) } - SessionResponseChild::SessionGetAppConfigRsp(evt) => { + SessionConfigResponseChild::SessionGetAppConfigRsp(evt) => { Ok(UciResponse::SessionGetAppConfig( status_code_to_result(evt.get_status()).map(|_| { evt.get_tlvs().clone().into_iter().map(|tlv| tlv.into()).collect() }), )) } + SessionConfigResponseChild::SessionQueryMaxDataSizeRsp(evt) => { + Ok(UciResponse::SessionQueryMaxDataSize(Ok(evt.get_max_data_size()))) + } _ => Err(Error::Unknown), } } } -impl TryFrom<uwb_uci_packets::RangingResponsePacket> for UciResponse { +impl TryFrom<uwb_uci_packets::SessionControlResponse> for UciResponse { type Error = Error; fn try_from( - evt: uwb_uci_packets::RangingResponsePacket, + evt: uwb_uci_packets::SessionControlResponse, ) -> std::result::Result<Self, Self::Error> { - use uwb_uci_packets::RangingResponseChild; + use uwb_uci_packets::SessionControlResponseChild; match evt.specialize() { - RangingResponseChild::RangeStartRsp(evt) => { - Ok(UciResponse::RangeStart(status_code_to_result(evt.get_status()))) + SessionControlResponseChild::SessionStartRsp(evt) => { + Ok(UciResponse::SessionStart(status_code_to_result(evt.get_status()))) } - RangingResponseChild::RangeStopRsp(evt) => { - Ok(UciResponse::RangeStop(status_code_to_result(evt.get_status()))) + SessionControlResponseChild::SessionStopRsp(evt) => { + Ok(UciResponse::SessionStop(status_code_to_result(evt.get_status()))) } - RangingResponseChild::RangeGetRangingCountRsp(evt) => { - Ok(UciResponse::RangeGetRangingCount( + SessionControlResponseChild::SessionGetRangingCountRsp(evt) => { + Ok(UciResponse::SessionGetRangingCount( status_code_to_result(evt.get_status()).map(|_| evt.get_count() as usize), )) } @@ -207,11 +226,9 @@ impl TryFrom<uwb_uci_packets::RangingResponsePacket> for UciResponse { } } -impl TryFrom<uwb_uci_packets::AndroidResponsePacket> for UciResponse { +impl TryFrom<uwb_uci_packets::AndroidResponse> for UciResponse { type Error = Error; - fn try_from( - evt: uwb_uci_packets::AndroidResponsePacket, - ) -> std::result::Result<Self, Self::Error> { + fn try_from(evt: uwb_uci_packets::AndroidResponse) -> std::result::Result<Self, Self::Error> { use uwb_uci_packets::AndroidResponseChild; match evt.specialize() { AndroidResponseChild::AndroidSetCountryCodeRsp(evt) => { @@ -227,39 +244,9 @@ impl TryFrom<uwb_uci_packets::AndroidResponsePacket> for UciResponse { } } -fn vendor_response(evt: uwb_uci_packets::UciResponsePacket) -> Result<UciResponse> { - Ok(UciResponse::RawVendorCmd(Ok(RawVendorMessage { - gid: evt.get_group_id().to_u32().ok_or(Error::Unknown)?, - oid: evt.get_opcode().to_u32().ok_or(Error::Unknown)?, - payload: get_vendor_uci_payload(evt)?, - }))) -} - -fn get_vendor_uci_payload(data: uwb_uci_packets::UciResponsePacket) -> Result<Vec<u8>> { - match data.specialize() { - uwb_uci_packets::UciResponseChild::UciVendor_9_Response(evt) => match evt.specialize() { - uwb_uci_packets::UciVendor_9_ResponseChild::Payload(payload) => Ok(payload.to_vec()), - uwb_uci_packets::UciVendor_9_ResponseChild::None => Ok(Vec::new()), - }, - uwb_uci_packets::UciResponseChild::UciVendor_A_Response(evt) => match evt.specialize() { - uwb_uci_packets::UciVendor_A_ResponseChild::Payload(payload) => Ok(payload.to_vec()), - uwb_uci_packets::UciVendor_A_ResponseChild::None => Ok(Vec::new()), - }, - uwb_uci_packets::UciResponseChild::UciVendor_B_Response(evt) => match evt.specialize() { - uwb_uci_packets::UciVendor_B_ResponseChild::Payload(payload) => Ok(payload.to_vec()), - uwb_uci_packets::UciVendor_B_ResponseChild::None => Ok(Vec::new()), - }, - uwb_uci_packets::UciResponseChild::UciVendor_E_Response(evt) => match evt.specialize() { - uwb_uci_packets::UciVendor_E_ResponseChild::Payload(payload) => Ok(payload.to_vec()), - uwb_uci_packets::UciVendor_E_ResponseChild::None => Ok(Vec::new()), - }, - uwb_uci_packets::UciResponseChild::UciVendor_F_Response(evt) => match evt.specialize() { - uwb_uci_packets::UciVendor_F_ResponseChild::Payload(payload) => Ok(payload.to_vec()), - uwb_uci_packets::UciVendor_F_ResponseChild::None => Ok(Vec::new()), - }, - _ => { - error!("Invalid vendor response with gid {:?}", data.get_group_id()); - Err(Error::Unknown) - } - } +fn raw_response(evt: uwb_uci_packets::UciResponse) -> Result<UciResponse> { + let gid: u32 = evt.get_group_id().into(); + let oid: u32 = evt.get_opcode().into(); + let packet: UciControlPacket = evt.into(); + Ok(UciResponse::RawUciCmd(Ok(RawUciMessage { gid, oid, payload: packet.to_raw_payload() }))) } diff --git a/src/rust/uwb_core/src/uci/timeout_uci_hal.rs b/src/rust/uwb_core/src/uci/timeout_uci_hal.rs index 9a660ff..53c1bcb 100644 --- a/src/rust/uwb_core/src/uci/timeout_uci_hal.rs +++ b/src/rust/uwb_core/src/uci/timeout_uci_hal.rs @@ -24,7 +24,9 @@ use crate::params::uci_packets::SessionId; use crate::uci::command::UciCommand; use crate::uci::uci_hal::{UciHal, UciHalPacket}; -const HAL_API_TIMEOUT_MS: u64 = 800; +const HAL_API_TIMEOUT_MS: u64 = 1000; +// TODO(b/279175027): Reduce this once vendor fixes their initialization sequence. +const HAL_OPEN_TIMEOUT_MS: u64 = 20000; // Extra time may be needed for starting UWB stack. pub(crate) struct TimeoutUciHal<T: UciHal>(T); @@ -33,8 +35,11 @@ impl<T: UciHal> TimeoutUciHal<T> { Self(hal) } - async fn call_with_timeout(future: impl Future<Output = Result<()>>) -> Result<()> { - match timeout(Duration::from_millis(HAL_API_TIMEOUT_MS), future).await { + async fn call_with_timeout( + future: impl Future<Output = Result<()>>, + duration: u64, + ) -> Result<()> { + match timeout(Duration::from_millis(duration), future).await { Ok(result) => result, Err(_) => Err(Error::Timeout), } @@ -44,23 +49,24 @@ impl<T: UciHal> TimeoutUciHal<T> { #[async_trait] impl<T: UciHal> UciHal for TimeoutUciHal<T> { async fn open(&mut self, packet_sender: mpsc::UnboundedSender<UciHalPacket>) -> Result<()> { - Self::call_with_timeout(self.0.open(packet_sender)).await + Self::call_with_timeout(self.0.open(packet_sender), HAL_OPEN_TIMEOUT_MS).await } async fn close(&mut self) -> Result<()> { - Self::call_with_timeout(self.0.close()).await + Self::call_with_timeout(self.0.close(), HAL_API_TIMEOUT_MS).await } async fn notify_session_initialized(&mut self, session_id: SessionId) -> Result<()> { - Self::call_with_timeout(self.0.notify_session_initialized(session_id)).await + Self::call_with_timeout(self.0.notify_session_initialized(session_id), HAL_API_TIMEOUT_MS) + .await } async fn send_command(&mut self, cmd: UciCommand) -> Result<()> { - Self::call_with_timeout(self.0.send_command(cmd)).await + Self::call_with_timeout(self.0.send_command(cmd), HAL_API_TIMEOUT_MS).await } async fn send_packet(&mut self, packet: UciHalPacket) -> Result<()> { - Self::call_with_timeout(self.0.send_packet(packet)).await + Self::call_with_timeout(self.0.send_packet(packet), HAL_API_TIMEOUT_MS).await } } diff --git a/src/rust/uwb_core/src/uci/uci_hal.rs b/src/rust/uwb_core/src/uci/uci_hal.rs index aaf6d22..1153872 100644 --- a/src/rust/uwb_core/src/uci/uci_hal.rs +++ b/src/rust/uwb_core/src/uci/uci_hal.rs @@ -18,7 +18,7 @@ use std::convert::TryInto; use async_trait::async_trait; use tokio::sync::mpsc; -use uwb_uci_packets::{Packet, UciCommandPacket, UciPacketHalPacket, UciPacketPacket}; +use uwb_uci_packets::{Packet, UciControlPacket, UciControlPacketHal}; use crate::error::Result; use crate::params::uci_packets::SessionId; @@ -37,7 +37,7 @@ pub trait UciHal: 'static + Send { /// /// All the other API should be called after the open() completes successfully. Once the method /// completes successfully, the UciHal instance should store |packet_sender| and send the UCI - /// packets (responses or notifications) back to the caller via the |packet_sender|. + /// packets (responses, notifications, data) back to the caller via the |packet_sender|. async fn open(&mut self, packet_sender: mpsc::UnboundedSender<UciHalPacket>) -> Result<()>; /// Close the UCI HAL. @@ -54,9 +54,8 @@ pub trait UciHal: 'static + Send { // A UCI command message may consist of multiple UCI packets when the payload is over the // maximum packet size. We convert the command into list of UciHalPacket, then send the // packets via send_packet(). - let packet: UciCommandPacket = cmd.try_into()?; - let packet: UciPacketPacket = packet.into(); - let fragmented_packets: Vec<UciPacketHalPacket> = packet.into(); + let packet: UciControlPacket = cmd.try_into()?; + let fragmented_packets: Vec<UciControlPacketHal> = packet.into(); for packet in fragmented_packets.into_iter() { self.send_packet(packet.to_vec()).await?; } @@ -72,6 +71,21 @@ pub trait UciHal: 'static + Send { } } +/// A placeholder implementation for UciHal that do nothing. +pub struct NopUciHal {} +#[async_trait] +impl UciHal for NopUciHal { + async fn open(&mut self, _packet_sender: mpsc::UnboundedSender<UciHalPacket>) -> Result<()> { + Ok(()) + } + async fn close(&mut self) -> Result<()> { + Ok(()) + } + async fn send_packet(&mut self, _packet: UciHalPacket) -> Result<()> { + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/rust/uwb_core/src/uci/uci_logger.rs b/src/rust/uwb_core/src/uci/uci_logger.rs index d29b047..5023d7a 100644 --- a/src/rust/uwb_core/src/uci/uci_logger.rs +++ b/src/rust/uwb_core/src/uci/uci_logger.rs @@ -16,9 +16,10 @@ use std::convert::TryFrom; use uwb_uci_packets::{ - AppConfigTlv, AppConfigTlvType, SessionCommandChild, SessionGetAppConfigRspBuilder, - SessionResponseChild, SessionSetAppConfigCmdBuilder, UciCommandChild, UciCommandPacket, - UciPacketPacket, UciResponseChild, UciResponsePacket, + AppConfigTlv, AppConfigTlvType, Packet, SessionConfigCommandChild, SessionConfigResponseChild, + SessionGetAppConfigRspBuilder, SessionSetAppConfigCmdBuilder, UciCommandChild, + UciControlPacket, UciControlPacketChild, UciDataPacket, UciResponse, UciResponseChild, + UCI_PACKET_HAL_HEADER_LEN, }; use crate::error::{Error, Result}; @@ -50,8 +51,11 @@ impl TryFrom<String> for UciLoggerMode { /// Trait definition for the thread-safe uci logger pub trait UciLogger: 'static + Send + Sync { - /// Logs Uci Packet. - fn log_uci_packet(&mut self, packet: UciPacketPacket); + /// Logs Uci Control Packet. + fn log_uci_control_packet(&mut self, packet: UciControlPacket); + /// Logs Uci Data Packet. This is being passed as a reference since most of the time logging is + /// disabled, and so this will avoid copying the data payload. + fn log_uci_data_packet(&mut self, packet: &UciDataPacket); /// Logs hal open event. fn log_hal_open(&mut self, result: Result<()>); /// Logs hal close event. @@ -65,25 +69,28 @@ fn filter_tlv(mut tlv: AppConfigTlv) -> AppConfigTlv { tlv } -fn filter_uci_command(cmd: UciCommandPacket) -> UciCommandPacket { +fn filter_uci_command(cmd: UciControlPacket) -> UciControlPacket { match cmd.specialize() { - UciCommandChild::SessionCommand(session_cmd) => match session_cmd.specialize() { - SessionCommandChild::SessionSetAppConfigCmd(set_config_cmd) => { - let session_id = set_config_cmd.get_session_id(); - let tlvs = set_config_cmd.get_tlvs().to_owned(); - let filtered_tlvs = tlvs.into_iter().map(filter_tlv).collect(); - SessionSetAppConfigCmdBuilder { session_id, tlvs: filtered_tlvs }.build().into() - } - _ => session_cmd.into(), + UciControlPacketChild::UciCommand(control_cmd) => match control_cmd.specialize() { + UciCommandChild::SessionConfigCommand(session_cmd) => match session_cmd.specialize() { + SessionConfigCommandChild::SessionSetAppConfigCmd(set_config_cmd) => { + let session_id = set_config_cmd.get_session_id(); + let tlvs = set_config_cmd.get_tlvs().to_owned(); + let filtered_tlvs = tlvs.into_iter().map(filter_tlv).collect(); + SessionSetAppConfigCmdBuilder { session_id, tlvs: filtered_tlvs }.build().into() + } + _ => session_cmd.into(), + }, + _ => cmd, }, _ => cmd, } } -fn filter_uci_response(rsp: UciResponsePacket) -> UciResponsePacket { +fn filter_uci_response(rsp: UciResponse) -> UciResponse { match rsp.specialize() { - UciResponseChild::SessionResponse(session_rsp) => match session_rsp.specialize() { - SessionResponseChild::SessionGetAppConfigRsp(rsp) => { + UciResponseChild::SessionConfigResponse(session_rsp) => match session_rsp.specialize() { + SessionConfigResponseChild::SessionGetAppConfigRsp(rsp) => { let status = rsp.get_status(); let tlvs = rsp.get_tlvs().to_owned(); let filtered_tlvs = tlvs.into_iter().map(filter_tlv).collect(); @@ -95,6 +102,20 @@ fn filter_uci_response(rsp: UciResponsePacket) -> UciResponsePacket { } } +// Log only the Data Packet header bytes, so that we don't log any PII (payload bytes). +fn filter_uci_data( + packet: &UciDataPacket, +) -> std::result::Result<UciDataPacket, uwb_uci_packets::Error> { + // Initialize a (zeroed out) Vec to the same length as the data packet, and then copy over + // only the Data Packet header bytes into it. This masks out all the payload bytes to 0. + let data_packet_bytes: Vec<u8> = packet.clone().to_vec(); + let mut filtered_data_packet_bytes: Vec<u8> = vec![0; data_packet_bytes.len()]; + for (i, &b) in data_packet_bytes[..UCI_PACKET_HAL_HEADER_LEN].iter().enumerate() { + filtered_data_packet_bytes[i] = b; + } + UciDataPacket::parse(&filtered_data_packet_bytes) +} + /// Wrapper struct that filters messages feeded to UciLogger. pub(crate) struct UciLoggerWrapper<T: UciLogger> { mode: UciLoggerMode, @@ -127,41 +148,52 @@ impl<T: UciLogger> UciLoggerWrapper<T> { match self.mode { UciLoggerMode::Disabled => (), UciLoggerMode::Unfiltered => { - if let Ok(packet) = UciCommandPacket::try_from(cmd.clone()) { - self.logger.log_uci_packet(packet.into()); + if let Ok(packet) = UciControlPacket::try_from(cmd.clone()) { + self.logger.log_uci_control_packet(packet); }; } UciLoggerMode::Filtered => { - if let Ok(packet) = UciCommandPacket::try_from(cmd.clone()) { - self.logger.log_uci_packet(filter_uci_command(packet).into()); + if let Ok(packet) = UciControlPacket::try_from(cmd.clone()) { + self.logger.log_uci_control_packet(filter_uci_command(packet)); }; } } } - pub fn log_uci_response_or_notification(&mut self, packet: &UciPacketPacket) { + pub fn log_uci_response_or_notification(&mut self, packet: &UciControlPacket) { match self.mode { UciLoggerMode::Disabled => (), - UciLoggerMode::Unfiltered => self.logger.log_uci_packet(packet.clone()), + UciLoggerMode::Unfiltered => self.logger.log_uci_control_packet(packet.clone()), UciLoggerMode::Filtered => match packet.clone().specialize() { - uwb_uci_packets::UciPacketChild::UciResponse(packet) => { - self.logger.log_uci_packet(filter_uci_response(packet).into()) + uwb_uci_packets::UciControlPacketChild::UciResponse(packet) => { + self.logger.log_uci_control_packet(filter_uci_response(packet).into()) } - uwb_uci_packets::UciPacketChild::UciNotification(packet) => { - self.logger.log_uci_packet(packet.into()) + uwb_uci_packets::UciControlPacketChild::UciNotification(packet) => { + self.logger.log_uci_control_packet(packet.into()) } _ => (), }, } } + + pub fn log_uci_data(&mut self, packet: &UciDataPacket) { + if self.mode == UciLoggerMode::Disabled { + return; + } + if let Ok(filtered_packet) = filter_uci_data(packet) { + self.logger.log_uci_data_packet(&filtered_packet); + } + } } -/// A null UciLogger implementation that does nothing. +/// A placeholder UciLogger implementation that does nothing. #[derive(Default)] -pub struct UciLoggerNull {} +pub struct NopUciLogger {} + +impl UciLogger for NopUciLogger { + fn log_uci_control_packet(&mut self, _packet: UciControlPacket) {} -impl UciLogger for UciLoggerNull { - fn log_uci_packet(&mut self, _packet: UciPacketPacket) {} + fn log_uci_data_packet(&mut self, _packet: &UciDataPacket) {} fn log_hal_open(&mut self, _result: Result<()>) {} @@ -207,7 +239,7 @@ mod tests { #[test] fn test_log_response_filter() -> Result<()> { - let unfiltered_rsp: UciPacketPacket = SessionGetAppConfigRspBuilder { + let unfiltered_rsp: UciControlPacket = SessionGetAppConfigRspBuilder { status: StatusCode::UciStatusOk, tlvs: vec![ AppConfigTlv { cfg_id: AppConfigTlvType::StaticStsIv, v: vec![0, 1, 2] }, diff --git a/src/rust/uwb_core/src/uci/uci_logger_factory.rs b/src/rust/uwb_core/src/uci/uci_logger_factory.rs index aa8d408..7938c77 100644 --- a/src/rust/uwb_core/src/uci/uci_logger_factory.rs +++ b/src/rust/uwb_core/src/uci/uci_logger_factory.rs @@ -15,7 +15,7 @@ //! This file defines UciLoggerFactory, which manages the shared log file for multiple UciManager //! instances. -use crate::uci::uci_logger::{UciLogger, UciLoggerNull}; +use crate::uci::uci_logger::{NopUciLogger, UciLogger}; /// Trait definition for UciLoggerFactory, which builds UciLoggers that shares a single log file /// created by this struct. @@ -31,14 +31,13 @@ pub trait UciLoggerFactory { fn build_logger(&mut self, chip_id: &str) -> Option<Self::Logger>; } -/// The null implementation for UciLoggerFactory. +/// The UciLoggerFactory implementation that always builds NopUciLogger. #[derive(Default)] -pub struct UciLoggerFactoryNull {} -//UciLoggerFactoryNull builds UciLoggerNull. -impl UciLoggerFactory for UciLoggerFactoryNull { - type Logger = UciLoggerNull; +pub struct NopUciLoggerFactory {} +impl UciLoggerFactory for NopUciLoggerFactory { + type Logger = NopUciLogger; - fn build_logger(&mut self, _chip_id: &str) -> Option<UciLoggerNull> { - Some(UciLoggerNull::default()) + fn build_logger(&mut self, _chip_id: &str) -> Option<NopUciLogger> { + Some(NopUciLogger::default()) } } diff --git a/src/rust/uwb_core/src/uci/uci_logger_pcapng.rs b/src/rust/uwb_core/src/uci/uci_logger_pcapng.rs index 9cbdeab..279713b 100644 --- a/src/rust/uwb_core/src/uci/uci_logger_pcapng.rs +++ b/src/rust/uwb_core/src/uci/uci_logger_pcapng.rs @@ -15,7 +15,7 @@ //! Implements UciLoggerPcapng, a UciLogger with PCAPNG format log. use log::warn; -use uwb_uci_packets::UciPacketPacket; +use uwb_uci_packets::{UciControlPacket, UciDataPacket}; use crate::uci::pcapng_block::{BlockBuilder, BlockOption, EnhancedPacketBlockBuilder}; use crate::uci::pcapng_uci_logger_factory::LogWriter; @@ -42,7 +42,7 @@ impl UciLoggerPcapng { } impl UciLogger for UciLoggerPcapng { - fn log_uci_packet(&mut self, packet: UciPacketPacket) { + fn log_uci_control_packet(&mut self, packet: UciControlPacket) { let block_bytes = match EnhancedPacketBlockBuilder::new() .interface_id(self.interface_id) .packet(packet.into()) @@ -54,6 +54,18 @@ impl UciLogger for UciLoggerPcapng { self.send_block_bytes(block_bytes); } + fn log_uci_data_packet(&mut self, packet: &UciDataPacket) { + let packet_header_bytes = match EnhancedPacketBlockBuilder::new() + .interface_id(self.interface_id) + .packet(packet.clone().into()) + .into_le_bytes() + { + Some(b) => b, + None => return, + }; + self.send_block_bytes(packet_header_bytes); + } + fn log_hal_open(&mut self, result: crate::error::Result<()>) { let block_option = match result { Ok(_) => BlockOption::new(0x1, "HAL OPEN: OKAY".to_owned().into_bytes()), diff --git a/src/rust/uwb_core/src/uci/uci_manager.rs b/src/rust/uwb_core/src/uci/uci_manager.rs index f03e9ce..9d05224 100644 --- a/src/rust/uwb_core/src/uci/uci_manager.rs +++ b/src/rust/uwb_core/src/uci/uci_manager.rs @@ -23,18 +23,24 @@ use crate::uci::command::UciCommand; //use crate::uci::error::{Error, Result}; use crate::error::{Error, Result}; use crate::params::uci_packets::{ - AppConfigTlv, AppConfigTlvType, CapTlv, Controlee, ControleesV2, CoreSetConfigResponse, - CountryCode, DeviceConfigId, DeviceConfigTlv, DeviceState, GetDeviceInfoResponse, PowerStats, - RawVendorMessage, ResetConfig, SessionId, SessionState, SessionType, SetAppConfigResponse, - UpdateMulticastListAction, + AppConfigTlv, AppConfigTlvType, CapTlv, Controlees, CoreSetConfigResponse, CountryCode, + CreditAvailability, DeviceConfigId, DeviceConfigTlv, DeviceState, FiraComponent, + GetDeviceInfoResponse, GroupId, MessageType, PowerStats, RawUciMessage, ResetConfig, SessionId, + SessionState, SessionType, SessionUpdateDtTagRangingRoundsResponse, SetAppConfigResponse, + UciDataPacket, UciDataPacketHal, UpdateMulticastListAction, }; +use crate::params::utils::bytes_to_u64; use crate::uci::message::UciMessage; -use crate::uci::notification::{CoreNotification, SessionNotification, UciNotification}; +use crate::uci::notification::{ + CoreNotification, DataRcvNotification, SessionNotification, UciNotification, +}; use crate::uci::response::UciResponse; use crate::uci::timeout_uci_hal::TimeoutUciHal; use crate::uci::uci_hal::{UciHal, UciHalPacket}; use crate::uci::uci_logger::{UciLogger, UciLoggerMode, UciLoggerWrapper}; use crate::utils::{clean_mpsc_receiver, PinSleep}; +use std::collections::{HashMap, VecDeque}; +use uwb_uci_packets::{Packet, RawUciControlPacket, UciDataSnd, UciDefragPacket}; const UCI_TIMEOUT_MS: u64 = 800; const MAX_RETRY_COUNT: usize = 3; @@ -42,8 +48,8 @@ const MAX_RETRY_COUNT: usize = 3; /// The UciManager organizes the state machine of the UWB HAL, and provides the interface which /// abstracts the UCI commands, responses, and notifications. #[async_trait] -pub(crate) trait UciManager: 'static + Send + Clone { - async fn set_logger_mode(&mut self, logger_mode: UciLoggerMode) -> Result<()>; +pub trait UciManager: 'static + Send + Sync + Clone { + async fn set_logger_mode(&self, logger_mode: UciLoggerMode) -> Result<()>; // Set the sendor of the UCI notificaions. async fn set_core_notification_sender( &mut self, @@ -55,84 +61,104 @@ pub(crate) trait UciManager: 'static + Send + Clone { ); async fn set_vendor_notification_sender( &mut self, - vendor_notf_sender: mpsc::UnboundedSender<RawVendorMessage>, + vendor_notf_sender: mpsc::UnboundedSender<RawUciMessage>, + ); + async fn set_data_rcv_notification_sender( + &mut self, + data_rcv_notf_sender: mpsc::UnboundedSender<DataRcvNotification>, ); // Open the UCI HAL. // All the UCI commands should be called after the open_hal() completes successfully. - async fn open_hal(&mut self) -> Result<()>; + async fn open_hal(&self) -> Result<()>; // Close the UCI HAL. - async fn close_hal(&mut self, force: bool) -> Result<()>; + async fn close_hal(&self, force: bool) -> Result<()>; // Send the standard UCI Commands. - async fn device_reset(&mut self, reset_config: ResetConfig) -> Result<()>; - async fn core_get_device_info(&mut self) -> Result<GetDeviceInfoResponse>; - async fn core_get_caps_info(&mut self) -> Result<Vec<CapTlv>>; + async fn device_reset(&self, reset_config: ResetConfig) -> Result<()>; + async fn core_get_device_info(&self) -> Result<GetDeviceInfoResponse>; + async fn core_get_caps_info(&self) -> Result<Vec<CapTlv>>; async fn core_set_config( - &mut self, + &self, config_tlvs: Vec<DeviceConfigTlv>, ) -> Result<CoreSetConfigResponse>; async fn core_get_config( - &mut self, + &self, config_ids: Vec<DeviceConfigId>, ) -> Result<Vec<DeviceConfigTlv>>; - async fn session_init( - &mut self, - session_id: SessionId, - session_type: SessionType, - ) -> Result<()>; - async fn session_deinit(&mut self, session_id: SessionId) -> Result<()>; + async fn session_init(&self, session_id: SessionId, session_type: SessionType) -> Result<()>; + async fn session_deinit(&self, session_id: SessionId) -> Result<()>; async fn session_set_app_config( - &mut self, + &self, session_id: SessionId, config_tlvs: Vec<AppConfigTlv>, ) -> Result<SetAppConfigResponse>; async fn session_get_app_config( - &mut self, + &self, session_id: SessionId, config_ids: Vec<AppConfigTlvType>, ) -> Result<Vec<AppConfigTlv>>; - async fn session_get_count(&mut self) -> Result<u8>; - async fn session_get_state(&mut self, session_id: SessionId) -> Result<SessionState>; + async fn session_get_count(&self) -> Result<u8>; + async fn session_get_state(&self, session_id: SessionId) -> Result<SessionState>; async fn session_update_controller_multicast_list( - &mut self, - session_id: SessionId, - action: UpdateMulticastListAction, - controlees: Vec<Controlee>, - ) -> Result<()>; - async fn session_update_controller_multicast_list_v2( - &mut self, + &self, session_id: SessionId, action: UpdateMulticastListAction, - controlees: ControleesV2, + controlees: Controlees, ) -> Result<()>; - async fn range_start(&mut self, session_id: SessionId) -> Result<()>; - async fn range_stop(&mut self, session_id: SessionId) -> Result<()>; - async fn range_get_ranging_count(&mut self, session_id: SessionId) -> Result<usize>; + + // Update ranging rounds for DT Tag + async fn session_update_dt_tag_ranging_rounds( + &self, + session_id: u32, + ranging_round_indexes: Vec<u8>, + ) -> Result<SessionUpdateDtTagRangingRoundsResponse>; + + async fn session_query_max_data_size(&self, session_id: SessionId) -> Result<u16>; + + async fn range_start(&self, session_id: SessionId) -> Result<()>; + async fn range_stop(&self, session_id: SessionId) -> Result<()>; + async fn range_get_ranging_count(&self, session_id: SessionId) -> Result<usize>; // Send the Android-specific UCI commands - async fn android_set_country_code(&mut self, country_code: CountryCode) -> Result<()>; - async fn android_get_power_stats(&mut self) -> Result<PowerStats>; + async fn android_set_country_code(&self, country_code: CountryCode) -> Result<()>; + async fn android_get_power_stats(&self) -> Result<PowerStats>; - // Send a raw vendor command. - async fn raw_vendor_cmd( - &mut self, + // Send a raw uci command. + async fn raw_uci_cmd( + &self, + mt: u32, gid: u32, oid: u32, payload: Vec<u8>, - ) -> Result<RawVendorMessage>; + ) -> Result<RawUciMessage>; + + // Send a Data packet. + async fn send_data_packet( + &self, + session_id: SessionId, + address: Vec<u8>, + dest_end_point: FiraComponent, + uci_sequence_number: u8, + app_payload_data: Vec<u8>, + ) -> Result<()>; } /// UciManagerImpl is the main implementation of UciManager. Using the actor model, UciManagerImpl /// delegates the requests to UciManagerActor. #[derive(Clone)] -pub(crate) struct UciManagerImpl { +pub struct UciManagerImpl { cmd_sender: mpsc::UnboundedSender<(UciManagerCmd, oneshot::Sender<Result<UciResponse>>)>, } impl UciManagerImpl { - pub fn new<T: UciHal, U: UciLogger>(hal: T, logger: U, logger_mode: UciLoggerMode) -> Self { + /// Constructor. Need to be called in an async context. + pub(crate) fn new<T: UciHal, U: UciLogger>( + hal: T, + logger: U, + logger_mode: UciLoggerMode, + ) -> Self { let (cmd_sender, cmd_receiver) = mpsc::unbounded_channel(); let mut actor = UciManagerActor::new(hal, logger, logger_mode, cmd_receiver); tokio::spawn(async move { actor.run().await }); @@ -155,7 +181,7 @@ impl UciManagerImpl { #[async_trait] impl UciManager for UciManagerImpl { - async fn set_logger_mode(&mut self, logger_mode: UciLoggerMode) -> Result<()> { + async fn set_logger_mode(&self, logger_mode: UciLoggerMode) -> Result<()> { match self.send_cmd(UciManagerCmd::SetLoggerMode { logger_mode }).await { Ok(UciResponse::SetLoggerMode) => Ok(()), Ok(_) => Err(Error::Unknown), @@ -178,13 +204,21 @@ impl UciManager for UciManagerImpl { } async fn set_vendor_notification_sender( &mut self, - vendor_notf_sender: mpsc::UnboundedSender<RawVendorMessage>, + vendor_notf_sender: mpsc::UnboundedSender<RawUciMessage>, ) { let _ = self.send_cmd(UciManagerCmd::SetVendorNotificationSender { vendor_notf_sender }).await; } + async fn set_data_rcv_notification_sender( + &mut self, + data_rcv_notf_sender: mpsc::UnboundedSender<DataRcvNotification>, + ) { + let _ = self + .send_cmd(UciManagerCmd::SetDataRcvNotificationSender { data_rcv_notf_sender }) + .await; + } - async fn open_hal(&mut self) -> Result<()> { + async fn open_hal(&self) -> Result<()> { match self.send_cmd(UciManagerCmd::OpenHal).await { Ok(UciResponse::OpenHal) => { // According to the UCI spec: "The Host shall send CORE_GET_DEVICE_INFO_CMD to @@ -200,7 +234,7 @@ impl UciManager for UciManagerImpl { } } - async fn close_hal(&mut self, force: bool) -> Result<()> { + async fn close_hal(&self, force: bool) -> Result<()> { match self.send_cmd(UciManagerCmd::CloseHal { force }).await { Ok(UciResponse::CloseHal) => Ok(()), Ok(_) => Err(Error::Unknown), @@ -208,7 +242,7 @@ impl UciManager for UciManagerImpl { } } - async fn device_reset(&mut self, reset_config: ResetConfig) -> Result<()> { + async fn device_reset(&self, reset_config: ResetConfig) -> Result<()> { let cmd = UciCommand::DeviceReset { reset_config }; match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { Ok(UciResponse::DeviceReset(resp)) => resp, @@ -217,7 +251,7 @@ impl UciManager for UciManagerImpl { } } - async fn core_get_device_info(&mut self) -> Result<GetDeviceInfoResponse> { + async fn core_get_device_info(&self) -> Result<GetDeviceInfoResponse> { let cmd = UciCommand::CoreGetDeviceInfo; match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { Ok(UciResponse::CoreGetDeviceInfo(resp)) => resp, @@ -226,7 +260,7 @@ impl UciManager for UciManagerImpl { } } - async fn core_get_caps_info(&mut self) -> Result<Vec<CapTlv>> { + async fn core_get_caps_info(&self) -> Result<Vec<CapTlv>> { let cmd = UciCommand::CoreGetCapsInfo; match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { Ok(UciResponse::CoreGetCapsInfo(resp)) => resp, @@ -236,7 +270,7 @@ impl UciManager for UciManagerImpl { } async fn core_set_config( - &mut self, + &self, config_tlvs: Vec<DeviceConfigTlv>, ) -> Result<CoreSetConfigResponse> { let cmd = UciCommand::CoreSetConfig { config_tlvs }; @@ -247,10 +281,7 @@ impl UciManager for UciManagerImpl { } } - async fn core_get_config( - &mut self, - cfg_id: Vec<DeviceConfigId>, - ) -> Result<Vec<DeviceConfigTlv>> { + async fn core_get_config(&self, cfg_id: Vec<DeviceConfigId>) -> Result<Vec<DeviceConfigTlv>> { let cmd = UciCommand::CoreGetConfig { cfg_id }; match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { Ok(UciResponse::CoreGetConfig(resp)) => resp, @@ -259,11 +290,7 @@ impl UciManager for UciManagerImpl { } } - async fn session_init( - &mut self, - session_id: SessionId, - session_type: SessionType, - ) -> Result<()> { + async fn session_init(&self, session_id: SessionId, session_type: SessionType) -> Result<()> { let cmd = UciCommand::SessionInit { session_id, session_type }; match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { Ok(UciResponse::SessionInit(resp)) => resp, @@ -272,7 +299,7 @@ impl UciManager for UciManagerImpl { } } - async fn session_deinit(&mut self, session_id: SessionId) -> Result<()> { + async fn session_deinit(&self, session_id: SessionId) -> Result<()> { let cmd = UciCommand::SessionDeinit { session_id }; match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { Ok(UciResponse::SessionDeinit(resp)) => resp, @@ -282,7 +309,7 @@ impl UciManager for UciManagerImpl { } async fn session_set_app_config( - &mut self, + &self, session_id: SessionId, config_tlvs: Vec<AppConfigTlv>, ) -> Result<SetAppConfigResponse> { @@ -295,7 +322,7 @@ impl UciManager for UciManagerImpl { } async fn session_get_app_config( - &mut self, + &self, session_id: SessionId, app_cfg: Vec<AppConfigTlvType>, ) -> Result<Vec<AppConfigTlv>> { @@ -307,7 +334,7 @@ impl UciManager for UciManagerImpl { } } - async fn session_get_count(&mut self) -> Result<u8> { + async fn session_get_count(&self) -> Result<u8> { let cmd = UciCommand::SessionGetCount; match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { Ok(UciResponse::SessionGetCount(resp)) => resp, @@ -316,7 +343,7 @@ impl UciManager for UciManagerImpl { } } - async fn session_get_state(&mut self, session_id: SessionId) -> Result<SessionState> { + async fn session_get_state(&self, session_id: SessionId) -> Result<SessionState> { let cmd = UciCommand::SessionGetState { session_id }; match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { Ok(UciResponse::SessionGetState(resp)) => resp, @@ -326,12 +353,17 @@ impl UciManager for UciManagerImpl { } async fn session_update_controller_multicast_list( - &mut self, + &self, session_id: SessionId, action: UpdateMulticastListAction, - controlees: Vec<Controlee>, + controlees: Controlees, ) -> Result<()> { - if !(1..=8).contains(&controlees.len()) { + let controlees_len = match controlees { + Controlees::NoSessionKey(ref controlee_vec) => controlee_vec.len(), + Controlees::ShortSessionKey(ref controlee_vec) => controlee_vec.len(), + Controlees::LongSessionKey(ref controlee_vec) => controlee_vec.len(), + }; + if !(1..=8).contains(&controlees_len) { warn!("Number of controlees should be between 1 to 8"); return Err(Error::BadParameters); } @@ -344,58 +376,56 @@ impl UciManager for UciManagerImpl { } } - async fn session_update_controller_multicast_list_v2( - &mut self, - session_id: SessionId, - action: UpdateMulticastListAction, - controlees: ControleesV2, - ) -> Result<()> { - let controlees_len = match controlees { - ControleesV2::NoSessionKey(ref controlee_vec) => controlee_vec.len(), - ControleesV2::ShortSessionKey(ref controlee_vec) => controlee_vec.len(), - ControleesV2::LongSessionKey(ref controlee_vec) => controlee_vec.len(), - }; - if !(1..=8).contains(&controlees_len) { - warn!("Number of controlees should be between 1 to 8"); - return Err(Error::BadParameters); + async fn session_update_dt_tag_ranging_rounds( + &self, + session_id: u32, + ranging_round_indexes: Vec<u8>, + ) -> Result<SessionUpdateDtTagRangingRoundsResponse> { + let cmd = UciCommand::SessionUpdateDtTagRangingRounds { session_id, ranging_round_indexes }; + match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { + Ok(UciResponse::SessionUpdateDtTagRangingRounds(resp)) => resp, + Ok(_) => Err(Error::Unknown), + Err(e) => Err(e), } - let cmd = - UciCommand::SessionUpdateControllerMulticastListV2 { session_id, action, controlees }; + } + + async fn session_query_max_data_size(&self, session_id: SessionId) -> Result<u16> { + let cmd = UciCommand::SessionQueryMaxDataSize { session_id }; match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { - Ok(UciResponse::SessionUpdateControllerMulticastList(resp)) => resp, + Ok(UciResponse::SessionQueryMaxDataSize(resp)) => resp, Ok(_) => Err(Error::Unknown), Err(e) => Err(e), } } - async fn range_start(&mut self, session_id: SessionId) -> Result<()> { - let cmd = UciCommand::RangeStart { session_id }; + async fn range_start(&self, session_id: SessionId) -> Result<()> { + let cmd = UciCommand::SessionStart { session_id }; match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { - Ok(UciResponse::RangeStart(resp)) => resp, + Ok(UciResponse::SessionStart(resp)) => resp, Ok(_) => Err(Error::Unknown), Err(e) => Err(e), } } - async fn range_stop(&mut self, session_id: SessionId) -> Result<()> { - let cmd = UciCommand::RangeStop { session_id }; + async fn range_stop(&self, session_id: SessionId) -> Result<()> { + let cmd = UciCommand::SessionStop { session_id }; match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { - Ok(UciResponse::RangeStop(resp)) => resp, + Ok(UciResponse::SessionStop(resp)) => resp, Ok(_) => Err(Error::Unknown), Err(e) => Err(e), } } - async fn range_get_ranging_count(&mut self, session_id: SessionId) -> Result<usize> { - let cmd = UciCommand::RangeGetRangingCount { session_id }; + async fn range_get_ranging_count(&self, session_id: SessionId) -> Result<usize> { + let cmd = UciCommand::SessionGetRangingCount { session_id }; match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { - Ok(UciResponse::RangeGetRangingCount(resp)) => resp, + Ok(UciResponse::SessionGetRangingCount(resp)) => resp, Ok(_) => Err(Error::Unknown), Err(e) => Err(e), } } - async fn android_set_country_code(&mut self, country_code: CountryCode) -> Result<()> { + async fn android_set_country_code(&self, country_code: CountryCode) -> Result<()> { let cmd = UciCommand::AndroidSetCountryCode { country_code }; match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { Ok(UciResponse::AndroidSetCountryCode(resp)) => resp, @@ -404,7 +434,7 @@ impl UciManager for UciManagerImpl { } } - async fn android_get_power_stats(&mut self) -> Result<PowerStats> { + async fn android_get_power_stats(&self) -> Result<PowerStats> { let cmd = UciCommand::AndroidGetPowerStats; match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { Ok(UciResponse::AndroidGetPowerStats(resp)) => resp, @@ -413,15 +443,47 @@ impl UciManager for UciManagerImpl { } } - async fn raw_vendor_cmd( - &mut self, + async fn raw_uci_cmd( + &self, + mt: u32, gid: u32, oid: u32, payload: Vec<u8>, - ) -> Result<RawVendorMessage> { - let cmd = UciCommand::RawVendorCmd { gid, oid, payload }; + ) -> Result<RawUciMessage> { + let cmd = UciCommand::RawUciCmd { mt, gid, oid, payload }; match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { - Ok(UciResponse::RawVendorCmd(resp)) => resp, + Ok(UciResponse::RawUciCmd(resp)) => resp, + Ok(_) => Err(Error::Unknown), + Err(e) => Err(e), + } + } + + // Send a data packet to the UWBS (use the UciManagerActor). + async fn send_data_packet( + &self, + session_id: SessionId, + dest_mac_address_bytes: Vec<u8>, + dest_fira_component: FiraComponent, + uci_sequence_number: u8, + data: Vec<u8>, + ) -> Result<()> { + debug!( + "send_data_packet(): will Tx a data packet, session_id {}, sequence_number {}", + session_id, uci_sequence_number + ); + let dest_mac_address = + bytes_to_u64(dest_mac_address_bytes).ok_or(Error::BadParameters).unwrap(); + let data_snd_packet = uwb_uci_packets::UciDataSndBuilder { + session_id, + dest_mac_address, + dest_fira_component, + uci_sequence_number, + data, + } + .build(); + + match self.send_cmd(UciManagerCmd::SendUciData { data_snd_packet }).await { + Ok(UciResponse::SendUciData(resp)) => resp, Ok(_) => Err(Error::Unknown), Err(e) => Err(e), } @@ -438,7 +500,7 @@ struct UciManagerActor<T: UciHal, U: UciLogger> { // Set to true when |hal| is opened successfully. is_hal_opened: bool, - // Receive the response and the notification from |hal|. Only used when |hal| is opened + // Receive response, notification and data packets from |hal|. Only used when |hal| is opened // successfully. packet_receiver: mpsc::UnboundedReceiver<UciHalPacket>, // Defrag the UCI packets. @@ -447,20 +509,38 @@ struct UciManagerActor<T: UciHal, U: UciLogger> { // The response sender of UciManager's open_hal() method. Used to wait for the device ready // notification. open_hal_result_sender: Option<oneshot::Sender<Result<UciResponse>>>, + + // Store per-session CreditAvailability. This should be initialized when a UWB session becomes + // ACTIVE, and updated every time a Data packet fragment is sent or a DataCreditNtf is received. + data_credit_map: HashMap<SessionId, CreditAvailability>, + + // Store the Uci Data packet fragments to be sent to the UWBS, keyed by the SessionId. This + // helps to retrieve the next packet fragment to be sent, when the UWBS is ready to accept it. + data_packet_fragments_map: HashMap<SessionId, VecDeque<UciDataPacketHal>>, + // The timeout of waiting for the notification of device ready notification. wait_device_status_timeout: PinSleep, // Used for the logic of retrying the command. Only valid when waiting for the response of a // UCI command. - retryer: Option<Retryer>, + uci_cmd_retryer: Option<UciCmdRetryer>, // The timeout of waiting for the response. Only used when waiting for the response of a UCI // command. wait_resp_timeout: PinSleep, + // Used for the logic of retrying the DataSnd packet. Only valid when waiting for the + // DATA_TRANSFER_STATUS_NTF. + uci_data_snd_retryer: Option<UciDataSndRetryer>, + + // Used to identify if response corresponds to the last vendor command, if so return + // a raw packet as a response to the sender. + last_raw_cmd: Option<RawUciControlPacket>, + // Send the notifications to the caller of UciManager. core_notf_sender: mpsc::UnboundedSender<CoreNotification>, session_notf_sender: mpsc::UnboundedSender<SessionNotification>, - vendor_notf_sender: mpsc::UnboundedSender<RawVendorMessage>, + vendor_notf_sender: mpsc::UnboundedSender<RawUciMessage>, + data_rcv_notf_sender: mpsc::UnboundedSender<DataRcvNotification>, } impl<T: UciHal, U: UciLogger> UciManagerActor<T, U> { @@ -481,12 +561,17 @@ impl<T: UciHal, U: UciLogger> UciManagerActor<T, U> { packet_receiver: mpsc::unbounded_channel().1, defrager: Default::default(), open_hal_result_sender: None, + data_credit_map: HashMap::new(), + data_packet_fragments_map: HashMap::new(), wait_device_status_timeout: PinSleep::new(Duration::MAX), - retryer: None, + uci_cmd_retryer: None, + uci_data_snd_retryer: None, wait_resp_timeout: PinSleep::new(Duration::MAX), + last_raw_cmd: None, core_notf_sender: mpsc::unbounded_channel().0, session_notf_sender: mpsc::unbounded_channel().0, vendor_notf_sender: mpsc::unbounded_channel().0, + data_rcv_notf_sender: mpsc::unbounded_channel().0, } } @@ -507,35 +592,15 @@ impl<T: UciHal, U: UciLogger> UciManagerActor<T, U> { } } - // Handle the UCI response or notification from HAL. Only when HAL is opened. + // Handle the UCI response, notification or data packet from HAL. Only when HAL + // is opened. packet = self.packet_receiver.recv(), if self.is_hal_opened => { - match packet { - None => { - warn!("UciHal dropped the packet_sender unexpectedly."); - self.on_hal_closed(); - }, - Some(packet) => { - if let Some(packet) = self.defrager.defragment_packet(&packet) { - self.logger.log_uci_response_or_notification(&packet); - match packet.try_into() { - Ok(UciMessage::Response(resp)) => { - self.handle_response(resp).await; - } - Ok(UciMessage::Notification(notf)) => { - self.handle_notification(notf).await; - } - Err(e)=> { - error!("Failed to parse received message: {:?}", e); - } - } - } - }, - } + self.handle_hal_packet(packet).await; } // Timeout waiting for the response of the UCI command. _ = &mut self.wait_resp_timeout, if self.is_waiting_resp() => { - self.retryer.take().unwrap().send_result(Err(Error::Timeout)); + self.uci_cmd_retryer.take().unwrap().send_result(Err(Error::Timeout)); } // Timeout waiting for the notification of the device status. @@ -578,6 +643,10 @@ impl<T: UciHal, U: UciLogger> UciManagerActor<T, U> { self.vendor_notf_sender = vendor_notf_sender; let _ = result_sender.send(Ok(UciResponse::SetNotification)); } + UciManagerCmd::SetDataRcvNotificationSender { data_rcv_notf_sender } => { + self.data_rcv_notf_sender = data_rcv_notf_sender; + let _ = result_sender.send(Ok(UciResponse::SetNotification)); + } UciManagerCmd::OpenHal => { if self.is_hal_opened { warn!("The UCI HAL is already opened, skip."); @@ -626,27 +695,85 @@ impl<T: UciHal, U: UciLogger> UciManagerActor<T, U> { } UciManagerCmd::SendUciCommand { cmd } => { - debug_assert!(self.retryer.is_none()); - self.retryer = Some(Retryer { cmd, result_sender, retry_count: MAX_RETRY_COUNT }); - self.retry_command().await; + debug_assert!(self.uci_cmd_retryer.is_none()); + + // Remember that this command is a raw UCI command, we'll use this later + // to send a raw UCI response. + if let UciCommand::RawUciCmd { mt: _, gid, oid, payload: _ } = cmd.clone() { + let gid_u8 = u8::try_from(gid); + if gid_u8.is_err() || GroupId::try_from(gid_u8.unwrap()).is_err() { + error!("Received an invalid GID={} for RawUciCmd", gid); + let _ = result_sender.send(Err(Error::BadParameters)); + return; + } + + let oid_u8 = u8::try_from(oid); + if oid_u8.is_err() { + error!("Received an invalid OID={} for RawUciCmd", oid); + let _ = result_sender.send(Err(Error::BadParameters)); + return; + } + self.last_raw_cmd = Some(RawUciControlPacket { + mt: u8::from(MessageType::Command), + gid: gid_u8.unwrap(), + oid: oid_u8.unwrap(), + payload: Vec::new(), // There's no need to store the Raw UCI CMD's payload. + }); + } + + self.uci_cmd_retryer = + Some(UciCmdRetryer { cmd, result_sender, retry_count: MAX_RETRY_COUNT }); + self.retry_uci_cmd().await; + } + + UciManagerCmd::SendUciData { data_snd_packet } => { + let result = self.handle_data_snd_packet(data_snd_packet).await; + let _ = result_sender.send(result); } } } - async fn retry_command(&mut self) { - if let Some(mut retryer) = self.retryer.take() { - if !retryer.could_retry() { - retryer.send_result(Err(Error::Timeout)); + async fn retry_uci_cmd(&mut self) { + if let Some(mut uci_cmd_retryer) = self.uci_cmd_retryer.take() { + if !uci_cmd_retryer.could_retry() { + error!("Out of retries for Uci Cmd packet"); + uci_cmd_retryer.send_result(Err(Error::Timeout)); return; } - match self.send_uci_command(retryer.cmd.clone()).await { + match self.send_uci_command(uci_cmd_retryer.cmd.clone()).await { Ok(_) => { self.wait_resp_timeout = PinSleep::new(Duration::from_millis(UCI_TIMEOUT_MS)); - self.retryer = Some(retryer); + self.uci_cmd_retryer = Some(uci_cmd_retryer); + } + Err(e) => { + error!("Uci Cmd send resulted in error:{}", e); + uci_cmd_retryer.send_result(Err(e)); + } + } + } + } + + async fn retry_uci_data_snd(&mut self) { + if let Some(mut uci_data_snd_retryer) = self.uci_data_snd_retryer.take() { + let data_packet_session_id = uci_data_snd_retryer.data_packet_session_id; + if !uci_data_snd_retryer.could_retry() { + error!( + "Out of retries for Uci DataSnd packet, last DataSnd packet session_id:{}", + data_packet_session_id + ); + return; + } + + match self.hal.send_packet(uci_data_snd_retryer.data_packet.clone().to_vec()).await { + Ok(_) => { + self.uci_data_snd_retryer = Some(uci_data_snd_retryer); } Err(e) => { - retryer.send_result(Err(e)); + error!( + "DataSnd packet fragment session_id:{} retry failed with error:{}", + data_packet_session_id, e + ); } } } @@ -664,14 +791,172 @@ impl<T: UciHal, U: UciLogger> UciManagerActor<T, U> { result } + async fn handle_data_snd_packet(&mut self, data_snd_packet: UciDataSnd) -> Result<UciResponse> { + // Verify that there's an entry for the Session in the CreditAvailability map. + let data_packet_session_id = data_snd_packet.get_session_id(); + let data_packet_sequence_number = data_snd_packet.get_uci_sequence_number(); + + if !self.data_credit_map.contains_key(&data_packet_session_id) { + error!( + "DataSnd packet session_id:{}, sequence_number:{} cannot be sent as unknown \ + credit availability for the session", + data_packet_session_id, data_packet_sequence_number + ); + return Err(Error::PacketTxError); + } + + // Enqueue the data packet fragments, from the data packet to be sent to UWBS. + let mut packet_fragments: Vec<UciDataPacketHal> = data_snd_packet.into(); + if packet_fragments.is_empty() { + error!( + "DataSnd packet session_id:{}, sequence number:{} could not be split into fragments", + data_packet_session_id, data_packet_sequence_number + ); + return Err(Error::PacketTxError); + } + + match self.data_packet_fragments_map.get_mut(&data_packet_session_id) { + Some(q) => { + for p in packet_fragments.drain(..) { + q.push_back(p); + } + } + None => { + error!( + "DataSnd packet fragments map not found for session_id:{}", + data_packet_session_id + ); + return Err(Error::PacketTxError); + } + } + + self.send_data_packet_fragment(data_packet_session_id).await + } + + async fn send_data_packet_fragment( + &mut self, + data_packet_session_id: SessionId, + ) -> Result<UciResponse> { + // Check if a credit is available before sending this data packet fragment. If not, return + // for now, and send this packet later when the credit becomes available (indicated by + // receiving a DataCreditNtf). + let credit = self.data_credit_map.get(&data_packet_session_id); + if credit.is_none() { + error!( + "DataSnd packet fragment cannot be sent for session_id:{} as unknown \ + credit availability for the session", + data_packet_session_id + ); + return Err(Error::PacketTxError); + } + if credit == Some(&CreditAvailability::CreditNotAvailable) { + return Ok(UciResponse::SendUciData(Ok(()))); + } + + // We have credit available, let's send the packet to UWBS. + let hal_data_packet_fragment = + match self.data_packet_fragments_map.get_mut(&data_packet_session_id) { + Some(q) => { + match q.pop_front() { + Some(p) => p, + None => { + // No more packets left to send. + return Ok(UciResponse::SendUciData(Ok(()))); + } + } + } + None => { + return Err(Error::PacketTxError); + } + }; + + // Create and save a retryer for sending this data packet fragment. + self.uci_data_snd_retryer = Some(UciDataSndRetryer { + data_packet: hal_data_packet_fragment.clone(), + data_packet_session_id, + retry_count: MAX_RETRY_COUNT, + }); + + let result = self.hal.send_packet(hal_data_packet_fragment.to_vec()).await; + if result.is_err() { + error!( + "Result {:?} of sending data packet fragment SessionId: {} to HAL", + result, data_packet_session_id + ); + return Err(Error::PacketTxError); + } + + // Update the map after the successful write. + self.data_credit_map.insert(data_packet_session_id, CreditAvailability::CreditNotAvailable); + Ok(UciResponse::SendUciData(Ok(()))) + } + + async fn handle_hal_packet(&mut self, packet: Option<UciHalPacket>) { + let defrag_packet = match packet { + Some(rx_packet) => { + self.defrager.defragment_packet(&rx_packet, self.last_raw_cmd.clone()) + } + None => { + warn!("UciHal dropped the packet_sender unexpectedly."); + self.on_hal_closed(); + return; + } + }; + let defrag_packet = match defrag_packet { + Some(p) => p, + None => return, + }; + + match defrag_packet { + UciDefragPacket::Control(packet) => { + self.logger.log_uci_response_or_notification(&packet); + + match packet.try_into() { + Ok(UciMessage::Response(resp)) => { + self.handle_response(resp).await; + } + Ok(UciMessage::Notification(notf)) => { + self.handle_notification(notf).await; + } + Err(e) => { + error!("Failed to parse received message: {:?}", e); + } + } + } + UciDefragPacket::Data(packet) => { + self.logger.log_uci_data(&packet); + self.handle_data_rcv(packet); + } + UciDefragPacket::Raw(result, raw_uci_control_packet) => { + // Handle response to raw UCI cmd. We want to send it back as + // raw UCI message instead of standard response message. + let resp = match result { + Ok(()) => { + // We should receive only a valid UCI response packet here. + UciResponse::RawUciCmd(Ok(RawUciMessage { + gid: raw_uci_control_packet.gid.into(), + oid: raw_uci_control_packet.oid.into(), + payload: raw_uci_control_packet.payload, + })) + } + // TODO: Implement conversion between Error::InvalidPacketError (returned by + // lib.rs and defined in the PDL uci_packets.rs) and the uwb_core::Error enums. + Err(_) => UciResponse::RawUciCmd(Err(Error::Unknown)), + }; + self.handle_response(resp).await; + self.last_raw_cmd = None; + } + } + } + async fn handle_response(&mut self, resp: UciResponse) { if resp.need_retry() { - self.retry_command().await; + self.retry_uci_cmd().await; return; } - if let Some(retryer) = self.retryer.take() { - retryer.send_result(Ok(resp)); + if let Some(uci_cmd_retryer) = self.uci_cmd_retryer.take() { + uci_cmd_retryer.send_result(Ok(resp)); } else { warn!("Received an UCI response unexpectedly: {:?}", resp); } @@ -679,7 +964,10 @@ impl<T: UciHal, U: UciLogger> UciManagerActor<T, U> { async fn handle_notification(&mut self, notf: UciNotification) { if notf.need_retry() { - self.retry_command().await; + // Retry sending both last sent UCI CMD and UCI DataSnd packet since the notification + // could be for either of them. + self.retry_uci_cmd().await; + self.retry_uci_data_snd().await; return; } @@ -699,15 +987,48 @@ impl<T: UciHal, U: UciLogger> UciManagerActor<T, U> { let _ = self.core_notf_sender.send(core_notf); } UciNotification::Session(session_notf) => { - if let SessionNotification::Status { - session_id, - session_state: SessionState::SessionStateInit, - reason_code: _, - } = session_notf + if let SessionNotification::Status { session_id, session_state, reason_code: _ } = + session_notf + { + self.handle_session_state_notification(session_id, session_state).await; + } + if let SessionNotification::DataCredit { session_id, credit_availability } = + session_notf { - if let Err(e) = self.hal.notify_session_initialized(session_id).await { - warn!("notify_session_initialized() failed: {:?}", e); + if !self.data_credit_map.contains_key(&session_id) { + // Currently just log, as this is unexpected (the entry should exist once + // the ranging session is Active and be removed once it is Idle). + debug!( + "Received a DataCreditNtf for non-existent session_id: {}", + session_id + ); } + self.data_credit_map.insert(session_id, credit_availability); + if credit_availability == CreditAvailability::CreditAvailable { + if let Err(e) = self.send_data_packet_fragment(session_id).await { + error!( + "Sending data packet fragment failed with Err:{}, after a\ + DataCreditNtf is received, for sessionId:{}", + e, session_id + ); + } + } else { + // Log as this should usually not happen (it's not an error). + debug!( + "Received a DataCreditNtf with no credit available for session_id:{}", + session_id + ); + } + return; // We consume these here and don't need to send to upper layer. + } + if let SessionNotification::DataTransferStatus { + session_id: _, + uci_sequence_number: _, + status: _, + } = session_notf + { + // Reset the UciDataSnd Retryer since we received a DataTransferStatusNtf. + let _ = self.uci_data_snd_retryer.take(); } let _ = self.session_notf_sender.send(session_notf); } @@ -717,6 +1038,40 @@ impl<T: UciHal, U: UciLogger> UciManagerActor<T, U> { } } + async fn handle_session_state_notification( + &mut self, + session_id: SessionId, + session_state: SessionState, + ) { + match session_state { + SessionState::SessionStateInit => { + if let Err(e) = self.hal.notify_session_initialized(session_id).await { + warn!("notify_session_initialized() failed: {:?}", e); + } + } + SessionState::SessionStateActive => { + self.data_credit_map.insert(session_id, CreditAvailability::CreditAvailable); + self.data_packet_fragments_map.insert(session_id, VecDeque::new()); + } + SessionState::SessionStateIdle => { + self.data_credit_map.remove(&session_id); + self.data_packet_fragments_map.remove(&session_id); + } + _ => {} + } + } + + fn handle_data_rcv(&mut self, packet: UciDataPacket) { + match packet.try_into() { + Ok(data_rcv) => { + let _ = self.data_rcv_notf_sender.send(data_rcv); + } + Err(e) => { + error!("Unable to parse incoming Data packet, error {:?}", e); + } + } + } + fn on_hal_open(&mut self, packet_receiver: mpsc::UnboundedReceiver<UciHalPacket>) { self.is_hal_opened = true; self.packet_receiver = packet_receiver; @@ -725,10 +1080,11 @@ impl<T: UciHal, U: UciLogger> UciManagerActor<T, U> { fn on_hal_closed(&mut self) { self.is_hal_opened = false; self.packet_receiver = mpsc::unbounded_channel().1; + self.last_raw_cmd = None; } fn is_waiting_resp(&self) -> bool { - self.retryer.is_some() + self.uci_cmd_retryer.is_some() } fn is_waiting_device_status(&self) -> bool { self.open_hal_result_sender.is_some() @@ -742,13 +1098,13 @@ impl<T: UciHal, U: UciLogger> Drop for UciManagerActor<T, U> { } } -struct Retryer { +struct UciCmdRetryer { cmd: UciCommand, result_sender: oneshot::Sender<Result<UciResponse>>, retry_count: usize, } -impl Retryer { +impl UciCmdRetryer { fn could_retry(&mut self) -> bool { if self.retry_count == 0 { return false; @@ -762,15 +1118,55 @@ impl Retryer { } } +struct UciDataSndRetryer { + // Store the last-sent DataSnd packet fragment across all the active UWB session, as the UCI + // spec states that the "last UCI packet should be re-transmitted from Host". + // + // TODO(b/273376343): The spec is open to a race condition in the scenario of multiple active + // sessions, as there can be outstanding DataSnd packet fragments across them. We could do an + // alternative implementation of sending all of them. + data_packet: UciDataPacketHal, + data_packet_session_id: u32, + retry_count: usize, +} + +impl UciDataSndRetryer { + fn could_retry(&mut self) -> bool { + if self.retry_count == 0 { + return false; + } + self.retry_count -= 1; + true + } +} + #[derive(Debug)] enum UciManagerCmd { - SetLoggerMode { logger_mode: UciLoggerMode }, - SetCoreNotificationSender { core_notf_sender: mpsc::UnboundedSender<CoreNotification> }, - SetSessionNotificationSender { session_notf_sender: mpsc::UnboundedSender<SessionNotification> }, - SetVendorNotificationSender { vendor_notf_sender: mpsc::UnboundedSender<RawVendorMessage> }, + SetLoggerMode { + logger_mode: UciLoggerMode, + }, + SetCoreNotificationSender { + core_notf_sender: mpsc::UnboundedSender<CoreNotification>, + }, + SetSessionNotificationSender { + session_notf_sender: mpsc::UnboundedSender<SessionNotification>, + }, + SetVendorNotificationSender { + vendor_notf_sender: mpsc::UnboundedSender<RawUciMessage>, + }, + SetDataRcvNotificationSender { + data_rcv_notf_sender: mpsc::UnboundedSender<DataRcvNotification>, + }, OpenHal, - CloseHal { force: bool }, - SendUciCommand { cmd: UciCommand }, + CloseHal { + force: bool, + }, + SendUciCommand { + cmd: UciCommand, + }, + SendUciData { + data_snd_packet: UciDataSnd, + }, } #[cfg(test)] @@ -778,24 +1174,42 @@ mod tests { use super::*; use bytes::Bytes; - use uwb_uci_packets::{ - Controlee_V2_0_0_Byte_Version, MessageControl, SessionGetCountCmdBuilder, - SessionGetCountRspBuilder, - }; + use uwb_uci_packets::{SessionGetCountCmdBuilder, SessionGetCountRspBuilder}; - use crate::params::uci_packets::{CapTlvType, StatusCode}; + use crate::params::uci_packets::{ + AppConfigStatus, AppConfigTlvType, CapTlvType, Controlee, DataTransferNtfStatusCode, + StatusCode, + }; use crate::uci::mock_uci_hal::MockUciHal; use crate::uci::mock_uci_logger::{MockUciLogger, UciLogEvent}; - use crate::uci::uci_logger::UciLoggerNull; + use crate::uci::uci_logger::NopUciLogger; use crate::utils::init_test_logging; - fn into_uci_hal_packets<T: Into<uwb_uci_packets::UciPacketPacket>>( + fn into_uci_hal_packets<T: Into<uwb_uci_packets::UciControlPacket>>( builder: T, ) -> Vec<UciHalPacket> { - let packets: Vec<uwb_uci_packets::UciPacketHalPacket> = builder.into().into(); + let packets: Vec<uwb_uci_packets::UciControlPacketHal> = builder.into().into(); packets.into_iter().map(|packet| packet.into()).collect() } + // Construct a UCI packet, with the header fields and payload bytes. + fn build_uci_packet(mt: u8, pbf: u8, gid: u8, oid: u8, mut payload: Vec<u8>) -> Vec<u8> { + let len: u16 = payload.len() as u16; + let mut bytes: Vec<u8> = vec![(mt & 0x7) << 5 | (pbf & 0x1) << 4 | (gid & 0xF), oid & 0x3F]; + if mt == 0 { + // UCI Data packet + // Store 16-bit payload length in LSB format. + bytes.push((len & 0xFF).try_into().unwrap()); + bytes.push((len >> 8).try_into().unwrap()); + } else { + // One byte RFU, followed by one-byte payload length. + bytes.push(0); + bytes.push((len & 0xFF).try_into().unwrap()); + } + bytes.append(&mut payload); + bytes + } + async fn setup_uci_manager_with_open_hal<F>( setup_hal_fn: F, uci_logger_mode: UciLoggerMode, @@ -828,7 +1242,7 @@ mod tests { setup_hal_fn(&mut hal); // Verify open_hal() is working. - let mut uci_manager = + let uci_manager = UciManagerImpl::new(hal.clone(), MockUciLogger::new(log_sender), uci_logger_mode); let result = uci_manager.open_hal().await; assert!(result.is_ok()); @@ -842,8 +1256,8 @@ mod tests { let mut hal = MockUciHal::new(); hal.expected_open(None, Ok(())); - let mut uci_manager = - UciManagerImpl::new(hal.clone(), UciLoggerNull::default(), UciLoggerMode::Disabled); + let uci_manager = + UciManagerImpl::new(hal.clone(), NopUciLogger::default(), UciLoggerMode::Disabled); let result = uci_manager.open_hal().await; assert!(matches!(result, Err(Error::Timeout))); @@ -852,7 +1266,7 @@ mod tests { #[tokio::test] async fn test_close_hal_explicitly() { - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( |hal| { hal.expected_close(Ok(())); }, @@ -887,8 +1301,8 @@ mod tests { init_test_logging(); let mut hal = MockUciHal::new(); - let mut uci_manager = - UciManagerImpl::new(hal.clone(), UciLoggerNull::default(), UciLoggerMode::Disabled); + let uci_manager = + UciManagerImpl::new(hal.clone(), NopUciLogger::default(), UciLoggerMode::Disabled); let result = uci_manager.close_hal(false).await; assert!(matches!(result, Err(Error::BadParameters))); @@ -897,7 +1311,7 @@ mod tests { #[tokio::test] async fn test_device_reset_ok() { - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( |hal| { let cmd = UciCommand::DeviceReset { reset_config: ResetConfig::UwbsReset }; let resp = into_uci_hal_packets(uwb_uci_packets::DeviceResetRspBuilder { @@ -926,7 +1340,7 @@ mod tests { let vendor_spec_info = vec![0x1, 0x2]; let vendor_spec_info_clone = vendor_spec_info.clone(); - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { let cmd = UciCommand::CoreGetDeviceInfo; let resp = into_uci_hal_packets(uwb_uci_packets::GetDeviceInfoRspBuilder { @@ -962,7 +1376,7 @@ mod tests { let tlv = CapTlv { t: CapTlvType::SupportedFiraPhyVersionRange, v: vec![0x12, 0x34, 0x56] }; let tlv_clone = tlv.clone(); - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { let cmd = UciCommand::CoreGetCapsInfo; let resp = into_uci_hal_packets(uwb_uci_packets::GetCapsInfoRspBuilder { @@ -993,7 +1407,7 @@ mod tests { let config_status = vec![]; let config_status_clone = config_status.clone(); - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { let cmd = UciCommand::CoreSetConfig { config_tlvs: vec![tlv_clone] }; let resp = into_uci_hal_packets(uwb_uci_packets::SetConfigRspBuilder { @@ -1020,7 +1434,7 @@ mod tests { let tlv = DeviceConfigTlv { cfg_id, v: vec![0x12, 0x34, 0x56] }; let tlv_clone = tlv.clone(); - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { let cmd = UciCommand::CoreGetConfig { cfg_id: vec![cfg_id] }; let resp = into_uci_hal_packets(uwb_uci_packets::GetConfigRspBuilder { @@ -1046,27 +1460,29 @@ mod tests { let session_id = 0x123; let session_type = SessionType::FiraRangingSession; - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( - move |hal| { - let cmd = UciCommand::SessionInit { session_id, session_type }; - let mut resp = into_uci_hal_packets(uwb_uci_packets::SessionInitRspBuilder { - status: uwb_uci_packets::StatusCode::UciStatusOk, - }); - let mut notf = into_uci_hal_packets(uwb_uci_packets::SessionStatusNtfBuilder { - session_id, - session_state: uwb_uci_packets::SessionState::SessionStateInit, - reason_code: - uwb_uci_packets::ReasonCode::StateChangeWithSessionManagementCommands, - }); - resp.append(&mut notf); - - hal.expected_send_command(cmd, resp, Ok(())); - hal.expected_notify_session_initialized(session_id, Ok(())); - }, - UciLoggerMode::Disabled, - mpsc::unbounded_channel::<UciLogEvent>().0, - ) - .await; + let (uci_manager, mut mock_hal) = + setup_uci_manager_with_open_hal( + move |hal| { + let cmd = UciCommand::SessionInit { session_id, session_type }; + let mut resp = into_uci_hal_packets(uwb_uci_packets::SessionInitRspBuilder { + status: uwb_uci_packets::StatusCode::UciStatusOk, + }); + let mut notf = into_uci_hal_packets(uwb_uci_packets::SessionStatusNtfBuilder { + session_id, + session_state: uwb_uci_packets::SessionState::SessionStateInit, + reason_code: + uwb_uci_packets::ReasonCode::StateChangeWithSessionManagementCommands + .into(), + }); + resp.append(&mut notf); + + hal.expected_send_command(cmd, resp, Ok(())); + hal.expected_notify_session_initialized(session_id, Ok(())); + }, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + ) + .await; let result = uci_manager.session_init(session_id, session_type).await; assert!(result.is_ok()); @@ -1077,7 +1493,7 @@ mod tests { async fn test_session_deinit_ok() { let session_id = 0x123; - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { let cmd = UciCommand::SessionDeinit { session_id }; let resp = into_uci_hal_packets(uwb_uci_packets::SessionDeinitRspBuilder { @@ -1102,7 +1518,7 @@ mod tests { let config_tlv = AppConfigTlv::new(AppConfigTlvType::DeviceType, vec![0x12, 0x34, 0x56]); let config_tlv_clone = config_tlv.clone(); - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( |hal| { let cmd = UciCommand::SessionSetAppConfig { session_id, @@ -1135,7 +1551,7 @@ mod tests { let tlv = AppConfigTlv::new(AppConfigTlvType::DeviceType, vec![0x12, 0x34, 0x56]); let tlv_clone = tlv.clone(); - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { let cmd = UciCommand::SessionGetAppConfig { session_id, app_cfg: vec![config_id] }; let resp = into_uci_hal_packets(uwb_uci_packets::SessionGetAppConfigRspBuilder { @@ -1160,7 +1576,7 @@ mod tests { async fn test_session_get_count_ok() { let session_count = 5; - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { let cmd = UciCommand::SessionGetCount; let resp = into_uci_hal_packets(uwb_uci_packets::SessionGetCountRspBuilder { @@ -1185,7 +1601,7 @@ mod tests { let session_id = 0x123; let session_state = SessionState::SessionStateActive; - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { let cmd = UciCommand::SessionGetState { session_id }; let resp = into_uci_hal_packets(uwb_uci_packets::SessionGetStateRspBuilder { @@ -1212,12 +1628,12 @@ mod tests { let controlee = Controlee { short_address: 0x4567, subsession_id: 0x90ab }; let controlee_clone = controlee.clone(); - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { let cmd = UciCommand::SessionUpdateControllerMulticastList { session_id, action, - controlees: vec![controlee_clone], + controlees: Controlees::NoSessionKey(vec![controlee_clone]), }; let resp = into_uci_hal_packets( uwb_uci_packets::SessionUpdateControllerMulticastListRspBuilder { @@ -1233,33 +1649,33 @@ mod tests { .await; let result = uci_manager - .session_update_controller_multicast_list(session_id, action, vec![controlee]) + .session_update_controller_multicast_list( + session_id, + action, + uwb_uci_packets::Controlees::NoSessionKey(vec![controlee]), + ) .await; assert!(result.is_ok()); assert!(mock_hal.wait_expected_calls_done().await); } #[tokio::test] - async fn test_session_update_controller_multicast_list_v2_ok() { - let session_id = 0x123; - let action = UpdateMulticastListAction::AddControlee; - let controlee = Controlee_V2_0_0_Byte_Version { - short_address: 0x4567, - subsession_id: 0x90ab, - message_control: MessageControl::SubSessionKeyNotConfigured, + async fn test_set_active_dt_tag_ranging_rounds() { + let ranging_rounds = SessionUpdateDtTagRangingRoundsResponse { + status: StatusCode::UciStatusErrorRoundIndexNotActivated, + ranging_round_indexes: vec![3], }; - let controlee_clone = controlee.clone(); - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { - let cmd = UciCommand::SessionUpdateControllerMulticastListV2 { - session_id, - action, - controlees: ControleesV2::NoSessionKey(vec![controlee_clone]), + let cmd = UciCommand::SessionUpdateDtTagRangingRounds { + session_id: 1, + ranging_round_indexes: vec![3, 5], }; let resp = into_uci_hal_packets( - uwb_uci_packets::SessionUpdateControllerMulticastListRspBuilder { - status: uwb_uci_packets::StatusCode::UciStatusOk, + uwb_uci_packets::SessionUpdateDtTagRangingRoundsRspBuilder { + status: StatusCode::UciStatusErrorRoundIndexNotActivated, + ranging_round_indexes: vec![3], }, ); @@ -1270,14 +1686,9 @@ mod tests { ) .await; - let result = uci_manager - .session_update_controller_multicast_list_v2( - session_id, - action, - ControleesV2::NoSessionKey(vec![controlee]), - ) - .await; - assert!(result.is_ok()); + let result = uci_manager.session_update_dt_tag_ranging_rounds(1, vec![3, 5]).await.unwrap(); + + assert_eq!(result, ranging_rounds); assert!(mock_hal.wait_expected_calls_done().await); } @@ -1285,10 +1696,10 @@ mod tests { async fn test_range_start_ok() { let session_id = 0x123; - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { - let cmd = UciCommand::RangeStart { session_id }; - let resp = into_uci_hal_packets(uwb_uci_packets::RangeStartRspBuilder { + let cmd = UciCommand::SessionStart { session_id }; + let resp = into_uci_hal_packets(uwb_uci_packets::SessionStartRspBuilder { status: uwb_uci_packets::StatusCode::UciStatusOk, }); @@ -1308,10 +1719,10 @@ mod tests { async fn test_range_stop_ok() { let session_id = 0x123; - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { - let cmd = UciCommand::RangeStop { session_id }; - let resp = into_uci_hal_packets(uwb_uci_packets::RangeStopRspBuilder { + let cmd = UciCommand::SessionStop { session_id }; + let resp = into_uci_hal_packets(uwb_uci_packets::SessionStopRspBuilder { status: uwb_uci_packets::StatusCode::UciStatusOk, }); @@ -1332,13 +1743,14 @@ mod tests { let session_id = 0x123; let count = 3; - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { - let cmd = UciCommand::RangeGetRangingCount { session_id }; - let resp = into_uci_hal_packets(uwb_uci_packets::RangeGetRangingCountRspBuilder { - status: uwb_uci_packets::StatusCode::UciStatusOk, - count, - }); + let cmd = UciCommand::SessionGetRangingCount { session_id }; + let resp = + into_uci_hal_packets(uwb_uci_packets::SessionGetRangingCountRspBuilder { + status: uwb_uci_packets::StatusCode::UciStatusOk, + count, + }); hal.expected_send_command(cmd, resp, Ok(())); }, @@ -1357,7 +1769,7 @@ mod tests { let country_code = CountryCode::new(b"US").unwrap(); let country_code_clone = country_code.clone(); - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { let cmd = UciCommand::AndroidSetCountryCode { country_code: country_code_clone }; let resp = into_uci_hal_packets(uwb_uci_packets::AndroidSetCountryCodeRspBuilder { @@ -1387,7 +1799,7 @@ mod tests { }; let power_stats_clone = power_stats.clone(); - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { let cmd = UciCommand::AndroidGetPowerStats; let resp = into_uci_hal_packets(uwb_uci_packets::AndroidGetPowerStatsRspBuilder { @@ -1407,17 +1819,18 @@ mod tests { } #[tokio::test] - async fn test_raw_vendor_cmd_ok() { - let gid = 0xF; + async fn test_raw_uci_cmd_vendor_gid_ok() { + let mt = 0x1; + let gid = 0xF; // Vendor reserved GID. let oid = 0x3; let cmd_payload = vec![0x11, 0x22, 0x33, 0x44]; let cmd_payload_clone = cmd_payload.clone(); let resp_payload = vec![0x55, 0x66, 0x77, 0x88]; let resp_payload_clone = resp_payload.clone(); - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { - let cmd = UciCommand::RawVendorCmd { gid, oid, payload: cmd_payload_clone }; + let cmd = UciCommand::RawUciCmd { mt, gid, oid, payload: cmd_payload_clone }; let resp = into_uci_hal_packets(uwb_uci_packets::UciVendor_F_ResponseBuilder { opcode: oid as u8, payload: Some(Bytes::from(resp_payload_clone)), @@ -1430,15 +1843,632 @@ mod tests { ) .await; - let expected_result = RawVendorMessage { gid, oid, payload: resp_payload }; - let result = uci_manager.raw_vendor_cmd(gid, oid, cmd_payload).await.unwrap(); + let expected_result = RawUciMessage { gid, oid, payload: resp_payload }; + let result = uci_manager.raw_uci_cmd(mt, gid, oid, cmd_payload).await.unwrap(); + assert_eq!(result, expected_result); + assert!(mock_hal.wait_expected_calls_done().await); + } + + #[tokio::test] + async fn test_raw_uci_cmd_fira_gid_ok() { + let mt = 0x1; + let gid = 0x1; // SESSION_CONFIG GID. + let oid = 0x3; + let cmd_payload = vec![0x11, 0x22, 0x33, 0x44]; + let cmd_payload_clone = cmd_payload.clone(); + let resp_payload = vec![0x00, 0x01, 0x07, 0x00]; + let status = StatusCode::UciStatusOk; + let cfg_id = AppConfigTlvType::DstMacAddress; + let app_config = AppConfigStatus { cfg_id, status }; + let cfg_status = vec![app_config]; + + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + move |hal| { + let cmd = UciCommand::RawUciCmd { mt, gid, oid, payload: cmd_payload_clone }; + let resp = into_uci_hal_packets(uwb_uci_packets::SessionSetAppConfigRspBuilder { + status, + cfg_status, + }); + + hal.expected_send_command(cmd, resp, Ok(())); + }, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + ) + .await; + + let expected_result = RawUciMessage { gid, oid, payload: resp_payload }; + let result = uci_manager.raw_uci_cmd(mt, gid, oid, cmd_payload).await.unwrap(); + assert_eq!(result, expected_result); + assert!(mock_hal.wait_expected_calls_done().await); + } + + #[tokio::test] + async fn test_raw_uci_cmd_undefined_mt_ok() { + let mt = 0x4; + let gid = 0x1; // SESSION_CONFIG GID. + let oid = 0x3; + let cmd_payload = vec![0x11, 0x22, 0x33, 0x44]; + let cmd_payload_clone = cmd_payload.clone(); + let resp_payload = vec![0x00, 0x01, 0x07, 0x00]; + let status = StatusCode::UciStatusOk; + let cfg_id = AppConfigTlvType::DstMacAddress; + let app_config = AppConfigStatus { cfg_id, status }; + let cfg_status = vec![app_config]; + + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + move |hal| { + let cmd = UciCommand::RawUciCmd { mt, gid, oid, payload: cmd_payload_clone }; + let resp = into_uci_hal_packets(uwb_uci_packets::SessionSetAppConfigRspBuilder { + status, + cfg_status, + }); + + hal.expected_send_command(cmd, resp, Ok(())); + }, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + ) + .await; + + let expected_result = RawUciMessage { gid, oid, payload: resp_payload }; + let result = uci_manager.raw_uci_cmd(mt, gid, oid, cmd_payload).await.unwrap(); + assert_eq!(result, expected_result); + assert!(mock_hal.wait_expected_calls_done().await); + } + + #[tokio::test] + async fn test_raw_uci_cmd_custom_payload_format() { + // Send a raw UCI command with a FiRa defined GID, OID (SESSION_SET_APP_CONFIG), and the + // UCI HAL returns a UCI response with a custom payload format. The UCI response packet + // should still be successfully parsed and returned, since it's a Raw UCI RSP. + let cmd_mt: u8 = 0x1; + let gid: u8 = 0x1; // Session Config. + let oid: u8 = 0x3; // SESSION_SET_APP_CONFIG + let cmd_payload = vec![0x11, 0x22, 0x33, 0x44]; + let cmd_payload_clone = cmd_payload.clone(); + let resp_mt: u8 = 0x2; + let resp_payload = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; + let resp_payload_clone = resp_payload.clone(); + + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + move |hal| { + let cmd = UciCommand::RawUciCmd { + mt: cmd_mt.into(), + gid: gid.into(), + oid: oid.into(), + payload: cmd_payload_clone, + }; + let resp = build_uci_packet(resp_mt, 0, gid, oid, resp_payload_clone); + hal.expected_send_command(cmd, vec![resp], Ok(())); + }, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + ) + .await; + + let expected_result = + Ok(RawUciMessage { gid: gid.into(), oid: oid.into(), payload: resp_payload }); + let result = + uci_manager.raw_uci_cmd(cmd_mt.into(), gid.into(), oid.into(), cmd_payload).await; + assert_eq!(result, expected_result); + assert!(mock_hal.wait_expected_calls_done().await); + } + + #[tokio::test] + async fn test_raw_uci_cmd_fragmented_responses() { + // Send a raw UCI command with a FiRa defined GID, OID (SESSION_SET_APP_CONFIG), and the + // UCI HAL returns a UCI response with a custom payload format, in 2 UCI packet fragments. + let cmd_mt: u8 = 0x1; + let gid: u8 = 0x1; // Session Config. + let oid: u8 = 0x3; // SESSION_SET_APP_CONFIG + let cmd_payload = vec![0x11, 0x22, 0x33, 0x44]; + let cmd_payload_clone = cmd_payload.clone(); + let resp_mt: u8 = 0x2; + let resp_payload_fragment_1 = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; + let resp_payload_fragment_2 = vec![0x09, 0x0a, 0x0b]; + let mut resp_payload_expected = resp_payload_fragment_1.clone(); + resp_payload_expected.extend(resp_payload_fragment_2.clone().into_iter()); + + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + move |hal| { + let cmd = UciCommand::RawUciCmd { + mt: cmd_mt.into(), + gid: gid.into(), + oid: oid.into(), + payload: cmd_payload_clone, + }; + let resp_fragment_1 = build_uci_packet( + resp_mt, + /* pbf = */ 1, + gid, + oid, + resp_payload_fragment_1, + ); + let resp_fragment_2 = build_uci_packet( + resp_mt, + /* pbf = */ 0, + gid, + oid, + resp_payload_fragment_2, + ); + hal.expected_send_command(cmd, vec![resp_fragment_1, resp_fragment_2], Ok(())); + }, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + ) + .await; + + let expected_result = + Ok(RawUciMessage { gid: gid.into(), oid: oid.into(), payload: resp_payload_expected }); + let result = + uci_manager.raw_uci_cmd(cmd_mt.into(), gid.into(), oid.into(), cmd_payload).await; + assert_eq!(result, expected_result); + assert!(mock_hal.wait_expected_calls_done().await); + } + + #[tokio::test] + async fn test_raw_uci_cmd_wrong_gid() { + // Send a raw UCI command with CORE GID, but UCI HAL returns a UCI response with + // SESSION_CONFIG GID. In this case, UciManager should return Error::Unknown, as the + // RawUciSignature fields (GID, OID) of the CMD and RSP packets don't match. + + let mt = 0x1; + let gid = 0x0; // CORE GID. + let oid = 0x1; + let cmd_payload = vec![0x11, 0x22, 0x33, 0x44]; + let cmd_payload_clone = cmd_payload.clone(); + let status = StatusCode::UciStatusOk; + let cfg_id = AppConfigTlvType::DstMacAddress; + let app_config = AppConfigStatus { cfg_id, status }; + let cfg_status = vec![app_config]; + + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + move |hal| { + let cmd = UciCommand::RawUciCmd { mt, gid, oid, payload: cmd_payload_clone }; + let resp = into_uci_hal_packets(uwb_uci_packets::SessionSetAppConfigRspBuilder { + status, + cfg_status, + }); + + hal.expected_send_command(cmd, resp, Ok(())); + }, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + ) + .await; + + let expected_result = Err(Error::Unknown); + let result = uci_manager.raw_uci_cmd(mt, gid, oid, cmd_payload).await; + assert_eq!(result, expected_result); + assert!(mock_hal.wait_expected_calls_done().await); + } + + #[tokio::test] + async fn test_raw_uci_cmd_out_of_range_gid() { + // Send a raw UCI command with a GID value outside it's 8-bit size. This should result in + // an error since the input GID value cannot be encoded into the UCI packet. + let mt = 0x1; + let gid = 0x1FF; + let oid = 0x1; + let cmd_payload = vec![0x11, 0x22, 0x33, 0x44]; + + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + move |_hal| {}, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + ) + .await; + + let expected_result = Err(Error::BadParameters); + let result = uci_manager.raw_uci_cmd(mt, gid, oid, cmd_payload).await; + assert_eq!(result, expected_result); + assert!(mock_hal.wait_expected_calls_done().await); + } + + #[tokio::test] + async fn test_raw_uci_cmd_out_of_range_oid() { + // Send a raw UCI command with a valid GID (CORE), but an OID value outside it's 8-bit + // size. This should result in an error since the input OID value cannot be encoded into + // the UCI packet. + let mt = 0x1; + let gid = 0x0; // CORE GID. + let oid = 0x1FF; + let cmd_payload = vec![0x11, 0x22, 0x33, 0x44]; + + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + move |_hal| {}, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + ) + .await; + + let expected_result = Err(Error::BadParameters); + let result = uci_manager.raw_uci_cmd(mt, gid, oid, cmd_payload).await; + assert_eq!(result, expected_result); + assert!(mock_hal.wait_expected_calls_done().await); + } + + #[tokio::test] + async fn test_raw_uci_cmd_uwbs_response_notification() { + // Send a raw UCI command with a FiRa defined GID, OID (SESSION_SET_APP_CONFIG), and the + // UCI HAL returns a valid UCI Notification packet before the raw UCI response. + let cmd_mt: u8 = 0x1; + let gid: u8 = 0x1; // Session Config. + let oid: u8 = 0x3; // SESSION_SET_APP_CONFIG + let cmd_payload = vec![0x11, 0x22, 0x33, 0x44]; + let cmd_payload_clone = cmd_payload.clone(); + let session_id = 0x123; + let resp_mt: u8 = 0x2; + let resp_payload = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; + let resp_payload_clone = resp_payload.clone(); + + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + move |hal| { + let cmd = UciCommand::RawUciCmd { + mt: cmd_mt.into(), + gid: gid.into(), + oid: oid.into(), + payload: cmd_payload_clone, + }; + let raw_resp = build_uci_packet(resp_mt, 0, gid, oid, resp_payload_clone); + let mut responses = + into_uci_hal_packets(uwb_uci_packets::SessionStatusNtfBuilder { + session_id, + session_state: uwb_uci_packets::SessionState::SessionStateInit, + reason_code: + uwb_uci_packets::ReasonCode::StateChangeWithSessionManagementCommands + .into(), + }); + responses.push(raw_resp); + hal.expected_send_command(cmd, responses, Ok(())); + }, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + ) + .await; + + let expected_result = + Ok(RawUciMessage { gid: gid.into(), oid: oid.into(), payload: resp_payload }); + let result = + uci_manager.raw_uci_cmd(cmd_mt.into(), gid.into(), oid.into(), cmd_payload).await; + assert_eq!(result, expected_result); + assert!(mock_hal.wait_expected_calls_done().await); + } + + #[tokio::test] + async fn test_raw_uci_cmd_uwbs_response_undefined_mt() { + // Send a raw UCI command with a FiRa defined GID, OID (SESSION_SET_APP_CONFIG), and the + // UCI HAL returns a UCI packet with an undefined MessageType in response. + let cmd_mt: u8 = 0x1; + let gid: u8 = 0x1; // Session Config. + let oid: u8 = 0x3; // SESSION_SET_APP_CONFIG + let cmd_payload = vec![0x11, 0x22, 0x33, 0x44]; + let cmd_payload_clone = cmd_payload.clone(); + let resp_mt: u8 = 0x7; // Undefined MessageType + let resp_payload = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; + + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + move |hal| { + let cmd = UciCommand::RawUciCmd { + mt: cmd_mt.into(), + gid: gid.into(), + oid: oid.into(), + payload: cmd_payload_clone, + }; + let resp = build_uci_packet(resp_mt, /* pbf = */ 0, gid, oid, resp_payload); + hal.expected_send_command(cmd, vec![resp], Ok(())); + }, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + ) + .await; + + let expected_result = Err(Error::Unknown); + let result = + uci_manager.raw_uci_cmd(cmd_mt.into(), gid.into(), oid.into(), cmd_payload).await; assert_eq!(result, expected_result); assert!(mock_hal.wait_expected_calls_done().await); } + fn setup_active_session(hal: &mut MockUciHal, session_id: u32) { + // First setup the Session to be in Active state. + let cmd = UciCommand::SessionStart { session_id }; + let mut responses = into_uci_hal_packets(uwb_uci_packets::SessionStartRspBuilder { + status: uwb_uci_packets::StatusCode::UciStatusOk, + }); + responses.append(&mut into_uci_hal_packets(uwb_uci_packets::SessionStatusNtfBuilder { + session_id, + session_state: SessionState::SessionStateActive, + reason_code: 0, /* ReasonCode::StateChangeWithSessionManagementCommands */ + })); + hal.expected_send_command(cmd, responses, Ok(())); + } + + // TODO(b/276320369): Listing down the Data Packet Rx scenarios below, will add unit tests + // for them in subsequent CLs. + #[tokio::test] + async fn test_data_packet_recv_ok() {} + + #[tokio::test] + async fn test_data_packet_recv_fragmented_packet_ok() {} + + #[tokio::test] + async fn test_data_packet_send_ok() { + // Test Data packet send for a single packet (on a UWB session). + let mt_data = 0x0; + let pbf = 0x0; + let dpf = 0x1; + let oid = 0x0; + let session_id = 0x5; + let dest_mac_address = vec![0xa0, 0xb0, 0xc0, 0xd0, 0xa1, 0xb1, 0xc1, 0xd1]; + let dest_fira_component = FiraComponent::Host; + let uci_sequence_number = 0xa; + let app_data = vec![0x01, 0x02, 0x03]; + let expected_data_snd_payload = vec![ + 0x05, 0x00, 0x00, 0x00, // SessionID + 0xa0, 0xb0, 0xc0, 0xd0, 0xa1, 0xb1, 0xc1, 0xd1, // MacAddress + 0x01, // FiraComponent + 0x0a, // UciSequenceNumber + 0x03, 0x00, // AppDataLen + 0x01, 0x02, 0x03, // AppData + ]; + let status = DataTransferNtfStatusCode::UciDataTransferStatusRepetitionOk; + + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + move |hal| { + setup_active_session(hal, session_id); + + // Now setup the notifications that should be received after a Data packet send. + let data_packet_snd = + build_uci_packet(mt_data, pbf, dpf, oid, expected_data_snd_payload); + let mut ntfs = into_uci_hal_packets(uwb_uci_packets::DataCreditNtfBuilder { + session_id, + credit_availability: CreditAvailability::CreditAvailable, + }); + ntfs.append(&mut into_uci_hal_packets( + uwb_uci_packets::DataTransferStatusNtfBuilder { + session_id, + uci_sequence_number, + status, + }, + )); + hal.expected_send_packet(data_packet_snd, ntfs, Ok(())); + }, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + ) + .await; + + let result = uci_manager.range_start(session_id).await; + assert!(result.is_ok()); + + let result = uci_manager + .send_data_packet( + session_id, + dest_mac_address, + dest_fira_component, + uci_sequence_number, + app_data, + ) + .await; + assert!(result.is_ok()); + assert!(mock_hal.wait_expected_calls_done().await); + + // TODO(b/276320369): Verify that session_notf_sender is called (once implemented), as a + // DataTransferStatusNtf is received in this test scenario. + } + + #[tokio::test] + async fn test_data_packet_send_fragmented_packet_ok() { + // Test Data packet send for a set of data packet fragments (on a UWB session). + let mt_data = 0x0; + let pbf_fragment_1 = 0x1; + let pbf_fragment_2 = 0x0; + let dpf = 0x1; + let oid = 0x0; + let session_id = 0x5; + let dest_mac_address = vec![0xa0, 0xb0, 0xc0, 0xd0, 0xa1, 0xb1, 0xc1, 0xd1]; + let dest_fira_component = FiraComponent::Host; + let uci_sequence_number = 0xa; + let app_data_len = 300; // Larger than MAX_PAYLOAD_LEN=255, so fragmentation occurs. + let mut app_data = Vec::new(); + let mut expected_data_snd_payload_fragment_1 = vec![ + 0x05, 0x00, 0x00, 0x00, // SessionID + 0xa0, 0xb0, 0xc0, 0xd0, 0xa1, 0xb1, 0xc1, 0xd1, // MacAddress + 0x01, // FiraComponent + 0x0a, // UciSequenceNumber + 0x2c, 0x01, // AppDataLen = 300 + ]; + let mut expected_data_snd_payload_fragment_2 = Vec::new(); + let status = DataTransferNtfStatusCode::UciDataTransferStatusRepetitionOk; + + // Setup the app data for both the Tx data packet and expected packet fragments. + let app_data_len_fragment_1 = 255 - expected_data_snd_payload_fragment_1.len(); + for i in 0..app_data_len { + app_data.push((i & 0xff).try_into().unwrap()); + if i < app_data_len_fragment_1 { + expected_data_snd_payload_fragment_1.push((i & 0xff).try_into().unwrap()); + } else { + expected_data_snd_payload_fragment_2.push((i & 0xff).try_into().unwrap()); + } + } + + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + move |hal| { + setup_active_session(hal, session_id); + + // Expected data packet fragment #1 (UCI Header + Initial App data bytes). + let data_packet_snd_fragment_1 = build_uci_packet( + mt_data, + pbf_fragment_1, + dpf, + oid, + expected_data_snd_payload_fragment_1, + ); + let ntfs = into_uci_hal_packets(uwb_uci_packets::DataCreditNtfBuilder { + session_id, + credit_availability: CreditAvailability::CreditAvailable, + }); + hal.expected_send_packet(data_packet_snd_fragment_1, ntfs, Ok(())); + + // Expected data packet fragment #2 (UCI Header + Remaining App data bytes). + let data_packet_snd_fragment_2 = build_uci_packet( + mt_data, + pbf_fragment_2, + dpf, + oid, + expected_data_snd_payload_fragment_2, + ); + let mut ntfs = into_uci_hal_packets(uwb_uci_packets::DataCreditNtfBuilder { + session_id, + credit_availability: CreditAvailability::CreditAvailable, + }); + ntfs.append(&mut into_uci_hal_packets( + uwb_uci_packets::DataTransferStatusNtfBuilder { + session_id, + uci_sequence_number, + status, + }, + )); + hal.expected_send_packet(data_packet_snd_fragment_2, ntfs, Ok(())); + }, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + ) + .await; + + let result = uci_manager.range_start(session_id).await; + assert!(result.is_ok()); + + let result = uci_manager + .send_data_packet( + session_id, + dest_mac_address, + dest_fira_component, + uci_sequence_number, + app_data, + ) + .await; + assert!(result.is_ok()); + assert!(mock_hal.wait_expected_calls_done().await); + } + + #[tokio::test] + async fn test_data_packet_send_retry_ok() { + // Test Data packet send for a single packet (on a UWB session). + let mt_data = 0x0; + let pbf = 0x0; + let dpf = 0x1; + let oid = 0x0; + let session_id = 0x5; + let dest_mac_address = vec![0xa0, 0xb0, 0xc0, 0xd0, 0xa1, 0xb1, 0xc1, 0xd1]; + let dest_fira_component = FiraComponent::Host; + let uci_sequence_number = 0xa; + let app_data = vec![0x01, 0x02, 0x03]; + let expected_data_snd_payload = vec![ + 0x05, 0x00, 0x00, 0x00, // SessionID + 0xa0, 0xb0, 0xc0, 0xd0, 0xa1, 0xb1, 0xc1, 0xd1, // MacAddress + 0x01, // FiraComponent + 0x0a, // UciSequenceNumber + 0x03, 0x00, // AppDataLen + 0x01, 0x02, 0x03, // AppData + ]; + let status = DataTransferNtfStatusCode::UciDataTransferStatusRepetitionOk; + + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + move |hal| { + setup_active_session(hal, session_id); + + // Setup receiving a CORE_GENERIC_ERROR_NTF with STATUS_COMMAND_RETRY after a + // failed Data packet send attempt. + let data_packet_snd = + build_uci_packet(mt_data, pbf, dpf, oid, expected_data_snd_payload); + let error_ntf = into_uci_hal_packets(uwb_uci_packets::GenericErrorBuilder { + status: StatusCode::UciStatusCommandRetry, + }); + hal.expected_send_packet(data_packet_snd.clone(), error_ntf, Ok(())); + + // Setup the notifications that should be received after the Data packet send + // is successfully retried. + let mut ntfs = into_uci_hal_packets(uwb_uci_packets::DataCreditNtfBuilder { + session_id, + credit_availability: CreditAvailability::CreditAvailable, + }); + ntfs.append(&mut into_uci_hal_packets( + uwb_uci_packets::DataTransferStatusNtfBuilder { + session_id, + uci_sequence_number, + status, + }, + )); + hal.expected_send_packet(data_packet_snd, ntfs, Ok(())); + }, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + ) + .await; + + let result = uci_manager.range_start(session_id).await; + assert!(result.is_ok()); + + let result = uci_manager + .send_data_packet( + session_id, + dest_mac_address, + dest_fira_component, + uci_sequence_number, + app_data, + ) + .await; + assert!(result.is_ok()); + assert!(mock_hal.wait_expected_calls_done().await); + + // TODO(b/276320369): Verify that session_notf_sender is called (once implemented), as a + // DataTransferStatusNtf is received in this test scenario. + } + + // TODO(b/276320369): Listing down the Data Packet Tx scenarios below, will add unit tests + // for them in subsequent CLs. + + // Sending one data packet should succeed, when no DataCreditNtf is received. + #[tokio::test] + async fn test_data_packet_send_missing_data_credit_ntf_success() {} + + // Sending the second data packet should fail, when no DataCreditNtf is received after + // sending the first data packet. + #[tokio::test] + async fn test_data_packet_send_missing_data_credit_ntf_subsequent_send_failure() {} + + #[tokio::test] + async fn test_data_packet_send_data_credit_ntf_bad_session_id() {} + + #[tokio::test] + async fn test_data_packet_send_data_credit_ntf_no_credit_available() {} + + #[tokio::test] + async fn test_data_packet_send_missing_data_transfer_status_ntf() {} + + #[tokio::test] + async fn test_data_packet_send_data_transfer_status_ntf_bad_session_id() {} + + #[tokio::test] + async fn test_data_packet_send_data_transfer_status_ntf_bad_uci_sequence_number() {} + + // Tests for the multiple Status values that indicate success + #[tokio::test] + async fn test_data_packet_send_data_transfer_status_ntf_status_ok() {} + + #[tokio::test] + async fn test_data_packet_send_data_transfer_status_ntf_status_repetition_ok() {} + + // Tests for some of the multiple Status values that indicate error. + #[tokio::test] + async fn test_data_packet_send_data_transfer_status_ntf_status_error() {} + #[tokio::test] async fn test_session_get_count_retry_no_response() { - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( |hal| { let cmd = UciCommand::SessionGetCount; hal.expected_send_command(cmd, vec![], Ok(())); @@ -1455,7 +2485,7 @@ mod tests { #[tokio::test] async fn test_session_get_count_timeout() { - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( |hal| { let cmd = UciCommand::SessionGetCount; hal.expected_send_command(cmd, vec![], Err(Error::Timeout)); @@ -1472,7 +2502,7 @@ mod tests { #[tokio::test] async fn test_session_get_count_retry_too_many_times() { - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( |hal| { let cmd = UciCommand::SessionGetCount; let retry_resp = into_uci_hal_packets(uwb_uci_packets::SessionGetCountRspBuilder { @@ -1498,7 +2528,7 @@ mod tests { async fn test_session_get_count_retry_notification() { let session_count = 5; - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { let cmd = UciCommand::SessionGetCount; let retry_resp = into_uci_hal_packets(uwb_uci_packets::SessionGetCountRspBuilder { @@ -1527,7 +2557,7 @@ mod tests { #[tokio::test] async fn test_log_manager_interaction() { let (log_sender, mut log_receiver) = mpsc::unbounded_channel::<UciLogEvent>(); - let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( + let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( move |hal| { let cmd = UciCommand::SessionGetCount; let resp1 = into_uci_hal_packets(uwb_uci_packets::SessionGetCountRspBuilder { diff --git a/src/rust/uwb_core/src/uci/uci_manager_sync.rs b/src/rust/uwb_core/src/uci/uci_manager_sync.rs index c2cb59f..0c62959 100644 --- a/src/rust/uwb_core/src/uci/uci_manager_sync.rs +++ b/src/rust/uwb_core/src/uci/uci_manager_sync.rs @@ -15,8 +15,9 @@ //! This module offers a synchornized interface at UCI level. //! //! The module is designed with the replacement for Android UCI JNI adaptation in mind. The handling -//! of UciNotifications is different in UciManager and UciManagerSync as the sync version has its -//! behavior aligned with the Android JNI UCI, and routes the UciNotifications to NotificationManager. +//! of UciNotifications is different in UciManager and UciManagerSyncImpl as the sync version has +//! its behavior aligned with the Android JNI UCI, and routes the UciNotifications to +//! NotificationManager. use log::{debug, error}; use tokio::runtime::{Builder as RuntimeBuilder, Handle}; @@ -25,19 +26,20 @@ use tokio::task; use crate::error::{Error, Result}; use crate::params::{ - AppConfigTlv, AppConfigTlvType, CapTlv, Controlee, CoreSetConfigResponse, CountryCode, - DeviceConfigId, DeviceConfigTlv, GetDeviceInfoResponse, PowerStats, RawVendorMessage, - ResetConfig, SessionId, SessionState, SessionType, SetAppConfigResponse, - UpdateMulticastListAction, + AppConfigTlv, AppConfigTlvType, CapTlv, CoreSetConfigResponse, CountryCode, DeviceConfigId, + DeviceConfigTlv, FiraComponent, GetDeviceInfoResponse, PowerStats, RawUciMessage, ResetConfig, + SessionId, SessionState, SessionType, SessionUpdateDtTagRangingRoundsResponse, + SetAppConfigResponse, UpdateMulticastListAction, }; -use crate::uci::notification::{CoreNotification, SessionNotification}; +#[cfg(any(test, feature = "mock-utils"))] +use crate::uci::mock_uci_manager::MockUciManager; +use crate::uci::notification::{CoreNotification, DataRcvNotification, SessionNotification}; use crate::uci::uci_hal::UciHal; use crate::uci::uci_logger::{UciLogger, UciLoggerMode}; use crate::uci::uci_manager::{UciManager, UciManagerImpl}; -use uwb_uci_packets::ControleesV2; +use uwb_uci_packets::Controlees; -/// The NotificationManager trait is needed to process UciNotification relayed from UciManagerSync. -/// +/// The NotificationManager processes UciNotification relayed from UciManagerSync in a sync fashion. /// The UciManagerSync assumes the NotificationManager takes the responsibility to properly handle /// the notifications, including tracking the state of HAL. UciManagerSync and lower levels only /// redirect and categorize the notifications. The notifications are processed through callbacks. @@ -49,33 +51,44 @@ pub trait NotificationManager: 'static { /// Callback for SessionNotification. fn on_session_notification(&mut self, session_notification: SessionNotification) -> Result<()>; - /// Callback for RawVendorMessage. - fn on_vendor_notification(&mut self, vendor_notification: RawVendorMessage) -> Result<()>; + /// Callback for RawUciMessage. + fn on_vendor_notification(&mut self, vendor_notification: RawUciMessage) -> Result<()>; + + /// Callback for DataRcvNotification. + fn on_data_rcv_notification( + &mut self, + data_rcv_notification: DataRcvNotification, + ) -> Result<()>; } /// Builder for NotificationManager. Builder is sent between threads. -pub trait NotificationManagerBuilder<T: NotificationManager>: 'static + Send + Sync { +pub trait NotificationManagerBuilder: 'static + Send + Sync { + /// Type of NotificationManager built. + type NotificationManager: NotificationManager; /// Builds NotificationManager. The build operation Consumes Builder. - fn build(self) -> Option<T>; + fn build(self) -> Option<Self::NotificationManager>; } struct NotificationDriver<U: NotificationManager> { core_notification_receiver: mpsc::UnboundedReceiver<CoreNotification>, session_notification_receiver: mpsc::UnboundedReceiver<SessionNotification>, - vendor_notification_receiver: mpsc::UnboundedReceiver<RawVendorMessage>, + vendor_notification_receiver: mpsc::UnboundedReceiver<RawUciMessage>, + data_rcv_notification_receiver: mpsc::UnboundedReceiver<DataRcvNotification>, notification_manager: U, } impl<U: NotificationManager> NotificationDriver<U> { fn new( core_notification_receiver: mpsc::UnboundedReceiver<CoreNotification>, session_notification_receiver: mpsc::UnboundedReceiver<SessionNotification>, - vendor_notification_receiver: mpsc::UnboundedReceiver<RawVendorMessage>, + vendor_notification_receiver: mpsc::UnboundedReceiver<RawUciMessage>, + data_rcv_notification_receiver: mpsc::UnboundedReceiver<DataRcvNotification>, notification_manager: U, ) -> Self { Self { core_notification_receiver, session_notification_receiver, vendor_notification_receiver, + data_rcv_notification_receiver, notification_manager, } } @@ -94,7 +107,12 @@ impl<U: NotificationManager> NotificationDriver<U> { } Some(ntf) = self.vendor_notification_receiver.recv() =>{ self.notification_manager.on_vendor_notification(ntf).unwrap_or_else(|e|{ - error!("NotificationDriver: RawVendorMessage callback error: {:?}",e); + error!("NotificationDriver: RawUciMessage callback error: {:?}",e); + }); + } + Some(data) = self.data_rcv_notification_receiver.recv() =>{ + self.notification_manager.on_data_rcv_notification(data).unwrap_or_else(|e|{ + error!("NotificationDriver: OnDataRcv callback error: {:?}",e); }); } else =>{ @@ -105,44 +123,37 @@ impl<U: NotificationManager> NotificationDriver<U> { } } } -/// The UciManagerSync provides a synchornized version of UciManager using the runtime supplied -/// at its initialization. + +/// The UciManagerSync provides a synchornized version of UciManager. /// -/// Note the processing of UciNotification is different: they are handled by NotificationManager -/// provided at construction, and the async version set_X_notification_sender methods are removed. -pub struct UciManagerSync { +/// Note the processing of UciNotification is different: +/// set_X_notification_sender methods are removed. Instead, the method +/// redirect_notification(NotificationManagerBuilder) is introduced to avoid the +/// exposure of async tokio::mpsc. +pub struct UciManagerSync<U: UciManager> { runtime_handle: Handle, - uci_manager_impl: UciManagerImpl, + uci_manager: U, } -impl UciManagerSync { - /// UciHal and NotificationManagerBuilder required at construction as they are required before - /// open_hal is called. runtime_handle must be a Handle to a multithread runtime that outlives - /// UciManagerSync. - pub fn new<T, U, V, W>( - hal: T, - notification_manager_builder: V, - logger: W, - runtime_handle: Handle, - ) -> Result<Self> - where - T: UciHal, - U: NotificationManager, - V: NotificationManagerBuilder<U>, - W: UciLogger, - { - // UciManagerImpl::new uses tokio::spawn, so it is called inside the runtime as async fn. - let mut uci_manager_impl = runtime_handle - .block_on(async { UciManagerImpl::new(hal, logger, UciLoggerMode::Disabled) }); +impl<U: UciManager> UciManagerSync<U> { + /// Redirects notification to a new NotificationManager using the notification_manager_builder. + /// The NotificationManager will live on a separate thread. + pub fn redirect_notification<T: NotificationManagerBuilder>( + &mut self, + notification_manager_builder: T, + ) -> Result<()> { let (core_notification_sender, core_notification_receiver) = mpsc::unbounded_channel::<CoreNotification>(); let (session_notification_sender, session_notification_receiver) = mpsc::unbounded_channel::<SessionNotification>(); let (vendor_notification_sender, vendor_notification_receiver) = - mpsc::unbounded_channel::<RawVendorMessage>(); - runtime_handle.block_on(async { - uci_manager_impl.set_core_notification_sender(core_notification_sender).await; - uci_manager_impl.set_session_notification_sender(session_notification_sender).await; - uci_manager_impl.set_vendor_notification_sender(vendor_notification_sender).await; + mpsc::unbounded_channel::<RawUciMessage>(); + let (data_rcv_notification_sender, data_rcv_notification_receiver) = + mpsc::unbounded_channel::<DataRcvNotification>(); + self.runtime_handle.to_owned().block_on(async { + self.uci_manager.set_core_notification_sender(core_notification_sender).await; + self.uci_manager.set_session_notification_sender(session_notification_sender).await; + self.uci_manager.set_vendor_notification_sender(vendor_notification_sender).await; + self.uci_manager.set_data_rcv_notification_sender(data_rcv_notification_sender).await; }); // The potentially !Send NotificationManager is created in a separate thread. let (driver_status_sender, mut driver_status_receiver) = mpsc::unbounded_channel::<bool>(); @@ -174,6 +185,7 @@ impl UciManagerSync { core_notification_receiver, session_notification_receiver, vendor_notification_receiver, + data_rcv_notification_receiver, notification_manager, ); local.spawn_local(async move { @@ -182,156 +194,220 @@ impl UciManagerSync { notification_runtime.block_on(local); }); match driver_status_receiver.blocking_recv() { - Some(true) => Ok(Self { runtime_handle, uci_manager_impl }), + Some(true) => Ok(()), _ => Err(Error::Unknown), } } - /// Set UCI logger mode - pub fn set_logger_mode(&mut self, logger_mode: UciLoggerMode) -> Result<()> { - self.runtime_handle.block_on(self.uci_manager_impl.set_logger_mode(logger_mode)) + /// Set logger mode. + pub fn set_logger_mode(&self, logger_mode: UciLoggerMode) -> Result<()> { + self.runtime_handle.block_on(self.uci_manager.set_logger_mode(logger_mode)) } /// Start UCI HAL and blocking until UCI commands can be sent. - pub fn open_hal(&mut self) -> Result<()> { - self.runtime_handle.block_on(self.uci_manager_impl.open_hal()) + pub fn open_hal(&self) -> Result<()> { + self.runtime_handle.block_on(self.uci_manager.open_hal()) } /// Stop the UCI HAL. - pub fn close_hal(&mut self, force: bool) -> Result<()> { - self.runtime_handle.block_on(self.uci_manager_impl.close_hal(force)) + pub fn close_hal(&self, force: bool) -> Result<()> { + self.runtime_handle.block_on(self.uci_manager.close_hal(force)) } // Methods for sending UCI commands. Functions are blocked until UCI response is received. /// Send UCI command for device reset. - pub fn device_reset(&mut self, reset_config: ResetConfig) -> Result<()> { - self.runtime_handle.block_on(self.uci_manager_impl.device_reset(reset_config)) + pub fn device_reset(&self, reset_config: ResetConfig) -> Result<()> { + self.runtime_handle.block_on(self.uci_manager.device_reset(reset_config)) } /// Send UCI command for getting device info. - pub fn core_get_device_info(&mut self) -> Result<GetDeviceInfoResponse> { - self.runtime_handle.block_on(self.uci_manager_impl.core_get_device_info()) + pub fn core_get_device_info(&self) -> Result<GetDeviceInfoResponse> { + self.runtime_handle.block_on(self.uci_manager.core_get_device_info()) } /// Send UCI command for getting capability info - pub fn core_get_caps_info(&mut self) -> Result<Vec<CapTlv>> { - self.runtime_handle.block_on(self.uci_manager_impl.core_get_caps_info()) + pub fn core_get_caps_info(&self) -> Result<Vec<CapTlv>> { + self.runtime_handle.block_on(self.uci_manager.core_get_caps_info()) } /// Send UCI command for setting core configuration. pub fn core_set_config( - &mut self, + &self, config_tlvs: Vec<DeviceConfigTlv>, ) -> Result<CoreSetConfigResponse> { - self.runtime_handle.block_on(self.uci_manager_impl.core_set_config(config_tlvs)) + self.runtime_handle.block_on(self.uci_manager.core_set_config(config_tlvs)) } /// Send UCI command for getting core configuration. - pub fn core_get_config( - &mut self, - config_ids: Vec<DeviceConfigId>, - ) -> Result<Vec<DeviceConfigTlv>> { - self.runtime_handle.block_on(self.uci_manager_impl.core_get_config(config_ids)) + pub fn core_get_config(&self, config_ids: Vec<DeviceConfigId>) -> Result<Vec<DeviceConfigTlv>> { + self.runtime_handle.block_on(self.uci_manager.core_get_config(config_ids)) } /// Send UCI command for initiating session. - pub fn session_init(&mut self, session_id: SessionId, session_type: SessionType) -> Result<()> { - self.runtime_handle.block_on(self.uci_manager_impl.session_init(session_id, session_type)) + pub fn session_init(&self, session_id: SessionId, session_type: SessionType) -> Result<()> { + self.runtime_handle.block_on(self.uci_manager.session_init(session_id, session_type)) } /// Send UCI command for deinitiating session. - pub fn session_deinit(&mut self, session_id: SessionId) -> Result<()> { - self.runtime_handle.block_on(self.uci_manager_impl.session_deinit(session_id)) + pub fn session_deinit(&self, session_id: SessionId) -> Result<()> { + self.runtime_handle.block_on(self.uci_manager.session_deinit(session_id)) } /// Send UCI command for setting app config. pub fn session_set_app_config( - &mut self, + &self, session_id: SessionId, config_tlvs: Vec<AppConfigTlv>, ) -> Result<SetAppConfigResponse> { self.runtime_handle - .block_on(self.uci_manager_impl.session_set_app_config(session_id, config_tlvs)) + .block_on(self.uci_manager.session_set_app_config(session_id, config_tlvs)) } /// Send UCI command for getting app config. pub fn session_get_app_config( - &mut self, + &self, session_id: SessionId, config_ids: Vec<AppConfigTlvType>, ) -> Result<Vec<AppConfigTlv>> { self.runtime_handle - .block_on(self.uci_manager_impl.session_get_app_config(session_id, config_ids)) + .block_on(self.uci_manager.session_get_app_config(session_id, config_ids)) } /// Send UCI command for getting count of sessions. - pub fn session_get_count(&mut self) -> Result<u8> { - self.runtime_handle.block_on(self.uci_manager_impl.session_get_count()) + pub fn session_get_count(&self) -> Result<u8> { + self.runtime_handle.block_on(self.uci_manager.session_get_count()) } /// Send UCI command for getting state of session. - pub fn session_get_state(&mut self, session_id: SessionId) -> Result<SessionState> { - self.runtime_handle.block_on(self.uci_manager_impl.session_get_state(session_id)) + pub fn session_get_state(&self, session_id: SessionId) -> Result<SessionState> { + self.runtime_handle.block_on(self.uci_manager.session_get_state(session_id)) } /// Send UCI command for updating multicast list for multicast session. pub fn session_update_controller_multicast_list( - &mut self, + &self, session_id: SessionId, action: UpdateMulticastListAction, - controlees: Vec<Controlee>, + controlees: Controlees, ) -> Result<()> { self.runtime_handle.block_on( - self.uci_manager_impl + self.uci_manager .session_update_controller_multicast_list(session_id, action, controlees), ) } - /// Send UCI command for updating multicast list for multicast session (Provisioned STS). - pub fn session_update_controller_multicast_list_v2( - &mut self, - session_id: SessionId, - action: UpdateMulticastListAction, - controlees: ControleesV2, - ) -> Result<()> { + /// Update ranging rounds for DT Tag + pub fn session_update_dt_tag_ranging_rounds( + &self, + session_id: u32, + ranging_round_indexes: Vec<u8>, + ) -> Result<SessionUpdateDtTagRangingRoundsResponse> { self.runtime_handle.block_on( - self.uci_manager_impl - .session_update_controller_multicast_list_v2(session_id, action, controlees), + self.uci_manager + .session_update_dt_tag_ranging_rounds(session_id, ranging_round_indexes), ) } + /// Send UCI command for getting max data size for session. + pub fn session_query_max_data_size(&self, session_id: SessionId) -> Result<u16> { + self.runtime_handle.block_on(self.uci_manager.session_query_max_data_size(session_id)) + } + /// Send UCI command for starting ranging of the session. - pub fn range_start(&mut self, session_id: SessionId) -> Result<()> { - self.runtime_handle.block_on(self.uci_manager_impl.range_start(session_id)) + pub fn range_start(&self, session_id: SessionId) -> Result<()> { + self.runtime_handle.block_on(self.uci_manager.range_start(session_id)) } /// Send UCI command for stopping ranging of the session. - pub fn range_stop(&mut self, session_id: SessionId) -> Result<()> { - self.runtime_handle.block_on(self.uci_manager_impl.range_stop(session_id)) + pub fn range_stop(&self, session_id: SessionId) -> Result<()> { + self.runtime_handle.block_on(self.uci_manager.range_stop(session_id)) } /// Send UCI command for getting ranging count. - pub fn range_get_ranging_count(&mut self, session_id: SessionId) -> Result<usize> { - self.runtime_handle.block_on(self.uci_manager_impl.range_get_ranging_count(session_id)) + pub fn range_get_ranging_count(&self, session_id: SessionId) -> Result<usize> { + self.runtime_handle.block_on(self.uci_manager.range_get_ranging_count(session_id)) } /// Set the country code. Android-specific method. - pub fn android_set_country_code(&mut self, country_code: CountryCode) -> Result<()> { - self.runtime_handle.block_on(self.uci_manager_impl.android_set_country_code(country_code)) + pub fn android_set_country_code(&self, country_code: CountryCode) -> Result<()> { + self.runtime_handle.block_on(self.uci_manager.android_set_country_code(country_code)) } /// Get the power statistics. Android-specific method. - pub fn android_get_power_stats(&mut self) -> Result<PowerStats> { - self.runtime_handle.block_on(self.uci_manager_impl.android_get_power_stats()) + pub fn android_get_power_stats(&self) -> Result<PowerStats> { + self.runtime_handle.block_on(self.uci_manager.android_get_power_stats()) } - /// Send UCI command for a vendor-specific message. - pub fn raw_vendor_cmd( - &mut self, + /// Send a raw UCI command. + pub fn raw_uci_cmd( + &self, + mt: u32, gid: u32, oid: u32, payload: Vec<u8>, - ) -> Result<RawVendorMessage> { - self.runtime_handle.block_on(self.uci_manager_impl.raw_vendor_cmd(gid, oid, payload)) + ) -> Result<RawUciMessage> { + self.runtime_handle.block_on(self.uci_manager.raw_uci_cmd(mt, gid, oid, payload)) + } + + /// Send a data packet + pub fn send_data_packet( + &self, + session_id: SessionId, + address: Vec<u8>, + dest_end_point: FiraComponent, + uci_sequence_num: u8, + app_payload_data: Vec<u8>, + ) -> Result<()> { + self.runtime_handle.block_on(self.uci_manager.send_data_packet( + session_id, + address, + dest_end_point, + uci_sequence_num, + app_payload_data, + )) + } +} + +impl UciManagerSync<UciManagerImpl> { + /// Constructor. + /// + /// UciHal and NotificationManagerBuilder required at construction as they are required before + /// open_hal is called. runtime_handle must be a Handle to a multithread runtime that outlives + /// UciManagerSyncImpl. + /// + /// Implementation note: An explicit decision is made to not use UciManagerImpl as a parameter. + /// UciManagerImpl::new() appears to be sync, but needs an async context to be called, but the + /// user is unlikely to be aware of this technicality. + pub fn new<H, B, L>( + hal: H, + notification_manager_builder: B, + logger: L, + runtime_handle: Handle, + ) -> Result<Self> + where + H: UciHal, + B: NotificationManagerBuilder, + L: UciLogger, + { + // UciManagerImpl::new uses tokio::spawn, so it is called inside the runtime as async fn. + let uci_manager = runtime_handle + .block_on(async { UciManagerImpl::new(hal, logger, UciLoggerMode::Disabled) }); + let mut uci_manager_sync = UciManagerSync { runtime_handle, uci_manager }; + uci_manager_sync.redirect_notification(notification_manager_builder)?; + Ok(uci_manager_sync) + } +} + +#[cfg(any(test, feature = "mock-utils"))] +impl UciManagerSync<MockUciManager> { + /// Constructor for mock version. + pub fn new_mock<T: NotificationManagerBuilder>( + uci_manager: MockUciManager, + runtime_handle: Handle, + notification_manager_builder: T, + ) -> Result<Self> { + let mut uci_manager_sync = UciManagerSync { uci_manager, runtime_handle }; + uci_manager_sync.redirect_notification(notification_manager_builder)?; + Ok(uci_manager_sync) } } @@ -343,80 +419,98 @@ mod tests { use std::rc::Rc; use tokio::runtime::Builder; - use uwb_uci_packets::DeviceState; + use uwb_uci_packets::DeviceState::DeviceStateReady; - use crate::error::Error; - use crate::uci::mock_uci_hal::MockUciHal; - use crate::uci::uci_hal::UciHalPacket; - use crate::uci::uci_logger::UciLoggerNull; + use crate::params::uci_packets::GetDeviceInfoResponse; + use crate::uci::mock_uci_manager::MockUciManager; + use crate::uci::{CoreNotification, UciNotification}; + /// Mock NotificationManager forwarding notifications received. + /// The nonsend_counter is deliberately !send to check UciManagerSync::redirect_notification. struct MockNotificationManager { - device_state_sender: mpsc::UnboundedSender<DeviceState>, + notf_sender: mpsc::UnboundedSender<UciNotification>, // nonsend_counter is an example of a !Send property. nonsend_counter: Rc<RefCell<usize>>, } + impl NotificationManager for MockNotificationManager { fn on_core_notification(&mut self, core_notification: CoreNotification) -> Result<()> { - match core_notification { - CoreNotification::DeviceStatus(device_state) => { - self.nonsend_counter.replace_with(|&mut prev| prev + 1); - self.device_state_sender.send(device_state).map_err(|_| Error::Unknown)?; - } - CoreNotification::GenericError(_) => {} - }; - Ok(()) + self.nonsend_counter.replace_with(|&mut prev| prev + 1); + self.notf_sender + .send(UciNotification::Core(core_notification)) + .map_err(|_| Error::Unknown) } fn on_session_notification( &mut self, - _session_notification: SessionNotification, + session_notification: SessionNotification, ) -> Result<()> { - Ok(()) + self.nonsend_counter.replace_with(|&mut prev| prev + 1); + self.notf_sender + .send(UciNotification::Session(session_notification)) + .map_err(|_| Error::Unknown) } - fn on_vendor_notification(&mut self, _vendor_notification: RawVendorMessage) -> Result<()> { + fn on_vendor_notification(&mut self, vendor_notification: RawUciMessage) -> Result<()> { + self.nonsend_counter.replace_with(|&mut prev| prev + 1); + self.notf_sender + .send(UciNotification::Vendor(vendor_notification)) + .map_err(|_| Error::Unknown) + } + fn on_data_rcv_notification(&mut self, _data_rcv_notf: DataRcvNotification) -> Result<()> { + self.nonsend_counter.replace_with(|&mut prev| prev + 1); Ok(()) } } + /// Builder for MockNotificationManager. struct MockNotificationManagerBuilder { - device_state_sender: mpsc::UnboundedSender<DeviceState>, + notf_sender: mpsc::UnboundedSender<UciNotification>, // initial_count is an example for a parameter undetermined at compile time. - initial_count: usize, } - impl NotificationManagerBuilder<MockNotificationManager> for MockNotificationManagerBuilder { - fn build(self) -> Option<MockNotificationManager> { - Some(MockNotificationManager { - device_state_sender: self.device_state_sender, - nonsend_counter: Rc::new(RefCell::new(self.initial_count)), - }) + + impl MockNotificationManagerBuilder { + /// Constructor for builder. + fn new(notf_sender: mpsc::UnboundedSender<UciNotification>) -> Self { + Self { notf_sender } } } - fn into_raw_messages<T: Into<uwb_uci_packets::UciPacketPacket>>( - builder: T, - ) -> Vec<UciHalPacket> { - let packets: Vec<uwb_uci_packets::UciPacketHalPacket> = builder.into().into(); - packets.into_iter().map(|packet| packet.into()).collect() + impl NotificationManagerBuilder for MockNotificationManagerBuilder { + type NotificationManager = MockNotificationManager; + + fn build(self) -> Option<Self::NotificationManager> { + Some(MockNotificationManager { + notf_sender: self.notf_sender, + nonsend_counter: Rc::new(RefCell::new(0)), + }) + } } #[test] - fn test_sync_uci_open_hal() { - let mut hal = MockUciHal::new(); - let notf = into_raw_messages(uwb_uci_packets::DeviceStatusNtfBuilder { - device_state: uwb_uci_packets::DeviceState::DeviceStateReady, - }); - hal.expected_open(Some(notf), Ok(())); + /// Tests that the Command, Response, and Notification pipeline are functional. + fn test_sync_uci_basic_sequence() { let test_rt = Builder::new_multi_thread().enable_all().build().unwrap(); - let (device_state_sender, mut device_state_receiver) = - mpsc::unbounded_channel::<DeviceState>(); - let mut uci_manager_sync = UciManagerSync::new( - hal, - MockNotificationManagerBuilder { device_state_sender, initial_count: 0 }, - UciLoggerNull::default(), + let (notf_sender, mut notf_receiver) = mpsc::unbounded_channel::<UciNotification>(); + let mut uci_manager_impl = MockUciManager::new(); + uci_manager_impl.expect_open_hal( + vec![UciNotification::Core(CoreNotification::DeviceStatus(DeviceStateReady))], + Ok(()), + ); + uci_manager_impl.expect_core_get_device_info(Ok(GetDeviceInfoResponse { + uci_version: 0, + mac_version: 0, + phy_version: 0, + uci_test_version: 0, + vendor_spec_info: vec![], + })); + let uci_manager_sync = UciManagerSync::new_mock( + uci_manager_impl, test_rt.handle().to_owned(), + MockNotificationManagerBuilder::new(notf_sender), ) .unwrap(); assert!(uci_manager_sync.open_hal().is_ok()); - let device_state = test_rt.block_on(async { device_state_receiver.recv().await }); - assert_eq!(device_state, Some(DeviceState::DeviceStateReady)); + let device_state = test_rt.block_on(async { notf_receiver.recv().await }); + assert!(device_state.is_some()); + assert!(uci_manager_sync.core_get_device_info().is_ok()); } } diff --git a/src/rust/uwb_uci_packets/build.rs b/src/rust/uwb_uci_packets/build.rs index 10ea8f1..f001de5 100644 --- a/src/rust/uwb_uci_packets/build.rs +++ b/src/rust/uwb_uci_packets/build.rs @@ -18,26 +18,27 @@ use std::process::Command; fn main() { let out_dir = std::env::var_os("OUT_DIR").unwrap(); let generated_file = "uci_packets.rs"; + let dst_path = Path::new(&out_dir).join(generated_file); if Path::new(generated_file).exists() { // Copy the rust code directly if the file exists. - let dst_path = Path::new(&out_dir).join(generated_file); let result = std::fs::copy(generated_file, &dst_path); eprintln!("{} exists, copy to {:?}: {:?}", generated_file, dst_path, result); return; } - // Generate the rust code by bluetooth_packetgen. - // The binary should be compiled by `m bluetooth_packetgen -j32` before calling cargo. + // The binary should be compiled by `m pdl` before calling cargo. let output = Command::new("env") - .arg("bluetooth_packetgen") - .arg("--out=".to_owned() + out_dir.to_str().unwrap()) - .arg("--include=.") - .arg("--rust") + .arg("pdl") + .arg("--output-format") + .arg("rust") .arg("uci_packets.pdl") .output() .unwrap(); + std::fs::write(&dst_path, &output.stdout) + .expect(&format!("Could not write {}", dst_path.display())); + eprintln!( "Status: {}, stdout: {}, stderr: {}", output.status, diff --git a/src/rust/uwb_uci_packets/src/lib.rs b/src/rust/uwb_uci_packets/src/lib.rs index dda91c0..c5bb3f5 100644 --- a/src/rust/uwb_uci_packets/src/lib.rs +++ b/src/rust/uwb_uci_packets/src/lib.rs @@ -22,6 +22,8 @@ use std::cmp; use log::error; +use num_derive::FromPrimitive; +use num_traits::FromPrimitive; use zeroize::Zeroize; include!(concat!(env!("OUT_DIR"), "/uci_packets.rs")); @@ -30,54 +32,376 @@ const MAX_PAYLOAD_LEN: usize = 255; // TODO: Use a PDL struct to represent the headers and avoid hardcoding // lengths below. // Real UCI packet header len. -const UCI_PACKET_HAL_HEADER_LEN: usize = 4; +pub const UCI_PACKET_HAL_HEADER_LEN: usize = 4; // Unfragmented UCI packet header len. -const UCI_PACKET_HEADER_LEN: usize = 7; +pub const UCI_PACKET_HEADER_LEN: usize = 7; +// Unfragmented UCI DATA_MESSAGE_SND packet header len. +const UCI_DATA_SND_PACKET_HEADER_LEN: usize = 6; + +// Opcode field byte position (within UCI packet header) and mask (of bits to be used). +const UCI_HEADER_MT_BYTE_POSITION: usize = 0; +const UCI_HEADER_MT_BIT_SHIFT: u8 = 5; +const UCI_HEADER_MT_MASK: u8 = 0x7; + +const UCI_HEADER_PBF_BYTE_POSITION: usize = 0; +const UCI_HEADER_PBF_BIT_SHIFT: u8 = 4; +const UCI_HEADER_PBF_MASK: u8 = 0x1; + +const UCI_CONTROL_HEADER_GID_BYTE_POSITION: usize = 0; +const UCI_CONTROL_HEADER_GID_MASK: u8 = 0xF; + +const UCI_CONTROL_HEADER_OID_BYTE_POSITION: usize = 1; +const UCI_CONTROL_HEADER_OID_MASK: u8 = 0x3F; + +#[derive(Debug, Clone, PartialEq, FromPrimitive)] +pub enum TimeStampLength { + Timestamp40Bit = 0x0, + Timestamp64Bit = 0x1, +} + +#[derive(Debug, Clone, PartialEq, FromPrimitive)] +pub enum DTAnchorLocationType { + NotIncluded = 0x0, + Wgs84 = 0x1, + Relative = 0x2, +} + +#[allow(dead_code)] +#[derive(Debug, Clone, PartialEq)] +pub struct DlTdoaRangingMeasurement { + pub status: u8, + pub message_type: u8, + pub message_control: u16, + pub block_index: u16, + pub round_index: u8, + pub nlos: u8, + pub aoa_azimuth: u16, + pub aoa_azimuth_fom: u8, + pub aoa_elevation: u16, + pub aoa_elevation_fom: u8, + pub rssi: u8, + pub tx_timestamp: u64, + pub rx_timestamp: u64, + pub anchor_cfo: u16, + pub cfo: u16, + pub initiator_reply_time: u32, + pub responder_reply_time: u32, + pub initiator_responder_tof: u16, + pub dt_anchor_location: Vec<u8>, + pub ranging_rounds: Vec<u8>, + total_size: usize, +} + +impl DlTdoaRangingMeasurement { + pub fn parse_one(bytes: &[u8]) -> Option<Self> { + let mut ptr = 0; + let status = extract_u8(bytes, &mut ptr, 1)?; + let message_type = extract_u8(bytes, &mut ptr, 1)?; + let message_control = extract_u16(bytes, &mut ptr, 2)?; + let block_index = extract_u16(bytes, &mut ptr, 2)?; + let round_index = extract_u8(bytes, &mut ptr, 1)?; + let nlos = extract_u8(bytes, &mut ptr, 1)?; + let aoa_azimuth = extract_u16(bytes, &mut ptr, 2)?; + let aoa_azimuth_fom = extract_u8(bytes, &mut ptr, 1)?; + let aoa_elevation = extract_u16(bytes, &mut ptr, 2)?; + let aoa_elevation_fom = extract_u8(bytes, &mut ptr, 1)?; + let rssi = extract_u8(bytes, &mut ptr, 1)?; + let tx_timestamp_length = (message_control >> 1) & 0x1; + let tx_timestamp = match TimeStampLength::from_u16(tx_timestamp_length)? { + TimeStampLength::Timestamp40Bit => extract_u64(bytes, &mut ptr, 5)?, + TimeStampLength::Timestamp64Bit => extract_u64(bytes, &mut ptr, 8)?, + }; + let rx_timestamp_length = (message_control >> 3) & 0x1; + let rx_timestamp = match TimeStampLength::from_u16(rx_timestamp_length)? { + TimeStampLength::Timestamp40Bit => extract_u64(bytes, &mut ptr, 5)?, + TimeStampLength::Timestamp64Bit => extract_u64(bytes, &mut ptr, 8)?, + }; + let anchor_cfo = extract_u16(bytes, &mut ptr, 2)?; + let cfo = extract_u16(bytes, &mut ptr, 2)?; + let initiator_reply_time = extract_u32(bytes, &mut ptr, 4)?; + let responder_reply_time = extract_u32(bytes, &mut ptr, 4)?; + let initiator_responder_tof = extract_u16(bytes, &mut ptr, 2)?; + let dt_location_type = (message_control >> 5) & 0x3; + let dt_anchor_location = match DTAnchorLocationType::from_u16(dt_location_type)? { + DTAnchorLocationType::Wgs84 => extract_vec(bytes, &mut ptr, 10)?, + DTAnchorLocationType::Relative => extract_vec(bytes, &mut ptr, 12)?, + _ => vec![], + }; + let active_ranging_rounds = ((message_control >> 7) & 0xf) as u8; + let ranging_round = extract_vec(bytes, &mut ptr, active_ranging_rounds as usize)?; + + Some(DlTdoaRangingMeasurement { + status, + message_type, + message_control, + block_index, + round_index, + nlos, + aoa_azimuth, + aoa_azimuth_fom, + aoa_elevation, + aoa_elevation_fom, + rssi, + tx_timestamp, + rx_timestamp, + anchor_cfo, + cfo, + initiator_reply_time, + responder_reply_time, + initiator_responder_tof, + dt_anchor_location: dt_anchor_location.to_vec(), + ranging_rounds: ranging_round.to_vec(), + total_size: ptr, + }) + } + pub fn get_total_size(&self) -> usize { + self.total_size + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ShortAddressDlTdoaRangingMeasurement { + pub mac_address: u16, + pub measurement: DlTdoaRangingMeasurement, +} + +impl ShortAddressDlTdoaRangingMeasurement { + /// Parse the `payload` byte buffer from PDL to the vector of measurement. + pub fn parse(bytes: &[u8], no_of_ranging_measurement: u8) -> Option<Vec<Self>> { + let mut ptr = 0; + let mut measurements = vec![]; + let mut count = 0; + while (count < no_of_ranging_measurement) { + let mac_address = extract_u16(bytes, &mut ptr, 2)?; + let rem = &bytes[ptr..]; + let measurement = DlTdoaRangingMeasurement::parse_one(rem); + match measurement { + Some(measurement) => { + ptr += measurement.get_total_size(); + measurements + .push(ShortAddressDlTdoaRangingMeasurement { mac_address, measurement }); + count = count + 1; + } + None => return None, + } + } + Some(measurements) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ExtendedAddressDlTdoaRangingMeasurement { + pub mac_address: u64, + pub measurement: DlTdoaRangingMeasurement, +} + +impl ExtendedAddressDlTdoaRangingMeasurement { + /// Parse the `payload` byte buffer from PDL to the vector of measurement. + pub fn parse(bytes: &[u8], no_of_ranging_measurement: u8) -> Option<Vec<Self>> { + let mut ptr = 0; + let mut measurements = vec![]; + let mut count = 0; + while (count < no_of_ranging_measurement) { + let mac_address = extract_u64(bytes, &mut ptr, 8)?; + let rem = &bytes[ptr..]; + let measurement = DlTdoaRangingMeasurement::parse_one(rem); + match measurement { + Some(measurement) => { + ptr += measurement.get_total_size(); + measurements + .push(ExtendedAddressDlTdoaRangingMeasurement { mac_address, measurement }); + count = count + 1; + } + None => return None, + } + } + Some(measurements) + } +} + +pub fn extract_vec(bytes: &[u8], ptr: &mut usize, consumed_size: usize) -> Option<Vec<u8>> { + if bytes.len() < *ptr + consumed_size { + return None; + } + + let res = bytes[*ptr..*ptr + consumed_size].to_vec(); + *ptr += consumed_size; + Some(res) +} + +/// Generate the function that extracts the value from byte buffers. +macro_rules! generate_extract_func { + ($func_name:ident, $type:ty) => { + /// Extract the value from |byte[ptr..ptr + consumed_size]| in little endian. + fn $func_name(bytes: &[u8], ptr: &mut usize, consumed_size: usize) -> Option<$type> { + const type_size: usize = std::mem::size_of::<$type>(); + if consumed_size > type_size { + return None; + } + + let extracted_bytes = extract_vec(bytes, ptr, consumed_size)?; + let mut le_bytes = [0; type_size]; + le_bytes[0..consumed_size].copy_from_slice(&extracted_bytes); + Some(<$type>::from_le_bytes(le_bytes)) + } + }; +} + +generate_extract_func!(extract_u8, u8); +generate_extract_func!(extract_u16, u16); +generate_extract_func!(extract_u32, u32); +generate_extract_func!(extract_u64, u64); + +// The GroupIdOrDataPacketFormat enum has all the values defined in both the GroupId and +// DataPacketFormat enums. It represents the same bits in UCI packet header - the GID field in +// a UCI control packet, and the DataPacketFormat field in a UCI data packet. Hence the unwrap() +// calls in the conversions below should always succeed (as long as care is taken in future, to +// keep the two enums in sync, for any additional values defined in the UCI spec). +impl From<GroupId> for GroupIdOrDataPacketFormat { + fn from(gid: GroupId) -> Self { + GroupIdOrDataPacketFormat::try_from(u8::from(gid)).unwrap() + } +} + +impl From<GroupIdOrDataPacketFormat> for GroupId { + fn from(gid_or_dpf: GroupIdOrDataPacketFormat) -> Self { + GroupId::try_from(u8::from(gid_or_dpf)).unwrap() + } +} + +impl From<DataPacketFormat> for GroupIdOrDataPacketFormat { + fn from(dpf: DataPacketFormat) -> Self { + GroupIdOrDataPacketFormat::try_from(u8::from(dpf)).unwrap() + } +} + +// The GroupIdOrDataPacketFormat enum has more values defined (for the GroupId bits) than the +// DataPacketFormat enum. Hence this is implemented as TryFrom() instead of From(). +impl TryFrom<GroupIdOrDataPacketFormat> for DataPacketFormat { + type Error = Error; + + fn try_from(gid_or_dpf: GroupIdOrDataPacketFormat) -> Result<Self> { + DataPacketFormat::try_from(u8::from(gid_or_dpf)).or(Err(Error::InvalidPacketError)) + } +} // Container for UCI packet header fields. -struct UciPacketHeader { +struct UciControlPacketHeader { message_type: MessageType, group_id: GroupId, opcode: u8, } +impl UciControlPacketHeader { + fn new(message_type: MessageType, group_id: GroupId, opcode: u8) -> Result<Self> { + if !is_uci_control_packet(message_type) { + return Err(Error::InvalidPacketError); + } + + Ok(UciControlPacketHeader { + message_type: message_type, + group_id: group_id, + opcode: opcode, + }) + } +} + +// Helper methods to extract the UCI Packet header fields. +fn get_mt_from_uci_packet(packet: &[u8]) -> u8 { + (packet[UCI_HEADER_MT_BYTE_POSITION] >> UCI_HEADER_MT_BIT_SHIFT) & UCI_HEADER_MT_MASK +} + +fn get_pbf_from_uci_packet(packet: &[u8]) -> u8 { + (packet[UCI_HEADER_PBF_BYTE_POSITION] >> UCI_HEADER_PBF_BIT_SHIFT) & UCI_HEADER_PBF_MASK +} + +fn get_gid_from_uci_control_packet(packet: &[u8]) -> u8 { + packet[UCI_CONTROL_HEADER_GID_BYTE_POSITION] & UCI_CONTROL_HEADER_GID_MASK +} + +fn get_oid_from_uci_control_packet(packet: &[u8]) -> u8 { + packet[UCI_CONTROL_HEADER_OID_BYTE_POSITION] & UCI_CONTROL_HEADER_OID_MASK +} + +// This function parses the packet bytes to return the Control Packet Opcode (OID) field. The +// caller should check that the packet bytes represent a UCI control packet. The code will not +// panic because UciPacketHal::to_bytes() should always be larger then the place we access. +fn get_opcode_from_uci_control_packet(packet: &UciPacketHal) -> u8 { + get_oid_from_uci_control_packet(&packet.clone().to_bytes()) +} + +fn is_uci_control_packet(message_type: MessageType) -> bool { + match message_type { + MessageType::Command + | MessageType::Response + | MessageType::Notification + | MessageType::ReservedForTesting1 + | MessageType::ReservedForTesting2 => true, + _ => false, + } +} + +pub fn build_uci_control_packet( + message_type: MessageType, + group_id: GroupId, + opcode: u8, + payload: Option<Bytes>, +) -> Option<UciControlPacket> { + if !is_uci_control_packet(message_type) { + error!("Only control packets are allowed, MessageType: {message_type:?}"); + return None; + } + Some(UciControlPacketBuilder { group_id, message_type, opcode, payload }.build()) +} + // Ensure that the new packet fragment belong to the same packet. -fn is_same_packet(header: &UciPacketHeader, packet: &UciPacketHalPacket) -> bool { - header.message_type == packet.get_message_type() - && header.group_id == packet.get_group_id() - && header.opcode == packet.get_opcode() +fn is_same_control_packet(header: &UciControlPacketHeader, packet: &UciPacketHal) -> bool { + is_uci_control_packet(header.message_type) + && header.message_type == packet.get_message_type() + && header.group_id == packet.get_group_id_or_data_packet_format().into() + && header.opcode == get_opcode_from_uci_control_packet(packet) +} + +impl UciControlPacket { + // For some usage, we need to get the raw payload. + pub fn to_raw_payload(self) -> Vec<u8> { + self.to_bytes().slice(UCI_PACKET_HEADER_LEN..).to_vec() + } } -// Helper to convert from vector of |UciPacketHalPacket| to |UciPacketPacket| -impl TryFrom<Vec<UciPacketHalPacket>> for UciPacketPacket { +// Helper to convert from vector of |UciPacketHal| to |UciControlPacket|. An example +// usage is to convert a list UciPacketHAL fragments to one UciPacket, during de-fragmentation. +impl TryFrom<Vec<UciPacketHal>> for UciControlPacket { type Error = Error; - fn try_from(packets: Vec<UciPacketHalPacket>) -> Result<Self> { + fn try_from(packets: Vec<UciPacketHal>) -> Result<Self> { if packets.is_empty() { return Err(Error::InvalidPacketError); } + // Store header info from the first packet. - let header = UciPacketHeader { - message_type: packets[0].get_message_type(), - group_id: packets[0].get_group_id(), - opcode: packets[0].get_opcode(), - }; + let header = UciControlPacketHeader::new( + packets[0].get_message_type(), + packets[0].get_group_id_or_data_packet_format().into(), + get_opcode_from_uci_control_packet(&packets[0]), + )?; - let mut payload_buf = BytesMut::new(); // Create the reassembled payload. + let mut payload_buf = BytesMut::new(); for packet in packets { // Ensure that the new fragment is part of the same packet. - if !is_same_packet(&header, &packet) { + if !is_same_control_packet(&header, &packet) { error!("Received unexpected fragment: {:?}", packet); return Err(Error::InvalidPacketError); } // get payload by stripping the header. payload_buf.extend_from_slice(&packet.to_bytes().slice(UCI_PACKET_HAL_HEADER_LEN..)) } - // Create assembled |UciPacketPacket| and convert to bytes again since we need to + + // Create assembled |UciControlPacket| and convert to bytes again since we need to // reparse the packet after defragmentation to get the appropriate message. - UciPacketPacket::parse( - &UciPacketBuilder { + UciControlPacket::parse( + &UciControlPacketBuilder { message_type: header.message_type, group_id: header.group_id, opcode: header.opcode, @@ -89,23 +413,102 @@ impl TryFrom<Vec<UciPacketHalPacket>> for UciPacketPacket { } } -// Helper to convert from |UciPacketPacket| to vector of |UciPacketHalPacket|s -impl From<UciPacketPacket> for Vec<UciPacketHalPacket> { - fn from(packet: UciPacketPacket) -> Self { +#[derive(Debug, Clone)] +pub struct RawUciControlPacket { + pub mt: u8, + pub gid: u8, + pub oid: u8, + pub payload: Vec<u8>, +} + +impl RawUciControlPacket { + // Match the GID and OID to confirm the UCI packet (represented by header) is + // the same as the stored signature. We don't match the MT because they can be + // different (eg: CMD/RSP pair). + pub fn is_same_signature_bytes(&self, header: &[u8]) -> bool { + let gid = get_gid_from_uci_control_packet(header); + let oid = get_oid_from_uci_control_packet(header); + gid == self.gid && oid == self.oid + } +} + +// UCI Data packet functions. +fn is_uci_data_rcv_packet(message_type: MessageType, data_packet_format: DataPacketFormat) -> bool { + message_type == MessageType::Data && data_packet_format == DataPacketFormat::DataRcv +} + +fn try_into_data_payload(packet: UciPacketHal) -> Result<Bytes> { + if is_uci_data_rcv_packet( + packet.get_message_type(), + packet.get_group_id_or_data_packet_format().try_into()?, + ) { + Ok(packet.to_bytes().slice(UCI_PACKET_HAL_HEADER_LEN..)) + } else { + error!("Received unexpected data packet fragment: {:?}", packet); + Err(Error::InvalidPacketError) + } +} + +// Helper to convert from vector of |UciPacketHal| to |UciDataPacket|. An example +// usage is to convert a list UciPacketHAL fragments to one UciPacket, during de-fragmentation. +impl TryFrom<Vec<UciPacketHal>> for UciDataPacket { + type Error = Error; + + fn try_from(packets: Vec<UciPacketHal>) -> Result<Self> { + if packets.is_empty() { + return Err(Error::InvalidPacketError); + } + + // Create the reassembled payload. + let mut payload_buf = Bytes::new(); + for packet in packets { + // Ensure that the fragment is a Data Rcv packet. + // Get payload by stripping the header. + payload_buf = [payload_buf, try_into_data_payload(packet)?].concat().into(); + } + + // Create assembled |UciDataPacket| and convert to bytes again since we need to + // reparse the packet after defragmentation to get the appropriate message. + UciDataPacket::parse( + &UciDataPacketBuilder { + message_type: MessageType::Data, + data_packet_format: DataPacketFormat::DataRcv, + payload: Some(payload_buf.into()), + } + .build() + .to_bytes(), + ) + } +} + +// Helper to convert from |UciControlPacket| to vector of |UciControlPacketHal|s. An +// example usage is to do this conversion for fragmentation (from Host to UWBS). +impl From<UciControlPacket> for Vec<UciControlPacketHal> { + fn from(packet: UciControlPacket) -> Self { // Store header info. - let header = UciPacketHeader { - message_type: packet.get_message_type(), - group_id: packet.get_group_id(), - opcode: packet.get_opcode(), + let header = match UciControlPacketHeader::new( + packet.get_message_type(), + packet.get_group_id(), + packet.get_opcode(), + ) { + Ok(hdr) => hdr, + _ => { + error!( + "Unable to parse UciControlPacketHeader from UciControlPacket: {:?}", + packet + ); + return Vec::new(); + } }; - let mut fragments: Vec<UciPacketHalPacket> = Vec::new(); + + let mut fragments = Vec::new(); // get payload by stripping the header. let payload = packet.to_bytes().slice(UCI_PACKET_HEADER_LEN..); if payload.is_empty() { fragments.push( - UciPacketHalBuilder { + UciControlPacketHalBuilder { message_type: header.message_type, - group_id: header.group_id, + group_id_or_data_packet_format: header.group_id.into(), opcode: header.opcode, packet_boundary_flag: PacketBoundaryFlag::Complete, payload: None, @@ -122,9 +525,9 @@ impl From<UciPacketPacket> for Vec<UciPacketHalPacket> { PacketBoundaryFlag::Complete }; fragments.push( - UciPacketHalBuilder { + UciControlPacketHalBuilder { message_type: header.message_type, - group_id: header.group_id, + group_id_or_data_packet_format: header.group_id.into(), opcode: header.opcode, packet_boundary_flag: pbf, payload: Some(Bytes::from(fragment.to_owned())), @@ -137,37 +540,175 @@ impl From<UciPacketPacket> for Vec<UciPacketHalPacket> { } } +// Helper to convert From<UciDataSnd> into Vec<UciDataPacketHal>. An +// example usage is for fragmentation in the Data Packet Tx flow. +impl From<UciDataSnd> for Vec<UciDataPacketHal> { + fn from(packet: UciDataSnd) -> Self { + let mut fragments = Vec::new(); + let dpf = packet.get_data_packet_format().into(); + + // get payload by stripping the header. + let payload = packet.to_bytes().slice(UCI_DATA_SND_PACKET_HEADER_LEN..); + if payload.is_empty() { + fragments.push( + UciDataPacketHalBuilder { + group_id_or_data_packet_format: dpf, + packet_boundary_flag: PacketBoundaryFlag::Complete, + payload: None, + } + .build(), + ); + } else { + let mut fragments_iter = payload.chunks(MAX_PAYLOAD_LEN).peekable(); + while let Some(fragment) = fragments_iter.next() { + // Set the last fragment complete if this is last fragment. + let pbf = if let Some(nxt_fragment) = fragments_iter.peek() { + PacketBoundaryFlag::NotComplete + } else { + PacketBoundaryFlag::Complete + }; + fragments.push( + UciDataPacketHalBuilder { + group_id_or_data_packet_format: dpf, + packet_boundary_flag: pbf, + payload: Some(Bytes::from(fragment.to_owned())), + } + .build(), + ); + } + } + fragments + } +} + #[derive(Default, Debug)] pub struct PacketDefrager { // Cache to store incoming fragmented packets in the middle of reassembly. // Will be empty if there is no reassembly in progress. - fragment_cache: Vec<UciPacketHalPacket>, + // TODO(b/261762781): Prefer this to be UciControlPacketHal + control_fragment_cache: Vec<UciPacketHal>, + // TODO(b/261762781): Prefer this to be UciDataPacketHal + data_fragment_cache: Vec<UciPacketHal>, + // Raw packet payload bytes cache + raw_fragment_cache: Vec<u8>, +} + +pub enum UciDefragPacket { + Control(UciControlPacket), + Data(UciDataPacket), + Raw(Result<()>, RawUciControlPacket), } impl PacketDefrager { - pub fn defragment_packet(&mut self, msg: &[u8]) -> Option<UciPacketPacket> { - match UciPacketHalPacket::parse(msg) { - Ok(packet) => { - let pbf = packet.get_packet_boundary_flag(); - // Add the incoming fragment to the packet cache. - self.fragment_cache.push(packet); - if pbf == PacketBoundaryFlag::NotComplete { - // Wait for remaining fragments. - return None; - } - // All fragments received, defragment the packet. - match self.fragment_cache.drain(..).collect::<Vec<_>>().try_into() { - Ok(packet) => Some(packet), - Err(e) => { - error!("Failed to defragment packet: {:?}", e); - None + pub fn defragment_packet( + &mut self, + msg: &[u8], + last_raw_cmd: Option<RawUciControlPacket>, + ) -> Option<UciDefragPacket> { + if let Some(raw_cmd) = last_raw_cmd { + let mt_u8 = get_mt_from_uci_packet(msg); + match MessageType::try_from(u8::from(mt_u8)) { + Ok(mt) => match mt { + // Parse only a UCI response packet as a Raw packet. + MessageType::Response => { + return self.defragment_raw_uci_response_packet(msg, raw_cmd); } + _ => { /* Fallthrough to de-frag as a normal UCI packet below */ } + }, + Err(_) => { + error!("Rx packet from HAL has unrecognized MT={}", mt_u8); + return Some(UciDefragPacket::Raw( + Err(Error::InvalidPacketError), + RawUciControlPacket { mt: mt_u8, gid: 0, oid: 0, payload: Vec::new() }, + )); } - } - Err(e) => { + }; + } + + let packet = UciPacketHal::parse(msg) + .or_else(|e| { error!("Failed to parse packet: {:?}", e); - None + Err(e) + }) + .ok()?; + + let pbf = packet.get_packet_boundary_flag(); + + // TODO(b/261762781): The current implementation allows for the possibility that we receive + // interleaved Control/Data HAL packets, and so uses separate caches for them. In the + // future, if we determine that interleaving is not possible, this can be simplified. + if is_uci_control_packet(packet.get_message_type()) { + // Add the incoming fragment to the control packet cache. + self.control_fragment_cache.push(packet); + if pbf == PacketBoundaryFlag::NotComplete { + // Wait for remaining fragments. + return None; + } + + // All fragments received, defragment the control packet. + match self.control_fragment_cache.drain(..).collect::<Vec<_>>().try_into() { + Ok(packet) => Some(UciDefragPacket::Control(packet)), + Err(e) => { + error!("Failed to defragment control packet: {:?}", e); + None + } } + } else { + // Add the incoming fragment to the data packet cache. + self.data_fragment_cache.push(packet); + if pbf == PacketBoundaryFlag::NotComplete { + // Wait for remaining fragments. + return None; + } + + // All fragments received, defragment the data packet. + match self.data_fragment_cache.drain(..).collect::<Vec<_>>().try_into() { + Ok(packet) => Some(UciDefragPacket::Data(packet)), + Err(e) => { + error!("Failed to defragment data packet: {:?}", e); + None + } + } + } + } + + fn defragment_raw_uci_response_packet( + &mut self, + msg: &[u8], + raw_cmd: RawUciControlPacket, + ) -> Option<UciDefragPacket> { + let mt_u8 = get_mt_from_uci_packet(msg); + let pbf = get_pbf_from_uci_packet(msg); + let gid = get_gid_from_uci_control_packet(msg); + let oid = get_oid_from_uci_control_packet(msg); + if raw_cmd.is_same_signature_bytes(msg) { + // Store only the packet payload bytes (UCI header should not be stored). + self.raw_fragment_cache.extend_from_slice(&msg[UCI_PACKET_HAL_HEADER_LEN..]); + + if pbf == u8::from(PacketBoundaryFlag::NotComplete) { + return None; + } + + // All fragments received, defragment and return the Raw packet's payload bytes. + return Some(UciDefragPacket::Raw( + Ok(()), + RawUciControlPacket { + mt: mt_u8, + gid, + oid, + payload: self.raw_fragment_cache.drain(..).collect(), + }, + )); + } else { + error!( + "Rx packet from HAL (MT={}, PBF={}, GID={}, OID={}) has non-matching\ + RawCmd signature", + mt_u8, pbf, gid, oid + ); + return Some(UciDefragPacket::Raw( + Err(Error::InvalidPacketError), + RawUciControlPacket { mt: mt_u8, gid, oid, payload: Vec::new() }, + )); } } } @@ -191,9 +732,7 @@ pub struct ParsedFrameReport { cir: Vec<CirValue>, } -pub fn parse_diagnostics_ntf( - evt: AndroidRangeDiagnosticsNtfPacket, -) -> Result<ParsedDiagnosticNtfPacket> { +pub fn parse_diagnostics_ntf(evt: AndroidRangeDiagnosticsNtf) -> Result<ParsedDiagnosticNtfPacket> { let session_id = evt.get_session_id(); let sequence_number = evt.get_sequence_number(); let mut parsed_frame_reports = Vec::new(); @@ -202,7 +741,7 @@ pub fn parse_diagnostics_ntf( let mut aoa_vec = Vec::new(); let mut cir_vec = Vec::new(); for tlv in &report.frame_report_tlvs { - match FrameReportTlvPacketPacket::parse( + match FrameReportTlvPacket::parse( &[vec![tlv.t as u8, tlv.v.len() as u8, (tlv.v.len() >> 8) as u8], tlv.v.clone()] .concat(), ) { @@ -241,8 +780,8 @@ pub fn parse_diagnostics_ntf( } #[derive(Debug, Clone, PartialEq)] -pub enum ControleesV2 { - NoSessionKey(Vec<Controlee_V2_0_0_Byte_Version>), +pub enum Controlees { + NoSessionKey(Vec<Controlee>), ShortSessionKey(Vec<Controlee_V2_0_16_Byte_Version>), LongSessionKey(Vec<Controlee_V2_0_32_Byte_Version>), } @@ -257,25 +796,12 @@ pub fn write_controlee(controlee: &Controlee) -> BytesMut { buffer } -pub fn write_controlee_2_0_0byte(controlee: &Controlee_V2_0_0_Byte_Version) -> BytesMut { - let mut buffer = BytesMut::new(); - let short_address = controlee.short_address; - buffer.extend_from_slice(&short_address.to_le_bytes()[0..2]); - let subsession_id = controlee.subsession_id; - buffer.extend_from_slice(&subsession_id.to_le_bytes()[0..4]); - let message_control = controlee.message_control.to_u8().unwrap(); - buffer.extend_from_slice(&message_control.to_le_bytes()[0..1]); - buffer -} - pub fn write_controlee_2_0_16byte(controlee: &Controlee_V2_0_16_Byte_Version) -> BytesMut { let mut buffer = BytesMut::new(); let short_address = controlee.short_address; buffer.extend_from_slice(&short_address.to_le_bytes()[0..2]); let subsession_id = controlee.subsession_id; buffer.extend_from_slice(&subsession_id.to_le_bytes()[0..4]); - let message_control = controlee.message_control.to_u8().unwrap(); - buffer.extend_from_slice(&message_control.to_le_bytes()[0..1]); buffer.extend_from_slice(&controlee.subsession_key); buffer } @@ -286,70 +812,54 @@ pub fn write_controlee_2_0_32byte(controlee: &Controlee_V2_0_32_Byte_Version) -> buffer.extend_from_slice(&short_address.to_le_bytes()[0..2]); let subsession_id = controlee.subsession_id; buffer.extend_from_slice(&subsession_id.to_le_bytes()[0..4]); - let message_control = controlee.message_control.to_u8().unwrap(); - buffer.extend_from_slice(&message_control.to_le_bytes()[0..1]); buffer.extend_from_slice(&controlee.subsession_key); buffer } -/// Generate the V1 SessionUpdateControllerMulticastListCmd packet. +/// Generate the SessionUpdateControllerMulticastListCmd packet. /// -/// Workaround for handling the non-compatible command. -/// Size check omitted and UCI spec compliancy is up to the caller of the method. -pub fn build_session_update_controller_multicast_list_cmd_v1( +/// This function can build the packet with/without message control, which +/// is indicated by action parameter. +pub fn build_session_update_controller_multicast_list_cmd( session_id: u32, action: UpdateMulticastListAction, - controlees: Vec<Controlee>, -) -> SessionUpdateControllerMulticastListCmdPacket { - let mut controlees_buf = BytesMut::new(); - controlees_buf.extend_from_slice(&(controlees.len() as u8).to_le_bytes()); - for controlee in controlees { - controlees_buf.extend_from_slice(&write_controlee(&controlee)); - } - SessionUpdateControllerMulticastListCmdBuilder { - session_id, - action, - payload: Some(controlees_buf.freeze()), - } - .build() -} - -/// Generate the V2 SessionUpdateControllerMulticastListCmd packet. -/// -/// Workaround for handling the non-compatible command. -/// Size check omitted and UCI spec compliancy is up to the caller of the method. -pub fn build_session_update_controller_multicast_list_cmd_v2( - session_id: u32, - action: UpdateMulticastListAction, - controlees: ControleesV2, -) -> SessionUpdateControllerMulticastListCmdPacket { + controlees: Controlees, +) -> Result<SessionUpdateControllerMulticastListCmd> { let mut controlees_buf = BytesMut::new(); match controlees { - ControleesV2::NoSessionKey(controlee_v2) => { - controlees_buf.extend_from_slice(&(controlee_v2.len() as u8).to_le_bytes()); - for controlee in controlee_v2 { - controlees_buf.extend_from_slice(&write_controlee_2_0_0byte(&controlee)); + Controlees::NoSessionKey(controlee_v1) + if action == UpdateMulticastListAction::AddControlee + || action == UpdateMulticastListAction::RemoveControlee => + { + controlees_buf.extend_from_slice(&(controlee_v1.len() as u8).to_le_bytes()); + for controlee in controlee_v1 { + controlees_buf.extend_from_slice(&write_controlee(&controlee)); } } - ControleesV2::ShortSessionKey(controlee_v2) => { + Controlees::ShortSessionKey(controlee_v2) + if action == UpdateMulticastListAction::AddControleeWithShortSubSessionKey => + { controlees_buf.extend_from_slice(&(controlee_v2.len() as u8).to_le_bytes()); for controlee in controlee_v2 { controlees_buf.extend_from_slice(&write_controlee_2_0_16byte(&controlee)); } } - ControleesV2::LongSessionKey(controlee_v2) => { + Controlees::LongSessionKey(controlee_v2) + if action == UpdateMulticastListAction::AddControleeWithLongSubSessionKey => + { controlees_buf.extend_from_slice(&(controlee_v2.len() as u8).to_le_bytes()); for controlee in controlee_v2 { controlees_buf.extend_from_slice(&write_controlee_2_0_32byte(&controlee)); } } + _ => return Err(Error::InvalidPacketError), } - SessionUpdateControllerMulticastListCmdBuilder { + Ok(SessionUpdateControllerMulticastListCmdBuilder { session_id, action, payload: Some(controlees_buf.freeze()), } - .build() + .build()) } impl Drop for AppConfigTlv { @@ -413,15 +923,16 @@ mod tests { } #[test] - fn test_build_multicast_update_v1_packet() { + fn test_build_multicast_update_packet() { let controlee = Controlee { short_address: 0x1234, subsession_id: 0x1324_3546 }; - let packet: UciPacketPacket = build_session_update_controller_multicast_list_cmd_v1( + let packet: UciControlPacket = build_session_update_controller_multicast_list_cmd( 0x1425_3647, UpdateMulticastListAction::AddControlee, - vec![controlee; 1], + Controlees::NoSessionKey(vec![controlee; 1]), ) + .unwrap() .into(); - let packet_fragments: Vec<UciPacketHalPacket> = packet.into(); + let packet_fragments: Vec<UciControlPacketHal> = packet.into(); let uci_packet: Vec<u8> = packet_fragments[0].clone().into(); assert_eq!( uci_packet, @@ -433,4 +944,212 @@ mod tests { ] ); } + + #[test] + fn test_to_raw_payload() { + let payload = vec![0x11, 0x22, 0x33]; + let payload_clone = payload.clone(); + let packet = UciControlPacketBuilder { + group_id: GroupId::Test, + message_type: MessageType::Response, + opcode: 0x5, + payload: Some(payload_clone.into()), + } + .build(); + + assert_eq!(payload, packet.to_raw_payload()); + } + + #[test] + fn test_to_raw_payload_empty() { + let payload: Vec<u8> = vec![]; + let packet = UciControlPacketBuilder { + group_id: GroupId::Test, + message_type: MessageType::Response, + opcode: 0x5, + payload: None, + } + .build(); + + assert_eq!(payload, packet.to_raw_payload()); + } + + #[cfg(test)] + mod tests { + use crate::{extract_u16, extract_u32, extract_u64, extract_u8, extract_vec}; + #[test] + fn test_extract_func() { + let bytes = [0x1, 0x3, 0x5, 0x7, 0x9, 0x2, 0x4, 0x05, 0x07, 0x09, 0x0a]; + let mut ptr = 0; + + let u8_val = extract_u8(&bytes, &mut ptr, 1); + assert_eq!(u8_val, Some(0x1)); + assert_eq!(ptr, 1); + + let u16_val = extract_u16(&bytes, &mut ptr, 2); + assert_eq!(u16_val, Some(0x0503)); + assert_eq!(ptr, 3); + + let u32_val = extract_u32(&bytes, &mut ptr, 3); + assert_eq!(u32_val, Some(0x020907)); + assert_eq!(ptr, 6); + + let u64_val = extract_u64(&bytes, &mut ptr, 5); + assert_eq!(u64_val, Some(0x0a09070504)); + assert_eq!(ptr, 11); + + let vec = extract_vec(&bytes, &mut ptr, 3); + assert_eq!(vec, None); + assert_eq!(ptr, 11); + } + } + + #[test] + fn test_short_dltdoa_ranging_measurement() { + let bytes = [ + // All Fields in Little Endian (LE) + // First measurement + 0x0a, 0x01, 0x33, 0x05, // 2(Mac address), Status, Message Type + 0x33, 0x05, 0x02, 0x05, // 2(Message control), 2(Block Index) + 0x07, 0x09, 0x0a, 0x01, // Round Index, NLoS, 2(AoA Azimuth) + 0x02, 0x05, 0x07, 0x09, // AoA Azimuth FOM, 2(AoA Elevation), AoA Elevation FOM + 0x0a, 0x01, 0x02, 0x05, // RSSI, 3(Tx Timestamp..) + 0x07, 0x09, 0x0a, 0x01, // 4(Tx Timestamp..) + 0x02, 0x05, 0x07, 0x09, // Tx Timestamp, 3(Rx Timestamp..) + 0x05, 0x07, 0x09, 0x0a, // 2(Rx Timestamp), 2(Anchor Cfo) + 0x01, 0x02, 0x05, 0x07, // 2(Cfo), 2(Initiator Reply Time..) + 0x09, 0x05, 0x07, 0x09, // 2(Initiator Reply Time), 2(Responder Reply Time..) + 0x0a, 0x01, 0x02, 0x05, // 2(Responder Reply Time), 2(Initiator-Responder ToF) + 0x07, 0x09, 0x07, 0x09, // 4(Anchor Location..) + 0x05, 0x07, 0x09, 0x0a, // 4(Anchor Location..) + 0x01, 0x02, 0x05, 0x07, // 2(Anchor Location..), 2(Active Ranging Rounds..) + 0x09, 0x0a, 0x01, 0x02, // 4(Active Ranging Rounds..) + 0x05, 0x07, 0x09, 0x05, // 4(Active Ranging Rounds) + // Second measurement + 0x0a, 0x01, 0x33, 0x05, // 2(Mac address), Status, Message Type + 0x33, 0x05, 0x02, 0x05, // 2(Message control), 2(Block Index) + 0x07, 0x09, 0x0a, 0x01, // Round Index, NLoS, 2(AoA Azimuth) + 0x02, 0x05, 0x07, 0x09, // AoA Azimuth FOM, 2(AoA Elevation), AoA Elevation FOM + 0x0a, 0x01, 0x02, 0x05, // RSSI, 3(Tx Timestamp..) + 0x07, 0x09, 0x0a, 0x01, // 4(Tx Timestamp..) + 0x02, 0x05, 0x07, 0x09, // Tx Timestamp, 3(Rx Timestamp..) + 0x05, 0x07, 0x09, 0x0a, // 2(Rx Timestamp), 2(Anchor Cfo) + 0x01, 0x02, 0x05, 0x07, // 2(Cfo), 2(Initiator Reply Time..) + 0x09, 0x05, 0x07, 0x09, // 2(Initiator Reply Time), 2(Responder Reply Time..) + 0x0a, 0x01, 0x02, 0x05, // 2(Responder Reply Time), 2(Initiator-Responder ToF) + 0x07, 0x09, 0x07, 0x09, // 4(Anchor Location..) + 0x05, 0x07, 0x09, 0x0a, // 4(Anchor Location..) + 0x01, 0x02, 0x05, 0x07, // 2(Anchor Location..), 2(Active Ranging Rounds..) + 0x09, 0x0a, 0x01, 0x02, // 4(Active Ranging Rounds..) + 0x05, 0x07, 0x09, 0x05, // 4(Active Ranging Rounds) + ]; + + let measurements = ShortAddressDlTdoaRangingMeasurement::parse(&bytes, 2).unwrap(); + assert_eq!(measurements.len(), 2); + let measurement_1 = &measurements[0].measurement; + let mac_address_1 = &measurements[0].mac_address; + assert_eq!(*mac_address_1, 0x010a); + assert_eq!(measurement_1.status, 0x33); + assert_eq!(measurement_1.message_type, 0x05); + assert_eq!(measurement_1.message_control, 0x0533); + assert_eq!(measurement_1.block_index, 0x0502); + assert_eq!(measurement_1.round_index, 0x07); + assert_eq!(measurement_1.nlos, 0x09); + assert_eq!(measurement_1.aoa_azimuth, 0x010a); + assert_eq!(measurement_1.aoa_azimuth_fom, 0x02); + assert_eq!(measurement_1.aoa_elevation, 0x0705); + assert_eq!(measurement_1.aoa_elevation_fom, 0x09); + assert_eq!(measurement_1.rssi, 0x0a); + assert_eq!(measurement_1.tx_timestamp, 0x02010a0907050201); + assert_eq!(measurement_1.rx_timestamp, 0x0705090705); + assert_eq!(measurement_1.anchor_cfo, 0x0a09); + assert_eq!(measurement_1.cfo, 0x0201); + assert_eq!(measurement_1.initiator_reply_time, 0x05090705); + assert_eq!(measurement_1.responder_reply_time, 0x010a0907); + assert_eq!(measurement_1.initiator_responder_tof, 0x0502); + assert_eq!( + measurement_1.dt_anchor_location, + vec![0x07, 0x09, 0x07, 0x09, 0x05, 0x07, 0x09, 0x0a, 0x01, 0x02] + ); + assert_eq!( + measurement_1.ranging_rounds, + vec![0x05, 0x07, 0x09, 0x0a, 0x01, 0x02, 0x05, 0x07, 0x09, 0x05,] + ); + + let measurement_2 = &measurements[1].measurement; + let mac_address_2 = &measurements[1].mac_address; + assert_eq!(*mac_address_2, 0x010a); + assert_eq!(measurement_2.status, 0x33); + assert_eq!(measurement_2.message_type, 0x05); + assert_eq!(measurement_2.message_control, 0x0533); + assert_eq!(measurement_2.block_index, 0x0502); + assert_eq!(measurement_2.round_index, 0x07); + assert_eq!(measurement_2.nlos, 0x09); + assert_eq!(measurement_2.aoa_azimuth, 0x010a); + assert_eq!(measurement_2.aoa_azimuth_fom, 0x02); + assert_eq!(measurement_2.aoa_elevation, 0x0705); + assert_eq!(measurement_2.aoa_elevation_fom, 0x09); + assert_eq!(measurement_2.rssi, 0x0a); + assert_eq!(measurement_2.tx_timestamp, 0x02010a0907050201); + assert_eq!(measurement_2.rx_timestamp, 0x0705090705); + assert_eq!(measurement_2.anchor_cfo, 0x0a09); + assert_eq!(measurement_2.cfo, 0x0201); + assert_eq!(measurement_2.initiator_reply_time, 0x05090705); + assert_eq!(measurement_2.responder_reply_time, 0x010a0907); + assert_eq!(measurement_2.initiator_responder_tof, 0x0502); + assert_eq!( + measurement_1.dt_anchor_location, + vec![0x07, 0x09, 0x07, 0x09, 0x05, 0x07, 0x09, 0x0a, 0x01, 0x02] + ); + assert_eq!( + measurement_1.ranging_rounds, + vec![0x05, 0x07, 0x09, 0x0a, 0x01, 0x02, 0x05, 0x07, 0x09, 0x05,] + ); + } + + #[test] + fn test_extended_dltdoa_ranging_measurement() { + let bytes = [ + // All Fields in Little Endian (LE) + /* First measurement */ + 0x0a, 0x01, 0x33, 0x05, // 4(Mac address..) + 0x33, 0x05, 0x02, 0x05, // 4(Mac address) + 0x07, 0x09, 0x0a, 0x01, // Status, Message Type, 2(Message control), + 0x02, 0x05, 0x07, 0x09, // 2(Block Index), Round Index, NLoS, + 0x0a, 0x01, 0x02, 0x05, // 2(AoA Azimuth), AoA Azimuth FOM, 1(AoA Elevation..) + 0x07, 0x09, 0x0a, // 1(AoA Elevation), AoA Elevation FOM, RSSI, + 0x01, 0x02, 0x05, 0x07, // 4(Tx Timestamp..) + 0x09, 0x05, 0x07, 0x09, // 4(Tx Timestamp), + 0x0a, 0x01, 0x02, 0x05, // 4(Rx Timestamp..) + 0x07, 0x09, 0x05, 0x07, // 4(Rx Timestamp) + 0x09, 0x0a, 0x01, 0x02, // 2(Anchor Cfo), 2(Cfo), + 0x05, 0x07, 0x09, 0x05, // 4(Initiator Reply Time) + 0x07, 0x09, 0x0a, 0x01, // 4(Responder Reply Time), + 0x02, 0x05, 0x02, 0x05, // 2(Initiator-Responder ToF), 2(Active Ranging Rounds) + ]; + + let measurements = ExtendedAddressDlTdoaRangingMeasurement::parse(&bytes, 1).unwrap(); + assert_eq!(measurements.len(), 1); + let measurement = &measurements[0].measurement; + let mac_address = &measurements[0].mac_address; + assert_eq!(*mac_address, 0x050205330533010a); + assert_eq!(measurement.message_control, 0x010a); + assert_eq!(measurement.block_index, 0x0502); + assert_eq!(measurement.round_index, 0x07); + assert_eq!(measurement.nlos, 0x09); + assert_eq!(measurement.aoa_azimuth, 0x010a); + assert_eq!(measurement.aoa_azimuth_fom, 0x02); + assert_eq!(measurement.aoa_elevation, 0x0705); + assert_eq!(measurement.aoa_elevation_fom, 0x09); + assert_eq!(measurement.rssi, 0x0a); + assert_eq!(measurement.tx_timestamp, 0x0907050907050201); + assert_eq!(measurement.rx_timestamp, 0x070509070502010a); + assert_eq!(measurement.anchor_cfo, 0x0a09); + assert_eq!(measurement.cfo, 0x0201); + assert_eq!(measurement.initiator_reply_time, 0x05090705); + assert_eq!(measurement.responder_reply_time, 0x010a0907); + assert_eq!(measurement.initiator_responder_tof, 0x0502); + assert_eq!(measurement.dt_anchor_location, vec![]); + assert_eq!(measurement.ranging_rounds, vec![0x02, 0x05]); + } } diff --git a/src/rust/uwb_uci_packets/uci_packets.pdl b/src/rust/uwb_uci_packets/uci_packets.pdl index 5ca02d2..848195a 100644 --- a/src/rust/uwb_uci_packets/uci_packets.pdl +++ b/src/rust/uwb_uci_packets/uci_packets.pdl @@ -8,7 +8,28 @@ enum PacketBoundaryFlag : 1 { enum GroupId : 4 { CORE = 0x00, SESSION_CONFIG = 0x01, - RANGING_SESSION_CONTROL = 0x02, + 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 { + 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, @@ -30,7 +51,7 @@ enum CoreOpCode : 6 { CORE_GENERIC_ERROR_NTF = 0x07, } -enum SessionOpCode : 6 { +enum SessionConfigOpCode : 6 { SESSION_INIT = 0x00, SESSION_DEINIT = 0x01, SESSION_STATUS_NTF = 0x02, @@ -39,13 +60,20 @@ enum SessionOpCode : 6 { 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 RangeOpCode : 6 { - RANGE_START = 0x00, - RANGE_STOP = 0x01, - RANGE_INTERVAL_UPDATE_REQ = 0x02, - RANGE_GET_RANGING_COUNT = 0x03, +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 { @@ -73,6 +101,8 @@ enum StatusCode : 8 { UCI_STATUS_UNKNOWN_OID = 0x08, UCI_STATUS_READ_ONLY = 0x09, UCI_STATUS_COMMAND_RETRY = 0x0A, + UCI_STATUS_NOT_APPLICABLE = 0x0B, + RFU_STATUS_CODE_RANGE_1 = 0x0C..0x10, // UWB Session Specific Status Codes UCI_STATUS_SESSION_NOT_EXIST = 0x11, @@ -84,6 +114,9 @@ enum StatusCode : 8 { 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, // UWB Ranging Session Specific Status Codes UCI_STATUS_RANGING_TX_FAILED = 0x20, @@ -94,14 +127,63 @@ enum StatusCode : 8 { 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_ROUND_INDEX_NOT_SET_AS_INITIATOR = 0x2A, + UCI_STATUS_ERROR_DL_TDOA_DEVICE_ADDRESS_NOT_MATCHING_IN_REPLY_TIME_LIST = 0x2B, + RFU_STATUS_CODE_RANGE_3 = 0x2C..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, // Vendor Specific Status Codes - UCI_STATUS_ERROR_CCC_SE_BUSY = 0x50, - UCI_STATUS_ERROR_CCC_LIFECYCLE = 0x51, + 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, + }, + + // For internal usage, we will use 0xFF as default. + VENDOR_SPECIFIC_STATUS_CODE_2 = 0xFF, +} + +// This needs a separate type as the Status code values in an OWR for AOA +// Measurement has different values. +enum OwrAoaStatusCode : 8 { + UCI_STATUS_SUCCESS = 0x00, + UCI_STATUS_INTER_FRAME_INTERVAL_TIMEOUT = 0x01, +} + +// This needs a separate StatusCode as the Status code values in the DATA_RCV packet have +// different values from the generic StatusCode above. +enum DataRcvStatusCode : 8 { + UCI_STATUS_SUCCESS = 0x00, + UCI_STATUS_ERROR = 0x01, + UCI_STATUS_UNKNOWN = 0x02, +} + +enum CreditAvailability : 8 { + CREDIT_NOT_AVAILABLE = 0, + CREDIT_AVAILABLE = 1, +} + +// The UCI spec defines these status codes for a DATA_TRANSFER_STATUS_NTF packet. +enum DataTransferNtfStatusCode : 8 { + UCI_DATA_TRANSFER_STATUS_REPETITION_OK = 0x00, + UCI_DATA_TRANSFER_STATUS_OK = 0x01, + UCI_DATA_TRANSFER_STATUS_ERROR_DATA_TRANSFER = 0x02, + UCI_DATA_TRANSFER_STATUS_ERROR_NO_CREDIT_AVAILABLE = 0x03, + UCI_DATA_TRANSFER_STATUS_ERROR_REJECTED = 0x04, + UCI_DATA_TRANSFER_STATUS_SESSION_TYPE_NOT_SUPPORTED = 0x05, + UCI_DATA_TRANSFER_STATUS_ERROR_DATA_TRANSFER_IS_ONGOING = 0x06, +} + +enum FiraComponent : 8 { + UWBS = 0x00, + HOST = 0x01, + SECURE_COMPONENT = 0x02, } enum ResetConfig : 8 { @@ -138,6 +220,7 @@ enum AppConfigTlvType : 8 { SFD_ID = 0x15, PSDU_DATA_RATE = 0x16, PREAMBLE_DURATION = 0x17, + LINK_LAYER_MODE = 0x18, RANGING_TIME_STRUCT = 0x1A, SLOTS_PER_RR = 0x1B, TX_ADAPTIVE_PAYLOAD_POWER = 0x1C, @@ -145,6 +228,7 @@ enum AppConfigTlvType : 8 { RNG_DATA_NTF_AOA_BOUND = 0x1D, RESPONDER_SLOT_INDEX = 0x1E, PRF_MODE = 0x1F, + CAP_SIZE_RANGE = 0x20, SCHEDULED_MODE = 0x22, KEY_ROTATION = 0x23, KEY_ROTATION_RATE = 0x24, @@ -162,25 +246,41 @@ 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, + UL_TDOA_DEVICE_ID = 0x38, + UL_TDOA_TX_TIMESTAMP = 0x39, + MIN_FRAMES_PER_RR = 0x3A, + MTU_SIZE = 0x3B, + INTER_FRAME_INTERVAL = 0x3C, + RFU_APP_CFG_TLV_TYPE_RANGE_1 = 0x3D..0x44, SESSION_KEY = 0x45, SUBSESSION_KEY = 0x46, - - // CCC specific - CCC_HOP_MODE_KEY = 0xA0, - CCC_UWB_TIME0 = 0xA1, - CCC_RANGING_PROTOCOL_VER = 0xA3, - CCC_UWB_CONFIG_ID = 0xA4, - CCC_PULSESHAPE_COMBO = 0xA5, - CCC_URSK_TTL = 0xA6, - - // Interleaving ratio if AOA_RESULT_REQ is set to 0xF0. - NB_OF_RANGE_MEASUREMENTS = 0xE3, - NB_OF_AZIMUTH_MEASUREMENTS = 0xE4, - NB_OF_ELEVATION_MEASUREMENTS = 0xE5, - - ENABLE_DIAGNOSTICS = 0xE8, - DIAGRAMS_FRAME_REPORTS_FIELDS = 0xE9, + RFU_APP_CFG_TLV_TYPE_RANGE_2 = 0x47..0x9F, + + VENDOR_SPECIFIC_APP_CFG_TLV_TYPE_RANGE_1 = 0xA0..0xDF { + // CCC specific + CCC_HOP_MODE_KEY = 0xA0, + CCC_UWB_TIME0 = 0xA1, + CCC_RANGING_PROTOCOL_VER = 0xA3, + CCC_UWB_CONFIG_ID = 0xA4, + CCC_PULSESHAPE_COMBO = 0xA5, + CCC_URSK_TTL = 0xA6, + CCC_LAST_INDEX_USED = 0xA8, + }, + + // Reserved for extension IDs. + RFU_APP_CFG_TLV_TYPE_RANGE_3 = 0xE0..0xE2, + + VENDOR_SPECIFIC_APP_CFG_TLV_TYPE_RANGE_2 = 0xE3..0xFF { + // Interleaving ratio if AOA_RESULT_REQ is set to 0xF0. + NB_OF_RANGE_MEASUREMENTS = 0xE3, + NB_OF_AZIMUTH_MEASUREMENTS = 0xE4, + NB_OF_ELEVATION_MEASUREMENTS = 0xE5, + ENABLE_DIAGNOSTICS = 0xE8, + DIAGRAMS_FRAME_REPORTS_FIELDS = 0xE9, + }, } enum FrameReportTlvType : 8 { @@ -208,23 +308,36 @@ enum CapTlvType : 8 { SUPPORTED_HPRF_PARAMETER_SETS = 0x0F, SUPPORTED_AOA = 0x10, SUPPORTED_EXTENDED_MAC_ADDRESS = 0x11, - SUPPORTED_AOA_RESULT_REQ_ANTENNA_INTERLEAVING = 0xE3, - SUPPORTED_MIN_RANGING_INTERVAL_MS = 0xE4, - SUPPORTED_RANGE_DATA_NTF_CONFIG = 0xE5, - SUPPORTED_RSSI_REPORTING = 0xE6, - SUPPORTED_DIAGNOSTICS = 0xE7, - - // CCC specific - CCC_SUPPORTED_CHAPS_PER_SLOT = 0xA0, - CCC_SUPPORTED_SYNC_CODES = 0xA1, - CCC_SUPPORTED_HOPPING_CONFIG_MODES_AND_SEQUENCES = 0xA2, - CCC_SUPPORTED_CHANNELS = 0xA3, - CCC_SUPPORTED_VERSIONS = 0xA4, - CCC_SUPPORTED_UWB_CONFIGS = 0xA5, - CCC_SUPPORTED_PULSE_SHAPE_COMBOS = 0xA6, - CCC_SUPPORTED_RAN_MULTIPLIER = 0xA7, + SUPPORTED_MAX_MESSAGE_SIZE = 0x12, + SUPPORTED_MAX_DATA_PACKET_PAYLOAD_SIZE = 0x13, + RFU_CAP_TLV_TYPE_RANGE_1 = 0x14..0x9F, + + VENDOR_SPECIFIC_CAP_TLV_TYPE_RANGE_1 = 0xA0..0xBF { + // CCC specific + CCC_SUPPORTED_CHAPS_PER_SLOT = 0xA0, + CCC_SUPPORTED_SYNC_CODES = 0xA1, + CCC_SUPPORTED_HOPPING_CONFIG_MODES_AND_SEQUENCES = 0xA2, + CCC_SUPPORTED_CHANNELS = 0xA3, + CCC_SUPPORTED_VERSIONS = 0xA4, + CCC_SUPPORTED_UWB_CONFIGS = 0xA5, + CCC_SUPPORTED_PULSE_SHAPE_COMBOS = 0xA6, + CCC_SUPPORTED_RAN_MULTIPLIER = 0xA7, + CCC_SUPPORTED_MAX_RANGING_SESSION_NUMBER = 0xA8, + }, SUPPORTED_POWER_STATS = 0xC0, + VENDOR_SPECIFIC_CAP_TLV_TYPE_RANGE_2 = 0xC1..0xDF, + RFU_CAP_TLV_TYPE_RANGE_2 = 0xE0..0xE2, + + VENDOR_SPECIFIC_CAP_TLV_TYPE_RANGE_3 = 0xE3..0xFF { + SUPPORTED_AOA_RESULT_REQ_ANTENNA_INTERLEAVING = 0xE3, + SUPPORTED_MIN_RANGING_INTERVAL_MS = 0xE4, + SUPPORTED_RANGE_DATA_NTF_CONFIG = 0xE5, + SUPPORTED_RSSI_REPORTING = 0xE6, + SUPPORTED_DIAGNOSTICS = 0xE7, + SUPPORTED_MIN_SLOT_DURATION_RSTU = 0xE8, + SUPPORTED_MAX_RANGING_SESSION_NUMBER = 0xE9, + }, } @@ -255,12 +368,55 @@ enum ReasonCode : 8 { STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS = 0x00, MAX_RANGING_ROUND_RETRY_COUNT_REACHED = 0x01, MAX_NUMBER_OF_MEASUREMENTS_REACHED = 0x02, + SESSION_SUSPENDED_DUE_TO_INBAND_SIGNAL = 0x03, + SESSION_RESUMED_DUE_TO_INBAND_SIGNAL = 0x04, + SESSION_STOPPED_DUE_TO_INBAND_SIGNAL = 0x05, + RFU_REASON_CODE_RANGE_1 = 0x06..0x1C, + ERROR_INVALID_UL_TDOA_RANDOM_WINDOW = 0x1D, + ERROR_MIN_RFRAMES_PER_RR_NOT_SUPPORTED = 0x1E, + ERROR_TX_DELAY_NOT_SUPPORTED = 0x1F, ERROR_SLOT_LENGTH_NOT_SUPPORTED = 0x20, ERROR_INSUFFICIENT_SLOTS_PER_RR = 0x21, ERROR_MAC_ADDRESS_MODE_NOT_SUPPORTED = 0x22, - ERROR_INVALID_RANGING_INTERVAL = 0x23, + ERROR_INVALID_RANGING_DURATION = 0x23, ERROR_INVALID_STS_CONFIG = 0x24, ERROR_INVALID_RFRAME_CONFIG = 0x25, + ERROR_HUS_NOT_ENOUGH_SLOTS = 0x26, + ERROR_HUS_CFP_PHASE_TOO_SHORT = 0x27, + ERROR_HUS_CAP_PHASE_TOO_SHORT = 0x28, + ERROR_HUS_OTHERS = 0x29, + ERROR_STATUS_SESSION_KEY_NOT_FOUND = 0x2A, + ERROR_STATUS_SUB_SESSION_KEY_NOT_FOUND = 0x2B, + ERROR_INVALID_PREAMBLE_CODE_INDEX = 0x2C, + ERROR_INVALID_SFD_ID = 0x2D, + ERROR_INVALID_PSDU_DATA_RATE = 0x2E, + ERROR_INVALID_PHR_DATA_RATE = 0x2F, + ERROR_INVALID_PREAMBLE_DURATION = 0x30, + ERROR_INVALID_STS_LENGTH = 0x31, + ERROR_INVALID_NUM_OF_STS_SEGMENTS = 0x32, + ERROR_INVALID_NUM_OF_CONTROLEES = 0x33, + ERROR_MAX_RANGING_REPLY_TIME_EXCEEDED = 0x34, + ERROR_INVALID_DST_ADDRESS_LIST = 0x35, + ERROR_INVALID_OR_NOT_FOUND_SUB_SESSION_ID = 0x36, + ERROR_INVALID_RESULT_REPORT_CONFIG = 0x37, + ERROR_INVALID_RANGING_ROUND_CONTROL_CONFIG = 0x38, + ERROR_INVALID_RANGING_ROUND_USAGE = 0x39, + ERROR_INVALID_MULTI_NODE_MODE = 0x3A, + ERROR_RDS_FETCH_FAILURE = 0x3B, + ERROR_REF_UWB_SESSION_DOES_NOT_EXIST = 0x3C, + ERROR_REF_UWB_SESSION_RANGING_DURATION_MISMATCH = 0x3D, + ERROR_REF_UWB_SESSION_INVALID_OFFSET_TIME = 0x3E, + ERROR_REF_UWB_SESSION_LOST = 0x3F, + RFU_REASON_CODE_RANGE_2 = 0x40..0x7F { + ERROR_DT_ANCHOR_RANGING_ROUNDS_NOT_CONFIGURED = 0x40, + ERROR_DT_TAG_RANGING_ROUNDS_NOT_CONFIGURED = 0x41, + }, + VENDOR_SPECIFIC_REASON_CODE_RANGE_1 = 0x80..0xFE { + ERROR_INVALID_CHANNEL_WITH_AOA = 0x80, + ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT = 0x81, + }, + // For internal usage, we will use 0xFF as default. + VENDOR_SPECIFIC_REASON_CODE_2 = 0xFF, } enum MulticastUpdateStatusCode : 8 { @@ -285,17 +441,33 @@ enum SessionType: 8 { } enum MessageType: 3 { + DATA = 0x00, COMMAND = 0x01, RESPONSE = 0x02, NOTIFICATION = 0x03, + RESERVED_FOR_TESTING_1 = 0x04, + RESERVED_FOR_TESTING_2 = 0x05, } // UCI packet description in compliance with the FIRA UCI spec. // Only this packet should be sent/expected across the HAL interface. -packet UciPacketHal; { - group_id: GroupId, +packet UciPacketHal { + group_id_or_data_packet_format: GroupIdOrDataPacketFormat, packet_boundary_flag: PacketBoundaryFlag, message_type: MessageType, + _body_ +} + +// The UciDataPacketHal must be declared before the UciControlPacketHal for correct parsing to +// happen. This is required as the Data packet specifies a value for the 'message_type' constraint, +// while the Control packet does not (as it encompasses multiple 'message_type' values). +packet UciDataPacketHal: UciPacketHal (message_type = DATA) { + _reserved_: 8, + _size_(_payload_): 16, + _payload_, +} + +packet UciControlPacketHal: UciPacketHal { opcode: 6, _reserved_: 2, _reserved_: 8, @@ -303,33 +475,76 @@ packet UciPacketHal; { _payload_, } -// This packet definition is used throughout the stack that holds a complete (i.e unfragmented) UCI -// command/response/notification. -// UciPacket needs to be converted to one or more UciPacketHal fragments before sending to the HAL. -// One or more UciPacketHal fragments needs to be converted to UciPacket when receiving from the HAL. +// This control packet definition is used throughout the stack that holds a complete +// (i.e unfragmented) UCI command/response/notification. +// +// UciControlPacket needs to be converted to one or more UciControlPacketHal fragments before +// sending to the HAL. One or more UciControlPacketHal fragments needs to be converted to +// UciControlPacket when receiving from the HAL. +// // TODO(b/202760099): Handle fragmentation more cleanly in the PDL. -packet UciPacket { +packet UciControlPacket { group_id: GroupId, - // This field is different from |UciPacketHal| to provide a placeholder for PBF flag. + // This field is different from |UciControlPacketHal| to provide a placeholder for PBF flag. _reserved_: 1, message_type: MessageType, opcode: 6, _reserved_: 2, _reserved_: 8, - // This field is different from |UciPacketHal| to allow holding large unfragmented packet. + // This field is different from |UciControlPacketHal| to allow holding a large unfragmented + // packet. + _size_(_payload_): 32, + _payload_, +} + +// This data packet definition is used throughout the stack that holds a complete +// (i.e unfragmented) UCI data packet. +// +// UciDataPacket needs to be converted to one or more UciDataPacketHal fragments before sending to +// the HAL. One or more UciDataPacketHal fragments needs to be converted to UciDataPacket when +// receiving from the HAL. +// +// TODO(b/202760099): Handle fragmentation more cleanly in the PDL. +packet UciDataPacket { + data_packet_format: DataPacketFormat, + // This field is different from |UciDataPacketHal| to provide a placeholder for PBF flag. + _reserved_: 1, + message_type: MessageType, + _reserved_: 8, + // This field is different from |UciDataPacketHal| to allow holding large unfragmented packet. _size_(_payload_): 32, _payload_, } -packet UciCommand : UciPacket (message_type = COMMAND) { +packet UciDataSnd : UciDataPacket (data_packet_format = DATA_SND, message_type = DATA) { + session_id: 32, + dest_mac_address: 64, + dest_fira_component: FiraComponent, + uci_sequence_number: 8, + _size_(data): 16, + data: 8[] +} + +packet UciDataRcv : UciDataPacket (data_packet_format = DATA_RCV, message_type = DATA) { + session_id: 32, + status: DataRcvStatusCode, + uci_sequence_number: 32, + source_mac_address: 64, + source_fira_component: FiraComponent, + dest_fira_component: FiraComponent, + _size_(data): 16, + data: 8[] +} + +packet UciCommand : UciControlPacket (message_type = COMMAND) { _payload_, } -packet UciResponse : UciPacket (message_type = RESPONSE) { +packet UciResponse : UciControlPacket (message_type = RESPONSE) { _payload_, } -packet UciNotification : UciPacket (message_type = NOTIFICATION) { +packet UciNotification : UciControlPacket (message_type = NOTIFICATION) { _payload_, } @@ -345,27 +560,27 @@ packet CoreNotification : UciNotification (group_id = CORE) { _body_, } -packet SessionCommand : UciCommand (group_id = SESSION_CONFIG) { +packet SessionConfigCommand : UciCommand (group_id = SESSION_CONFIG) { _body_, } -packet SessionResponse : UciResponse (group_id = SESSION_CONFIG) { +packet SessionConfigResponse : UciResponse (group_id = SESSION_CONFIG) { _body_, } -packet SessionNotification : UciNotification (group_id = SESSION_CONFIG) { +packet SessionConfigNotification : UciNotification (group_id = SESSION_CONFIG) { _body_, } -packet RangingCommand : UciCommand (group_id = RANGING_SESSION_CONTROL) { +packet SessionControlCommand : UciCommand (group_id = SESSION_CONTROL) { _body_, } -packet RangingResponse : UciResponse (group_id = RANGING_SESSION_CONTROL) { +packet SessionControlResponse : UciResponse (group_id = SESSION_CONTROL) { _body_, } -packet RangingNotification : UciNotification (group_id = RANGING_SESSION_CONTROL) { +packet SessionControlNotification : UciNotification (group_id = SESSION_CONTROL) { _body_, } @@ -479,6 +694,7 @@ packet SetConfigRsp : CoreResponse (opcode = 0x4) { //CORE_SET_CONFIG test SetConfigRsp { "\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 @@ -508,7 +724,7 @@ test GenericError { "\x60\x07\x00\x01\x00\x00\x00\x01", } -packet SessionInitCmd : SessionCommand (opcode = 0x0) { //SESSION_INIT +packet SessionInitCmd : SessionConfigCommand (opcode = 0x0) { //SESSION_INIT session_id: 32, session_type: SessionType, } @@ -517,7 +733,7 @@ test SessionInitCmd { "\x21\x00\x00\x05\x00\x00\x00\x01\x02\x03\x04\x01", } -packet SessionInitRsp : SessionResponse (opcode = 0x0) { //SESSION_INIT +packet SessionInitRsp : SessionConfigResponse (opcode = 0x0) { //SESSION_INIT status: StatusCode, } @@ -525,7 +741,7 @@ test SessionInitRsp { "\x41\x00\x00\x01\x00\x00\x00\x11", } -packet SessionDeinitCmd : SessionCommand (opcode = 0x1) { //SESSION_DEINIT +packet SessionDeinitCmd : SessionConfigCommand (opcode = 0x1) { //SESSION_DEINIT session_id: 32, } @@ -533,7 +749,7 @@ test SessionDeinitCmd { "\x21\x01\x00\x04\x00\x00\x00\x01\x02\x03\x04", } -packet SessionDeinitRsp : SessionResponse (opcode = 0x1) { //SESSION_DEINIT +packet SessionDeinitRsp : SessionConfigResponse (opcode = 0x1) { //SESSION_DEINIT status: StatusCode, } @@ -541,14 +757,17 @@ test SessionDeinitRsp { "\x41\x01\x00\x01\x00\x00\x00\x00", } -packet SessionStatusNtf : SessionNotification (opcode = 0x2) { //SESSION_STATUS_NTF +packet SessionStatusNtf : SessionConfigNotification (opcode = 0x2) { //SESSION_STATUS_NTF session_id: 32, session_state: SessionState, - reason_code: ReasonCode, + // TODO(b/272775225): Switch back to the enum type ReasonCode, once PDL supports defining a + // range inside an enum (for the vendor-specific space), in b/267339120. + reason_code: 8, } test SessionStatusNtf { "\x61\x02\x00\x06\x00\x00\x00\x01\x02\x03\x04\x02\x21", + "\x61\x02\x00\x06\x00\x00\x00\x01\x02\x03\x04\x01\x82", // Vendor Specific Reason Code } struct AppConfigTlv { @@ -557,7 +776,7 @@ struct AppConfigTlv { v: 8[], } -packet SessionSetAppConfigCmd : SessionCommand (opcode = 0x3) { //SESSION_SET_APP_CONFIG +packet SessionSetAppConfigCmd : SessionConfigCommand (opcode = 0x3) { //SESSION_SET_APP_CONFIG session_id: 32, _count_(tlvs): 8, tlvs: AppConfigTlv[] @@ -572,7 +791,7 @@ struct AppConfigStatus { status: StatusCode, } -packet SessionSetAppConfigRsp : SessionResponse (opcode = 0x3) { //SESSION_SET_APP_CONFIG +packet SessionSetAppConfigRsp : SessionConfigResponse (opcode = 0x3) { //SESSION_SET_APP_CONFIG status: StatusCode, _count_(cfg_status): 8, cfg_status: AppConfigStatus[], @@ -582,7 +801,7 @@ test SessionSetAppConfigRsp { "\x41\x03\x00\x04\x00\x00\x00\x01\x01\x01\x00", } -packet SessionGetAppConfigCmd : SessionCommand (opcode = 0x4) { //SESSION_GET_APP_CONFIG +packet SessionGetAppConfigCmd : SessionConfigCommand (opcode = 0x4) { //SESSION_GET_APP_CONFIG session_id: 32, _count_(app_cfg): 8, app_cfg: 8[], // AppConfigTlvType (Infra does not allow array of enums) @@ -592,7 +811,7 @@ test SessionGetAppConfigCmd { "\x21\x04\x00\x05\x00\x00\x00\x01\x02\x03\x04\x00", } -packet SessionGetAppConfigRsp : SessionResponse (opcode = 0x4) { //SESSION_GET_APP_CONFIG +packet SessionGetAppConfigRsp : SessionConfigResponse (opcode = 0x4) { //SESSION_GET_APP_CONFIG status: StatusCode, _count_(tlvs): 8, tlvs: AppConfigTlv[], @@ -602,14 +821,14 @@ test SessionGetAppConfigRsp { "\x41\x04\x00\x02\x00\x00\x00\x01\x00", } -packet SessionGetCountCmd : SessionCommand (opcode = 0x5) { //SESSION_GET_COUNT +packet SessionGetCountCmd : SessionConfigCommand (opcode = 0x5) { //SESSION_GET_COUNT } test SessionGetCountCmd { "\x21\x05\x00\x00\x00\x00\x00", } -packet SessionGetCountRsp : SessionResponse (opcode = 0x5) { //SESSION_GET_COUNT +packet SessionGetCountRsp : SessionConfigResponse (opcode = 0x5) { //SESSION_GET_COUNT status: StatusCode, session_count: 8, } @@ -618,7 +837,7 @@ test SessionGetCountRsp { "\x41\x05\x00\x02\x00\x00\x00\x00\x01", } -packet SessionGetStateCmd : SessionCommand (opcode = 0x6) { //SESSION_GET_STATE +packet SessionGetStateCmd : SessionConfigCommand (opcode = 0x6) { //SESSION_GET_STATE session_id: 32, } @@ -626,7 +845,7 @@ test SessionGetStateCmd { "\x21\x06\x00\x04\x00\x00\x00\x00\x01\x02\x03", } -packet SessionGetStateRsp : SessionResponse (opcode = 0x6) { //SESSION_GET_STATE +packet SessionGetStateRsp : SessionConfigResponse (opcode = 0x6) { //SESSION_GET_STATE status: StatusCode, session_state: SessionState, } @@ -635,43 +854,51 @@ test SessionGetStateRsp { "\x41\x06\x00\x02\x00\x00\x00\x00\x01", } -enum MessageControl: 8 { - SUB_SESSION_KEY_NOT_CONFIGURED = 0x0, - SHORT_SUB_SESSION_KEY_CONFIGURED = 0x8, - LONG_SUB_SESSION_KEY_CONFIGURED = 0x9, +packet SessionUpdateDtTagRangingRoundsCmd : SessionConfigCommand (opcode = 0x9) { //SESSION_UPDATE_ACTIVE_ROUNDS_DT_TAG + session_id: 32, + _count_(ranging_round_indexes): 8, + ranging_round_indexes: 8[], } -struct Controlee { - short_address: 16, - subsession_id: 32, +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, + _count_(ranging_round_indexes): 8, + ranging_round_indexes: 8[], +} + +test SessionUpdateDtTagRangingRoundsRsp { + "\x41\x09\x00\x03\x00\x00\x00\x01\x01\x01", } -struct Controlee_V2_0_0_Byte_Version { +struct Controlee { short_address: 16, subsession_id: 32, - message_control: MessageControl, } struct Controlee_V2_0_16_Byte_Version { short_address: 16, subsession_id: 32, - message_control: MessageControl, subsession_key: 8[16], } struct Controlee_V2_0_32_Byte_Version { short_address: 16, subsession_id: 32, - message_control: MessageControl, subsession_key: 8[32], } enum UpdateMulticastListAction: 8 { - ADD_CONTROLEE = 0, - REMOVE_CONTROLEE = 1, + ADD_CONTROLEE = 0x00, + REMOVE_CONTROLEE = 0x01, + ADD_CONTROLEE_WITH_SHORT_SUB_SESSION_KEY = 0x02, + ADD_CONTROLEE_WITH_LONG_SUB_SESSION_KEY = 0x03, } -packet SessionUpdateControllerMulticastListCmd : SessionCommand (opcode = 0x7) { //SESSION_UPDATE_CONTROLLER_MULTICAST_LIST +packet SessionUpdateControllerMulticastListCmd : SessionConfigCommand (opcode = 0x7) { //SESSION_UPDATE_CONTROLLER_MULTICAST_LIST session_id: 32, action: UpdateMulticastListAction, _payload_, @@ -682,11 +909,6 @@ struct SessionUpdateControllerMulticastListCmdPayload { controlees: Controlee[], } -struct SessionUpdateControllerMulticastListCmd_2_0_0_Byte_Payload { - _count_(controlees): 8, - controlees: Controlee_V2_0_0_Byte_Version[], -} - struct SessionUpdateControllerMulticastListCmd_2_0_16_Byte_Payload { _count_(controlees): 8, controlees: Controlee_V2_0_16_Byte_Version[], @@ -697,7 +919,7 @@ struct SessionUpdateControllerMulticastListCmd_2_0_32_Byte_Payload { controlees: Controlee_V2_0_32_Byte_Version[], } -packet SessionUpdateControllerMulticastListRsp : SessionResponse (opcode = 0x7) { //SESSION_UPDATE_CONTROLLER_MULTICAST_LIST +packet SessionUpdateControllerMulticastListRsp : SessionConfigResponse (opcode = 0x7) { //SESSION_UPDATE_CONTROLLER_MULTICAST_LIST status: StatusCode, } @@ -711,7 +933,7 @@ struct ControleeStatus { status: MulticastUpdateStatusCode, } -packet SessionUpdateControllerMulticastListNtf : SessionNotification (opcode = 0x7) { //SESSION_UPDATE_CONTROLLER_MULTICAST_LIST +packet SessionUpdateControllerMulticastListNtf : SessionConfigNotification (opcode = 0x7) { //SESSION_UPDATE_CONTROLLER_MULTICAST_LIST session_id: 32, remaining_multicast_list_size: 8, _count_(controlee_status): 8, @@ -722,20 +944,56 @@ 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_id: 32, + credit_availability: CreditAvailability, +} -packet RangeStartCmd : RangingCommand (opcode = 0x0) { //RANGE_START +test DataCreditNtf { + "\x62\x04\x00\x05\x00\x00\x00\x00\x00\x00\x01\x01", +} + +packet DataTransferStatusNtf : SessionControlNotification (opcode = 0x05) { // SESSION_DATA_TRANSFER_STATUS_NTF + session_id: 32, + uci_sequence_number: 8, + status: DataTransferNtfStatusCode, + // TODO(b/269779288): Add the tx_count field for implementing the DATA_REPETITION added in CR490. +} + +test DataTransferStatusNtf { + "\x62\x05\x00\x06\x00\x00\x00\x00\x00\x00\x01\x01\x00", +} + +packet SessionQueryMaxDataSizeCmd : SessionConfigCommand (opcode = 0xB) { //QUERY_MAX_DATA_SIZE + session_id: 32, +} + +test SessionQueryMaxDataSizeCmd { + "\x21\x0B\x00\x04\x00\x00\x00\x00", +} + +packet SessionQueryMaxDataSizeRsp : SessionConfigResponse (opcode = 0xB) { //QUER_MAX_DATA_SIZE + session_id: 32, + max_data_size: 16, +} + +test SessionQueryMaxDataSizeRsp { + "\x41\x0B\x00\x06\x00\x00\x00\x00\x0E7\0x07", +} + +packet SessionStartCmd : SessionControlCommand (opcode = 0x0) { //RANGE_START session_id: 32, } -test RangeStartCmd { +test SessionStartCmd { "\x22\x00\x00\x04\x00\x00\x00\x00\x01\x02\x03", } -packet RangeStartRsp : RangingResponse (opcode = 0x0) { //RANGE_START +packet SessionStartRsp : SessionControlResponse (opcode = 0x0) { //RANGE_START status: StatusCode, } -test RangeStartRsp { +test SessionStartRsp { "\x42\x00\x00\x01\x00\x00\x00\x00", } @@ -754,7 +1012,11 @@ struct ShortAddressTwoWayRangingMeasurement { aoa_destination_elevation_fom: 8, slot_index: 8, rssi: 8, - _reserved_: 88, + // b/272301550: The pdl compiler cannot handle individual fields + // larger than 64 bit. The work around is to split the 88 bit + // field into two. + _reserved_: 64, + _reserved_: 24, } struct ExtendedAddressTwoWayRangingMeasurement { @@ -775,12 +1037,38 @@ struct ExtendedAddressTwoWayRangingMeasurement { _reserved_: 40, } +struct ShortAddressOwrAoaRangingMeasurement { + mac_address: 16, + status: OwrAoaStatusCode, + nlos: 8, + frame_sequence_number: 8, + block_index: 16, + aoa_azimuth: 16, + aoa_azimuth_fom: 8, + aoa_elevation: 16, + aoa_elevation_fom: 8, +} + +struct ExtendedAddressOwrAoaRangingMeasurement { + mac_address: 64, + status: OwrAoaStatusCode, + nlos: 8, + frame_sequence_number: 8, + block_index: 16, + aoa_azimuth: 16, + aoa_azimuth_fom: 8, + aoa_elevation: 16, + aoa_elevation_fom: 8, +} + enum RangingMeasurementType : 8 { ONE_WAY = 0x0, TWO_WAY = 0x1, + DL_TDOA = 0x02, + OWR_AOA = 0x03, } -packet RangeDataNtf : RangingNotification (opcode = 0x0) { //RANGE_START +packet SessionInfoNtf : SessionControlNotification (opcode = 0x0) { // SESSION_INFO sequence_number: 32, session_id: 32, rcr_indicator: 8, @@ -792,54 +1080,96 @@ packet RangeDataNtf : RangingNotification (opcode = 0x0) { //RANGE_START _body_, } -packet ShortMacTwoWayRangeDataNtf : RangeDataNtf (ranging_measurement_type = TWO_WAY, mac_address_indicator = SHORT_ADDRESS) { +packet ShortMacTwoWaySessionInfoNtf : SessionInfoNtf (ranging_measurement_type = TWO_WAY, mac_address_indicator = SHORT_ADDRESS) { _count_(two_way_ranging_measurements) : 8, two_way_ranging_measurements : ShortAddressTwoWayRangingMeasurement[], + vendor_data: 8[], } -test ShortMacTwoWayRangeDataNtf { +test ShortMacTwoWaySessionInfoNtf { "\x62\x00\x00\x19\x00\x00\x00\x00\x02\x03\x04\x05\x06\x07\x08\x00\x0a\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", } -packet ExtendedMacTwoWayRangeDataNtf : RangeDataNtf (ranging_measurement_type = TWO_WAY, mac_address_indicator = EXTENDED_ADDRESS) { +packet ExtendedMacTwoWaySessionInfoNtf : SessionInfoNtf (ranging_measurement_type = TWO_WAY, mac_address_indicator = EXTENDED_ADDRESS) { _count_(two_way_ranging_measurements) : 8, two_way_ranging_measurements : ExtendedAddressTwoWayRangingMeasurement[], + vendor_data: 8[], } -test ExtendedMacTwoWayRangeDataNtf { +test ExtendedMacTwoWaySessionInfoNtf { "\x62\x00\x00\x19\x00\x00\x00\x00\x02\x03\x04\x05\x06\x07\x08\x00\x0a\x01\x01\x01\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00", } -packet RangeStopCmd : RangingCommand (opcode = 0x1) { //RANGE_STOP +packet ShortMacDlTDoASessionInfoNtf : SessionInfoNtf (ranging_measurement_type = DL_TDOA, mac_address_indicator = SHORT_ADDRESS) { + no_of_ranging_measurements : 8, + dl_tdoa_measurements : 8[], +} + +test ShortMacDlTDoASessionInfoNtf { + "\x62\x00\x00\x19\x00\x00\x00\x00\x02\x03\x04\x05\x06\x07\x08\x00\x0a\x02\x01\x01\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", +} + +packet ExtendedMacDlTDoASessionInfoNtf : SessionInfoNtf (ranging_measurement_type = DL_TDOA, mac_address_indicator = EXTENDED_ADDRESS) { + no_of_ranging_measurements : 8, + dl_tdoa_measurements : 8[], +} + +test ExtendedMacDlTDoASessionInfoNtf { + "\x62\x00\x00\x19\x00\x00\x00\x00\x02\x03\x04\x05\x06\x07\x08\x00\x0a\x01\x01\x01\x02\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00", +} + +packet ShortMacOwrAoaSessionInfoNtf : SessionInfoNtf (ranging_measurement_type = OWR_AOA, mac_address_indicator = SHORT_ADDRESS) { + _count_(owr_aoa_ranging_measurements) : 8, + owr_aoa_ranging_measurements : ShortAddressOwrAoaRangingMeasurement[], + vendor_data: 8[], +} + +test ShortMacOwrAoaSessionInfoNtf { + "\x62\x00\x00\x19\x00\x00\x00\x00\x02\x03\x04\x05\x06\x07\x08\x00\x0a\x01\x01\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + "\x62\x00\x00\x26\x00\x00\x00\x00\x02\x03\x04\x05\x06\x07\x08\x00\x0a\x01\x01\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xbb\x00\x00\x01\x01\x00\x03\x04\x60\x05\x06\x50", +} + +packet ExtendedMacOwrAoaSessionInfoNtf : SessionInfoNtf (ranging_measurement_type = OWR_AOA, mac_address_indicator = EXTENDED_ADDRESS) { + _count_(owr_aoa_ranging_measurements) : 8, + owr_aoa_ranging_measurements : ExtendedAddressOwrAoaRangingMeasurement[], + vendor_data: 8[], +} + +test ExtendedMacOwrAoaSessionInfoNtf { + "\x62\x00\x00\x19\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\x00", + "\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 session_id: 32, } -test RangeStopCmd { +test SessionStopCmd { "\x22\x01\x00\x04\x00\x00\x00\x00\x02\x03\x04", } -packet RangeStopRsp : RangingResponse (opcode = 0x1) { //RANGE_STOP +packet SessionStopRsp : SessionControlResponse (opcode = 0x1) { // SESSION_STOP status: StatusCode, } -test RangeStopRsp { +test SessionStopRsp { "\x42\x01\x00\x01\x00\x00\x00\x00", } -packet RangeGetRangingCountCmd : RangingCommand (opcode = 0x3) { //RANGE_GET_RANGING_COUNT +packet SessionGetRangingCountCmd : SessionControlCommand (opcode = 0x3) { // SESSION_GET_RANGING_COUNT session_id: 32, } -test RangeGetRangingCountCmd { +test SessionGetRangingCountCmd { "\x22\x03\x00\x04\x00\x00\x00\x00\x02\x03\x04", } -packet RangeGetRangingCountRsp : RangingResponse (opcode = 0x3) { //RANGE_GET_RANGING_COUNT +packet SessionGetRangingCountRsp : SessionControlResponse (opcode = 0x3) { // SESSION_GET_RANGING_COUNT status: StatusCode, count: 32, } -test RangeGetRangingCountRsp { +test SessionGetRangingCountRsp { "\x42\x03\x00\x05\x00\x00\x00\x00\x02\x03\x04\x05", } @@ -957,26 +1287,6 @@ test AndroidRangeDiagnosticsNtf { "\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 (group_id = VENDOR_RESERVED_9) { - _payload_, -} - -packet UciVendor_A_Command : UciCommand (group_id = VENDOR_RESERVED_A) { - _payload_, -} - -packet UciVendor_B_Command : UciCommand (group_id = VENDOR_RESERVED_B) { - _payload_, -} - -packet UciVendor_E_Command : UciCommand (group_id = VENDOR_RESERVED_E) { - _payload_, -} - -packet UciVendor_F_Command : UciCommand (group_id = VENDOR_RESERVED_F) { - _payload_, -} - packet UciVendor_9_Response : UciResponse (group_id = VENDOR_RESERVED_9) { _payload_, } diff --git a/src/uci/include/uci_defs.h b/src/uci/include/uci_defs.h index 9237a75..8579e63 100755 --- a/src/uci/include/uci_defs.h +++ b/src/uci/include/uci_defs.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2021 The Android Open Source Project * - * Copyright 2021 NXP. + * Copyright 2021-2022 NXP. * * Licensed under the Apache License, Version 2.0 (the "License"); * You may not use this file except in compliance with the License. @@ -27,6 +27,9 @@ #include <stdint.h> +#define UCI_PAYLOAD_SUPPORT 1 +#define UCI_MAX_PAYLOAD_SIZE 4096 + /* Define the message header size for all UCI Commands and Notifications. */ #define UCI_MSG_HDR_SIZE 0x04 /* per UCI spec */ @@ -37,9 +40,10 @@ #define UCI_PAYLOAD_SUPPORT 1 #define MAX_UCI_DATA_PKT_SIZE 4096 -#define LENGTH_INDCATOR_BIT 0x80 // 1000 0000 #define UCI_LENGTH_SHIFT 8 #define UCI_RESPONSE_STATUS_OFFSET 0x04 +#define UCI_RESPONSE_PAYLOAD_OFFSET 0x05 +#define UCI_MAX_FRAGMENT_BUFF_SIZE 4200 /* UCI Command and Notification Format: * 4 byte message header: @@ -51,6 +55,7 @@ /* MT: Message Type (byte 0) */ #define UCI_MT_MASK 0xE0 #define UCI_MT_SHIFT 0x05 +#define UCI_MT_DATA 0x00 /* (UCI_MT_DATA << UCI_MT_SHIFT) = 0x00 */ #define UCI_MT_CMD 0x01 /* (UCI_MT_CMD << UCI_MT_SHIFT) = 0x20 */ #define UCI_MT_RSP 0x02 /* (UCI_MT_RSP << UCI_MT_SHIFT) = 0x40 */ #define UCI_MT_NTF 0x03 /* (UCI_MT_NTF << UCI_MT_SHIFT) = 0x60 */ @@ -68,13 +73,15 @@ #define UCI_PBF_NO_OR_LAST 0x00 /* not fragmented or last fragment */ #define UCI_PBF_ST_CONT 0x10 /* start or continuing fragment */ +#define DATA_MESSAGE_SND 0x01 + /* GID: Group Identifier (byte 0) */ #define UCI_GID_MASK 0x0F #define UCI_GID_SHIFT 0x00 #define UCI_GID_CORE 0x00 /* 0000b UCI Core group */ #define UCI_GID_SESSION_MANAGE 0x01 /* 0001b Session Config commands */ #define UCI_GID_RANGE_MANAGE 0x02 /* 0010b Range Management group */ -#define UCI_GID_ANDROID 0x0E /* 1110b Android vendor group */ +#define UCI_GID_ANDROID 0x0C /* 1110b Android vendor group */ #define UCI_GID_TEST 0x0D /* 1101b RF Test Gropup */ /* Vendor specific group Identifier */ @@ -120,6 +127,17 @@ ((UWB_HDR*)phUwb_GKI_getbuf((uint16_t)(UWB_HDR_SIZE + UCI_MSG_HDR_SIZE + \ UCI_MSG_OFFSET_SIZE + (paramlen)))) +/* UCI Data Format: + * byte 0: MT(0) PBF DPF + * byte 1: RFU + * byte 2: Data Length + * byte 3: Data Length */ +#define UCI_DATA_PBLD_HDR(p, pbf, len) \ + *(p)++ = (uint8_t)(((pbf) << UCI_PBF_SHIFT) | DATA_MESSAGE_SND); \ + *(p)++ = 0x00; \ + *(p)++ = (uint8_t)(len); \ + *(p)++ = (uint8_t)(((len) >> UCI_LENGTH_SHIFT)); + /********************************************** * UCI Core Group-0: Opcodes and size of commands **********************************************/ @@ -136,6 +154,9 @@ #define UCI_MSG_CORE_DEVICE_INFO_CMD_SIZE 0x00 #define UCI_MSG_CORE_GET_CAPS_INFO_CMD_SIZE 0x00 +#define UCI_MSG_DATA_CREDIT_NTF 0x0B +#define UCI_MSG_DATA_TRANSFER_STATUS_NTF 0x0C + /********************************************************* * UCI session config Group-2: Opcodes and size of command ********************************************************/ @@ -147,6 +168,9 @@ #define UCI_MSG_SESSION_GET_COUNT 0x05 #define UCI_MSG_SESSION_GET_STATE 0x06 #define UCI_MSG_SESSION_UPDATE_CONTROLLER_MULTICAST_LIST 0x07 +#define UCI_MSG_SESSION_UPDATE_ACTIVE_ROUNDS_OF_DT_ANCHOR 0x08 +#define UCI_MSG_SESSION_UPDATE_ACTIVE_ROUNDS_OF_DT_TAG 0x09 +#define UCI_MSG_SESSION_CONFIGURE_DT_ANCHOR_RR_RDM_LIST 0x0A /* Pay load size for each command*/ #define UCI_MSG_SESSION_INIT_CMD_SIZE 0x05 @@ -219,6 +243,7 @@ #define UCI_PARAM_ID_TX_ADAPTIVE_PAYLOAD_POWER 0x1C #define UCI_PARAM_ID_RESPONDER_SLOT_INDEX 0x1E #define UCI_PARAM_ID_PRF_MODE 0x1F +#define UCI_PARAM_ID_CAP_SIZE_RANGE 0x20 #define UCI_PARAM_ID_SCHEDULED_MODE 0x22 #define UCI_PARAM_ID_KEY_ROTATION 0x23 #define UCI_PARAM_ID_KEY_ROTATION_RATE 0x24 @@ -236,6 +261,10 @@ #define UCI_PARAM_ID_SUB_SESSION_ID 0x30 #define UCI_PARAM_ID_BPRF_PHR_DATA_RATE 0x31 #define UCI_PARAM_ID_MAX_NUMBER_OF_MEASUREMENTS 0x32 +#define UCI_PARAM_ID_UL_TDOA_TX_INTERVAL 0x33 +#define UCI_PARAM_ID_UL_TDOA_RANDOM_WINDOW 0x34 +#define UCI_PARAM_ID_UL_TDOA_DEVICE_ID 0x38 +#define UCI_PARAM_ID_UL_TDOA_TX_TIMESTAMP 0x39 /* UCI Parameter ID Length */ #define UCI_PARAM_LEN_DEVICE_ROLE 0x01 @@ -278,6 +307,10 @@ #define UCI_PARAM_LEN_IN_BAND_TERMINATION_ATTEMPT_COUNT 0x01 #define UCI_PARAM_LEN_SUB_SESSION_ID 0x04 #define UCI_PARAM_LEN_BLOCK_STRIDE_LENGTH 0x01 +#define UCI_PARAM_LEN_UL_TDOA_TX_INTERVAL 0x04 +#define UCI_PARAM_LEN_UL_TDOA_RANDOM_WINDOW 0x04 +#define UCI_PARAM_LEN_UL_TDOA_DEVICE_ID 0x09 +#define UCI_PARAM_LEN_UL_TDOA_TX_TIMESTAMP 0x01 #define MAX_VENDOR_INFO_LENGTH 1000 // vendor specific info of rangedata max length considering 24 measures for TDOA @@ -313,6 +346,8 @@ #define UCI_STATUS_RANGING_RX_MAC_DEC_FAILED 0x25 #define UCI_STATUS_RANGING_RX_MAC_IE_DEC_FAILED 0x26 #define UCI_STATUS_RANGING_RX_MAC_IE_MISSING 0x27 +#define STS_LENGTH 0x35 +#define RSSI_REPORTING 0x36 /* UWB Data Session Specific Status Codes */ #define UCI_STATUS_DATA_MAX_TX_PSDU_SIZE_EXCEEDED 0x30 @@ -334,8 +369,21 @@ /************************************************* * Ranging Mesaurement type **************************************************/ -#define MEASUREMENT_TYPE_ONEWAY 0x00 +#define MEASUREMENT_TYPE_ULTDOA 0x00 #define MEASUREMENT_TYPE_TWOWAY 0x01 +#define MEASUREMENT_TYPE_DLTDOA 0x02 +#define MEASUREMENT_TYPE_OWR_WITH_AOA 0x03 + +#define EXTENDED_ADDRESS_LEN 0x08 +#define EXTENDED_PARAM_ID_MASK 0xF0 + +/* Maximum size of UCI DATA Message the UWBS can receive */ +#define MAX_DATA_MSG_SIZE 0x00 +#define MAX_DATA_PKT_PAYLOAD_SIZE 0x01 + + +/* Maximum Length of RrRdmList*/ +#define MAX_RRRDM_LIST_LENGTH 0XFF /************************************************* * Mac Addressing Mode Indicator @@ -348,6 +396,10 @@ #define SHORT_ADDRESS_LEN 0x02 #define EXTENDED_ADDRESS_LEN 0x08 #define MAX_NUM_OF_TDOA_MEASURES 24 +#define MAX_NUM_OF_DLTDOA_MEASURES 10 +#define MAX_NUM_OWR_AOA_MEASURES 1 +#define UCI_MAX_DATA_SIZE 4196 + #define MAX_NUM_RESPONDERS \ 12 // max number of responders for contention based raning #define MAX_NUM_CONTROLLEES \ diff --git a/src/uwa/dm/uwa_dm_act.cc b/src/uwa/dm/uwa_dm_act.cc index 57e27ed..e1d13f9 100755 --- a/src/uwa/dm/uwa_dm_act.cc +++ b/src/uwa/dm/uwa_dm_act.cc @@ -2,7 +2,7 @@ /****************************************************************************** * * Copyright (C) 1999-2014 Broadcom Corporation - * Copyright 2018-2020 NXP + * Copyright 2018-2022 NXP * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -359,6 +359,32 @@ static void uwa_dm_uwb_response_cback(tUWB_RESPONSE_EVT event, &dm_cback_data); } break; + case UWB_SESSION_ACTIVE_ROUNDS_INDEX_UPDATE_REVT: + { + dm_cback_data.status = p_data->status; + tUWA_UPDATE_RANGE_ROUND_INDEX_REVT* p_resp_data = &dm_cback_data.sRange_round_index; + p_resp_data->status = p_data->sRange_round_index.status; + p_resp_data->len = p_data->sRange_round_index.len; + if(p_resp_data->len > 0){ + memcpy(p_resp_data->rng_round_index, p_data->sRange_round_index.rng_round_index, p_data->sRange_round_index.len); + } + } + (*uwa_dm_cb.p_dm_cback)(UWA_DM_SESSION_ACTIVE_ROUNDS_INDEX_UPDATE_REVT, &dm_cback_data); + break; + + case UWB_SESSION_CONFIGURE_DT_ANCHOR_RR_RDM_REVT: + { + dm_cback_data.status = p_data->status; + tUWA_CONFIGURE_DT_ANCHOR_RR_RDM_LIST_REVT* p_resp_data = &dm_cback_data.sConfigure_dt_anchor_rr_rdm_list; + p_resp_data->status = p_data->sConfigure_dt_anchor_rr_rdm_list.status; + p_resp_data->len = p_data->sConfigure_dt_anchor_rr_rdm_list.len; + if(p_resp_data->len > 0){ + memcpy(p_resp_data->rng_round_indexs, p_data->sConfigure_dt_anchor_rr_rdm_list.rng_round_indexs, p_data->sConfigure_dt_anchor_rr_rdm_list.len); + } + } + (*uwa_dm_cb.p_dm_cback)(UWA_DM_SESSION_CONFIGURE_DT_ANCHOR_RR_RDM_REVT, &dm_cback_data); + break; + case UWB_SET_COUNTRY_CODE_REVT: /* set country code response*/ if (p_data->status != UWB_STATUS_OK) { UCI_TRACE_E(" Set country code request failed"); @@ -386,6 +412,32 @@ static void uwa_dm_uwb_response_cback(tUWB_RESPONSE_EVT event, (*uwa_dm_cb.p_dm_cback)(UWA_DM_SEND_BLINK_DATA_NTF_EVT, &dm_cback_data); } break; + case UWB_SEND_DATA_STATUS_EVT: + { + dm_cback_data.status = p_data->status; + (*uwa_dm_cb.p_dm_cback)(UWA_DM_SEND_DATA_STATUS_EVT, &dm_cback_data); + } + break; + + case UWB_DATA_TRANSFER_STATUS_NTF_REVT: + { + tUWA_DATA_TRANSFER_STATUS_NTF_REVT* p_data_transmit = &dm_cback_data.sData_xfer_status; + p_data_transmit->session_id = p_data->sData_xfer_status.session_id; + p_data_transmit->sequence_num = p_data->sData_xfer_status.sequence_num; + p_data_transmit->status = p_data->sData_xfer_status.status; + (*uwa_dm_cb.p_dm_cback)(UWA_DM_DATA_TRANSFER_STATUS_NTF_EVT, &dm_cback_data); + } + break; + + case UWB_DATA_RECV_REVT: + { + tUWA_RX_DATA_REVT* p_rcv_data = &dm_cback_data.sRcvd_data; + memset(p_rcv_data, 0, sizeof(tUWA_RX_DATA_REVT)); + memcpy((uint8_t*)p_rcv_data, (uint8_t*)&p_data->sRcvd_data, sizeof(tUWA_RX_DATA_REVT)); + (*uwa_dm_cb.p_dm_cback)(UWA_DM_DATA_RECV_EVT, &dm_cback_data); + } + break; + case UWB_CONFORMANCE_TEST_DATA: /* conformance test notification */ { tUWA_CONFORMANCE_TEST_DATA* p_sConformance_data_ntf = @@ -403,8 +455,8 @@ static void uwa_dm_uwb_response_cback(tUWB_RESPONSE_EVT event, } break; case UWB_VENDOR_SPECIFIC_UCI_NTF_EVT: { - dm_cback_data.sVendor_specific_ntf.len = p_data->sVendor_specific_ntf.len; - memcpy((uint8_t*)dm_cback_data.sVendor_specific_ntf.data, p_data->sVendor_specific_ntf.data, p_data->sVendor_specific_ntf.len); + dm_cback_data.vendor_specific_ntf.len = p_data->vendor_specific_ntf.len; + memcpy((uint8_t*)dm_cback_data.vendor_specific_ntf.data, p_data->vendor_specific_ntf.data, p_data->vendor_specific_ntf.len); (*uwa_dm_cb.p_dm_cback)(UWA_VENDOR_SPECIFIC_UCI_NTF_EVT, &dm_cback_data); } break; default: @@ -909,6 +961,63 @@ bool uwa_dm_act_send_raw_cmd(tUWA_DM_MSG* p_data) { /******************************************************************************* ** +** Function uwa_dm_act_send_data_frame +** +** Description send data frame over UWB +** +** Returns FALSE (message buffer is NOT freed by caller) +** +*******************************************************************************/ +bool uwa_dm_act_send_data_frame(tUWA_DM_MSG* p_data){ + tUWB_STATUS status; + + if(p_data == NULL) { + UCI_TRACE_E("uwa_dm_act_test_stop_session(): p_data is NULL)"); + return false; + } else { + status = UWB_SendData( + p_data->send_data_frame.session_id, + p_data->send_data_frame.p_addr, p_data->send_data_frame.dest_end_point, + p_data->send_data_frame.sequence_num, p_data->send_data_frame.data_len, + p_data->send_data_frame.p_data); + } + if(status == UWB_STATUS_OK) { + UCI_TRACE_D("uwa_dm_act_send_data_frame(): success , status=0x%X",status); + } else { + UCI_TRACE_E("uwa_dm_act_send_data_frame(): failed , status=0x%X",status); + } + return true; +} + +/******************************************************************************* +** +** Function uwa_dm_act_update_active_range_round_index +** +** Description Update Active Ranging Index Command +** +** Returns FALSE (message buffer is NOT freed by caller) +** +*******************************************************************************/ +bool uwa_dm_act_update_active_range_round_index(tUWA_DM_MSG* p_data){ + tUWB_STATUS status; + + if(p_data == NULL) { + UCI_TRACE_E("uwa_dm_act_update_active_range_round_index(): p_data is NULL)"); + return false; + } else { + status = UWB_UpdateRangingRoundIndex(p_data->update_rng_index.dlTdoaRole, p_data->update_rng_index.session_id, p_data->update_rng_index.number_of_rng_index, + p_data->update_rng_index.length, p_data->update_rng_index.p_rng_index); + } + if(status == UWB_STATUS_OK) { + UCI_TRACE_D("uwa_dm_act_update_active_range_round_index(): success , status=0x%X",status); + } else { + UCI_TRACE_E("uwa_dm_act_update_active_range_round_index(): failed , status=0x%X",status); + } + return true; +} + +/******************************************************************************* +** ** Function uwa_dm_act_get_range_count ** ** Description Send the get range count command to the ranging count @@ -1020,6 +1129,32 @@ bool uwa_dm_act_multicast_list_update(tUWA_DM_MSG* p_data) { /******************************************************************************* ** +** Function uwa_dm_act_configure_dt_anchor_rr_rdm +** +** Description Configure dt anchor rr rdm list command +** +** Returns FALSE (message buffer is NOT freed by caller) +** +*******************************************************************************/ +bool uwa_dm_act_configure_dt_anchor_rr_rdm(tUWA_DM_MSG* p_data){ + tUWB_STATUS status; + if (p_data->sConfigure_dt_anchor_rr_rdm_list.length + 2 > MAX_RRRDM_LIST_LENGTH) { + /* Total length of mac addr list must be less than 256 (1 byte) */ + status = UWA_STATUS_FAILED; + } else { + status = UWB_ConfigureDTAnchorForRrRdmList(p_data->sConfigure_dt_anchor_rr_rdm_list.session_id, p_data->sConfigure_dt_anchor_rr_rdm_list.rr_rdm_count, p_data->sConfigure_dt_anchor_rr_rdm_list.length, + p_data->sConfigure_dt_anchor_rr_rdm_list.p_data); + if(UWB_STATUS_OK == status){ + UCI_TRACE_D("uwa_dm_act_configure_dt_anchor_rr_rdm(): success ,status=0x%X",status); + } else { + UCI_TRACE_E("uwa_dm_act_configure_dt_anchor_rr_rdm(): failed ,status=0x%X",status); + } + } + return true; +} + +/******************************************************************************* +** ** Function uwa_dm_act_set_country_code ** ** Description send country code set command. @@ -1346,6 +1481,15 @@ std::string uwa_dm_uwb_revt_2_str(tUWB_RESPONSE_EVT event) { case UWB_BLINK_DATA_TX_NTF_REVT: return "UWB_BLINK_DATA_TX_NTF_REVT"; + case UWB_DATA_RECV_REVT: + return "UWB_DATA_RECV_REVT"; + + case UWB_DATA_TRANSFER_STATUS_NTF_REVT: + return "UWB_DATA_TRANSFER_STATUS_NTF_REVT"; + + case UWB_SEND_DATA_STATUS_EVT: + return "UWB_SEND_DATA_STATUS_EVT"; + case UWB_CONFORMANCE_TEST_DATA: return "UWB_CONFORMANCE_TEST_DATA"; diff --git a/src/uwa/dm/uwa_dm_api.cc b/src/uwa/dm/uwa_dm_api.cc index f394318..b58ccba 100755 --- a/src/uwa/dm/uwa_dm_api.cc +++ b/src/uwa/dm/uwa_dm_api.cc @@ -1,7 +1,7 @@ /****************************************************************************** * * Copyright (C) 2010-2014 Broadcom Corporation - * Copyright 2018-2020 NXP + * Copyright 2018-2022 NXP * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -413,6 +413,45 @@ tUWA_STATUS UWA_GetAppConfig(uint32_t session_id, uint8_t noOfParams, return (UWA_STATUS_FAILED); } + +/******************************************************************************* +** +** Function UWA_UpdateRangingRoundIndex +** +** Description Update the Ranging Round Index for the given session for +** TDoA Feature,The result is reported with an +** UWA_DM_SESSION_ACTIVE_ROUNDS_INDEX_UPDATE_REVT in the tUWA_DM_CBACK +** callback. +** +** Returns UWA_STATUS_OK if command is sent successfully +** UWA_STATUS_FAILED otherwise +** +*******************************************************************************/ +tUWA_STATUS UWA_UpdateRangingRoundIndex(uint8_t dlTdoaRole, uint32_t session_id, uint8_t number_of_active_rngIndex, + uint8_t rng_round_index_len, uint8_t* p_rng_round_index) { + tUWA_DM_API_UPDATE_ACTIVE_RNG_INDEX* p_msg; + + if ((p_msg = (tUWA_DM_API_UPDATE_ACTIVE_RNG_INDEX*)phUwb_GKI_getbuf( + (uint16_t)(sizeof(tUWA_DM_API_UPDATE_ACTIVE_RNG_INDEX) + rng_round_index_len))) != NULL) + { + p_msg->hdr.event = UWA_DM_API_UPDATE_ACTIVE_RNG_INDEX_EVT; + p_msg->session_id = session_id; + p_msg->dlTdoaRole = dlTdoaRole; + p_msg->number_of_rng_index = number_of_active_rngIndex; + p_msg->length = rng_round_index_len; + p_msg->p_rng_index = (uint8_t*)(p_msg + 1); + + /* Copy the param IDs */ + memcpy(p_msg->p_rng_index, p_rng_round_index, rng_round_index_len); + + uwa_sys_sendmsg(p_msg); + + return (UWA_STATUS_OK); + } + + return (UWA_STATUS_FAILED); +} + /******************************************************************************* ** ** Function UWA_StartRangingSession @@ -599,6 +638,38 @@ extern tUWA_STATUS UWA_ControllerMulticastListUpdate( /******************************************************************************* ** +** Function UWA_ConfigureDTAnchorForRrRdmList +** +** Description This function is called to Configure DT Anchor RR RDM List Update. +** The result is reported with an +** UWA_SESSION_CONFIGURE_DT_ANCHOR_RR_RDM_REVT in the tUWA_DM_CBACK +** callback. +** Returns UWA_STATUS_OK if command is successfully initiated +** UWA_STATUS_FAILED otherwise +** +*******************************************************************************/ +tUWA_STATUS UWA_ConfigureDTAnchorForRrRdmList(uint32_t session_id, uint8_t noOfParams, uint8_t rrRdmConfigParamLen , uint8_t rrRdmConfigParam[]) { + tUWA_DM_API_CONFIGURE_DT_ANCHOR_RR_RDM_LIST* p_msg; + p_msg = (tUWA_DM_API_CONFIGURE_DT_ANCHOR_RR_RDM_LIST*)phUwb_GKI_getbuf(sizeof(tUWA_DM_API_CONFIGURE_DT_ANCHOR_RR_RDM_LIST)); + + if (p_msg != NULL) { + p_msg->hdr.event = UWA_DM_API_CONFIGURE_DT_ANCHOR_RR_RDM_LIST_EVT; + p_msg->session_id = session_id; + p_msg->rr_rdm_count = noOfParams; + p_msg->length = rrRdmConfigParamLen; + p_msg->p_data= (uint8_t*)(p_msg + 1); + + memcpy(p_msg->p_data, rrRdmConfigParam, (rrRdmConfigParamLen)); + + uwa_sys_sendmsg(p_msg); + + return UWA_STATUS_OK; + } + return UWA_STATUS_FAILED; +} + +/******************************************************************************* +** ** Function UWA_ControllerSetCountryCode ** ** Description This function is called to set country code. @@ -928,4 +999,43 @@ tUWA_STATUS UWA_SendRawCommand(uint16_t cmd_params_len, uint8_t* p_cmd_params, } return UWA_STATUS_FAILED; -}
\ No newline at end of file +} + +/******************************************************************************* +** +** Function UWA_SendUwbDataFrame +** +** Description This function is called to send UWB data over UWB RF interafce . +** +** Returns UWA_STATUS_OK if data sucessfully accepeted by UWB subsystem +** UWA_STATUS_FAILED otherwise +** +*******************************************************************************/ +tUWA_STATUS UWA_SendUwbData(uint32_t session_id, + uint8_t* p_addr, uint8_t dest_end_point, uint8_t sequence_num, + uint16_t data_len, + uint8_t* p_data) { + tUWA_DM_API_SEND_DATA_FRAME* p_msg; + UCI_TRACE_I("UWA_SendUwbDataFrame: data_len = %d", data_len); + if ((data_len == 0) || (p_data == nullptr) || (p_addr == nullptr)) + return (UWA_STATUS_FAILED); + + p_msg = (tUWA_DM_API_SEND_DATA_FRAME*)phUwb_GKI_getbuf(sizeof(tUWA_DM_API_SEND_DATA_FRAME) + data_len); + if (p_msg != nullptr) { + p_msg->hdr.event = UWA_DM_API_SEND_DATA_FRAME_EVT; + p_msg->session_id = session_id; + memcpy(p_msg->p_addr, p_addr, EXTENDED_ADDRESS_LEN); + p_msg->dest_end_point = dest_end_point; + p_msg->sequence_num = sequence_num; + p_msg->data_len = data_len; + + p_msg->p_data = (uint8_t*)(p_msg + 1); + memcpy(p_msg->p_data, p_data, data_len); + + uwa_sys_sendmsg(p_msg); + + return (UWA_STATUS_OK); + } + return UWA_STATUS_FAILED; + +} diff --git a/src/uwa/dm/uwa_dm_main.cc b/src/uwa/dm/uwa_dm_main.cc index 014bc93..7502e56 100755 --- a/src/uwa/dm/uwa_dm_main.cc +++ b/src/uwa/dm/uwa_dm_main.cc @@ -1,7 +1,7 @@ /* * Copyright (C) 2021 The Android Open Source Project * - * Copyright 2021 NXP. + * Copyright 2021-2022 NXP. * * Licensed under the Apache License, Version 2.0 (the "License"); * You may not use this file except in compliance with the License. @@ -71,7 +71,11 @@ const tUWA_DM_ACTION uwa_dm_action[] = { uwa_dm_act_test_per_rx, /* UWA_DM_API_TEST_PER_RX_EVT */ uwa_dm_act_test_uwb_loopback, /* UWA_DM_API_TEST_UWB_LOOPBACK_EVT */ uwa_dm_act_test_rx, /* UWA_DM_API_TEST_RX_EVT */ - uwa_dm_act_test_stop_session /* UWA_DM_API_TEST_STOP_SESSION_EVT */ + uwa_dm_act_test_stop_session, /* UWA_DM_API_TEST_STOP_SESSION_EVT */ + /* API events for Data tranfer handling */ + uwa_dm_act_send_data_frame, /* UWA_DM_API_SEND_DATA_FRAME_EVT */ + uwa_dm_act_configure_dt_anchor_rr_rdm, /* UWA_DM_API_CONFIGURE_DT_ANCHOR_RR_RDM_LIST_EVT */ + uwa_dm_act_update_active_range_round_index /* UWA_DM_API_UPDATE_ACTIVE_RNG_INDEX_EVT */ }; /***************************************************************************** diff --git a/src/uwa/include/uwa_api.h b/src/uwa/include/uwa_api.h index 5da7cba..0e14a4a 100755 --- a/src/uwa/include/uwa_api.h +++ b/src/uwa/include/uwa_api.h @@ -1,7 +1,7 @@ /****************************************************************************** * * Copyright (C) 1999-2012 Broadcom Corporation - * Copyright 2018-2020 NXP + * Copyright 2018-2022 NXP * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -146,6 +146,12 @@ enum { UWA_DM_CONFORMANCE_NTF_EVT, /* Conformance Test Ntf Event */ UWA_DM_SET_COUNTRY_CODE_RSP_EVT, /* Country code update resp event */ UWA_VENDOR_SPECIFIC_UCI_NTF_EVT, /* Proprietary Ntf Event */ + UWA_DM_SEND_DATA_STATUS_EVT, /* data status EVT */ + UWA_DM_SEND_DATA_PACKET_RSP_EVT, /* data reception status by UWBS */ + UWA_DM_DATA_TRANSFER_STATUS_NTF_EVT, /* data transfer status over UWB */ + UWA_DM_DATA_RECV_EVT, /* Recieved data over UWB */ + UWA_DM_SESSION_CONFIGURE_DT_ANCHOR_RR_RDM_REVT, /* Result of Configure DT Anchor RR RDM List Cmd */ + UWA_DM_SESSION_ACTIVE_ROUNDS_INDEX_UPDATE_REVT, /* Result of Update Active Ranging Index Cmd */ }; /* UWA_DM callback events for UWB RF events */ @@ -186,7 +192,7 @@ typedef struct { uint16_t phy_version; uint16_t uciTest_version; uint8_t vendor_info_len; - uint8_t vendor_info[UCI_VENDOR_INFO_MAX_SIZE]; + uint8_t vendor_info[UCI_MAX_FRAGMENT_BUFF_SIZE]; } tUWA_GET_DEVICE_INFO_REVT; /* Data for UWA_DM_CORE_SET_CONFIG_RSP_EVT */ @@ -261,30 +267,71 @@ typedef struct { uint16_t aoa_dest_elevation; uint8_t aoa_dest_elevation_FOM; uint8_t slot_index; - uint8_t rfu[12]; + uint8_t rssi; + uint8_t rfu[11]; } tUWA_TWR_RANGING_MEASR; typedef struct { uint8_t mac_addr[8]; + uint8_t message_control; uint8_t frame_type; uint8_t nLos; /* non line of sight */ uint16_t aoa_azimuth; uint8_t aoa_azimuth_FOM; uint16_t aoa_elevation; uint8_t aoa_elevation_FOM; - uint64_t timeStamp; - uint32_t blink_frame_number; /* blink frame number received from tag/master - anchor */ - uint8_t rfu[12]; - uint8_t device_info_size; /* Size of Device Specific Information */ - uint8_t* device_info; /* Device Specific Information */ - uint8_t blink_payload_size; /* Size of Blink Payload Data */ - uint8_t* blink_payload_data; /* Blink Payload Data */ + uint32_t frame_number; + uint8_t rxTimeStamp[8]; + uint8_t ulTdoa_device_id[8]; + uint8_t txTimeStamp[8]; } tUWA_TDoA_RANGING_MEASR; +/* the data type associated with vendor notification */ +typedef struct { + uint16_t len; + uint8_t data[UCI_VENDOR_INFO_MAX_SIZE]; +}tUWA_VENDOR_SPECIFIC_NTF; + +typedef struct { + uint8_t mac_addr[8]; + uint8_t status; + uint8_t nLos; + uint8_t frame_seq_num; + uint16_t block_index; + uint16_t aoa_azimuth; + uint8_t aoa_azimuth_FOM; + uint16_t aoa_elevation; + uint8_t aoa_elevation_FOM; +} tUWA_OWR_WITH_AOA_RANGING_MEASR; + +typedef struct { + uint8_t mac_addr[8]; + uint8_t status; + uint8_t message_type; + uint16_t message_control; + uint16_t block_index; + uint8_t round_index; + uint8_t nLos; + uint16_t aoa_azimuth; + uint8_t aoa_azimuth_FOM; + uint16_t aoa_elevation; + uint8_t aoa_elevation_FOM; + uint8_t txTimeStamp[8]; + uint8_t rxTimeStamp[8]; + uint16_t cfo_anchor; + uint16_t cfo; + uint32_t initiator_reply_time; + uint32_t responder_reply_time; + uint16_t initiator_responder_TOF; + uint8_t anchor_location[12]; + uint8_t active_ranging_round[15]; +} tUWA_DLTDOA_RANGING_MEASR; + typedef union { tUWA_TWR_RANGING_MEASR twr_range_measr[MAX_NUM_RESPONDERS]; tUWA_TDoA_RANGING_MEASR tdoa_range_measr[MAX_NUM_OF_TDOA_MEASURES]; + tUWA_DLTDOA_RANGING_MEASR dltdoa_range_measr[MAX_NUM_OF_DLTDOA_MEASURES]; + tUWA_OWR_WITH_AOA_RANGING_MEASR owr_with_aoa_range_measr; } tUWA_RANGING_MEASR; typedef struct { @@ -299,6 +346,7 @@ typedef struct { uint8_t reserved[8]; uint8_t no_of_measurements; tUWA_RANGING_MEASR ranging_measures; + tUWA_VENDOR_SPECIFIC_NTF vendor_specific_ntf; } tUWA_RANGE_DATA_NTF; /* the data type associated with UWB_GET_RANGE_COUNT_REVT */ @@ -332,17 +380,44 @@ typedef struct { exhausted */ } tUWA_SEND_BLINK_DATA_NTF; +/* the data type associated with UWB_DATA_TRANSFER_STATUS_NTF_REVT */ +typedef struct { + uint32_t session_id; + uint8_t sequence_num; + uint8_t status; +} tUWA_DATA_TRANSFER_STATUS_NTF_REVT; + +/* the data type associated with UWA_DM_DATA_RECV_REVT */ +typedef struct { + uint32_t session_id; + uint8_t status; + uint32_t sequence_num; + uint8_t address[EXTENDED_ADDRESS_LEN]; + uint8_t source_end_point; + uint8_t dest_end_point; + uint16_t data_len; + uint8_t data[UCI_MAX_DATA_SIZE]; +} tUWA_RX_DATA_REVT; + /* the data type associated with UWB_CONFORMANCE_TEST_DATA */ typedef struct { uint16_t length; uint8_t data[CONFORMANCE_TEST_MAX_UCI_PKT_LENGTH]; } tUWA_CONFORMANCE_TEST_DATA; -/* the data type associated with vendor notification */ +/* the data type associated with UWB_SESSION_ACTIVE_ROUNDS_INDEX_UPDATE_REVT */ typedef struct { + uint8_t status; uint16_t len; - uint8_t data[UCI_VENDOR_INFO_MAX_SIZE]; -}tUWA_VENDOR_SPECIFIC_NTF; + uint8_t rng_round_index[255]; +}tUWA_UPDATE_RANGE_ROUND_INDEX_REVT; + +/* the data type associated with UWB_SESSION_CONFIGURE_DT_ANCHOR_RR_RDM_REVT */ +typedef struct { + uint8_t status; + uint16_t len; + uint8_t rng_round_indexs[255]; +}tUWA_CONFIGURE_DT_ANCHOR_RR_RDM_LIST_REVT; /* Union of all DM callback structures */ typedef union { @@ -373,8 +448,12 @@ typedef union { tUWA_SESSION_UPDATE_MULTICAST_LIST_NTF sMulticast_list_ntf; /*UWA_DM_SESSION_MC_LIST_UPDATE_NTF_EVT*/ tUWA_SEND_BLINK_DATA_NTF sBlink_data_ntf; /*UWA_DM_SEND_BLINK_DATA_NTF_EVT*/ + tUWA_DATA_TRANSFER_STATUS_NTF_REVT sData_xfer_status ; /*UWA_DATA_TRANSFER_STATUS_NTF_REVT*/ + tUWA_RX_DATA_REVT sRcvd_data; /*UWA_DM_DATA_RECV_REVT */ + tUWA_UPDATE_RANGE_ROUND_INDEX_REVT sRange_round_index; /*UWA_DM_UPDATE_RANGE_ROUND_INDEX_REVT */ + tUWA_CONFIGURE_DT_ANCHOR_RR_RDM_LIST_REVT sConfigure_dt_anchor_rr_rdm_list; /*UWA_SESSION_CONFIGURE_DT_ANCHOR_RR_RDM_REVT */ tUWA_CONFORMANCE_TEST_DATA sConformance_ntf; /* UWA_DM_CONFORMANCE_NTF_EVT */ - tUWA_VENDOR_SPECIFIC_NTF sVendor_specific_ntf; /*Vendor Specific ntf data */ + tUWA_VENDOR_SPECIFIC_NTF vendor_specific_ntf; /*Vendor Specific ntf data */ void* p_vs_evt_data; /* Vendor-specific evt data */ } tUWA_DM_CBACK_DATA; @@ -843,4 +922,48 @@ extern tUWA_STATUS UWA_SendRawCommand(uint16_t cmd_params_len, uint8_t* p_cmd_params, tUWA_RAW_CMD_CBACK* p_cback); +/******************************************************************************* +** +** Function UWA_SendUwbDataFrame +** +** Description This function is called to send UWB data over UWB RF interafce . +** +** data_len - The data length +** p_data - pointer to data buffer +** +** Returns UWA_STATUS_OK if data sucessfully accepeted by UWB subsystem +** UWA_STATUS_FAILED otherwise +** +*******************************************************************************/ +extern tUWA_STATUS UWA_SendUwbData(uint32_t session_id, + uint8_t* p_addr, uint8_t dest_end_point, uint8_t sequence_num, + uint16_t data_len, + uint8_t* p_data); + +/******************************************************************************* +** +** Function UWA_ConfigureDTAnchorForRrRdmList +** +** Description This function is called to Configure DT anchor RR RDM List. +** +** Returns tUWA_STATUS +** +*******************************************************************************/ +extern tUWA_STATUS UWA_ConfigureDTAnchorForRrRdmList(uint32_t session_id, uint8_t rr_rdm_count, + uint8_t rrRdmConfigParamLen, uint8_t rrRdmConfigParam[]); + +/******************************************************************************* +** +** Function UWA_UpdateRangingRoundIndex +** +** Description This function is called to update Ranging round index for TDoA feature. +** +** Returns UWA_STATUS_OK if data sucessfully accepeted by UWB subsystem +** UWA_STATUS_FAILED otherwise +** +*******************************************************************************/ + +extern tUWA_STATUS UWA_UpdateRangingRoundIndex(uint8_t dlTdoaRole, uint32_t session_id, + uint8_t number_of_active_rngIndex, uint8_t rng_round_index_len, + uint8_t* p_rng_round_index); #endif /* UWA_API_H */ diff --git a/src/uwa/include/uwa_dm_int.h b/src/uwa/include/uwa_dm_int.h index d88d378..160501e 100755 --- a/src/uwa/include/uwa_dm_int.h +++ b/src/uwa/include/uwa_dm_int.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2021 The Android Open Source Project * - * Copyright 2021 NXP. + * Copyright 2021-2022 NXP. * * Licensed under the Apache License, Version 2.0 (the "License"); * You may not use this file except in compliance with the License. @@ -66,6 +66,9 @@ enum { UWA_DM_API_TEST_RX_EVT, UWA_DM_API_TEST_STOP_SESSION_EVT, /* UWB Data packet events */ + UWA_DM_API_SEND_DATA_FRAME_EVT, + UWA_DM_API_CONFIGURE_DT_ANCHOR_RR_RDM_LIST_EVT, + UWA_DM_API_UPDATE_ACTIVE_RNG_INDEX_EVT, UWA_DM_MAX_EVT }; @@ -192,6 +195,15 @@ typedef struct { uint32_t subsession_id_list[MAX_NUM_CONTROLLEES]; } tUWA_DM_API_SESSION_UPDATE_MULTICAST_LIST; +/* data type for UWA_DM_API_CONFIGURE_DT_ANCHOR_RR_RDM_LIST_EVT */ +typedef struct { + UWB_HDR hdr; + uint32_t session_id; + uint8_t rr_rdm_count; + uint8_t length; + uint8_t* p_data; +} tUWA_DM_API_CONFIGURE_DT_ANCHOR_RR_RDM_LIST; + /* data type for UWA_DM_API_SESSION_UPDATE_MULTICAST_LIST_EVT */ typedef struct { UWB_HDR hdr; @@ -207,6 +219,27 @@ typedef struct { uint8_t app_data[UCI_MAX_PAYLOAD_SIZE]; } tUWA_DM_API_SEND_BLINK_DATA; +/* data type for UWA_DM_API_SEND_DATA_FRAME_EVT */ +typedef struct { + UWB_HDR hdr; + uint32_t session_id; + uint8_t p_addr[EXTENDED_ADDRESS_LEN]; + uint8_t dest_end_point; + uint8_t sequence_num; + uint16_t data_len; + uint8_t* p_data; +} tUWA_DM_API_SEND_DATA_FRAME; + +/* data type for UWA_DM_API_UPDATE_ACTIVE_RNG_INDEX_EVT */ +typedef struct { + UWB_HDR hdr; + uint8_t dlTdoaRole; + uint32_t session_id; + uint8_t number_of_rng_index; + uint8_t length; + uint8_t* p_rng_index; +} tUWA_DM_API_UPDATE_ACTIVE_RNG_INDEX; + /* data type for UWA_DM_API_TEST_SET_CONFIG_EVT */ typedef struct { UWB_HDR hdr; @@ -288,11 +321,16 @@ typedef union { sUwb_loopback; /* UWA_DM_API_TEST_UWB_LOOPBACK_EVT */ tUWA_DM_API_SESSION_UPDATE_MULTICAST_LIST sMulticast_list; /* UWA_DM_API_SESSION_UPDATE_MULTICAST_LIST_EVT */ + tUWA_DM_API_CONFIGURE_DT_ANCHOR_RR_RDM_LIST + sConfigure_dt_anchor_rr_rdm_list; /* UWA_DM_API_CONFIGURE_DT_ANCHOR_RR_RDM_LIST_EVT */ + tUWA_DM_API_UPDATE_ACTIVE_RNG_INDEX + update_rng_index; /* UWA_DM_API_UPDATE_ACTIVE_RNG_INDEX_EVT */ tUWA_DM_API_SET_COUNTRY_CODE sCountryCode; /* UWA_DM_API_SET_COUNTRY_CODE_EVT */ tUWA_DM_API_SEND_BLINK_DATA sSend_blink_data; /* UWA_DM_API_SEND_BLINK_DATA_EVT */ /* data types for all UWB RF TEST events */ + tUWA_DM_API_SEND_DATA_FRAME send_data_frame; /* UWA_DM_API_SEND_DATA_FRAME_EVT */ tUWA_DM_API_TEST_GET_CONFIG sTest_get_config; /* UWA_DM_API_TEST_GET_CONFIG_EVT */ tUWA_DM_API_TEST_SET_CONFIG @@ -341,6 +379,9 @@ bool uwa_dm_act_get_device_capability(tUWA_DM_MSG* p_data); bool uwa_dm_act_multicast_list_update(tUWA_DM_MSG* p_data); bool uwa_dm_act_set_country_code(tUWA_DM_MSG* p_data); bool uwa_dm_act_send_blink_data(tUWA_DM_MSG* p_data); +bool uwa_dm_act_send_data_frame(tUWA_DM_MSG* p_data); +bool uwa_dm_act_configure_dt_anchor_rr_rdm(tUWA_DM_MSG* p_data); +bool uwa_dm_act_update_active_range_round_index(tUWA_DM_MSG* p_data); /* Action function prototypes for all RF test functionality */ bool uwa_dm_act_test_set_config(tUWA_DM_MSG* p_data); diff --git a/src/uwb/include/uci_hmsgs.h b/src/uwb/include/uci_hmsgs.h index f317100..2ecbe04 100755 --- a/src/uwb/include/uci_hmsgs.h +++ b/src/uwb/include/uci_hmsgs.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2021 The Android Open Source Project * - * Copyright 2021 NXP. + * Copyright 2021-2022 NXP. * * Licensed under the Apache License, Version 2.0 (the "License"); * You may not use this file except in compliance with the License. @@ -52,6 +52,15 @@ uint8_t uci_snd_range_start_cmd(uint32_t session_id); uint8_t uci_snd_range_stop_cmd(uint32_t session_id); uint8_t uci_snd_blink_data_cmd(uint32_t session_id, uint8_t repetition_count, uint8_t app_data_len, uint8_t* app_data); +uint8_t uci_send_data_frame(uint32_t session_id, + uint8_t* p_addr, uint8_t dest_end_point, uint8_t sequence_num, + uint16_t data_len, + uint8_t* p_data); +uint8_t uci_send_range_round_index_update_cmd(uint8_t dlTdoaRole, + uint32_t session_id, uint8_t number_of_active_rngIndex, + uint8_t rng_round_index_len, uint8_t* p_rng_round_index); +uint8_t uci_snd_configure_dt_anchor_for_rr_rdm_list_cmd(uint32_t session_id, + uint8_t rr_rdm_count, uint8_t length, uint8_t* data); /* APIs for UWB RF test functionality */ uint8_t uci_snd_test_get_config_cmd(uint32_t session_id, uint8_t num_ids, @@ -83,6 +92,9 @@ extern void uci_proc_test_management_rsp(uint8_t op_code, uint8_t* p_buf, uint16_t len); extern void uci_proc_raw_cmd_rsp(uint8_t* p_buf, uint16_t len); +extern void uci_proc_data_control_ntf(uint8_t op_code, uint8_t* p_buf, uint16_t len); + +extern void uci_proc_app_data_management_ntf(uint8_t op_code, uint8_t* p_buf, uint16_t len); extern void uci_proc_vendor_specific_ntf(uint8_t gid, uint8_t* p_buf, uint16_t len); #endif /* UWB_UCI_MSGS_H */ diff --git a/src/uwb/include/uwb_api.h b/src/uwb/include/uwb_api.h index 1378cab..e75e924 100755 --- a/src/uwb/include/uwb_api.h +++ b/src/uwb/include/uwb_api.h @@ -104,7 +104,7 @@ typedef uint8_t tUWB_STATUS; // RFU size for tdoa Ranging #define TDOA_RANGE_MEASR_RFU 12 -#define TWR_RANGE_MEASR_RFU 12 +#define TWR_RANGE_MEASR_RFU 11 #define CONFORMANCE_TEST_MAX_UCI_PKT_LENGTH 260 @@ -141,7 +141,14 @@ enum { UWB_BLINK_DATA_TX_NTF_REVT, /* 31 Blink Data Tx ntf */ UWB_CONFORMANCE_TEST_DATA, /* 32 Conformance test data ntf */ UWB_SET_COUNTRY_CODE_REVT, /* 33 Set country code resp */ - UWB_VENDOR_SPECIFIC_UCI_NTF_EVT /* 34 Proprietary ntf */ + UWB_VENDOR_SPECIFIC_UCI_NTF_EVT, /* 34 Proprietary ntf */ + UWB_SEND_DATA_STATUS_EVT, /* 35 UWB data reception status by UWBS */ + UWB_DATA_TRANSFER_STATUS_NTF_REVT, /* 36 UWB data transfer status over UWB */ + UWB_DATA_RECV_REVT, /* 37 received data over UWB */ + UWB_SESSION_CONFIGURE_DT_ANCHOR_RR_RDM_REVT, /* 38 DL TDoA Configure DT Anchor + RR RDM List Response */ + UWB_SESSION_ACTIVE_ROUNDS_INDEX_UPDATE_REVT /* 39 DL-TDoA Update Ranging Index + status Response */ }; typedef uint16_t tUWB_RESPONSE_EVT; @@ -261,32 +268,81 @@ typedef struct { uint16_t aoa_dest_elevation; uint8_t aoa_dest_elevation_FOM; uint8_t slot_index; + uint8_t rssi; uint8_t rfu[TWR_RANGE_MEASR_RFU]; } tUWB_TWR_RANGING_MEASR; typedef struct { uint8_t mac_addr[8]; + uint8_t message_control; uint8_t frame_type; uint8_t nLos; /* non line of sight */ uint16_t aoa_azimuth; uint8_t aoa_azimuth_FOM; uint16_t aoa_elevation; uint8_t aoa_elevation_FOM; - uint64_t timeStamp; /* Time stamp */ - uint32_t blink_frame_number; /* blink frame number received from tag/master - anchor */ - uint8_t rfu[TDOA_RANGE_MEASR_RFU]; - uint8_t device_info_size; /* Size of Device Specific Information */ - uint8_t* device_info; /* Device Specific Information */ - uint8_t blink_payload_size; /* Size of Blink Payload Data */ - uint8_t* blink_payload_data; /* Blink Payload Data */ + uint32_t frame_number; /* Number received in the payload of the Blink UTM */ + uint8_t rxTimeStamp[8]; /* Local RX timestamp of the received UWB RFRAME */ + uint8_t ulTdoa_device_id[8]; /* Device ID of the sender of the received UTM */ + uint8_t txTimeStamp[8]; /* TX timestamp of the UWB RFRAME */ } tUWB_TDoA_RANGING_MEASR; + +/* the data type associated with vendor notification */ +typedef struct { + uint16_t len; + uint8_t data[UCI_VENDOR_INFO_MAX_SIZE]; +}tUWB_VENDOR_SPECIFIC_NTF; + +typedef struct { + uint8_t mac_addr[8]; + uint8_t status; + uint8_t message_type; + uint16_t message_control; + uint16_t block_index; + uint8_t round_index; + uint8_t nLos; + uint16_t aoa_azimuth; + uint8_t aoa_azimuth_FOM; + uint16_t aoa_elevation; + uint8_t aoa_elevation_FOM; + uint8_t rssi; + uint64_t txTimeStamp; + uint64_t rxTimeStamp; + uint16_t cfo_anchor; + uint16_t cfo; + uint32_t initiator_reply_time; + uint32_t responder_reply_time; + uint16_t initiator_responder_TOF; + uint8_t anchor_location[12]; + uint8_t active_ranging_round[15]; +} tUWB_DLTDOA_RANGING_MEASR; + +typedef struct { + uint8_t mac_addr[8]; + uint8_t status; + uint8_t nLos; + uint8_t frame_seq_num; + uint16_t block_index; + uint16_t aoa_azimuth; + uint8_t aoa_azimuth_FOM; + uint16_t aoa_elevation; + uint8_t aoa_elevation_FOM; +} tUWB_OWR_WITH_AOA_RANGING_MEASR; + typedef union { tUWB_TWR_RANGING_MEASR twr_range_measr[MAX_NUM_RESPONDERS]; tUWB_TDoA_RANGING_MEASR tdoa_range_measr[MAX_NUM_OF_TDOA_MEASURES]; + tUWB_DLTDOA_RANGING_MEASR dltdoa_range_measr[MAX_NUM_OF_DLTDOA_MEASURES]; + tUWB_OWR_WITH_AOA_RANGING_MEASR owr_with_aoa_range_measr; } tUWB_RANGING_MEASR; +/* the data type associated with vendor notification */ +typedef struct { + uint16_t len; + uint8_t data[UCI_VENDOR_INFO_MAX_SIZE]; +}tUWB_VENDOR_SPECIFIC_NTF; + /* the data type associated with UWB_RANGE_DATA_REVT */ typedef struct { uint16_t range_data_len; @@ -300,6 +356,7 @@ typedef struct { uint8_t reserved[8]; uint8_t no_of_measurements; tUWB_RANGING_MEASR ranging_measures; + tUWB_VENDOR_SPECIFIC_NTF vendor_specific_ntf; } tUWB_RANGE_DATA_REVT; /* the data type associated with UWB_CONFORMANCE_TEST_DATA */ @@ -343,6 +400,40 @@ typedef struct { uint8_t repetition_count_status; /* repetition count status */ } tUWB_SEND_BLINK_DATA_NTF_REVT; +/* the data type associated with UWB_DATA_TRANSFER_STATUS_NTF_REVT */ +typedef struct { + uint32_t session_id; + uint32_t sequence_num; + uint8_t status; +}tUWB_DATA_TRANSFER_STATUS_NTF_REVT; + +/* the data type associated with UWB_DATA_RECV_REVT */ +typedef struct { + uint32_t session_id; + uint8_t status; + uint32_t sequence_num; + uint8_t address[EXTENDED_ADDRESS_LEN]; + uint8_t source_end_point; + uint8_t dest_end_point; + uint16_t data_len; + uint8_t data[UCI_MAX_DATA_SIZE]; +}tUWB_RX_DATA_REVT; + + +/* the data type associated with UWB_SESSION_CONFIGURE_DT_ANCHOR_RR_RDM_REVT */ +typedef struct { + uint8_t status; + uint16_t len; + uint8_t rng_round_indexs[255]; +}tUWB_CONFIGURE_DT_ANCHOR_RR_RDM_LIST_REVT; + +/* the data type associated with UWB_DATA_RECV_REVT */ +typedef struct { + uint8_t status; + uint16_t len; + uint8_t rng_round_index[255]; +}tUWB_UPDATE_RANGE_ROUND_INDEX_REVT; + typedef struct { tUWB_STATUS status; /* The event status. */ } tUWB_SET_COUNTRY_CODE_REVT; @@ -368,7 +459,11 @@ typedef union { tUWB_SET_COUNTRY_CODE_REVT sSet_country_code_status; tUWB_SEND_BLINK_DATA_NTF_REVT sSend_blink_data_ntf; tUWB_CONFORMANCE_TEST_DATA sConformance_test_data; - tUWB_VENDOR_SPECIFIC_REVT sVendor_specific_ntf; + tUWB_VENDOR_SPECIFIC_REVT vendor_specific_ntf; + tUWB_DATA_TRANSFER_STATUS_NTF_REVT sData_xfer_status; + tUWB_RX_DATA_REVT sRcvd_data; + tUWB_UPDATE_RANGE_ROUND_INDEX_REVT sRange_round_index; + tUWB_CONFIGURE_DT_ANCHOR_RR_RDM_LIST_REVT sConfigure_dt_anchor_rr_rdm_list; } tUWB_RESPONSE; /* Data types associated with all RF test Events */ @@ -794,6 +889,55 @@ extern tUWB_STATUS UWB_SendRawCommand(UWB_HDR* p_data, tUWB_RAW_CBACK* p_cback); /******************************************************************************* ** +** Function UWB_SendData +** +** Description This function is called to send the data packet over UWB. +** +** Parameters p_data - The data buffer +** +** Returns tUWB_STATUS +** +*******************************************************************************/ +tUWB_STATUS UWB_SendData(uint32_t session_id, uint8_t* p_addr, + uint8_t dest_end_point, uint8_t sequence_num, + uint16_t data_len, uint8_t* p_data); +/******************************************************************************* +** +** Function UWB_ConfigureDTAnchorForRrRdmList +** +** Description This function is called to Configure DT anchor RR RDM List. +** +** Parameter session_id - Session id To which Rangng index need to update +** rr_rdm_count - Number Of rr_rdm +** length - length of data buffer +** p_data - The data buffer +** +** Returns tUWB_STATUS +** +*******************************************************************************/ +extern tUWB_STATUS UWB_ConfigureDTAnchorForRrRdmList(uint32_t session_id, uint8_t rr_rdm_count, + uint8_t length, uint8_t* p_data); + +/******************************************************************************* +** +** Function UWB_UpdateRangingRoundIndex +** +** Description This function is called to Update Active Ranging Index. +** +** Parameter dlTdoaRole - 0x00(Anchor) or 0x01(Tag) +** session_id - Session id To which Rangng index need to update +** number_of_active_rngIndex - NUmber Of Ranging index to be updated +** rng_round_index_len - Range Round Index Len +** p_rng_round_index - pointer to Ranging Index buffer +** +** Returns tUWB_STATUS +** +*******************************************************************************/ +tUWB_STATUS UWB_UpdateRangingRoundIndex(uint8_t dlTdoaRole, uint32_t session_id, uint8_t number_of_rng_index, + uint8_t rng_round_index_len, uint8_t* p_rng_round_index); + +/******************************************************************************* +** ** Function UWB_EnableConformanceTest ** ** Description This function is called to set MCTT/PCTT mode. @@ -808,6 +952,32 @@ void UWB_EnableConformanceTest(uint8_t enable); /******************************************************************************* ** +** Function UWB_SetDataXferCapMaxMsgSize +** +** Description This function is called to set max msg size supported for data Tranfer +** +** Parameters maxMsgSize - max msg size value +** +** Returns None +** +*******************************************************************************/ +void UWB_SetDataXferCapMaxMsgSize(uint16_t maxMsgSize); + +/******************************************************************************* +** +** Function UWB_SetDataXferCapMaxDataPktPayloadSize +** +** Description This function is called to set max data packet size at one time supported for data Tranfer +** +** Parameters maxDataPktPayloadSize - max data packet size value +** +** Returns None +** +*******************************************************************************/ +void UWB_SetDataXferCapMaxDataPktPayloadSize(uint16_t maxDataPktPayloadSize); + +/******************************************************************************* +** ** Function UWB_GetStatusName ** ** Description This function returns the status name. diff --git a/src/uwb/include/uwb_int.h b/src/uwb/include/uwb_int.h index 16d6af7..da2952e 100755 --- a/src/uwb/include/uwb_int.h +++ b/src/uwb/include/uwb_int.h @@ -34,6 +34,7 @@ /* UWB Timer events */ #define UWB_TTYPE_UCI_WAIT_RSP 0x00 #define UWB_WAIT_RSP_RAW_CMD 0x01 +#define UWB_TTYPE_UCI_WAIT_DATA_NTF 0x02 #define UWB_SAVED_HDR_SIZE 2 @@ -97,17 +98,48 @@ typedef struct { uint8_t device_state; uint16_t cmd_retry_count; + uint8_t invalid_len_cmd_retry_cnt; UWB_HDR* pLast_cmd_buf; + UWB_HDR* pLast_data_buf; + uint8_t data_pkt_retry_count; + bool is_credit_ntf_pending; + bool IsConformaceTestEnabled; /* MCTT mode indicator */ + + uint8_t data_credits; /* number of buffer credits */ + TIMER_LIST_ENT + uci_wait_credit_ntf_timer; /* Timer for waiting for uci credit ntf */ + uint16_t uci_credit_ntf_timeout; /* UCI credit timeout (in ms) */ + bool is_first_frgmnt_done; /*flag indicates recieved pbf=1 uci pkt before*/ } tUWB_CB; +typedef struct { + DATA_BUFFER_Q tx_data_pkt[5]; + uint8_t no_of_sessions; + uint32_t curr_session_id; + uint8_t curr_session_idx; + uint16_t max_data_pkt_payload_size; + uint16_t max_msg_size; +}tDATA_TX_CB; + +struct chained_uci_packet { + uint8_t buffer[UCI_MAX_FRAGMENT_BUFF_SIZE]; + uint8_t oid; + uint8_t gid; + uint16_t offset; +}; + +typedef struct chained_uci_packet chained_uci_packet; +extern chained_uci_packet chained_packet; + /***************************************************************************** ** EXTERNAL FUNCTION DECLARATIONS *****************************************************************************/ /* Global UWB data */ extern tUWB_CB uwb_cb; +extern tDATA_TX_CB data_tx_cb; /**************************************************************************** ** Internal uwb functions @@ -160,6 +192,15 @@ extern void uwb_ucif_proc_test_set_config_status(uint8_t* p_buf, uint16_t len); extern void uwb_ucif_proc_rf_test_data(tUWB_RESPONSE_EVT event, uint8_t* p_buf, uint16_t len); +/* APIs for handling data transfer */ +extern void uwb_ucif_send_data_frame(uint32_t session_id, uint8_t* p_addr, + uint16_t data_len, uint8_t* p_data); +extern void uwb_ucif_proc_data_credit_ntf(uint8_t* p_buf, uint16_t len); +extern void uwb_ucif_proc_data_transfer_status_ntf(uint8_t* p_buf, uint16_t len); +extern void uci_ucif_proc_data_packet(uint8_t* p_buf, uint16_t len); +extern void uwb_ucif_credit_ntf_timeout(void); +extern void uwb_ucif_send_data_frame(UWB_HDR* p_data); + /* From uwb_task.c */ extern uint32_t uwb_task(uint32_t param); void uwb_task_shutdown_uwbc(void); @@ -168,6 +209,7 @@ void uwb_task_shutdown_uwbc(void); void uwb_enabled(tUWB_STATUS uwb_status, UWB_HDR* p_init_rsp_msg); void uwb_set_state(tUWB_STATE uwb_state); void uwb_main_flush_cmd_queue(void); +void uwb_main_flush_data_queue(void); void uwb_main_handle_hal_evt(tUWB_HAL_EVT_MSG* p_msg); void uwb_gen_cleanup(void); diff --git a/src/uwb/uci/uci_hmsgs.cc b/src/uwb/uci/uci_hmsgs.cc index 247c79d..8e57954 100755 --- a/src/uwb/uci/uci_hmsgs.cc +++ b/src/uwb/uci/uci_hmsgs.cc @@ -204,6 +204,11 @@ uint8_t uci_snd_session_init_cmd(uint32_t session_id, uint8_t sessionType) { UINT32_TO_STREAM(pp, session_id); UINT8_TO_STREAM(pp, sessionType); + data_tx_cb.tx_data_pkt[data_tx_cb.no_of_sessions].session_id = session_id; + data_tx_cb.tx_data_pkt[data_tx_cb.no_of_sessions].credit_available = 1; + phUwb_GKI_init_q(&data_tx_cb.tx_data_pkt[data_tx_cb.no_of_sessions].tx_data_pkt_q); + data_tx_cb.no_of_sessions++; + uwb_ucif_send_cmd(p); return (UCI_STATUS_OK); } @@ -233,6 +238,14 @@ uint8_t uci_snd_session_deinit_cmd(uint32_t session_id) { UINT8_TO_STREAM(pp, UCI_MSG_SESSION_DEINIT_CMD_SIZE); UINT32_TO_STREAM(pp, session_id); + for(int i=0; i < data_tx_cb.no_of_sessions; i++) { + if(data_tx_cb.tx_data_pkt[i].session_id == session_id) + data_tx_cb.tx_data_pkt[i].session_id = 0; + } + if (data_tx_cb.no_of_sessions > 0) { + data_tx_cb.no_of_sessions--; + } + uwb_ucif_send_cmd(p); return (UCI_STATUS_OK); } @@ -526,7 +539,7 @@ uint8_t uci_snd_multicast_list_update_cmd(uint32_t session_id, uint8_t action, UINT8_TO_STREAM(pp, action); UINT8_TO_STREAM(pp, noOfControlees); for (uint8_t i = 0; i < noOfControlees; i++) { - UINT16_TO_BE_STREAM(pp, shortAddressList[i]); + UINT16_TO_STREAM(pp, shortAddressList[i]); UINT32_TO_STREAM(pp, subSessionIdList[i]); } @@ -536,6 +549,41 @@ uint8_t uci_snd_multicast_list_update_cmd(uint32_t session_id, uint8_t action, /******************************************************************************* ** +** Function uci_snd_configure_dt_anchor_for_rr_rdm_list_cmd +** +** Description compose and send SESSION_CONFIGURE_DT_ANCHOR_RR_RDM_LIST_CMD command +** +** Returns status +** +*******************************************************************************/ +uint8_t uci_snd_configure_dt_anchor_for_rr_rdm_list_cmd(uint32_t session_id, uint8_t rr_rdm_count, uint8_t length, uint8_t* data) { + UWB_HDR* p; + uint8_t* pp; + uint16_t total_size = 0; + + total_size = sizeof(session_id) + sizeof(rr_rdm_count) + length; + if ((p = UCI_GET_CMD_BUF(total_size)) == NULL) return (UCI_STATUS_FAILED); + p->event = BT_EVT_TO_UWB_UCI; + p->len = (uint16_t) (UCI_MSG_HDR_SIZE + total_size); + + p->offset = UCI_MSG_OFFSET_SIZE; + p->layer_specific = 0; + pp = (uint8_t*)(p + 1) + p->offset; + + UCI_MSG_BLD_HDR0(pp, UCI_MT_CMD, UCI_GID_SESSION_MANAGE); + UCI_MSG_BLD_HDR1(pp, UCI_MSG_SESSION_CONFIGURE_DT_ANCHOR_RR_RDM_LIST); + UINT8_TO_STREAM(pp, 0x00); + UINT8_TO_STREAM(pp, total_size); + UINT32_TO_STREAM(pp, session_id); + UINT8_TO_STREAM(pp, rr_rdm_count); + ARRAY_TO_STREAM(pp, data, length); + + uwb_ucif_send_cmd(p); + return (UCI_STATUS_OK); +} + +/******************************************************************************* +** ** Function uci_snd_set_country_code_cmd ** ** Description compose and send SET_COUNTRY_CODE_CMD command @@ -558,7 +606,7 @@ uint8_t uci_snd_set_country_code_cmd(uint8_t *country_code) { UCI_MSG_BLD_HDR1(pp, UCI_MSG_ANDROID_SET_COUNTRY_CODE); UINT8_TO_STREAM(pp, 0x00); UINT8_TO_STREAM(pp, UCI_MSG_ANDROID_SET_COUNTRY_CODE_CMD_SIZE); - ARRAY8_TO_STREAM(pp, country_code); + ARRAY8_TO_STREAM(pp, country_code, UCI_MSG_ANDROID_SET_COUNTRY_CODE_CMD_SIZE); uwb_ucif_send_cmd(p); return (UCI_STATUS_OK); @@ -850,3 +898,135 @@ uint8_t uci_snd_test_stop_session_cmd(void) { uwb_ucif_send_cmd(p); return (UCI_STATUS_OK); } + +/******************************************************************************* +** +** Function uci_send_data_frame +** +** Description compose and send data packets +** +** Returns status +** +*******************************************************************************/ +uint8_t uci_send_data_frame(uint32_t session_id, uint8_t* p_addr, uint8_t dest_end_point, uint8_t sequence_num, + uint16_t data_len, uint8_t* p_data) { + UCI_TRACE_I("uci_send_data_frame()"); + UWB_HDR* p; + uint8_t* pp; + uint16_t uci_pkt_len; + uint8_t pbf = 1; + tUWB_RESPONSE evt_data; + uint16_t max_supported_uci_payload; + uint16_t starIndex = 0; + bool isFirstSegment = true; + + int16_t total_size = sizeof(session_id) + EXTENDED_ADDRESS_LEN + data_len + + sizeof(dest_end_point) + sizeof(uint8_t) + + sizeof(data_len); + data_tx_cb.curr_session_id = session_id; + + for (int i = 0; i < data_tx_cb.no_of_sessions; i++) { + if (data_tx_cb.curr_session_id == data_tx_cb.tx_data_pkt[i].session_id) { + data_tx_cb.curr_session_idx = i; + } + } + + + if((p_data == nullptr) || (p_addr == nullptr)){ + evt_data.status = UCI_STATUS_FAILED; + (*uwb_cb.p_resp_cback)(UWB_SEND_DATA_STATUS_EVT, &evt_data); + return (UCI_STATUS_FAILED); + } + + max_supported_uci_payload = data_tx_cb.max_data_pkt_payload_size; + while (data_len > 0) { + if (total_size <= max_supported_uci_payload) { + pbf = 0; /* last fragment */ + uci_pkt_len = total_size; + } else { /* Handling PBF as per generic specification*/ + pbf = 1; + uci_pkt_len = max_supported_uci_payload; + } + if ((p = UCI_GET_CMD_BUF(uci_pkt_len)) == NULL) { + evt_data.status = UCI_STATUS_FAILED; + (*uwb_cb.p_resp_cback)(UWB_SEND_DATA_STATUS_EVT, &evt_data); + } + + p->event = BT_EVT_TO_UWB_UCI; + p->len = uci_pkt_len + UCI_MSG_HDR_SIZE; + pp = (uint8_t*)(p + 1) + p->offset; + + UCI_DATA_PBLD_HDR(pp, pbf, uci_pkt_len); + + if (isFirstSegment) { + UINT32_TO_STREAM(pp, session_id); + ARRAY_TO_STREAM(pp, p_addr, EXTENDED_ADDRESS_LEN); + UINT8_TO_STREAM(pp, dest_end_point); + UINT8_TO_STREAM(pp, sequence_num); + uci_pkt_len -= (sizeof(session_id) + EXTENDED_ADDRESS_LEN + sizeof(dest_end_point) + + sizeof(data_len) + sizeof(uint8_t)); + } + if(!isFirstSegment){ + uci_pkt_len -= sizeof(data_len); + } + UINT16_TO_STREAM(pp, uci_pkt_len); + + ARRAY_TO_STREAM(pp, (p_data + starIndex), uci_pkt_len); + starIndex += uci_pkt_len; + data_len -= uci_pkt_len; + total_size = sizeof(data_len) + data_len; + + uwb_ucif_send_data_frame(p); + isFirstSegment = false; + } + evt_data.status = UCI_STATUS_OK; + (*uwb_cb.p_resp_cback)(UWB_SEND_DATA_STATUS_EVT, &evt_data); + return (UCI_STATUS_OK); +} + +/******************************************************************************* +** +** Function uci_send_range_round_index_update_cmd +** +** Description compose and send range round index update command +** +** Returns status +** +*******************************************************************************/ +uint8_t uci_send_range_round_index_update_cmd(uint8_t dlTdoaRole, uint32_t session_id, uint8_t number_of_active_rngIndex, + uint8_t rng_round_index_len, uint8_t* p_rng_round_index) { + UWB_HDR* p; + uint8_t* pp; + uint16_t total_size; + uint8_t oid; + + total_size = sizeof(session_id) + sizeof(number_of_active_rngIndex) + rng_round_index_len; + + if ((p = UCI_GET_CMD_BUF(total_size)) == NULL) return (UCI_STATUS_FAILED); + + p->event = BT_EVT_TO_UWB_UCI; + + p->len = (uint16_t) (UCI_MSG_HDR_SIZE + total_size); + + p->offset = UCI_MSG_OFFSET_SIZE; + p->layer_specific = 0; + pp = (uint8_t*)(p + 1) + p->offset; + + if(dlTdoaRole == 0x00) { + oid = UCI_MSG_SESSION_UPDATE_ACTIVE_ROUNDS_OF_DT_ANCHOR; + } else { + oid = UCI_MSG_SESSION_UPDATE_ACTIVE_ROUNDS_OF_DT_TAG; + } + + UCI_MSG_BLD_HDR0(pp, UCI_MT_CMD, UCI_GID_SESSION_MANAGE); + UCI_MSG_BLD_HDR1(pp, oid); + UINT8_TO_STREAM(pp, 0x00); + UINT8_TO_STREAM(pp, total_size); + + UINT32_TO_STREAM(pp, session_id); + UINT8_TO_STREAM(pp, number_of_active_rngIndex); + ARRAY_TO_STREAM(pp, p_rng_round_index, rng_round_index_len); + + uwb_ucif_send_cmd(p); + return (UCI_STATUS_OK); +} diff --git a/src/uwb/uci/uci_hrcv.cc b/src/uwb/uci/uci_hrcv.cc index e8ff495..69c5591 100755 --- a/src/uwb/uci/uci_hrcv.cc +++ b/src/uwb/uci/uci_hrcv.cc @@ -1,7 +1,7 @@ /* * Copyright (C) 2021 The Android Open Source Project * - * Copyright 2021 NXP. + * Copyright 2021-2022 NXP. * * Licensed under the Apache License, Version 2.0 (the "License"); * You may not use this file except in compliance with the License. @@ -131,6 +131,14 @@ void uci_proc_session_management_rsp(uint8_t op_code, uint8_t* p_buf, uwb_ucif_session_management_status(UWB_SESSION_UPDATE_MULTICAST_LIST_REVT, p_buf, len); break; + case UCI_MSG_SESSION_CONFIGURE_DT_ANCHOR_RR_RDM_LIST: + uwb_ucif_session_management_status(UWB_SESSION_CONFIGURE_DT_ANCHOR_RR_RDM_REVT, p_buf, len); + break; + case UCI_MSG_SESSION_UPDATE_ACTIVE_ROUNDS_OF_DT_ANCHOR: + case UCI_MSG_SESSION_UPDATE_ACTIVE_ROUNDS_OF_DT_TAG: + uwb_ucif_session_management_status(UWB_SESSION_ACTIVE_ROUNDS_INDEX_UPDATE_REVT, p_buf, + len); + break; default: UCI_TRACE_E("%s: unknown opcode:0x%x", __func__, op_code); break; @@ -194,6 +202,12 @@ void uci_proc_session_management_ntf(uint8_t op_code, uint8_t* p_buf, case UCI_MSG_SESSION_UPDATE_CONTROLLER_MULTICAST_LIST: uwb_ucif_proc_multicast_list_update_ntf(p_buf, len); break; + case UCI_MSG_DATA_CREDIT_NTF: + uwb_ucif_proc_data_credit_ntf(p_buf, len); + break; + case UCI_MSG_DATA_TRANSFER_STATUS_NTF: + uwb_ucif_proc_data_transfer_status_ntf(p_buf, len); + break; default: UCI_TRACE_E("%s: unknown opcode:0x%x", __func__, op_code); break; @@ -293,9 +307,9 @@ void uci_proc_vendor_specific_ntf(uint8_t gid, uint8_t* p_buf, uint16_t len) { if (uwb_cb.p_resp_cback == NULL) { UCI_TRACE_E("ext response callback is null"); } else { - evt_data.sVendor_specific_ntf.len = len; - if (evt_data.sVendor_specific_ntf.len > 0) { - STREAM_TO_ARRAY(evt_data.sVendor_specific_ntf.data, p_buf, + evt_data.vendor_specific_ntf.len = len; + if (evt_data.vendor_specific_ntf.len > 0) { + STREAM_TO_ARRAY(evt_data.vendor_specific_ntf.data, p_buf, len); } (*uwb_cb.p_resp_cback)(UWB_VENDOR_SPECIFIC_UCI_NTF_EVT, &evt_data); diff --git a/src/uwb/uwb/uwb_main.cc b/src/uwb/uwb/uwb_main.cc index 51b9471..b79de22 100755 --- a/src/uwb/uwb/uwb_main.cc +++ b/src/uwb/uwb/uwb_main.cc @@ -1,7 +1,7 @@ /* * Copyright (C) 2021 The Android Open Source Project * - * Copyright 2021 NXP. + * Copyright 2021-2022 NXP. * * Licensed under the Apache License, Version 2.0 (the "License"); * You may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ ** Declarations ****************************************************************************/ tUWB_CB uwb_cb; +tDATA_TX_CB data_tx_cb; /******************************************************************************* ** @@ -159,6 +160,7 @@ void uwb_set_state(tUWB_STATE uwb_state) { void uwb_gen_cleanup(void) { /* clear any pending CMD/RSP */ uwb_main_flush_cmd_queue(); + uwb_main_flush_data_queue(); } /******************************************************************************* @@ -192,6 +194,10 @@ void uwb_main_handle_hal_evt(tUWB_HAL_EVT_MSG* p_msg) { } break; + case HAL_UWB_INIT_CPLT_EVT: /* only for failure case */ + uwb_set_state(UWB_STATE_NONE); + break; + case HAL_UWB_ERROR_EVT: switch (p_msg->status) { case HAL_UWB_STATUS_ERR_TRANSPORT: @@ -247,6 +253,34 @@ void uwb_main_flush_cmd_queue(void) { /******************************************************************************* ** +** Function uwb_main_flush_data_queue +** +** Description This function is called when setting power off sleep state. +** +** Returns void +** +*******************************************************************************/ +void uwb_main_flush_data_queue(void) { + UWB_HDR* p_msg; + + UCI_TRACE_I(__func__); + + /* Stop command-pending timer */ + uwb_stop_quick_timer(&uwb_cb.uci_wait_credit_ntf_timer); + uwb_cb.is_credit_ntf_pending = false; + uwb_cb.data_pkt_retry_count = 0; + + /* dequeue and free buffer */ + for (int i = 0; i < data_tx_cb.no_of_sessions; i++) { + while ((p_msg = (UWB_HDR*)phUwb_GKI_dequeue(&data_tx_cb.tx_data_pkt[i].tx_data_pkt_q)) != + NULL) { + phUwb_GKI_freebuf(p_msg); + } + } +} + +/******************************************************************************* +** ** Function uwb_main_post_hal_evt ** ** Description This function posts HAL event to UWB_TASK @@ -300,6 +334,10 @@ static void uwb_main_hal_cback(uint8_t event, tUWB_STATUS status) { } break; + case HAL_UWB_INIT_CPLT_EVT: + UCI_TRACE_D("uwb_main_hal_cback HAL Init complete %x", event); + break; + case HAL_UWB_CLOSE_CPLT_EVT: case HAL_UWB_ERROR_EVT: uwb_main_post_hal_evt(event, status); @@ -435,9 +473,20 @@ void UWB_Init(tHAL_UWB_CONTEXT* p_hal_entry_cntxt) { ((UWB_CMD_CMPL_TIMEOUT * QUICK_TIMER_TICKS_PER_SEC) / 1000); uwb_cb.pLast_cmd_buf = NULL; uwb_cb.is_resp_pending = false; + uwb_cb.is_credit_ntf_pending = false; + data_tx_cb.max_data_pkt_payload_size = 255; + data_tx_cb.max_msg_size = 255; + data_tx_cb.no_of_sessions = 0; uwb_cb.cmd_retry_count = 0; uwb_cb.is_recovery_in_progress = false; uwb_cb.IsConformaceTestEnabled = false; + uwb_cb.uci_credit_ntf_timeout = ((UWB_CMD_CMPL_TIMEOUT * QUICK_TIMER_TICKS_PER_SEC) / 1000); // currently used same timeout value as cmd timeout + uwb_cb.data_credits = 1; + /* initialize the segment handling context */ + chained_packet.offset = 0; + uwb_cb.is_first_frgmnt_done = false; + chained_packet.oid = 0xFF; + chained_packet.gid = 0xFF; } /******************************************************************************* @@ -954,6 +1003,61 @@ tUWB_STATUS UWB_SendRawCommand(UWB_HDR* p_data, tUWB_RAW_CBACK* p_cback) { /******************************************************************************* ** +** Function UWB_SendData +** +** Description This function is called to send the data packet over UWB. +** +** Parameters p_data - The data buffer +** +** Returns tUWB_STATUS +** +*******************************************************************************/ +tUWB_STATUS UWB_SendData(uint32_t session_id, uint8_t* p_addr, + uint8_t dest_end_point, uint8_t sequence_num, + uint16_t data_len, uint8_t* p_data) { + return uci_send_data_frame(session_id, p_addr, dest_end_point, sequence_num, data_len, p_data); +} + +/******************************************************************************* +** +** Function UWB_UpdateRangingRoundIndex +** +** Description This function is called to Update Active Ranging Index. +** +** Parameter dlTdoaRole - 0x00(Anchor) or 0x01(Tag) +** session_id - Session id To which Rangng index need to update +** number_of_active_rngIndex - NUmber Of Ranging index to be updated +** rng_round_index_len - Range Round Index Len +** p_rng_round_index - pointer to Ranging Index buffer +** +** Returns tUWB_STATUS +** +*******************************************************************************/ +tUWB_STATUS UWB_UpdateRangingRoundIndex(uint8_t dlTdoaRole, uint32_t session_id, uint8_t number_of_rng_index, + uint8_t rng_round_index_len, uint8_t* p_rng_round_index) { + return uci_send_range_round_index_update_cmd(dlTdoaRole, session_id, number_of_rng_index, rng_round_index_len, p_rng_round_index); +} + +/******************************************************************************* +** +** Function UWB_ConfigureDTAnchorForRrRdmList +** +** Description This function is called to Configure DT anchor RR RDM List. +** +** Parameter session_id - Session id To which Rangng index need to update +** rr_rdm_count - Number Of rr_rdm +** length - length of data buffer +** p_data - The data buffer +** +** Returns tUWB_STATUS +** +*******************************************************************************/ +tUWB_STATUS UWB_ConfigureDTAnchorForRrRdmList(uint32_t session_id, uint8_t rr_rdm_count, uint8_t length, uint8_t* p_data) { + return uci_snd_configure_dt_anchor_for_rr_rdm_list_cmd(session_id, rr_rdm_count, length, p_data); +} + +/******************************************************************************* +** ** Function UWB_EnableConformanceTest ** ** Description This function is called to set MCTT/PCTT mode. @@ -970,6 +1074,41 @@ void UWB_EnableConformanceTest(uint8_t enable) { /******************************************************************************* ** +** Function UWB_SetDataXferCapMaxMsgSize +** +** Description This function is called to set max msg size supported for data Tranfer +** +** Parameters maxMsgSize - max msg size value +** +** Returns None +** +*******************************************************************************/ +void UWB_SetDataXferCapMaxMsgSize(uint16_t maxMsgSize){ + data_tx_cb.max_msg_size = maxMsgSize; + UCI_TRACE_D( + "UWB_SetDataXferCapMaxMsgSize %d ",data_tx_cb.max_msg_size); +} + +/******************************************************************************* +** +** Function UWB_SetDataXferCapMaxDataPktPayloadSize +** +** Description This function is called to set max data packet size at one time supported for data Tranfer +** +** Parameters maxDataPktPayloadSize - max data packet size value +** +** Returns None +** +*******************************************************************************/ +void UWB_SetDataXferCapMaxDataPktPayloadSize(uint16_t maxDataPktPayloadSize) +{ + data_tx_cb.max_data_pkt_payload_size = maxDataPktPayloadSize; + UCI_TRACE_D( + "UWB_SetDataXferCapMaxDataPktPayloadSize %d ",data_tx_cb.max_data_pkt_payload_size); +} + +/******************************************************************************* +** ** Function UWB_GetStatusName ** ** Description This function returns the status name. diff --git a/src/uwb/uwb/uwb_ucif.cc b/src/uwb/uwb/uwb_ucif.cc index 327aa19..04cbd77 100755 --- a/src/uwb/uwb/uwb_ucif.cc +++ b/src/uwb/uwb/uwb_ucif.cc @@ -1,7 +1,7 @@ /* * Copyright (C) 2021 The Android Open Source Project * - * Copyright 2021 NXP. + * Copyright 2021-2022 NXP. * * Licensed under the Apache License, Version 2.0 (the "License"); * You may not use this file except in compliance with the License. @@ -34,6 +34,22 @@ #include "uwb_target.h" #define NORMAL_MODE_LENGTH_OFFSET 0x03 +#define DATA_PACKET_LEN_SHIFT 0x08 +#define TDOA_TX_TIMESTAMP_OFFSET 0x00FF +#define TDOA_TX_TIMESTAMP_OFFSET_MASK 0x06 +#define TDOA_RX_TIMESTAMP_OFFSET 0x00FF +#define TDOA_RX_TIMESTAMP_OFFSET_MASK 0x18 +#define ULTDOA_RX_TIMESTAMP_OFFSET 0xF0 +#define ULTDOA_RX_TIMESTAMP_OFFSET_MASK 0x30 +#define ULTDOA_DEVICE_ID_OFFSET 0x0F +#define ULTDOA_DEVICE_ID_OFFSET_MASK 0X03 +#define ULTDOA_TX_TIMESTAMP_OFFSET 0x0F +#define ULTDOA_TX_TIMESTAMP_OFFSET_MASK 0x0C +#define TDOA_ANCHOR_LOC_OFFSET 0x00FF +#define TDOA_ANCHOR_LOC_OFFSET_MASK 0x60 +#define TDOA_ACTIVE_RR_OFFSET 0x0FF0 +#define TDOA_ACTIVE_RR_OFFSET_MASK 0x0780 + #define MAC_SHORT_ADD_LEN 2 #define MAC_EXT_ADD_LEN 8 #define PDOA_LEN 4 @@ -41,29 +57,48 @@ #define AOA_DEST_LEN 4 #define CONFIG_TLV_OFFSET 2 #define TWO_WAY_MEASUREMENT_LENGTH 31 +#define ULTDOA_MEASUREMENT_LENGTH 45 #define ONE_WAY_MEASUREMENT_LENGTH 36 #define RANGING_DATA_LENGTH 25 #define VENDOR_SPEC_INFO_LEN 2 +#define OWR_WITH_AOA_MEASUREMENT_LENGTH 11 +#define TDOA_TIMESTAMP_LEN_40BITS 5 +#define TDOA_TIMESTAMP_LEN_64BITS 8 +#define TDOA_ANCHOR_LOC_LEN_10BYTES 10 +#define TDOA_ANCHOR_LOC_LEN_12BYTES 12 +#define TDOA_TX_TIMESTAMP_40BITS 0 +#define TDOA_TX_TIMESTAMP_64BITS 2 +#define TDOA_RX_TIMESTAMP_40BITS 0 +#define TDOA_RX_TIMESTAMP_64BITS 8 +#define ULTDOA_TIMESTAMP_LEN 0 +#define ULTDOA_TIMESTAMP_LEN_40BITS 5 +#define ULTDOA_TIMESTAMP_LEN_64BITS 8 +#define ULTDOA_DEVICE_ID_LEN 0 +#define ULTDOA_DEVICE_ID_LEN_16BITS 2 +#define ULTDOA_DEVICE_ID_LEN_32BITS 4 +#define ULTDOA_DEVICE_ID_LEN_64BITS 8 +#define ULTDOA_RX_TIMESTAMP_40BITS 0 +#define ULTDOA_RX_TIMESTAMP_64BITS 20 +#define ULTDOA_DEVICE_ID_PRESCENCE 0 +#define ULTDOA_DEVICE_ID_16BITS 1 +#define ULTDOA_DEVICE_ID_32BITS 2 +#define ULTDOA_DEVICE_ID_64BITS 3 +#define ULTDOA_TX_TIMESTAMP_PRESENCE 0 +#define ULTDOA_TX_TIMESTAMP_40BITS 8 +#define ULTDOA_TX_TIMESTAMP_64BITS 4 +#define TDOA_ANCHOR_LOC_NOT_INCLUDED 0 +#define TDOA_ANCHOR_LOC_NOT_INCLUDED 0 +#define TDOA_ANCHOR_LOC_IN_RELATIVE_SYSTEM 0x40 +#define TDOA_ANCHOR_LOC_IN_WGS84_SYSTEM 0x20 +#define TDOA_ACTIVE_RR_INDEX_POSITION 7 uint8_t last_cmd_buff[UCI_MAX_PAYLOAD_SIZE]; uint8_t last_data_buff[4096]; -static uint8_t device_info_buffer[MAX_NUM_OF_TDOA_MEASURES] - [UCI_MAX_PAYLOAD_SIZE]; -static uint8_t blink_payload_buffer[MAX_NUM_OF_TDOA_MEASURES] - [UCI_MAX_PAYLOAD_SIZE]; static uint8_t range_data_ntf_buffer[2048]; static uint8_t range_data_ntf_len =0; -struct chained_uci_packet { - uint8_t buffer[4192]; - uint8_t oid; - uint8_t gid; - uint16_t offset; - uint8_t is_first_frgmnt_done; -}; - -typedef struct chained_uci_packet chained_uci_packet; +chained_uci_packet chained_packet; /******************************************************************************* ** @@ -113,7 +148,6 @@ void uwb_ucif_cmd_timeout(void) { uwb_cb.cmd_retry_count++; } else { uwb_ucif_event_status(UWB_UWBS_RESP_TIMEOUT_REVT, UWB_STATUS_FAILED); - uwb_ucif_uwb_recovery(); } } @@ -141,6 +175,28 @@ void uwb_ucif_retransmit_cmd(UWB_HDR* p_buf) { /******************************************************************************* ** + ** Function uwb_ucif_retransmit_data + ** + ** Description Retransmission of last data packet + ** + ** Returns void + ** + *******************************************************************************/ +void uwb_ucif_retransmit_data(UWB_HDR* p_buf) { + UCI_TRACE_I("uwb_ucif_retransmit_data"); + if (p_buf == NULL) { + UCI_TRACE_E("uwb_ucif_retransmit_data: p_data is NULL"); + return; + } + HAL_RE_WRITE(p_buf); + + /* start the credit timeout timer */ + uwb_start_quick_timer(&uwb_cb.uci_wait_credit_ntf_timer, + (uint16_t)(UWB_TTYPE_UCI_WAIT_DATA_NTF),uwb_cb.uci_credit_ntf_timeout); +} + +/******************************************************************************* + ** ** Function uwb_ucif_check_cmd_queue ** ** Description Send UCI command to the transport @@ -199,7 +255,7 @@ void uwb_ucif_check_cmd_queue(UWB_HDR* p_buf) { uwb_cb.rawCmdCbflag = true; } - /* Indicate command is pending */ + /* Indicate command is pending */ uwb_cb.uci_cmd_window--; uwb_cb.is_resp_pending = true; uwb_cb.cmd_retry_count = 0; @@ -241,6 +297,287 @@ void uwb_ucif_send_cmd(UWB_HDR* p_buf) { /******************************************************************************* ** + ** Function uwb_ucif_credit_ntf_timeout + ** + ** Description Handle a credit ntf timeout + ** + ** Returns void + ** + *******************************************************************************/ +void uwb_ucif_credit_ntf_timeout(void){ + UCI_TRACE_I("uwb_ucif_credit_ntf_timeout"); +/* if enabling UWB, notify upper layer of failure */ + if (uwb_cb.is_credit_ntf_pending && (uwb_cb.data_pkt_retry_count < UCI_CMD_MAX_RETRY_COUNT)) { + uwb_stop_quick_timer(&uwb_cb.uci_wait_credit_ntf_timer); /*stop the pending timer */ + uwb_ucif_retransmit_data(uwb_cb.pLast_data_buf); + uwb_cb.data_pkt_retry_count++; + } else { + uwb_cb.is_credit_ntf_pending = false; + uwb_cb.data_pkt_retry_count = 0; + uwb_ucif_event_status(UWB_UWBS_RESP_TIMEOUT_REVT, UWB_STATUS_FAILED); + uwb_ucif_uwb_recovery(); + } +} + +/******************************************************************************* +** +** Function uwb_ucif_send_data_frame +** +** Description This function is called to send UCI data packet to UWB subsystem as credits are available +** +** Returns void +** +*******************************************************************************/ + +void uwb_ucif_send_data_frame(UWB_HDR* p_data){ + UCI_TRACE_I("uwb_ucif_send_data_frame()"); + uint8_t* ps; + uint8_t* pTemp; + + if (uwb_cb.uwb_state == UWB_STATE_W4_HAL_CLOSE || + uwb_cb.uwb_state == UWB_STATE_NONE) { + UCI_TRACE_E("%s: HAL is not initialized", __func__); + return; + } + + /* If no credit available */ + /* then enqueue this command */ + if (p_data) { + if ((data_tx_cb.tx_data_pkt[data_tx_cb.curr_session_idx].credit_available == + 0) || + (data_tx_cb.tx_data_pkt[data_tx_cb.curr_session_idx] + .tx_data_pkt_q.count) || + (uwb_cb.uci_cmd_window == 0)) { + UCI_TRACE_D( + "Enqueuing p_Data"); + phUwb_GKI_enqueue( + &data_tx_cb.tx_data_pkt[data_tx_cb.curr_session_idx].tx_data_pkt_q, + p_data); + if (p_data != NULL) { + UCI_TRACE_D( + "uwb_ucif_send_data_frame : making p_data NULL."); + p_data = NULL; + } + } + } + + if ((data_tx_cb.tx_data_pkt[data_tx_cb.curr_session_idx].credit_available) && (uwb_cb.uci_cmd_window > 0)) { + if (!p_data) { + UCI_TRACE_D( + "Dequeueing p_Data"); + p_data = (UWB_HDR*)phUwb_GKI_dequeue( + &data_tx_cb.tx_data_pkt[data_tx_cb.curr_session_idx].tx_data_pkt_q); + } + + if (p_data) { + /* save the message header to double check the response */ + ps = (uint8_t*)(p_data + 1) + p_data->offset; + /* copying command to temp buff for retransmission */ + uwb_cb.pLast_data_buf = (UWB_HDR*)last_data_buff; + uwb_cb.pLast_data_buf->offset = p_data->offset; + pTemp = + (uint8_t*)(uwb_cb.pLast_data_buf + 1) + uwb_cb.pLast_data_buf->offset; + uwb_cb.pLast_data_buf->len = p_data->len; + memcpy(pTemp, ps, p_data->len); + /* decrease the credits */ + data_tx_cb.tx_data_pkt[data_tx_cb.curr_session_idx].credit_available = 0; + uwb_cb.is_credit_ntf_pending = true; + uwb_cb.uci_cmd_window--; + /* send to HAL */ + HAL_WRITE(p_data); + /* start the credit timeout timer */ + uwb_start_quick_timer(&uwb_cb.uci_wait_credit_ntf_timer, + (uint16_t)(UWB_TTYPE_UCI_WAIT_DATA_NTF), + uwb_cb.uci_credit_ntf_timeout); + } + } +} + + +/******************************************************************************* +** +** Function uwb_ucif_proc_data_credit_ntf +** +** Description This function is called to process credits ntf +** +** Returns void +** +*******************************************************************************/ +void uwb_ucif_proc_data_credit_ntf(uint8_t* p_buf, uint16_t len) { + uint32_t session_id; + + /* Stop pending credit ntf timer */ + if (uwb_cb.is_credit_ntf_pending) { + uwb_stop_quick_timer(&uwb_cb.uci_wait_credit_ntf_timer); + uwb_cb.is_credit_ntf_pending = false; + uwb_cb.data_pkt_retry_count = 0; + uwb_cb.invalid_len_cmd_retry_cnt = 0; + uwb_cb.uci_cmd_window++; + } + + if (len != 0) { + STREAM_TO_UINT32(session_id, p_buf); + STREAM_TO_UINT8(uwb_cb.data_credits, p_buf); + for (int i = 0; i < data_tx_cb.no_of_sessions; i++) { + if (session_id == data_tx_cb.tx_data_pkt[i].session_id) { + data_tx_cb.curr_session_idx = i; + } + } + data_tx_cb.tx_data_pkt[data_tx_cb.curr_session_idx].credit_available = + uwb_cb.data_credits; + if ((data_tx_cb.tx_data_pkt[data_tx_cb.curr_session_idx] + .credit_available) && + (data_tx_cb.tx_data_pkt[data_tx_cb.curr_session_idx] + .tx_data_pkt_q.count)) { + uwb_ucif_send_data_frame(NULL); + } else { + for (int i = 0; i < data_tx_cb.no_of_sessions; i++) { + if ((data_tx_cb.tx_data_pkt[i].credit_available) && + (data_tx_cb.tx_data_pkt[i].tx_data_pkt_q.count)) { + data_tx_cb.curr_session_idx = i; + break; + } + } + uwb_ucif_send_data_frame(NULL); + } + } +} + +/******************************************************************************* +** +** Function uwb_ucif_proc_data_transfer_status_ntf +** +** Description This function is called to process data transfer status over UWB +** +** Returns void +** +*******************************************************************************/ +void uwb_ucif_proc_data_transfer_status_ntf(uint8_t* p_buf, uint16_t len){ + tUWB_DATA_TRANSFER_STATUS_NTF_REVT dataXferStatus; + tUWB_RESPONSE uwb_response; + if (len == 0) { + UCI_TRACE_E("%s: len is zero", __func__); + return; + } + if (uwb_cb.is_credit_ntf_pending == true) { + uwb_stop_quick_timer(&uwb_cb.uci_wait_credit_ntf_timer); + uwb_cb.is_credit_ntf_pending = false; + uwb_cb.data_pkt_retry_count = 0; + uwb_cb.uci_cmd_window++; + } + + if (uwb_cb.p_resp_cback == NULL) { + UCI_TRACE_E("%s: response callback is null", __func__); + return; + } + memset(&dataXferStatus, 0, sizeof(tUWB_DATA_TRANSFER_STATUS_NTF_REVT )); + STREAM_TO_UINT32(dataXferStatus.session_id, p_buf); + STREAM_TO_UINT8(dataXferStatus.sequence_num, p_buf); + STREAM_TO_UINT8(dataXferStatus.status, p_buf); + + uwb_response.sData_xfer_status = dataXferStatus; + + for (int i = 0; i < data_tx_cb.no_of_sessions; i++) { + if (dataXferStatus.session_id == data_tx_cb.tx_data_pkt[i].session_id) { + data_tx_cb.tx_data_pkt[i].credit_available = 1; + data_tx_cb.curr_session_idx = i; + break; + } + } + + (*uwb_cb.p_resp_cback)(UWB_DATA_TRANSFER_STATUS_NTF_REVT, &uwb_response); + + if ((data_tx_cb.tx_data_pkt[data_tx_cb.curr_session_idx].credit_available) && + (data_tx_cb.tx_data_pkt[data_tx_cb.curr_session_idx] + .tx_data_pkt_q.count)) { + uwb_ucif_send_data_frame(NULL); + } +} + +/******************************************************************************* + ** + ** Function uci_ucif_proc_data_packet() + ** + ** Description This function is called to report received data + ** + ** Returns void + ** + *******************************************************************************/ +void uci_ucif_proc_data_packet(uint8_t* p_buf, uint16_t len) { + tUWB_RESPONSE evt_data; + + UCI_TRACE_D("%s: len = %d", __func__, len); + + if (uwb_cb.p_resp_cback == NULL) { + UCI_TRACE_E("%s: response callback is null", __func__); + return; + } + STREAM_TO_UINT32(evt_data.sRcvd_data.session_id, p_buf); + STREAM_TO_UINT8(evt_data.sRcvd_data.status, p_buf); + STREAM_TO_UINT32(evt_data.sRcvd_data.sequence_num, p_buf); + STREAM_TO_ARRAY(evt_data.sRcvd_data.address, p_buf, EXTENDED_ADDRESS_LEN); + STREAM_TO_UINT8(evt_data.sRcvd_data.source_end_point, p_buf); + STREAM_TO_UINT8(evt_data.sRcvd_data.dest_end_point, p_buf); + STREAM_TO_UINT16(evt_data.sRcvd_data.data_len, p_buf); + STREAM_TO_ARRAY(evt_data.sRcvd_data.data, p_buf, evt_data.sRcvd_data.data_len); + + (*uwb_cb.p_resp_cback)(UWB_DATA_RECV_REVT, &evt_data); +} + +void chain_data_packet(UWB_HDR* p_msg) { + uint16_t payload_length = 0; + uint8_t mt, pbf, dpf, *p, *pp; + p = (uint8_t*)(p_msg + 1) + p_msg->offset; + pp = p; + if ((p != NULL) & (pp != NULL)) { + UCI_MSG_PRS_HDR0(pp, mt, pbf, dpf); + pp = pp + 3; + payload_length = p[NORMAL_MODE_LENGTH_OFFSET]; + payload_length = (uint16_t)((payload_length << DATA_PACKET_LEN_SHIFT) | + p[NORMAL_MODE_LENGTH_OFFSET - 1]); + if (!uwb_cb.IsConformaceTestEnabled) { + if (pbf) { + if (!uwb_cb.is_first_frgmnt_done) { + chained_packet.gid = dpf; + memcpy(&chained_packet.buffer[chained_packet.offset], p, p_msg->len); + chained_packet.offset = p_msg->len; + uwb_cb.is_first_frgmnt_done = true; + } else { + memcpy(&chained_packet.buffer[chained_packet.offset], pp, + payload_length); + chained_packet.offset = + (uint16_t)(chained_packet.offset + payload_length); + } + } else { + if (uwb_cb.is_first_frgmnt_done) { + memcpy(&chained_packet.buffer[chained_packet.offset], pp, + payload_length); // Append only payload to chained packet + chained_packet.offset = + (uint16_t)(chained_packet.offset + payload_length); + + // Update P & PP + p = &chained_packet + .buffer[0]; // p -> points to complete UCI packet + pp = p + 2; // Skip oid & gid bytes + payload_length = + (uint16_t)(chained_packet.offset - UCI_MSG_HDR_SIZE); + UINT16_TO_STREAM(pp, + payload_length); // Update overall payload length + // into the chained packet + } + // Clear flags + chained_packet.offset = 0; + uwb_cb.is_first_frgmnt_done = false; + chained_packet.oid = 0xFF; + chained_packet.gid = 0xFF; + } + } + uci_ucif_proc_data_packet(pp, payload_length); + } +} + +/******************************************************************************* + ** ** Function uwb_ucif_process_event ** ** Description This function is called to process the @@ -252,71 +589,74 @@ void uwb_ucif_send_cmd(UWB_HDR* p_buf) { bool uwb_ucif_process_event(UWB_HDR* p_msg) { uint8_t mt, pbf, gid, oid, *p, *pp; bool free = true; - uint16_t payload_length; + uint16_t payload_length =0; uint8_t *p_old, old_gid, old_oid, old_mt; - static chained_uci_packet chained_packet; + uint8_t is_extended_length = 0; p = (uint8_t*)(p_msg + 1) + p_msg->offset; pp = p; if ((p != NULL) & (pp != NULL)) { UCI_MSG_PRS_HDR0(pp, mt, pbf, gid); - UCI_MSG_PRS_HDR1(pp, oid); - pp = pp + 2; // Skip payload fields - UCI_TRACE_E("uwb_ucif_process_event enter gid:0x%x status:0x%x", p[0], + if (mt == UCI_MT_DATA) { + chain_data_packet(p_msg); + } else { + UCI_MSG_PRS_HDR1(pp, oid); + pp = pp + 2; // Skip payload fields + UCI_TRACE_I("uwb_ucif_process_event enter gid:0x%x status:0x%x", p[0], pp[0]); - payload_length = p[NORMAL_MODE_LENGTH_OFFSET]; - - if (!uwb_cb.IsConformaceTestEnabled) { - if (pbf) { - if (!chained_packet.is_first_frgmnt_done) { - chained_packet.oid = oid; - chained_packet.gid = gid; - memcpy(&chained_packet.buffer[chained_packet.offset], p, - p_msg->len); // Copy first fragment(uci packet with header)(p) - chained_packet.offset = p_msg->len; - chained_packet.is_first_frgmnt_done = true; - } else { - // if first fragment is copied, then copy only uci payload(pp) for - // subsequent fragments - if ((chained_packet.oid == oid) && (chained_packet.gid == gid)) { - memcpy(&chained_packet.buffer[chained_packet.offset], pp, - payload_length); - chained_packet.offset = - (uint16_t)(chained_packet.offset + payload_length); + payload_length = p[NORMAL_MODE_LENGTH_OFFSET]; + if (!uwb_cb.IsConformaceTestEnabled) { + if (pbf) { + if (!uwb_cb.is_first_frgmnt_done) { + chained_packet.oid = oid; + chained_packet.gid = gid; + memcpy(&chained_packet.buffer[chained_packet.offset], p, + p_msg->len); // Copy first fragment(uci packet with header)(p) + chained_packet.offset = p_msg->len; + uwb_cb.is_first_frgmnt_done = true; } else { - UCI_TRACE_E( - "uwb_ucif_process_event: unexpected chain packet: " - "chained_packed_gid: 0x%x, chained_packet_oid=0x%x, received " - "packet gid:0x%x, recived packet oid:0x%x", - chained_packet.gid, chained_packet.oid, gid, oid); + // if first fragment is copied, then copy only uci payload(pp) for + // subsequent fragments + if ((chained_packet.oid == oid) && (chained_packet.gid == gid)) { + memcpy(&chained_packet.buffer[chained_packet.offset], pp, + payload_length); + chained_packet.offset = + (uint16_t)(chained_packet.offset + payload_length); + } else { + UCI_TRACE_D( + "uwb_ucif_process_event: unexpected chain packet: " + "chained_packed_gid: 0x%x, chained_packet_oid=0x%x, received " + "packet gid:0x%x, recived packet oid:0x%x", + chained_packet.gid, chained_packet.oid, gid, oid); + } } - } - return (free); - } else { - if (chained_packet.is_first_frgmnt_done) { - if ((chained_packet.oid == oid) && (chained_packet.gid == gid)) { - memcpy(&chained_packet.buffer[chained_packet.offset], pp, - payload_length); // Append only payload to chained packet - chained_packet.offset = - (uint16_t)(chained_packet.offset + payload_length); - - // Update P & PP - p = &chained_packet - .buffer[0]; // p -> points to complete UCI packet - pp = p + 2; // Skip oid & gid bytes - payload_length = - (uint16_t)(chained_packet.offset - UCI_MSG_HDR_SIZE); - UINT16_TO_STREAM(pp, - payload_length); // Update overall payload length - // into the chained packet - - // Clear flags - chained_packet.offset = 0; - chained_packet.is_first_frgmnt_done = false; - chained_packet.oid = 0xFF; - chained_packet.gid = 0xFF; + return (free); + } else { + if (uwb_cb.is_first_frgmnt_done) { + if ((chained_packet.oid == oid) && (chained_packet.gid == gid)) { + memcpy(&chained_packet.buffer[chained_packet.offset], pp, + payload_length); // Append only payload to chained packet + chained_packet.offset = + (uint16_t)(chained_packet.offset + payload_length); + + // Update P & PP + p = &chained_packet + .buffer[0]; // p -> points to complete UCI packet + pp = p + 2; // Skip oid & gid bytes + payload_length = + (uint16_t)(chained_packet.offset - UCI_MSG_HDR_SIZE); + UCI_TRACE_I("%s: payloadLength is %d", __func__, payload_length); + UINT16_TO_STREAM(pp, + payload_length); // Update overall payload length + // into the chained packet + } } + // Clear flags + chained_packet.offset = 0; + chained_packet.oid = 0xFF; + chained_packet.gid = 0xFF; + uwb_cb.is_first_frgmnt_done = false; } } } @@ -381,7 +721,7 @@ bool uwb_ucif_process_event(UWB_HDR* p_msg) { case UCI_GID_CORE: uci_proc_core_management_ntf(oid, pp, payload_length); break; - case UCI_GID_SESSION_MANAGE: /* 0010b UCI management group */ + case UCI_GID_SESSION_MANAGE: /* 0001b UCI management group */ uci_proc_session_management_ntf(oid, pp, payload_length); break; case UCI_GID_RANGE_MANAGE: /* 0011b UCI Range management group */ @@ -573,6 +913,24 @@ void uwb_ucif_session_management_status(tUWB_RESPONSE_EVT event, uint8_t* p_buf, evt = UWB_SESSION_UPDATE_MULTICAST_LIST_REVT; evt_data.status = status; break; + case UWB_SESSION_CONFIGURE_DT_ANCHOR_RR_RDM_REVT: + evt_data.status = status; + evt = UWB_SESSION_CONFIGURE_DT_ANCHOR_RR_RDM_REVT; + evt_data.sConfigure_dt_anchor_rr_rdm_list.status = status; + evt_data.sConfigure_dt_anchor_rr_rdm_list.len = len; + if(len > 0){ + STREAM_TO_ARRAY(evt_data.sConfigure_dt_anchor_rr_rdm_list.rng_round_indexs, p_buf, len); + } + break; + case UWB_SESSION_ACTIVE_ROUNDS_INDEX_UPDATE_REVT: + evt_data.status = status; + evt = UWB_SESSION_ACTIVE_ROUNDS_INDEX_UPDATE_REVT; + evt_data.sRange_round_index.status = status; + evt_data.sRange_round_index.len = len; + if(len > 0){ + STREAM_TO_ARRAY(evt_data.sRange_round_index.rng_round_index, p_buf, len); + } + break; default: UCI_TRACE_E("unknown response event %x", event); } @@ -711,6 +1069,7 @@ void uwb_ucif_get_range_count_status(tUWB_RESPONSE_EVT event, uint8_t* p_buf, tUWB_RESPONSE evt_data; tUWB_RESPONSE_EVT evt = 0; tUWB_GET_RANGE_COUNT_REVT get_count; + get_count.count = 0x00; uint8_t* p = p_buf; if (len == 0) { @@ -760,10 +1119,6 @@ void uwb_ucif_proc_core_device_status(uint8_t* p_buf, uint16_t len) { uwb_cb.device_state = status; (*uwb_cb.p_resp_cback)(UWB_DEVICE_STATUS_REVT, &uwb_response); - if (status == UWBS_STATUS_ERROR) { - uwb_stop_quick_timer(&uwb_cb.uci_wait_rsp_timer); - uwb_ucif_uwb_recovery(); - } } /******************************************************************************* @@ -839,10 +1194,21 @@ void uwb_ucif_proc_ranging_data(uint8_t* p, uint16_t len) { "%s: MEASUREMENT_TYPE_TWOWAY Wrong number of measurements received:%d", __func__, sRange_data.no_of_measurements); return; - } else if (sRange_data.ranging_measure_type == MEASUREMENT_TYPE_ONEWAY && + } else if (sRange_data.ranging_measure_type == MEASUREMENT_TYPE_ULTDOA && sRange_data.no_of_measurements > MAX_NUM_OF_TDOA_MEASURES) { UCI_TRACE_E( - "%s: MEASUREMENT_TYPE_ONEWAY Wrong number of measurements received:%d", + "%s: MEASUREMENT_TYPE_ULTDOA Wrong number of measurements received:%d", + __func__, sRange_data.no_of_measurements); + return; + } else if (sRange_data.ranging_measure_type == MEASUREMENT_TYPE_DLTDOA && + sRange_data.no_of_measurements > MAX_NUM_OF_DLTDOA_MEASURES) { + UCI_TRACE_E( + "%s: MEASUREMENT_TYPE_DLTDOA Wrong number of measurements received:%d", + __func__, sRange_data.no_of_measurements); + return; + } else if (sRange_data.ranging_measure_type == MEASUREMENT_TYPE_OWR_WITH_AOA && + sRange_data.no_of_measurements > MAX_NUM_OWR_AOA_MEASURES) { + UCI_TRACE_E("%s: MEASUREMENT_TYPE_OWR_WITH_AOA Wrong number of measurements received:%d", __func__, sRange_data.no_of_measurements); return; } @@ -877,28 +1243,99 @@ void uwb_ucif_proc_ranging_data(uint8_t* p, uint16_t len) { STREAM_TO_UINT16(twr_range_measr->aoa_dest_elevation, p); STREAM_TO_UINT8(twr_range_measr->aoa_dest_elevation_FOM, p); STREAM_TO_UINT8(twr_range_measr->slot_index, p); + STREAM_TO_UINT8(twr_range_measr->rssi, p); /* Read & Ignore RFU bytes - if mac address format is short, then 12 bytes - if mac address format is extended, then read 6 bytes */ + if mac address format is short, then 11 bytes + if mac address format is extended, then read 5 bytes */ if (sRange_data.mac_addr_mode_indicator == SHORT_MAC_ADDRESS) { - STREAM_TO_ARRAY(&twr_range_measr->rfu[0], p, 12); + STREAM_TO_ARRAY(&twr_range_measr->rfu[0], p, 11); + } else { + STREAM_TO_ARRAY(&twr_range_measr->rfu[0], p, 5); + } + } + } else if (sRange_data.ranging_measure_type == MEASUREMENT_TYPE_DLTDOA) { + for (uint8_t i = 0; i < sRange_data.no_of_measurements; i++) { + tUWB_DLTDOA_RANGING_MEASR *dltdoa_range_measr = (tUWB_DLTDOA_RANGING_MEASR*)&sRange_data.ranging_measures.dltdoa_range_measr[i]; + uint16_t txTimeStampValue = 0; + uint16_t rxTimeStampValue = 0; + uint16_t anchorLocationValue = 0; + uint16_t activeRangingRoundValue = 0; + + if(sRange_data.mac_addr_mode_indicator == SHORT_MAC_ADDRESS) { + STREAM_TO_ARRAY(&dltdoa_range_measr->mac_addr[0], p, MAC_SHORT_ADD_LEN); + } else if(sRange_data.mac_addr_mode_indicator == EXTENDED_MAC_ADDRESS) { + STREAM_TO_ARRAY(&dltdoa_range_measr->mac_addr[0], p, MAC_EXT_ADD_LEN); + } else { + UCI_TRACE_E("%s: Invalid mac addressing indicator", __func__); + return; + } + STREAM_TO_UINT8(dltdoa_range_measr->status, p); + STREAM_TO_UINT8(dltdoa_range_measr->message_type, p); + STREAM_TO_UINT16(dltdoa_range_measr->message_control, p); + STREAM_TO_UINT16(dltdoa_range_measr->block_index, p); + STREAM_TO_UINT8(dltdoa_range_measr->round_index, p); + STREAM_TO_UINT8(dltdoa_range_measr->nLos, p); + STREAM_TO_UINT16(dltdoa_range_measr->aoa_azimuth, p); + STREAM_TO_UINT8(dltdoa_range_measr->aoa_azimuth_FOM, p); + STREAM_TO_UINT16(dltdoa_range_measr->aoa_elevation, p); + STREAM_TO_UINT8(dltdoa_range_measr->aoa_elevation_FOM, p); + STREAM_TO_UINT8(dltdoa_range_measr->rssi, p); + txTimeStampValue = ((dltdoa_range_measr->message_control & TDOA_TX_TIMESTAMP_OFFSET ) & (TDOA_TX_TIMESTAMP_OFFSET_MASK)); + if(txTimeStampValue == TDOA_TX_TIMESTAMP_40BITS) { + STREAM_TO_UINT40(dltdoa_range_measr->txTimeStamp, p); + } else if(txTimeStampValue == TDOA_TX_TIMESTAMP_64BITS) { + STREAM_TO_UINT64(dltdoa_range_measr->txTimeStamp, p); + } else { + UCI_TRACE_E("%s: Invalid txTimeStamp value", __func__); + return; + } + rxTimeStampValue = ((dltdoa_range_measr->message_control & TDOA_RX_TIMESTAMP_OFFSET ) & (TDOA_RX_TIMESTAMP_OFFSET_MASK)); + if(rxTimeStampValue == TDOA_RX_TIMESTAMP_40BITS) { + STREAM_TO_UINT40(dltdoa_range_measr->rxTimeStamp, p); + } else if(rxTimeStampValue == TDOA_RX_TIMESTAMP_64BITS) { + STREAM_TO_UINT64(dltdoa_range_measr->rxTimeStamp, p); + } else { + UCI_TRACE_E("%s: Invalid rxTimeStamp value", __func__); + return; + } + STREAM_TO_UINT16(dltdoa_range_measr->cfo_anchor, p); + STREAM_TO_UINT16(dltdoa_range_measr->cfo, p); + STREAM_TO_UINT32(dltdoa_range_measr->initiator_reply_time, p); + STREAM_TO_UINT32(dltdoa_range_measr->responder_reply_time, p); + STREAM_TO_UINT16(dltdoa_range_measr->initiator_responder_TOF, p); + anchorLocationValue = ((dltdoa_range_measr->message_control & TDOA_ANCHOR_LOC_OFFSET ) & (TDOA_ANCHOR_LOC_OFFSET_MASK)); + if(anchorLocationValue == TDOA_ANCHOR_LOC_NOT_INCLUDED) { + UCI_TRACE_D("%s: anchorLocation not included", __func__); + } else if(anchorLocationValue == TDOA_ANCHOR_LOC_IN_RELATIVE_SYSTEM) { + STREAM_TO_ARRAY(&dltdoa_range_measr->anchor_location[0], p, TDOA_ANCHOR_LOC_LEN_10BYTES); + } else if(anchorLocationValue == TDOA_ANCHOR_LOC_IN_WGS84_SYSTEM) { + STREAM_TO_ARRAY(&dltdoa_range_measr->anchor_location[0], p, TDOA_ANCHOR_LOC_LEN_12BYTES); + } else { + UCI_TRACE_E("%s: Invalid anchorLocationvalue value", __func__); + return; + } + activeRangingRoundValue = ((dltdoa_range_measr->message_control & TDOA_ACTIVE_RR_OFFSET) + & (TDOA_ACTIVE_RR_OFFSET_MASK)) >> TDOA_ACTIVE_RR_INDEX_POSITION; + if(activeRangingRoundValue != 0) { + STREAM_TO_ARRAY(&dltdoa_range_measr->active_ranging_round[0], p, activeRangingRoundValue); } else { - STREAM_TO_ARRAY(&twr_range_measr->rfu[0], p, 6); + UCI_TRACE_D("%s: activeRangingRound not included", __func__); } } - } else if (sRange_data.ranging_measure_type == MEASUREMENT_TYPE_ONEWAY) { + } else if (sRange_data.ranging_measure_type == MEASUREMENT_TYPE_ULTDOA) { for (uint8_t i = 0; i < sRange_data.no_of_measurements; i++) { tUWA_TDoA_RANGING_MEASR* tdoa_range_measr = (tUWA_TDoA_RANGING_MEASR*)&sRange_data.ranging_measures .tdoa_range_measr[i]; - uint16_t blink_payload_length = 0; - uint16_t device_info_length = 0; - if (ranging_measures_length < ONE_WAY_MEASUREMENT_LENGTH) { + if (ranging_measures_length < ULTDOA_MEASUREMENT_LENGTH) { UCI_TRACE_E("%s: Invalid ranging_measures_length = %x", __func__, ranging_measures_length); return; } - ranging_measures_length -= ONE_WAY_MEASUREMENT_LENGTH; + uint16_t txTimeStampValue = 0; + uint16_t ultdoaDeviceIdValue = 0; + uint16_t rxTimeStampValue = 0; + ranging_measures_length -= ULTDOA_MEASUREMENT_LENGTH; if (sRange_data.mac_addr_mode_indicator == SHORT_MAC_ADDRESS) { STREAM_TO_ARRAY(&tdoa_range_measr->mac_addr[0], p, MAC_SHORT_ADD_LEN); } else if (sRange_data.mac_addr_mode_indicator == EXTENDED_MAC_ADDRESS) { @@ -907,52 +1344,79 @@ void uwb_ucif_proc_ranging_data(uint8_t* p, uint16_t len) { UCI_TRACE_E("%s: Invalid mac addressing indicator", __func__); return; } + STREAM_TO_UINT8(tdoa_range_measr->message_control, p); STREAM_TO_UINT8(tdoa_range_measr->frame_type, p); STREAM_TO_UINT8(tdoa_range_measr->nLos, p); STREAM_TO_UINT16(tdoa_range_measr->aoa_azimuth, p); STREAM_TO_UINT8(tdoa_range_measr->aoa_azimuth_FOM, p); STREAM_TO_UINT16(tdoa_range_measr->aoa_elevation, p); STREAM_TO_UINT8(tdoa_range_measr->aoa_elevation_FOM, p); - STREAM_TO_UINT64(tdoa_range_measr->timeStamp, p); - STREAM_TO_UINT32(tdoa_range_measr->blink_frame_number, p); - if (sRange_data.mac_addr_mode_indicator == SHORT_MAC_ADDRESS) { - STREAM_TO_ARRAY(&tdoa_range_measr->rfu[0], p, 12); + STREAM_TO_UINT32(tdoa_range_measr->frame_number, p); + rxTimeStampValue = ((tdoa_range_measr->message_control & ULTDOA_RX_TIMESTAMP_OFFSET ) & (ULTDOA_RX_TIMESTAMP_OFFSET_MASK)); + if(rxTimeStampValue == ULTDOA_RX_TIMESTAMP_40BITS) { + STREAM_TO_ARRAY(&tdoa_range_measr->rxTimeStamp[0], p, ULTDOA_TIMESTAMP_LEN_40BITS); + } else if(rxTimeStampValue == ULTDOA_RX_TIMESTAMP_64BITS) { + STREAM_TO_ARRAY(&tdoa_range_measr->rxTimeStamp[0], p, ULTDOA_TIMESTAMP_LEN_64BITS); } else { - STREAM_TO_ARRAY(&tdoa_range_measr->rfu[0], p, 6); - } - STREAM_TO_UINT8(tdoa_range_measr->device_info_size, p); - device_info_length = tdoa_range_measr->device_info_size; - ranging_measures_length -= device_info_length; - if (ranging_measures_length < device_info_length) { - UCI_TRACE_E( - "%s: Invalid ranging_measures_length to copy device_info_length = " - "%x", - __func__, ranging_measures_length); + UCI_TRACE_E("%s: Invalid rxTimeStamp value", __func__); return; } - STREAM_TO_ARRAY(&device_info_buffer[i][0], p, - tdoa_range_measr->device_info_size); - tdoa_range_measr->device_info = &device_info_buffer[i][0]; - STREAM_TO_UINT8(tdoa_range_measr->blink_payload_size, p); - blink_payload_length = tdoa_range_measr->blink_payload_size; - ranging_measures_length -= blink_payload_length; - if (ranging_measures_length < blink_payload_length) { - UCI_TRACE_E( - "%s: Invalid ranging_measures_length to copy blink_payload_length " - "= %x", - __func__, ranging_measures_length); - return; + ultdoaDeviceIdValue = ((tdoa_range_measr->message_control & ULTDOA_DEVICE_ID_OFFSET ) & (ULTDOA_DEVICE_ID_OFFSET_MASK)); + if(ultdoaDeviceIdValue > 0) { + if(ultdoaDeviceIdValue == ULTDOA_DEVICE_ID_16BITS) { + STREAM_TO_ARRAY(&tdoa_range_measr->ulTdoa_device_id[0], p, ULTDOA_DEVICE_ID_LEN_16BITS); + } else if(ultdoaDeviceIdValue == ULTDOA_DEVICE_ID_32BITS) { + STREAM_TO_ARRAY(&tdoa_range_measr->ulTdoa_device_id[0], p, ULTDOA_DEVICE_ID_LEN_32BITS); + } else if(ultdoaDeviceIdValue == ULTDOA_DEVICE_ID_64BITS) { + STREAM_TO_ARRAY(&tdoa_range_measr->ulTdoa_device_id[0], p, ULTDOA_DEVICE_ID_LEN_64BITS); + } else { + UCI_TRACE_E("%s: Invalid Device Id value", __func__); + return; + } + } + txTimeStampValue = ((tdoa_range_measr->message_control & ULTDOA_TX_TIMESTAMP_OFFSET ) & (ULTDOA_TX_TIMESTAMP_OFFSET_MASK)); + if(txTimeStampValue > 0) { + if(txTimeStampValue == ULTDOA_TX_TIMESTAMP_40BITS) { + STREAM_TO_ARRAY(&tdoa_range_measr->txTimeStamp[0], p, ULTDOA_TIMESTAMP_LEN_40BITS); + } else if(txTimeStampValue == ULTDOA_TX_TIMESTAMP_64BITS){ + STREAM_TO_ARRAY(&tdoa_range_measr->txTimeStamp[0], p, ULTDOA_TIMESTAMP_LEN_64BITS); + } else { + UCI_TRACE_E("%s: Invalid txTimeStamp value", __func__); + return; + } } - STREAM_TO_ARRAY(&blink_payload_buffer[i][0], p, - tdoa_range_measr->blink_payload_size); - tdoa_range_measr->blink_payload_data = &blink_payload_buffer[i][0]; } + } else if (sRange_data.ranging_measure_type == MEASUREMENT_TYPE_OWR_WITH_AOA) { + tUWA_OWR_WITH_AOA_RANGING_MEASR* owr_aoa_range_measr = + (tUWA_OWR_WITH_AOA_RANGING_MEASR*)&sRange_data.ranging_measures + .owr_with_aoa_range_measr; + if (ranging_measures_length < OWR_WITH_AOA_MEASUREMENT_LENGTH) { + UCI_TRACE_E("%s: Invalid one way ranging_measures_length = %x", __func__, + ranging_measures_length); + return; + } + ranging_measures_length -= OWR_WITH_AOA_MEASUREMENT_LENGTH; + if (sRange_data.mac_addr_mode_indicator == SHORT_MAC_ADDRESS) { + STREAM_TO_ARRAY(&owr_aoa_range_measr->mac_addr[0], p, MAC_SHORT_ADD_LEN); + ranging_measures_length -= MAC_SHORT_ADD_LEN; + } else if (sRange_data.mac_addr_mode_indicator == EXTENDED_MAC_ADDRESS) { + STREAM_TO_ARRAY(&owr_aoa_range_measr->mac_addr[0], p, MAC_EXT_ADD_LEN); + ranging_measures_length -= MAC_EXT_ADD_LEN; + } else { + UCI_TRACE_E("%s: Invalid mac addressing indicator", __func__); + return; + } + STREAM_TO_UINT8(owr_aoa_range_measr->status, p); + STREAM_TO_UINT8(owr_aoa_range_measr->nLos, p); + STREAM_TO_UINT8(owr_aoa_range_measr->frame_seq_num, p); + STREAM_TO_UINT16(owr_aoa_range_measr->block_index, p); + STREAM_TO_UINT16(owr_aoa_range_measr->aoa_azimuth, p); + STREAM_TO_UINT8(owr_aoa_range_measr->aoa_azimuth_FOM, p); + STREAM_TO_UINT16(owr_aoa_range_measr->aoa_elevation, p); + STREAM_TO_UINT8(owr_aoa_range_measr->aoa_elevation_FOM, p); } else { - UCI_TRACE_E("%s: Measurement type not matched", __func__); + UCI_TRACE_E("%s: Measurement type(%d) not matched", __func__, sRange_data.ranging_measure_type); } - uwb_response.sRange_data = sRange_data; - - (*uwb_cb.p_resp_cback)(UWB_RANGE_DATA_REVT, &uwb_response); UCI_TRACE_I("%s: ranging_measures_length = %d range_data_ntf_len = %d", __func__,ranging_measures_length,range_data_ntf_len); if (ranging_measures_length >= VENDOR_SPEC_INFO_LEN) { @@ -962,15 +1426,16 @@ void uwb_ucif_proc_ranging_data(uint8_t* p, uint16_t len) { if (vendor_specific_length > MAX_VENDOR_INFO_LENGTH) { UCI_TRACE_E("%s: Invalid Range_data vendor_specific_length = %x", __func__, vendor_specific_length); - return; } - uint8_t *range_data_with_vendor_info = range_data_ntf_buffer; - uwb_response.sVendor_specific_ntf.len = range_data_ntf_len; - STREAM_TO_ARRAY(uwb_response.sVendor_specific_ntf.data, range_data_with_vendor_info, range_data_ntf_len); - (*uwb_cb.p_resp_cback)(UWB_VENDOR_SPECIFIC_UCI_NTF_EVT, &uwb_response); + STREAM_TO_ARRAY(sRange_data.vendor_specific_ntf.data, p, vendor_specific_length); + sRange_data.vendor_specific_ntf.len = vendor_specific_length; } } + + uwb_response.sRange_data = sRange_data; + + (*uwb_cb.p_resp_cback)(UWB_RANGE_DATA_REVT, &uwb_response); } /******************************************************************************* |