1// Copyright 2024 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.
45use core::ffi::c_void;
6use core::ptr::NonNull;
7use std::num::NonZero;
8use std::ops::ControlFlow;
9use std::sync::OnceLock;
1011use log::{debug, warn};
12use zx::Status;
1314use fdf::{Channel, Dispatcher, DispatcherBuilder, DispatcherRef};
15use fidl_fuchsia_driver_framework::DriverRequest;
1617use fdf::{fdf_handle_t, DriverHandle, Message};
1819use crate::{Driver, DriverContext};
20use fdf_sys::fdf_dispatcher_get_current_dispatcher;
21use fidl_fuchsia_driver_framework::DriverStartArgs;
2223/// Implements the lifecycle management of a rust driver, including starting and stopping it
24/// and setting up the rust async dispatcher and logging for the driver to use, and running a
25/// message loop for the driver start and stop messages.
26pub struct DriverServer<T> {
27 server_handle: OnceLock<Channel<[u8]>>,
28 root_dispatcher: DispatcherRef<'static>,
29 driver: OnceLock<T>,
30}
3132impl<T: Driver> DriverServer<T> {
33/// Called by the driver host to start the driver.
34 ///
35 /// # Safety
36 ///
37 /// The caller must provide a valid non-zero driver transport channel handle for
38 /// `server_handle`.
39pub unsafe extern "C" fn initialize(server_handle: fdf_handle_t) -> *mut c_void {
40// SAFETY: We verify that the pointer returned is non-null, ensuring that this was
41 // called from within a driver context.
42let root_dispatcher = NonNull::new(unsafe { fdf_dispatcher_get_current_dispatcher() })
43 .expect("Non-null current dispatcher");
44// SAFETY: We use NonZero::new to verify that we've been given a non-zero
45 // driver handle, and expect that the caller (which is the driver runtime) has given us
46 // a valid driver transport fidl channel.
47let server_handle = OnceLock::from(unsafe {
48 Channel::from_driver_handle(DriverHandle::new_unchecked(
49 NonZero::new(server_handle).expect("valid driver handle"),
50 ))
51 });
5253// SAFETY: the root dispatcher is expected to live as long as this driver is loaded.
54let root_dispatcher = unsafe { DispatcherRef::from_raw(root_dispatcher) };
55// We leak the box holding the server so that the driver runtime can take control over the
56 // lifetime of the server object.
57let server_ptr = Box::into_raw(Box::new(Self {
58 server_handle,
59 root_dispatcher: root_dispatcher.clone(),
60 driver: OnceLock::default(),
61 }));
6263// Reconstitute the pointer to the `DriverServer` as a mut reference to use it in the main
64 // loop.
65 // SAFETY: We are the exclusive owner of the object until we drop the server handle,
66 // triggering the driver host to call `destroy`.
67let server = unsafe { &mut *server_ptr };
6869// Build a new dispatcher that we can have spin on a fuchsia-async executor main loop
70 // to act as a reactor for non-driver events.
71let rust_async_dispatcher = DispatcherBuilder::new()
72 .name("fuchsia-async")
73 .allow_thread_blocking()
74 .create_released()
75 .expect("failure creating blocking dispatcher for rust async");
76// Post the task to the dispatcher that will run the fuchsia-async loop, and have it run
77 // the server's message loop waiting for start and stop messages from the driver host.
78rust_async_dispatcher
79 .post_task_sync(move |status| {
80// bail immediately if we were somehow cancelled before we started
81let Status::OK = status else { return };
82 Dispatcher::override_current(root_dispatcher.clone(), || {
83// create and run a fuchsia-async executor, giving it the "root" dispatcher to
84 // actually execute driver tasks on, as this thread will be effectively blocked
85 // by the reactor loop.
86let mut executor = fuchsia_async::LocalExecutor::new();
87 executor.run_singlethreaded(async move {
88 server.message_loop(root_dispatcher).await;
89// take the server handle so it can drop after the async block is done,
90 // which will signal to the driver host that the driver has finished
91 // shutdown, so that we are can guarantee that when `destroy` is called, we
92 // are not still using `server`.
93server.server_handle.take()
94 });
95 });
96 })
97 .expect("failure spawning main event loop for rust async dispatch");
9899// Take the pointer of the server object to use as the identifier for the server to the
100 // driver runtime. It uses this as an opaque identifier and expects no particular layout of
101 // the object pointed to, and we use it to free the box at unload in `Self::destroy`.
102server_ptr.cast()
103 }
104105/// Called by the driver host after shutting down a driver and once the handle passed to
106 /// [`Self::initialize`] is dropped.
107 ///
108 /// # Safety
109 ///
110 /// This must only be called after the handle provided to [`Self::initialize`] has been
111 /// dropped, which indicates that the main event loop of the driver lifecycle has ended.
112pub unsafe extern "C" fn destroy(obj: *mut c_void) {
113let obj: *mut Self = obj.cast();
114// SAFETY: We built this object in `initialize` and gave ownership of its
115 // lifetime to the driver framework, which is now giving it to us to free.
116unsafe { drop(Box::from_raw(obj)) }
117 }
118119/// Implements the main message loop for handling start and stop messages from rust
120 /// driver host and passing them on to the implementation of [`Driver`] we contain.
121async fn message_loop(&mut self, dispatcher: DispatcherRef<'_>) {
122loop {
123let server_handle_lock = self.server_handle.get();
124let Some(server_handle) = server_handle_lock else {
125panic!("driver already shut down while message loop was running")
126 };
127match server_handle.read_bytes(dispatcher.clone()).await {
128Ok(Some(message)) => {
129if let ControlFlow::Break(_) = self.handle_message(message).await {
130// driver shut down or failed to start, exit message loop
131return;
132 }
133 }
134Ok(None) => panic!("unexpected empty message on server channel"),
135Err(status @ Status::PEER_CLOSED) | Err(status @ Status::UNAVAILABLE) => {
136warn!("Driver server channel closed before a stop message with status {status}, exiting main loop early but stop() will not be called.");
137return;
138 }
139Err(e) => panic!("unexpected error on server channel {e}"),
140 }
141 }
142 }
143144/// Handles the start message by initializing logging and calling the [`Driver::start`] with
145 /// a constructed [`DriverContext`].
146 ///
147 /// # Panics
148 ///
149 /// This method panics if the start message has already been received.
150async fn handle_start(&self, start_args: DriverStartArgs) -> Result<(), Status> {
151let context = DriverContext::new(self.root_dispatcher.clone(), start_args)?;
152 context.start_logging(T::NAME)?;
153154log::debug!("driver starting");
155156let driver = T::start(context).await?;
157self.driver.set(driver).map_err(|_| ()).expect("Driver received start message twice");
158Ok(())
159 }
160161async fn handle_stop(&mut self) {
162log::debug!("driver stopping");
163self.driver
164 .take()
165 .expect("received stop message more than once or without successfully starting")
166 .stop()
167 .await;
168 }
169170/// Dispatches messages from the driver host to the appropriate implementation.
171 ///
172 /// # Panics
173 ///
174 /// This method panics if the messages are received out of order somehow (two start messages,
175 /// stop before start, etc).
176async fn handle_message(&mut self, message: Message<[u8]>) -> ControlFlow<()> {
177let (_, request) = DriverRequest::read_from_message(message).unwrap();
178match request {
179 DriverRequest::Start { start_args, responder } => {
180let res = self.handle_start(start_args).await.map_err(Status::into_raw);
181let Some(server_handle) = self.server_handle.get() else {
182panic!("driver shutting down before it was finished starting")
183 };
184 responder.send_response(server_handle, res).unwrap();
185if res.is_ok() {
186 ControlFlow::Continue(())
187 } else {
188debug!("driver failed to start, exiting main loop");
189 ControlFlow::Break(())
190 }
191 }
192 DriverRequest::Stop {} => {
193self.handle_stop().await;
194 ControlFlow::Break(())
195 }
196_ => panic!("Unknown message on server channel"),
197 }
198 }
199}
200201#[cfg(test)]
202mod tests {
203use super::*;
204205use fdf::{CurrentDispatcher, OnDispatcher};
206use fdf_env::test::spawn_in_driver;
207use fidl_next_fuchsia_driver_framework::DriverClientHandler;
208use zx::Status;
209210use fdf::Channel;
211use fidl_next::{Client, ClientEnd};
212use fidl_next_fuchsia_driver_framework::{DriverClientSender, DriverStartRequest};
213214#[derive(Default)]
215struct TestDriver {
216 _not_empty: bool,
217 }
218219impl Driver for TestDriver {
220const NAME: &str = "test_driver";
221222async fn start(context: DriverContext) -> Result<Self, Status> {
223let DriverContext { root_dispatcher, start_args, .. } = context;
224println!("created new test driver on dispatcher: {root_dispatcher:?}");
225println!("driver start message: {start_args:?}");
226Ok(Self::default())
227 }
228async fn stop(&self) {
229println!("driver stop message");
230 }
231 }
232233crate::driver_register!(TestDriver);
234235struct DriverClient;
236impl DriverClientHandler<fdf_fidl::DriverChannel> for DriverClient {}
237238#[test]
239fn register_driver() {
240assert_eq!(__fuchsia_driver_registration__.version, 1);
241let initialize_func = __fuchsia_driver_registration__.v1.initialize.expect("initializer");
242let destroy_func = __fuchsia_driver_registration__.v1.destroy.expect("destroy function");
243244let (server_chan, client_chan) = Channel::<[fidl_next::Chunk]>::create();
245246let (client_exit_tx, client_exit_rx) = futures::channel::oneshot::channel();
247 spawn_in_driver("driver registration", async move {
248let client_end = ClientEnd::from_untyped(fdf_fidl::DriverChannel::new(client_chan));
249let mut client = Client::new(client_end);
250let client_sender = client.sender().clone();
251252 CurrentDispatcher
253 .spawn_task(async move {
254 client.run(DriverClient).await.unwrap_err();
255 client_exit_tx.send(()).unwrap();
256 })
257 .unwrap();
258259let channel_handle = server_chan.into_driver_handle().into_raw().get();
260let driver_server = unsafe { initialize_func(channel_handle) } as usize;
261assert_ne!(driver_server, 0);
262let mut start_request = DriverStartRequest {
263 start_args: fidl_next_fuchsia_driver_framework::DriverStartArgs {
264 node: None,
265 symbols: None,
266 url: None,
267 program: None,
268 incoming: None,
269 outgoing_dir: None,
270 config: None,
271 node_name: None,
272 node_properties: None,
273 node_offers: None,
274 node_token: None,
275 node_properties_2: None,
276 },
277 };
278279let mut start_res = client_sender.start(&mut start_request).unwrap().await.unwrap();
280 start_res.decode().unwrap().unwrap();
281282 client_sender.stop().unwrap().await.unwrap();
283 client_exit_rx.await.unwrap();
284285unsafe {
286 destroy_func(driver_server as *mut c_void);
287 }
288 })
289 }
290}