| // Copyright 2023 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| use std::{ |
| collections::BTreeMap, |
| sync::{Arc, Mutex}, |
| }; |
| |
| use lazy_static::lazy_static; |
| |
| use netsim_proto::common::ChipKind as ProtoChipKind; |
| use netsim_proto::model::Chip as ProtoChip; |
| use netsim_proto::stats::NetsimRadioStats as ProtoRadioStats; |
| |
| use crate::{ |
| devices::{ |
| chip::{ChipIdentifier, FacadeIdentifier}, |
| device::DeviceIdentifier, |
| }, |
| echip::{ble_beacon, mocked, SharedEmulatedChip}, |
| }; |
| |
| #[cfg(not(test))] |
| use crate::echip::{bluetooth, wifi}; |
| |
| // ECHIPS is a singleton that contains a hash map from |
| // ChipIdentifier to SharedEmulatedChip |
| lazy_static! { |
| static ref ECHIPS: Arc<Mutex<BTreeMap<ChipIdentifier, SharedEmulatedChip>>> = |
| Arc::new(Mutex::new(BTreeMap::new())); |
| } |
| |
| /// Parameter for each constructor of Emulated Chips |
| #[allow(clippy::large_enum_variant)] |
| pub enum CreateParam { |
| BleBeacon(ble_beacon::CreateParams), |
| #[cfg(not(test))] |
| Bluetooth(bluetooth::CreateParams), |
| #[cfg(not(test))] |
| Wifi(wifi::CreateParams), |
| #[cfg(not(test))] |
| Uwb, |
| Mock(mocked::CreateParams), |
| } |
| |
| // TODO: Factory trait to include start, stop, and add |
| /// EmulatedChip is a trait that provides interface between the generic Chip |
| /// and Radio specific library (rootcanal, libslirp, pica). |
| pub trait EmulatedChip { |
| /// This is the main entry for incoming host-to-controller packets |
| /// from virtual devices called by the transport module. The format of the |
| /// packet depends on the emulated chip kind: |
| /// * Bluetooth - packet is H4 HCI format |
| /// * Wi-Fi - packet is Radiotap format |
| /// * UWB - packet is UCI format |
| /// * NFC - packet is NCI format |
| fn handle_request(&self, packet: &[u8]); |
| |
| /// Reset the internal state of the emulated chip for the virtual device. |
| /// The transmitted and received packet count will be set to 0 and the chip |
| /// shall be in the enabled state following a call to this function. |
| fn reset(&self); |
| |
| /// Return the Chip model protobuf from the emulated chip. This is part of |
| /// the Frontend API. |
| fn get(&self) -> ProtoChip; |
| |
| /// Patch the state of the emulated chip. For example enable/disable the |
| /// chip's host-to-controller packet processing. This is part of the |
| /// Frontend API |
| fn patch(&self, chip: &ProtoChip); |
| |
| /// Remove the emulated chip from the emulated chip library. No further calls will |
| /// be made on this emulated chip. This is called when the packet stream from |
| /// the virtual device closes. |
| fn remove(&self); |
| |
| /// Return the NetsimRadioStats protobuf from the emulated chip. This is |
| /// part of NetsimStats protobuf. |
| fn get_stats(&self, duration_secs: u64) -> Vec<ProtoRadioStats>; |
| |
| /// Returns the kind of the emulated chip. |
| fn get_kind(&self) -> ProtoChipKind; |
| |
| // TODO(b/311480905): Remove this method and get rid of facade_id in devices crate. |
| /// Returns Facade Identifier. |
| fn get_facade_id(&self) -> FacadeIdentifier; |
| } |
| |
| /// Lookup for SharedEmulatedChip with chip_id |
| /// Returns None if chip_id is non-existent key. |
| pub fn get(chip_id: ChipIdentifier) -> Option<SharedEmulatedChip> { |
| ECHIPS.lock().expect("Failed to acquire lock on ECHIPS").get(&chip_id).cloned() |
| } |
| |
| /// Remove and return SharedEmulatedchip from ECHIPS. |
| /// Returns None if chip_id is non-existent key. |
| pub fn remove(chip_id: ChipIdentifier) -> Option<SharedEmulatedChip> { |
| let echip = ECHIPS.lock().expect("Failed to acquire lock on ECHIPS").remove(&chip_id); |
| if echip.is_some() { |
| echip.clone().unwrap().remove(); |
| } |
| echip |
| } |
| |
| /// This is called when the transport module receives a new packet stream |
| /// connection from a virtual device. |
| pub fn new( |
| create_param: &CreateParam, |
| device_id: DeviceIdentifier, |
| chip_id: ChipIdentifier, |
| ) -> SharedEmulatedChip { |
| // Based on create_param, construct SharedEmulatedChip. |
| let shared_echip = match create_param { |
| CreateParam::BleBeacon(params) => ble_beacon::new(params, device_id, chip_id), |
| #[cfg(not(test))] |
| CreateParam::Bluetooth(params) => bluetooth::new(params, device_id, chip_id), |
| #[cfg(not(test))] |
| CreateParam::Wifi(params) => wifi::new(params, device_id, chip_id), |
| #[cfg(not(test))] |
| CreateParam::Uwb => todo!(), |
| CreateParam::Mock(params) => mocked::new(params, device_id), |
| }; |
| |
| // Insert into ECHIPS Map |
| ECHIPS.lock().expect("Failed to acquire lock on ECHIPS").insert(chip_id, shared_echip.clone()); |
| shared_echip |
| } |
| |
| // TODO(b/309529194): |
| // 1. Create Mock echip, patch and get |
| // 2. Create Mock echip, patch and reset |
| #[cfg(test)] |
| mod tests { |
| |
| use super::*; |
| |
| #[test] |
| fn test_echip_new() { |
| let mock_param = |
| CreateParam::Mock(mocked::CreateParams { chip_kind: ProtoChipKind::UNSPECIFIED }); |
| let mock_device_id = 0; |
| let mock_chip_id = 0; |
| let echip = new(&mock_param, mock_device_id, mock_chip_id); |
| assert_eq!(echip.get_kind(), ProtoChipKind::UNSPECIFIED); |
| assert_eq!(echip.get(), ProtoChip::new()); |
| } |
| } |