diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-12-15 09:31:28 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-12-15 09:31:28 +0000 |
commit | b004512285dfbcaada78815c6a40509a38ddabfa (patch) | |
tree | 38957bc15fe1779c276bcfdce3f459a2c3b7d7b0 | |
parent | ab0a82bbae99ec1a6183ddb39082cf977d61e476 (diff) | |
parent | 5100eb322e6fa1ed6dc7db36501a42774de885ef (diff) | |
download | uwb-aml_tz5_341510010.tar.gz |
Snap for 11224086 from 5100eb322e6fa1ed6dc7db36501a42774de885ef to mainline-tzdata5-releaseaml_tz5_341510070aml_tz5_341510050aml_tz5_341510010aml_tz5_341510010
Change-Id: Ib74ce235da75abac429642f71554453509d30198
-rwxr-xr-x | src/Android.bp | 3 | ||||
-rw-r--r-- | src/rust/uwb_core/examples/main.rs | 1 | ||||
-rw-r--r-- | src/rust/uwb_core/protos/uwb_service.proto | 20 | ||||
-rw-r--r-- | src/rust/uwb_core/src/error.rs | 3 | ||||
-rw-r--r-- | src/rust/uwb_core/src/params/app_config_params.rs | 6 | ||||
-rw-r--r-- | src/rust/uwb_core/src/params/uci_packets.rs | 48 | ||||
-rw-r--r-- | src/rust/uwb_core/src/proto/mappings.rs | 14 | ||||
-rw-r--r-- | src/rust/uwb_core/src/service/uwb_service.rs | 25 | ||||
-rw-r--r-- | src/rust/uwb_core/src/session/session_manager.rs | 13 | ||||
-rw-r--r-- | src/rust/uwb_core/src/uci.rs | 4 | ||||
-rw-r--r-- | src/rust/uwb_core/src/uci/command.rs | 68 | ||||
-rw-r--r-- | src/rust/uwb_core/src/uci/error.rs | 5 | ||||
-rw-r--r-- | src/rust/uwb_core/src/uci/mock_uci_manager.rs | 186 | ||||
-rw-r--r-- | src/rust/uwb_core/src/uci/notification.rs | 162 | ||||
-rw-r--r-- | src/rust/uwb_core/src/uci/response.rs | 29 | ||||
-rw-r--r-- | src/rust/uwb_core/src/uci/uci_manager.rs | 410 | ||||
-rw-r--r-- | src/rust/uwb_core/src/uci/uci_manager_sync.rs | 97 | ||||
-rw-r--r-- | src/rust/uwb_uci_packets/src/lib.rs | 72 | ||||
-rw-r--r-- | src/rust/uwb_uci_packets/uci_packets.pdl | 141 |
19 files changed, 1180 insertions, 127 deletions
diff --git a/src/Android.bp b/src/Android.bp index 193ac98..c0448b7 100755 --- a/src/Android.bp +++ b/src/Android.bp @@ -331,6 +331,9 @@ rust_test { "libcutils", "liblog", "libutils", + "libbinder_ndk", + "libbinder", + "libc++", ], // See b/268061150 stem: "libuci_hal_android_tests_host", diff --git a/src/rust/uwb_core/examples/main.rs b/src/rust/uwb_core/examples/main.rs index 63317b1..5f298c6 100644 --- a/src/rust/uwb_core/examples/main.rs +++ b/src/rust/uwb_core/examples/main.rs @@ -52,6 +52,7 @@ fn main() { UwbError::Timeout => {} UwbError::CommandRetry => {} UwbError::DuplicatedSessionId => {} + UwbError::RegulationUwbOff => {} UwbError::Unknown => {} // UwbError is non_exhaustive so we need to add a wild branch here. diff --git a/src/rust/uwb_core/protos/uwb_service.proto b/src/rust/uwb_core/protos/uwb_service.proto index e13765e..d7c4c38 100644 --- a/src/rust/uwb_core/protos/uwb_service.proto +++ b/src/rust/uwb_core/protos/uwb_service.proto @@ -56,8 +56,11 @@ enum Status { // Duplicated SessionId. DUPLICATED_SESSION_ID = 8; + //Regulation UWB off + REGULATION_UWB_OFF = 9; + // The unknown error. - UNKNOWN = 9; + UNKNOWN = 10; } // Represent uwb_uci_packets::StatusCode. @@ -73,6 +76,7 @@ enum StatusCode { UCI_STATUS_UNKNOWN_OID = 8; UCI_STATUS_READ_ONLY = 9; UCI_STATUS_COMMAND_RETRY = 10; + UCI_STATUS_UNKNOWN = 11; UCI_STATUS_SESSION_NOT_EXIST = 17; UCI_STATUS_SESSION_DUPLICATE = 18; @@ -103,6 +107,7 @@ enum StatusCode { UCI_STATUS_ERROR_CCC_SE_BUSY = 80; UCI_STATUS_ERROR_CCC_LIFECYCLE = 81; UCI_STATUS_ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT = 82; + UCI_STATUS_REGULATION_UWB_OFF = 83; // All vendor specific status code will be mapped to UCI_STATUS_VENDOR_SPECIFIC. UCI_STATUS_RFU_OR_VENDOR_SPECIFIC = 255; } @@ -183,10 +188,15 @@ enum RangingMeasurementType { // Represent uwb_uci_packets::SessionType. enum SessionType { - FIRA_RANGING_SESSION = 0; - FIRA_DATA_TRANSFER = 1; - CCC = 2; - DEVICE_TEST_MODE = 3; + FIRA_RANGING_SESSION = 0x00; + FIRA_RANGING_AND_IN_BAND_DATA_SESSION = 0x01; + FIRA_DATA_TRANSFER = 0x02; + FIRA_RANGING_ONLY_PHASE = 0x03; + FIRA_IN_BAND_DATA_PHASE = 0x04; + FIRA_RANGING_WITH_DATA_PHASE = 0x05; + CCC = 0xA0; + RADAR_SESSION = 0xA1; + DEVICE_TEST_MODE = 0xD0; } // Represent uwb_uci_packets::UpdateMulticastListAction. diff --git a/src/rust/uwb_core/src/error.rs b/src/rust/uwb_core/src/error.rs index 0b9c6b3..5889448 100644 --- a/src/rust/uwb_core/src/error.rs +++ b/src/rust/uwb_core/src/error.rs @@ -49,6 +49,9 @@ pub enum Error { /// Packet Tx Error #[error("The packet send failed with an error")] PacketTxError, + /// Country code regulation UWB Off + #[error("The country code command failed with a UWB regulatory error")] + RegulationUwbOff, /// The unknown error. #[error("The unknown error")] Unknown, 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 bb06d52..a264002 100644 --- a/src/rust/uwb_core/src/params/app_config_params.rs +++ b/src/rust/uwb_core/src/params/app_config_params.rs @@ -94,8 +94,12 @@ impl AppConfigParams { pub fn is_type_matched(&self, session_type: SessionType) -> bool { match self { Self::Fira(_) => { - session_type == SessionType::FiraDataTransfer + session_type == SessionType::FiraDataTransferSession || session_type == SessionType::FiraRangingSession + || session_type == SessionType::FiraRangingAndInBandDataSession + || session_type == SessionType::FiraRangingOnlyPhase + || session_type == SessionType::FiraInBandDataPhase + || session_type == SessionType::FiraRangingWithDataPhase } Self::Ccc(_) | Self::CccStarted(_) => session_type == SessionType::Ccc, } diff --git a/src/rust/uwb_core/src/params/uci_packets.rs b/src/rust/uwb_core/src/params/uci_packets.rs index 5cd5822..e3a7722 100644 --- a/src/rust/uwb_core/src/params/uci_packets.rs +++ b/src/rust/uwb_core/src/params/uci_packets.rs @@ -20,12 +20,13 @@ 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, Controlees, CreditAvailability, DataRcvStatusCode, + AppConfigStatus, AppConfigTlv as RawAppConfigTlv, AppConfigTlvType, BitsPerSample, CapTlv, + CapTlvType, Controlee, ControleeStatus, Controlees, CreditAvailability, DataRcvStatusCode, DataTransferNtfStatusCode, DeviceConfigId, DeviceConfigStatus, DeviceConfigTlv, DeviceState, ExtendedAddressDlTdoaRangingMeasurement, ExtendedAddressOwrAoaRangingMeasurement, ExtendedAddressTwoWayRangingMeasurement, GroupId, MessageType, MulticastUpdateStatusCode, - PowerStats, RangingMeasurementType, ReasonCode, ResetConfig, SessionState, SessionType, + PhaseList, PowerStats, RadarConfigStatus, RadarConfigTlv, RadarConfigTlvType, RadarDataType, + RangingMeasurementType, ReasonCode, ResetConfig, SessionState, SessionType, ShortAddressDlTdoaRangingMeasurement, ShortAddressOwrAoaRangingMeasurement, ShortAddressTwoWayRangingMeasurement, StatusCode, UpdateMulticastListAction, }; @@ -124,6 +125,19 @@ fn device_config_tlvs_to_map( HashMap::from_iter(tlvs.iter().map(|config| (config.cfg_id, &config.v))) } +/// Compare if two RadarConfigTlv array are equal. Convert the array to HashMap before comparing +/// because the order of TLV elements doesn't matter. +#[allow(dead_code)] +pub fn radar_config_tlvs_eq(a: &[RadarConfigTlv], b: &[RadarConfigTlv]) -> bool { + radar_config_tlvs_to_map(a) == radar_config_tlvs_to_map(b) +} + +fn radar_config_tlvs_to_map( + tlvs: &[RadarConfigTlv], +) -> HashMap<RadarConfigTlvType, &Vec<u8>, RandomState> { + HashMap::from_iter(tlvs.iter().map(|config| (config.cfg_id, &config.v))) +} + /// The response of the UciManager::core_set_config() method. #[derive(Debug, Clone, PartialEq)] pub struct CoreSetConfigResponse { @@ -142,6 +156,15 @@ pub struct SetAppConfigResponse { pub config_status: Vec<AppConfigStatus>, } +/// The response of the UciManager::android_set_radar_config() method. +#[derive(Debug, Clone, PartialEq)] +pub struct AndroidRadarConfigResponse { + /// The status code of the response. + pub status: StatusCode, + /// The status of each config TLV. + pub config_status: Vec<RadarConfigStatus>, +} + /// The response from UciManager::session_update_dt_tag_ranging_rounds() method. #[derive(Debug, Clone, PartialEq, Eq)] pub struct SessionUpdateDtTagRangingRoundsResponse { @@ -184,9 +207,28 @@ impl TryFrom<String> for CountryCode { } } +/// absolute time in UWBS Time domain(ms) when this configuration applies +#[derive(Debug, Clone, PartialEq, Copy)] +pub struct UpdateTime([u8; 8]); + +impl UpdateTime { + /// Create a UpdateTime instance. + pub fn new(update_time: &[u8; 8]) -> Option<Self> { + Some(Self(*update_time)) + } +} + +impl From<UpdateTime> for [u8; 8] { + fn from(item: UpdateTime) -> [u8; 8] { + item.0 + } +} + /// The response of the UciManager::core_get_device_info() method. #[derive(Debug, Clone, PartialEq, Eq)] pub struct GetDeviceInfoResponse { + /// Status + pub status: StatusCode, /// The UCI version. pub uci_version: u16, /// The MAC version. diff --git a/src/rust/uwb_core/src/proto/mappings.rs b/src/rust/uwb_core/src/proto/mappings.rs index a08046d..c10cf0e 100644 --- a/src/rust/uwb_core/src/proto/mappings.rs +++ b/src/rust/uwb_core/src/proto/mappings.rs @@ -116,6 +116,7 @@ impl From<ProtoStatusCode> for StatusCode { ProtoStatusCode::UCI_STATUS_UNKNOWN_OID => StatusCode::UciStatusUnknownOid, ProtoStatusCode::UCI_STATUS_READ_ONLY => StatusCode::UciStatusReadOnly, ProtoStatusCode::UCI_STATUS_COMMAND_RETRY => StatusCode::UciStatusCommandRetry, + ProtoStatusCode::UCI_STATUS_UNKNOWN => StatusCode::UciStatusUnknown, ProtoStatusCode::UCI_STATUS_SESSION_NOT_EXIST => StatusCode::UciStatusSessionNotExist, ProtoStatusCode::UCI_STATUS_SESSION_DUPLICATE => StatusCode::UciStatusSessionDuplicate, ProtoStatusCode::UCI_STATUS_SESSION_ACTIVE => StatusCode::UciStatusSessionActive, @@ -179,6 +180,7 @@ impl From<ProtoStatusCode> for StatusCode { ProtoStatusCode::UCI_STATUS_ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT => { StatusCode::UciStatusErrorStoppedDueToOtherSessionConflict } + ProtoStatusCode::UCI_STATUS_REGULATION_UWB_OFF => StatusCode::UciStatusRegulationUwbOff, _ => StatusCode::VendorSpecificStatusCode2, } } @@ -198,6 +200,7 @@ impl From<StatusCode> for ProtoStatusCode { StatusCode::UciStatusUnknownOid => ProtoStatusCode::UCI_STATUS_UNKNOWN_OID, StatusCode::UciStatusReadOnly => ProtoStatusCode::UCI_STATUS_READ_ONLY, StatusCode::UciStatusCommandRetry => ProtoStatusCode::UCI_STATUS_COMMAND_RETRY, + StatusCode::UciStatusUnknown => ProtoStatusCode::UCI_STATUS_UNKNOWN, StatusCode::UciStatusSessionNotExist => ProtoStatusCode::UCI_STATUS_SESSION_NOT_EXIST, StatusCode::UciStatusSessionDuplicate => ProtoStatusCode::UCI_STATUS_SESSION_DUPLICATE, StatusCode::UciStatusSessionActive => ProtoStatusCode::UCI_STATUS_SESSION_ACTIVE, @@ -270,6 +273,9 @@ impl From<StatusCode> for ProtoStatusCode { StatusCode::UciStatusErrorStoppedDueToOtherSessionConflict => { ProtoStatusCode::UCI_STATUS_ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT } + StatusCode::UciStatusRegulationUwbOff => { + ProtoStatusCode::UCI_STATUS_REGULATION_UWB_OFF + } _ => ProtoStatusCode::UCI_STATUS_RFU_OR_VENDOR_SPECIFIC, } } @@ -526,8 +532,13 @@ enum_mapping! { enum_mapping! { ProtoSessionType => SessionType, FIRA_RANGING_SESSION => FiraRangingSession, - FIRA_DATA_TRANSFER => FiraDataTransfer, + FIRA_DATA_TRANSFER => FiraDataTransferSession, + FIRA_RANGING_AND_IN_BAND_DATA_SESSION => FiraRangingAndInBandDataSession, + FIRA_RANGING_ONLY_PHASE => FiraRangingOnlyPhase, + FIRA_IN_BAND_DATA_PHASE => FiraInBandDataPhase, + FIRA_RANGING_WITH_DATA_PHASE => FiraRangingWithDataPhase, CCC => Ccc, + RADAR_SESSION => RadarSession, DEVICE_TEST_MODE => DeviceTestMode, } @@ -703,6 +714,7 @@ impl<T> From<Result<T>> for ProtoStatus { Err(Error::Timeout) => Self::TIMEOUT, Err(Error::CommandRetry) => Self::COMMAND_RETRY, Err(Error::DuplicatedSessionId) => Self::DUPLICATED_SESSION_ID, + Err(Error::RegulationUwbOff) => Self::REGULATION_UWB_OFF, Err(_) => Self::UNKNOWN, } } diff --git a/src/rust/uwb_core/src/service/uwb_service.rs b/src/rust/uwb_core/src/service/uwb_service.rs index ce00f95..012fbda 100644 --- a/src/rust/uwb_core/src/service/uwb_service.rs +++ b/src/rust/uwb_core/src/service/uwb_service.rs @@ -576,6 +576,7 @@ mod tests { use tokio::runtime::Runtime; use crate::params::uci_packets::{SessionState, SetAppConfigResponse, StatusCode}; + use crate::params::GetDeviceInfoResponse; use crate::service::mock_uwb_service_callback::MockUwbServiceCallback; use crate::service::uwb_service_builder::default_runtime; use crate::service::uwb_service_callback_builder::UwbServiceCallbackSendBuilder; @@ -584,6 +585,16 @@ mod tests { }; use crate::uci::mock_uci_manager::MockUciManager; use crate::uci::notification::UciNotification; + use uwb_uci_packets::StatusCode::UciStatusOk; + + const GET_DEVICE_INFO_RSP: GetDeviceInfoResponse = GetDeviceInfoResponse { + status: UciStatusOk, + uci_version: 0, + mac_version: 0, + phy_version: 0, + uci_test_version: 0, + vendor_spec_info: vec![], + }; fn setup_uwb_service( uci_manager: MockUciManager, @@ -599,7 +610,7 @@ mod tests { #[test] fn test_open_close_uci() { let mut uci_manager = MockUciManager::new(); - uci_manager.expect_open_hal(vec![], Ok(())); + uci_manager.expect_open_hal(vec![], Ok(GET_DEVICE_INFO_RSP)); uci_manager.expect_close_hal(false, Ok(())); let (service, _, _runtime) = setup_uwb_service(uci_manager); @@ -618,7 +629,7 @@ mod tests { let range_data = session_range_data(session_id); let mut uci_manager = MockUciManager::new(); - uci_manager.expect_open_hal(vec![], Ok(())); + uci_manager.expect_open_hal(vec![], Ok(GET_DEVICE_INFO_RSP)); uci_manager.expect_session_init( session_id, session_type, @@ -788,7 +799,7 @@ mod tests { let mut uci_manager = MockUciManager::new(); uci_manager.expect_open_hal( vec![UciNotification::Vendor(RawUciMessage { gid, oid, payload: payload.clone() })], - Ok(()), + Ok(GET_DEVICE_INFO_RSP), ); let (service, mut callback, _runtime) = setup_uwb_service(uci_manager); @@ -804,7 +815,7 @@ mod tests { let mut uci_manager = MockUciManager::new(); uci_manager.expect_open_hal( vec![UciNotification::Core(CoreNotification::DeviceStatus(state))], - Ok(()), + Ok(GET_DEVICE_INFO_RSP), ); let (service, mut callback, _runtime) = setup_uwb_service(uci_manager); callback.expect_on_uci_device_status_changed(state); @@ -819,7 +830,7 @@ mod tests { uci_manager.expect_open_hal(vec![], Err(Error::Timeout)); // 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(())); + uci_manager.expect_open_hal(vec![], Ok(GET_DEVICE_INFO_RSP)); let (service, mut callback, _runtime) = setup_uwb_service(uci_manager.clone()); callback.expect_on_service_reset(true); @@ -838,11 +849,11 @@ mod tests { vec![UciNotification::Core(CoreNotification::DeviceStatus( DeviceState::DeviceStateError, ))], - Ok(()), + Ok(GET_DEVICE_INFO_RSP), ); // 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(())); + uci_manager.expect_open_hal(vec![], Ok(GET_DEVICE_INFO_RSP)); let (service, mut callback, _runtime) = setup_uwb_service(uci_manager.clone()); callback.expect_on_service_reset(true); diff --git a/src/rust/uwb_core/src/session/session_manager.rs b/src/rust/uwb_core/src/session/session_manager.rs index e49dd9c..449d50d 100644 --- a/src/rust/uwb_core/src/session/session_manager.rs +++ b/src/rust/uwb_core/src/session/session_manager.rs @@ -443,9 +443,20 @@ pub(crate) mod test_utils { use crate::params::uci_packets::{ RangingMeasurementType, ReasonCode, ShortAddressTwoWayRangingMeasurement, StatusCode, }; + use crate::params::GetDeviceInfoResponse; use crate::uci::mock_uci_manager::MockUciManager; use crate::uci::notification::{RangingMeasurements, UciNotification}; use crate::utils::init_test_logging; + use uwb_uci_packets::StatusCode::UciStatusOk; + + const GET_DEVICE_INFO_RSP: GetDeviceInfoResponse = GetDeviceInfoResponse { + status: UciStatusOk, + uci_version: 0, + mac_version: 0, + phy_version: 0, + uci_test_version: 0, + vendor_spec_info: vec![], + }; pub(crate) fn generate_params() -> AppConfigParams { FiraAppConfigParamsBuilder::new() @@ -533,7 +544,7 @@ pub(crate) mod test_utils { let (uci_notf_sender, uci_notf_receiver) = mpsc::unbounded_channel(); let (session_notf_sender, session_notf_receiver) = mpsc::unbounded_channel(); let mut uci_manager = MockUciManager::new(); - uci_manager.expect_open_hal(vec![], Ok(())); + uci_manager.expect_open_hal(vec![], Ok(GET_DEVICE_INFO_RSP)); setup_uci_manager_fn(&mut uci_manager); uci_manager.set_session_notification_sender(uci_notf_sender).await; let _ = uci_manager.open_hal().await; diff --git a/src/rust/uwb_core/src/uci.rs b/src/rust/uwb_core/src/uci.rs index cac1be2..4364966 100644 --- a/src/rust/uwb_core/src/uci.rs +++ b/src/rust/uwb_core/src/uci.rs @@ -41,8 +41,8 @@ pub mod mock_uci_manager; // Re-export the public elements. pub use command::UciCommand; pub use notification::{ - CoreNotification, DataRcvNotification, RangingMeasurements, SessionNotification, - SessionRangeData, UciNotification, + CoreNotification, DataRcvNotification, RadarDataRcvNotification, RadarSweepData, + RangingMeasurements, SessionNotification, SessionRangeData, UciNotification, }; pub use uci_hal::{NopUciHal, UciHal, UciHalPacket}; pub use uci_logger_factory::{NopUciLoggerFactory, UciLoggerFactory}; diff --git a/src/rust/uwb_core/src/uci/command.rs b/src/rust/uwb_core/src/uci/command.rs index 2afb5a8..864ca8f 100644 --- a/src/rust/uwb_core/src/uci/command.rs +++ b/src/rust/uwb_core/src/uci/command.rs @@ -20,9 +20,12 @@ use log::error; use crate::error::{Error, Result}; use crate::params::uci_packets::{ AppConfigTlv, AppConfigTlvType, Controlees, CountryCode, DeviceConfigId, DeviceConfigTlv, - ResetConfig, SessionId, SessionToken, SessionType, UpdateMulticastListAction, + RadarConfigTlv, RadarConfigTlvType, ResetConfig, SessionId, SessionToken, SessionType, + UpdateMulticastListAction, UpdateTime, +}; +use uwb_uci_packets::{ + build_session_update_controller_multicast_list_cmd, GroupId, MessageType, PhaseList, }; -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)] @@ -80,10 +83,24 @@ pub enum UciCommand { SessionGetRangingCount { session_token: SessionToken, }, + SessionSetHybridConfig { + session_token: SessionToken, + number_of_phases: u8, + update_time: UpdateTime, + phase_list: Vec<PhaseList>, + }, AndroidSetCountryCode { country_code: CountryCode, }, AndroidGetPowerStats, + AndroidSetRadarConfig { + session_token: SessionToken, + config_tlvs: Vec<RadarConfigTlv>, + }, + AndroidGetRadarConfig { + session_token: SessionToken, + radar_cfg: Vec<RadarConfigTlvType>, + }, RawUciCmd { mt: u32, gid: u32, @@ -148,6 +165,22 @@ impl TryFrom<UciCommand> for uwb_uci_packets::UciControlPacket { .build() .into() } + UciCommand::AndroidSetRadarConfig { session_token, config_tlvs } => { + uwb_uci_packets::AndroidSetRadarConfigCmdBuilder { + session_token, + tlvs: config_tlvs, + } + .build() + .into() + } + UciCommand::AndroidGetRadarConfig { session_token, radar_cfg } => { + uwb_uci_packets::AndroidGetRadarConfigCmdBuilder { + session_token, + tlvs: radar_cfg.into_iter().map(u8::from).collect(), + } + .build() + .into() + } UciCommand::SessionUpdateDtTagRangingRounds { session_token, ranging_round_indexes, @@ -189,6 +222,19 @@ impl TryFrom<UciCommand> for uwb_uci_packets::UciControlPacket { UciCommand::SessionQueryMaxDataSize { session_token } => { uwb_uci_packets::SessionQueryMaxDataSizeCmdBuilder { session_token }.build().into() } + UciCommand::SessionSetHybridConfig { + session_token, + number_of_phases, + update_time, + phase_list, + } => uwb_uci_packets::SessionSetHybridConfigCmdBuilder { + session_token, + number_of_phases, + update_time: update_time.into(), + phase_list, + } + .build() + .into(), }; Ok(packet) } @@ -387,5 +433,23 @@ mod tests { cmd = UciCommand::AndroidGetPowerStats {}; packet = uwb_uci_packets::UciControlPacket::try_from(cmd).unwrap(); assert_eq!(packet, uwb_uci_packets::AndroidGetPowerStatsCmdBuilder {}.build().into()); + + cmd = UciCommand::AndroidSetRadarConfig { session_token: 1, config_tlvs: vec![] }; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd.clone()).unwrap(); + assert_eq!( + packet, + uwb_uci_packets::AndroidSetRadarConfigCmdBuilder { session_token: 1, tlvs: vec![] } + .build() + .into() + ); + + cmd = UciCommand::AndroidGetRadarConfig { session_token: 1, radar_cfg: vec![] }; + packet = uwb_uci_packets::UciControlPacket::try_from(cmd).unwrap(); + assert_eq!( + packet, + uwb_uci_packets::AndroidGetRadarConfigCmdBuilder { session_token: 1, tlvs: vec![] } + .build() + .into() + ); } } diff --git a/src/rust/uwb_core/src/uci/error.rs b/src/rust/uwb_core/src/uci/error.rs index 3101eec..3f040eb 100644 --- a/src/rust/uwb_core/src/uci/error.rs +++ b/src/rust/uwb_core/src/uci/error.rs @@ -22,7 +22,10 @@ pub(crate) fn status_code_to_result(status: StatusCode) -> Result<()> { StatusCode::UciStatusInvalidParam | StatusCode::UciStatusInvalidRange | StatusCode::UciStatusInvalidMsgSize => Err(Error::BadParameters), - StatusCode::UciStatusSessionNotExist + StatusCode::UciStatusSessionDuplicate => Err(Error::DuplicatedSessionId), + StatusCode::UciStatusFailed + | StatusCode::UciStatusSessionNotExist + | StatusCode::UciStatusSessionNotConfigured | StatusCode::UciStatusErrorCccSeBusy | StatusCode::UciStatusErrorCccLifecycle => Err(Error::ProtocolSpecific), StatusCode::UciStatusCommandRetry => Err(Error::CommandRetry), 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 21e1ddb..f6e84ba 100644 --- a/src/rust/uwb_core/src/uci/mock_uci_manager.rs +++ b/src/rust/uwb_core/src/uci/mock_uci_manager.rs @@ -27,13 +27,16 @@ 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, Controlees, - CoreSetConfigResponse, CountryCode, DeviceConfigId, DeviceConfigTlv, GetDeviceInfoResponse, - PowerStats, RawUciMessage, ResetConfig, SessionId, SessionState, SessionToken, SessionType, - SessionUpdateDtTagRangingRoundsResponse, SetAppConfigResponse, UpdateMulticastListAction, + app_config_tlvs_eq, device_config_tlvs_eq, radar_config_tlvs_eq, AndroidRadarConfigResponse, + AppConfigTlv, AppConfigTlvType, CapTlv, Controlees, CoreSetConfigResponse, CountryCode, + DeviceConfigId, DeviceConfigTlv, GetDeviceInfoResponse, PhaseList, PowerStats, RadarConfigTlv, + RadarConfigTlvType, RawUciMessage, ResetConfig, SessionId, SessionState, SessionToken, + SessionType, SessionUpdateDtTagRangingRoundsResponse, SetAppConfigResponse, + UpdateMulticastListAction, UpdateTime, }; use crate::uci::notification::{ - CoreNotification, DataRcvNotification, SessionNotification, UciNotification, + CoreNotification, DataRcvNotification, RadarDataRcvNotification, SessionNotification, + UciNotification, }; use crate::uci::uci_logger::UciLoggerMode; use crate::uci::uci_manager::UciManager; @@ -47,6 +50,7 @@ pub struct MockUciManager { session_notf_sender: mpsc::UnboundedSender<SessionNotification>, vendor_notf_sender: mpsc::UnboundedSender<RawUciMessage>, data_rcv_notf_sender: mpsc::UnboundedSender<DataRcvNotification>, + radar_data_rcv_notf_sender: mpsc::UnboundedSender<RadarDataRcvNotification>, } #[allow(dead_code)] @@ -60,6 +64,7 @@ impl MockUciManager { session_notf_sender: mpsc::unbounded_channel().0, vendor_notf_sender: mpsc::unbounded_channel().0, data_rcv_notf_sender: mpsc::unbounded_channel().0, + radar_data_rcv_notf_sender: mpsc::unbounded_channel().0, } } @@ -79,7 +84,11 @@ impl MockUciManager { /// 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<()>) { + pub fn expect_open_hal( + &mut self, + notfs: Vec<UciNotification>, + out: Result<GetDeviceInfoResponse>, + ) { self.expected_calls.lock().unwrap().push_back(ExpectedCall::OpenHal { notfs, out }); } @@ -368,6 +377,41 @@ impl MockUciManager { self.expected_calls.lock().unwrap().push_back(ExpectedCall::AndroidGetPowerStats { out }); } + /// Prepare Mock to expect android_set_radar_config. + /// + /// MockUciManager expects call with parameters, returns out as response, followed by notfs + /// sent. + pub fn expect_android_set_radar_config( + &mut self, + expected_session_id: SessionId, + expected_config_tlvs: Vec<RadarConfigTlv>, + notfs: Vec<UciNotification>, + out: Result<AndroidRadarConfigResponse>, + ) { + self.expected_calls.lock().unwrap().push_back(ExpectedCall::AndroidSetRadarConfig { + expected_session_id, + expected_config_tlvs, + notfs, + out, + }); + } + + /// Prepare Mock to expect android_get_app_config. + /// + /// MockUciManager expects call with parameters, returns out as response. + pub fn expect_android_get_radar_config( + &mut self, + expected_session_id: SessionId, + expected_config_ids: Vec<RadarConfigTlvType>, + out: Result<Vec<RadarConfigTlv>>, + ) { + self.expected_calls.lock().unwrap().push_back(ExpectedCall::AndroidGetRadarConfig { + expected_session_id, + expected_config_ids, + out, + }); + } + /// Prepare Mock to expect raw_uci_cmd. /// /// MockUciManager expects call with parameters, returns out as response. @@ -408,6 +452,26 @@ impl MockUciManager { }); } + /// Prepare Mock to expect session_set_hybrid_config. + /// + /// MockUciManager expects call with parameters, returns out as response + pub fn expect_session_set_hybrid_config( + &mut self, + expected_session_id: SessionId, + expected_number_of_phases: u8, + expected_update_time: UpdateTime, + expected_phase_list: Vec<PhaseList>, + out: Result<()>, + ) { + self.expected_calls.lock().unwrap().push_back(ExpectedCall::SessionSetHybridConfig { + expected_session_id, + expected_number_of_phases, + expected_update_time, + expected_phase_list, + out, + }); + } + /// Call Mock to send notifications. fn send_notifications(&self, notfs: Vec<UciNotification>) { for notf in notfs.into_iter() { @@ -461,8 +525,14 @@ impl UciManager for MockUciManager { ) { self.data_rcv_notf_sender = data_rcv_notf_sender; } + async fn set_radar_data_rcv_notification_sender( + &mut self, + radar_data_rcv_notf_sender: mpsc::UnboundedSender<RadarDataRcvNotification>, + ) { + self.radar_data_rcv_notf_sender = radar_data_rcv_notf_sender; + } - async fn open_hal(&self) -> Result<()> { + async fn open_hal(&self) -> Result<GetDeviceInfoResponse> { let mut expected_calls = self.expected_calls.lock().unwrap(); match expected_calls.pop_front() { Some(ExpectedCall::OpenHal { notfs, out }) => { @@ -873,6 +943,56 @@ impl UciManager for MockUciManager { } } + async fn android_set_radar_config( + &self, + session_id: SessionId, + config_tlvs: Vec<RadarConfigTlv>, + ) -> Result<AndroidRadarConfigResponse> { + let mut expected_calls = self.expected_calls.lock().unwrap(); + match expected_calls.pop_front() { + Some(ExpectedCall::AndroidSetRadarConfig { + expected_session_id, + expected_config_tlvs, + notfs, + out, + }) if expected_session_id == session_id + && radar_config_tlvs_eq(&expected_config_tlvs, &config_tlvs) => + { + self.expect_call_consumed.notify_one(); + self.send_notifications(notfs); + out + } + Some(call) => { + expected_calls.push_front(call); + Err(Error::MockUndefined) + } + None => Err(Error::MockUndefined), + } + } + + async fn android_get_radar_config( + &self, + session_id: SessionId, + config_ids: Vec<RadarConfigTlvType>, + ) -> Result<Vec<RadarConfigTlv>> { + let mut expected_calls = self.expected_calls.lock().unwrap(); + match expected_calls.pop_front() { + Some(ExpectedCall::AndroidGetRadarConfig { + expected_session_id, + expected_config_ids, + out, + }) if expected_session_id == session_id && expected_config_ids == config_ids => { + self.expect_call_consumed.notify_one(); + out + } + Some(call) => { + expected_calls.push_front(call); + Err(Error::MockUndefined) + } + None => Err(Error::MockUndefined), + } + } + async fn raw_uci_cmd( &self, mt: u32, @@ -941,13 +1061,45 @@ impl UciManager for MockUciManager { ) -> Result<SessionToken> { Ok(1) // No uci call here, no mock required. } + + async fn session_set_hybrid_config( + &self, + session_id: SessionId, + number_of_phases: u8, + update_time: UpdateTime, + phase_list: Vec<PhaseList>, + ) -> Result<()> { + let mut expected_calls = self.expected_calls.lock().unwrap(); + match expected_calls.pop_front() { + Some(ExpectedCall::SessionSetHybridConfig { + expected_session_id, + expected_number_of_phases, + expected_update_time, + expected_phase_list, + out, + }) if expected_session_id == session_id + && expected_number_of_phases == number_of_phases + && expected_update_time == update_time + && expected_phase_list.len() == phase_list.len() + && expected_phase_list == phase_list => + { + self.expect_call_consumed.notify_one(); + out + } + Some(call) => { + expected_calls.push_front(call); + Err(Error::MockUndefined) + } + None => Err(Error::MockUndefined), + } + } } #[derive(Clone)] enum ExpectedCall { OpenHal { notfs: Vec<UciNotification>, - out: Result<()>, + out: Result<GetDeviceInfoResponse>, }, CloseHal { expected_force: bool, @@ -1040,6 +1192,17 @@ enum ExpectedCall { AndroidGetPowerStats { out: Result<PowerStats>, }, + AndroidSetRadarConfig { + expected_session_id: SessionId, + expected_config_tlvs: Vec<RadarConfigTlv>, + notfs: Vec<UciNotification>, + out: Result<AndroidRadarConfigResponse>, + }, + AndroidGetRadarConfig { + expected_session_id: SessionId, + expected_config_ids: Vec<RadarConfigTlvType>, + out: Result<Vec<RadarConfigTlv>>, + }, RawUciCmd { expected_mt: u32, expected_gid: u32, @@ -1054,4 +1217,11 @@ enum ExpectedCall { expected_app_payload_data: Vec<u8>, out: Result<()>, }, + SessionSetHybridConfig { + expected_session_id: SessionId, + expected_number_of_phases: u8, + expected_update_time: UpdateTime, + expected_phase_list: Vec<PhaseList>, + out: Result<()>, + }, } diff --git a/src/rust/uwb_core/src/uci/notification.rs b/src/rust/uwb_core/src/uci/notification.rs index eab8233..d99f602 100644 --- a/src/rust/uwb_core/src/uci/notification.rs +++ b/src/rust/uwb_core/src/uci/notification.rs @@ -15,15 +15,20 @@ use std::convert::{TryFrom, TryInto}; use log::{debug, error}; -use uwb_uci_packets::{parse_diagnostics_ntf, Packet, UCI_PACKET_HEADER_LEN}; +use uwb_uci_packets::{ + parse_diagnostics_ntf, radar_bytes_per_sample_value, Packet, RadarDataRcv, RadarSweepDataRaw, + UCI_PACKET_HEADER_LEN, UCI_RADAR_SEQUENCE_NUMBER_LEN, UCI_RADAR_TIMESTAMP_LEN, + UCI_RADAR_VENDOR_DATA_LEN_LEN, +}; use crate::error::{Error, Result}; use crate::params::fira_app_config_params::UwbAddress; use crate::params::uci_packets::{ - ControleeStatus, CreditAvailability, DataRcvStatusCode, DataTransferNtfStatusCode, DeviceState, - ExtendedAddressDlTdoaRangingMeasurement, ExtendedAddressOwrAoaRangingMeasurement, - ExtendedAddressTwoWayRangingMeasurement, RangingMeasurementType, RawUciMessage, SessionState, - SessionToken, ShortAddressDlTdoaRangingMeasurement, ShortAddressOwrAoaRangingMeasurement, + BitsPerSample, ControleeStatus, CreditAvailability, DataRcvStatusCode, + DataTransferNtfStatusCode, DeviceState, ExtendedAddressDlTdoaRangingMeasurement, + ExtendedAddressOwrAoaRangingMeasurement, ExtendedAddressTwoWayRangingMeasurement, + RadarDataType, RangingMeasurementType, RawUciMessage, SessionState, SessionToken, + ShortAddressDlTdoaRangingMeasurement, ShortAddressOwrAoaRangingMeasurement, ShortAddressTwoWayRangingMeasurement, StatusCode, }; @@ -81,8 +86,8 @@ pub enum SessionNotification { DataTransferStatus { /// SessionToken : u32 session_token: SessionToken, - /// Sequence Number: u8 - uci_sequence_number: u8, + /// Sequence Number: u16 + uci_sequence_number: u16, /// Data Transfer Status Code status: DataTransferNtfStatusCode, /// Transmission count @@ -145,7 +150,7 @@ pub struct DataRcvNotification { pub session_token: SessionToken, /// The status of the data rx. - pub status: DataRcvStatusCode, + pub status: StatusCode, /// The sequence number of the data packet. pub uci_sequence_num: u16, @@ -157,6 +162,142 @@ pub struct DataRcvNotification { pub payload: Vec<u8>, } +/// The Radar sweep data struct +#[derive(Debug, Clone, std::cmp::PartialEq)] +pub struct RadarSweepData { + /// Counter of a single radar sweep per receiver. Starting + /// with 0 when the radar session is started. + pub sequence_number: u32, + + /// Timestamp when this radar sweep is received. Unit is + /// based on the PRF. + pub timestamp: u32, + + /// The radar vendor specific data. + pub vendor_specific_data: Vec<u8>, + + /// The radar sample data. + pub sample_data: Vec<u8>, +} + +/// The RADAR_DATA_RCV packet +#[derive(Debug, Clone, std::cmp::PartialEq)] +pub struct RadarDataRcvNotification { + /// The identifier of the session on which radar data transfer is happening. + pub session_token: SessionToken, + + /// The status of the radar data rx. + pub status: DataRcvStatusCode, + + /// The radar data type. + pub radar_data_type: RadarDataType, + + /// The number of sweeps. + pub number_of_sweeps: u8, + + /// Number of samples captured for each radar sweep. + pub samples_per_sweep: u8, + + /// Bits per sample in the radar sweep. + pub bits_per_sample: BitsPerSample, + + /// Defines the start offset with respect to 0cm distance. Unit in samples. + pub sweep_offset: u16, + + /// Radar sweep data. + pub sweep_data: Vec<RadarSweepData>, +} + +impl From<&uwb_uci_packets::RadarSweepDataRaw> for RadarSweepData { + fn from(evt: &uwb_uci_packets::RadarSweepDataRaw) -> Self { + Self { + sequence_number: evt.sequence_number, + timestamp: evt.timestamp, + vendor_specific_data: evt.vendor_specific_data.clone(), + sample_data: evt.sample_data.clone(), + } + } +} + +impl TryFrom<uwb_uci_packets::UciDataPacket> for RadarDataRcvNotification { + type Error = Error; + fn try_from(evt: uwb_uci_packets::UciDataPacket) -> std::result::Result<Self, Self::Error> { + match evt.specialize() { + uwb_uci_packets::UciDataPacketChild::RadarDataRcv(evt) => parse_radar_data(evt), + _ => Err(Error::Unknown), + } + } +} + +fn parse_radar_data(data: RadarDataRcv) -> Result<RadarDataRcvNotification> { + let session_token = data.get_session_handle(); + let status = data.get_status(); + let radar_data_type = data.get_radar_data_type(); + let number_of_sweeps = data.get_number_of_sweeps(); + let samples_per_sweep = data.get_samples_per_sweep(); + let bits_per_sample = data.get_bits_per_sample(); + let bytes_per_sample_value = radar_bytes_per_sample_value(bits_per_sample); + let sweep_offset = data.get_sweep_offset(); + + Ok(RadarDataRcvNotification { + session_token, + status, + radar_data_type, + number_of_sweeps, + samples_per_sweep, + bits_per_sample, + sweep_offset, + sweep_data: parse_radar_sweep_data( + number_of_sweeps, + samples_per_sweep, + bytes_per_sample_value, + data.get_sweep_data().clone(), + )?, + }) +} + +fn parse_radar_sweep_data( + number_of_sweeps: u8, + samples_per_sweep: u8, + bytes_per_sample_value: u8, + data: Vec<u8>, +) -> Result<Vec<RadarSweepData>> { + let mut radar_sweep_data: Vec<RadarSweepData> = Vec::new(); + let mut sweep_data_cursor = 0; + for _ in 0..number_of_sweeps { + let vendor_data_len_index = + sweep_data_cursor + UCI_RADAR_SEQUENCE_NUMBER_LEN + UCI_RADAR_TIMESTAMP_LEN; + if data.len() <= vendor_data_len_index { + error!("Invalid radar sweep data length for vendor, data: {:?}", &data); + return Err(Error::BadParameters); + } + let vendor_specific_data_len = data[vendor_data_len_index] as usize; + let sweep_data_len = UCI_RADAR_SEQUENCE_NUMBER_LEN + + UCI_RADAR_TIMESTAMP_LEN + + UCI_RADAR_VENDOR_DATA_LEN_LEN + + vendor_specific_data_len + + (samples_per_sweep * bytes_per_sample_value) as usize; + if data.len() < sweep_data_cursor + sweep_data_len { + error!("Invalid radar sweep data length, data: {:?}", &data); + return Err(Error::BadParameters); + } + radar_sweep_data.push( + (&RadarSweepDataRaw::parse( + &data[sweep_data_cursor..sweep_data_cursor + sweep_data_len], + ) + .map_err(|e| { + error!("Failed to parse raw Radar Sweep Data {:?}, data: {:?}", e, &data); + Error::BadParameters + })?) + .into(), + ); + + sweep_data_cursor += sweep_data_len; + } + + Ok(radar_sweep_data) +} + 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> { @@ -168,10 +309,7 @@ impl TryFrom<uwb_uci_packets::UciDataPacket> for DataRcvNotification { source_address: UwbAddress::Extended(evt.get_source_mac_address().to_le_bytes()), payload: evt.get_data().to_vec(), }), - _ => { - error!("Unknown UciData packet: {:?}", evt); - Err(Error::Unknown) - } + _ => Err(Error::Unknown), } } } diff --git a/src/rust/uwb_core/src/uci/response.rs b/src/rust/uwb_core/src/uci/response.rs index dfeb6c8..d70f9f7 100644 --- a/src/rust/uwb_core/src/uci/response.rs +++ b/src/rust/uwb_core/src/uci/response.rs @@ -16,8 +16,8 @@ use std::convert::{TryFrom, TryInto}; use crate::error::{Error, Result}; use crate::params::uci_packets::{ - AppConfigTlv, CapTlv, CoreSetConfigResponse, DeviceConfigTlv, GetDeviceInfoResponse, - PowerStats, RawUciMessage, SessionHandle, SessionState, + AndroidRadarConfigResponse, AppConfigTlv, CapTlv, CoreSetConfigResponse, DeviceConfigTlv, + GetDeviceInfoResponse, PowerStats, RadarConfigTlv, RawUciMessage, SessionHandle, SessionState, SessionUpdateDtTagRangingRoundsResponse, SetAppConfigResponse, StatusCode, UciControlPacket, }; use crate::uci::error::status_code_to_result; @@ -48,8 +48,11 @@ pub(super) enum UciResponse { SessionGetRangingCount(Result<usize>), AndroidSetCountryCode(Result<()>), AndroidGetPowerStats(Result<PowerStats>), + AndroidSetRadarConfig(AndroidRadarConfigResponse), + AndroidGetRadarConfig(Result<Vec<RadarConfigTlv>>), RawUciCmd(Result<RawUciMessage>), SendUciData(Result<()>), + SessionSetHybridConfig(Result<()>), } impl UciResponse { @@ -75,7 +78,10 @@ impl UciResponse { 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::AndroidGetRadarConfig(result) => Self::matches_result_retry(result), + Self::AndroidSetRadarConfig(resp) => Self::matches_status_retry(&resp.status), Self::RawUciCmd(result) => Self::matches_result_retry(result), + Self::SessionSetHybridConfig(result) => Self::matches_result_retry(result), Self::CoreSetConfig(resp) => Self::matches_status_retry(&resp.status), Self::SessionSetAppConfig(resp) => Self::matches_status_retry(&resp.status), @@ -120,6 +126,7 @@ impl TryFrom<uwb_uci_packets::CoreResponse> for UciResponse { match evt.specialize() { CoreResponseChild::GetDeviceInfoRsp(evt) => Ok(UciResponse::CoreGetDeviceInfo( status_code_to_result(evt.get_status()).map(|_| GetDeviceInfoResponse { + status: evt.get_status(), uci_version: evt.get_uci_version(), mac_version: evt.get_mac_version(), phy_version: evt.get_phy_version(), @@ -204,7 +211,12 @@ impl TryFrom<uwb_uci_packets::SessionConfigResponse> for UciResponse { )) } SessionConfigResponseChild::SessionQueryMaxDataSizeRsp(evt) => { - Ok(UciResponse::SessionQueryMaxDataSize(Ok(evt.get_max_data_size()))) + Ok(UciResponse::SessionQueryMaxDataSize( + status_code_to_result(evt.get_status()).map(|_| evt.get_max_data_size()), + )) + } + SessionConfigResponseChild::SessionSetHybridConfigRsp(evt) => { + Ok(UciResponse::SessionSetHybridConfig(status_code_to_result(evt.get_status()))) } _ => Err(Error::Unknown), } @@ -247,6 +259,17 @@ impl TryFrom<uwb_uci_packets::AndroidResponse> for UciResponse { status_code_to_result(evt.get_stats().status).map(|_| evt.get_stats().clone()), )) } + AndroidResponseChild::AndroidSetRadarConfigRsp(evt) => { + Ok(UciResponse::AndroidSetRadarConfig(AndroidRadarConfigResponse { + status: evt.get_status(), + config_status: evt.get_cfg_status().clone(), + })) + } + AndroidResponseChild::AndroidGetRadarConfigRsp(evt) => { + Ok(UciResponse::AndroidGetRadarConfig( + status_code_to_result(evt.get_status()).map(|_| evt.get_tlvs().clone()), + )) + } _ => Err(Error::Unknown), } } diff --git a/src/rust/uwb_core/src/uci/uci_manager.rs b/src/rust/uwb_core/src/uci/uci_manager.rs index 24c0737..a1a97f1 100644 --- a/src/rust/uwb_core/src/uci/uci_manager.rs +++ b/src/rust/uwb_core/src/uci/uci_manager.rs @@ -24,16 +24,18 @@ use crate::uci::command::UciCommand; //use crate::uci::error::{Error, Result}; use crate::error::{Error, Result}; use crate::params::uci_packets::{ - AppConfigTlv, AppConfigTlvType, CapTlv, Controlees, CoreSetConfigResponse, CountryCode, - CreditAvailability, DeviceConfigId, DeviceConfigTlv, DeviceState, GetDeviceInfoResponse, - GroupId, MessageType, PowerStats, RawUciMessage, ResetConfig, SessionId, SessionState, - SessionToken, SessionType, SessionUpdateDtTagRangingRoundsResponse, SetAppConfigResponse, - UciDataPacket, UciDataPacketHal, UpdateMulticastListAction, + AndroidRadarConfigResponse, AppConfigTlv, AppConfigTlvType, CapTlv, Controlees, + CoreSetConfigResponse, CountryCode, CreditAvailability, DeviceConfigId, DeviceConfigTlv, + DeviceState, GetDeviceInfoResponse, GroupId, MessageType, PowerStats, RadarConfigTlv, + RadarConfigTlvType, RawUciMessage, ResetConfig, SessionId, SessionState, SessionToken, + SessionType, SessionUpdateDtTagRangingRoundsResponse, SetAppConfigResponse, UciDataPacket, + UciDataPacketHal, UpdateMulticastListAction, UpdateTime, }; use crate::params::utils::bytes_to_u64; use crate::uci::message::UciMessage; use crate::uci::notification::{ - CoreNotification, DataRcvNotification, SessionNotification, SessionRangeData, UciNotification, + CoreNotification, DataRcvNotification, RadarDataRcvNotification, SessionNotification, + SessionRangeData, UciNotification, }; use crate::uci::response::UciResponse; use crate::uci::timeout_uci_hal::TimeoutUciHal; @@ -41,7 +43,7 @@ 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}; +use uwb_uci_packets::{Packet, PhaseList, RawUciControlPacket, UciDataSnd, UciDefragPacket}; const UCI_TIMEOUT_MS: u64 = 800; const MAX_RETRY_COUNT: usize = 3; @@ -68,10 +70,14 @@ pub trait UciManager: 'static + Send + Sync + Clone { &mut self, data_rcv_notf_sender: mpsc::UnboundedSender<DataRcvNotification>, ); + async fn set_radar_data_rcv_notification_sender( + &mut self, + radar_data_rcv_notf_sender: mpsc::UnboundedSender<RadarDataRcvNotification>, + ); // Open the UCI HAL. // All the UCI commands should be called after the open_hal() completes successfully. - async fn open_hal(&self) -> Result<()>; + async fn open_hal(&self) -> Result<GetDeviceInfoResponse>; // Close the UCI HAL. async fn close_hal(&self, force: bool) -> Result<()>; @@ -126,6 +132,16 @@ pub trait UciManager: 'static + Send + Sync + Clone { // Send the Android-specific UCI commands async fn android_set_country_code(&self, country_code: CountryCode) -> Result<()>; async fn android_get_power_stats(&self) -> Result<PowerStats>; + async fn android_set_radar_config( + &self, + session_id: SessionId, + config_tlvs: Vec<RadarConfigTlv>, + ) -> Result<AndroidRadarConfigResponse>; + async fn android_get_radar_config( + &self, + session_id: SessionId, + config_ids: Vec<RadarConfigTlvType>, + ) -> Result<Vec<RadarConfigTlv>>; // Send a raw uci command. async fn raw_uci_cmd( @@ -150,6 +166,14 @@ pub trait UciManager: 'static + Send + Sync + Clone { &self, session_id: SessionId, ) -> Result<SessionToken>; + /// Send UCI command for setting hybrid config + async fn session_set_hybrid_config( + &self, + session_id: SessionId, + number_of_phases: u8, + update_time: UpdateTime, + phase_list: Vec<PhaseList>, + ) -> Result<()>; } /// UciManagerImpl is the main implementation of UciManager. Using the actor model, UciManagerImpl @@ -246,17 +270,32 @@ impl UciManager for UciManagerImpl { .send_cmd(UciManagerCmd::SetDataRcvNotificationSender { data_rcv_notf_sender }) .await; } + async fn set_radar_data_rcv_notification_sender( + &mut self, + radar_data_rcv_notf_sender: mpsc::UnboundedSender<RadarDataRcvNotification>, + ) { + let _ = self + .send_cmd(UciManagerCmd::SetRadarDataRcvNotificationSender { + radar_data_rcv_notf_sender, + }) + .await; + } - async fn open_hal(&self) -> Result<()> { + async fn open_hal(&self) -> Result<GetDeviceInfoResponse> { 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 // retrieve the device information.", we call get_device_info() after successfully // opening the HAL. - let device_info = self.core_get_device_info().await; + let device_info = match self.core_get_device_info().await { + Ok(resp) => resp, + Err(e) => { + return Err(e); + } + }; debug!("UCI device info: {:?}", device_info); - Ok(()) + Ok(device_info) } Ok(_) => Err(Error::Unknown), Err(e) => Err(e), @@ -502,6 +541,38 @@ impl UciManager for UciManagerImpl { } } + async fn android_set_radar_config( + &self, + session_id: SessionId, + config_tlvs: Vec<RadarConfigTlv>, + ) -> Result<AndroidRadarConfigResponse> { + let cmd = UciCommand::AndroidSetRadarConfig { + session_token: self.get_session_token(&session_id).await?, + config_tlvs, + }; + match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { + Ok(UciResponse::AndroidSetRadarConfig(resp)) => Ok(resp), + Ok(_) => Err(Error::Unknown), + Err(e) => Err(e), + } + } + + async fn android_get_radar_config( + &self, + session_id: SessionId, + radar_cfg: Vec<RadarConfigTlvType>, + ) -> Result<Vec<RadarConfigTlv>> { + let cmd = UciCommand::AndroidGetRadarConfig { + session_token: self.get_session_token(&session_id).await?, + radar_cfg, + }; + match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { + Ok(UciResponse::AndroidGetRadarConfig(resp)) => resp, + Ok(_) => Err(Error::Unknown), + Err(e) => Err(e), + } + } + async fn raw_uci_cmd( &self, mt: u32, @@ -552,6 +623,27 @@ impl UciManager for UciManagerImpl { ) -> Result<SessionToken> { Ok(self.get_session_token(&session_id).await?) } + + /// Send UCI command for setting hybrid config + async fn session_set_hybrid_config( + &self, + session_id: SessionId, + number_of_phases: u8, + update_time: UpdateTime, + phase_list: Vec<PhaseList>, + ) -> Result<()> { + let cmd = UciCommand::SessionSetHybridConfig { + session_token: self.get_session_token(&session_id).await?, + number_of_phases, + update_time, + phase_list, + }; + match self.send_cmd(UciManagerCmd::SendUciCommand { cmd }).await { + Ok(UciResponse::SessionSetHybridConfig(resp)) => resp, + Ok(_) => Err(Error::Unknown), + Err(e) => Err(e), + } + } } struct UciManagerActor<T: UciHal, U: UciLogger> { @@ -605,6 +697,7 @@ struct UciManagerActor<T: UciHal, U: UciLogger> { session_notf_sender: mpsc::UnboundedSender<SessionNotification>, vendor_notf_sender: mpsc::UnboundedSender<RawUciMessage>, data_rcv_notf_sender: mpsc::UnboundedSender<DataRcvNotification>, + radar_data_rcv_notf_sender: mpsc::UnboundedSender<RadarDataRcvNotification>, // Used to store the last init session id to help map the session handle sent // in session int response can be correctly mapped. @@ -645,6 +738,7 @@ impl<T: UciHal, U: UciLogger> UciManagerActor<T, U> { session_notf_sender: mpsc::unbounded_channel().0, vendor_notf_sender: mpsc::unbounded_channel().0, data_rcv_notf_sender: mpsc::unbounded_channel().0, + radar_data_rcv_notf_sender: mpsc::unbounded_channel().0, last_init_session_id: None, session_id_to_token_map, } @@ -778,6 +872,10 @@ impl<T: UciHal, U: UciLogger> UciManagerActor<T, U> { self.data_rcv_notf_sender = data_rcv_notf_sender; let _ = result_sender.send(Ok(UciResponse::SetNotification)); } + UciManagerCmd::SetRadarDataRcvNotificationSender { radar_data_rcv_notf_sender } => { + self.radar_data_rcv_notf_sender = radar_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."); @@ -1064,7 +1162,7 @@ impl<T: UciHal, U: UciLogger> UciManagerActor<T, U> { } UciDefragPacket::Data(packet) => { self.logger.log_uci_data(&packet); - self.handle_data_rcv(packet); + self.handle_data_rcv(packet).await; } UciDefragPacket::Raw(result, raw_uci_control_packet) => { // Handle response to raw UCI cmd. We want to send it back as @@ -1275,14 +1373,42 @@ impl<T: UciHal, U: UciLogger> UciManagerActor<T, U> { } } - fn handle_data_rcv(&mut self, packet: UciDataPacket) { - match packet.try_into() { - Ok(data_rcv) => { - let _ = self.data_rcv_notf_sender.send(data_rcv); + async fn handle_data_rcv(&mut self, packet: UciDataPacket) { + if let Ok(data) = DataRcvNotification::try_from(packet.clone()) { + match self.get_session_id(&data.session_token).await { + Ok(session_id) => { + let _ = self.data_rcv_notf_sender.send(DataRcvNotification { + session_token: session_id, + status: data.status, + uci_sequence_num: data.uci_sequence_num, + source_address: data.source_address, + payload: data.payload, + }); + } + Err(e) => { + error!("Unable to find session Id, error {:?}", e); + } } - Err(e) => { - error!("Unable to parse incoming Data packet, error {:?}", e); + } else if let Ok(data) = RadarDataRcvNotification::try_from(packet.clone()) { + match self.get_session_id(&data.session_token).await { + Ok(session_id) => { + let _ = self.radar_data_rcv_notf_sender.send(RadarDataRcvNotification { + session_token: session_id, + status: data.status, + radar_data_type: data.radar_data_type, + number_of_sweeps: data.number_of_sweeps, + samples_per_sweep: data.samples_per_sweep, + bits_per_sample: data.bits_per_sample, + sweep_offset: data.sweep_offset, + sweep_data: data.sweep_data, + }); + } + Err(e) => { + error!("Unable to find session Id, error {:?}", e); + } } + } else { + error!("Unable to parse incoming Data packet, packet {:?}", packet); } } @@ -1371,6 +1497,9 @@ enum UciManagerCmd { SetDataRcvNotificationSender { data_rcv_notf_sender: mpsc::UnboundedSender<DataRcvNotification>, }, + SetRadarDataRcvNotificationSender { + radar_data_rcv_notf_sender: mpsc::UnboundedSender<RadarDataRcvNotification>, + }, OpenHal, CloseHal { force: bool, @@ -1383,7 +1512,7 @@ enum UciManagerCmd { }, } -#[cfg(any(test))] +#[cfg(test)] mod tests { use super::*; @@ -1392,12 +1521,13 @@ mod tests { use uwb_uci_packets::{SessionGetCountCmdBuilder, SessionGetCountRspBuilder}; use crate::params::uci_packets::{ - AppConfigStatus, AppConfigTlvType, CapTlvType, Controlee, DataRcvStatusCode, - DataTransferNtfStatusCode, StatusCode, + AppConfigStatus, AppConfigTlvType, BitsPerSample, CapTlvType, Controlee, DataRcvStatusCode, + DataTransferNtfStatusCode, RadarDataType, StatusCode, }; use crate::params::UwbAddress; use crate::uci::mock_uci_hal::MockUciHal; use crate::uci::mock_uci_logger::{MockUciLogger, UciLogEvent}; + use crate::uci::notification::RadarSweepData; use crate::uci::uci_logger::NopUciLogger; use crate::utils::init_test_logging; @@ -1582,6 +1712,7 @@ mod tests { .await; let expected_result = GetDeviceInfoResponse { + status, uci_version, mac_version, phy_version, @@ -1897,6 +2028,47 @@ mod tests { } #[tokio::test] + async fn test_session_set_hybrid_config_ok() { + let session_id = 0x123; + let session_token = 0x123; + let number_of_phases = 0x02; + let update_time = UpdateTime::new(&[0x0; 8]).unwrap(); + let phase_list = vec![ + PhaseList { session_token: 0x12, start_slot_index: 0x13, end_slot_index: 0x01 }, + PhaseList { session_token: 0x14, start_slot_index: 0x13, end_slot_index: 0x01 }, + ]; + let phase_list_clone = phase_list.clone(); + + let (uci_manager, mut mock_hal) = setup_uci_manager_with_session_active( + |mut hal| async move { + let cmd = UciCommand::SessionSetHybridConfig { + session_token, + number_of_phases, + update_time, + phase_list: phase_list_clone, + }; + let resp = + into_uci_hal_packets(uwb_uci_packets::SessionSetHybridConfigRspBuilder { + status: uwb_uci_packets::StatusCode::UciStatusOk, + }); + + hal.expected_send_command(cmd, resp, Ok(())); + }, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + session_id, + session_token, + ) + .await; + + let result = uci_manager + .session_set_hybrid_config(session_token, number_of_phases, update_time, phase_list) + .await; + assert!(result.is_ok()); + assert!(mock_hal.wait_expected_calls_done().await); + } + + #[tokio::test] async fn test_session_get_app_config_ok() { let session_id = 0x123; let session_token = 0x123; @@ -2197,6 +2369,76 @@ mod tests { } #[tokio::test] + async fn test_android_set_radar_config_ok() { + let session_id = 0x123; + let session_token = 0x123; + let config_tlv = + RadarConfigTlv { cfg_id: RadarConfigTlvType::SamplesPerSweep, v: vec![0x12, 0x34] }; + let config_tlv_clone = config_tlv.clone(); + + let (uci_manager, mut mock_hal) = setup_uci_manager_with_session_initialized( + |mut hal| async move { + let cmd = UciCommand::AndroidSetRadarConfig { + session_token, + config_tlvs: vec![config_tlv_clone], + }; + let resp = into_uci_hal_packets(uwb_uci_packets::AndroidSetRadarConfigRspBuilder { + status: uwb_uci_packets::StatusCode::UciStatusOk, + cfg_status: vec![], + }); + + hal.expected_send_command(cmd, resp, Ok(())); + }, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + session_id, + session_token, + ) + .await; + + let expected_result = + AndroidRadarConfigResponse { status: StatusCode::UciStatusOk, config_status: vec![] }; + let result = + uci_manager.android_set_radar_config(session_id, vec![config_tlv]).await.unwrap(); + assert_eq!(result, expected_result); + assert!(mock_hal.wait_expected_calls_done().await); + } + + #[tokio::test] + async fn test_android_get_radar_config_ok() { + let session_id = 0x123; + let session_token = 0x123; + let config_id = RadarConfigTlvType::SamplesPerSweep; + let tlv = + RadarConfigTlv { cfg_id: RadarConfigTlvType::SamplesPerSweep, v: vec![0x12, 0x34] }; + let tlv_clone = tlv.clone(); + + let (uci_manager, mut mock_hal) = setup_uci_manager_with_session_initialized( + |mut hal| async move { + let cmd = + UciCommand::AndroidGetRadarConfig { session_token, radar_cfg: vec![config_id] }; + let resp = into_uci_hal_packets(uwb_uci_packets::AndroidGetRadarConfigRspBuilder { + status: uwb_uci_packets::StatusCode::UciStatusOk, + tlvs: vec![tlv_clone], + }); + + hal.expected_send_command(cmd, resp, Ok(())); + }, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + session_id, + session_token, + ) + .await; + + let expected_result = vec![tlv]; + let result = + uci_manager.android_get_radar_config(session_id, vec![config_id]).await.unwrap(); + assert_eq!(result, expected_result); + assert!(mock_hal.wait_expected_calls_done().await); + } + + #[tokio::test] async fn test_raw_uci_cmd_vendor_gid_ok() { let mt = 0x1; let gid = 0xF; // Vendor reserved GID. @@ -2346,7 +2588,7 @@ mod tests { 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()); + resp_payload_expected.extend(resp_payload_fragment_2.clone()); let (uci_manager, mut mock_hal) = setup_uci_manager_with_open_hal( |mut hal| async move { @@ -2623,7 +2865,7 @@ mod tests { let app_data = vec![0x01, 0x02, 0x03]; let data_rcv_payload = vec![ 0x05, 0x00, 0x00, 0x00, // SessionToken - 0x00, // DataRcvStatusCode + 0x00, // StatusCode 0xa0, 0xb0, 0xc0, 0xd0, 0xa1, 0xb1, 0xc1, 0xd1, // MacAddress 0x0a, 0x00, // UciSequenceNumber 0x03, 0x00, // AppDataLen @@ -2633,8 +2875,8 @@ mod tests { // Setup the DataPacketRcv (Rx by HAL) and the expected DataRcvNotification. let data_packet_rcv = build_uci_packet(mt_data, pbf, dpf, oid, data_rcv_payload); let expected_data_rcv_notification = DataRcvNotification { - session_token, - status: DataRcvStatusCode::UciStatusSuccess, + session_token: session_id, + status: StatusCode::UciStatusOk, uci_sequence_num, source_address, payload: app_data, @@ -2683,7 +2925,7 @@ mod tests { let app_data_fragment_1_len = 200; let mut data_rcv_payload_fragment_1: Vec<u8> = vec![ 0x05, 0x00, 0x00, 0x00, // SessionToken - 0x00, // DataRcvStatusCode + 0x00, // StatusCode 0xa0, 0xb0, 0xc0, 0xd0, 0xa1, 0xb1, 0xc1, 0xd1, // MacAddress 0x0a, 0x00, // UciSequenceNumber 0x2c, 0x01, // AppData Length (300) @@ -2704,8 +2946,8 @@ mod tests { let data_packet_rcv_fragment_2 = build_uci_packet(mt_data, pbf_fragment_2, dpf, oid, data_rcv_payload_fragment_2); let expected_data_rcv_notification = DataRcvNotification { - session_token, - status: DataRcvStatusCode::UciStatusSuccess, + session_token: session_id, + status: StatusCode::UciStatusOk, uci_sequence_num, source_address, payload: app_data, @@ -2743,6 +2985,109 @@ mod tests { #[tokio::test] async fn test_data_packet_recv_bad_payload_len_failure() {} + // Test Radar Data packet receive for a single packet (on an active UWB session). + #[tokio::test] + async fn test_radar_data_packet_recv_ok() { + let mt_data = 0x0; + let pbf = 0x0; + let dpf = 0xf; + let oid = 0x0; + let session_id = 0x3; + let session_token = 0x5; + let radar_data_type = RadarDataType::RadarSweepSamples; + let number_of_sweeps = 0x02; + let samples_per_sweep = 0x02; + let bits_per_sample = BitsPerSample::Value32; + let sweep_offset = 0x0; + let sequence_number_1 = 0xa; + let sequence_number_2 = 0xb; + let timestamp_1 = 0xc; + let timestamp_2 = 0xd; + let vendor_specific_data_1 = vec![0x0b]; + let vendor_specific_data_2 = vec![0x0b, 0x0c]; + let sample_data_1 = vec![0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa]; + let sample_data_2 = vec![0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]; + let radar_data_rcv_payload = vec![ + 0x05, 0x00, 0x00, 0x00, // session_handle + 0x00, // status + 0x00, // radar data type + 0x02, // number of sweeps + 0x02, // samples per sweep + 0x00, // bits per sample + 0x00, 0x00, // sweep offset + 0x10, 0x11, // sweep data size + // sweep data 1 + 0x0a, 0x00, 0x00, 0x00, // sequence number + 0x0c, 0x00, 0x00, 0x00, // timestamp + 0x01, // vendor specific data length + 0x0b, // vendor specific data + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, // sample data + // sweep data 2 + 0x0b, 0x00, 0x00, 0x00, // sequence number + 0x0d, 0x00, 0x00, 0x00, // timestamp + 0x02, // vendor specific data length + 0x0b, 0x0c, // vendor specific data + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // sample data + ]; + + // Setup the DataPacketRcv (Rx by HAL) and the expected DataRcvNotification. + let radar_data_packet_rcv = + build_uci_packet(mt_data, pbf, dpf, oid, radar_data_rcv_payload); + let expected_radar_data_rcv_notification = RadarDataRcvNotification { + session_token: session_id, + status: DataRcvStatusCode::UciStatusSuccess, + radar_data_type, + number_of_sweeps, + samples_per_sweep, + bits_per_sample, + sweep_offset, + sweep_data: vec![ + RadarSweepData { + sequence_number: sequence_number_1, + timestamp: timestamp_1, + vendor_specific_data: vendor_specific_data_1, + sample_data: sample_data_1, + }, + RadarSweepData { + sequence_number: sequence_number_2, + timestamp: timestamp_2, + vendor_specific_data: vendor_specific_data_2, + sample_data: sample_data_2, + }, + ], + }; + + // Setup an active UWBS session over which the DataPacket will be received by the Host. + let (mut uci_manager, mut mock_hal) = setup_uci_manager_with_session_active( + |_| async move {}, + UciLoggerMode::Disabled, + mpsc::unbounded_channel::<UciLogEvent>().0, + session_id, + session_token, + ) + .await; + + let (radar_data_rcv_notification_sender, mut radar_data_rcv_notification_receiver) = + mpsc::unbounded_channel::<RadarDataRcvNotification>(); + uci_manager + .set_radar_data_rcv_notification_sender(radar_data_rcv_notification_sender) + .await; + + // Inject the UCI DataPacketRcv into HAL. + let result = mock_hal.receive_packet(radar_data_packet_rcv); + assert!(result.is_ok()); + + // UciManager should send a DataRcvNotification (for the valid Rx packet). + let result = tokio::time::timeout( + Duration::from_millis(100), + radar_data_rcv_notification_receiver.recv(), + ) + .await; + assert!(result.is_ok()); + assert_eq!(result.unwrap(), Some(expected_radar_data_rcv_notification)); + assert!(mock_hal.wait_expected_calls_done().await); + } + #[tokio::test] async fn test_data_packet_send_ok() { // Test Data packet send for a single packet (on a UWB session). @@ -2777,8 +3122,7 @@ mod tests { ntfs.append(&mut into_uci_hal_packets( uwb_uci_packets::DataTransferStatusNtfBuilder { session_token, - // TODO(b/282230468): Remove the u16-to-u8 conversion once spec is updated. - uci_sequence_number: uci_sequence_number.try_into().unwrap(), + uci_sequence_number, status, tx_count, }, @@ -2868,8 +3212,7 @@ mod tests { ntfs.append(&mut into_uci_hal_packets( uwb_uci_packets::DataTransferStatusNtfBuilder { session_token, - // TODO(b/282230468): Remove the u16-to-u8 conversion once spec is updated. - uci_sequence_number: uci_sequence_number.try_into().unwrap(), + uci_sequence_number, status, tx_count, }, @@ -2932,8 +3275,7 @@ mod tests { ntfs.append(&mut into_uci_hal_packets( uwb_uci_packets::DataTransferStatusNtfBuilder { session_token, - // TODO(b/282230468): Remove the u16-to-u8 conversion once spec is updated. - uci_sequence_number: uci_sequence_number.try_into().unwrap(), + uci_sequence_number, status, tx_count, }, 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 4b1cf25..27a213b 100644 --- a/src/rust/uwb_core/src/uci/uci_manager_sync.rs +++ b/src/rust/uwb_core/src/uci/uci_manager_sync.rs @@ -26,18 +26,21 @@ use tokio::task; use crate::error::{Error, Result}; use crate::params::{ - AppConfigTlv, AppConfigTlvType, CapTlv, CoreSetConfigResponse, CountryCode, DeviceConfigId, - DeviceConfigTlv, GetDeviceInfoResponse, PowerStats, RawUciMessage, ResetConfig, SessionId, - SessionState, SessionType, SessionUpdateDtTagRangingRoundsResponse, SetAppConfigResponse, - UpdateMulticastListAction, + AndroidRadarConfigResponse, AppConfigTlv, AppConfigTlvType, CapTlv, CoreSetConfigResponse, + CountryCode, DeviceConfigId, DeviceConfigTlv, GetDeviceInfoResponse, PowerStats, + RadarConfigTlv, RadarConfigTlvType, RawUciMessage, ResetConfig, SessionId, SessionState, + SessionType, SessionUpdateDtTagRangingRoundsResponse, SetAppConfigResponse, + UpdateMulticastListAction, UpdateTime, }; #[cfg(any(test, feature = "mock-utils"))] use crate::uci::mock_uci_manager::MockUciManager; -use crate::uci::notification::{CoreNotification, DataRcvNotification, SessionNotification}; +use crate::uci::notification::{ + CoreNotification, DataRcvNotification, RadarDataRcvNotification, 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::Controlees; +use uwb_uci_packets::{Controlees, PhaseList}; /// The NotificationManager processes UciNotification relayed from UciManagerSync in a sync fashion. /// The UciManagerSync assumes the NotificationManager takes the responsibility to properly handle @@ -59,6 +62,12 @@ pub trait NotificationManager: 'static { &mut self, data_rcv_notification: DataRcvNotification, ) -> Result<()>; + + /// Callback for RadarDataRcvNotification. + fn on_radar_data_rcv_notification( + &mut self, + radar_data_rcv_notification: RadarDataRcvNotification, + ) -> Result<()>; } /// Builder for NotificationManager. Builder is sent between threads. @@ -74,6 +83,7 @@ struct NotificationDriver<U: NotificationManager> { session_notification_receiver: mpsc::UnboundedReceiver<SessionNotification>, vendor_notification_receiver: mpsc::UnboundedReceiver<RawUciMessage>, data_rcv_notification_receiver: mpsc::UnboundedReceiver<DataRcvNotification>, + radar_data_rcv_notification_receiver: mpsc::UnboundedReceiver<RadarDataRcvNotification>, notification_manager: U, } impl<U: NotificationManager> NotificationDriver<U> { @@ -82,6 +92,7 @@ impl<U: NotificationManager> NotificationDriver<U> { session_notification_receiver: mpsc::UnboundedReceiver<SessionNotification>, vendor_notification_receiver: mpsc::UnboundedReceiver<RawUciMessage>, data_rcv_notification_receiver: mpsc::UnboundedReceiver<DataRcvNotification>, + radar_data_rcv_notification_receiver: mpsc::UnboundedReceiver<RadarDataRcvNotification>, notification_manager: U, ) -> Self { Self { @@ -89,6 +100,7 @@ impl<U: NotificationManager> NotificationDriver<U> { session_notification_receiver, vendor_notification_receiver, data_rcv_notification_receiver, + radar_data_rcv_notification_receiver, notification_manager, } } @@ -115,6 +127,11 @@ impl<U: NotificationManager> NotificationDriver<U> { error!("NotificationDriver: OnDataRcv callback error: {:?}",e); }); } + Some(data) = self.radar_data_rcv_notification_receiver.recv() =>{ + self.notification_manager.on_radar_data_rcv_notification(data).unwrap_or_else(|e|{ + error!("NotificationDriver: OnRadarDataRcv callback error: {:?}",e); + }); + } else =>{ debug!("NotificationDriver dropping."); break; @@ -149,11 +166,16 @@ impl<U: UciManager> UciManagerSync<U> { mpsc::unbounded_channel::<RawUciMessage>(); let (data_rcv_notification_sender, data_rcv_notification_receiver) = mpsc::unbounded_channel::<DataRcvNotification>(); + let (radar_data_rcv_notification_sender, radar_data_rcv_notification_receiver) = + mpsc::unbounded_channel::<RadarDataRcvNotification>(); 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; + self.uci_manager + .set_radar_data_rcv_notification_sender(radar_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>(); @@ -186,6 +208,7 @@ impl<U: UciManager> UciManagerSync<U> { session_notification_receiver, vendor_notification_receiver, data_rcv_notification_receiver, + radar_data_rcv_notification_receiver, notification_manager, ); local.spawn_local(async move { @@ -204,7 +227,7 @@ impl<U: UciManager> UciManagerSync<U> { 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(&self) -> Result<()> { + pub fn open_hal(&self) -> Result<GetDeviceInfoResponse> { self.runtime_handle.block_on(self.uci_manager.open_hal()) } @@ -342,6 +365,26 @@ impl<U: UciManager> UciManagerSync<U> { self.runtime_handle.block_on(self.uci_manager.android_get_power_stats()) } + /// Set radar config. Android-specific method. + pub fn android_set_radar_config( + &self, + session_id: SessionId, + config_tlvs: Vec<RadarConfigTlv>, + ) -> Result<AndroidRadarConfigResponse> { + self.runtime_handle + .block_on(self.uci_manager.android_set_radar_config(session_id, config_tlvs)) + } + + /// Get radar config. Android-specific method. + pub fn android_get_radar_config( + &self, + session_id: SessionId, + config_ids: Vec<RadarConfigTlvType>, + ) -> Result<Vec<RadarConfigTlv>> { + self.runtime_handle + .block_on(self.uci_manager.android_get_radar_config(session_id, config_ids)) + } + /// Send a raw UCI command. pub fn raw_uci_cmd( &self, @@ -368,10 +411,27 @@ impl<U: UciManager> UciManagerSync<U> { app_payload_data, )) } + /// Get session token for session id. pub fn get_session_token(&self, session_id: SessionId) -> Result<u32> { self.runtime_handle.block_on(self.uci_manager.get_session_token_from_session_id(session_id)) } + + /// Send UCI command for setting hybrid configuration + pub fn session_set_hybrid_config( + &self, + session_id: SessionId, + number_of_phases: u8, + update_time: UpdateTime, + phase_list: Vec<PhaseList>, + ) -> Result<()> { + self.runtime_handle.block_on(self.uci_manager.session_set_hybrid_config( + session_id, + number_of_phases, + update_time, + phase_list, + )) + } } impl UciManagerSync<UciManagerImpl> { @@ -432,6 +492,7 @@ mod tests { use crate::params::uci_packets::GetDeviceInfoResponse; use crate::uci::mock_uci_manager::MockUciManager; use crate::uci::{CoreNotification, UciNotification}; + use uwb_uci_packets::StatusCode::UciStatusOk; /// Mock NotificationManager forwarding notifications received. /// The nonsend_counter is deliberately !send to check UciManagerSync::redirect_notification. @@ -467,6 +528,13 @@ mod tests { self.nonsend_counter.replace_with(|&mut prev| prev + 1); Ok(()) } + fn on_radar_data_rcv_notification( + &mut self, + _data_rcv_notf: RadarDataRcvNotification, + ) -> Result<()> { + self.nonsend_counter.replace_with(|&mut prev| prev + 1); + Ok(()) + } } /// Builder for MockNotificationManager. @@ -499,17 +567,20 @@ mod tests { let test_rt = Builder::new_multi_thread().enable_all().build().unwrap(); 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 { + let get_device_info_rsp = GetDeviceInfoResponse { + status: UciStatusOk, uci_version: 0, mac_version: 0, phy_version: 0, uci_test_version: 0, vendor_spec_info: vec![], - })); + }; + + uci_manager_impl.expect_open_hal( + vec![UciNotification::Core(CoreNotification::DeviceStatus(DeviceStateReady))], + Ok(get_device_info_rsp.clone()), + ); + uci_manager_impl.expect_core_get_device_info(Ok(get_device_info_rsp)); let uci_manager_sync = UciManagerSync::new_mock( uci_manager_impl, test_rt.handle().to_owned(), diff --git a/src/rust/uwb_uci_packets/src/lib.rs b/src/rust/uwb_uci_packets/src/lib.rs index 38b0910..eab7f9f 100644 --- a/src/rust/uwb_uci_packets/src/lib.rs +++ b/src/rust/uwb_uci_packets/src/lib.rs @@ -53,6 +53,11 @@ 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; +// Radar field lengths +pub const UCI_RADAR_SEQUENCE_NUMBER_LEN: usize = 4; +pub const UCI_RADAR_TIMESTAMP_LEN: usize = 4; +pub const UCI_RADAR_VENDOR_DATA_LEN_LEN: usize = 1; + #[derive(Debug, Clone, PartialEq, FromPrimitive)] pub enum TimeStampLength { Timestamp40Bit = 0x0, @@ -123,8 +128,8 @@ impl DlTdoaRangingMeasurement { 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)?, + DTAnchorLocationType::Wgs84 => extract_vec(bytes, &mut ptr, 12)?, + DTAnchorLocationType::Relative => extract_vec(bytes, &mut ptr, 10)?, _ => vec![], }; let active_ranging_rounds = ((message_control >> 7) & 0xf) as u8; @@ -432,16 +437,21 @@ impl RawUciControlPacket { } } -// 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 is_uci_data_packet(message_type: MessageType) -> bool { + message_type == MessageType::Data +} + +fn is_data_rcv_or_radar_format(data_packet_format: DataPacketFormat) -> bool { + data_packet_format == DataPacketFormat::DataRcv + || data_packet_format == DataPacketFormat::RadarDataMessage } -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()?, - ) { +fn try_into_data_payload( + packet: UciPacketHal, + expected_data_packet_format: DataPacketFormat, +) -> Result<Bytes> { + let dpf: DataPacketFormat = packet.get_group_id_or_data_packet_format().try_into()?; + if is_uci_data_packet(packet.get_message_type()) && dpf == expected_data_packet_format { Ok(packet.to_bytes().slice(UCI_PACKET_HAL_HEADER_LEN..)) } else { error!("Received unexpected data packet fragment: {:?}", packet); @@ -459,12 +469,17 @@ impl TryFrom<Vec<UciPacketHal>> for UciDataPacket { return Err(Error::InvalidPacketError); } + let dpf: DataPacketFormat = packets[0].get_group_id_or_data_packet_format().try_into()?; + if !is_data_rcv_or_radar_format(dpf) { + error!("Unexpected data packet format {:?}", dpf); + } + // 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(); + payload_buf = [payload_buf, try_into_data_payload(packet, dpf)?].concat().into(); } // Create assembled |UciDataPacket| and convert to bytes again since we need to @@ -472,7 +487,7 @@ impl TryFrom<Vec<UciPacketHal>> for UciDataPacket { UciDataPacket::parse( &UciDataPacketBuilder { message_type: MessageType::Data, - data_packet_format: DataPacketFormat::DataRcv, + data_packet_format: dpf, payload: Some(payload_buf.into()), } .build() @@ -824,10 +839,7 @@ pub fn build_session_update_controller_multicast_list_cmd( ) -> Result<SessionUpdateControllerMulticastListCmd> { let mut controlees_buf = BytesMut::new(); match controlees { - Controlees::NoSessionKey(controlee_v1) - if action == UpdateMulticastListAction::AddControlee - || action == UpdateMulticastListAction::RemoveControlee => - { + Controlees::NoSessionKey(controlee_v1) => { 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)); @@ -868,6 +880,17 @@ impl Drop for AppConfigTlv { } } +// Radar data 'bits per sample' field isn't a raw value, instead it's an enum +// that maps to the raw value. We need this mapping to get the max sample size +// length. +pub fn radar_bytes_per_sample_value(bps: BitsPerSample) -> u8 { + match bps { + BitsPerSample::Value32 => 4, + BitsPerSample::Value48 => 6, + BitsPerSample::Value64 => 8, + } +} + #[cfg(test)] mod tests { use super::*; @@ -1012,7 +1035,7 @@ mod tests { // 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) + 0x53, 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..) @@ -1041,9 +1064,10 @@ mod tests { 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) + 0x01, 0x02, 0x01, 0x02, // 4(Anchor Location) + 0x05, 0x07, 0x09, 0x0a, // 4(Active Ranging Rounds..) + 0x01, 0x02, 0x05, 0x07, // 4(Active Ranging Rounds..) + 0x09, 0x05, // 2(Active Ranging Rounds) ]; let measurements = ShortAddressDlTdoaRangingMeasurement::parse(&bytes, 2).unwrap(); @@ -1053,7 +1077,7 @@ mod tests { 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.message_control, 0x0553); assert_eq!(measurement_1.block_index, 0x0502); assert_eq!(measurement_1.round_index, 0x07); assert_eq!(measurement_1.nlos, 0x09); @@ -1100,11 +1124,11 @@ mod tests { 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] + measurement_2.dt_anchor_location, + vec![0x07, 0x09, 0x07, 0x09, 0x05, 0x07, 0x09, 0x0a, 0x01, 0x02, 0x01, 0x02] ); assert_eq!( - measurement_1.ranging_rounds, + measurement_2.ranging_rounds, vec![0x05, 0x07, 0x09, 0x0a, 0x01, 0x02, 0x05, 0x07, 0x09, 0x05,] ); } diff --git a/src/rust/uwb_uci_packets/uci_packets.pdl b/src/rust/uwb_uci_packets/uci_packets.pdl index 12bdb6c..1f3969c 100644 --- a/src/rust/uwb_uci_packets/uci_packets.pdl +++ b/src/rust/uwb_uci_packets/uci_packets.pdl @@ -22,6 +22,7 @@ enum GroupId : 4 { enum DataPacketFormat: 4 { DATA_SND = 0x01, DATA_RCV = 0x02, + RADAR_DATA_MESSAGE = 0x0f, } // Define a merged enum across GroupId & DataPacketFormat as they are at the same bits in @@ -87,6 +88,8 @@ enum AndroidOpCode : 6 { ANDROID_GET_POWER_STATS = 0x0, ANDROID_SET_COUNTRY_CODE = 0x1, ANDROID_FIRA_RANGE_DIAGNOSTICS = 0x2, + ANDROID_RADAR_SET_APP_CONFIG = 0x11, + ANDROID_RADAR_GET_APP_CONFIG = 0x12, } enum StatusCode : 8 { @@ -144,6 +147,7 @@ enum StatusCode : 8 { UCI_STATUS_ERROR_CCC_SE_BUSY = 0x50, UCI_STATUS_ERROR_CCC_LIFECYCLE = 0x51, UCI_STATUS_ERROR_STOPPED_DUE_TO_OTHER_SESSION_CONFLICT = 0x52, + UCI_STATUS_REGULATION_UWB_OFF = 0x53, }, // For internal usage, we will use 0xFF as default. @@ -172,6 +176,7 @@ enum DataTransferNtfStatusCode : 8 { 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, + UCI_DATA_TRANSFER_STATUS_INVALID_FORMAT = 0x07, } enum ResetConfig : 8 { @@ -245,11 +250,14 @@ enum AppConfigTlvType : 8 { MIN_FRAMES_PER_RR = 0x3A, MTU_SIZE = 0x3B, INTER_FRAME_INTERVAL = 0x3C, - RFU_APP_CFG_TLV_TYPE_RANGE_1 = 0x3D..0x44, + RFU_APP_CFG_TLV_TYPE_RANGE_1 = 0x3D..0x42, + DLTDOA_BLOCK_STRIDING = 0x43, + RFU_APP_CFG_TLV_TYPE_RANGE_2 = 0x44, SESSION_KEY = 0x45, SUBSESSION_KEY = 0x46, SESSION_DATA_TRANSFER_STATUS_NTF_CONFIG = 0x47, - RFU_APP_CFG_TLV_TYPE_RANGE_2 = 0x48..0x9F, + SESSION_TIME_BASE = 0x48, + RFU_APP_CFG_TLV_TYPE_RANGE_3 = 0x49..0x9F, VENDOR_SPECIFIC_APP_CFG_TLV_TYPE_RANGE_1 = 0xA0..0xDF { // CCC specific @@ -263,7 +271,7 @@ enum AppConfigTlvType : 8 { }, // Reserved for extension IDs. - RFU_APP_CFG_TLV_TYPE_RANGE_3 = 0xE0..0xE2, + RFU_APP_CFG_TLV_TYPE_RANGE_4 = 0xE0..0xE2, VENDOR_SPECIFIC_APP_CFG_TLV_TYPE_RANGE_2 = 0xE3..0xFF { // Interleaving ratio if AOA_RESULT_REQ is set to 0xF0. @@ -315,6 +323,8 @@ enum CapTlvType : 8 { CCC_SUPPORTED_PULSE_SHAPE_COMBOS = 0xA6, CCC_SUPPORTED_RAN_MULTIPLIER = 0xA7, CCC_SUPPORTED_MAX_RANGING_SESSION_NUMBER = 0xA8, + // RADAR specific + RADAR_SUPPORT = 0xB0 }, SUPPORTED_POWER_STATS = 0xC0, @@ -416,9 +426,12 @@ enum MulticastUpdateStatusCode : 8 { STATUS_ERROR_MULTICAST_LIST_FULL = 0x01, STATUS_ERROR_KEY_FETCH_FAIL = 0x02, STATUS_ERROR_SUB_SESSION_ID_NOT_FOUND = 0x03, - STATUS_ERROR_SUB_SESSION_KEY_NOT_FOUND = 0x05, - STATUS_ERROR_SUB_SESSION_KEY_NOT_APPLICABLE = 0x06, - STATUS_ERROR_SESSION_KEY_NOT_FOUND = 0x07, + STATUS_ERROR_SUB_SESSION_KEY_NOT_FOUND = 0x04, + STATUS_ERROR_SUB_SESSION_KEY_NOT_APPLICABLE = 0x05, + STATUS_ERROR_SESSION_KEY_NOT_FOUND = 0x06, + STATUS_ERROR_ADDRESS_NOT_FOUND = 0x07, + STATUS_ERROR_ADDRESS_ALREADY_PRESENT = 0x08, + RFU_STATUS_CODE_RANGE_1 = 0x09..0xFF, } enum MacAddressIndicator : 8 { @@ -428,8 +441,13 @@ enum MacAddressIndicator : 8 { enum SessionType: 8 { FIRA_RANGING_SESSION = 0x00, - FIRA_DATA_TRANSFER = 0x01, + FIRA_RANGING_AND_IN_BAND_DATA_SESSION = 0x01, + FIRA_DATA_TRANSFER_SESSION = 0x02, + FIRA_RANGING_ONLY_PHASE = 0x03, + FIRA_IN_BAND_DATA_PHASE = 0x04, + FIRA_RANGING_WITH_DATA_PHASE = 0x05, CCC = 0xA0, + RADAR_SESSION = 0xA1, DEVICE_TEST_MODE = 0xD0, } @@ -519,7 +537,7 @@ packet UciDataSnd : UciDataPacket (data_packet_format = DATA_SND, message_type = packet UciDataRcv : UciDataPacket (data_packet_format = DATA_RCV, message_type = DATA) { session_token: 32, // Session ID or Session Handle (based on UWBS version) - status: DataRcvStatusCode, + status: StatusCode, source_mac_address: 64, uci_sequence_number: 16, _size_(data): 16, @@ -922,6 +940,23 @@ packet SessionUpdateControllerMulticastListCmd : SessionConfigCommand (opcode = _payload_, } +struct PhaseList { + session_token: 32, + start_slot_index: 16, + end_slot_index: 16, +} + +packet SessionSetHybridConfigCmd : SessionConfigCommand (opcode = 0x0c) { //SESSION_SET_HUS_CONFIG + session_token: 32, + number_of_phases: 8, + update_time: 8[8], + phase_list: PhaseList[], +} + +packet SessionSetHybridConfigRsp : SessionConfigResponse (opcode = 0x0c) { //SESSION_SET_HUS_CONFIG + status: StatusCode, +} + struct SessionUpdateControllerMulticastListCmdPayload { _count_(controlees): 8, controlees: Controlee[], @@ -973,7 +1008,7 @@ test DataCreditNtf { packet DataTransferStatusNtf : SessionControlNotification (opcode = 0x05) { // SESSION_DATA_TRANSFER_STATUS_NTF session_token: 32, // Session ID or Session Handle (based on UWBS version) - uci_sequence_number: 8, + uci_sequence_number: 16, status: DataTransferNtfStatusCode, tx_count: 8, } @@ -992,11 +1027,12 @@ test SessionQueryMaxDataSizeCmd { packet SessionQueryMaxDataSizeRsp : SessionConfigResponse (opcode = 0xB) { //QUER_MAX_DATA_SIZE session_token: 32, // Session ID or Session Handle (based on UWBS version) + status: StatusCode, max_data_size: 16, } test SessionQueryMaxDataSizeRsp { - "\x41\x0B\x00\x06\x00\x00\x00\x00\x0E7\0x07", + "\x41\x0B\x00\x06\x00\x00\x00\x00\x00\x0E7\0x07", } packet SessionStartCmd : SessionControlCommand (opcode = 0x0) { //RANGE_START @@ -1348,3 +1384,88 @@ packet UciVendor_F_Notification : UciNotification (group_id = VENDOR_RESERVED_F) packet TestNotification : UciNotification (group_id = TEST) { _payload_, } + +enum RadarDataType : 8 { + RADAR_SWEEP_SAMPLES = 0x00, +} + +enum RadarConfigTlvType : 8 { + RADAR_TIMING_PARAMS = 0x00, + SAMPLES_PER_SWEEP = 0x01, + CHANNEL_NUMBER = 0x02, + SWEEP_OFFSET = 0x03, + RFRAME_CONFIG = 0x04, + PREAMBLE_DURATION = 0x05, + PREAMBLE_CODE_INDEX = 0x06, + SESSION_PRIORITY = 0x07, + BITS_PER_SAMPLE = 0x08, + PRF_MODE = 0x09, + NUMBER_OF_BURSTS = 0x0A, + RADAR_DATA_TYPE = 0x0B, + + RFU_RADAR_APP_CFG_TLV_TYPE_RANGE = 0x0C..0x9F, + + VENDOR_SPECIFIC_RADAR_APP_CFG_TLV_TYPE_RANGE = 0xA0..0xDF, +} + +struct RadarConfigTlv { + cfg_id: RadarConfigTlvType, + _count_(v): 8, + v: 8[], +} + +struct RadarConfigStatus { + cfg_id: RadarConfigTlvType, + status: StatusCode, +} + +packet AndroidSetRadarConfigCmd: AndroidCommand (opcode = 0x11) { + session_token: 32, + _count_(tlvs): 8, + tlvs: RadarConfigTlv[] +} + +packet AndroidSetRadarConfigRsp : AndroidResponse (opcode = 0x11) { + status: StatusCode, + _count_(cfg_status): 8, + cfg_status: RadarConfigStatus[], +} + +packet AndroidGetRadarConfigCmd: AndroidCommand (opcode = 0x12) { + session_token: 32, + _count_(tlvs): 8, + tlvs: 8[], // RadarConfigTlvType (Infra does not allow array of enums) +} + +packet AndroidGetRadarConfigRsp : AndroidResponse (opcode = 0x12) { + status: StatusCode, + _count_(tlvs): 8, + tlvs: RadarConfigTlv[], +} + +enum BitsPerSample : 8 { + VALUE_32 = 0x00, + VALUE_48 = 0x01, + VALUE_64 = 0x02 +} + +struct RadarSweepDataRaw { + sequence_number: 32, + timestamp: 32, + _count_(vendor_specific_data): 8, + vendor_specific_data: 8[], + sample_data: 8[], +} + +packet RadarDataRcv : UciDataPacket (data_packet_format = RADAR_DATA_MESSAGE, message_type = DATA) { + session_handle: 32, + status: DataRcvStatusCode, + radar_data_type: RadarDataType, + number_of_sweeps: 8, + samples_per_sweep: 8, + bits_per_sample: BitsPerSample, + sweep_offset: 16, + sweep_data_size: 16, + sweep_data: 8[], +} + |