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::{
10 CurrentTask, CurrentTaskAndLocked, Kernel, KernelOrTask, SimpleWaiter, register_delayed_release,
11};
12use crate::vfs::pseudo::simple_directory::SimpleDirectoryMutator;
13use crate::vfs::{FileOps, FsStr, FsString, NamespaceNode};
14use starnix_lifecycle::{ObjectReleaser, ReleaserAction};
15use starnix_logging::log_error;
16use starnix_sync::{InterruptibleEvent, LockBefore, LockEqualOrBefore, OrderedMutex};
17use starnix_types::ownership::{Releasable, ReleaseGuard};
18use starnix_uapi::as_any::AsAny;
19use starnix_uapi::device_type::{
20 DYN_MAJOR_RANGE, DeviceType, MISC_DYNANIC_MINOR_RANGE, MISC_MAJOR,
21};
22use starnix_uapi::error;
23use starnix_uapi::errors::Errno;
24use starnix_uapi::open_flags::OpenFlags;
25
26use starnix_sync::{FileOpsCore, Locked, MappedMutexGuard, MutexGuard};
27use std::collections::btree_map::{BTreeMap, Entry};
28use std::ops::{Deref, Range};
29use std::sync::Arc;
30
31use dyn_clone::{DynClone, clone_trait_object};
32
33const CHRDEV_MINOR_MAX: u32 = 256;
34const BLKDEV_MINOR_MAX: u32 = 2u32.pow(20);
35
36#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
38pub enum DeviceMode {
39 Char,
41
42 Block,
44}
45
46impl DeviceMode {
47 fn minor_count(&self) -> u32 {
49 match self {
50 Self::Char => CHRDEV_MINOR_MAX,
51 Self::Block => BLKDEV_MINOR_MAX,
52 }
53 }
54
55 pub fn minor_range(&self) -> Range<u32> {
57 0..self.minor_count()
58 }
59}
60
61pub trait DeviceOps: DynClone + Send + Sync + AsAny + 'static {
62 fn open(
67 &self,
68 locked: &mut Locked<FileOpsCore>,
69 current_task: &CurrentTask,
70 device_type: DeviceType,
71 node: &NamespaceNode,
72 flags: OpenFlags,
73 ) -> Result<Box<dyn FileOps>, Errno>;
74}
75
76clone_trait_object!(DeviceOps);
77
78impl<F> DeviceOps for F
81where
82 F: Clone
83 + Send
84 + Sync
85 + Clone
86 + Fn(
87 &mut Locked<FileOpsCore>,
88 &CurrentTask,
89 DeviceType,
90 &NamespaceNode,
91 OpenFlags,
92 ) -> Result<Box<dyn FileOps>, Errno>
93 + 'static,
94{
95 fn open(
96 &self,
97 locked: &mut Locked<FileOpsCore>,
98 current_task: &CurrentTask,
99 id: DeviceType,
100 node: &NamespaceNode,
101 flags: OpenFlags,
102 ) -> Result<Box<dyn FileOps>, Errno> {
103 self(locked, current_task, id, node, flags)
104 }
105}
106
107pub fn simple_device_ops<T: Default + FileOps + 'static>(
109 _locked: &mut Locked<FileOpsCore>,
110 _current_task: &CurrentTask,
111 _id: DeviceType,
112 _node: &NamespaceNode,
113 _flags: OpenFlags,
114) -> Result<Box<dyn FileOps>, Errno> {
115 Ok(Box::new(T::default()))
116}
117
118pub type DeviceListenerKey = u64;
121
122pub trait DeviceListener: Send + Sync {
124 fn on_device_event(&self, action: UEventAction, device: Device, context: UEventContext);
125}
126
127pub struct DeviceOpsWrapper(Box<dyn DeviceOps>);
128impl ReleaserAction<DeviceOpsWrapper> for DeviceOpsWrapper {
129 fn release(device_ops: ReleaseGuard<DeviceOpsWrapper>) {
130 register_delayed_release(device_ops);
131 }
132}
133impl Deref for DeviceOpsWrapper {
134 type Target = dyn DeviceOps;
135 fn deref(&self) -> &Self::Target {
136 self.0.deref()
137 }
138}
139pub type DeviceReleaser = ObjectReleaser<DeviceOpsWrapper, DeviceOpsWrapper>;
140pub type DeviceHandle = Arc<DeviceReleaser>;
141impl Releasable for DeviceOpsWrapper {
142 type Context<'a> = CurrentTaskAndLocked<'a>;
143
144 fn release<'a>(self, _context: CurrentTaskAndLocked<'a>) {}
145}
146
147struct DeviceEntry {
149 name: FsString,
153
154 ops: DeviceHandle,
156}
157
158impl DeviceEntry {
159 fn new(name: FsString, ops: impl DeviceOps) -> Self {
160 Self { name, ops: Arc::new(DeviceOpsWrapper(Box::new(ops)).into()) }
161 }
162}
163
164#[derive(Default)]
168struct RegisteredDevices {
169 majors: BTreeMap<u32, DeviceEntry>,
178
179 minors: BTreeMap<DeviceType, DeviceEntry>,
183}
184
185impl RegisteredDevices {
186 fn register_major(&mut self, major: u32, entry: DeviceEntry) -> Result<(), Errno> {
190 if let Entry::Vacant(slot) = self.majors.entry(major) {
191 slot.insert(entry);
192 Ok(())
193 } else {
194 error!(EINVAL)
195 }
196 }
197
198 fn register_minor(&mut self, device_type: DeviceType, entry: DeviceEntry) {
202 self.minors.insert(device_type, entry);
203 }
204
205 fn get(&self, device_type: DeviceType) -> Result<DeviceHandle, Errno> {
212 if let Some(major_device) = self.majors.get(&device_type.major()) {
213 Ok(Arc::clone(&major_device.ops))
214 } else if let Some(minor_device) = self.minors.get(&device_type) {
215 Ok(Arc::clone(&minor_device.ops))
216 } else {
217 error!(ENODEV)
218 }
219 }
220
221 fn list_major_devices(&self) -> Vec<(u32, FsString)> {
223 self.majors.iter().map(|(major, entry)| (*major, entry.name.clone())).collect()
224 }
225
226 fn list_minor_devices(&self, range: Range<DeviceType>) -> Vec<(DeviceType, FsString)> {
228 self.minors
229 .range(range)
230 .map(|(device_type, entry)| (device_type.clone(), entry.name.clone()))
231 .collect()
232 }
233}
234
235pub struct DeviceRegistry {
244 pub objects: KObjectStore,
246
247 state: OrderedMutex<DeviceRegistryState, starnix_sync::DeviceRegistryState>,
249}
250struct DeviceRegistryState {
251 char_devices: RegisteredDevices,
253
254 block_devices: RegisteredDevices,
256
257 misc_chardev_allocator: DeviceTypeAllocator,
261
262 dyn_chardev_allocator: DeviceTypeAllocator,
266
267 next_anon_minor: u32,
269
270 listeners: BTreeMap<u64, Box<dyn DeviceListener>>,
275
276 next_listener_id: u64,
278
279 next_event_id: u64,
281}
282
283impl DeviceRegistry {
284 fn notify_device<L>(
286 &self,
287 locked: &mut Locked<L>,
288 kernel: &Kernel,
289 device: Device,
290 event: Option<Arc<InterruptibleEvent>>,
291 ) where
292 L: LockEqualOrBefore<FileOpsCore>,
293 {
294 if let Some(metadata) = &device.metadata {
295 devtmpfs_create_device(kernel, metadata.clone(), event);
296 self.dispatch_uevent(locked, UEventAction::Add, device);
297 }
298 }
299
300 pub fn register_device<'a, L>(
347 &self,
348 locked: &mut Locked<L>,
349 kernel_or_task: impl KernelOrTask<'a>,
350 name: &FsStr,
351 metadata: DeviceMetadata,
352 class: Class,
353 dev_ops: impl DeviceOps,
354 ) -> Result<Device, Errno>
355 where
356 L: LockEqualOrBefore<FileOpsCore>,
357 {
358 self.register_device_with_dir(
359 locked,
360 kernel_or_task,
361 name,
362 metadata,
363 class,
364 build_device_directory,
365 dev_ops,
366 )
367 }
368
369 pub fn register_device_with_dir<'a, L>(
373 &self,
374 locked: &mut Locked<L>,
375 kernel_or_task: impl KernelOrTask<'a>,
376 name: &FsStr,
377 metadata: DeviceMetadata,
378 class: Class,
379 build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
380 dev_ops: impl DeviceOps,
381 ) -> Result<Device, Errno>
382 where
383 L: LockEqualOrBefore<FileOpsCore>,
384 {
385 let locked = locked.cast_locked::<FileOpsCore>();
386 let entry = DeviceEntry::new(name.into(), dev_ops);
387 self.devices(locked, metadata.mode).register_minor(metadata.device_type, entry);
388 self.add_device(locked, kernel_or_task, name, metadata, class, build_directory)
389 }
390
391 pub fn register_misc_device<'a, L>(
399 &self,
400 locked: &mut Locked<L>,
401 kernel_or_task: impl KernelOrTask<'a>,
402 name: &FsStr,
403 dev_ops: impl DeviceOps,
404 ) -> Result<Device, Errno>
405 where
406 L: LockEqualOrBefore<FileOpsCore>,
407 {
408 let device_type =
409 self.state.lock(locked.cast_locked()).misc_chardev_allocator.allocate()?;
410 let metadata = DeviceMetadata::new(name.into(), device_type, DeviceMode::Char);
411 Ok(self.register_device(
412 locked,
413 kernel_or_task,
414 name,
415 metadata,
416 self.objects.misc_class(),
417 dev_ops,
418 )?)
419 }
420
421 pub fn register_dyn_device<'a, L>(
432 &self,
433 locked: &mut Locked<L>,
434 kernel_or_task: impl KernelOrTask<'a>,
435 name: &FsStr,
436 class: Class,
437 dev_ops: impl DeviceOps,
438 ) -> Result<Device, Errno>
439 where
440 L: LockEqualOrBefore<FileOpsCore>,
441 {
442 self.register_dyn_device_with_dir(
443 locked,
444 kernel_or_task,
445 name,
446 class,
447 build_device_directory,
448 dev_ops,
449 )
450 }
451
452 pub fn register_dyn_device_with_dir<'a, L>(
456 &self,
457 locked: &mut Locked<L>,
458 kernel_or_task: impl KernelOrTask<'a>,
459 name: &FsStr,
460 class: Class,
461 build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
462 dev_ops: impl DeviceOps,
463 ) -> Result<Device, Errno>
464 where
465 L: LockEqualOrBefore<FileOpsCore>,
466 {
467 self.register_dyn_device_with_devname(
468 locked,
469 kernel_or_task,
470 name,
471 name,
472 class,
473 build_directory,
474 dev_ops,
475 )
476 }
477
478 pub fn register_dyn_device_with_devname<'a, L>(
489 &self,
490 locked: &mut Locked<L>,
491 kernel_or_task: impl KernelOrTask<'a>,
492 name: &FsStr,
493 devname: &FsStr,
494 class: Class,
495 build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
496 dev_ops: impl DeviceOps,
497 ) -> Result<Device, Errno>
498 where
499 L: LockEqualOrBefore<FileOpsCore>,
500 {
501 let device_type = self.state.lock(locked.cast_locked()).dyn_chardev_allocator.allocate()?;
502 let metadata = DeviceMetadata::new(devname.into(), device_type, DeviceMode::Char);
503 Ok(self.register_device_with_dir(
504 locked,
505 kernel_or_task,
506 name,
507 metadata,
508 class,
509 build_directory,
510 dev_ops,
511 )?)
512 }
513
514 pub fn register_silent_dyn_device<'a, L>(
521 &self,
522 locked: &mut Locked<L>,
523 name: &FsStr,
524 dev_ops: impl DeviceOps,
525 ) -> Result<DeviceMetadata, Errno>
526 where
527 L: LockEqualOrBefore<FileOpsCore>,
528 {
529 let locked = locked.cast_locked::<FileOpsCore>();
530 let device_type = self.state.lock(locked).dyn_chardev_allocator.allocate()?;
531 let metadata = DeviceMetadata::new(name.into(), device_type, DeviceMode::Char);
532 let entry = DeviceEntry::new(name.into(), dev_ops);
533 self.devices(locked, metadata.mode).register_minor(metadata.device_type, entry);
534 Ok(metadata)
535 }
536
537 pub fn add_device<'a, L>(
545 &self,
546 locked: &mut Locked<L>,
547 kernel_or_task: impl KernelOrTask<'a>,
548 name: &FsStr,
549 metadata: DeviceMetadata,
550 class: Class,
551 build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
552 ) -> Result<Device, Errno>
553 where
554 L: LockEqualOrBefore<FileOpsCore>,
555 {
556 self.devices(locked.cast_locked(), metadata.mode)
557 .get(metadata.device_type)
558 .expect("device is registered");
559 let device = self.objects.create_device(name, Some(metadata), class, build_directory);
560
561 block_task_until(kernel_or_task, |kernel, event| {
562 Ok(self.notify_device(locked, kernel, device.clone(), event))
563 })?;
564 Ok(device)
565 }
566
567 pub fn add_net_device(&self, name: &FsStr) -> Device {
579 self.objects.create_device(name, None, self.objects.net_class(), build_device_directory)
580 }
581
582 pub fn remove_net_device(&self, device: Device) {
586 assert!(device.metadata.is_none());
587 self.objects.remove(&device);
588 }
589
590 pub fn add_numberless_device<L>(
597 &self,
598 _locked: &mut Locked<L>,
599 name: &FsStr,
600 class: Class,
601 build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
602 ) -> Device
603 where
604 L: LockEqualOrBefore<FileOpsCore>,
605 {
606 self.objects.create_device(name, None, class, build_directory)
607 }
608
609 pub fn remove_device<L>(
614 &self,
615 locked: &mut Locked<L>,
616 current_task: &CurrentTask,
617 device: Device,
618 ) where
619 L: LockEqualOrBefore<FileOpsCore>,
620 {
621 if let Some(metadata) = &device.metadata {
622 self.dispatch_uevent(locked, UEventAction::Remove, device.clone());
623
624 if let Err(err) = devtmpfs_remove_path(locked, current_task, metadata.devname.as_ref())
625 {
626 log_error!("Cannot remove device {:?} ({:?})", device, err);
627 }
628 }
629
630 self.objects.remove(&device);
631 }
632
633 pub fn list_major_devices<L>(
636 &self,
637 locked: &mut Locked<L>,
638 mode: DeviceMode,
639 ) -> Vec<(u32, FsString)>
640 where
641 L: LockBefore<starnix_sync::DeviceRegistryState>,
642 {
643 self.devices(locked, mode).list_major_devices()
644 }
645
646 pub fn list_minor_devices<L>(
648 &self,
649 locked: &mut Locked<L>,
650 mode: DeviceMode,
651 range: Range<DeviceType>,
652 ) -> Vec<(DeviceType, FsString)>
653 where
654 L: LockBefore<starnix_sync::DeviceRegistryState>,
655 {
656 self.devices(locked, mode).list_minor_devices(range)
657 }
658
659 fn devices<'a, L>(
661 &'a self,
662 locked: &'a mut Locked<L>,
663 mode: DeviceMode,
664 ) -> MappedMutexGuard<'a, RegisteredDevices>
665 where
666 L: LockBefore<starnix_sync::DeviceRegistryState>,
667 {
668 MutexGuard::map(self.state.lock(locked), |state| match mode {
669 DeviceMode::Char => &mut state.char_devices,
670 DeviceMode::Block => &mut state.block_devices,
671 })
672 }
673
674 pub fn register_major<'a, L>(
680 &self,
681 locked: &mut Locked<L>,
682 name: FsString,
683 mode: DeviceMode,
684 major: u32,
685 dev_ops: impl DeviceOps,
686 ) -> Result<(), Errno>
687 where
688 L: LockEqualOrBefore<FileOpsCore>,
689 {
690 let locked = locked.cast_locked::<FileOpsCore>();
691 let entry = DeviceEntry::new(name, dev_ops);
692 self.devices(locked, mode).register_major(major, entry)
693 }
694
695 pub fn next_anonymous_dev_id<'a, L>(&self, locked: &mut Locked<L>) -> DeviceType
697 where
698 L: LockEqualOrBefore<FileOpsCore>,
699 {
700 let locked = locked.cast_locked::<FileOpsCore>();
701 let mut state = self.state.lock(locked);
702 let id = DeviceType::new(0, state.next_anon_minor);
703 state.next_anon_minor += 1;
704 id
705 }
706
707 pub fn register_listener<'a, L>(
711 &self,
712 locked: &mut Locked<L>,
713 listener: impl DeviceListener + 'static,
714 ) -> DeviceListenerKey
715 where
716 L: LockEqualOrBefore<FileOpsCore>,
717 {
718 let mut state = self.state.lock(locked.cast_locked());
719 let key = state.next_listener_id;
720 state.next_listener_id += 1;
721 state.listeners.insert(key, Box::new(listener));
722 key
723 }
724
725 pub fn unregister_listener<'a, L>(&self, locked: &mut Locked<L>, key: &DeviceListenerKey)
727 where
728 L: LockEqualOrBefore<FileOpsCore>,
729 {
730 self.state.lock(locked.cast_locked()).listeners.remove(key);
731 }
732
733 pub fn dispatch_uevent<'a, L>(
735 &self,
736 locked: &mut Locked<L>,
737 action: UEventAction,
738 device: Device,
739 ) where
740 L: LockEqualOrBefore<FileOpsCore>,
741 {
742 let mut state = self.state.lock(locked.cast_locked());
743 let event_id = state.next_event_id;
744 state.next_event_id += 1;
745 let context = UEventContext { seqnum: event_id };
746 for listener in state.listeners.values() {
747 listener.on_device_event(action, device.clone(), context);
748 }
749 }
750
751 pub fn open_device<L>(
755 &self,
756 locked: &mut Locked<L>,
757 current_task: &CurrentTask,
758 node: &NamespaceNode,
759 flags: OpenFlags,
760 device_type: DeviceType,
761 mode: DeviceMode,
762 ) -> Result<Box<dyn FileOps>, Errno>
763 where
764 L: LockEqualOrBefore<FileOpsCore>,
765 {
766 let locked = locked.cast_locked::<FileOpsCore>();
767 let dev_ops = self.devices(locked, mode).get(device_type)?;
768 dev_ops.open(locked, current_task, device_type, node, flags)
769 }
770
771 pub fn get_device<L>(
772 &self,
773 locked: &mut Locked<L>,
774 device_type: DeviceType,
775 mode: DeviceMode,
776 ) -> Result<DeviceHandle, Errno>
777 where
778 L: LockBefore<starnix_sync::DeviceRegistryState>,
779 {
780 self.devices(locked, mode).get(device_type)
781 }
782}
783
784impl Default for DeviceRegistry {
785 fn default() -> Self {
786 let misc_available = vec![DeviceType::new_range(MISC_MAJOR, MISC_DYNANIC_MINOR_RANGE)];
787 let dyn_available = DYN_MAJOR_RANGE
788 .map(|major| DeviceType::new_range(major, DeviceMode::Char.minor_range()))
789 .rev()
790 .collect();
791 let state = DeviceRegistryState {
792 char_devices: Default::default(),
793 block_devices: Default::default(),
794 misc_chardev_allocator: DeviceTypeAllocator::new(misc_available),
795 dyn_chardev_allocator: DeviceTypeAllocator::new(dyn_available),
796 next_anon_minor: 1,
797 listeners: Default::default(),
798 next_listener_id: 0,
799 next_event_id: 0,
800 };
801 Self { objects: Default::default(), state: OrderedMutex::new(state) }
802 }
803}
804
805struct DeviceTypeAllocator {
807 freelist: Vec<Range<DeviceType>>,
811}
812
813impl DeviceTypeAllocator {
814 fn new(mut available: Vec<Range<DeviceType>>) -> Self {
818 available.reverse();
819 Self { freelist: available }
820 }
821
822 fn allocate(&mut self) -> Result<DeviceType, Errno> {
826 let Some(range) = self.freelist.pop() else {
827 return error!(ENOMEM);
828 };
829 let allocated = range.start;
830 if let Some(next) = allocated.next_minor() {
831 if next < range.end {
832 self.freelist.push(next..range.end);
833 }
834 }
835 Ok(allocated)
836 }
837}
838
839fn block_task_until<'a, T, F>(kernel_or_task: impl KernelOrTask<'a>, f: F) -> Result<T, Errno>
842where
843 F: FnOnce(&Kernel, Option<Arc<InterruptibleEvent>>) -> Result<T, Errno>,
844{
845 let kernel = kernel_or_task.kernel();
846 match kernel_or_task.maybe_task() {
847 None => f(kernel, None),
848 Some(task) => {
849 let event = InterruptibleEvent::new();
850 let (_waiter, guard) = SimpleWaiter::new(&event);
851 let result = f(kernel, Some(event.clone()))?;
852 task.block_until(guard, zx::MonotonicInstant::INFINITE)?;
853 Ok(result)
854 }
855 }
856}
857
858#[cfg(test)]
859mod tests {
860 use super::*;
861 use crate::device::mem::DevNull;
862 use crate::testing::*;
863 use crate::vfs::*;
864 use starnix_uapi::device_type::{INPUT_MAJOR, MEM_MAJOR};
865
866 #[::fuchsia::test]
867 async fn registry_fails_to_add_duplicate_device() {
868 spawn_kernel_and_run(async |locked, _current_task| {
869 let registry = DeviceRegistry::default();
870 registry
871 .register_major(
872 locked,
873 "mem".into(),
874 DeviceMode::Char,
875 MEM_MAJOR,
876 simple_device_ops::<DevNull>,
877 )
878 .expect("registers once");
879 registry
880 .register_major(
881 locked,
882 "random".into(),
883 DeviceMode::Char,
884 123,
885 simple_device_ops::<DevNull>,
886 )
887 .expect("registers unique");
888 registry
889 .register_major(
890 locked,
891 "mem".into(),
892 DeviceMode::Char,
893 MEM_MAJOR,
894 simple_device_ops::<DevNull>,
895 )
896 .expect_err("fail to register duplicate");
897 })
898 .await;
899 }
900
901 #[::fuchsia::test]
902 async fn registry_opens_device() {
903 spawn_kernel_and_run(async |locked, current_task| {
904 let kernel = current_task.kernel();
905 let registry = DeviceRegistry::default();
906 registry
907 .register_major(
908 locked,
909 "mem".into(),
910 DeviceMode::Char,
911 MEM_MAJOR,
912 simple_device_ops::<DevNull>,
913 )
914 .expect("registers unique");
915
916 let fs = create_testfs(locked, &kernel);
917 let node = create_namespace_node_for_testing(&fs, PanickingFsNode);
918
919 assert!(
921 registry
922 .open_device(
923 locked,
924 ¤t_task,
925 &node,
926 OpenFlags::RDONLY,
927 DeviceType::NONE,
928 DeviceMode::Char
929 )
930 .is_err()
931 );
932
933 assert!(
935 registry
936 .open_device(
937 locked,
938 ¤t_task,
939 &node,
940 OpenFlags::RDONLY,
941 DeviceType::NULL,
942 DeviceMode::Block
943 )
944 .is_err()
945 );
946
947 let _ = registry
949 .open_device(
950 locked,
951 ¤t_task,
952 &node,
953 OpenFlags::RDONLY,
954 DeviceType::NULL,
955 DeviceMode::Char,
956 )
957 .expect("opens device");
958 })
959 .await;
960 }
961
962 #[::fuchsia::test]
963 async fn registry_dynamic_misc() {
964 spawn_kernel_and_run(async |locked, current_task| {
965 let kernel = current_task.kernel();
966 fn create_test_device(
967 _locked: &mut Locked<FileOpsCore>,
968 _current_task: &CurrentTask,
969 _id: DeviceType,
970 _node: &NamespaceNode,
971 _flags: OpenFlags,
972 ) -> Result<Box<dyn FileOps>, Errno> {
973 Ok(Box::new(PanickingFile))
974 }
975
976 let registry = &kernel.device_registry;
977 let device = registry
978 .register_dyn_device(
979 locked,
980 &*current_task,
981 "test-device".into(),
982 registry.objects.virtual_block_class(),
983 create_test_device,
984 )
985 .unwrap();
986 let device_type = device.metadata.expect("has metadata").device_type;
987 assert!(DYN_MAJOR_RANGE.contains(&device_type.major()));
988
989 let fs = create_testfs(locked, &kernel);
990 let node = create_namespace_node_for_testing(&fs, PanickingFsNode);
991 let _ = registry
992 .open_device(
993 locked,
994 ¤t_task,
995 &node,
996 OpenFlags::RDONLY,
997 device_type,
998 DeviceMode::Char,
999 )
1000 .expect("opens device");
1001 })
1002 .await;
1003 }
1004
1005 #[::fuchsia::test]
1006 async fn registery_add_class() {
1007 spawn_kernel_and_run(async |locked, current_task| {
1008 let kernel = current_task.kernel();
1009 let registry = &kernel.device_registry;
1010 registry
1011 .register_major(
1012 locked,
1013 "input".into(),
1014 DeviceMode::Char,
1015 INPUT_MAJOR,
1016 simple_device_ops::<DevNull>,
1017 )
1018 .expect("can register input");
1019
1020 let input_class = registry
1021 .objects
1022 .get_or_create_class("input".into(), registry.objects.virtual_bus());
1023 registry
1024 .add_device(
1025 locked,
1026 &*current_task,
1027 "mouse".into(),
1028 DeviceMetadata::new(
1029 "mouse".into(),
1030 DeviceType::new(INPUT_MAJOR, 0),
1031 DeviceMode::Char,
1032 ),
1033 input_class,
1034 build_device_directory,
1035 )
1036 .expect("add_device");
1037
1038 assert!(registry.objects.root.lookup("class/input/mouse".into()).is_some());
1039 })
1040 .await;
1041 }
1042
1043 #[::fuchsia::test]
1044 async fn registry_add_bus() {
1045 spawn_kernel_and_run(async |locked, current_task| {
1046 let kernel = current_task.kernel();
1047 let registry = &kernel.device_registry;
1048 registry
1049 .register_major(
1050 locked,
1051 "input".into(),
1052 DeviceMode::Char,
1053 INPUT_MAJOR,
1054 simple_device_ops::<DevNull>,
1055 )
1056 .expect("can register input");
1057
1058 let bus = registry.objects.get_or_create_bus("my-bus".into());
1059 let class = registry.objects.get_or_create_class("my-class".into(), bus);
1060 registry
1061 .add_device(
1062 locked,
1063 &*current_task,
1064 "my-device".into(),
1065 DeviceMetadata::new(
1066 "my-device".into(),
1067 DeviceType::new(INPUT_MAJOR, 0),
1068 DeviceMode::Char,
1069 ),
1070 class,
1071 build_device_directory,
1072 )
1073 .expect("add_device");
1074 assert!(registry.objects.root.lookup("bus/my-bus".into()).is_some());
1075 assert!(registry.objects.root.lookup("devices/my-bus/my-class".into()).is_some());
1076 assert!(
1077 registry.objects.root.lookup("devices/my-bus/my-class/my-device".into()).is_some()
1078 );
1079 })
1080 .await;
1081 }
1082
1083 #[::fuchsia::test]
1084 async fn registry_remove_device() {
1085 spawn_kernel_and_run(async |locked, current_task| {
1086 let kernel = current_task.kernel();
1087 let registry = &kernel.device_registry;
1088 registry
1089 .register_major(
1090 locked,
1091 "input".into(),
1092 DeviceMode::Char,
1093 INPUT_MAJOR,
1094 simple_device_ops::<DevNull>,
1095 )
1096 .expect("can register input");
1097
1098 let pci_bus = registry.objects.get_or_create_bus("pci".into());
1099 let input_class = registry.objects.get_or_create_class("input".into(), pci_bus);
1100 let mouse_dev = registry
1101 .add_device(
1102 locked,
1103 &*current_task,
1104 "mouse".into(),
1105 DeviceMetadata::new(
1106 "mouse".into(),
1107 DeviceType::new(INPUT_MAJOR, 0),
1108 DeviceMode::Char,
1109 ),
1110 input_class.clone(),
1111 build_device_directory,
1112 )
1113 .expect("add_device");
1114
1115 assert!(registry.objects.root.lookup("bus/pci/devices/mouse".into()).is_some());
1116 assert!(registry.objects.root.lookup("devices/pci/input/mouse".into()).is_some());
1117 assert!(registry.objects.root.lookup("class/input/mouse".into()).is_some());
1118
1119 registry.remove_device(locked, ¤t_task, mouse_dev);
1120
1121 assert!(registry.objects.root.lookup("bus/pci/devices/mouse".into()).is_none());
1122 assert!(registry.objects.root.lookup("devices/pci/input/mouse".into()).is_none());
1123 assert!(registry.objects.root.lookup("class/input/mouse".into()).is_none());
1124 })
1125 .await;
1126 }
1127
1128 #[::fuchsia::test]
1129 async fn registry_add_and_remove_numberless_device() {
1130 spawn_kernel_and_run(async |locked, current_task| {
1131 let kernel = current_task.kernel();
1132 let registry = &kernel.device_registry;
1133
1134 let cooling_device = registry.add_numberless_device(
1135 locked,
1136 "cooling_device0".into(),
1137 registry.objects.virtual_thermal_class(),
1138 build_device_directory,
1139 );
1140
1141 assert!(registry.objects.root.lookup("class/thermal/cooling_device0".into()).is_some());
1142 assert!(
1143 registry
1144 .objects
1145 .root
1146 .lookup("devices/virtual/thermal/cooling_device0".into())
1147 .is_some()
1148 );
1149
1150 registry.remove_device(locked, ¤t_task, cooling_device);
1151
1152 assert!(registry.objects.root.lookup("class/thermal/cooling_device0".into()).is_none());
1153 assert!(
1154 registry
1155 .objects
1156 .root
1157 .lookup("devices/virtual/thermal/cooling_device0".into())
1158 .is_none()
1159 );
1160 })
1161 .await;
1162 }
1163}