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 crate::{Incoming, Node};
6use fuchsia_component::server::{ServiceFs, ServiceObjTrait};
7use log::error;
8use namespace::Namespace;
9use zx::Status;
1011use fdf::DispatcherRef;
12use fidl_fuchsia_driver_framework::DriverStartArgs;
1314/// The context arguments passed to the driver in its start arguments.
15pub struct DriverContext {
16/// A reference to the root [`fdf::Dispatcher`] for this driver.
17pub root_dispatcher: DispatcherRef<'static>,
18/// The original [`DriverStartArgs`] passed in as start arguments, minus any parts that were
19 /// used to construct other elements of [`Self`].
20pub start_args: DriverStartArgs,
21/// The incoming namespace constructed from [`DriverStartArgs::incoming`]. Since producing this
22 /// consumes the incoming namespace from [`Self::start_args`], that will always be [`None`].
23pub incoming: Incoming,
24#[doc(hidden)]
25_private: (),
26}
2728impl DriverContext {
29/// Binds the node proxy client end from the start args into a [`NodeProxy`] that can be used
30 /// to add child nodes. Dropping this proxy will result in the node being removed and the
31 /// driver starting shutdown, so it should be bound and stored in your driver object in its
32 /// [`crate::Driver::start`] method.
33 ///
34 /// After calling this, [`DriverStartArgs::node`] in [`Self::start_args`] will be `None`.
35 ///
36 /// Returns [`Status::INVALID_ARGS`] if the node client end is not present in the start
37 /// arguments.
38pub fn take_node(&mut self) -> Result<Node, Status> {
39let node_client = self.start_args.node.take().ok_or(Status::INVALID_ARGS)?;
40Ok(Node::from(node_client.into_proxy()))
41 }
4243/// Serves the given [`ServiceFs`] on the node's outgoing directory. This can only be called
44 /// once, and after this the [`DriverStartArgs::outgoing_dir`] member will be [`None`].
45 ///
46 /// Logs an error and returns [`Status::INVALID_ARGS`] if the outgoing directory server end is
47 /// not present in the start arguments, or [`Status::INTERNAL`] if serving the connection
48 /// failed.
49pub fn serve_outgoing<O: ServiceObjTrait>(
50&mut self,
51 outgoing_fs: &mut ServiceFs<O>,
52 ) -> Result<(), Status> {
53let Some(outgoing_dir) = self.start_args.outgoing_dir.take() else {
54error!("Tried to serve on outgoing directory but it wasn't available");
55return Err(Status::INVALID_ARGS);
56 };
57 outgoing_fs.serve_connection(outgoing_dir).map_err(|err| {
58error!("Failed to serve outgoing directory: {err}");
59 Status::INTERNAL
60 })?;
6162Ok(())
63 }
6465pub(crate) fn new(
66 root_dispatcher: DispatcherRef<'static>,
67mut start_args: DriverStartArgs,
68 ) -> Result<Self, Status> {
69let incoming_namespace: Namespace = start_args
70 .incoming
71 .take()
72 .unwrap_or_else(|| vec![])
73 .try_into()
74 .map_err(|_| Status::INVALID_ARGS)?;
75let incoming = incoming_namespace.try_into().map_err(|_| Status::INVALID_ARGS)?;
76Ok(DriverContext { root_dispatcher, start_args, incoming, _private: () })
77 }
7879pub(crate) fn start_logging(&self, driver_name: &str) -> Result<(), Status> {
80let log_proxy = match self.incoming.protocol().connect() {
81Ok(log_proxy) => log_proxy,
82Err(err) => {
83eprintln!("Error connecting to log sink proxy at driver startup: {err}. Continuing without logging.");
84return Ok(());
85 }
86 };
8788if let Err(e) = diagnostics_log::initialize(
89 diagnostics_log::PublishOptions::default()
90 .use_log_sink(log_proxy)
91 .tags(&["driver", driver_name]),
92 ) {
93eprintln!("Error initializing logging at driver startup: {e}");
94 }
95Ok(())
96 }
97}