1#![warn(missing_docs)]
6
7use fake_bti::FakeBti;
10use fidl_fuchsia_driver_framework as fdf;
11use fidl_next::{Request, Responder};
12use fidl_next_fuchsia_hardware_platform_device::{self as fdevice, DeviceServerHandler};
13use fuchsia_async as fasync;
14use fuchsia_component::server::ServiceFs;
15use fuchsia_sync::Mutex;
16use std::collections::HashMap;
17use std::sync::Arc;
18use zx::Status;
19
20#[derive(Default)]
21pub struct Config {
23 pub use_fake_bti: bool,
25 pub use_fake_smc: bool,
27 pub use_fake_irq: bool,
29 pub mmios: HashMap<u32, fdevice::natural::Mmio>,
31 pub mmio_names: HashMap<String, u32>,
34 pub irqs: HashMap<u32, zx::Interrupt>,
36 pub irq_names: HashMap<String, u32>,
39 pub btis: HashMap<u32, zx::Bti>,
41 pub bti_names: HashMap<String, u32>,
44 pub smcs: HashMap<u32, zx::Resource>,
46 pub device_info: Option<fdevice::natural::NodeDeviceInfo>,
48 pub board_info: Option<fdevice::natural::BoardInfo>,
50 pub power_elements: Vec<fidl_next_fuchsia_hardware_power::natural::PowerElementConfiguration>,
52}
53
54struct FakePDevState {
55 config: Config,
56 metadata: HashMap<String, Vec<u8>>,
57}
58
59#[derive(Clone)]
60pub struct FakePDev {
62 state: Arc<Mutex<FakePDevState>>,
63}
64
65impl Default for FakePDev {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71impl FakePDev {
72 pub fn new() -> Self {
74 Self {
75 state: Arc::new(Mutex::new(FakePDevState {
76 config: Config::default(),
77 metadata: HashMap::new(),
78 })),
79 }
80 }
81
82 pub fn set_config(&self, config: Config) {
84 self.state.lock().config = config;
85 }
86
87 pub fn add_metadata(&self, id: &str, data: Vec<u8>) {
89 self.state.lock().metadata.insert(id.to_string(), data);
90 }
91
92 pub fn serve(
94 &self,
95 service_fs: &mut ServiceFs<fuchsia_component::server::ServiceObj<'static, ()>>,
96 scope: fasync::ScopeHandle,
97 instance_name: &str,
98 ) -> fdf::Offer {
99 let state_clone = self.state.clone();
100
101 fdf_component::ServiceOffer::<fdevice::Service>::new_next()
102 .add_default_named_next(
103 service_fs,
104 instance_name,
105 FakePDevService { state: state_clone, scope },
106 )
107 .build_zircon_offer_next()
108 }
109}
110
111struct FakePDevService {
112 state: Arc<Mutex<FakePDevState>>,
113 scope: fasync::ScopeHandle,
114}
115
116impl fdevice::ServiceHandler for FakePDevService {
117 fn device(&self, server_end: fidl_next::ServerEnd<fdevice::Device>) {
118 server_end.spawn_on(FakePDevServer { state: self.state.clone() }, &self.scope);
119 }
120}
121
122struct FakePDevServer {
123 state: Arc<Mutex<FakePDevState>>,
124}
125
126impl FakePDevServer {
127 fn duplicate_mmio(mmio: &fdevice::natural::Mmio) -> fdevice::natural::Mmio {
128 let dup_vmo =
129 mmio.vmo.as_ref().map(|v| v.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap());
130 fdevice::natural::Mmio {
131 offset: mmio.offset,
132 size: mmio.size,
133 vmo: dup_vmo.map(Into::into),
134 }
135 }
136}
137
138impl DeviceServerHandler for FakePDevServer {
139 async fn get_mmio_by_id(
140 &mut self,
141 request: Request<fdevice::device::GetMmioById>,
142 responder: Responder<fdevice::device::GetMmioById>,
143 ) {
144 let index = request.payload().index;
145 let mmio_clone =
146 self.state.lock().config.mmios.get(&index).map(FakePDevServer::duplicate_mmio);
147 if let Some(mmio) = mmio_clone {
148 let _ = responder.respond(mmio).await;
149 } else {
150 let _ = responder.respond_err(Status::NOT_FOUND).await;
151 }
152 }
153
154 async fn get_mmio_by_name(
155 &mut self,
156 request: Request<fdevice::device::GetMmioByName>,
157 responder: Responder<fdevice::device::GetMmioByName>,
158 ) {
159 let name = request.payload().name.as_str().to_string();
160 let mmio_clone = {
161 let state = self.state.lock();
162 state
163 .config
164 .mmio_names
165 .get(&name)
166 .and_then(|idx| state.config.mmios.get(idx))
167 .map(FakePDevServer::duplicate_mmio)
168 };
169
170 let _ = responder.respond_with(mmio_clone.ok_or(Status::NOT_FOUND)).await;
171 }
172
173 async fn get_interrupt_by_id(
174 &mut self,
175 request: Request<fdevice::device::GetInterruptById>,
176 responder: Responder<fdevice::device::GetInterruptById>,
177 ) {
178 let index = request.payload().index;
179 let (irq_res, use_fake) = {
180 let state = self.state.lock();
181 let irq_res =
182 state.config.irqs.get(&index).map(|i| i.duplicate_handle(zx::Rights::SAME_RIGHTS));
183 (irq_res, state.config.use_fake_irq)
184 };
185
186 let res: Result<zx::Interrupt, zx::Status> = if let Some(res) = irq_res {
187 res
188 } else if use_fake {
189 zx::VirtualInterrupt::create_virtual().map(|irq| zx::Interrupt::from(irq.into_handle()))
190 } else {
191 Err(Status::NOT_FOUND)
192 };
193
194 match res {
195 Ok(irq) => {
196 let _ = responder.respond(irq).await;
197 }
198 Err(e) => {
199 let _ = responder.respond_err(e).await;
200 }
201 }
202 }
203
204 async fn get_interrupt_by_name(
205 &mut self,
206 request: Request<fdevice::device::GetInterruptByName>,
207 responder: Responder<fdevice::device::GetInterruptByName>,
208 ) {
209 let name = request.payload().name.as_str().to_string();
210 let (irq_res, use_fake) = {
211 let state = self.state.lock();
212 let irq_res = state
213 .config
214 .irq_names
215 .get(&name)
216 .and_then(|idx| state.config.irqs.get(idx))
217 .map(|i| i.duplicate_handle(zx::Rights::SAME_RIGHTS));
218 (irq_res, state.config.use_fake_irq)
219 };
220
221 let res = if let Some(res) = irq_res {
222 res
223 } else if use_fake {
224 zx::VirtualInterrupt::create_virtual().map(|irq| zx::Interrupt::from(irq.into_handle()))
225 } else {
226 Err(Status::NOT_FOUND)
227 };
228
229 match res {
230 Ok(irq) => {
231 let _ = responder.respond(irq).await;
232 }
233 Err(e) => {
234 let _ = responder.respond_err(e).await;
235 }
236 }
237 }
238
239 async fn get_bti_by_id(
240 &mut self,
241 request: Request<fdevice::device::GetBtiById>,
242 responder: Responder<fdevice::device::GetBtiById>,
243 ) {
244 let index = request.payload().index;
245 let (bti_res, use_fake) = {
246 let state = self.state.lock();
247 let bti_res =
248 state.config.btis.get(&index).map(|b| b.duplicate_handle(zx::Rights::SAME_RIGHTS));
249 (bti_res, state.config.use_fake_bti)
250 };
251
252 let res = if let Some(res) = bti_res {
253 res
254 } else if use_fake {
255 FakeBti::create().and_then(|fake| fake.duplicate_handle(zx::Rights::SAME_RIGHTS))
256 } else {
257 Err(Status::NOT_FOUND)
258 };
259
260 match res {
261 Ok(bti) => {
262 let _ = responder.respond(bti).await;
263 }
264 Err(status) => {
265 let _ = responder.respond_err(status).await;
266 }
267 }
268 }
269
270 async fn get_bti_by_name(
271 &mut self,
272 request: Request<fdevice::device::GetBtiByName>,
273 responder: Responder<fdevice::device::GetBtiByName>,
274 ) {
275 let name = request.payload().name.as_str().to_string();
276 let (bti_res, use_fake) = {
277 let state = self.state.lock();
278 let bti_res = state
279 .config
280 .bti_names
281 .get(&name)
282 .and_then(|idx| state.config.btis.get(idx))
283 .map(|b| b.duplicate_handle(zx::Rights::SAME_RIGHTS));
284 (bti_res, state.config.use_fake_bti)
285 };
286
287 let res = if let Some(res) = bti_res {
288 res
289 } else if use_fake {
290 FakeBti::create().and_then(|fake| fake.duplicate_handle(zx::Rights::SAME_RIGHTS))
291 } else {
292 Err(Status::NOT_FOUND)
293 };
294
295 match res {
296 Ok(bti) => {
297 let _ = responder.respond(bti).await;
298 }
299 Err(status) => {
300 let _ = responder.respond_err(status).await;
301 }
302 }
303 }
304
305 async fn get_smc_by_id(
306 &mut self,
307 request: Request<fdevice::device::GetSmcById>,
308 responder: Responder<fdevice::device::GetSmcById>,
309 ) {
310 let index = request.payload().index;
311 let smc_res = self
312 .state
313 .lock()
314 .config
315 .smcs
316 .get(&index)
317 .map(|s| s.duplicate_handle(zx::Rights::SAME_RIGHTS));
318
319 let res = smc_res.unwrap_or(Err(Status::NOT_FOUND));
320
321 match res {
322 Ok(dup) => {
323 let _ = responder.respond(dup).await;
324 }
325 Err(status) => {
326 let _ = responder.respond_err(status).await;
327 }
328 }
329 }
330
331 async fn get_smc_by_name(
332 &mut self,
333 _request: Request<fdevice::device::GetSmcByName>,
334 responder: Responder<fdevice::device::GetSmcByName>,
335 ) {
336 let _ = responder.respond_err(Status::NOT_FOUND).await;
337 }
338
339 async fn get_power_configuration(
340 &mut self,
341 responder: Responder<fdevice::device::GetPowerConfiguration>,
342 ) {
343 let power_elements = self.state.lock().config.power_elements.clone();
344 let _ = responder.respond(power_elements).await;
345 }
346
347 async fn get_node_device_info(
348 &mut self,
349 responder: Responder<fdevice::device::GetNodeDeviceInfo>,
350 ) {
351 let device_info = self.state.lock().config.device_info.clone();
352 let _ = responder.respond_with(device_info.ok_or(Status::NOT_SUPPORTED)).await;
353 }
354
355 async fn get_board_info(&mut self, responder: Responder<fdevice::device::GetBoardInfo>) {
356 let board_info = self.state.lock().config.board_info.clone();
357 let _ = responder.respond_with(board_info.ok_or(Status::NOT_SUPPORTED)).await;
358 }
359
360 async fn get_metadata(
361 &mut self,
362 request: Request<fdevice::device::GetMetadata>,
363 responder: Responder<fdevice::device::GetMetadata>,
364 ) {
365 let metadata = self.state.lock().metadata.get(request.payload().id.as_str()).cloned();
366 if let Some(data) = metadata {
367 let _ = responder.respond(data).await;
368 } else {
369 let _ = responder.respond_err(Status::NOT_FOUND).await;
370 }
371 }
372}
373
374#[cfg(test)]
375mod tests {
376 use super::*;
377 use fidl_next::fuchsia::create_channel;
378
379 async fn run_test_with_config<F, Fut>(config: Config, test_func: F)
380 where
381 F: FnOnce(fidl_next::Client<fdevice::Device>, FakePDev) -> Fut,
382 Fut: std::future::Future<Output = ()>,
383 {
384 let fake_pdev = FakePDev::new();
385 fake_pdev.set_config(config);
386
387 let (client_end, server_end) = create_channel::<fdevice::Device>();
388 let server = FakePDevServer { state: fake_pdev.state.clone() };
389 let scope = fasync::Scope::new_with_name("test");
390 server_end.spawn_on(server, &scope);
391
392 let client = client_end.spawn();
393 test_func(client, fake_pdev).await;
394 }
395
396 #[fuchsia::test]
397 async fn test_get_mmios() {
398 let mut mmios = HashMap::new();
399 let vmo = zx::Vmo::create(11).unwrap();
400 mmios.insert(
401 5,
402 fdevice::natural::Mmio { offset: Some(10), size: Some(11), vmo: Some(vmo.into()) },
403 );
404 let mut mmio_names = HashMap::new();
405 mmio_names.insert("test-name".to_string(), 5);
406
407 run_test_with_config(
408 Config { mmios, mmio_names, ..Default::default() },
409 |client, _| async move {
410 let res = client.get_mmio_by_id(5).await.unwrap();
412 assert!(res.is_ok());
413 let mmio = res.unwrap();
414 assert_eq!(mmio.offset, Some(10));
415 assert_eq!(mmio.size, Some(11));
416
417 let res_err = client.get_mmio_by_id(4).await.unwrap();
418 assert!(res_err.is_err());
419
420 let res_name = client.get_mmio_by_name("test-name").await.unwrap();
422 assert!(res_name.is_ok());
423 let mmio_name = res_name.unwrap();
424 assert_eq!(mmio_name.offset, Some(10));
425 assert_eq!(mmio_name.size, Some(11));
426
427 let res_name_err = client.get_mmio_by_name("unknown-name").await.unwrap();
428 assert!(res_name_err.is_err());
429 },
430 )
431 .await;
432 }
433
434 #[fuchsia::test]
435 async fn test_invalid_mmio() {
436 let mut mmios = HashMap::new();
437 mmios.insert(
438 5,
439 fdevice::natural::Mmio {
440 offset: Some(10),
441 size: Some(11),
442 vmo: None, },
444 );
445 run_test_with_config(Config { mmios, ..Default::default() }, |client, _| async move {
446 let res = client.get_mmio_by_id(5).await.unwrap();
447 assert!(res.is_ok());
448 assert!(res.unwrap().vmo.is_none());
449 })
450 .await;
451 }
452
453 #[fuchsia::test]
454 async fn test_get_irqs() {
455 let mut irqs = HashMap::new();
456 let irq = zx::VirtualInterrupt::create_virtual().unwrap();
457 irqs.insert(5, zx::Interrupt::from(zx::NullableHandle::from(irq.into_handle())));
458 let mut irq_names = HashMap::new();
459 irq_names.insert("test-name".to_string(), 5);
460
461 run_test_with_config(
462 Config { irqs, irq_names, ..Default::default() },
463 |client, _| async move {
464 let res = client.get_interrupt_by_id(5, 0).await.unwrap();
465 assert!(res.is_ok());
466
467 let res_err = client.get_interrupt_by_id(4, 0).await.unwrap();
468 assert!(res_err.is_err());
469
470 let res_name = client.get_interrupt_by_name("test-name", 0).await.unwrap();
471 assert!(res_name.is_ok());
472 },
473 )
474 .await;
475 }
476
477 #[fuchsia::test]
478 async fn test_get_btis() {
479 let mut btis = HashMap::new();
480 let bti = FakeBti::create().unwrap();
481 btis.insert(5, bti.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap());
482
483 run_test_with_config(Config { btis, ..Default::default() }, |client, _| async move {
484 let res = client.get_bti_by_id(5).await.unwrap();
485 assert!(res.is_ok());
486
487 let res_err = client.get_bti_by_id(4).await.unwrap();
488 assert!(res_err.is_err());
489 })
490 .await;
491 }
492
493 #[fuchsia::test]
494 async fn test_get_smc() {
495 unsafe extern "C" {
496 fn fake_root_resource_create(out: *mut zx::sys::zx_handle_t) -> zx::sys::zx_status_t;
497 }
498 let mut raw = zx::sys::ZX_HANDLE_INVALID;
499 unsafe {
500 assert_eq!(fake_root_resource_create(&mut raw), zx::sys::ZX_OK);
501 }
502 let smc = unsafe { zx::Resource::from(zx::Handle::from_raw(raw).unwrap()) };
503
504 let mut smcs = HashMap::new();
505 smcs.insert(5, smc);
506
507 run_test_with_config(Config { smcs, ..Default::default() }, |client, _| async move {
508 let res = client.get_smc_by_id(5).await.unwrap();
509 assert!(res.is_ok());
510
511 let res_err = client.get_smc_by_id(4).await.unwrap();
512 assert!(res_err.is_err());
513 })
514 .await;
515 }
516
517 #[fuchsia::test]
518 async fn test_get_device_info() {
519 let device_info = Some(fdevice::natural::NodeDeviceInfo {
520 vid: Some(1),
521 pid: Some(1),
522 name: Some("test device".to_string()),
523 ..Default::default()
524 });
525 run_test_with_config(
526 Config { device_info, ..Default::default() },
527 |client, _| async move {
528 let res = client.get_node_device_info().await.unwrap();
529 assert!(res.is_ok());
530 let info = res.unwrap();
531 assert_eq!(info.vid, Some(1));
532 assert_eq!(info.pid, Some(1));
533 assert_eq!(info.name, Some("test device".to_string()));
534 },
535 )
536 .await;
537 }
538
539 #[fuchsia::test]
540 async fn test_get_board_info() {
541 let board_info =
542 Some(fdevice::natural::BoardInfo { vid: Some(1), pid: Some(1), ..Default::default() });
543 run_test_with_config(Config { board_info, ..Default::default() }, |client, _| async move {
544 let res = client.get_board_info().await.unwrap();
545 assert!(res.is_ok());
546 let info = res.unwrap();
547 assert_eq!(info.vid, Some(1));
548 assert_eq!(info.pid, Some(1));
549 })
550 .await;
551 }
552
553 #[fuchsia::test]
554 async fn test_get_power_configuration() {
555 let power_elements =
556 vec![fidl_next_fuchsia_hardware_power::natural::PowerElementConfiguration {
557 element: Some(fidl_next_fuchsia_hardware_power::natural::PowerElement {
558 name: Some("test power element".to_string()),
559 ..Default::default()
560 }),
561 ..Default::default()
562 }];
563 run_test_with_config(
564 Config { power_elements, ..Default::default() },
565 |client, _| async move {
566 let res = client.get_power_configuration().await.unwrap();
567 assert!(res.is_ok());
568 let configs = res.unwrap().config;
569 assert_eq!(configs.len(), 1);
570 assert_eq!(
571 configs[0].element.as_ref().unwrap().name,
572 Some("test power element".to_string())
573 );
574 },
575 )
576 .await;
577 }
578
579 #[fuchsia::test]
580 async fn test_get_metadata() {
581 run_test_with_config(Default::default(), |client, fake_pdev| async move {
582 fake_pdev.add_metadata("test_id", vec![1, 2, 3, 4]);
583 let res = client.get_metadata("test_id").await.unwrap();
584 assert!(res.is_ok());
585 assert_eq!(res.unwrap().metadata, vec![1, 2, 3, 4]);
586
587 let res_err = client.get_metadata("unknown_id").await.unwrap();
588 assert!(res_err.is_err());
589 })
590 .await;
591 }
592}