1 // Copyright 2022, The Android Open Source Project 2 // 3 // Licensed under the Apache License, item 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #![allow(missing_docs)] 16 17 use std::collections::HashMap; 18 19 use log::error; 20 21 use crate::params::app_config_params::{AppConfigParams, AppConfigTlvMap}; 22 use crate::params::fira_app_config_params::{ 23 DeviceRole, DeviceType, KeyRotation, MultiNodeMode, RangeDataNtfConfig, StsConfig, 24 }; 25 use crate::params::uci_packets::{AppConfigTlvType, SessionState}; 26 use crate::params::utils::{u16_to_bytes, u32_to_bytes, u8_to_bytes, validate}; 27 use crate::utils::{builder_field, getter_field}; 28 use num_derive::{FromPrimitive, ToPrimitive}; 29 30 const CHAP_IN_RSTU: u16 = 400; // 1 Chap = 400 RSTU. 31 pub(super) const MINIMUM_BLOCK_DURATION_MS: u32 = 96; 32 33 // The constant AppConfigTlv values for CCC. 34 const CCC_DEVICE_TYPE: DeviceType = DeviceType::Controlee; 35 const CCC_STS_CONFIG: StsConfig = StsConfig::Dynamic; 36 const CCC_MULTI_NODE_MODE: MultiNodeMode = MultiNodeMode::OneToMany; 37 const CCC_RANGE_DATA_NTF_CONFIG: RangeDataNtfConfig = RangeDataNtfConfig::Disable; 38 const CCC_DEVICE_ROLE: DeviceRole = DeviceRole::Initiator; 39 const CCC_KEY_ROTATION: KeyRotation = KeyRotation::Enable; 40 const CCC_URSK_TTL: u16 = 0x2D0; 41 42 const DEFAULT_PROTOCOL_VERSION: CccProtocolVersion = CccProtocolVersion { major: 1, minor: 0 }; 43 44 #[derive(Debug, Clone, PartialEq, Eq)] 45 pub struct CccAppConfigParams { 46 protocol_version: CccProtocolVersion, 47 uwb_config: CccUwbConfig, 48 pulse_shape_combo: CccPulseShapeCombo, 49 ran_multiplier: u32, 50 channel_number: CccUwbChannel, 51 chaps_per_slot: ChapsPerSlot, 52 num_responder_nodes: u8, 53 slots_per_rr: u8, 54 sync_code_index: u8, 55 hopping_mode: CccHoppingMode, 56 } 57 58 #[allow(missing_docs)] 59 impl CccAppConfigParams { 60 // Generate the getter methods for all the fields. 61 getter_field!(protocol_version, CccProtocolVersion); 62 getter_field!(uwb_config, CccUwbConfig); 63 getter_field!(pulse_shape_combo, CccPulseShapeCombo); 64 getter_field!(ran_multiplier, u32); 65 getter_field!(channel_number, CccUwbChannel); 66 getter_field!(chaps_per_slot, ChapsPerSlot); 67 getter_field!(num_responder_nodes, u8); 68 getter_field!(slots_per_rr, u8); 69 getter_field!(sync_code_index, u8); 70 getter_field!(hopping_mode, CccHoppingMode); 71 is_config_updatable(config_map: &AppConfigTlvMap, session_state: SessionState) -> bool72 pub fn is_config_updatable(config_map: &AppConfigTlvMap, session_state: SessionState) -> bool { 73 match session_state { 74 SessionState::SessionStateIdle => { 75 // Only ran_multiplier can be updated at idle state. 76 config_map.keys().all(|key| key == &AppConfigTlvType::RangingDuration) 77 } 78 _ => false, 79 } 80 } 81 generate_config_map(&self) -> AppConfigTlvMap82 pub fn generate_config_map(&self) -> AppConfigTlvMap { 83 debug_assert!(self.is_valid().is_some()); 84 85 HashMap::from([ 86 (AppConfigTlvType::DeviceType, u8_to_bytes(CCC_DEVICE_TYPE as u8)), 87 (AppConfigTlvType::StsConfig, u8_to_bytes(CCC_STS_CONFIG as u8)), 88 (AppConfigTlvType::MultiNodeMode, u8_to_bytes(CCC_MULTI_NODE_MODE as u8)), 89 (AppConfigTlvType::ChannelNumber, u8_to_bytes(self.channel_number as u8)), 90 (AppConfigTlvType::NoOfControlee, u8_to_bytes(self.num_responder_nodes)), 91 ( 92 AppConfigTlvType::SlotDuration, 93 u16_to_bytes((self.chaps_per_slot as u16) * CHAP_IN_RSTU), 94 ), 95 ( 96 AppConfigTlvType::RangingDuration, 97 u32_to_bytes(self.ran_multiplier * MINIMUM_BLOCK_DURATION_MS), 98 ), 99 (AppConfigTlvType::RngDataNtf, u8_to_bytes(CCC_RANGE_DATA_NTF_CONFIG as u8)), 100 (AppConfigTlvType::DeviceRole, u8_to_bytes(CCC_DEVICE_ROLE as u8)), 101 (AppConfigTlvType::PreambleCodeIndex, u8_to_bytes(self.sync_code_index)), 102 (AppConfigTlvType::SlotsPerRr, u8_to_bytes(self.slots_per_rr)), 103 (AppConfigTlvType::KeyRotation, u8_to_bytes(CCC_KEY_ROTATION as u8)), 104 (AppConfigTlvType::HoppingMode, u8_to_bytes(self.hopping_mode as u8)), 105 (AppConfigTlvType::CccRangingProtocolVer, self.protocol_version.clone().into()), 106 (AppConfigTlvType::CccUwbConfigId, u16_to_bytes(self.uwb_config as u16)), 107 (AppConfigTlvType::CccPulseshapeCombo, self.pulse_shape_combo.clone().into()), 108 (AppConfigTlvType::CccUrskTtl, u16_to_bytes(CCC_URSK_TTL)), 109 ]) 110 } 111 is_valid(&self) -> Option<()>112 fn is_valid(&self) -> Option<()> { 113 validate( 114 (1..=32).contains(&self.sync_code_index), 115 "sync_code_index should be between 1 to 32", 116 )?; 117 118 self.ran_multiplier.checked_mul(MINIMUM_BLOCK_DURATION_MS).or_else(|| { 119 error!("ran_multiplier * MINIMUM_BLOCK_DURATION_MS overflows"); 120 None 121 })?; 122 123 Some(()) 124 } 125 } 126 127 pub struct CccAppConfigParamsBuilder { 128 protocol_version: CccProtocolVersion, 129 uwb_config: Option<CccUwbConfig>, 130 pulse_shape_combo: Option<CccPulseShapeCombo>, 131 ran_multiplier: Option<u32>, 132 channel_number: Option<CccUwbChannel>, 133 chaps_per_slot: Option<ChapsPerSlot>, 134 num_responder_nodes: Option<u8>, 135 slots_per_rr: Option<u8>, 136 sync_code_index: Option<u8>, 137 hopping_mode: Option<CccHoppingMode>, 138 } 139 140 #[allow(clippy::new_without_default)] 141 impl CccAppConfigParamsBuilder { new() -> Self142 pub fn new() -> Self { 143 Self { 144 protocol_version: DEFAULT_PROTOCOL_VERSION, 145 uwb_config: None, 146 pulse_shape_combo: None, 147 ran_multiplier: None, 148 channel_number: None, 149 chaps_per_slot: None, 150 num_responder_nodes: None, 151 slots_per_rr: None, 152 sync_code_index: None, 153 hopping_mode: None, 154 } 155 } 156 build(&self) -> Option<AppConfigParams>157 pub fn build(&self) -> Option<AppConfigParams> { 158 let params = CccAppConfigParams { 159 protocol_version: self.protocol_version.clone(), 160 uwb_config: self.uwb_config?, 161 pulse_shape_combo: self.pulse_shape_combo.clone()?, 162 ran_multiplier: self.ran_multiplier?, 163 channel_number: self.channel_number?, 164 chaps_per_slot: self.chaps_per_slot?, 165 num_responder_nodes: self.num_responder_nodes?, 166 slots_per_rr: self.slots_per_rr?, 167 sync_code_index: self.sync_code_index?, 168 hopping_mode: self.hopping_mode?, 169 }; 170 params.is_valid()?; 171 Some(AppConfigParams::Ccc(params)) 172 } 173 from_params(params: &AppConfigParams) -> Option<Self>174 pub fn from_params(params: &AppConfigParams) -> Option<Self> { 175 match params { 176 AppConfigParams::Ccc(params) => Some(Self { 177 protocol_version: params.protocol_version.clone(), 178 uwb_config: Some(params.uwb_config), 179 pulse_shape_combo: Some(params.pulse_shape_combo.clone()), 180 ran_multiplier: Some(params.ran_multiplier), 181 channel_number: Some(params.channel_number), 182 chaps_per_slot: Some(params.chaps_per_slot), 183 num_responder_nodes: Some(params.num_responder_nodes), 184 slots_per_rr: Some(params.slots_per_rr), 185 sync_code_index: Some(params.sync_code_index), 186 hopping_mode: Some(params.hopping_mode), 187 }), 188 _ => None, 189 } 190 } 191 192 // Generate the setter methods for all the fields. 193 builder_field!(protocol_version, CccProtocolVersion); 194 builder_field!(uwb_config, CccUwbConfig, Some); 195 builder_field!(pulse_shape_combo, CccPulseShapeCombo, Some); 196 builder_field!(ran_multiplier, u32, Some); 197 builder_field!(channel_number, CccUwbChannel, Some); 198 builder_field!(chaps_per_slot, ChapsPerSlot, Some); 199 builder_field!(num_responder_nodes, u8, Some); 200 builder_field!(slots_per_rr, u8, Some); 201 builder_field!(sync_code_index, u8, Some); 202 builder_field!(hopping_mode, CccHoppingMode, Some); 203 } 204 205 #[derive(Debug, Clone, PartialEq, Eq)] 206 pub struct CccProtocolVersion { 207 pub major: u8, 208 pub minor: u8, 209 } 210 211 impl From<CccProtocolVersion> for Vec<u8> { from(item: CccProtocolVersion) -> Self212 fn from(item: CccProtocolVersion) -> Self { 213 vec![item.major, item.minor] 214 } 215 } 216 217 #[repr(u16)] 218 #[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)] 219 pub enum CccUwbConfig { 220 Config0 = 0, 221 Config1 = 1, 222 } 223 224 #[derive(Debug, Clone, PartialEq, Eq)] 225 pub struct CccPulseShapeCombo { 226 pub initiator_tx: PulseShape, 227 pub responder_tx: PulseShape, 228 } 229 230 impl From<CccPulseShapeCombo> for Vec<u8> { from(item: CccPulseShapeCombo) -> Self231 fn from(item: CccPulseShapeCombo) -> Self { 232 vec![((item.initiator_tx as u8) << 4) | (item.responder_tx as u8)] 233 } 234 } 235 236 #[repr(u8)] 237 #[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)] 238 pub enum PulseShape { 239 SymmetricalRootRaisedCosine = 0x0, 240 PrecursorFree = 0x1, 241 PrecursorFreeSpecial = 0x2, 242 } 243 244 #[repr(u8)] 245 #[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)] 246 pub enum CccUwbChannel { 247 Channel5 = 5, 248 Channel9 = 9, 249 } 250 251 #[repr(u8)] 252 #[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)] 253 pub enum HoppingConfigMode { 254 None = 0, 255 Continuous = 1, 256 Adaptive = 2, 257 } 258 259 #[repr(u8)] 260 #[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)] 261 pub enum HoppingSequence { 262 Default = 0, 263 Aes = 1, 264 } 265 266 #[repr(u16)] 267 #[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)] 268 pub enum ChapsPerSlot { 269 Value3 = 3, 270 Value4 = 4, 271 Value6 = 6, 272 Value8 = 8, 273 Value9 = 9, 274 Value12 = 12, 275 Value24 = 24, 276 } 277 278 #[repr(u8)] 279 #[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)] 280 pub enum CccHoppingMode { 281 Disable = 0, 282 AdaptiveDefault = 2, 283 ContinuousDefault = 3, 284 AdaptiveAes = 4, 285 ContinuousAes = 5, 286 } 287 288 #[cfg(test)] 289 mod tests { 290 use super::*; 291 292 #[test] test_ok()293 fn test_ok() { 294 let protocol_version = CccProtocolVersion { major: 2, minor: 1 }; 295 let uwb_config = CccUwbConfig::Config0; 296 let pulse_shape_combo = CccPulseShapeCombo { 297 initiator_tx: PulseShape::PrecursorFree, 298 responder_tx: PulseShape::PrecursorFreeSpecial, 299 }; 300 let ran_multiplier = 3; 301 let channel_number = CccUwbChannel::Channel9; 302 let chaps_per_slot = ChapsPerSlot::Value9; 303 let num_responder_nodes = 1; 304 let slots_per_rr = 3; 305 let sync_code_index = 12; 306 let hopping_mode = CccHoppingMode::ContinuousAes; 307 308 let params = CccAppConfigParamsBuilder::new() 309 .protocol_version(protocol_version) 310 .uwb_config(uwb_config) 311 .pulse_shape_combo(pulse_shape_combo.clone()) 312 .ran_multiplier(ran_multiplier) 313 .channel_number(channel_number) 314 .chaps_per_slot(chaps_per_slot) 315 .num_responder_nodes(num_responder_nodes) 316 .slots_per_rr(slots_per_rr) 317 .sync_code_index(sync_code_index) 318 .hopping_mode(hopping_mode) 319 .build() 320 .unwrap(); 321 322 // Verify the generated TLV. 323 let config_map = params.generate_config_map(); 324 let expected_config_map = HashMap::from([ 325 (AppConfigTlvType::DeviceType, u8_to_bytes(CCC_DEVICE_TYPE as u8)), 326 (AppConfigTlvType::StsConfig, u8_to_bytes(CCC_STS_CONFIG as u8)), 327 (AppConfigTlvType::MultiNodeMode, u8_to_bytes(CCC_MULTI_NODE_MODE as u8)), 328 (AppConfigTlvType::ChannelNumber, u8_to_bytes(channel_number as u8)), 329 (AppConfigTlvType::NoOfControlee, u8_to_bytes(num_responder_nodes)), 330 (AppConfigTlvType::SlotDuration, u16_to_bytes((chaps_per_slot as u16) * CHAP_IN_RSTU)), 331 ( 332 AppConfigTlvType::RangingDuration, 333 u32_to_bytes(ran_multiplier * MINIMUM_BLOCK_DURATION_MS), 334 ), 335 (AppConfigTlvType::RngDataNtf, u8_to_bytes(CCC_RANGE_DATA_NTF_CONFIG as u8)), 336 (AppConfigTlvType::DeviceRole, u8_to_bytes(CCC_DEVICE_ROLE as u8)), 337 (AppConfigTlvType::PreambleCodeIndex, u8_to_bytes(sync_code_index)), 338 (AppConfigTlvType::SlotsPerRr, u8_to_bytes(slots_per_rr)), 339 (AppConfigTlvType::KeyRotation, u8_to_bytes(CCC_KEY_ROTATION as u8)), 340 (AppConfigTlvType::HoppingMode, u8_to_bytes(hopping_mode as u8)), 341 (AppConfigTlvType::CccRangingProtocolVer, vec![2, 1]), 342 (AppConfigTlvType::CccUwbConfigId, u16_to_bytes(uwb_config as u16)), 343 (AppConfigTlvType::CccPulseshapeCombo, pulse_shape_combo.into()), 344 (AppConfigTlvType::CccUrskTtl, u16_to_bytes(CCC_URSK_TTL)), 345 ]); 346 assert_eq!(config_map, expected_config_map); 347 348 // Update the value from the params. 349 let updated_ran_multiplier = 5; 350 assert_ne!(ran_multiplier, updated_ran_multiplier); 351 let expected_updated_config_map = HashMap::from([( 352 AppConfigTlvType::RangingDuration, 353 u32_to_bytes(updated_ran_multiplier * MINIMUM_BLOCK_DURATION_MS), 354 )]); 355 356 let updated_params1 = CccAppConfigParamsBuilder::from_params(¶ms) 357 .unwrap() 358 .ran_multiplier(updated_ran_multiplier) 359 .build() 360 .unwrap(); 361 let updated_config_map1 = updated_params1 362 .generate_updated_config_map(¶ms, SessionState::SessionStateIdle) 363 .unwrap(); 364 assert_eq!(updated_config_map1, expected_updated_config_map); 365 } 366 367 #[test] test_update_config()368 fn test_update_config() { 369 let mut builder = CccAppConfigParamsBuilder::new(); 370 builder 371 .protocol_version(CccProtocolVersion { major: 2, minor: 1 }) 372 .uwb_config(CccUwbConfig::Config0) 373 .pulse_shape_combo(CccPulseShapeCombo { 374 initiator_tx: PulseShape::PrecursorFree, 375 responder_tx: PulseShape::PrecursorFreeSpecial, 376 }) 377 .ran_multiplier(3) 378 .channel_number(CccUwbChannel::Channel9) 379 .chaps_per_slot(ChapsPerSlot::Value9) 380 .num_responder_nodes(1) 381 .slots_per_rr(3) 382 .sync_code_index(12) 383 .hopping_mode(CccHoppingMode::ContinuousAes); 384 let params = builder.build().unwrap(); 385 386 builder.ran_multiplier(5); 387 let updated_params = builder.build().unwrap(); 388 // ran_multiplier can be updated at idle state. 389 assert!(updated_params 390 .generate_updated_config_map(¶ms, SessionState::SessionStateIdle) 391 .is_some()); 392 // ran_multiplier cannot be updated at active state. 393 assert!(updated_params 394 .generate_updated_config_map(¶ms, SessionState::SessionStateActive) 395 .is_none()); 396 } 397 } 398