Skip to main content

driver_manager_devfs/
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 crate::builtin_devices::BuiltinDevVnode;
6use crate::class_names::{
7    CLASS_NAME_TO_SERVICE, CLASSES_THAT_ALLOW_TOPOLOGICAL_PATH, CLASSES_THAT_ASSUME_ORDERING, State,
8};
9use driver_manager_types::StartRequestReceiver;
10use fidl::endpoints::ServerEnd;
11use fidl_fuchsia_device as fdevice;
12use fidl_fuchsia_device_fs as fdevfs;
13use fidl_fuchsia_io as fio;
14use fuchsia_async as fasync;
15use fuchsia_sync::Mutex;
16use futures::StreamExt;
17use futures::channel::mpsc;
18use log::{debug, error, warn};
19use rand::{Rng, SeedableRng};
20use std::collections::HashMap;
21use std::sync::{Arc, Weak};
22use vfs::directory::entry::{DirectoryEntry, EntryInfo, GetEntryInfo, OpenRequest};
23use vfs::directory::entry_container::Directory;
24use vfs::directory::helper::DirectlyMutable;
25use vfs::directory::simple::Simple;
26use vfs::execution_scope::ExecutionScope;
27use vfs::path::Path;
28use vfs::remote::RemoteLike;
29use vfs::service::endpoint;
30use vfs::{ObjectRequestRef, pseudo_directory};
31
32mod builtin_devices;
33mod class_names;
34
35pub enum ConnectorMsg {
36    Controller(ServerEnd<fdevice::ControllerMarker>),
37    Protocol(zx::Channel),
38}
39
40pub type Connector = mpsc::UnboundedSender<ConnectorMsg>;
41
42pub enum OutgoingDirectoryMsg {
43    Connect(ServerEnd<fio::DirectoryMarker>),
44    AddServiceInstance(String, String, fio::DirectoryProxy),
45}
46
47pub type OutgoingDirectory = mpsc::UnboundedSender<OutgoingDirectoryMsg>;
48
49struct PathServer {
50    path: String,
51    scope: fasync::ScopeHandle,
52}
53
54impl PathServer {
55    fn new(path: String, scope: fasync::ScopeHandle) -> Self {
56        Self { path, scope }
57    }
58
59    fn serve(&self, channel: zx::Channel, class_name: &str) {
60        if !CLASSES_THAT_ALLOW_TOPOLOGICAL_PATH.contains(class_name) {
61            error!(
62                "Access to the topological path channel is not permitted for class {}",
63                class_name
64            );
65            return;
66        }
67        let mut stream = ServerEnd::<fdevfs::TopologicalPathMarker>::new(channel).into_stream();
68        let path = self.path.clone();
69        self.scope.spawn_local(async move {
70            while let Some(Ok(msg)) = stream.next().await {
71                match msg {
72                    fdevfs::TopologicalPathRequest::GetTopologicalPath { responder } => {
73                        let _ = responder.send(Ok(&path));
74                    }
75                }
76            }
77        });
78    }
79}
80
81struct ServiceInfo {
82    parent_dir: Arc<Simple>,
83    member_name: String,
84}
85
86pub struct Devnode {
87    devfs: Weak<Devfs>,
88    parent: Weak<Simple>,
89    vnode: Arc<DevnodeVnode>,
90    name: String,
91    path_server: PathServer,
92    children: Arc<Simple>,
93    service_info: Mutex<Option<ServiceInfo>>,
94    #[allow(unused)]
95    scope: fasync::Scope,
96}
97
98impl Drop for Devnode {
99    fn drop(&mut self) {
100        let mut service_info = self.service_info.lock();
101        if let Some(info) = service_info.take() {
102            // The second argument to remove_entry is whether the entry is a directory.
103            // We are removing a service, which is not a directory.
104            let _ = info.parent_dir.remove_entry(&info.member_name, false);
105            let _ = info.parent_dir.remove_entry(fdevfs::DEVICE_TOPOLOGY_NAME, false);
106        }
107        if let Some(parent_dir) = self.parent.upgrade() {
108            let _ = parent_dir.remove_entry(self.name.clone(), false);
109        }
110    }
111}
112
113impl Devnode {
114    fn new(
115        devfs: Weak<Devfs>,
116        parent: Weak<Simple>,
117        connector: Connector,
118        name: String,
119        path: &str,
120        class_name: &str,
121    ) -> Arc<Self> {
122        let scope = fasync::Scope::new_with_name("path_server");
123
124        let this = Arc::new_cyclic(|weak_self: &Weak<Devnode>| {
125            let children = Simple::new();
126            let vnode =
127                Arc::new(DevnodeVnode { connector: Some(connector), children: children.clone() });
128
129            let this = Self {
130                devfs,
131                parent,
132                vnode,
133                name,
134                path_server: PathServer::new(path.to_string(), scope.as_handle().clone()),
135                children,
136                service_info: Mutex::new(None),
137                scope,
138            };
139
140            let weak_self2 = weak_self.clone();
141            this.children
142                .add_entry(
143                    fdevfs::DEVICE_CONTROLLER_NAME,
144                    endpoint(move |_, channel| {
145                        if let Some(this) = weak_self2.upgrade() {
146                            let _ = this.vnode.connector.as_ref().unwrap().unbounded_send(
147                                ConnectorMsg::Controller(channel.into_zx_channel().into()),
148                            );
149                        }
150                    }),
151                )
152                .unwrap();
153
154            let weak_self2 = weak_self.clone();
155            this.children
156                .add_entry(
157                    fdevfs::DEVICE_PROTOCOL_NAME,
158                    endpoint(move |_, channel| {
159                        if let Some(this) = weak_self2.upgrade() {
160                            let _ = this
161                                .vnode
162                                .connector
163                                .as_ref()
164                                .unwrap()
165                                .unbounded_send(ConnectorMsg::Protocol(channel.into()));
166                        }
167                    }),
168                )
169                .unwrap();
170
171            let class_name_clone = class_name.to_string();
172            let weak_self = weak_self.clone();
173            this.children
174                .add_entry(
175                    fdevfs::DEVICE_TOPOLOGY_NAME,
176                    endpoint(move |_, channel| {
177                        if let Some(this) = weak_self.upgrade() {
178                            this.path_server.serve(channel.into(), &class_name_clone);
179                        }
180                    }),
181                )
182                .unwrap();
183
184            this
185        });
186
187        if let Some(parent) = this.parent.upgrade() {
188            parent.add_entry(this.name.clone(), this.vnode.clone()).unwrap();
189        }
190        this
191    }
192
193    fn try_add_service(
194        self: &Arc<Self>,
195        class_name: &str,
196        connector: Connector,
197        instance_name: &str,
198    ) -> Result<(), zx::Status> {
199        let service_entry = if let Some(entry) = CLASS_NAME_TO_SERVICE.get(class_name) {
200            entry
201        } else {
202            return Ok(());
203        };
204
205        let devfs = self.devfs.upgrade().ok_or(zx::Status::BAD_STATE)?;
206
207        let instance_dir = Simple::new();
208        let handler = endpoint(move |_, channel| {
209            let _ = connector.unbounded_send(ConnectorMsg::Protocol(channel.into()));
210        });
211
212        let full_path = format!(
213            "svc/{}/{}/{}",
214            service_entry.service_name, instance_name, service_entry.member_name
215        );
216        if let Err(e) = instance_dir.add_entry(service_entry.member_name, handler) {
217            warn!("Failed to add service entry '{}' for class '{}': {}", full_path, class_name, e);
218            return Err(e);
219        }
220        debug!("Added service entry '{}' for class '{}'", full_path, class_name);
221
222        *self.service_info.lock() = Some(ServiceInfo {
223            parent_dir: instance_dir.clone(),
224            member_name: service_entry.member_name.to_string(),
225        });
226
227        // Add topological path service
228        let weak_self = Arc::downgrade(self);
229        let class_name_clone = class_name.to_string();
230        let topo_handler = endpoint(move |_, channel| {
231            if let Some(this) = weak_self.upgrade() {
232                this.path_server.serve(channel.into(), &class_name_clone);
233            }
234        });
235        let topo_full_path = format!("{}/{}", instance_name, fdevfs::DEVICE_TOPOLOGY_NAME);
236        if let Err(e) = instance_dir.add_entry(fdevfs::DEVICE_TOPOLOGY_NAME, topo_handler) {
237            warn!(
238                "Failed to add topological path service entry '{}' for class '{}': {}",
239                topo_full_path, class_name, e
240            );
241            let _ = instance_dir.remove_entry(service_entry.member_name, false);
242            return Err(e);
243        }
244
245        let instance_dir = vfs::directory::serve(
246            instance_dir,
247            ExecutionScope::new(),
248            fio::PERM_READABLE | fio::PERM_WRITABLE,
249        );
250
251        if let Err(e) = devfs.outgoing.unbounded_send(OutgoingDirectoryMsg::AddServiceInstance(
252            service_entry.service_name.to_string(),
253            instance_name.to_string(),
254            instance_dir,
255        )) {
256            warn!(
257                "Failed to add instance to outgoing directory '{}' for class '{}': {}",
258                topo_full_path, class_name, e
259            );
260            return Err(zx::Status::BAD_STATE);
261        }
262
263        Ok(())
264    }
265
266    fn add_child(
267        self: &Arc<Self>,
268        name: &str,
269        class_name: Option<&str>,
270        connector: Connector,
271    ) -> Result<DevfsDevice, zx::Status> {
272        if self.children.get_entry(name).is_ok() {
273            warn!("rejecting duplicate device name '{}'", name);
274            return Err(zx::Status::ALREADY_EXISTS);
275        }
276
277        let mut child = DevfsDevice::new();
278        let child_path = format!("{}/{}", self.path_server.path, name);
279        if let Some(class_name) = class_name
280            && let Some(service_entry) = CLASS_NAME_TO_SERVICE.get(class_name)
281        {
282            let instance_name = self.devfs.upgrade().unwrap().make_instance_name(class_name)?;
283            if matches!(service_entry.state, State::Devfs | State::DevfsAndService) {
284                let devfs = self.devfs.upgrade().unwrap();
285                let class_dir = devfs.class_dirs.get(class_name).unwrap();
286                child.protocol = Some(Devnode::new(
287                    self.devfs.clone(),
288                    Arc::downgrade(class_dir),
289                    connector.clone(),
290                    instance_name.clone(),
291                    &child_path,
292                    class_name,
293                ));
294            }
295
296            if matches!(service_entry.state, State::DevfsAndService) {
297                let protocol_node = child.protocol.as_ref().unwrap().clone();
298                protocol_node.try_add_service(class_name, connector.clone(), &instance_name)?;
299            }
300        }
301
302        child.topological = Some(TopologicalDevnode::Devnode(Devnode::new(
303            self.devfs.clone(),
304            Arc::downgrade(&self.children),
305            connector,
306            name.to_string(),
307            &child_path,
308            class_name.unwrap_or(""),
309        )));
310
311        Ok(child)
312    }
313}
314
315struct DevnodeVnode {
316    connector: Option<Connector>,
317    children: Arc<Simple>,
318}
319
320impl RemoteLike for DevnodeVnode {
321    fn open(
322        self: Arc<DevnodeVnode>,
323        scope: ExecutionScope,
324        path: Path,
325        flags: fio::Flags,
326        object_request: ObjectRequestRef<'_>,
327    ) -> Result<(), zx::Status> {
328        if flags.contains(fio::Flags::PROTOCOL_DIRECTORY)
329            || flags.contains(fio::Flags::PROTOCOL_NODE)
330            || !path.is_empty()
331        {
332            self.children.clone().open(scope, path, flags, object_request)
333        } else if let Some(ref connector) = self.connector
334            && flags.contains(fio::Flags::PROTOCOL_SERVICE)
335        {
336            // This is a connection to the device itself.
337            let _ = connector
338                .unbounded_send(ConnectorMsg::Protocol(object_request.take().into_channel()));
339            Ok(())
340        } else {
341            Err(zx::Status::NOT_FOUND)
342        }
343    }
344}
345
346impl DirectoryEntry for DevnodeVnode {
347    fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), zx::Status> {
348        request.open_remote(self)
349    }
350}
351
352impl GetEntryInfo for DevnodeVnode {
353    fn entry_info(&self) -> EntryInfo {
354        EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)
355    }
356}
357
358#[derive(Clone)]
359pub struct RootDevnode {
360    children: Arc<Simple>,
361    devfs: Weak<Devfs>,
362}
363
364impl RootDevnode {
365    fn add_child(
366        &self,
367        name: &str,
368        class_name: Option<&str>,
369        connector: Connector,
370    ) -> Result<DevfsDevice, zx::Status> {
371        let devfs = self.devfs.upgrade().unwrap();
372
373        let mut child = DevfsDevice::new();
374        let child_path = format!("/{}", name);
375        if let Some(class_name) = class_name
376            && let Some(service_entry) = CLASS_NAME_TO_SERVICE.get(class_name)
377        {
378            let instance_name = devfs.make_instance_name(class_name)?;
379            if matches!(service_entry.state, State::Devfs | State::DevfsAndService) {
380                let class_dir = devfs.class_dirs.get(class_name).unwrap();
381                child.protocol = Some(Devnode::new(
382                    self.devfs.clone(),
383                    Arc::downgrade(class_dir),
384                    connector.clone(),
385                    instance_name.clone(),
386                    &child_path,
387                    class_name,
388                ));
389            }
390
391            if matches!(service_entry.state, State::DevfsAndService) {
392                let protocol_node = child.protocol.as_ref().unwrap().clone();
393                protocol_node.try_add_service(class_name, connector.clone(), &instance_name)?;
394            }
395        }
396
397        child.topological = Some(TopologicalDevnode::Devnode(Devnode::new(
398            self.devfs.clone(),
399            Arc::downgrade(&self.children),
400            connector,
401            name.to_string(),
402            &child_path,
403            class_name.unwrap_or(""),
404        )));
405
406        Ok(child)
407    }
408}
409
410#[derive(Clone)]
411pub enum TopologicalDevnode {
412    Root(RootDevnode),
413    Devnode(Arc<Devnode>),
414}
415
416impl TopologicalDevnode {
417    pub fn add_child(
418        &self,
419        name: &str,
420        class_name: Option<&str>,
421        connector: Connector,
422    ) -> Result<DevfsDevice, zx::Status> {
423        match self {
424            Self::Root(devnode) => devnode.add_child(name, class_name, connector),
425            Self::Devnode(devnode) => devnode.add_child(name, class_name, connector),
426        }
427    }
428}
429
430#[derive(Clone)]
431pub struct DevfsDevice {
432    pub topological: Option<TopologicalDevnode>,
433    pub protocol: Option<Arc<Devnode>>,
434}
435
436impl DevfsDevice {
437    pub fn new() -> Self {
438        Self { topological: None, protocol: None }
439    }
440}
441
442impl Default for DevfsDevice {
443    fn default() -> Self {
444        Self::new()
445    }
446}
447
448pub struct Devfs {
449    root: Arc<Simple>,
450    class_dirs: HashMap<String, Arc<Simple>>,
451    device_number_generator: Mutex<rand::rngs::StdRng>,
452    classes_that_assume_ordering: Mutex<HashMap<String, u32>>,
453    outgoing: OutgoingDirectory,
454    component_controller_proxy: Mutex<Option<fidl_fuchsia_component::ControllerProxy>>,
455}
456
457impl Devfs {
458    pub fn new(outgoing: OutgoingDirectory) -> Arc<Self> {
459        let class = Simple::new();
460        let root = pseudo_directory!(
461            "class" => class.clone(),
462            "null" => BuiltinDevVnode::new(true),
463            "zero" => BuiltinDevVnode::new(false),
464            "builtin" => pseudo_directory!(
465                "null" => BuiltinDevVnode::new(true),
466                "zero" => BuiltinDevVnode::new(false),
467            ),
468        );
469
470        let mut class_dirs = HashMap::new();
471        for (class_name, _) in &CLASS_NAME_TO_SERVICE {
472            let dir = Simple::new();
473            class.add_entry(*class_name, dir.clone()).unwrap();
474            class_dirs.insert(class_name.to_string(), dir);
475        }
476
477        let mut ordering = HashMap::new();
478        for class_name in CLASSES_THAT_ASSUME_ORDERING.iter() {
479            ordering.insert(class_name.to_string(), 0);
480        }
481
482        Arc::new(Self {
483            root,
484            class_dirs,
485            device_number_generator: Mutex::new(rand::rngs::StdRng::from_seed(Default::default())),
486            classes_that_assume_ordering: Mutex::new(ordering),
487            outgoing,
488            component_controller_proxy: Mutex::new(None),
489        })
490    }
491
492    pub fn root_node(self: &Arc<Self>) -> TopologicalDevnode {
493        TopologicalDevnode::Root(RootDevnode {
494            children: self.root.clone(),
495            devfs: Arc::downgrade(self),
496        })
497    }
498
499    pub fn serve(&self) -> fio::DirectoryProxy {
500        vfs::directory::serve(
501            self.root.clone(),
502            ExecutionScope::new(),
503            fio::PERM_READABLE | fio::PERM_WRITABLE,
504        )
505    }
506
507    pub fn set_component_controller_proxy(
508        &self,
509        controller: fidl_fuchsia_component::ControllerProxy,
510    ) {
511        let mut proxy = self.component_controller_proxy.lock();
512        *proxy = Some(controller);
513    }
514
515    pub async fn send_start_request(
516        &self,
517        handle: fidl_fuchsia_process::HandleInfo,
518    ) -> Result<(), zx::Status> {
519        let start_child_args = fidl_fuchsia_component::StartChildArgs {
520            numbered_handles: Some(vec![handle]),
521            ..Default::default()
522        };
523        let (_, server_end) = fidl::endpoints::create_endpoints();
524
525        let proxy = self.component_controller_proxy.lock().clone().ok_or_else(|| {
526            error!("no component controller proxy");
527            zx::Status::INTERNAL
528        })?;
529
530        proxy
531            .start(start_child_args, server_end)
532            .await
533            .map_err(|e| {
534                error!("Failed to start driver for devfs: {}", e);
535                zx::Status::INTERNAL
536            })?
537            .map_err(|e| {
538                error!("Failed to start driver for devfs: {:?}", e);
539                zx::Status::INTERNAL
540            })?;
541        Ok(())
542    }
543
544    pub async fn attach_component(
545        &self,
546        handle: fidl_fuchsia_process::HandleInfo,
547        mut receiver: StartRequestReceiver,
548    ) -> Result<(), zx::Status> {
549        self.send_start_request(handle).await?;
550        let start_request = receiver.next().await.ok_or(zx::Status::TIMED_OUT)??;
551        let start_info = start_request.info;
552        let controller = start_request.controller;
553
554        if let Some(outgoing_dir) = start_info.outgoing_dir {
555            let _ = self.outgoing.unbounded_send(OutgoingDirectoryMsg::Connect(outgoing_dir));
556        } else {
557            warn!("No outgoing dir available for devfs component.");
558        }
559        fasync::Task::local(async move {
560            let (mut stream, handle) = controller.into_stream_and_control_handle();
561            if let Some(Ok(_)) = stream.next().await {
562                let _ = handle
563                    .send_on_stop(fidl_fuchsia_component_runner::ComponentStopInfo {
564                        ..Default::default()
565                    })
566                    .inspect_err(|e| {
567                        error!("Failed to stop driver for devfs: {}", e);
568                    });
569            }
570        })
571        .detach();
572        Ok(())
573    }
574
575    pub fn make_instance_name(&self, class_name: &str) -> Result<String, zx::Status> {
576        if !CLASS_NAME_TO_SERVICE.contains_key(class_name) {
577            return Err(zx::Status::NOT_FOUND);
578        }
579        if let Some(next_id) = self.classes_that_assume_ordering.lock().get_mut(class_name) {
580            let id = *next_id;
581            *next_id += 1;
582            Ok(format!("{:03}", id))
583        } else {
584            let mut rng = self.device_number_generator.lock();
585            Ok(format!("{}", rng.random::<u32>()))
586        }
587    }
588}