1use crate::{HealthAlertReporter, Incoming, Node, ParentProperty, PropertyValue};
6use fuchsia_async::ScopeHandle;
7use fuchsia_component::server::{ServiceFs, ServiceObjTrait};
8use fuchsia_component_config::Config;
9use fuchsia_inspect::Inspector;
10use inspect_runtime::PublishOptions;
11use log::error;
12use namespace::Namespace;
13use zx::Status;
14
15use fdf::AsyncDispatcher;
16use fidl_next_fuchsia_driver_framework::{DriverStartArgs, HealthAlert};
17
18pub use fuchsia_inspect_contrib::content_publisher as inspect_publisher;
19
20#[non_exhaustive]
22pub struct DriverContext {
23 pub root_dispatcher: AsyncDispatcher,
25 pub start_args: DriverStartArgs,
28 pub incoming: Incoming,
31 health_alert_reporter: HealthAlertReporter,
32}
33
34impl DriverContext {
35 pub fn health_alert_reporter(&self) -> HealthAlertReporter {
39 self.health_alert_reporter.clone()
40 }
41
42 pub fn report_health_alert(&self, alert: HealthAlert) {
46 self.health_alert_reporter.report_health_alert(alert);
47 }
48
49 pub fn take_node(&mut self) -> Result<Node, Status> {
59 let node_client = self.start_args.node.take().ok_or(Status::INVALID_ARGS)?;
60 let node_client =
61 fidl::endpoints::ClientEnd::<fidl_fuchsia_driver_framework::NodeMarker>::new(
62 node_client.into_untyped(),
63 );
64 Ok(Node::from(node_client.into_proxy()))
65 }
66
67 pub fn take_config<C: Config>(&mut self) -> Result<C, Status> {
73 let vmo = self.start_args.config.take().ok_or(Status::INVALID_ARGS)?;
74 Ok(Config::from_vmo(&vmo).expect("Config VMO handle must be valid."))
75 }
76
77 pub fn serve_outgoing<O: ServiceObjTrait>(
84 &mut self,
85 outgoing_fs: &mut ServiceFs<O>,
86 ) -> Result<(), Status> {
87 let Some(outgoing_dir) = self.start_args.outgoing_dir.take() else {
88 error!("Tried to serve on outgoing directory but it wasn't available");
89 return Err(Status::INVALID_ARGS);
90 };
91 let outgoing_dir = into_legacy_server_end(outgoing_dir);
92 outgoing_fs.serve_connection(outgoing_dir).map_err(|err| {
93 error!("Failed to serve outgoing directory: {err}");
94 Status::INTERNAL
95 })?;
96
97 Ok(())
98 }
99
100 pub fn publish_inspect(&self, inspector: &Inspector, scope: ScopeHandle) -> Result<(), Status> {
105 let client = self.incoming.connect_protocol().map_err(|err| {
106 error!("Error connecting to inspect : {err}");
107 Status::INTERNAL
108 })?;
109
110 let task = inspect_runtime::publish(
111 inspector,
112 PublishOptions::default().on_inspect_sink_client(client),
113 )
114 .ok_or(Status::INTERNAL)?;
115
116 scope.spawn_local(task);
117
118 Ok(())
119 }
120
121 pub fn inspect_content_publisher(&self) -> Result<inspect_publisher::ContentPublisher, Status> {
123 let inspect_sink_client = self.incoming.connect_protocol().map_err(|err| {
124 error!("Error connecting to inspect : {err}");
125 Status::INTERNAL
126 })?;
127
128 let options = inspect_publisher::PublishOptions { inspect_sink_client };
129 inspect_publisher::content_publisher(options).map_err(|err| {
130 error!("Error creating inspect content publisher: {err}");
131 Status::INTERNAL
132 })
133 }
134
135 pub fn node_name(&self) -> Option<&str> {
138 self.start_args.node_name.as_deref()
139 }
140
141 pub fn take_node_token(&mut self) -> Option<zx::Event> {
146 self.start_args.node_token.take()
147 }
148
149 pub fn take_power_element_token(&mut self) -> Option<zx::Event> {
155 self.start_args.power_element_args.as_mut()?.token.take()
156 }
157
158 pub fn node_properties(&self) -> impl Iterator<Item = ParentProperty<'_>> + '_ {
162 self.start_args.node_properties_2.iter().flatten().flat_map(|entry| {
163 entry.properties.iter().map(move |property| ParentProperty {
164 parent: &entry.name,
165 key: &property.key,
166 value: PropertyValue::from(&property.value),
167 })
168 })
169 }
170
171 pub fn vmar(&self) -> zx::Unowned<'_, zx::Vmar> {
176 if let Some(vmar) = self.start_args.vmar.as_ref().map(zx::Unowned::new) {
179 vmar
180 } else {
181 fuchsia_runtime::vmar_root_self()
182 }
183 }
184
185 pub(crate) fn new(
186 root_dispatcher: AsyncDispatcher,
187 mut start_args: DriverStartArgs,
188 health_alert_reporter: HealthAlertReporter,
189 ) -> Result<Self, Status> {
190 let mut incoming_namespace = Namespace::new();
191 for entry in start_args.incoming.take().unwrap_or_default() {
192 let path = entry
193 .path
194 .ok_or(Status::INVALID_ARGS)?
195 .parse()
196 .map_err(|_| Status::INVALID_ARGS)?;
197 let directory = into_legacy_client_end(entry.directory.ok_or(Status::INVALID_ARGS)?);
198 incoming_namespace.add(&path, directory).map_err(|_| Status::INVALID_ARGS)?;
199 }
200 let incoming = Incoming::from(incoming_namespace);
201 Ok(DriverContext { root_dispatcher, start_args, incoming, health_alert_reporter })
202 }
203
204 pub(crate) fn start_logging(&self, driver_name: &str) -> Result<(), Status> {
205 let log_client = match self.incoming.connect_protocol() {
206 Ok(log_client) => log_client,
207 Err(err) => {
208 eprintln!(
209 "Error connecting to log sink proxy at driver startup: {err}. Continuing without logging."
210 );
211 return Ok(());
212 }
213 };
214
215 if let Err(e) = driver_diagnostics_log::initialize(
216 driver_diagnostics_log::PublishOptions::default()
217 .use_log_sink(log_client)
218 .tags(&["driver", driver_name]),
219 ) {
220 eprintln!("Error initializing logging at driver startup: {e}");
221 }
222 Ok(())
223 }
224}
225
226fn into_legacy_client_end<P, M: fidl::endpoints::ProtocolMarker>(
229 client_end: fidl_next::ClientEnd<P, zx::Channel>,
230) -> fidl::endpoints::ClientEnd<M> {
231 fidl::endpoints::ClientEnd::new(client_end.into_untyped())
232}
233
234fn into_legacy_server_end<P, M: fidl::endpoints::ProtocolMarker>(
238 server_end: fidl_next::ServerEnd<P, zx::Channel>,
239) -> fidl::endpoints::ServerEnd<M> {
240 fidl::endpoints::ServerEnd::new(server_end.into_untyped())
241}