Skip to main content

fake_gpio/
lib.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fidl_fuchsia_driver_framework as fdf;
6use fidl_next::{Request, Responder};
7use fidl_next_fuchsia_hardware_gpio::{self as fgpio, GpioServerHandler};
8use fuchsia_async as fasync;
9use fuchsia_component::server::{ServiceFs, ServiceObjTrait};
10use fuchsia_sync::Mutex;
11use std::sync::Arc;
12
13struct FakeGpioState {
14    read_value: bool,
15    buffer_mode: fgpio::BufferMode,
16    interrupt: zx::Interrupt,
17    client_has_interrupt: bool,
18}
19
20pub struct FakeGpio {
21    state: Arc<Mutex<FakeGpioState>>,
22}
23
24impl Default for FakeGpio {
25    fn default() -> Self {
26        Self::new(zx::Interrupt::invalid())
27    }
28}
29
30impl FakeGpio {
31    pub fn new(interrupt: zx::Interrupt) -> Self {
32        Self {
33            state: Arc::new(Mutex::new(FakeGpioState {
34                read_value: false,
35                buffer_mode: fgpio::BufferMode::Input,
36                interrupt,
37                client_has_interrupt: false,
38            })),
39        }
40    }
41
42    pub fn set_read_value(&self, read_value: bool) {
43        self.state.lock().read_value = read_value;
44    }
45
46    pub fn buffer_mode(&self) -> fgpio::BufferMode {
47        self.state.lock().buffer_mode
48    }
49
50    pub fn set_buffer_mode(&self, buffer_mode: fgpio::BufferMode) {
51        self.state.lock().buffer_mode = buffer_mode;
52    }
53
54    pub fn serve<O: ServiceObjTrait>(
55        &self,
56        service_fs: &mut ServiceFs<O>,
57        scope: fasync::ScopeHandle,
58        instance_name: &str,
59    ) -> fdf::Offer {
60        fdf_component::ServiceOffer::<fgpio::Service>::new_next()
61            .add_default_named_next(
62                service_fs,
63                instance_name,
64                FakeGpioService { state: self.state.clone(), scope },
65            )
66            .build_zircon_offer_next()
67    }
68}
69
70struct FakeGpioService {
71    state: Arc<Mutex<FakeGpioState>>,
72    scope: fasync::ScopeHandle,
73}
74
75impl fgpio::ServiceHandler for FakeGpioService {
76    fn device(&self, server_end: fidl_next::ServerEnd<fgpio::Gpio>) {
77        server_end.spawn_on(FakeGpioServer { state: self.state.clone() }, &self.scope);
78    }
79}
80
81struct FakeGpioServer {
82    state: Arc<Mutex<FakeGpioState>>,
83}
84
85impl GpioServerHandler for FakeGpioServer {
86    async fn read(&mut self, responder: Responder<fgpio::gpio::Read>) {
87        let read_value = self.state.lock().read_value;
88        let _ = responder.respond(read_value).await;
89    }
90
91    async fn set_buffer_mode(
92        &mut self,
93        request: Request<fgpio::gpio::SetBufferMode>,
94        responder: Responder<fgpio::gpio::SetBufferMode>,
95    ) {
96        self.state.lock().buffer_mode = request.payload().mode;
97        let _ = responder.respond(()).await;
98    }
99
100    async fn get_interrupt(
101        &mut self,
102        _request: Request<fgpio::gpio::GetInterrupt>,
103        responder: Responder<fgpio::gpio::GetInterrupt>,
104    ) {
105        let result: Result<zx::Interrupt, zx::Status> = {
106            let mut state = self.state.lock();
107            if state.client_has_interrupt {
108                Err(zx::Status::ACCESS_DENIED)
109            } else if state.interrupt.is_invalid() {
110                Err(zx::Status::NOT_SUPPORTED)
111            } else {
112                state.client_has_interrupt = true;
113                state.interrupt.duplicate_handle(zx::Rights::SAME_RIGHTS)
114            }
115        };
116        match result {
117            Ok(interrupt) => {
118                let _ = responder.respond(interrupt).await;
119            }
120            Err(e) => {
121                let _ = responder.respond_err(e.into_raw()).await;
122            }
123        }
124    }
125
126    async fn configure_interrupt(
127        &mut self,
128        _request: Request<fgpio::gpio::ConfigureInterrupt>,
129        responder: Responder<fgpio::gpio::ConfigureInterrupt>,
130    ) {
131        let _ = responder.respond(()).await;
132    }
133
134    async fn release_interrupt(&mut self, responder: Responder<fgpio::gpio::ReleaseInterrupt>) {
135        self.state.lock().client_has_interrupt = false;
136        let _ = responder.respond(()).await;
137    }
138}