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 fuchsia_sync::Mutex;
12use futures::StreamExt;
13use futures::channel::mpsc;
14use log::{debug, error, warn};
15use rand::{Rng, SeedableRng};
16use std::collections::HashMap;
17use std::sync::{Arc, Weak};
18use vfs::directory::entry::{DirectoryEntry, EntryInfo, GetEntryInfo, OpenRequest};
19use vfs::directory::entry_container::Directory;
20use vfs::directory::helper::DirectlyMutable;
21use vfs::directory::simple::Simple;
22use vfs::execution_scope::ExecutionScope;
23use vfs::path::Path;
24use vfs::remote::RemoteLike;
25use vfs::service::endpoint;
26use vfs::{ObjectRequestRef, pseudo_directory};
27use {
28    fidl_fuchsia_device as fdevice, fidl_fuchsia_device_fs as fdevfs, fidl_fuchsia_io as fio,
29    fuchsia_async as fasync,
30};
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 =
246            vfs::directory::serve(instance_dir, fio::PERM_READABLE | fio::PERM_WRITABLE);
247
248        if let Err(e) = devfs.outgoing.unbounded_send(OutgoingDirectoryMsg::AddServiceInstance(
249            service_entry.service_name.to_string(),
250            instance_name.to_string(),
251            instance_dir,
252        )) {
253            warn!(
254                "Failed to add instance to outgoing directory '{}' for class '{}': {}",
255                topo_full_path, class_name, e
256            );
257            return Err(zx::Status::BAD_STATE);
258        }
259
260        Ok(())
261    }
262
263    fn add_child(
264        self: &Arc<Self>,
265        name: &str,
266        class_name: Option<&str>,
267        connector: Connector,
268    ) -> Result<DevfsDevice, zx::Status> {
269        if self.children.get_entry(name).is_ok() {
270            warn!("rejecting duplicate device name '{}'", name);
271            return Err(zx::Status::ALREADY_EXISTS);
272        }
273
274        let mut child = DevfsDevice::new();
275        let child_path = format!("{}/{}", self.path_server.path, name);
276        if let Some(class_name) = class_name
277            && let Some(service_entry) = CLASS_NAME_TO_SERVICE.get(class_name)
278        {
279            let instance_name = self.devfs.upgrade().unwrap().make_instance_name(class_name)?;
280            if matches!(service_entry.state, State::Devfs | State::DevfsAndService) {
281                let devfs = self.devfs.upgrade().unwrap();
282                let class_dir = devfs.class_dirs.get(class_name).unwrap();
283                child.protocol = Some(Devnode::new(
284                    self.devfs.clone(),
285                    Arc::downgrade(class_dir),
286                    connector.clone(),
287                    instance_name.clone(),
288                    &child_path,
289                    class_name,
290                ));
291            }
292
293            if matches!(service_entry.state, State::DevfsAndService) {
294                let protocol_node = child.protocol.as_ref().unwrap().clone();
295                protocol_node.try_add_service(class_name, connector.clone(), &instance_name)?;
296            }
297        }
298
299        child.topological = Some(TopologicalDevnode::Devnode(Devnode::new(
300            self.devfs.clone(),
301            Arc::downgrade(&self.children),
302            connector,
303            name.to_string(),
304            &child_path,
305            class_name.unwrap_or(""),
306        )));
307
308        Ok(child)
309    }
310}
311
312struct DevnodeVnode {
313    connector: Option<Connector>,
314    children: Arc<Simple>,
315}
316
317impl RemoteLike for DevnodeVnode {
318    fn open(
319        self: Arc<DevnodeVnode>,
320        scope: ExecutionScope,
321        path: Path,
322        flags: fio::Flags,
323        object_request: ObjectRequestRef<'_>,
324    ) -> Result<(), zx::Status> {
325        if flags.contains(fio::Flags::PROTOCOL_DIRECTORY)
326            || flags.contains(fio::Flags::PROTOCOL_NODE)
327            || !path.is_empty()
328        {
329            self.children.clone().open(scope, path, flags, object_request)
330        } else if let Some(ref connector) = self.connector
331            && flags.contains(fio::Flags::PROTOCOL_SERVICE)
332        {
333            // This is a connection to the device itself.
334            let _ = connector
335                .unbounded_send(ConnectorMsg::Protocol(object_request.take().into_channel()));
336            Ok(())
337        } else {
338            Err(zx::Status::NOT_FOUND)
339        }
340    }
341}
342
343impl DirectoryEntry for DevnodeVnode {
344    fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), zx::Status> {
345        request.open_remote(self)
346    }
347}
348
349impl GetEntryInfo for DevnodeVnode {
350    fn entry_info(&self) -> EntryInfo {
351        EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)
352    }
353}
354
355#[derive(Clone)]
356pub struct RootDevnode {
357    children: Arc<Simple>,
358    devfs: Weak<Devfs>,
359}
360
361impl RootDevnode {
362    fn add_child(
363        &self,
364        name: &str,
365        class_name: Option<&str>,
366        connector: Connector,
367    ) -> Result<DevfsDevice, zx::Status> {
368        let devfs = self.devfs.upgrade().unwrap();
369
370        let mut child = DevfsDevice::new();
371        let child_path = format!("/{}", name);
372        if let Some(class_name) = class_name
373            && let Some(service_entry) = CLASS_NAME_TO_SERVICE.get(class_name)
374        {
375            let instance_name = devfs.make_instance_name(class_name)?;
376            if matches!(service_entry.state, State::Devfs | State::DevfsAndService) {
377                let class_dir = devfs.class_dirs.get(class_name).unwrap();
378                child.protocol = Some(Devnode::new(
379                    self.devfs.clone(),
380                    Arc::downgrade(class_dir),
381                    connector.clone(),
382                    instance_name.clone(),
383                    &child_path,
384                    class_name,
385                ));
386            }
387
388            if matches!(service_entry.state, State::DevfsAndService) {
389                let protocol_node = child.protocol.as_ref().unwrap().clone();
390                protocol_node.try_add_service(class_name, connector.clone(), &instance_name)?;
391            }
392        }
393
394        child.topological = Some(TopologicalDevnode::Devnode(Devnode::new(
395            self.devfs.clone(),
396            Arc::downgrade(&self.children),
397            connector,
398            name.to_string(),
399            &child_path,
400            class_name.unwrap_or(""),
401        )));
402
403        Ok(child)
404    }
405}
406
407#[derive(Clone)]
408pub enum TopologicalDevnode {
409    Root(RootDevnode),
410    Devnode(Arc<Devnode>),
411}
412
413impl TopologicalDevnode {
414    pub fn add_child(
415        &self,
416        name: &str,
417        class_name: Option<&str>,
418        connector: Connector,
419    ) -> Result<DevfsDevice, zx::Status> {
420        match self {
421            Self::Root(devnode) => devnode.add_child(name, class_name, connector),
422            Self::Devnode(devnode) => devnode.add_child(name, class_name, connector),
423        }
424    }
425}
426
427#[derive(Clone)]
428pub struct DevfsDevice {
429    pub topological: Option<TopologicalDevnode>,
430    pub protocol: Option<Arc<Devnode>>,
431}
432
433impl DevfsDevice {
434    pub fn new() -> Self {
435        Self { topological: None, protocol: None }
436    }
437}
438
439impl Default for DevfsDevice {
440    fn default() -> Self {
441        Self::new()
442    }
443}
444
445pub struct Devfs {
446    root: Arc<Simple>,
447    class_dirs: HashMap<String, Arc<Simple>>,
448    device_number_generator: Mutex<rand::rngs::StdRng>,
449    classes_that_assume_ordering: Mutex<HashMap<String, u32>>,
450    outgoing: OutgoingDirectory,
451    component_controller_proxy: Mutex<Option<fidl_fuchsia_component::ControllerProxy>>,
452}
453
454impl Devfs {
455    pub fn new(outgoing: OutgoingDirectory) -> Arc<Self> {
456        let class = Simple::new();
457        let root = pseudo_directory!(
458            "class" => class.clone(),
459            "null" => BuiltinDevVnode::new(true),
460            "zero" => BuiltinDevVnode::new(false),
461            "builtin" => pseudo_directory!(
462                "null" => BuiltinDevVnode::new(true),
463                "zero" => BuiltinDevVnode::new(false),
464            ),
465        );
466
467        let mut class_dirs = HashMap::new();
468        for (class_name, _) in &CLASS_NAME_TO_SERVICE {
469            let dir = Simple::new();
470            class.add_entry(*class_name, dir.clone()).unwrap();
471            class_dirs.insert(class_name.to_string(), dir);
472        }
473
474        let mut ordering = HashMap::new();
475        for class_name in CLASSES_THAT_ASSUME_ORDERING.iter() {
476            ordering.insert(class_name.to_string(), 0);
477        }
478
479        Arc::new(Self {
480            root,
481            class_dirs,
482            device_number_generator: Mutex::new(rand::rngs::StdRng::from_seed(Default::default())),
483            classes_that_assume_ordering: Mutex::new(ordering),
484            outgoing,
485            component_controller_proxy: Mutex::new(None),
486        })
487    }
488
489    pub fn root_node(self: &Arc<Self>) -> TopologicalDevnode {
490        TopologicalDevnode::Root(RootDevnode {
491            children: self.root.clone(),
492            devfs: Arc::downgrade(self),
493        })
494    }
495
496    pub fn serve(&self) -> fio::DirectoryProxy {
497        vfs::directory::serve(self.root.clone(), fio::PERM_READABLE | fio::PERM_WRITABLE)
498    }
499
500    pub fn set_component_controller_proxy(
501        &self,
502        controller: fidl_fuchsia_component::ControllerProxy,
503    ) {
504        let mut proxy = self.component_controller_proxy.lock();
505        *proxy = Some(controller);
506    }
507
508    pub async fn send_start_request(
509        &self,
510        handle: fidl_fuchsia_process::HandleInfo,
511    ) -> Result<(), zx::Status> {
512        let start_child_args = fidl_fuchsia_component::StartChildArgs {
513            numbered_handles: Some(vec![handle]),
514            ..Default::default()
515        };
516        let (_, server_end) = fidl::endpoints::create_endpoints();
517
518        let proxy = self.component_controller_proxy.lock().clone().ok_or_else(|| {
519            error!("no component controller proxy");
520            zx::Status::INTERNAL
521        })?;
522
523        proxy
524            .start(start_child_args, server_end)
525            .await
526            .map_err(|e| {
527                error!("Failed to start driver for devfs: {}", e);
528                zx::Status::INTERNAL
529            })?
530            .map_err(|e| {
531                error!("Failed to start driver for devfs: {:?}", e);
532                zx::Status::INTERNAL
533            })?;
534        Ok(())
535    }
536
537    pub async fn attach_component(
538        &self,
539        handle: fidl_fuchsia_process::HandleInfo,
540        mut receiver: StartRequestReceiver,
541    ) -> Result<(), zx::Status> {
542        self.send_start_request(handle).await?;
543        let start_request = receiver.next().await.ok_or(zx::Status::TIMED_OUT)??;
544        let start_info = start_request.info;
545        let controller = start_request.controller;
546
547        if let Some(outgoing_dir) = start_info.outgoing_dir {
548            let _ = self.outgoing.unbounded_send(OutgoingDirectoryMsg::Connect(outgoing_dir));
549        } else {
550            warn!("No outgoing dir available for devfs component.");
551        }
552        fasync::Task::local(async move {
553            let (mut stream, handle) = controller.into_stream_and_control_handle();
554            if let Some(Ok(_)) = stream.next().await {
555                let _ = handle
556                    .send_on_stop(fidl_fuchsia_component_runner::ComponentStopInfo {
557                        ..Default::default()
558                    })
559                    .inspect_err(|e| {
560                        error!("Failed to stop driver for devfs: {}", e);
561                    });
562            }
563        })
564        .detach();
565        Ok(())
566    }
567
568    pub fn make_instance_name(&self, class_name: &str) -> Result<String, zx::Status> {
569        if !CLASS_NAME_TO_SERVICE.contains_key(class_name) {
570            return Err(zx::Status::NOT_FOUND);
571        }
572        if let Some(next_id) = self.classes_that_assume_ordering.lock().get_mut(class_name) {
573            let id = *next_id;
574            *next_id += 1;
575            Ok(format!("{:03}", id))
576        } else {
577            let mut rng = self.device_number_generator.lock();
578            Ok(format!("{}", rng.random::<u32>()))
579        }
580    }
581}