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::{Class, Device, DeviceMetadata, UEventAction, UEventContext};
6use crate::device::kobject_store::KObjectStore;
7use crate::fs::devtmpfs::{devtmpfs_create_device, devtmpfs_remove_path};
8use crate::fs::sysfs::build_device_directory;
9use crate::task::{CurrentTask, CurrentTaskAndLocked, Kernel, register_delayed_release};
10use crate::vfs::pseudo::simple_directory::SimpleDirectoryMutator;
11use crate::vfs::{FileOps, FsStr, FsString, NamespaceNode};
12use starnix_lifecycle::{ObjectReleaser, ReleaserAction};
13use starnix_logging::log_error;
14use starnix_sync::{LockBefore, LockDepGuard, LockEqualOrBefore, OrderedMutex};
15use starnix_types::ownership::{Releasable, ReleaseGuard};
16use starnix_uapi::as_any::AsAny;
17use starnix_uapi::device_id::{DYN_MAJOR_RANGE, DeviceId, MISC_DYNANIC_MINOR_RANGE, MISC_MAJOR};
18use starnix_uapi::error;
19use starnix_uapi::errors::Errno;
20use starnix_uapi::open_flags::OpenFlags;
21
22use starnix_sync::{FileOpsCore, Locked, MappedLockDepGuard};
23use std::collections::btree_map::{BTreeMap, Entry};
24use std::ops::{Deref, Range};
25use std::sync::Arc;
26
27use dyn_clone::{DynClone, clone_trait_object};
28
29const CHRDEV_MINOR_MAX: u32 = 256;
30const BLKDEV_MINOR_MAX: u32 = 2u32.pow(20);
31
32/// The mode or category of the device driver.
33#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
34pub enum DeviceMode {
35    /// This device is a character device.
36    Char,
37
38    /// This device is a block device.
39    Block,
40}
41
42impl DeviceMode {
43    /// The number of minor device numbers available for this device mode.
44    fn minor_count(&self) -> u32 {
45        match self {
46            Self::Char => CHRDEV_MINOR_MAX,
47            Self::Block => BLKDEV_MINOR_MAX,
48        }
49    }
50
51    /// The range of minor device numbers available for this device mode.
52    pub fn minor_range(&self) -> Range<u32> {
53        0..self.minor_count()
54    }
55}
56
57pub trait DeviceOps: DynClone + Send + Sync + AsAny + 'static {
58    /// Instantiate a FileOps for this device.
59    ///
60    /// This function is called when userspace opens a file with a `DeviceId`
61    /// assigned to this device.
62    fn open(
63        &self,
64        locked: &mut Locked<FileOpsCore>,
65        current_task: &CurrentTask,
66        devt: DeviceId,
67        node: &NamespaceNode,
68        flags: OpenFlags,
69    ) -> Result<Box<dyn FileOps>, Errno>;
70}
71
72clone_trait_object!(DeviceOps);
73
74/// Allows directly using a function or closure as an implementation of DeviceOps, avoiding having
75/// to write a zero-size struct and an impl for it.
76impl<F> DeviceOps for F
77where
78    F: Clone
79        + Send
80        + Sync
81        + Clone
82        + Fn(
83            &mut Locked<FileOpsCore>,
84            &CurrentTask,
85            DeviceId,
86            &NamespaceNode,
87            OpenFlags,
88        ) -> Result<Box<dyn FileOps>, Errno>
89        + 'static,
90{
91    fn open(
92        &self,
93        locked: &mut Locked<FileOpsCore>,
94        current_task: &CurrentTask,
95        id: DeviceId,
96        node: &NamespaceNode,
97        flags: OpenFlags,
98    ) -> Result<Box<dyn FileOps>, Errno> {
99        self(locked, current_task, id, node, flags)
100    }
101}
102
103/// A simple `DeviceOps` function for any device that implements `FileOps + Default`.
104pub fn simple_device_ops<T: Default + FileOps + 'static>(
105    _locked: &mut Locked<FileOpsCore>,
106    _current_task: &CurrentTask,
107    _id: DeviceId,
108    _node: &NamespaceNode,
109    _flags: OpenFlags,
110) -> Result<Box<dyn FileOps>, Errno> {
111    Ok(Box::new(T::default()))
112}
113
114/// Keys returned by the registration method for `DeviceListener`s that allows to unregister a
115/// listener.
116pub type DeviceListenerKey = u64;
117
118/// A listener for uevents on devices.
119pub trait DeviceListener: Send + Sync {
120    fn on_device_event(&self, action: UEventAction, device: Device, context: UEventContext);
121}
122
123pub struct DeviceOpsWrapper(Box<dyn DeviceOps>);
124impl ReleaserAction<DeviceOpsWrapper> for DeviceOpsWrapper {
125    fn release(device_ops: ReleaseGuard<DeviceOpsWrapper>) {
126        register_delayed_release(device_ops);
127    }
128}
129impl Deref for DeviceOpsWrapper {
130    type Target = dyn DeviceOps;
131    fn deref(&self) -> &Self::Target {
132        self.0.deref()
133    }
134}
135pub type DeviceReleaser = ObjectReleaser<DeviceOpsWrapper, DeviceOpsWrapper>;
136pub type DeviceHandle = Arc<DeviceReleaser>;
137impl Releasable for DeviceOpsWrapper {
138    type Context<'a> = CurrentTaskAndLocked<'a>;
139
140    fn release<'a>(self, _context: CurrentTaskAndLocked<'a>) {}
141}
142
143/// An entry in the `DeviceRegistry`.
144struct DeviceEntry {
145    /// The name of the device.
146    ///
147    /// This name is the same as the name of the KObject for the device.
148    name: FsString,
149
150    /// The ops used to open the device.
151    ops: DeviceHandle,
152}
153
154impl DeviceEntry {
155    fn new(name: FsString, ops: impl DeviceOps) -> Self {
156        Self { name, ops: Arc::new(DeviceOpsWrapper(Box::new(ops)).into()) }
157    }
158}
159
160/// The devices registered for a given `DeviceMode`.
161///
162/// Each `DeviceMode` has its own namespace of registered devices.
163#[derive(Default)]
164struct RegisteredDevices {
165    /// The major devices registered for this device mode.
166    ///
167    /// Typically the devices registered here will add and remove individual devices using the
168    /// `add_device` and `remove_device` functions on `DeviceRegistry`.
169    ///
170    /// A major device registration shadows any minor device registrations for the same major
171    /// device number. We might need to reconsider this choice in the future in order to make
172    /// the /proc/devices file correctly list major devices such as `misc`.
173    majors: BTreeMap<u32, DeviceEntry>,
174
175    /// Individually registered minor devices.
176    ///
177    /// These devices are registered using the `register_device` function on `DeviceRegistry`.
178    minors: BTreeMap<DeviceId, DeviceEntry>,
179}
180
181impl RegisteredDevices {
182    /// Register a major device.
183    ///
184    /// Returns `EINVAL` if the major device is already registered.
185    fn register_major(&mut self, major: u32, entry: DeviceEntry) -> Result<(), Errno> {
186        if let Entry::Vacant(slot) = self.majors.entry(major) {
187            slot.insert(entry);
188            Ok(())
189        } else {
190            error!(EINVAL)
191        }
192    }
193
194    /// Register a minor device.
195    ///
196    /// Overwrites any existing minor device registered with the given `DeviceId`.
197    fn register_minor(&mut self, devt: DeviceId, entry: DeviceEntry) {
198        self.minors.insert(devt, entry);
199    }
200
201    /// Get the ops for a given `DeviceId`.
202    ///
203    /// If there is a major device registered with the major device number of the
204    /// `DeviceId`, the ops for that major device will be returned. Otherwise,
205    /// if there is a minor device registered, the ops for that minor device will be
206    /// returned. Otherwise, returns `ENODEV`.
207    fn get(&self, devt: DeviceId) -> Result<DeviceHandle, Errno> {
208        if let Some(major_device) = self.majors.get(&devt.major()) {
209            Ok(Arc::clone(&major_device.ops))
210        } else if let Some(minor_device) = self.minors.get(&devt) {
211            Ok(Arc::clone(&minor_device.ops))
212        } else {
213            error!(ENODEV)
214        }
215    }
216
217    /// Returns a list of the registered major device numbers and their names.
218    fn list_major_devices(&self) -> Vec<(u32, FsString)> {
219        self.majors.iter().map(|(major, entry)| (*major, entry.name.clone())).collect()
220    }
221
222    /// Returns a list of the registered minor devices and their names.
223    fn list_minor_devices(&self, range: Range<DeviceId>) -> Vec<(DeviceId, FsString)> {
224        self.minors.range(range).map(|(devt, entry)| (devt.clone(), entry.name.clone())).collect()
225    }
226}
227
228/// The registry for devices.
229///
230/// Devices are specified in file systems with major and minor device numbers, together referred to
231/// as a `DeviceId`. When userspace opens one of those files, we look up the `DeviceId` in the
232/// device registry to instantiate a file for that device.
233///
234/// The `DeviceRegistry` also manages the `KObjectStore`, which provides metadata for devices via
235/// the sysfs file system, typically mounted at /sys.
236pub struct DeviceRegistry {
237    /// The KObjects for registered devices.
238    pub objects: KObjectStore,
239
240    /// Mutable state for the device registry.
241    state: OrderedMutex<DeviceRegistryState, starnix_sync::DeviceRegistryState>,
242}
243struct DeviceRegistryState {
244    /// The registered character devices.
245    char_devices: RegisteredDevices,
246
247    /// The registered block devices.
248    block_devices: RegisteredDevices,
249
250    /// Some of the misc devices (devices with the `MISC_MAJOR` major number) are dynamically
251    /// allocated. This allocator keeps track of which device numbers have been allocated to
252    /// such devices.
253    misc_chardev_allocator: DeviceIdAllocator,
254
255    /// A range of large major device numbers are reserved for other dynamically allocated
256    /// devices. This allocator keeps track of which device numbers have been allocated to
257    /// such devices.
258    dyn_chardev_allocator: DeviceIdAllocator,
259
260    /// The next anonymous device number to assign to a file system.
261    next_anon_minor: u32,
262
263    /// Listeners registered to learn about new devices being added to the registry.
264    ///
265    /// These listeners generate uevents for those devices, which populates /dev on some
266    /// systems.
267    listeners: BTreeMap<u64, Box<dyn DeviceListener>>,
268
269    /// The next identifier to use for a listener.
270    next_listener_id: u64,
271
272    /// The next event identifier to use when notifying listeners.
273    next_event_id: u64,
274}
275
276impl DeviceRegistry {
277    /// Notify devfs and listeners that a device has been added to the registry.
278    fn notify_device<L>(
279        &self,
280        locked: &mut Locked<L>,
281        kernel: &Kernel,
282        device: Device,
283    ) -> Result<(), Errno>
284    where
285        L: LockEqualOrBefore<FileOpsCore>,
286    {
287        if let Some(metadata) = &device.metadata {
288            devtmpfs_create_device(kernel, metadata.clone())?;
289            self.dispatch_uevent(locked, UEventAction::Add, device);
290        }
291        Ok(())
292    }
293
294    /// Register a device with the `DeviceRegistry`.
295    ///
296    /// If you are registering a device that exists in other systems, please check the metadata
297    /// for that device in /sys and make sure you use the same properties when calling this
298    /// function because these value are visible to userspace.
299    ///
300    /// For example, a typical device will appear in sysfs at a path like:
301    ///
302    ///   `/sys/devices/{bus}/{class}/{name}`
303    ///
304    /// Many common classes have convenient accessors on `DeviceRegistry::objects`.
305    ///
306    /// To fill out the `DeviceMetadata`, look at the `uevent` file:
307    ///
308    ///   `/sys/devices/{bus}/{class}/{name}/uevent`
309    ///
310    /// which as the following format:
311    ///
312    /// ```
313    ///   MAJOR={major-number}
314    ///   MINOR={minor-number}
315    ///   DEVNAME={devname}
316    ///   DEVMODE={devmode}
317    /// ```
318    ///
319    /// Often, the `{name}` and the `{devname}` are the same, but if they are not the same,
320    /// please take care to use the correct string in the correct field.
321    ///
322    /// If the `{major-number}` is 10 and the `{minor-number}` is in the range 52..128, please use
323    /// `register_misc_device` instead because these device numbers are dynamically allocated.
324    ///
325    /// If the `{major-number}` is in the range 234..255, please use `register_dyn_device` instead
326    /// because these device are also dynamically allocated.
327    ///
328    /// If you are unsure which device numbers to use, consult devices.txt:
329    ///
330    ///   https://www.kernel.org/doc/Documentation/admin-guide/devices.txt
331    ///
332    /// If you are still unsure, please ask an experienced Starnix contributor rather than make up
333    /// a device number.
334    ///
335    /// For most devices, the `create_device_sysfs_ops` parameter should be
336    /// `DeviceDirectory::new`, but some devices have custom directories in sysfs.
337    ///
338    /// Finally, the `dev_ops` parameter is where you provide the callback for instantiating
339    /// your device.
340    pub fn register_device<'a, L>(
341        &self,
342        locked: &mut Locked<L>,
343        kernel: &Kernel,
344        name: &FsStr,
345        metadata: DeviceMetadata,
346        class: Class,
347        dev_ops: impl DeviceOps,
348    ) -> Result<Device, Errno>
349    where
350        L: LockEqualOrBefore<FileOpsCore>,
351    {
352        self.register_device_with_dir(
353            locked,
354            kernel,
355            name,
356            metadata,
357            class,
358            build_device_directory,
359            dev_ops,
360        )
361    }
362
363    /// Register a device with a custom directory.
364    ///
365    /// See `register_device` for an explanation of the parameters.
366    pub fn register_device_with_dir<'a, L>(
367        &self,
368        locked: &mut Locked<L>,
369        kernel: &Kernel,
370        name: &FsStr,
371        metadata: DeviceMetadata,
372        class: Class,
373        build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
374        dev_ops: impl DeviceOps,
375    ) -> Result<Device, Errno>
376    where
377        L: LockEqualOrBefore<FileOpsCore>,
378    {
379        let locked = locked.cast_locked::<FileOpsCore>();
380        let entry = DeviceEntry::new(name.into(), dev_ops);
381        self.devices(locked, metadata.mode).register_minor(metadata.devt, entry);
382        self.add_device(locked, kernel, name, metadata, class, build_directory)
383    }
384
385    /// Register a dynamic device in the `MISC_MAJOR` major device number.
386    ///
387    /// MISC devices (major number 10) with minor numbers in the range 52..128 are dynamically
388    /// assigned. Rather than hardcoding registrations with these device numbers, use this
389    /// function instead to register the device.
390    ///
391    /// See `register_device` for an explanation of the parameters.
392    pub fn register_misc_device<'a, L>(
393        &self,
394        locked: &mut Locked<L>,
395        kernel: &Kernel,
396        name: &FsStr,
397        dev_ops: impl DeviceOps,
398    ) -> Result<Device, Errno>
399    where
400        L: LockEqualOrBefore<FileOpsCore>,
401    {
402        let devt = self.state.lock(locked.cast_locked()).misc_chardev_allocator.allocate()?;
403        let metadata = DeviceMetadata::new(name.into(), devt, DeviceMode::Char);
404        Ok(self.register_device(
405            locked,
406            kernel,
407            name,
408            metadata,
409            self.objects.misc_class(),
410            dev_ops,
411        )?)
412    }
413
414    /// Register a dynamic device with major numbers 234..255.
415    ///
416    /// Majors device numbers 234..255 are dynamically assigned. Rather than hardcoding
417    /// registrations with these device numbers, use this function instead to register the device.
418    ///
419    /// Note: We do not currently allocate from this entire range because we have mistakenly
420    /// hardcoded some device registrations from the dynamic range. Once we fix these registrations
421    /// to be dynamic, we should expand to using the full dynamic range.
422    ///
423    /// See `register_device` for an explanation of the parameters.
424    pub fn register_dyn_device<'a, L>(
425        &self,
426        locked: &mut Locked<L>,
427        kernel: &Kernel,
428        name: &FsStr,
429        class: Class,
430        dev_ops: impl DeviceOps,
431    ) -> Result<Device, Errno>
432    where
433        L: LockEqualOrBefore<FileOpsCore>,
434    {
435        self.register_dyn_device_with_dir(
436            locked,
437            kernel,
438            name,
439            class,
440            build_device_directory,
441            dev_ops,
442        )
443    }
444
445    /// Register a dynamic device with a custom directory.
446    ///
447    /// See `register_device` for an explanation of the parameters.
448    pub fn register_dyn_device_with_dir<'a, L>(
449        &self,
450        locked: &mut Locked<L>,
451        kernel: &Kernel,
452        name: &FsStr,
453        class: Class,
454        build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
455        dev_ops: impl DeviceOps,
456    ) -> Result<Device, Errno>
457    where
458        L: LockEqualOrBefore<FileOpsCore>,
459    {
460        self.register_dyn_device_with_devname(
461            locked,
462            kernel,
463            name,
464            name,
465            class,
466            build_directory,
467            dev_ops,
468        )
469    }
470
471    /// Register a dynamic device with major numbers 234..255.
472    ///
473    /// Majors device numbers 234..255 are dynamically assigned. Rather than hardcoding
474    /// registrations with these device numbers, use this function instead to register the device.
475    ///
476    /// Note: We do not currently allocate from this entire range because we have mistakenly
477    /// hardcoded some device registrations from the dynamic range. Once we fix these registrations
478    /// to be dynamic, we should expand to using the full dynamic range.
479    ///
480    /// See `register_device` for an explanation of the parameters.
481    pub fn register_dyn_device_with_devname<'a, L>(
482        &self,
483        locked: &mut Locked<L>,
484        kernel: &Kernel,
485        name: &FsStr,
486        devname: &FsStr,
487        class: Class,
488        build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
489        dev_ops: impl DeviceOps,
490    ) -> Result<Device, Errno>
491    where
492        L: LockEqualOrBefore<FileOpsCore>,
493    {
494        let devt = self.state.lock(locked.cast_locked()).dyn_chardev_allocator.allocate()?;
495        let metadata = DeviceMetadata::new(devname.into(), devt, DeviceMode::Char);
496        Ok(self.register_device_with_dir(
497            locked,
498            kernel,
499            name,
500            metadata,
501            class,
502            build_directory,
503            dev_ops,
504        )?)
505    }
506
507    /// Register a "silent" dynamic device with major numbers 234..255.
508    ///
509    /// Only use for devices that should not be registered with the KObjectStore/appear in sysfs.
510    /// This is a rare occurrence.
511    ///
512    /// See `register_dyn_device` for an explanation of dyn devices and of the parameters.
513    pub fn register_silent_dyn_device<'a, L>(
514        &self,
515        locked: &mut Locked<L>,
516        name: &FsStr,
517        dev_ops: impl DeviceOps,
518    ) -> Result<DeviceMetadata, Errno>
519    where
520        L: LockEqualOrBefore<FileOpsCore>,
521    {
522        let locked = locked.cast_locked::<FileOpsCore>();
523        let devt = self.state.lock(locked).dyn_chardev_allocator.allocate()?;
524        let metadata = DeviceMetadata::new(name.into(), devt, DeviceMode::Char);
525        let entry = DeviceEntry::new(name.into(), dev_ops);
526        self.devices(locked, metadata.mode).register_minor(metadata.devt, entry);
527        Ok(metadata)
528    }
529
530    /// Directly add a device to the KObjectStore.
531    ///
532    /// This function should be used only by device that have registered an entire major device
533    /// number. If you want to add a single minor device, use the `register_device` function
534    /// instead.
535    ///
536    /// See `register_device` for an explanation of the parameters.
537    pub fn add_device<'a, L>(
538        &self,
539        locked: &mut Locked<L>,
540        kernel: &Kernel,
541        name: &FsStr,
542        metadata: DeviceMetadata,
543        class: Class,
544        build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
545    ) -> Result<Device, Errno>
546    where
547        L: LockEqualOrBefore<FileOpsCore>,
548    {
549        self.devices(locked.cast_locked(), metadata.mode)
550            .get(metadata.devt)
551            .expect("device is registered");
552        let device = self.objects.create_device(name, Some(metadata), class, build_directory);
553
554        self.notify_device(locked, kernel, device.clone())?;
555        Ok(device)
556    }
557
558    /// Add a net device to the device registry.
559    ///
560    /// Net devices are different from other devices because they do not have a device number.
561    /// Instead, their `uevent` files have the following format:
562    ///
563    /// ```
564    /// INTERFACE={name}
565    /// IFINDEX={index}
566    /// ```
567    ///
568    /// Currently, we only register the net devices by name and use an empty `uevent` file.
569    pub fn add_net_device(&self, name: &FsStr) -> Device {
570        self.objects.create_device(name, None, self.objects.net_class(), build_device_directory)
571    }
572
573    /// Remove a net device from the device registry.
574    ///
575    /// See `add_net_device` for more details.
576    pub fn remove_net_device(&self, device: Device) {
577        assert!(device.metadata.is_none());
578        self.objects.remove(&device);
579    }
580
581    /// Directly add a device to the KObjectStore that lacks a device number.
582    ///
583    /// This function should be used only by device do not have a major or a minor number. You can
584    /// identify these devices because they appear in sysfs and have an empty `uevent` file.
585    ///
586    /// See `register_device` for an explanation of the parameters.
587    pub fn add_numberless_device<L>(
588        &self,
589        _locked: &mut Locked<L>,
590        name: &FsStr,
591        class: Class,
592        build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
593    ) -> Device
594    where
595        L: LockEqualOrBefore<FileOpsCore>,
596    {
597        self.objects.create_device(name, None, class, build_directory)
598    }
599
600    /// Remove a device directly added with `add_device`.
601    ///
602    /// This function should be used only by device that have registered an entire major device
603    /// number. Individually registered minor device cannot be removed at this time.
604    pub fn remove_device<L>(
605        &self,
606        locked: &mut Locked<L>,
607        current_task: &CurrentTask,
608        device: Device,
609    ) where
610        L: LockEqualOrBefore<FileOpsCore>,
611    {
612        if let Some(metadata) = &device.metadata {
613            self.dispatch_uevent(locked, UEventAction::Remove, device.clone());
614
615            if let Err(err) = devtmpfs_remove_path(locked, current_task, metadata.devname.as_ref())
616            {
617                log_error!("Cannot remove device {:?} ({:?})", device, err);
618            }
619        }
620
621        self.objects.remove(&device);
622    }
623
624    /// Returns a list of the registered major device numbers for the given `DeviceMode` and their
625    /// names.
626    pub fn list_major_devices<L>(
627        &self,
628        locked: &mut Locked<L>,
629        mode: DeviceMode,
630    ) -> Vec<(u32, FsString)>
631    where
632        L: LockBefore<starnix_sync::DeviceRegistryState>,
633    {
634        self.devices(locked, mode).list_major_devices()
635    }
636
637    /// Returns a list of the registered minor devices for the given `DeviceMode` and their names.
638    pub fn list_minor_devices<L>(
639        &self,
640        locked: &mut Locked<L>,
641        mode: DeviceMode,
642        range: Range<DeviceId>,
643    ) -> Vec<(DeviceId, FsString)>
644    where
645        L: LockBefore<starnix_sync::DeviceRegistryState>,
646    {
647        self.devices(locked, mode).list_minor_devices(range)
648    }
649
650    /// The `RegisteredDevice` object for the given `DeviceMode`.
651    fn devices<'a, L>(
652        &'a self,
653        locked: &'a mut Locked<L>,
654        mode: DeviceMode,
655    ) -> MappedLockDepGuard<'a, RegisteredDevices>
656    where
657        L: LockBefore<starnix_sync::DeviceRegistryState>,
658    {
659        LockDepGuard::map(self.state.lock(locked), |state| match mode {
660            DeviceMode::Char => &mut state.char_devices,
661            DeviceMode::Block => &mut state.block_devices,
662        })
663    }
664
665    /// Register an entire major device number.
666    ///
667    /// If you register an entire major device, use `add_device` and `remove_device` to manage the
668    /// sysfs entiries for your device rather than trying to register and unregister individual
669    /// minor devices.
670    pub fn register_major<'a, L>(
671        &self,
672        locked: &mut Locked<L>,
673        name: FsString,
674        mode: DeviceMode,
675        major: u32,
676        dev_ops: impl DeviceOps,
677    ) -> Result<(), Errno>
678    where
679        L: LockEqualOrBefore<FileOpsCore>,
680    {
681        let locked = locked.cast_locked::<FileOpsCore>();
682        let entry = DeviceEntry::new(name, dev_ops);
683        self.devices(locked, mode).register_major(major, entry)
684    }
685
686    /// Allocate an anonymous device identifier.
687    pub fn next_anonymous_dev_id<'a, L>(&self, locked: &mut Locked<L>) -> DeviceId
688    where
689        L: LockEqualOrBefore<FileOpsCore>,
690    {
691        let locked = locked.cast_locked::<FileOpsCore>();
692        let mut state = self.state.lock(locked);
693        let id = DeviceId::new(0, state.next_anon_minor);
694        state.next_anon_minor += 1;
695        id
696    }
697
698    /// Register a new listener for uevents on devices.
699    ///
700    /// Returns a key used to unregister the listener.
701    pub fn register_listener<'a, L>(
702        &self,
703        locked: &mut Locked<L>,
704        listener: impl DeviceListener + 'static,
705    ) -> DeviceListenerKey
706    where
707        L: LockEqualOrBefore<FileOpsCore>,
708    {
709        let mut state = self.state.lock(locked.cast_locked());
710        let key = state.next_listener_id;
711        state.next_listener_id += 1;
712        state.listeners.insert(key, Box::new(listener));
713        key
714    }
715
716    /// Unregister a listener previously registered through `register_listener`.
717    pub fn unregister_listener<'a, L>(&self, locked: &mut Locked<L>, key: &DeviceListenerKey)
718    where
719        L: LockEqualOrBefore<FileOpsCore>,
720    {
721        self.state.lock(locked.cast_locked()).listeners.remove(key);
722    }
723
724    /// Dispatch an uevent for the given `device`.
725    pub fn dispatch_uevent<'a, L>(
726        &self,
727        locked: &mut Locked<L>,
728        action: UEventAction,
729        device: Device,
730    ) where
731        L: LockEqualOrBefore<FileOpsCore>,
732    {
733        let mut state = self.state.lock(locked.cast_locked());
734        let event_id = state.next_event_id;
735        state.next_event_id += 1;
736        let context = UEventContext { seqnum: event_id };
737        for listener in state.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<L>(
746        &self,
747        locked: &mut Locked<L>,
748        current_task: &CurrentTask,
749        node: &NamespaceNode,
750        flags: OpenFlags,
751        devt: DeviceId,
752        mode: DeviceMode,
753    ) -> Result<Box<dyn FileOps>, Errno>
754    where
755        L: LockEqualOrBefore<FileOpsCore>,
756    {
757        let locked = locked.cast_locked::<FileOpsCore>();
758        let dev_ops = self.devices(locked, mode).get(devt)?;
759        dev_ops.open(locked, current_task, devt, node, flags)
760    }
761
762    pub fn get_device<L>(
763        &self,
764        locked: &mut Locked<L>,
765        devt: DeviceId,
766        mode: DeviceMode,
767    ) -> Result<DeviceHandle, Errno>
768    where
769        L: LockBefore<starnix_sync::DeviceRegistryState>,
770    {
771        self.devices(locked, mode).get(devt)
772    }
773}
774
775impl Default for DeviceRegistry {
776    fn default() -> Self {
777        let misc_available = vec![DeviceId::new_range(MISC_MAJOR, MISC_DYNANIC_MINOR_RANGE)];
778        let dyn_available = DYN_MAJOR_RANGE
779            .map(|major| DeviceId::new_range(major, DeviceMode::Char.minor_range()))
780            .rev()
781            .collect();
782        let state = DeviceRegistryState {
783            char_devices: Default::default(),
784            block_devices: Default::default(),
785            misc_chardev_allocator: DeviceIdAllocator::new(misc_available),
786            dyn_chardev_allocator: DeviceIdAllocator::new(dyn_available),
787            next_anon_minor: 1,
788            listeners: Default::default(),
789            next_listener_id: 0,
790            next_event_id: 0,
791        };
792        Self { objects: Default::default(), state: OrderedMutex::new(state) }
793    }
794}
795
796/// An allocator for `DeviceId`
797struct DeviceIdAllocator {
798    /// The available ranges of device types to allocate.
799    ///
800    /// Devices will be allocated from the back of the vector first.
801    freelist: Vec<Range<DeviceId>>,
802}
803
804impl DeviceIdAllocator {
805    /// Create an allocator for the given ranges of device types.
806    ///
807    /// The devices will be allocated from the front of the vector first.
808    fn new(mut available: Vec<Range<DeviceId>>) -> Self {
809        available.reverse();
810        Self { freelist: available }
811    }
812
813    /// Allocate a `DeviceId`.
814    ///
815    /// Once allocated, there is no mechanism for freeing a `DeviceId`.
816    fn allocate(&mut self) -> Result<DeviceId, Errno> {
817        let Some(range) = self.freelist.pop() else {
818            return error!(ENOMEM);
819        };
820        let allocated = range.start;
821        if let Some(next) = allocated.next_minor() {
822            if next < range.end {
823                self.freelist.push(next..range.end);
824            }
825        }
826        Ok(allocated)
827    }
828}
829
830#[cfg(test)]
831mod tests {
832    use super::*;
833    use crate::device::mem::DevNull;
834    use crate::testing::*;
835    use crate::vfs::*;
836    use starnix_uapi::device_id::{INPUT_MAJOR, MEM_MAJOR};
837
838    #[::fuchsia::test]
839    async fn registry_fails_to_add_duplicate_device() {
840        spawn_kernel_and_run(async |locked, _current_task| {
841            let registry = DeviceRegistry::default();
842            registry
843                .register_major(
844                    locked,
845                    "mem".into(),
846                    DeviceMode::Char,
847                    MEM_MAJOR,
848                    simple_device_ops::<DevNull>,
849                )
850                .expect("registers once");
851            registry
852                .register_major(
853                    locked,
854                    "random".into(),
855                    DeviceMode::Char,
856                    123,
857                    simple_device_ops::<DevNull>,
858                )
859                .expect("registers unique");
860            registry
861                .register_major(
862                    locked,
863                    "mem".into(),
864                    DeviceMode::Char,
865                    MEM_MAJOR,
866                    simple_device_ops::<DevNull>,
867                )
868                .expect_err("fail to register duplicate");
869        })
870        .await;
871    }
872
873    #[::fuchsia::test]
874    async fn registry_opens_device() {
875        spawn_kernel_and_run(async |locked, current_task| {
876            let kernel = current_task.kernel();
877            let registry = DeviceRegistry::default();
878            registry
879                .register_major(
880                    locked,
881                    "mem".into(),
882                    DeviceMode::Char,
883                    MEM_MAJOR,
884                    simple_device_ops::<DevNull>,
885                )
886                .expect("registers unique");
887
888            let fs = create_testfs(locked, &kernel);
889            let node = create_namespace_node_for_testing(&fs, PanickingFsNode);
890
891            // Fail to open non-existent device.
892            assert!(
893                registry
894                    .open_device(
895                        locked,
896                        &current_task,
897                        &node,
898                        OpenFlags::RDONLY,
899                        DeviceId::NONE,
900                        DeviceMode::Char
901                    )
902                    .is_err()
903            );
904
905            // Fail to open in wrong mode.
906            assert!(
907                registry
908                    .open_device(
909                        locked,
910                        &current_task,
911                        &node,
912                        OpenFlags::RDONLY,
913                        DeviceId::NULL,
914                        DeviceMode::Block
915                    )
916                    .is_err()
917            );
918
919            // Open in correct mode.
920            let _ = registry
921                .open_device(
922                    locked,
923                    &current_task,
924                    &node,
925                    OpenFlags::RDONLY,
926                    DeviceId::NULL,
927                    DeviceMode::Char,
928                )
929                .expect("opens device");
930        })
931        .await;
932    }
933
934    #[::fuchsia::test]
935    async fn registry_dynamic_misc() {
936        spawn_kernel_and_run(async |locked, current_task| {
937            let kernel = current_task.kernel();
938            fn create_test_device(
939                _locked: &mut Locked<FileOpsCore>,
940                _current_task: &CurrentTask,
941                _id: DeviceId,
942                _node: &NamespaceNode,
943                _flags: OpenFlags,
944            ) -> Result<Box<dyn FileOps>, Errno> {
945                Ok(Box::new(PanickingFile))
946            }
947
948            let registry = &kernel.device_registry;
949            let device = registry
950                .register_dyn_device(
951                    locked,
952                    kernel,
953                    "test-device".into(),
954                    registry.objects.virtual_block_class(),
955                    create_test_device,
956                )
957                .unwrap();
958            let devt = device.metadata.expect("has metadata").devt;
959            assert!(DYN_MAJOR_RANGE.contains(&devt.major()));
960
961            let fs = create_testfs(locked, &kernel);
962            let node = create_namespace_node_for_testing(&fs, PanickingFsNode);
963            let _ = registry
964                .open_device(
965                    locked,
966                    &current_task,
967                    &node,
968                    OpenFlags::RDONLY,
969                    devt,
970                    DeviceMode::Char,
971                )
972                .expect("opens device");
973        })
974        .await;
975    }
976
977    #[::fuchsia::test]
978    async fn registery_add_class() {
979        spawn_kernel_and_run(async |locked, current_task| {
980            let kernel = current_task.kernel();
981            let registry = &kernel.device_registry;
982            registry
983                .register_major(
984                    locked,
985                    "input".into(),
986                    DeviceMode::Char,
987                    INPUT_MAJOR,
988                    simple_device_ops::<DevNull>,
989                )
990                .expect("can register input");
991
992            let input_class = registry
993                .objects
994                .get_or_create_class("input".into(), registry.objects.virtual_bus());
995            registry
996                .add_device(
997                    locked,
998                    kernel,
999                    "mouse".into(),
1000                    DeviceMetadata::new(
1001                        "mouse".into(),
1002                        DeviceId::new(INPUT_MAJOR, 0),
1003                        DeviceMode::Char,
1004                    ),
1005                    input_class,
1006                    build_device_directory,
1007                )
1008                .expect("add_device");
1009
1010            assert!(registry.objects.root.lookup("class/input/mouse".into()).is_some());
1011        })
1012        .await;
1013    }
1014
1015    #[::fuchsia::test]
1016    async fn registry_add_bus() {
1017        spawn_kernel_and_run(async |locked, current_task| {
1018            let kernel = current_task.kernel();
1019            let registry = &kernel.device_registry;
1020            registry
1021                .register_major(
1022                    locked,
1023                    "input".into(),
1024                    DeviceMode::Char,
1025                    INPUT_MAJOR,
1026                    simple_device_ops::<DevNull>,
1027                )
1028                .expect("can register input");
1029
1030            let bus = registry.objects.get_or_create_bus("my-bus".into());
1031            let class = registry.objects.get_or_create_class("my-class".into(), bus);
1032            registry
1033                .add_device(
1034                    locked,
1035                    kernel,
1036                    "my-device".into(),
1037                    DeviceMetadata::new(
1038                        "my-device".into(),
1039                        DeviceId::new(INPUT_MAJOR, 0),
1040                        DeviceMode::Char,
1041                    ),
1042                    class,
1043                    build_device_directory,
1044                )
1045                .expect("add_device");
1046            assert!(registry.objects.root.lookup("bus/my-bus".into()).is_some());
1047            assert!(registry.objects.root.lookup("devices/my-bus/my-class".into()).is_some());
1048            assert!(
1049                registry.objects.root.lookup("devices/my-bus/my-class/my-device".into()).is_some()
1050            );
1051        })
1052        .await;
1053    }
1054
1055    #[::fuchsia::test]
1056    async fn registry_remove_device() {
1057        spawn_kernel_and_run(async |locked, current_task| {
1058            let kernel = current_task.kernel();
1059            let registry = &kernel.device_registry;
1060            registry
1061                .register_major(
1062                    locked,
1063                    "input".into(),
1064                    DeviceMode::Char,
1065                    INPUT_MAJOR,
1066                    simple_device_ops::<DevNull>,
1067                )
1068                .expect("can register input");
1069
1070            let pci_bus = registry.objects.get_or_create_bus("pci".into());
1071            let input_class = registry.objects.get_or_create_class("input".into(), pci_bus);
1072            let mouse_dev = registry
1073                .add_device(
1074                    locked,
1075                    kernel,
1076                    "mouse".into(),
1077                    DeviceMetadata::new(
1078                        "mouse".into(),
1079                        DeviceId::new(INPUT_MAJOR, 0),
1080                        DeviceMode::Char,
1081                    ),
1082                    input_class.clone(),
1083                    build_device_directory,
1084                )
1085                .expect("add_device");
1086
1087            assert!(registry.objects.root.lookup("bus/pci/devices/mouse".into()).is_some());
1088            assert!(registry.objects.root.lookup("devices/pci/input/mouse".into()).is_some());
1089            assert!(registry.objects.root.lookup("class/input/mouse".into()).is_some());
1090
1091            registry.remove_device(locked, &current_task, mouse_dev);
1092
1093            assert!(registry.objects.root.lookup("bus/pci/devices/mouse".into()).is_none());
1094            assert!(registry.objects.root.lookup("devices/pci/input/mouse".into()).is_none());
1095            assert!(registry.objects.root.lookup("class/input/mouse".into()).is_none());
1096        })
1097        .await;
1098    }
1099
1100    #[::fuchsia::test]
1101    async fn registry_add_and_remove_numberless_device() {
1102        spawn_kernel_and_run(async |locked, current_task| {
1103            let kernel = current_task.kernel();
1104            let registry = &kernel.device_registry;
1105
1106            let cooling_device = registry.add_numberless_device(
1107                locked,
1108                "cooling_device0".into(),
1109                registry.objects.virtual_thermal_class(),
1110                build_device_directory,
1111            );
1112
1113            assert!(registry.objects.root.lookup("class/thermal/cooling_device0".into()).is_some());
1114            assert!(
1115                registry
1116                    .objects
1117                    .root
1118                    .lookup("devices/virtual/thermal/cooling_device0".into())
1119                    .is_some()
1120            );
1121
1122            registry.remove_device(locked, &current_task, cooling_device);
1123
1124            assert!(registry.objects.root.lookup("class/thermal/cooling_device0".into()).is_none());
1125            assert!(
1126                registry
1127                    .objects
1128                    .root
1129                    .lookup("devices/virtual/thermal/cooling_device0".into())
1130                    .is_none()
1131            );
1132        })
1133        .await;
1134    }
1135}