xref: /aosp_15_r20/external/uwb/src/rust/uwb_core/src/params/ccc_app_config_params.rs (revision e0df40009cb5d71e642272d38ba1bb7ffccfce41)
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(&params)
357             .unwrap()
358             .ran_multiplier(updated_ran_multiplier)
359             .build()
360             .unwrap();
361         let updated_config_map1 = updated_params1
362             .generate_updated_config_map(&params, 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(&params, SessionState::SessionStateIdle)
391             .is_some());
392         // ran_multiplier cannot be updated at active state.
393         assert!(updated_params
394             .generate_updated_config_map(&params, SessionState::SessionStateActive)
395             .is_none());
396     }
397 }
398