• 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 /// 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