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.into_raw()).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 if let Some(mmio) = mmio_clone {
170 let _ = responder.respond(mmio).await;
171 } else {
172 let _ = responder.respond_err(Status::NOT_FOUND.into_raw()).await;
173 }
174 }
175
176 async fn get_interrupt_by_id(
177 &mut self,
178 request: Request<fdevice::device::GetInterruptById>,
179 responder: Responder<fdevice::device::GetInterruptById>,
180 ) {
181 let index = request.payload().index;
182 let (irq_res, use_fake) = {
183 let state = self.state.lock();
184 let irq_res =
185 state.config.irqs.get(&index).map(|i| i.duplicate_handle(zx::Rights::SAME_RIGHTS));
186 (irq_res, state.config.use_fake_irq)
187 };
188
189 let res: Result<zx::Interrupt, zx::Status> = if let Some(res) = irq_res {
190 res
191 } else if use_fake {
192 zx::VirtualInterrupt::create_virtual().map(|irq| zx::Interrupt::from(irq.into_handle()))
193 } else {
194 Err(Status::NOT_FOUND)
195 };
196
197 match res {
198 Ok(irq) => {
199 let _ = responder.respond(irq).await;
200 }
201 Err(e) => {
202 let _ = responder.respond_err(e.into_raw()).await;
203 }
204 }
205 }
206
207 async fn get_interrupt_by_name(
208 &mut self,
209 request: Request<fdevice::device::GetInterruptByName>,
210 responder: Responder<fdevice::device::GetInterruptByName>,
211 ) {
212 let name = request.payload().name.as_str().to_string();
213 let (irq_res, use_fake) = {
214 let state = self.state.lock();
215 let irq_res = state
216 .config
217 .irq_names
218 .get(&name)
219 .and_then(|idx| state.config.irqs.get(idx))
220 .map(|i| i.duplicate_handle(zx::Rights::SAME_RIGHTS));
221 (irq_res, state.config.use_fake_irq)
222 };
223
224 let res = if let Some(res) = irq_res {
225 res
226 } else if use_fake {
227 zx::VirtualInterrupt::create_virtual().map(|irq| zx::Interrupt::from(irq.into_handle()))
228 } else {
229 Err(Status::NOT_FOUND)
230 };
231
232 match res {
233 Ok(irq) => {
234 let _ = responder.respond(irq).await;
235 }
236 Err(e) => {
237 let _ = responder.respond_err(e.into_raw()).await;
238 }
239 }
240 }
241
242 async fn get_bti_by_id(
243 &mut self,
244 request: Request<fdevice::device::GetBtiById>,
245 responder: Responder<fdevice::device::GetBtiById>,
246 ) {
247 let index = request.payload().index;
248 let (bti_res, use_fake) = {
249 let state = self.state.lock();
250 let bti_res =
251 state.config.btis.get(&index).map(|b| b.duplicate_handle(zx::Rights::SAME_RIGHTS));
252 (bti_res, state.config.use_fake_bti)
253 };
254
255 let res = if let Some(res) = bti_res {
256 res
257 } else if use_fake {
258 FakeBti::create().and_then(|fake| fake.duplicate_handle(zx::Rights::SAME_RIGHTS))
259 } else {
260 Err(Status::NOT_FOUND)
261 };
262
263 match res {
264 Ok(bti) => {
265 let _ = responder.respond(bti).await;
266 }
267 Err(status) => {
268 let _ = responder.respond_err(status.into_raw()).await;
269 }
270 }
271 }
272
273 async fn get_bti_by_name(
274 &mut self,
275 request: Request<fdevice::device::GetBtiByName>,
276 responder: Responder<fdevice::device::GetBtiByName>,
277 ) {
278 let name = request.payload().name.as_str().to_string();
279 let (bti_res, use_fake) = {
280 let state = self.state.lock();
281 let bti_res = state
282 .config
283 .bti_names
284 .get(&name)
285 .and_then(|idx| state.config.btis.get(idx))
286 .map(|b| b.duplicate_handle(zx::Rights::SAME_RIGHTS));
287 (bti_res, state.config.use_fake_bti)
288 };
289
290 let res = if let Some(res) = bti_res {
291 res
292 } else if use_fake {
293 FakeBti::create().and_then(|fake| fake.duplicate_handle(zx::Rights::SAME_RIGHTS))
294 } else {
295 Err(Status::NOT_FOUND)
296 };
297
298 match res {
299 Ok(bti) => {
300 let _ = responder.respond(bti).await;
301 }
302 Err(status) => {
303 let _ = responder.respond_err(status.into_raw()).await;
304 }
305 }
306 }
307
308 async fn get_smc_by_id(
309 &mut self,
310 request: Request<fdevice::device::GetSmcById>,
311 responder: Responder<fdevice::device::GetSmcById>,
312 ) {
313 let index = request.payload().index;
314 let smc_res = self
315 .state
316 .lock()
317 .config
318 .smcs
319 .get(&index)
320 .map(|s| s.duplicate_handle(zx::Rights::SAME_RIGHTS));
321
322 let res = smc_res.unwrap_or(Err(Status::NOT_FOUND));
323
324 match res {
325 Ok(dup) => {
326 let _ = responder.respond(dup).await;
327 }
328 Err(status) => {
329 let _ = responder.respond_err(status.into_raw()).await;
330 }
331 }
332 }
333
334 async fn get_smc_by_name(
335 &mut self,
336 _request: Request<fdevice::device::GetSmcByName>,
337 responder: Responder<fdevice::device::GetSmcByName>,
338 ) {
339 let _ = responder.respond_err(Status::NOT_FOUND.into_raw()).await;
340 }
341
342 async fn get_power_configuration(
343 &mut self,
344 responder: Responder<fdevice::device::GetPowerConfiguration>,
345 ) {
346 let power_elements = self.state.lock().config.power_elements.clone();
347 let _ = responder.respond(power_elements).await;
348 }
349
350 async fn get_node_device_info(
351 &mut self,
352 responder: Responder<fdevice::device::GetNodeDeviceInfo>,
353 ) {
354 let device_info = self.state.lock().config.device_info.clone();
355 if let Some(info) = device_info {
356 let _ = responder.respond(info).await;
357 } else {
358 let _ = responder.respond_err(Status::NOT_SUPPORTED.into_raw()).await;
359 }
360 }
361
362 async fn get_board_info(&mut self, responder: Responder<fdevice::device::GetBoardInfo>) {
363 let board_info = self.state.lock().config.board_info.clone();
364 if let Some(info) = board_info {
365 let _ = responder.respond(info).await;
366 } else {
367 let _ = responder.respond_err(Status::NOT_SUPPORTED.into_raw()).await;
368 }
369 }
370
371 async fn get_metadata(
372 &mut self,
373 request: Request<fdevice::device::GetMetadata>,
374 responder: Responder<fdevice::device::GetMetadata>,
375 ) {
376 let metadata = self.state.lock().metadata.get(request.payload().id.as_str()).cloned();
377 if let Some(data) = metadata {
378 let _ = responder.respond(data).await;
379 } else {
380 let _ = responder.respond_err(Status::NOT_FOUND.into_raw()).await;
381 }
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388 use fidl_next::fuchsia::create_channel;
389
390 async fn run_test_with_config<F, Fut>(config: Config, test_func: F)
391 where
392 F: FnOnce(fidl_next::Client<fdevice::Device>, FakePDev) -> Fut,
393 Fut: std::future::Future<Output = ()>,
394 {
395 let fake_pdev = FakePDev::new();
396 fake_pdev.set_config(config);
397
398 let (client_end, server_end) = create_channel::<fdevice::Device>();
399 let server = FakePDevServer { state: fake_pdev.state.clone() };
400 let scope = fasync::Scope::new_with_name("test");
401 server_end.spawn_on(server, &scope);
402
403 let client = client_end.spawn();
404 test_func(client, fake_pdev).await;
405 }
406
407 #[fuchsia::test]
408 async fn test_get_mmios() {
409 let mut mmios = HashMap::new();
410 let vmo = zx::Vmo::create(11).unwrap();
411 mmios.insert(
412 5,
413 fdevice::natural::Mmio { offset: Some(10), size: Some(11), vmo: Some(vmo.into()) },
414 );
415 let mut mmio_names = HashMap::new();
416 mmio_names.insert("test-name".to_string(), 5);
417
418 run_test_with_config(
419 Config { mmios, mmio_names, ..Default::default() },
420 |client, _| async move {
421 let res = client.get_mmio_by_id(5).await.unwrap();
423 assert!(res.is_ok());
424 let mmio = res.unwrap();
425 assert_eq!(mmio.offset, Some(10));
426 assert_eq!(mmio.size, Some(11));
427
428 let res_err = client.get_mmio_by_id(4).await.unwrap();
429 assert!(res_err.is_err());
430
431 let res_name = client.get_mmio_by_name("test-name").await.unwrap();
433 assert!(res_name.is_ok());
434 let mmio_name = res_name.unwrap();
435 assert_eq!(mmio_name.offset, Some(10));
436 assert_eq!(mmio_name.size, Some(11));
437
438 let res_name_err = client.get_mmio_by_name("unknown-name").await.unwrap();
439 assert!(res_name_err.is_err());
440 },
441 )
442 .await;
443 }
444
445 #[fuchsia::test]
446 async fn test_invalid_mmio() {
447 let mut mmios = HashMap::new();
448 mmios.insert(
449 5,
450 fdevice::natural::Mmio {
451 offset: Some(10),
452 size: Some(11),
453 vmo: None, },
455 );
456 run_test_with_config(Config { mmios, ..Default::default() }, |client, _| async move {
457 let res = client.get_mmio_by_id(5).await.unwrap();
458 assert!(res.is_ok());
459 assert!(res.unwrap().vmo.is_none());
460 })
461 .await;
462 }
463
464 #[fuchsia::test]
465 async fn test_get_irqs() {
466 let mut irqs = HashMap::new();
467 let irq = zx::VirtualInterrupt::create_virtual().unwrap();
468 irqs.insert(5, zx::Interrupt::from(zx::NullableHandle::from(irq.into_handle())));
469 let mut irq_names = HashMap::new();
470 irq_names.insert("test-name".to_string(), 5);
471
472 run_test_with_config(
473 Config { irqs, irq_names, ..Default::default() },
474 |client, _| async move {
475 let res = client.get_interrupt_by_id(5, 0).await.unwrap();
476 assert!(res.is_ok());
477
478 let res_err = client.get_interrupt_by_id(4, 0).await.unwrap();
479 assert!(res_err.is_err());
480
481 let res_name = client.get_interrupt_by_name("test-name", 0).await.unwrap();
482 assert!(res_name.is_ok());
483 },
484 )
485 .await;
486 }
487
488 #[fuchsia::test]
489 async fn test_get_btis() {
490 let mut btis = HashMap::new();
491 let bti = FakeBti::create().unwrap();
492 btis.insert(5, bti.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap());
493
494 run_test_with_config(Config { btis, ..Default::default() }, |client, _| async move {
495 let res = client.get_bti_by_id(5).await.unwrap();
496 assert!(res.is_ok());
497
498 let res_err = client.get_bti_by_id(4).await.unwrap();
499 assert!(res_err.is_err());
500 })
501 .await;
502 }
503
504 #[fuchsia::test]
505 async fn test_get_smc() {
506 unsafe extern "C" {
507 fn fake_root_resource_create(out: *mut zx::sys::zx_handle_t) -> zx::sys::zx_status_t;
508 }
509 let mut raw = zx::sys::ZX_HANDLE_INVALID;
510 unsafe {
511 assert_eq!(fake_root_resource_create(&mut raw), zx::sys::ZX_OK);
512 }
513 let smc = unsafe { zx::Resource::from(zx::Handle::from_raw(raw).unwrap()) };
514
515 let mut smcs = HashMap::new();
516 smcs.insert(5, smc);
517
518 run_test_with_config(Config { smcs, ..Default::default() }, |client, _| async move {
519 let res = client.get_smc_by_id(5).await.unwrap();
520 assert!(res.is_ok());
521
522 let res_err = client.get_smc_by_id(4).await.unwrap();
523 assert!(res_err.is_err());
524 })
525 .await;
526 }
527
528 #[fuchsia::test]
529 async fn test_get_device_info() {
530 let device_info = Some(fdevice::natural::NodeDeviceInfo {
531 vid: Some(1),
532 pid: Some(1),
533 name: Some("test device".to_string()),
534 ..Default::default()
535 });
536 run_test_with_config(
537 Config { device_info, ..Default::default() },
538 |client, _| async move {
539 let res = client.get_node_device_info().await.unwrap();
540 assert!(res.is_ok());
541 let info = res.unwrap();
542 assert_eq!(info.vid, Some(1));
543 assert_eq!(info.pid, Some(1));
544 assert_eq!(info.name, Some("test device".to_string()));
545 },
546 )
547 .await;
548 }
549
550 #[fuchsia::test]
551 async fn test_get_board_info() {
552 let board_info =
553 Some(fdevice::natural::BoardInfo { vid: Some(1), pid: Some(1), ..Default::default() });
554 run_test_with_config(Config { board_info, ..Default::default() }, |client, _| async move {
555 let res = client.get_board_info().await.unwrap();
556 assert!(res.is_ok());
557 let info = res.unwrap();
558 assert_eq!(info.vid, Some(1));
559 assert_eq!(info.pid, Some(1));
560 })
561 .await;
562 }
563
564 #[fuchsia::test]
565 async fn test_get_power_configuration() {
566 let power_elements =
567 vec![fidl_next_fuchsia_hardware_power::natural::PowerElementConfiguration {
568 element: Some(fidl_next_fuchsia_hardware_power::natural::PowerElement {
569 name: Some("test power element".to_string()),
570 ..Default::default()
571 }),
572 ..Default::default()
573 }];
574 run_test_with_config(
575 Config { power_elements, ..Default::default() },
576 |client, _| async move {
577 let res = client.get_power_configuration().await.unwrap();
578 assert!(res.is_ok());
579 let configs = res.unwrap().config;
580 assert_eq!(configs.len(), 1);
581 assert_eq!(
582 configs[0].element.as_ref().unwrap().name,
583 Some("test power element".to_string())
584 );
585 },
586 )
587 .await;
588 }
589
590 #[fuchsia::test]
591 async fn test_get_metadata() {
592 run_test_with_config(Default::default(), |client, fake_pdev| async move {
593 fake_pdev.add_metadata("test_id", vec![1, 2, 3, 4]);
594 let res = client.get_metadata("test_id").await.unwrap();
595 assert!(res.is_ok());
596 assert_eq!(res.unwrap().metadata, vec![1, 2, 3, 4]);
597
598 let res_err = client.get_metadata("unknown_id").await.unwrap();
599 assert!(res_err.is_err());
600 })
601 .await;
602 }
603}