Skip to main content

starnix_core/device/
registry.rs

1// Copyright 2021 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::device::kobject::{
6    Bus, Class, Device, DeviceMetadata, DeviceSubsystem, UEventAction, UEventContext,
7};
8use crate::device::kobject_store::KObjectStore;
9use crate::fs::devtmpfs::{devtmpfs_create_device, devtmpfs_remove_path};
10use crate::fs::sysfs::build_device_directory;
11use crate::task::{CurrentTask, Kernel, register_delayed_release};
12use crate::vfs::pseudo::simple_directory::SimpleDirectoryMutator;
13use crate::vfs::pseudo::stub_empty_file::StubEmptyFile;
14use crate::vfs::{FileOps, FsStr, FsString, NamespaceNode};
15use starnix_lifecycle::{ObjectReleaser, ReleaserAction};
16use starnix_logging::{bug_ref, log_error};
17use starnix_sync::{
18    DeviceRegistryListenersLock, DeviceRegistryStateLevel, LockDepGuard, LockDepMutex,
19    MappedLockDepGuard,
20};
21use starnix_types::ownership::{Releasable, ReleaseGuard};
22use starnix_uapi::as_any::AsAny;
23use starnix_uapi::device_id::{DYN_MAJOR_RANGE, DeviceId, MISC_DYNANIC_MINOR_RANGE, MISC_MAJOR};
24use starnix_uapi::error;
25use starnix_uapi::errors::Errno;
26use starnix_uapi::file_mode::mode;
27use starnix_uapi::open_flags::OpenFlags;
28use std::collections::btree_map::{BTreeMap, Entry};
29use std::ops::{Deref, Range};
30use std::sync::Arc;
31
32use dyn_clone::{DynClone, clone_trait_object};
33
34const CHRDEV_MINOR_MAX: u32 = 256;
35const BLKDEV_MINOR_MAX: u32 = 2u32.pow(20);
36
37/// The mode or category of the device driver.
38#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
39pub enum DeviceMode {
40    /// This device is a character device.
41    Char,
42
43    /// This device is a block device.
44    Block,
45}
46
47impl DeviceMode {
48    /// The number of minor device numbers available for this device mode.
49    fn minor_count(&self) -> u32 {
50        match self {
51            Self::Char => CHRDEV_MINOR_MAX,
52            Self::Block => BLKDEV_MINOR_MAX,
53        }
54    }
55
56    /// The range of minor device numbers available for this device mode.
57    pub fn minor_range(&self) -> Range<u32> {
58        0..self.minor_count()
59    }
60}
61
62pub trait DeviceOps: DynClone + Send + Sync + AsAny + 'static {
63    /// Instantiate a FileOps for this device.
64    ///
65    /// This function is called when userspace opens a file with a `DeviceId`
66    /// assigned to this device.
67    fn open(
68        &self,
69        current_task: &CurrentTask,
70        devt: DeviceId,
71        node: &NamespaceNode,
72        flags: OpenFlags,
73    ) -> Result<Box<dyn FileOps>, Errno>;
74}
75
76clone_trait_object!(DeviceOps);
77
78/// Allows directly using a function or closure as an implementation of DeviceOps, avoiding having
79/// to write a zero-size struct and an impl for it.
80impl<F> DeviceOps for F
81where
82    F: Clone
83        + Send
84        + Sync
85        + Clone
86        + Fn(&CurrentTask, DeviceId, &NamespaceNode, OpenFlags) -> Result<Box<dyn FileOps>, Errno>
87        + 'static,
88{
89    fn open(
90        &self,
91        current_task: &CurrentTask,
92        id: DeviceId,
93        node: &NamespaceNode,
94        flags: OpenFlags,
95    ) -> Result<Box<dyn FileOps>, Errno> {
96        self(current_task, id, node, flags)
97    }
98}
99
100/// A simple `DeviceOps` function for any device that implements `FileOps + Default`.
101pub fn simple_device_ops<T: Default + FileOps + 'static>(
102    _current_task: &CurrentTask,
103    _id: DeviceId,
104    _node: &NamespaceNode,
105    _flags: OpenFlags,
106) -> Result<Box<dyn FileOps>, Errno> {
107    Ok(Box::new(T::default()))
108}
109
110/// Keys returned by the registration method for `DeviceListener`s that allows to unregister a
111/// listener.
112pub type DeviceListenerKey = u64;
113
114/// Listener for device uevents dispatched by [`DeviceRegistry`].
115///
116/// [`Self::on_device_event`] is called synchronously with the registry's listener lock held so
117/// that all listeners observe events in monotonic `seqnum` order. Implementations may query
118/// registered devices on [`DeviceRegistry`], but must not register or unregister listeners or
119/// dispatch further uevents while handling an event.
120pub trait DeviceListener: Send + Sync {
121    fn on_device_event(&self, action: UEventAction, device: Device, context: UEventContext);
122}
123
124/// Allows directly using a function or closure as an implementation of [`DeviceListener`].
125impl<F> DeviceListener for F
126where
127    F: Fn(UEventAction, Device, UEventContext) + Send + Sync,
128{
129    fn on_device_event(&self, action: UEventAction, device: Device, context: UEventContext) {
130        self(action, device, context);
131    }
132}
133
134pub struct DeviceOpsWrapper(Box<dyn DeviceOps>);
135impl ReleaserAction<DeviceOpsWrapper> for DeviceOpsWrapper {
136    fn release(device_ops: ReleaseGuard<DeviceOpsWrapper>) {
137        register_delayed_release(device_ops);
138    }
139}
140impl Deref for DeviceOpsWrapper {
141    type Target = dyn DeviceOps;
142    fn deref(&self) -> &Self::Target {
143        self.0.deref()
144    }
145}
146pub type DeviceReleaser = ObjectReleaser<DeviceOpsWrapper, DeviceOpsWrapper>;
147pub type DeviceHandle = Arc<DeviceReleaser>;
148impl Releasable for DeviceOpsWrapper {
149    type Context<'a> = &'a CurrentTask;
150
151    fn release<'a>(self, _context: &'a CurrentTask) {}
152}
153
154/// An entry in the `DeviceRegistry`.
155struct DeviceEntry {
156    /// The name of the device.
157    ///
158    /// This name is the same as the name of the KObject for the device.
159    name: FsString,
160
161    /// The ops used to open the device.
162    ops: DeviceHandle,
163}
164
165impl DeviceEntry {
166    fn new(name: FsString, ops: impl DeviceOps) -> Self {
167        Self { name, ops: Arc::new(DeviceOpsWrapper(Box::new(ops)).into()) }
168    }
169}
170
171/// The devices registered for a given `DeviceMode`.
172///
173/// Each `DeviceMode` has its own namespace of registered devices.
174#[derive(Default)]
175struct RegisteredDevices {
176    /// The major devices registered for this device mode.
177    ///
178    /// Typically the devices registered here will add and remove individual devices using the
179    /// `add_major_device` and `remove_device` functions on `DeviceRegistry`.
180    ///
181    /// A major device registration shadows any minor device registrations for the same major
182    /// device number. We might need to reconsider this choice in the future in order to make
183    /// the /proc/devices file correctly list major devices such as `misc`.
184    majors: BTreeMap<u32, DeviceEntry>,
185
186    /// Individually registered minor devices.
187    ///
188    /// These devices are added using the `add_device` family of functions on `DeviceRegistry`.
189    minors: BTreeMap<DeviceId, DeviceEntry>,
190}
191
192impl RegisteredDevices {
193    /// Register a major device.
194    ///
195    /// Returns `EINVAL` if the major device is already registered.
196    fn register_major(&mut self, major: u32, entry: DeviceEntry) -> Result<(), Errno> {
197        if let Entry::Vacant(slot) = self.majors.entry(major) {
198            slot.insert(entry);
199            Ok(())
200        } else {
201            error!(EINVAL)
202        }
203    }
204
205    /// Register a minor device.
206    ///
207    /// Overwrites any existing minor device registered with the given `DeviceId`.
208    fn register_minor(&mut self, devt: DeviceId, entry: DeviceEntry) {
209        self.minors.insert(devt, entry);
210    }
211
212    /// Unregister a minor device.
213    fn unregister_minor(&mut self, devt: DeviceId) {
214        self.minors.remove(&devt);
215    }
216
217    /// Get the ops for a given `DeviceId`.
218    ///
219    /// If there is a major device registered with the major device number of the
220    /// `DeviceId`, the ops for that major device will be returned. Otherwise,
221    /// if there is a minor device registered, the ops for that minor device will be
222    /// returned. Otherwise, returns `ENODEV`.
223    fn get(&self, devt: DeviceId) -> Result<DeviceHandle, Errno> {
224        if let Some(major_device) = self.majors.get(&devt.major()) {
225            Ok(Arc::clone(&major_device.ops))
226        } else if let Some(minor_device) = self.minors.get(&devt) {
227            Ok(Arc::clone(&minor_device.ops))
228        } else {
229            error!(ENODEV)
230        }
231    }
232
233    /// Returns a list of the registered major device numbers and their names.
234    fn list_major_devices(&self) -> Vec<(u32, FsString)> {
235        self.majors.iter().map(|(major, entry)| (*major, entry.name.clone())).collect()
236    }
237
238    /// Returns a list of the registered minor devices and their names.
239    fn list_minor_devices(&self, range: Range<DeviceId>) -> Vec<(DeviceId, FsString)> {
240        self.minors.range(range).map(|(devt, entry)| (devt.clone(), entry.name.clone())).collect()
241    }
242}
243
244/// The registry for devices.
245///
246/// Devices are specified in file systems with major and minor device numbers, together referred to
247/// as a `DeviceId`. When userspace opens one of those files, we look up the `DeviceId` in the
248/// device registry to instantiate a file for that device.
249///
250/// The `DeviceRegistry` also manages the `KObjectStore`, which provides metadata for devices via
251/// the sysfs file system, typically mounted at /sys.
252pub struct DeviceRegistry {
253    /// The KObjects for registered devices.
254    pub objects: KObjectStore,
255
256    /// Mutable state for the device registry's major/minor device tables and allocators.
257    state: LockDepMutex<DeviceRegistryState, DeviceRegistryStateLevel>,
258
259    /// Listeners notified when device uevents are dispatched by the registry.
260    listeners: LockDepMutex<DeviceRegistryListeners, DeviceRegistryListenersLock>,
261}
262struct DeviceRegistryState {
263    /// The registered character devices.
264    char_devices: RegisteredDevices,
265
266    /// The registered block devices.
267    block_devices: RegisteredDevices,
268
269    /// Some of the misc devices (devices with the `MISC_MAJOR` major number) are dynamically
270    /// allocated. This allocator keeps track of which device numbers have been allocated to
271    /// such devices.
272    misc_chardev_allocator: DeviceIdAllocator,
273
274    /// A range of large major device numbers are reserved for other dynamically allocated
275    /// devices. This allocator keeps track of which device numbers have been allocated to
276    /// such devices.
277    dyn_chardev_allocator: DeviceIdAllocator,
278
279    /// The next anonymous device number to assign to a file system.
280    next_anon_minor: u32,
281}
282
283#[derive(Default)]
284struct DeviceRegistryListeners {
285    /// Listeners notified when device uevents are dispatched by the registry.
286    listeners: BTreeMap<u64, Box<dyn DeviceListener>>,
287
288    /// The next identifier to use for a listener.
289    next_listener_id: u64,
290
291    /// The next event identifier to use when notifying listeners.
292    next_event_id: u64,
293}
294
295impl DeviceRegistry {
296    /// Adds a numbered minor device to the [`DeviceRegistry`].
297    ///
298    /// If you are adding a device that exists in other systems, please check the metadata
299    /// for that device in /sys and make sure you use the same properties when calling this
300    /// function because these values are visible to userspace.
301    ///
302    /// For example, a typical device will appear in sysfs at a path like:
303    ///
304    ///   `/sys/devices/{bus}/{class}/{name}`
305    ///
306    /// Many common classes have convenient accessors on `DeviceRegistry::objects`.
307    ///
308    /// To fill out the `DeviceMetadata`, look at the `uevent` file:
309    ///
310    ///   `/sys/devices/{bus}/{class}/{name}/uevent`
311    ///
312    /// which has the following format:
313    ///
314    /// ```
315    ///   MAJOR={major-number}
316    ///   MINOR={minor-number}
317    ///   DEVNAME={devname}
318    ///   DEVMODE={devmode}
319    /// ```
320    ///
321    /// Often, the `{name}` and the `{devname}` are the same, but if they are not the same,
322    /// please take care to use the correct string in the correct field.
323    ///
324    /// If the `{major-number}` is 10 and the `{minor-number}` is in the range 52..128, please use
325    /// [`Self::add_misc_device`] instead because these device numbers are dynamically allocated.
326    ///
327    /// If the `{major-number}` is in the range 234..255, please use [`Self::add_dyn_device`]
328    /// instead because these devices are also dynamically allocated.
329    ///
330    /// If you are unsure which device numbers to use, consult devices.txt:
331    ///
332    ///   https://www.kernel.org/doc/Documentation/admin-guide/devices.txt
333    ///
334    /// If you are still unsure, please ask an experienced Starnix contributor rather than make up
335    /// a device number.
336    ///
337    /// Uses [`build_device_directory`] to populate the standard `sysfs` directory for the device.
338    /// To populate custom `sysfs` attributes or specify a parent device, use
339    /// [`Self::add_device_with_dir`].
340    ///
341    /// Finally, the `dev_ops` parameter is where you provide the callback for instantiating
342    /// your device. The returned [`Device`] can be removed with [`Self::remove_device`].
343    pub fn add_device(
344        &self,
345        kernel: &Kernel,
346        name: &FsStr,
347        metadata: DeviceMetadata,
348        class: Class,
349        dev_ops: impl DeviceOps,
350    ) -> Device {
351        self.add_device_with_dir(
352            kernel,
353            name,
354            metadata,
355            /* parent = */ None,
356            class,
357            build_device_directory,
358            dev_ops,
359        )
360    }
361
362    /// Adds a numbered minor device to the [`DeviceRegistry`], with a `sysfs` directory populated
363    /// by `build_directory` beneath `parent`.
364    ///
365    /// If `parent` is itself a class device then the device directory is created directly within
366    /// the parent's directory, otherwise it is created within a `<class>` directory under
367    /// `parent`. If `parent` is `None` then `/sys/devices/virtual` is used.
368    ///
369    /// Dispatches [`UEventAction::Add`] after creating the `sysfs` and `devtmpfs` nodes. The
370    /// returned [`Device`] can be removed with [`Self::remove_device`].
371    ///
372    /// See [`Self::add_device`] for an explanation of the other parameters.
373    pub fn add_device_with_dir(
374        &self,
375        kernel: &Kernel,
376        name: &FsStr,
377        metadata: DeviceMetadata,
378        parent: Option<Device>,
379        subsystem: impl Into<DeviceSubsystem>,
380        build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
381        dev_ops: impl DeviceOps,
382    ) -> Device {
383        let entry = DeviceEntry::new(name.into(), dev_ops);
384        self.devices(metadata.mode).register_minor(metadata.devt, entry);
385        self.add_major_device(kernel, name, metadata, parent, subsystem, build_directory)
386    }
387
388    /// Allocates a dynamic device number in `MISC_MAJOR` and adds the device to the
389    /// [`DeviceRegistry`].
390    ///
391    /// MISC devices (major number 10) with minor numbers in the range 52..128 are dynamically
392    /// assigned. Rather than hardcoding registrations with these device numbers, use this
393    /// function instead to add the device.
394    ///
395    /// See [`Self::add_device`] for an explanation of the parameters.
396    pub fn add_misc_device(
397        &self,
398        kernel: &Kernel,
399        name: &FsStr,
400        dev_ops: impl DeviceOps,
401    ) -> Result<Device, Errno> {
402        let devt = self.state.lock().misc_chardev_allocator.allocate()?;
403        let metadata = DeviceMetadata::new(name.into(), devt, DeviceMode::Char);
404        Ok(self.add_device(kernel, name, metadata, self.objects.misc_class(), dev_ops))
405    }
406
407    /// Allocates a dynamic character device number (major 234..255) and adds the device to the
408    /// [`DeviceRegistry`].
409    ///
410    /// Major device numbers 234..255 are dynamically assigned. Rather than hardcoding
411    /// registrations with these device numbers, use this function instead to add the device.
412    ///
413    /// Note: We do not currently allocate from this entire range because we have mistakenly
414    /// hardcoded some device registrations from the dynamic range. Once we fix these registrations
415    /// to be dynamic, we should expand to using the full dynamic range.
416    ///
417    /// See [`Self::add_device`] for an explanation of the parameters.
418    pub fn add_dyn_device(
419        &self,
420        kernel: &Kernel,
421        name: &FsStr,
422        class: Class,
423        dev_ops: impl DeviceOps,
424    ) -> Result<Device, Errno> {
425        self.add_dyn_device_with_dir(kernel, name, class, build_device_directory, dev_ops)
426    }
427
428    /// Allocates a dynamic character device number (major 234..255) and adds the device to the
429    /// [`DeviceRegistry`], with a `sysfs` directory populated by `build_directory`.
430    ///
431    /// See [`Self::add_device`] for an explanation of the parameters.
432    pub fn add_dyn_device_with_dir(
433        &self,
434        kernel: &Kernel,
435        name: &FsStr,
436        class: Class,
437        build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
438        dev_ops: impl DeviceOps,
439    ) -> Result<Device, Errno> {
440        self.add_dyn_device_with_devname(kernel, name, name, class, build_directory, dev_ops)
441    }
442
443    /// Allocates a dynamic character device number (major 234..255) and adds the device to the
444    /// [`DeviceRegistry`] with a separate `sysfs` `name` and `/dev` `devname`, and a `sysfs`
445    /// directory populated by `build_directory`.
446    ///
447    /// See [`Self::add_dyn_device`] for an explanation of dynamic devices and parameters.
448    pub fn add_dyn_device_with_devname(
449        &self,
450        kernel: &Kernel,
451        name: &FsStr,
452        devname: &FsStr,
453        class: Class,
454        build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
455        dev_ops: impl DeviceOps,
456    ) -> Result<Device, Errno> {
457        let devt = self.state.lock().dyn_chardev_allocator.allocate()?;
458        let metadata = DeviceMetadata::new(devname.into(), devt, DeviceMode::Char);
459        Ok(self.add_device_with_dir(
460            kernel,
461            name,
462            metadata,
463            /* parent = */ None,
464            class,
465            build_directory,
466            dev_ops,
467        ))
468    }
469
470    /// Registers a "silent" dynamic device with major numbers 234..255.
471    ///
472    /// Only use for devices that should not be added to the [`KObjectStore`] or appear in `sysfs`.
473    /// This is a rare occurrence.
474    ///
475    /// See [`Self::add_dyn_device`] for an explanation of dynamic devices and parameters.
476    pub fn register_silent_dyn_device(
477        &self,
478        name: &FsStr,
479        dev_ops: impl DeviceOps,
480    ) -> Result<DeviceMetadata, Errno> {
481        let devt = self.state.lock().dyn_chardev_allocator.allocate()?;
482        let metadata = DeviceMetadata::new(name.into(), devt, DeviceMode::Char);
483        let entry = DeviceEntry::new(name.into(), dev_ops);
484        self.devices(metadata.mode).register_minor(metadata.devt, entry);
485        Ok(metadata)
486    }
487
488    /// Adds a numbered device whose major number was previously registered via
489    /// [`Self::register_major`], with a `sysfs` directory populated by `build_directory`.
490    ///
491    /// If you want to add a single minor device with its own [`DeviceOps`], use
492    /// [`Self::add_device`] or [`Self::add_device_with_dir`] instead.
493    ///
494    /// The returned [`Device`] can be removed with [`Self::remove_device`].
495    ///
496    /// See [`Self::add_device_with_dir`] for an explanation of the parameters.
497    pub fn add_major_device(
498        &self,
499        kernel: &Kernel,
500        name: &FsStr,
501        metadata: DeviceMetadata,
502        parent: Option<Device>,
503        subsystem: impl Into<DeviceSubsystem>,
504        build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
505    ) -> Device {
506        self.devices(metadata.mode).get(metadata.devt).expect("device is registered");
507        let device = self.objects.create_device(
508            name,
509            parent,
510            Some(subsystem.into()),
511            Some(metadata.clone()),
512            build_directory,
513        );
514
515        if kernel.features.devtmpfs {
516            if let Err(err) = devtmpfs_create_device(kernel, metadata) {
517                log_error!("Cannot create device {:?} in devtmpfs ({:?})", device, err);
518            }
519        }
520        self.dispatch_uevent(UEventAction::Add, device.clone());
521        device
522    }
523
524    /// Adds a network device to the device registry and dispatches [`UEventAction::Add`].
525    ///
526    /// Network devices do not have a `/dev` node or device number. Instead, in addition to
527    /// `DEVPATH` and `SUBSYSTEM=net`, their `uevent` files have the following properties:
528    ///
529    /// ```
530    /// INTERFACE={name}
531    /// IFINDEX={index}
532    /// ```
533    ///
534    /// TODO(https://fxbug.dev/297438880): Populate `INTERFACE` and `IFINDEX` in the `uevent` file
535    /// and uevent netlink messages. Currently, we only register net devices by name with the
536    /// standard `DEVPATH` and `SUBSYSTEM=net` properties, and stub the `address` attribute so that
537    /// reads by userspace are tracked.
538    ///
539    /// The returned [`Device`] can be removed with [`Self::remove_device`].
540    pub fn add_net_device(&self, name: &FsStr) -> Device {
541        self.add_numberless_device(
542            name,
543            /* parent = */ None,
544            self.objects.net_class(),
545            |device, dir| {
546                build_device_directory(device, dir);
547                dir.entry(
548                    "address",
549                    StubEmptyFile::new_node(bug_ref!("https://fxbug.dev/452096300")),
550                    mode!(IFREG, 0o444),
551                );
552            },
553        )
554    }
555
556    fn add_device_without_metadata(
557        &self,
558        name: &FsStr,
559        parent: Option<Device>,
560        subsystem: Option<DeviceSubsystem>,
561        build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
562    ) -> Device {
563        let device = self.objects.create_device(
564            name,
565            parent,
566            subsystem,
567            /* metadata = */ None,
568            build_directory,
569        );
570        self.dispatch_uevent(UEventAction::Add, device.clone());
571        device
572    }
573
574    /// Directly adds a device without a bus or class subsystem to the [`KObjectStore`], populated
575    /// by `build_directory`, and dispatches [`UEventAction::Add`].
576    ///
577    /// Such devices typically act as structural parents for other devices, e.g. the root of a
578    /// subsystem's devices (`/sys/devices/system/cpu`). If `parent` is `Some`, the device
579    /// directory is created under `parent`; otherwise it is created directly under
580    /// `/sys/devices/<name>`.
581    ///
582    /// The returned [`Device`] can be removed with [`Self::remove_device`].
583    pub fn add_subsystemless_device(
584        &self,
585        name: &FsStr,
586        parent: Option<Device>,
587        build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
588    ) -> Device {
589        self.add_device_without_metadata(name, parent, /* subsystem = */ None, build_directory)
590    }
591
592    /// Directly adds a bus device to the [`KObjectStore`], populated by `build_directory`, and
593    /// dispatches [`UEventAction::Add`].
594    ///
595    /// If `parent` is `Some`, the device directory is created under `parent`; otherwise it is
596    /// created directly under `/sys/devices/<name>`. A symlink is also created under
597    /// `/sys/bus/<bus.name>/devices/<name>`.
598    ///
599    /// The returned [`Device`] can be removed with [`Self::remove_device`].
600    pub fn add_bus_device(
601        &self,
602        name: &FsStr,
603        parent: Option<Device>,
604        bus: Bus,
605        build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
606    ) -> Device {
607        self.add_device_without_metadata(name, parent, Some(bus.into()), build_directory)
608    }
609
610    /// Directly adds a class device without a device number to the [`KObjectStore`], populated by
611    /// `build_directory`, and dispatches [`UEventAction::Add`].
612    ///
613    /// This function should be used by class devices that are exposed via `sysfs` or netlink and
614    /// do not have a `/dev` device node (and therefore have no major or minor device number).
615    ///
616    /// If `parent` is `Some` and is itself a class device, the device directory is created under
617    /// `<parent>/<name>`; otherwise it is created under `<parent>/<class.name>/<name>` (defaulting
618    /// `parent` to `/sys/devices/virtual`). A symlink is also created under
619    /// `/sys/class/<class.name>/<name>`.
620    ///
621    /// The returned [`Device`] can be removed with [`Self::remove_device`].
622    pub fn add_numberless_device(
623        &self,
624        name: &FsStr,
625        parent: Option<Device>,
626        class: Class,
627        build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
628    ) -> Device {
629        self.add_device_without_metadata(name, parent, Some(class.into()), build_directory)
630    }
631
632    /// Unregisters `device` from `devtmpfs`, `sysfs`, and the minor device table, recycling any
633    /// dynamically allocated [`DeviceId`] before dispatching [`UEventAction::Remove`].
634    ///
635    /// Any child devices parented under `device` must be removed before `device` itself so
636    /// removal uevents are dispatched in child-before-parent order and intermediate class
637    /// directories are cleaned up.
638    pub fn remove_device(&self, current_task: &CurrentTask, device: Device) {
639        if let Some(metadata) = device.metadata() {
640            if current_task.kernel().features.devtmpfs {
641                if let Err(err) = devtmpfs_remove_path(current_task, metadata.devname.as_ref()) {
642                    log_error!("Cannot remove device {:?} ({:?})", device, err);
643                }
644            }
645        }
646
647        self.objects.remove(&device);
648
649        if let Some(metadata) = device.metadata() {
650            let mut state = self.state.lock();
651            match metadata.mode {
652                DeviceMode::Char => {
653                    state.char_devices.unregister_minor(metadata.devt);
654                    state.misc_chardev_allocator.free(metadata.devt);
655                    state.dyn_chardev_allocator.free(metadata.devt);
656                }
657                DeviceMode::Block => {
658                    state.block_devices.unregister_minor(metadata.devt);
659                }
660            }
661        }
662
663        self.dispatch_uevent(UEventAction::Remove, device);
664    }
665
666    /// Returns a list of the registered major device numbers for the given `DeviceMode` and their
667    /// names.
668    pub fn list_major_devices(&self, mode: DeviceMode) -> Vec<(u32, FsString)> {
669        self.devices(mode).list_major_devices()
670    }
671
672    /// Returns a list of the registered minor devices for the given `DeviceMode` and their names.
673    pub fn list_minor_devices(
674        &self,
675        mode: DeviceMode,
676        range: Range<DeviceId>,
677    ) -> Vec<(DeviceId, FsString)> {
678        self.devices(mode).list_minor_devices(range)
679    }
680
681    /// The `RegisteredDevice` object for the given `DeviceMode`.
682    fn devices<'a>(&'a self, mode: DeviceMode) -> MappedLockDepGuard<'a, RegisteredDevices> {
683        LockDepGuard::map(self.state.lock(), |state| match mode {
684            DeviceMode::Char => &mut state.char_devices,
685            DeviceMode::Block => &mut state.block_devices,
686        })
687    }
688
689    /// Register an entire major device number.
690    ///
691    /// If you register an entire major device, use [`Self::add_major_device`] and
692    /// [`Self::remove_device`] to manage the `sysfs` and `devtmpfs` entries for each minor device
693    /// rather than registering individual minor devices.
694    pub fn register_major<'a>(
695        &self,
696        name: FsString,
697        mode: DeviceMode,
698        major: u32,
699        dev_ops: impl DeviceOps,
700    ) -> Result<(), Errno> {
701        let entry = DeviceEntry::new(name, dev_ops);
702        self.devices(mode).register_major(major, entry)
703    }
704
705    /// Allocate an anonymous device identifier.
706    pub fn next_anonymous_dev_id<'a>(&self) -> DeviceId {
707        let mut state = self.state.lock();
708        let id = DeviceId::new(0, state.next_anon_minor);
709        state.next_anon_minor += 1;
710        id
711    }
712
713    /// Registers a listener for device uevents and returns its [`DeviceListenerKey`].
714    pub fn register_listener<'a>(
715        &self,
716        listener: impl DeviceListener + 'static,
717    ) -> DeviceListenerKey {
718        let mut listeners = self.listeners.lock();
719        let key = listeners.next_listener_id;
720        listeners.next_listener_id += 1;
721        listeners.listeners.insert(key, Box::new(listener));
722        key
723    }
724
725    /// Unregisters a listener previously registered via [`Self::register_listener`].
726    pub fn unregister_listener<'a>(&self, key: &DeviceListenerKey) {
727        self.listeners.lock().listeners.remove(key);
728    }
729
730    /// Dispatches a uevent with the next monotonic sequence number for `device` to all registered
731    /// listeners.
732    pub fn dispatch_uevent<'a>(&self, action: UEventAction, device: Device) {
733        let mut listeners = self.listeners.lock();
734        let event_id = listeners.next_event_id;
735        listeners.next_event_id += 1;
736        let context = UEventContext { seqnum: event_id };
737        for listener in listeners.listeners.values() {
738            listener.on_device_event(action, device.clone(), context);
739        }
740    }
741
742    /// Instantiate a file for the specified device.
743    ///
744    /// The device will be looked up in the device registry by `DeviceMode` and `DeviceId`.
745    pub fn open_device(
746        &self,
747        current_task: &CurrentTask,
748        node: &NamespaceNode,
749        flags: OpenFlags,
750        devt: DeviceId,
751        mode: DeviceMode,
752    ) -> Result<Box<dyn FileOps>, Errno> {
753        let dev_ops = self.devices(mode).get(devt)?;
754        dev_ops.open(current_task, devt, node, flags)
755    }
756
757    pub fn get_device(&self, devt: DeviceId, mode: DeviceMode) -> Result<DeviceHandle, Errno> {
758        self.devices(mode).get(devt)
759    }
760}
761
762impl Default for DeviceRegistry {
763    fn default() -> Self {
764        let misc_available = vec![DeviceId::new_range(MISC_MAJOR, MISC_DYNANIC_MINOR_RANGE)];
765        let dyn_available = DYN_MAJOR_RANGE
766            .map(|major| DeviceId::new_range(major, DeviceMode::Char.minor_range()))
767            .rev()
768            .collect();
769        let state = DeviceRegistryState {
770            char_devices: Default::default(),
771            block_devices: Default::default(),
772            misc_chardev_allocator: DeviceIdAllocator::new(misc_available),
773            dyn_chardev_allocator: DeviceIdAllocator::new(dyn_available),
774            next_anon_minor: 1,
775        };
776        Self { objects: Default::default(), state: state.into(), listeners: Default::default() }
777    }
778}
779
780/// An allocator for `DeviceId`
781struct DeviceIdAllocator {
782    /// Total ranges of device identifiers managed by this allocator.
783    ranges: Vec<Range<DeviceId>>,
784
785    /// The available ranges of device types to allocate.
786    ///
787    /// Devices will be allocated from the back of the vector first.
788    freelist: Vec<Range<DeviceId>>,
789}
790
791impl DeviceIdAllocator {
792    /// Create an allocator for the given ranges of device types.
793    ///
794    /// The devices will be allocated from the front of the vector first.
795    fn new(mut available: Vec<Range<DeviceId>>) -> Self {
796        let ranges = available.clone();
797        available.reverse();
798        Self { ranges, freelist: available }
799    }
800
801    /// Allocate a `DeviceId`.
802    fn allocate(&mut self) -> Result<DeviceId, Errno> {
803        let Some(range) = self.freelist.pop() else {
804            return error!(ENOMEM);
805        };
806        let allocated = range.start;
807        if let Some(next) = allocated.next_minor() {
808            if next < range.end {
809                self.freelist.push(next..range.end);
810            }
811        }
812        Ok(allocated)
813    }
814
815    /// Returns a previously allocated number to the pool so it can be reused.
816    ///
817    /// Does nothing if `id` is outside the ranges managed by this allocator.
818    fn free(&mut self, id: DeviceId) {
819        if !self.ranges.iter().any(|range| range.contains(&id)) {
820            return;
821        }
822        let Some(next) = id.next_minor() else {
823            return;
824        };
825        if self.freelist.iter().any(|range| range.start <= id && id < range.end) {
826            return;
827        };
828        // Coalesce with any immediately adjacent free ranges to prevent freelist fragmentation
829        // across repeated device removals.
830        let mut new_range = id..next;
831        if let Some(idx) = self.freelist.iter().position(|range| range.end == new_range.start) {
832            let prev = self.freelist.remove(idx);
833            new_range.start = prev.start;
834        }
835        if let Some(idx) = self.freelist.iter().position(|range| range.start == new_range.end) {
836            self.freelist[idx].start = new_range.start;
837        } else {
838            self.freelist.push(new_range);
839        }
840    }
841}
842
843#[cfg(test)]
844mod tests {
845    use super::*;
846    use crate::device::mem::DevNull;
847    use crate::task::{DelayedReleaser, KernelFeatures};
848    use crate::testing::*;
849    use crate::vfs::*;
850    use starnix_uapi::device_id::{INPUT_MAJOR, MEM_MAJOR};
851
852    #[::fuchsia::test]
853    async fn registry_fails_to_add_duplicate_device() {
854        spawn_kernel_and_run(async |_current_task| {
855            let registry = DeviceRegistry::default();
856            registry
857                .register_major(
858                    "mem".into(),
859                    DeviceMode::Char,
860                    MEM_MAJOR,
861                    simple_device_ops::<DevNull>,
862                )
863                .expect("registers once");
864            registry
865                .register_major(
866                    "random".into(),
867                    DeviceMode::Char,
868                    123,
869                    simple_device_ops::<DevNull>,
870                )
871                .expect("registers unique");
872            registry
873                .register_major(
874                    "mem".into(),
875                    DeviceMode::Char,
876                    MEM_MAJOR,
877                    simple_device_ops::<DevNull>,
878                )
879                .expect_err("fail to register duplicate");
880        })
881        .await;
882    }
883
884    #[::fuchsia::test]
885    async fn registry_opens_device() {
886        spawn_kernel_and_run(async |current_task| {
887            let kernel = current_task.kernel();
888            let registry = DeviceRegistry::default();
889            registry
890                .register_major(
891                    "mem".into(),
892                    DeviceMode::Char,
893                    MEM_MAJOR,
894                    simple_device_ops::<DevNull>,
895                )
896                .expect("registers unique");
897
898            let fs = create_testfs(&kernel);
899            let node = create_namespace_node_for_testing(&fs, PanickingFsNode);
900
901            // Fail to open non-existent device.
902            assert!(
903                registry
904                    .open_device(
905                        &current_task,
906                        &node,
907                        OpenFlags::RDONLY,
908                        DeviceId::NONE,
909                        DeviceMode::Char
910                    )
911                    .is_err()
912            );
913
914            // Fail to open in wrong mode.
915            assert!(
916                registry
917                    .open_device(
918                        &current_task,
919                        &node,
920                        OpenFlags::RDONLY,
921                        DeviceId::NULL,
922                        DeviceMode::Block
923                    )
924                    .is_err()
925            );
926
927            // Open in correct mode.
928            let _ = registry
929                .open_device(
930                    &current_task,
931                    &node,
932                    OpenFlags::RDONLY,
933                    DeviceId::NULL,
934                    DeviceMode::Char,
935                )
936                .expect("opens device");
937        })
938        .await;
939    }
940
941    #[::fuchsia::test]
942    async fn registry_dynamic_misc() {
943        spawn_kernel_and_run(async |current_task| {
944            let kernel = current_task.kernel();
945            fn create_test_device(
946                _current_task: &CurrentTask,
947                _id: DeviceId,
948                _node: &NamespaceNode,
949                _flags: OpenFlags,
950            ) -> Result<Box<dyn FileOps>, Errno> {
951                Ok(Box::new(PanickingFile))
952            }
953
954            let registry = &kernel.device_registry;
955            let device = registry
956                .add_dyn_device(
957                    kernel,
958                    "test-device".into(),
959                    registry.objects.block_class(),
960                    create_test_device,
961                )
962                .unwrap();
963            let devt = device.metadata().expect("has metadata").devt;
964            assert!(DYN_MAJOR_RANGE.contains(&devt.major()));
965
966            let fs = create_testfs(&kernel);
967            let node = create_namespace_node_for_testing(&fs, PanickingFsNode);
968            let _ = registry
969                .open_device(&current_task, &node, OpenFlags::RDONLY, devt, DeviceMode::Char)
970                .expect("opens device");
971        })
972        .await;
973    }
974
975    #[::fuchsia::test]
976    async fn registery_add_class() {
977        spawn_kernel_and_run(async |current_task| {
978            let kernel = current_task.kernel();
979            let registry = &kernel.device_registry;
980            registry
981                .register_major(
982                    "input".into(),
983                    DeviceMode::Char,
984                    INPUT_MAJOR,
985                    simple_device_ops::<DevNull>,
986                )
987                .expect("can register input");
988
989            let input_class = registry.objects.get_or_create_class("input".into());
990            registry.add_major_device(
991                kernel,
992                "mouse".into(),
993                DeviceMetadata::new(
994                    "mouse".into(),
995                    DeviceId::new(INPUT_MAJOR, 0),
996                    DeviceMode::Char,
997                ),
998                /* parent = */ None,
999                input_class,
1000                build_device_directory,
1001            );
1002
1003            assert!(registry.objects.root.lookup("class/input/mouse".into()).is_some());
1004            assert!(registry.objects.root.lookup("devices/virtual/input/mouse".into()).is_some());
1005        })
1006        .await;
1007    }
1008
1009    #[::fuchsia::test]
1010    async fn registry_add_bus() {
1011        spawn_kernel_and_run(async |current_task| {
1012            let kernel = current_task.kernel();
1013            let registry = &kernel.device_registry;
1014
1015            let bus = registry.objects.get_or_create_bus("my-bus".into());
1016            let bus_dev = registry.add_bus_device(
1017                "my-device".into(),
1018                /* parent = */ None,
1019                bus.clone(),
1020                build_device_directory,
1021            );
1022            assert!(registry.objects.root.lookup("bus/my-bus/devices/my-device".into()).is_some());
1023            assert!(registry.objects.root.lookup("devices/my-device".into()).is_some());
1024
1025            let class = registry.objects.get_or_create_class("my-class".into());
1026            let class_dev = registry.add_numberless_device(
1027                "my-class-dev".into(),
1028                Some(bus_dev.clone()),
1029                class,
1030                build_device_directory,
1031            );
1032            assert!(registry.objects.root.lookup("class/my-class/my-class-dev".into()).is_some());
1033            assert!(
1034                registry
1035                    .objects
1036                    .root
1037                    .lookup("devices/my-device/my-class/my-class-dev".into())
1038                    .is_some()
1039            );
1040
1041            let sub_class = registry.objects.get_or_create_class("my-subclass".into());
1042            let child_class_dev = registry.add_numberless_device(
1043                "my-child-class-dev".into(),
1044                Some(class_dev.clone()),
1045                sub_class,
1046                build_device_directory,
1047            );
1048            assert!(
1049                registry
1050                    .objects
1051                    .root
1052                    .lookup("class/my-subclass/my-child-class-dev".into())
1053                    .is_some()
1054            );
1055            assert!(
1056                registry
1057                    .objects
1058                    .root
1059                    .lookup("devices/my-device/my-class/my-class-dev/my-child-class-dev".into())
1060                    .is_some()
1061            );
1062
1063            registry.remove_device(&current_task, child_class_dev);
1064            assert!(
1065                registry
1066                    .objects
1067                    .root
1068                    .lookup("class/my-subclass/my-child-class-dev".into())
1069                    .is_none()
1070            );
1071            assert!(
1072                registry
1073                    .objects
1074                    .root
1075                    .lookup("devices/my-device/my-class/my-class-dev/my-child-class-dev".into())
1076                    .is_none()
1077            );
1078
1079            registry.remove_device(&current_task, class_dev);
1080            assert!(registry.objects.root.lookup("class/my-class/my-class-dev".into()).is_none());
1081            assert!(
1082                registry
1083                    .objects
1084                    .root
1085                    .lookup("devices/my-device/my-class/my-class-dev".into())
1086                    .is_none()
1087            );
1088            assert!(registry.objects.root.lookup("devices/my-device/my-class".into()).is_none());
1089
1090            let bus_dev_check = bus_dev.clone();
1091            registry.remove_device(&current_task, bus_dev);
1092            assert!(registry.objects.root.lookup("bus/my-bus/devices/my-device".into()).is_none());
1093            assert!(registry.objects.root.lookup("devices/my-device".into()).is_none());
1094            DelayedReleaser::default().apply(&current_task);
1095            assert!(bus_dev_check.dir().is_none());
1096        })
1097        .await;
1098    }
1099
1100    #[::fuchsia::test]
1101    async fn registry_add_root_and_child_bus_device() {
1102        spawn_kernel_and_run(async |current_task| {
1103            let kernel = current_task.kernel();
1104            let registry = &kernel.device_registry;
1105
1106            let root_dev = registry.add_subsystemless_device(
1107                "my-root".into(),
1108                /* parent = */ None,
1109                build_device_directory,
1110            );
1111            assert!(registry.objects.root.lookup("devices/my-root/uevent".into()).is_some());
1112
1113            let bus = registry.objects.get_or_create_bus("my-bus".into());
1114            let child_bus_dev = registry.add_bus_device(
1115                "my-child".into(),
1116                Some(root_dev.clone()),
1117                bus.clone(),
1118                build_device_directory,
1119            );
1120            assert!(
1121                registry.objects.root.lookup("devices/my-root/my-child/uevent".into()).is_some()
1122            );
1123            assert!(registry.objects.root.lookup("bus/my-bus/devices/my-child".into()).is_some());
1124
1125            let grandchild_bus_dev = registry.add_bus_device(
1126                "my-grandchild".into(),
1127                Some(child_bus_dev.clone()),
1128                bus,
1129                build_device_directory,
1130            );
1131            assert!(
1132                registry
1133                    .objects
1134                    .root
1135                    .lookup("devices/my-root/my-child/my-grandchild/uevent".into())
1136                    .is_some()
1137            );
1138            assert!(
1139                registry.objects.root.lookup("bus/my-bus/devices/my-grandchild".into()).is_some()
1140            );
1141
1142            registry.remove_device(&current_task, grandchild_bus_dev);
1143            assert!(
1144                registry
1145                    .objects
1146                    .root
1147                    .lookup("devices/my-root/my-child/my-grandchild".into())
1148                    .is_none()
1149            );
1150            assert!(
1151                registry.objects.root.lookup("bus/my-bus/devices/my-grandchild".into()).is_none()
1152            );
1153
1154            registry.remove_device(&current_task, child_bus_dev);
1155            assert!(registry.objects.root.lookup("devices/my-root/my-child".into()).is_none());
1156            assert!(registry.objects.root.lookup("bus/my-bus/devices/my-child".into()).is_none());
1157
1158            registry.remove_device(&current_task, root_dev);
1159            assert!(registry.objects.root.lookup("devices/my-root".into()).is_none());
1160        })
1161        .await;
1162    }
1163
1164    #[::fuchsia::test]
1165    async fn registry_remove_device() {
1166        spawn_kernel_and_run(async |current_task| {
1167            let kernel = current_task.kernel();
1168            let registry = &kernel.device_registry;
1169            registry
1170                .register_major(
1171                    "input".into(),
1172                    DeviceMode::Char,
1173                    INPUT_MAJOR,
1174                    simple_device_ops::<DevNull>,
1175                )
1176                .expect("can register input");
1177
1178            let input_class = registry.objects.get_or_create_class("input".into());
1179            let mouse_dev = registry.add_major_device(
1180                kernel,
1181                "mouse".into(),
1182                DeviceMetadata::new(
1183                    "mouse".into(),
1184                    DeviceId::new(INPUT_MAJOR, 0),
1185                    DeviceMode::Char,
1186                ),
1187                /* parent = */ None,
1188                input_class.clone(),
1189                build_device_directory,
1190            );
1191            let keyboard_dev = registry.add_major_device(
1192                kernel,
1193                "keyboard".into(),
1194                DeviceMetadata::new(
1195                    "keyboard".into(),
1196                    DeviceId::new(INPUT_MAJOR, 1),
1197                    DeviceMode::Char,
1198                ),
1199                /* parent = */ None,
1200                input_class,
1201                build_device_directory,
1202            );
1203
1204            assert!(registry.objects.root.lookup("devices/virtual/input/mouse".into()).is_some());
1205            assert!(
1206                registry.objects.root.lookup("devices/virtual/input/keyboard".into()).is_some()
1207            );
1208            assert!(registry.objects.root.lookup("class/input/mouse".into()).is_some());
1209            assert!(registry.objects.root.lookup("class/input/keyboard".into()).is_some());
1210
1211            registry.remove_device(&current_task, mouse_dev);
1212
1213            assert!(registry.objects.root.lookup("devices/virtual/input/mouse".into()).is_none());
1214            assert!(registry.objects.root.lookup("devices/virtual/input".into()).is_some());
1215            assert!(registry.objects.root.lookup("class/input/mouse".into()).is_none());
1216
1217            registry.remove_device(&current_task, keyboard_dev.clone());
1218
1219            assert!(
1220                registry.objects.root.lookup("devices/virtual/input/keyboard".into()).is_none()
1221            );
1222            assert!(registry.objects.root.lookup("devices/virtual/input".into()).is_none());
1223            assert!(registry.objects.root.lookup("class/input/keyboard".into()).is_none());
1224
1225            // Removing an already-removed device must not recreate missing parent directories.
1226            registry.objects.remove(&keyboard_dev);
1227            assert!(registry.objects.root.lookup("devices/virtual/input".into()).is_none());
1228        })
1229        .await;
1230    }
1231
1232    #[::fuchsia::test]
1233    async fn registry_add_and_remove_numberless_device() {
1234        spawn_kernel_and_run(async |current_task| {
1235            let kernel = current_task.kernel();
1236            let registry = &kernel.device_registry;
1237
1238            let cooling_device = registry.add_numberless_device(
1239                "cooling_device0".into(),
1240                /* parent = */ None,
1241                registry.objects.thermal_class(),
1242                build_device_directory,
1243            );
1244
1245            assert!(registry.objects.root.lookup("class/thermal/cooling_device0".into()).is_some());
1246            assert!(
1247                registry
1248                    .objects
1249                    .root
1250                    .lookup("devices/virtual/thermal/cooling_device0".into())
1251                    .is_some()
1252            );
1253
1254            registry.remove_device(&current_task, cooling_device);
1255
1256            assert!(registry.objects.root.lookup("class/thermal/cooling_device0".into()).is_none());
1257            assert!(
1258                registry
1259                    .objects
1260                    .root
1261                    .lookup("devices/virtual/thermal/cooling_device0".into())
1262                    .is_none()
1263            );
1264            assert!(registry.objects.root.lookup("devices/virtual/thermal".into()).is_none());
1265        })
1266        .await;
1267    }
1268
1269    #[::fuchsia::test]
1270    async fn registry_add_and_remove_net_device() {
1271        spawn_kernel_and_run(async |current_task| {
1272            let kernel = current_task.kernel();
1273            let registry = &kernel.device_registry;
1274
1275            let wlan0 = registry.add_net_device("wlan0".into());
1276            assert!(registry.objects.root.lookup("class/net/wlan0".into()).is_some());
1277            assert!(
1278                registry.objects.root.lookup("devices/virtual/net/wlan0/address".into()).is_some()
1279            );
1280            assert!(
1281                registry.objects.root.lookup("devices/virtual/net/wlan0/uevent".into()).is_some()
1282            );
1283
1284            registry.remove_device(&current_task, wlan0);
1285            assert!(registry.objects.root.lookup("class/net/wlan0".into()).is_none());
1286            assert!(registry.objects.root.lookup("devices/virtual/net/wlan0".into()).is_none());
1287        })
1288        .await;
1289    }
1290
1291    #[::fuchsia::test]
1292    async fn registry_remove_minor_device() {
1293        spawn_kernel_with_features_and_run(
1294            async |current_task| {
1295                let kernel = current_task.kernel();
1296                let registry = &kernel.device_registry;
1297
1298                let dev = registry
1299                    .add_misc_device(kernel, "test_misc".into(), simple_device_ops::<DevNull>)
1300                    .expect("add misc device");
1301                let devt = dev.metadata().expect("metadata").devt;
1302
1303                assert!(registry.get_device(devt, DeviceMode::Char).is_ok());
1304
1305                registry.remove_device(&current_task, dev);
1306
1307                assert_eq!(registry.get_device(devt, DeviceMode::Char).map(|_| ()), error!(ENODEV));
1308
1309                // Re-adding after removal reuses the freed dynamic misc DeviceId and devtmpfs path.
1310                let dev2 = registry
1311                    .add_misc_device(kernel, "test_misc".into(), simple_device_ops::<DevNull>)
1312                    .expect("re-add misc device");
1313                assert_eq!(dev2.metadata().expect("metadata").devt, devt);
1314                registry.remove_device(&current_task, dev2);
1315            },
1316            KernelFeatures { devtmpfs: true, ..Default::default() },
1317        )
1318        .await;
1319    }
1320
1321    #[::fuchsia::test]
1322    async fn registry_remove_dyn_device_recycles_device_id() {
1323        spawn_kernel_with_features_and_run(
1324            async |current_task| {
1325                let kernel = current_task.kernel();
1326                let registry = &kernel.device_registry;
1327                let class = registry.objects.get_or_create_class("test_dyn_class".into());
1328
1329                let dev1 = registry
1330                    .add_dyn_device(
1331                        kernel,
1332                        "test_dyn_0".into(),
1333                        class.clone(),
1334                        simple_device_ops::<DevNull>,
1335                    )
1336                    .expect("add first dyn device");
1337                let dev2 = registry
1338                    .add_dyn_device(
1339                        kernel,
1340                        "test_dyn_1".into(),
1341                        class.clone(),
1342                        simple_device_ops::<DevNull>,
1343                    )
1344                    .expect("add second dyn device");
1345                let devt1 = dev1.metadata().expect("metadata").devt;
1346                let devt2 = dev2.metadata().expect("metadata").devt;
1347                assert_ne!(devt1, devt2);
1348
1349                // Free out of order, then in order, verifying range coalescing and reallocation.
1350                registry.remove_device(&current_task, dev1);
1351                registry.remove_device(&current_task, dev2);
1352
1353                let dev1_rebound = registry
1354                    .add_dyn_device(
1355                        kernel,
1356                        "test_dyn_0".into(),
1357                        class,
1358                        simple_device_ops::<DevNull>,
1359                    )
1360                    .expect("re-add dyn device");
1361                assert_eq!(dev1_rebound.metadata().expect("metadata").devt, devt1);
1362                registry.remove_device(&current_task, dev1_rebound);
1363            },
1364            KernelFeatures { devtmpfs: true, ..Default::default() },
1365        )
1366        .await;
1367    }
1368
1369    #[::fuchsia::test]
1370    async fn registry_listener_can_query_registry_and_devtmpfs_collision_does_not_fail_add() {
1371        spawn_kernel_with_features_and_run(
1372            async |current_task| {
1373                let kernel = current_task.kernel();
1374                let registry = &kernel.device_registry;
1375                let observed = Arc::new(starnix_sync::Mutex::new(Vec::new()));
1376                let listener_observed = observed.clone();
1377                let kernel_weak = Arc::downgrade(kernel);
1378                let key = registry.register_listener(
1379                    move |action: UEventAction, device: Device, _context: UEventContext| {
1380                        let Some(kernel) = kernel_weak.upgrade() else {
1381                            return;
1382                        };
1383                        if let Some(metadata) = device.metadata() {
1384                            let is_registered = kernel
1385                                .device_registry
1386                                .get_device(metadata.devt, metadata.mode)
1387                                .is_ok();
1388                            listener_observed.lock().push((action, is_registered));
1389                        }
1390                    },
1391                );
1392
1393                // First device creates `/dev/colliding_dev` in `devtmpfs`.
1394                let dev1 = registry
1395                    .add_misc_device(kernel, "colliding_dev".into(), simple_device_ops::<DevNull>)
1396                    .expect("add first misc device");
1397                // Second device with a colliding `devtmpfs` name still succeeds and dispatches Add.
1398                let dev2 = registry
1399                    .add_misc_device(kernel, "colliding_dev".into(), simple_device_ops::<DevNull>)
1400                    .expect("add second misc device despite devtmpfs collision");
1401
1402                registry.remove_device(&current_task, dev2);
1403                registry.remove_device(&current_task, dev1);
1404
1405                assert_eq!(
1406                    std::mem::take(&mut *observed.lock()),
1407                    vec![
1408                        (UEventAction::Add, true),
1409                        (UEventAction::Add, true),
1410                        (UEventAction::Remove, false),
1411                        (UEventAction::Remove, false),
1412                    ]
1413                );
1414
1415                registry.unregister_listener(&key);
1416            },
1417            KernelFeatures { devtmpfs: true, ..Default::default() },
1418        )
1419        .await;
1420    }
1421
1422    #[::fuchsia::test]
1423    async fn registry_dispatches_uevents_for_bus_and_numberless_devices() {
1424        spawn_kernel_and_run(async |current_task| {
1425            let kernel = current_task.kernel();
1426            let registry = &kernel.device_registry;
1427            let events = Arc::new(starnix_sync::Mutex::new(Vec::new()));
1428            let listener_events = events.clone();
1429            let root = registry.objects.root.clone();
1430            let key = registry.register_listener(
1431                move |action: UEventAction, device: Device, _context: UEventContext| {
1432                    let path = device.path_from_depth(0);
1433                    match action {
1434                        UEventAction::Add => {
1435                            assert!(
1436                                root.lookup(path.as_ref()).is_some(),
1437                                "sysfs entry for {path} must exist when Add uevent is dispatched"
1438                            );
1439                        }
1440                        UEventAction::Remove => {
1441                            assert!(
1442                                root.lookup(path.as_ref()).is_none(),
1443                                "sysfs entry for {path} must be removed before Remove uevent is dispatched"
1444                            );
1445                        }
1446                        _ => {}
1447                    }
1448                    listener_events.lock().push((
1449                        action,
1450                        path,
1451                        device.subsystem().map(|s| s.name().to_owned()),
1452                    ));
1453                },
1454            );
1455
1456            let root_dev = registry.add_subsystemless_device(
1457                "my-root".into(),
1458                /* parent = */ None,
1459                build_device_directory,
1460            );
1461            let bus = registry.objects.get_or_create_bus("my-bus".into());
1462            let bus_dev = registry.add_bus_device(
1463                "my-bus-dev".into(),
1464                Some(root_dev.clone()),
1465                bus,
1466                build_device_directory,
1467            );
1468            let class = registry.objects.get_or_create_class("my-class".into());
1469            let class_dev = registry.add_numberless_device(
1470                "my-class-dev".into(),
1471                /* parent = */ None,
1472                class,
1473                build_device_directory,
1474            );
1475            let net_dev = registry.add_net_device("wlan0".into());
1476
1477            assert_eq!(
1478                std::mem::take(&mut *events.lock()),
1479                vec![
1480                    (UEventAction::Add, "devices/my-root".into(), None),
1481                    (
1482                        UEventAction::Add,
1483                        "devices/my-root/my-bus-dev".into(),
1484                        Some("my-bus".into()),
1485                    ),
1486                    (
1487                        UEventAction::Add,
1488                        "devices/virtual/my-class/my-class-dev".into(),
1489                        Some("my-class".into()),
1490                    ),
1491                    (UEventAction::Add, "devices/virtual/net/wlan0".into(), Some("net".into())),
1492                ]
1493            );
1494
1495            registry.remove_device(&current_task, net_dev);
1496            registry.remove_device(&current_task, class_dev);
1497            registry.remove_device(&current_task, bus_dev);
1498            registry.remove_device(&current_task, root_dev);
1499
1500            assert_eq!(
1501                std::mem::take(&mut *events.lock()),
1502                vec![
1503                    (UEventAction::Remove, "devices/virtual/net/wlan0".into(), Some("net".into())),
1504                    (
1505                        UEventAction::Remove,
1506                        "devices/virtual/my-class/my-class-dev".into(),
1507                        Some("my-class".into()),
1508                    ),
1509                    (
1510                        UEventAction::Remove,
1511                        "devices/my-root/my-bus-dev".into(),
1512                        Some("my-bus".into()),
1513                    ),
1514                    (UEventAction::Remove, "devices/my-root".into(), None),
1515                ]
1516            );
1517
1518            registry.unregister_listener(&key);
1519        })
1520        .await;
1521    }
1522
1523    #[::fuchsia::test]
1524    async fn device_edit_dir() {
1525        spawn_kernel_and_run(async |current_task| {
1526            let kernel = current_task.kernel();
1527            let registry = &kernel.device_registry;
1528
1529            let bus = registry.objects.get_or_create_bus("my-bus".into());
1530            let bus_dev = registry.add_bus_device(
1531                "my-device".into(),
1532                /* parent = */ None,
1533                bus,
1534                build_device_directory,
1535            );
1536            assert!(registry.objects.root.lookup("devices/my-device/uevent".into()).is_some());
1537            assert!(registry.objects.root.lookup("devices/my-device/extra_attr".into()).is_none());
1538
1539            bus_dev.edit_dir(kernel, |dir| {
1540                dir.entry(
1541                    "extra_attr",
1542                    StubEmptyFile::new_node(bug_ref!("https://fxbug.dev/560216403")),
1543                    mode!(IFREG, 0o444),
1544                );
1545            });
1546            assert!(registry.objects.root.lookup("devices/my-device/extra_attr".into()).is_some());
1547
1548            bus_dev.edit_dir(kernel, |dir| {
1549                dir.remove("extra_attr".into());
1550            });
1551            assert!(registry.objects.root.lookup("devices/my-device/extra_attr".into()).is_none());
1552
1553            let bus_dev_check = bus_dev.clone();
1554            registry.remove_device(&current_task, bus_dev);
1555            DelayedReleaser::default().apply(&current_task);
1556
1557            // Editing a removed device's directory is a no-op.
1558            bus_dev_check.edit_dir(kernel, |dir| {
1559                dir.entry(
1560                    "extra_attr",
1561                    StubEmptyFile::new_node(bug_ref!("https://fxbug.dev/560216403")),
1562                    mode!(IFREG, 0o444),
1563                );
1564            });
1565            assert!(registry.objects.root.lookup("devices/my-device".into()).is_none());
1566        })
1567        .await;
1568    }
1569}