1use 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, 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::{DeviceRegistryStateLevel, LockDepGuard, LockDepMutex, MappedLockDepGuard};
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;
21use std::collections::btree_map::{BTreeMap, Entry};
22use std::ops::{Deref, Range};
23use std::sync::Arc;
24
25use dyn_clone::{DynClone, clone_trait_object};
26
27const CHRDEV_MINOR_MAX: u32 = 256;
28const BLKDEV_MINOR_MAX: u32 = 2u32.pow(20);
29
30#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
32pub enum DeviceMode {
33 Char,
35
36 Block,
38}
39
40impl DeviceMode {
41 fn minor_count(&self) -> u32 {
43 match self {
44 Self::Char => CHRDEV_MINOR_MAX,
45 Self::Block => BLKDEV_MINOR_MAX,
46 }
47 }
48
49 pub fn minor_range(&self) -> Range<u32> {
51 0..self.minor_count()
52 }
53}
54
55pub trait DeviceOps: DynClone + Send + Sync + AsAny + 'static {
56 fn open(
61 &self,
62 current_task: &CurrentTask,
63 devt: DeviceId,
64 node: &NamespaceNode,
65 flags: OpenFlags,
66 ) -> Result<Box<dyn FileOps>, Errno>;
67}
68
69clone_trait_object!(DeviceOps);
70
71impl<F> DeviceOps for F
74where
75 F: Clone
76 + Send
77 + Sync
78 + Clone
79 + Fn(&CurrentTask, DeviceId, &NamespaceNode, OpenFlags) -> Result<Box<dyn FileOps>, Errno>
80 + 'static,
81{
82 fn open(
83 &self,
84 current_task: &CurrentTask,
85 id: DeviceId,
86 node: &NamespaceNode,
87 flags: OpenFlags,
88 ) -> Result<Box<dyn FileOps>, Errno> {
89 self(current_task, id, node, flags)
90 }
91}
92
93pub fn simple_device_ops<T: Default + FileOps + 'static>(
95 _current_task: &CurrentTask,
96 _id: DeviceId,
97 _node: &NamespaceNode,
98 _flags: OpenFlags,
99) -> Result<Box<dyn FileOps>, Errno> {
100 Ok(Box::new(T::default()))
101}
102
103pub type DeviceListenerKey = u64;
106
107pub trait DeviceListener: Send + Sync {
109 fn on_device_event(&self, action: UEventAction, device: Device, context: UEventContext);
110}
111
112pub struct DeviceOpsWrapper(Box<dyn DeviceOps>);
113impl ReleaserAction<DeviceOpsWrapper> for DeviceOpsWrapper {
114 fn release(device_ops: ReleaseGuard<DeviceOpsWrapper>) {
115 register_delayed_release(device_ops);
116 }
117}
118impl Deref for DeviceOpsWrapper {
119 type Target = dyn DeviceOps;
120 fn deref(&self) -> &Self::Target {
121 self.0.deref()
122 }
123}
124pub type DeviceReleaser = ObjectReleaser<DeviceOpsWrapper, DeviceOpsWrapper>;
125pub type DeviceHandle = Arc<DeviceReleaser>;
126impl Releasable for DeviceOpsWrapper {
127 type Context<'a> = &'a CurrentTask;
128
129 fn release<'a>(self, _context: &'a CurrentTask) {}
130}
131
132struct DeviceEntry {
134 name: FsString,
138
139 ops: DeviceHandle,
141}
142
143impl DeviceEntry {
144 fn new(name: FsString, ops: impl DeviceOps) -> Self {
145 Self { name, ops: Arc::new(DeviceOpsWrapper(Box::new(ops)).into()) }
146 }
147}
148
149#[derive(Default)]
153struct RegisteredDevices {
154 majors: BTreeMap<u32, DeviceEntry>,
163
164 minors: BTreeMap<DeviceId, DeviceEntry>,
168}
169
170impl RegisteredDevices {
171 fn register_major(&mut self, major: u32, entry: DeviceEntry) -> Result<(), Errno> {
175 if let Entry::Vacant(slot) = self.majors.entry(major) {
176 slot.insert(entry);
177 Ok(())
178 } else {
179 error!(EINVAL)
180 }
181 }
182
183 fn register_minor(&mut self, devt: DeviceId, entry: DeviceEntry) {
187 self.minors.insert(devt, entry);
188 }
189
190 fn unregister_minor(&mut self, devt: DeviceId) {
192 self.minors.remove(&devt);
193 }
194
195 fn get(&self, devt: DeviceId) -> Result<DeviceHandle, Errno> {
202 if let Some(major_device) = self.majors.get(&devt.major()) {
203 Ok(Arc::clone(&major_device.ops))
204 } else if let Some(minor_device) = self.minors.get(&devt) {
205 Ok(Arc::clone(&minor_device.ops))
206 } else {
207 error!(ENODEV)
208 }
209 }
210
211 fn list_major_devices(&self) -> Vec<(u32, FsString)> {
213 self.majors.iter().map(|(major, entry)| (*major, entry.name.clone())).collect()
214 }
215
216 fn list_minor_devices(&self, range: Range<DeviceId>) -> Vec<(DeviceId, FsString)> {
218 self.minors.range(range).map(|(devt, entry)| (devt.clone(), entry.name.clone())).collect()
219 }
220}
221
222pub struct DeviceRegistry {
231 pub objects: KObjectStore,
233
234 state: LockDepMutex<DeviceRegistryState, DeviceRegistryStateLevel>,
236}
237struct DeviceRegistryState {
238 char_devices: RegisteredDevices,
240
241 block_devices: RegisteredDevices,
243
244 misc_chardev_allocator: DeviceIdAllocator,
248
249 dyn_chardev_allocator: DeviceIdAllocator,
253
254 next_anon_minor: u32,
256
257 listeners: BTreeMap<u64, Box<dyn DeviceListener>>,
262
263 next_listener_id: u64,
265
266 next_event_id: u64,
268}
269
270impl DeviceRegistry {
271 fn notify_device(&self, kernel: &Kernel, device: Device) -> Result<(), Errno> {
273 if let Some(metadata) = &device.metadata {
274 devtmpfs_create_device(kernel, metadata.clone())?;
275 self.dispatch_uevent(UEventAction::Add, device);
276 }
277 Ok(())
278 }
279
280 pub fn register_device<'a>(
327 &self,
328 kernel: &Kernel,
329 name: &FsStr,
330 metadata: DeviceMetadata,
331 class: Class,
332 dev_ops: impl DeviceOps,
333 ) -> Result<Device, Errno> {
334 self.register_device_with_dir(
335 kernel,
336 name,
337 metadata,
338 class,
339 build_device_directory,
340 dev_ops,
341 )
342 }
343
344 pub fn register_device_with_dir<'a>(
348 &self,
349 kernel: &Kernel,
350 name: &FsStr,
351 metadata: DeviceMetadata,
352 class: Class,
353 build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
354 dev_ops: impl DeviceOps,
355 ) -> Result<Device, Errno> {
356 let entry = DeviceEntry::new(name.into(), dev_ops);
357 self.devices(metadata.mode).register_minor(metadata.devt, entry);
358 self.add_device(kernel, name, metadata, class, build_directory)
359 }
360
361 pub fn register_misc_device<'a>(
369 &self,
370 kernel: &Kernel,
371 name: &FsStr,
372 dev_ops: impl DeviceOps,
373 ) -> Result<Device, Errno> {
374 let devt = self.state.lock().misc_chardev_allocator.allocate()?;
375 let metadata = DeviceMetadata::new(name.into(), devt, DeviceMode::Char);
376 Ok(self.register_device(kernel, name, metadata, self.objects.misc_class(), dev_ops)?)
377 }
378
379 pub fn register_dyn_device<'a>(
390 &self,
391 kernel: &Kernel,
392 name: &FsStr,
393 class: Class,
394 dev_ops: impl DeviceOps,
395 ) -> Result<Device, Errno> {
396 self.register_dyn_device_with_dir(kernel, name, class, build_device_directory, dev_ops)
397 }
398
399 pub fn register_dyn_device_with_dir<'a>(
403 &self,
404 kernel: &Kernel,
405 name: &FsStr,
406 class: Class,
407 build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
408 dev_ops: impl DeviceOps,
409 ) -> Result<Device, Errno> {
410 self.register_dyn_device_with_devname(kernel, name, name, class, build_directory, dev_ops)
411 }
412
413 pub fn register_dyn_device_with_devname<'a>(
424 &self,
425 kernel: &Kernel,
426 name: &FsStr,
427 devname: &FsStr,
428 class: Class,
429 build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
430 dev_ops: impl DeviceOps,
431 ) -> Result<Device, Errno> {
432 let devt = self.state.lock().dyn_chardev_allocator.allocate()?;
433 let metadata = DeviceMetadata::new(devname.into(), devt, DeviceMode::Char);
434 Ok(self.register_device_with_dir(
435 kernel,
436 name,
437 metadata,
438 class,
439 build_directory,
440 dev_ops,
441 )?)
442 }
443
444 pub fn register_silent_dyn_device<'a>(
451 &self,
452 name: &FsStr,
453 dev_ops: impl DeviceOps,
454 ) -> Result<DeviceMetadata, Errno> {
455 let devt = self.state.lock().dyn_chardev_allocator.allocate()?;
456 let metadata = DeviceMetadata::new(name.into(), devt, DeviceMode::Char);
457 let entry = DeviceEntry::new(name.into(), dev_ops);
458 self.devices(metadata.mode).register_minor(metadata.devt, entry);
459 Ok(metadata)
460 }
461
462 pub fn add_device<'a>(
470 &self,
471 kernel: &Kernel,
472 name: &FsStr,
473 metadata: DeviceMetadata,
474 class: Class,
475 build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
476 ) -> Result<Device, Errno> {
477 self.devices(metadata.mode).get(metadata.devt).expect("device is registered");
478 let device = self.objects.create_device(name, Some(metadata), class, build_directory);
479
480 self.notify_device(kernel, device.clone())?;
481 Ok(device)
482 }
483
484 pub fn add_net_device(&self, name: &FsStr) -> Device {
496 self.objects.create_device(name, None, self.objects.net_class(), build_device_directory)
497 }
498
499 pub fn remove_net_device(&self, device: Device) {
503 assert!(device.metadata.is_none());
504 self.objects.remove(&device);
505 }
506
507 pub fn add_numberless_device(
514 &self,
515 name: &FsStr,
516 class: Class,
517 build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
518 ) -> Device {
519 self.objects.create_device(name, None, class, build_directory)
520 }
521
522 pub fn remove_device(&self, current_task: &CurrentTask, device: Device) {
524 if let Some(metadata) = &device.metadata {
525 self.dispatch_uevent(UEventAction::Remove, device.clone());
526
527 if let Err(err) = devtmpfs_remove_path(current_task, metadata.devname.as_ref()) {
528 log_error!("Cannot remove device {:?} ({:?})", device, err);
529 }
530
531 self.devices(metadata.mode).unregister_minor(metadata.devt);
532 }
533
534 self.objects.remove(&device);
535 }
536
537 pub fn list_major_devices(&self, mode: DeviceMode) -> Vec<(u32, FsString)> {
540 self.devices(mode).list_major_devices()
541 }
542
543 pub fn list_minor_devices(
545 &self,
546 mode: DeviceMode,
547 range: Range<DeviceId>,
548 ) -> Vec<(DeviceId, FsString)> {
549 self.devices(mode).list_minor_devices(range)
550 }
551
552 fn devices<'a>(&'a self, mode: DeviceMode) -> MappedLockDepGuard<'a, RegisteredDevices> {
554 LockDepGuard::map(self.state.lock(), |state| match mode {
555 DeviceMode::Char => &mut state.char_devices,
556 DeviceMode::Block => &mut state.block_devices,
557 })
558 }
559
560 pub fn register_major<'a>(
566 &self,
567 name: FsString,
568 mode: DeviceMode,
569 major: u32,
570 dev_ops: impl DeviceOps,
571 ) -> Result<(), Errno> {
572 let entry = DeviceEntry::new(name, dev_ops);
573 self.devices(mode).register_major(major, entry)
574 }
575
576 pub fn next_anonymous_dev_id<'a>(&self) -> DeviceId {
578 let mut state = self.state.lock();
579 let id = DeviceId::new(0, state.next_anon_minor);
580 state.next_anon_minor += 1;
581 id
582 }
583
584 pub fn register_listener<'a>(
588 &self,
589 listener: impl DeviceListener + 'static,
590 ) -> DeviceListenerKey {
591 let mut state = self.state.lock();
592 let key = state.next_listener_id;
593 state.next_listener_id += 1;
594 state.listeners.insert(key, Box::new(listener));
595 key
596 }
597
598 pub fn unregister_listener<'a>(&self, key: &DeviceListenerKey) {
600 self.state.lock().listeners.remove(key);
601 }
602
603 pub fn dispatch_uevent<'a>(&self, action: UEventAction, device: Device) {
605 let mut state = self.state.lock();
606 let event_id = state.next_event_id;
607 state.next_event_id += 1;
608 let context = UEventContext { seqnum: event_id };
609 for listener in state.listeners.values() {
610 listener.on_device_event(action, device.clone(), context);
611 }
612 }
613
614 pub fn open_device(
618 &self,
619 current_task: &CurrentTask,
620 node: &NamespaceNode,
621 flags: OpenFlags,
622 devt: DeviceId,
623 mode: DeviceMode,
624 ) -> Result<Box<dyn FileOps>, Errno> {
625 let dev_ops = self.devices(mode).get(devt)?;
626 dev_ops.open(current_task, devt, node, flags)
627 }
628
629 pub fn get_device(&self, devt: DeviceId, mode: DeviceMode) -> Result<DeviceHandle, Errno> {
630 self.devices(mode).get(devt)
631 }
632}
633
634impl Default for DeviceRegistry {
635 fn default() -> Self {
636 let misc_available = vec![DeviceId::new_range(MISC_MAJOR, MISC_DYNANIC_MINOR_RANGE)];
637 let dyn_available = DYN_MAJOR_RANGE
638 .map(|major| DeviceId::new_range(major, DeviceMode::Char.minor_range()))
639 .rev()
640 .collect();
641 let state = DeviceRegistryState {
642 char_devices: Default::default(),
643 block_devices: Default::default(),
644 misc_chardev_allocator: DeviceIdAllocator::new(misc_available),
645 dyn_chardev_allocator: DeviceIdAllocator::new(dyn_available),
646 next_anon_minor: 1,
647 listeners: Default::default(),
648 next_listener_id: 0,
649 next_event_id: 0,
650 };
651 Self { objects: Default::default(), state: state.into() }
652 }
653}
654
655struct DeviceIdAllocator {
657 freelist: Vec<Range<DeviceId>>,
661}
662
663impl DeviceIdAllocator {
664 fn new(mut available: Vec<Range<DeviceId>>) -> Self {
668 available.reverse();
669 Self { freelist: available }
670 }
671
672 fn allocate(&mut self) -> Result<DeviceId, Errno> {
676 let Some(range) = self.freelist.pop() else {
677 return error!(ENOMEM);
678 };
679 let allocated = range.start;
680 if let Some(next) = allocated.next_minor() {
681 if next < range.end {
682 self.freelist.push(next..range.end);
683 }
684 }
685 Ok(allocated)
686 }
687}
688
689#[cfg(test)]
690mod tests {
691 use super::*;
692 use crate::device::mem::DevNull;
693 use crate::testing::*;
694 use crate::vfs::*;
695 use starnix_uapi::device_id::{INPUT_MAJOR, MEM_MAJOR};
696
697 #[::fuchsia::test]
698 async fn registry_fails_to_add_duplicate_device() {
699 spawn_kernel_and_run(async |_current_task| {
700 let registry = DeviceRegistry::default();
701 registry
702 .register_major(
703 "mem".into(),
704 DeviceMode::Char,
705 MEM_MAJOR,
706 simple_device_ops::<DevNull>,
707 )
708 .expect("registers once");
709 registry
710 .register_major(
711 "random".into(),
712 DeviceMode::Char,
713 123,
714 simple_device_ops::<DevNull>,
715 )
716 .expect("registers unique");
717 registry
718 .register_major(
719 "mem".into(),
720 DeviceMode::Char,
721 MEM_MAJOR,
722 simple_device_ops::<DevNull>,
723 )
724 .expect_err("fail to register duplicate");
725 })
726 .await;
727 }
728
729 #[::fuchsia::test]
730 async fn registry_opens_device() {
731 spawn_kernel_and_run(async |current_task| {
732 let kernel = current_task.kernel();
733 let registry = DeviceRegistry::default();
734 registry
735 .register_major(
736 "mem".into(),
737 DeviceMode::Char,
738 MEM_MAJOR,
739 simple_device_ops::<DevNull>,
740 )
741 .expect("registers unique");
742
743 let fs = create_testfs(&kernel);
744 let node = create_namespace_node_for_testing(&fs, PanickingFsNode);
745
746 assert!(
748 registry
749 .open_device(
750 ¤t_task,
751 &node,
752 OpenFlags::RDONLY,
753 DeviceId::NONE,
754 DeviceMode::Char
755 )
756 .is_err()
757 );
758
759 assert!(
761 registry
762 .open_device(
763 ¤t_task,
764 &node,
765 OpenFlags::RDONLY,
766 DeviceId::NULL,
767 DeviceMode::Block
768 )
769 .is_err()
770 );
771
772 let _ = registry
774 .open_device(
775 ¤t_task,
776 &node,
777 OpenFlags::RDONLY,
778 DeviceId::NULL,
779 DeviceMode::Char,
780 )
781 .expect("opens device");
782 })
783 .await;
784 }
785
786 #[::fuchsia::test]
787 async fn registry_dynamic_misc() {
788 spawn_kernel_and_run(async |current_task| {
789 let kernel = current_task.kernel();
790 fn create_test_device(
791 _current_task: &CurrentTask,
792 _id: DeviceId,
793 _node: &NamespaceNode,
794 _flags: OpenFlags,
795 ) -> Result<Box<dyn FileOps>, Errno> {
796 Ok(Box::new(PanickingFile))
797 }
798
799 let registry = &kernel.device_registry;
800 let device = registry
801 .register_dyn_device(
802 kernel,
803 "test-device".into(),
804 registry.objects.virtual_block_class(),
805 create_test_device,
806 )
807 .unwrap();
808 let devt = device.metadata.expect("has metadata").devt;
809 assert!(DYN_MAJOR_RANGE.contains(&devt.major()));
810
811 let fs = create_testfs(&kernel);
812 let node = create_namespace_node_for_testing(&fs, PanickingFsNode);
813 let _ = registry
814 .open_device(¤t_task, &node, OpenFlags::RDONLY, devt, DeviceMode::Char)
815 .expect("opens device");
816 })
817 .await;
818 }
819
820 #[::fuchsia::test]
821 async fn registery_add_class() {
822 spawn_kernel_and_run(async |current_task| {
823 let kernel = current_task.kernel();
824 let registry = &kernel.device_registry;
825 registry
826 .register_major(
827 "input".into(),
828 DeviceMode::Char,
829 INPUT_MAJOR,
830 simple_device_ops::<DevNull>,
831 )
832 .expect("can register input");
833
834 let input_class = registry
835 .objects
836 .get_or_create_class("input".into(), registry.objects.virtual_bus());
837 registry
838 .add_device(
839 kernel,
840 "mouse".into(),
841 DeviceMetadata::new(
842 "mouse".into(),
843 DeviceId::new(INPUT_MAJOR, 0),
844 DeviceMode::Char,
845 ),
846 input_class,
847 build_device_directory,
848 )
849 .expect("add_device");
850
851 assert!(registry.objects.root.lookup("class/input/mouse".into()).is_some());
852 })
853 .await;
854 }
855
856 #[::fuchsia::test]
857 async fn registry_add_bus() {
858 spawn_kernel_and_run(async |current_task| {
859 let kernel = current_task.kernel();
860 let registry = &kernel.device_registry;
861 registry
862 .register_major(
863 "input".into(),
864 DeviceMode::Char,
865 INPUT_MAJOR,
866 simple_device_ops::<DevNull>,
867 )
868 .expect("can register input");
869
870 let bus = registry.objects.get_or_create_bus("my-bus".into());
871 let class = registry.objects.get_or_create_class("my-class".into(), bus);
872 registry
873 .add_device(
874 kernel,
875 "my-device".into(),
876 DeviceMetadata::new(
877 "my-device".into(),
878 DeviceId::new(INPUT_MAJOR, 0),
879 DeviceMode::Char,
880 ),
881 class,
882 build_device_directory,
883 )
884 .expect("add_device");
885 assert!(registry.objects.root.lookup("bus/my-bus".into()).is_some());
886 assert!(registry.objects.root.lookup("devices/my-bus/my-class".into()).is_some());
887 assert!(
888 registry.objects.root.lookup("devices/my-bus/my-class/my-device".into()).is_some()
889 );
890 })
891 .await;
892 }
893
894 #[::fuchsia::test]
895 async fn registry_remove_device() {
896 spawn_kernel_and_run(async |current_task| {
897 let kernel = current_task.kernel();
898 let registry = &kernel.device_registry;
899 registry
900 .register_major(
901 "input".into(),
902 DeviceMode::Char,
903 INPUT_MAJOR,
904 simple_device_ops::<DevNull>,
905 )
906 .expect("can register input");
907
908 let pci_bus = registry.objects.get_or_create_bus("pci".into());
909 let input_class = registry.objects.get_or_create_class("input".into(), pci_bus);
910 let mouse_dev = registry
911 .add_device(
912 kernel,
913 "mouse".into(),
914 DeviceMetadata::new(
915 "mouse".into(),
916 DeviceId::new(INPUT_MAJOR, 0),
917 DeviceMode::Char,
918 ),
919 input_class.clone(),
920 build_device_directory,
921 )
922 .expect("add_device");
923
924 assert!(registry.objects.root.lookup("bus/pci/devices/mouse".into()).is_some());
925 assert!(registry.objects.root.lookup("devices/pci/input/mouse".into()).is_some());
926 assert!(registry.objects.root.lookup("class/input/mouse".into()).is_some());
927
928 registry.remove_device(¤t_task, mouse_dev);
929
930 assert!(registry.objects.root.lookup("bus/pci/devices/mouse".into()).is_none());
931 assert!(registry.objects.root.lookup("devices/pci/input/mouse".into()).is_none());
932 assert!(registry.objects.root.lookup("class/input/mouse".into()).is_none());
933 })
934 .await;
935 }
936
937 #[::fuchsia::test]
938 async fn registry_add_and_remove_numberless_device() {
939 spawn_kernel_and_run(async |current_task| {
940 let kernel = current_task.kernel();
941 let registry = &kernel.device_registry;
942
943 let cooling_device = registry.add_numberless_device(
944 "cooling_device0".into(),
945 registry.objects.virtual_thermal_class(),
946 build_device_directory,
947 );
948
949 assert!(registry.objects.root.lookup("class/thermal/cooling_device0".into()).is_some());
950 assert!(
951 registry
952 .objects
953 .root
954 .lookup("devices/virtual/thermal/cooling_device0".into())
955 .is_some()
956 );
957
958 registry.remove_device(¤t_task, cooling_device);
959
960 assert!(registry.objects.root.lookup("class/thermal/cooling_device0".into()).is_none());
961 assert!(
962 registry
963 .objects
964 .root
965 .lookup("devices/virtual/thermal/cooling_device0".into())
966 .is_none()
967 );
968 })
969 .await;
970 }
971
972 #[::fuchsia::test]
973 async fn registry_remove_minor_device() {
974 spawn_kernel_and_run(async |current_task| {
975 let kernel = current_task.kernel();
976 let registry = &kernel.device_registry;
977
978 let dev = registry
979 .register_misc_device(kernel, "test_misc".into(), simple_device_ops::<DevNull>)
980 .expect("register misc device");
981 let devt = dev.metadata.as_ref().expect("metadata").devt;
982
983 assert!(registry.get_device(devt, DeviceMode::Char).is_ok());
984
985 registry.remove_device(¤t_task, dev);
986
987 assert_eq!(registry.get_device(devt, DeviceMode::Char).map(|_| ()), error!(ENODEV));
988 })
989 .await;
990 }
991}