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 /// WirelessAdaptor.` 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::WirelessAdaptorImpl;
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_adaptor.
87 pub struct Chip {
88 pub id: ChipIdentifier,
89 pub device_id: DeviceIdentifier,
90 pub wireless_adaptor: WirelessAdaptorImpl,
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_adaptor: WirelessAdaptorImpl, ) -> Self115 fn new(
116 id: ChipIdentifier,
117 device_id: DeviceIdentifier,
118 device_name: &str,
119 create_params: &CreateParams,
120 wireless_adaptor: WirelessAdaptorImpl,
121 ) -> Self {
122 Self {
123 id,
124 device_id,
125 wireless_adaptor,
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_adaptor.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_adaptor.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_adaptor.patch(patch);
166 Ok(())
167 }
168
reset(&self) -> Result<(), String>169 pub fn reset(&self) -> Result<(), String> {
170 self.wireless_adaptor.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_adaptor: WirelessAdaptorImpl, ) -> 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_adaptor: WirelessAdaptorImpl,
196 ) -> Result<Arc<Chip>, String> {
197 get_chip_manager().new_chip(id, device_id, device_name, create_params, wireless_adaptor)
198 }
199
200 impl ChipManager {
new_chip( &self, id: ChipIdentifier, device_id: DeviceIdentifier, device_name: &str, create_params: &CreateParams, wireless_adaptor: WirelessAdaptorImpl, ) -> 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_adaptor: WirelessAdaptorImpl,
208 ) -> Result<Arc<Chip>, String> {
209 let chip = Arc::new(Chip::new(id, device_id, device_name, create_params, wireless_adaptor));
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_adaptor: WirelessAdaptorImpl) -> Arc<Chip>240 fn new_test_chip(&self, wireless_adaptor: WirelessAdaptorImpl) -> 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_adaptor)
249 .unwrap()
250 }
251 }
252
253 #[test]
test_new_and_get_with_singleton()254 fn test_new_and_get_with_singleton() {
255 let mocked_adaptor = mocked::new(
256 &mocked::CreateParams { chip_kind: ProtoChipKind::UNSPECIFIED },
257 ChipIdentifier(0),
258 );
259 let chip_manager = ChipManager::new(INITIAL_CHIP_ID);
260 let chip = chip_manager.new_test_chip(mocked_adaptor);
261
262 // Check if the Chip has been successfully inserted
263 let chip_id = chip.id;
264 assert!(chip_manager.chips.read().unwrap().contains_key(&chip_id));
265
266 // Check if the chip_manager can successfully fetch the chip
267 let chip_result = chip_manager.get_chip(&chip_id);
268 assert!(chip_result.is_some());
269
270 // Check if the fields are correctly populated
271 let chip = chip_result.unwrap();
272 assert_eq!(chip.device_id, DEVICE_ID);
273 assert_eq!(chip.device_name, DEVICE_NAME);
274 assert_eq!(chip.kind, CHIP_KIND);
275 assert_eq!(chip.address, ADDRESS);
276 assert_eq!(chip.name, format!("chip-{chip_id}"));
277 assert_eq!(chip.manufacturer.read().unwrap().to_string(), MANUFACTURER);
278 assert_eq!(chip.product_name.read().unwrap().to_string(), PRODUCT_NAME);
279 }
280
281 #[test]
test_chip_get_stats()282 fn test_chip_get_stats() {
283 // When wireless_adaptor is constructed
284 let mocked_adaptor = mocked::new(
285 &mocked::CreateParams { chip_kind: ProtoChipKind::UNSPECIFIED },
286 ChipIdentifier(0),
287 );
288 let chip_manager = ChipManager::new(INITIAL_CHIP_ID);
289
290 let chip = chip_manager.new_test_chip(mocked_adaptor);
291 assert_eq!(netsim_radio_stats::Kind::UNSPECIFIED, chip.get_stats().first().unwrap().kind());
292 }
293
294 #[test]
test_chip_get()295 fn test_chip_get() {
296 let mocked_adaptor = mocked::new(
297 &mocked::CreateParams { chip_kind: ProtoChipKind::UNSPECIFIED },
298 ChipIdentifier(0),
299 );
300 let chip_manager = ChipManager::new(INITIAL_CHIP_ID);
301 let chip = chip_manager.new_test_chip(mocked_adaptor);
302
303 // Obtain actual chip.get()
304 let actual = chip.get().unwrap();
305
306 // Construct expected ProtoChip
307 let mut expected = chip.wireless_adaptor.get();
308 expected.kind = EnumOrUnknown::new(chip.kind);
309 expected.id = chip.id.0;
310 expected.name.clone_from(&chip.name);
311 expected.manufacturer.clone_from(&chip.manufacturer.read().unwrap());
312 expected.product_name.clone_from(&chip.product_name.read().unwrap());
313
314 // Compare
315 assert_eq!(expected, actual);
316 }
317
318 #[test]
test_chip_patch()319 fn test_chip_patch() {
320 let mocked_adaptor = mocked::new(
321 &mocked::CreateParams { chip_kind: ProtoChipKind::UNSPECIFIED },
322 ChipIdentifier(0),
323 );
324 let chip_manager = ChipManager::new(INITIAL_CHIP_ID);
325 let chip = chip_manager.new_test_chip(mocked_adaptor);
326
327 // Construct the patch body for modifying manufacturer and product_name
328 let mut patch_body = ProtoChip::new();
329 patch_body.manufacturer = "patched_manufacturer".to_string();
330 patch_body.product_name = "patched_product_name".to_string();
331
332 // Perform Patch
333 assert!(chip.patch(&patch_body).is_ok());
334
335 // Check if fields of chip has been patched
336 assert_eq!(patch_body.manufacturer, chip.manufacturer.read().unwrap().to_string());
337 assert_eq!(patch_body.product_name, chip.product_name.read().unwrap().to_string());
338 }
339
340 // TODO (b/309529194)
341 // Implement wireless/mocked.rs to test wireless_adaptor level of patch and resets.
342 }
343