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 /// request packets flow into netsim
16 /// response packets flow out of netsim
17 /// packet transports read requests and write response packets over gRPC or Fds.
18 use super::h4;
19 use super::uci;
20 use crate::ffi::{add_chip_cxx, handle_request_cxx};
21 use cxx::let_cxx_string;
22 use lazy_static::lazy_static;
23 use serde::{Deserialize, Serialize};
24 use std::collections::HashMap;
25 use std::fs::File;
26 use std::io::{IoSlice, Write};
27 use std::os::fd::FromRawFd;
28 use std::sync::RwLock;
29 use std::thread::JoinHandle;
30 use std::{fmt, thread};
31
32 #[derive(Serialize, Deserialize, Debug)]
33 struct StartupInfo {
34 devices: Vec<Device>,
35 }
36
37 #[derive(Serialize, Deserialize, Debug)]
38 struct Device {
39 name: String,
40 chips: Vec<Chip>,
41 }
42
43 #[derive(Serialize, Deserialize, Debug, Copy, Clone)]
44 enum ChipKindEnum {
45 UNKNOWN = 0,
46 BLUETOOTH = 1,
47 WIFI = 2,
48 UWB = 3,
49 }
50
51 impl fmt::Display for ChipKindEnum {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result52 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53 write!(f, "{:?}", self)
54 }
55 }
56
57 #[derive(Serialize, Deserialize, Debug)]
58 struct Chip {
59 kind: ChipKindEnum,
60 id: Option<String>,
61 manufacturer: Option<String>,
62 product_name: Option<String>,
63 #[serde(rename = "fdIn")]
64 fd_in: u32,
65 #[serde(rename = "fdOut")]
66 fd_out: u32,
67 loopback: Option<bool>,
68 }
69
70 // TRANSPORTS is a singleton that contains the output FiFo
71 lazy_static! {
72 static ref TRANSPORTS: RwLock<HashMap<String, File>> = RwLock::new(HashMap::new());
73 }
74
key(kind: u32, facade_id: u32) -> String75 fn key(kind: u32, facade_id: u32) -> String {
76 format!("{}/{}", kind, facade_id)
77 }
78
handle_response(kind: u32, facade_id: u32, packet: &cxx::CxxVector<u8>, packet_type: u8)79 pub fn handle_response(kind: u32, facade_id: u32, packet: &cxx::CxxVector<u8>, packet_type: u8) {
80 let binding = TRANSPORTS.read().unwrap();
81 let key = key(kind, facade_id);
82 match binding.get(&key) {
83 Some(mut fd_out) => {
84 // todo add error checking
85 let temp = [packet_type];
86 let bufs = [IoSlice::new(&temp), IoSlice::new(packet.as_slice())];
87 if let Err(e) = fd_out.write_vectored(&bufs) {
88 println!("netsimd: error writing {}", e);
89 };
90 }
91 None => {
92 println!("netsimd: Error unknown key {}/{}", kind, facade_id);
93 }
94 };
95 }
96
97 /// read from the raw fd and pass to the packet hub.
98 ///
fd_reader(fd_rx: i32, kind: ChipKindEnum, facade_id: u32) -> JoinHandle<()>99 fn fd_reader(fd_rx: i32, kind: ChipKindEnum, facade_id: u32) -> JoinHandle<()> {
100 thread::Builder::new()
101 .name(format!("fd_reader_{}", fd_rx))
102 .spawn(move || {
103 let mut rx = unsafe { File::from_raw_fd(fd_rx) };
104
105 println!(
106 "netsimd: thread handling kind:{:?} facade_id:{:?} fd:{}",
107 kind, facade_id, fd_rx
108 );
109
110 loop {
111 match kind {
112 ChipKindEnum::UWB => match uci::read_uci_packet(&mut rx) {
113 Err(e) => {
114 println!(
115 "netsimd: error reading uci control packet fd {} {:?}",
116 fd_rx, e
117 );
118 return;
119 }
120 Ok(uci::Packet { payload }) => {
121 handle_request_cxx(kind as u32, facade_id, &payload, 0);
122 }
123 },
124 ChipKindEnum::BLUETOOTH => match h4::read_h4_packet(&mut rx) {
125 Ok(h4::Packet { h4_type, payload }) => {
126 handle_request_cxx(kind as u32, facade_id, &payload, h4_type);
127 }
128 Err(e) => {
129 println!(
130 "netsimd: error reading hci control packet fd {} {:?}",
131 fd_rx, e
132 );
133 return;
134 }
135 },
136 _ => {
137 println!("netsimd: unknown control packet kind: {:?}", kind);
138 return;
139 }
140 };
141 }
142 })
143 .unwrap()
144 }
145
146 /// start_fd_transport
147 ///
148 /// Create threads to read and write to file descriptors
149 //
run_fd_transport(startup_json: &String)150 pub fn run_fd_transport(startup_json: &String) {
151 println!("netsimd: fd_transport starting with {startup_json}");
152 let startup_info = match serde_json::from_str::<StartupInfo>(startup_json.as_str()) {
153 Err(e) => {
154 println!("Error parsing startup info: {:?}", e);
155 return;
156 }
157 Ok(startup_info) => startup_info,
158 };
159 // See https://tokio.rs/tokio/topics/bridging
160 // This code is synchronous hosting asynchronous until main is converted to rust.
161 thread::Builder::new()
162 .name("fd_transport".to_string())
163 .spawn(move || {
164 let chip_count = startup_info.devices.iter().map(|d| d.chips.len()).sum();
165 let mut handles = Vec::with_capacity(chip_count);
166 for device in startup_info.devices {
167 for chip in device.chips {
168 let_cxx_string!(guid = chip.fd_in.to_string());
169 let_cxx_string!(device_name = device.name.clone());
170 let_cxx_string!(name = chip.id.unwrap_or_default());
171 let_cxx_string!(manufacturer = chip.manufacturer.unwrap_or_default());
172 let_cxx_string!(product_name = chip.product_name.unwrap_or_default());
173 let result = add_chip_cxx(
174 &guid,
175 &device_name,
176 chip.kind as u32,
177 &name,
178 &manufacturer,
179 &product_name,
180 );
181 let key = key(chip.kind as u32, result.get_facade_id());
182
183 // Cf writes to fd_out and reads from fd_in
184 let file_in = unsafe { File::from_raw_fd(chip.fd_in as i32) };
185
186 TRANSPORTS.write().unwrap().insert(key, file_in);
187 // TODO: switch to runtime.spawn once FIFOs are available in Tokio
188 handles.push(fd_reader(chip.fd_out as i32, chip.kind, result.get_facade_id()));
189 }
190 }
191 // Wait for all of them to complete.
192 for handle in handles {
193 // The `spawn` method returns a `JoinHandle`. A `JoinHandle` is
194 // a future, so we can wait for it using `block_on`.
195 // runtime.block_on(handle).unwrap();
196 // TODO: use runtime.block_on once FIFOs are available in Tokio
197 handle.join().unwrap();
198 }
199 println!("netsimd:: done with all fd handlers");
200 })
201 .unwrap();
202 }
203
204 mod tests {
205 #[allow(unused)]
206 use super::StartupInfo;
207
208 #[test]
test_serde()209 fn test_serde() {
210 let s = r#"
211 {"devices": [
212 {"name": "emulator-5554",
213 "chips": [{"kind": "WIFI", "fdIn": 1, "fdOut": 2},
214 {"kind": "BLUETOOTH", "fdIn": 20, "fdOut":21}]
215 },
216 {"name": "emulator-5555",
217 "chips": [{"kind": "BLUETOOTH", "fdIn": 3, "fdOut": 4},
218 {"kind": "UWB", "fdIn": 5, "fdOut": 6, "model": "DW300"}]
219 }
220 ]
221 }"#;
222 let startup_info = serde_json::from_str::<StartupInfo>(s).unwrap();
223 for device in startup_info.devices {
224 println!("device {:?}", device);
225 }
226 }
227 }
228