• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 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 //     https://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 /// A `Chip` is a generic struct that wraps a radio specific
16 /// WirelessChip.` The Chip layer provides for common operations and
17 /// data.
18 ///
19 /// The emulated chip facade is a library that implements the
20 /// controller protocol.
21 ///
22 use crate::wireless::WirelessChipImpl;
23 use netsim_proto::common::ChipKind as ProtoChipKind;
24 use netsim_proto::model::Chip as ProtoChip;
25 use netsim_proto::stats::NetsimRadioStats as ProtoRadioStats;
26 use protobuf::EnumOrUnknown;
27 use std::collections::HashMap;
28 use std::fmt;
29 use std::sync::atomic::{AtomicU32, Ordering};
30 use std::sync::{Arc, OnceLock, RwLock};
31 use std::time::Instant;
32 
33 use super::device::DeviceIdentifier;
34 
35 #[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialOrd, PartialEq)]
36 pub struct ChipIdentifier(pub u32);
37 #[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialOrd, PartialEq)]
38 pub struct FacadeIdentifier(pub u32);
39 
40 impl fmt::Display for ChipIdentifier {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result41     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42         write!(f, "{}", self.0)
43     }
44 }
45 
46 impl fmt::Debug for ChipIdentifier {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result47     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48         write!(f, "{}", self.0)
49     }
50 }
51 
52 impl fmt::Display for FacadeIdentifier {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result53     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54         write!(f, "{}", self.0)
55     }
56 }
57 
58 impl fmt::Debug for FacadeIdentifier {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result59     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60         write!(f, "{}", self.0)
61     }
62 }
63 
64 const INITIAL_CHIP_ID: u32 = 1000;
65 
66 struct ChipManager {
67     ids: AtomicU32,
68     chips: RwLock<HashMap<ChipIdentifier, Arc<Chip>>>,
69 }
70 
71 static CHIP_MANAGER: OnceLock<ChipManager> = OnceLock::new();
72 
get_chip_manager() -> &'static ChipManager73 fn get_chip_manager() -> &'static ChipManager {
74     CHIP_MANAGER.get_or_init(|| ChipManager::new(INITIAL_CHIP_ID))
75 }
76 
77 pub struct CreateParams {
78     pub kind: ProtoChipKind,
79     pub address: String,
80     pub name: Option<String>,
81     pub manufacturer: String,
82     pub product_name: String,
83 }
84 
85 /// Chip contains the common information for each Chip/Controller.
86 /// Radio-specific information is contained in the wireless_chip.
87 pub struct Chip {
88     pub id: ChipIdentifier,
89     pub device_id: DeviceIdentifier,
90     pub wireless_chip: WirelessChipImpl,
91     pub kind: ProtoChipKind,
92     #[allow(dead_code)]
93     pub address: String,
94     pub name: String,
95     // TODO: may not be necessary
96     #[allow(dead_code)]
97     pub device_name: String,
98     // These are patchable
99     pub manufacturer: RwLock<String>,
100     pub product_name: RwLock<String>,
101     pub start: Instant,
102 }
103 
104 impl ChipManager {
new(start: u32) -> Self105     fn new(start: u32) -> Self {
106         ChipManager { ids: AtomicU32::new(start), chips: RwLock::new(HashMap::new()) }
107     }
108 
next_id(&self) -> ChipIdentifier109     fn next_id(&self) -> ChipIdentifier {
110         ChipIdentifier(self.ids.fetch_add(1, Ordering::SeqCst))
111     }
112 }
113 
114 impl Chip {
new( id: ChipIdentifier, device_id: DeviceIdentifier, device_name: &str, create_params: &CreateParams, wireless_chip: WirelessChipImpl, ) -> Self115     fn new(
116         id: ChipIdentifier,
117         device_id: DeviceIdentifier,
118         device_name: &str,
119         create_params: &CreateParams,
120         wireless_chip: WirelessChipImpl,
121     ) -> Self {
122         Self {
123             id,
124             device_id,
125             wireless_chip,
126             kind: create_params.kind,
127             address: create_params.address.clone(),
128             name: create_params.name.clone().unwrap_or(format!("chip-{}", id.0)),
129             device_name: device_name.to_string(),
130             manufacturer: RwLock::new(create_params.manufacturer.clone()),
131             product_name: RwLock::new(create_params.product_name.clone()),
132             start: Instant::now(),
133         }
134     }
135 
136     // Get the stats protobuf for a chip controller instance.
137     //
138     // This currently wraps the chip "get" facade method because the
139     // counts are phy level. We need a vec since Bluetooth reports
140     // stats for BLE and CLASSIC.
get_stats(&self) -> Vec<ProtoRadioStats>141     pub fn get_stats(&self) -> Vec<ProtoRadioStats> {
142         self.wireless_chip.get_stats(self.start.elapsed().as_secs())
143     }
144 
145     /// Create the model protobuf
get(&self) -> Result<ProtoChip, String>146     pub fn get(&self) -> Result<ProtoChip, String> {
147         let mut proto_chip = self.wireless_chip.get();
148         proto_chip.kind = EnumOrUnknown::new(self.kind);
149         proto_chip.id = self.id.0;
150         proto_chip.name.clone_from(&self.name);
151         proto_chip.manufacturer.clone_from(&self.manufacturer.read().unwrap());
152         proto_chip.product_name.clone_from(&self.product_name.read().unwrap());
153         Ok(proto_chip)
154     }
155 
156     /// Patch processing for the chip. Validate and move state from the patch
157     /// into the chip changing the ChipFacade as needed.
patch(&self, patch: &ProtoChip) -> Result<(), String>158     pub fn patch(&self, patch: &ProtoChip) -> Result<(), String> {
159         if !patch.manufacturer.is_empty() {
160             self.manufacturer.write().unwrap().clone_from(&patch.manufacturer);
161         }
162         if !patch.product_name.is_empty() {
163             self.product_name.write().unwrap().clone_from(&patch.product_name);
164         }
165         self.wireless_chip.patch(patch);
166         Ok(())
167     }
168 
reset(&self) -> Result<(), String>169     pub fn reset(&self) -> Result<(), String> {
170         self.wireless_chip.reset();
171         Ok(())
172     }
173 }
174 
175 /// Obtains a Chip with given chip_id
get_chip(chip_id: &ChipIdentifier) -> Option<Arc<Chip>>176 pub fn get_chip(chip_id: &ChipIdentifier) -> Option<Arc<Chip>> {
177     get_chip_manager().get_chip(chip_id)
178 }
179 
180 /// Remove a Chip with given chip_id
remove_chip(chip_id: &ChipIdentifier) -> Option<Arc<Chip>>181 pub fn remove_chip(chip_id: &ChipIdentifier) -> Option<Arc<Chip>> {
182     get_chip_manager().remove_chip(chip_id)
183 }
184 
next_id() -> ChipIdentifier185 pub fn next_id() -> ChipIdentifier {
186     get_chip_manager().next_id()
187 }
188 
189 /// Allocates a new chip.
new( id: ChipIdentifier, device_id: DeviceIdentifier, device_name: &str, create_params: &CreateParams, wireless_chip: WirelessChipImpl, ) -> Result<Arc<Chip>, String>190 pub fn new(
191     id: ChipIdentifier,
192     device_id: DeviceIdentifier,
193     device_name: &str,
194     create_params: &CreateParams,
195     wireless_chip: WirelessChipImpl,
196 ) -> Result<Arc<Chip>, String> {
197     get_chip_manager().new_chip(id, device_id, device_name, create_params, wireless_chip)
198 }
199 
200 impl ChipManager {
new_chip( &self, id: ChipIdentifier, device_id: DeviceIdentifier, device_name: &str, create_params: &CreateParams, wireless_chip: WirelessChipImpl, ) -> Result<Arc<Chip>, String>201     fn new_chip(
202         &self,
203         id: ChipIdentifier,
204         device_id: DeviceIdentifier,
205         device_name: &str,
206         create_params: &CreateParams,
207         wireless_chip: WirelessChipImpl,
208     ) -> Result<Arc<Chip>, String> {
209         let chip = Arc::new(Chip::new(id, device_id, device_name, create_params, wireless_chip));
210         self.chips.write().unwrap().insert(id, Arc::clone(&chip));
211         Ok(chip)
212     }
213 
get_chip(&self, chip_id: &ChipIdentifier) -> Option<Arc<Chip>>214     fn get_chip(&self, chip_id: &ChipIdentifier) -> Option<Arc<Chip>> {
215         self.chips.read().unwrap().get(chip_id).cloned()
216     }
217 
remove_chip(&self, chip_id: &ChipIdentifier) -> Option<Arc<Chip>>218     fn remove_chip(&self, chip_id: &ChipIdentifier) -> Option<Arc<Chip>> {
219         self.chips.write().unwrap().remove(chip_id)
220     }
221 }
222 
223 #[cfg(test)]
224 mod tests {
225     use netsim_proto::stats::netsim_radio_stats;
226 
227     use crate::wireless::mocked;
228 
229     use super::*;
230 
231     const DEVICE_ID: DeviceIdentifier = DeviceIdentifier(0);
232     const CHIP_ID: ChipIdentifier = ChipIdentifier(1000);
233     const DEVICE_NAME: &str = "device";
234     const CHIP_KIND: ProtoChipKind = ProtoChipKind::UNSPECIFIED;
235     const ADDRESS: &str = "address";
236     const MANUFACTURER: &str = "manufacturer";
237     const PRODUCT_NAME: &str = "product_name";
238 
239     impl ChipManager {
new_test_chip(&self, wireless_chip: WirelessChipImpl) -> Arc<Chip>240         fn new_test_chip(&self, wireless_chip: WirelessChipImpl) -> Arc<Chip> {
241             let create_params = CreateParams {
242                 kind: CHIP_KIND,
243                 address: ADDRESS.to_string(),
244                 name: None,
245                 manufacturer: MANUFACTURER.to_string(),
246                 product_name: PRODUCT_NAME.to_string(),
247             };
248             self.new_chip(CHIP_ID, DEVICE_ID, DEVICE_NAME, &create_params, wireless_chip).unwrap()
249         }
250     }
251 
252     #[test]
test_new_and_get_with_singleton()253     fn test_new_and_get_with_singleton() {
254         let mocked_adaptor = mocked::add_chip(
255             &mocked::CreateParams { chip_kind: ProtoChipKind::UNSPECIFIED },
256             ChipIdentifier(0),
257         );
258         let chip_manager = ChipManager::new(INITIAL_CHIP_ID);
259         let chip = chip_manager.new_test_chip(mocked_adaptor);
260 
261         // Check if the Chip has been successfully inserted
262         let chip_id = chip.id;
263         assert!(chip_manager.chips.read().unwrap().contains_key(&chip_id));
264 
265         // Check if the chip_manager can successfully fetch the chip
266         let chip_result = chip_manager.get_chip(&chip_id);
267         assert!(chip_result.is_some());
268 
269         // Check if the fields are correctly populated
270         let chip = chip_result.unwrap();
271         assert_eq!(chip.device_id, DEVICE_ID);
272         assert_eq!(chip.device_name, DEVICE_NAME);
273         assert_eq!(chip.kind, CHIP_KIND);
274         assert_eq!(chip.address, ADDRESS);
275         assert_eq!(chip.name, format!("chip-{chip_id}"));
276         assert_eq!(chip.manufacturer.read().unwrap().to_string(), MANUFACTURER);
277         assert_eq!(chip.product_name.read().unwrap().to_string(), PRODUCT_NAME);
278     }
279 
280     #[test]
test_chip_get_stats()281     fn test_chip_get_stats() {
282         // When wireless_chip is constructed
283         let mocked_adaptor = mocked::add_chip(
284             &mocked::CreateParams { chip_kind: ProtoChipKind::UNSPECIFIED },
285             ChipIdentifier(0),
286         );
287         let chip_manager = ChipManager::new(INITIAL_CHIP_ID);
288 
289         let chip = chip_manager.new_test_chip(mocked_adaptor);
290         assert_eq!(netsim_radio_stats::Kind::UNSPECIFIED, chip.get_stats().first().unwrap().kind());
291     }
292 
293     #[test]
test_chip_get()294     fn test_chip_get() {
295         let mocked_adaptor = mocked::add_chip(
296             &mocked::CreateParams { chip_kind: ProtoChipKind::UNSPECIFIED },
297             ChipIdentifier(0),
298         );
299         let chip_manager = ChipManager::new(INITIAL_CHIP_ID);
300         let chip = chip_manager.new_test_chip(mocked_adaptor);
301 
302         // Obtain actual chip.get()
303         let actual = chip.get().unwrap();
304 
305         // Construct expected ProtoChip
306         let mut expected = chip.wireless_chip.get();
307         expected.kind = EnumOrUnknown::new(chip.kind);
308         expected.id = chip.id.0;
309         expected.name.clone_from(&chip.name);
310         expected.manufacturer.clone_from(&chip.manufacturer.read().unwrap());
311         expected.product_name.clone_from(&chip.product_name.read().unwrap());
312 
313         // Compare
314         assert_eq!(expected, actual);
315     }
316 
317     #[test]
test_chip_patch()318     fn test_chip_patch() {
319         let mocked_adaptor = mocked::add_chip(
320             &mocked::CreateParams { chip_kind: ProtoChipKind::UNSPECIFIED },
321             ChipIdentifier(0),
322         );
323         let chip_manager = ChipManager::new(INITIAL_CHIP_ID);
324         let chip = chip_manager.new_test_chip(mocked_adaptor);
325 
326         // Construct the patch body for modifying manufacturer and product_name
327         let mut patch_body = ProtoChip::new();
328         patch_body.manufacturer = "patched_manufacturer".to_string();
329         patch_body.product_name = "patched_product_name".to_string();
330 
331         // Perform Patch
332         assert!(chip.patch(&patch_body).is_ok());
333 
334         // Check if fields of chip has been patched
335         assert_eq!(patch_body.manufacturer, chip.manufacturer.read().unwrap().to_string());
336         assert_eq!(patch_body.product_name, chip.product_name.read().unwrap().to_string());
337     }
338 
339     // TODO (b/309529194)
340     // Implement wireless/mocked.rs to test wireless_chip level of patch and resets.
341 }
342