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, 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 entry = DeviceEntry::new(name.into(), dev_ops);
386 self.devices(locked, metadata.mode).register_minor(metadata.device_type, entry);
387 self.add_device(locked, kernel_or_task, name, metadata, class, build_directory)
388 }
389
390 pub fn register_misc_device<'a, L>(
398 &self,
399 locked: &mut Locked<L>,
400 kernel_or_task: impl KernelOrTask<'a>,
401 name: &FsStr,
402 dev_ops: impl DeviceOps,
403 ) -> Result<Device, Errno>
404 where
405 L: LockEqualOrBefore<FileOpsCore>,
406 {
407 let device_type =
408 self.state.lock(locked.cast_locked()).misc_chardev_allocator.allocate()?;
409 let metadata = DeviceMetadata::new(name.into(), device_type, DeviceMode::Char);
410 Ok(self.register_device(
411 locked,
412 kernel_or_task,
413 name,
414 metadata,
415 self.objects.misc_class(),
416 dev_ops,
417 )?)
418 }
419
420 pub fn register_dyn_device<'a, L>(
431 &self,
432 locked: &mut Locked<L>,
433 kernel_or_task: impl KernelOrTask<'a>,
434 name: &FsStr,
435 class: Class,
436 dev_ops: impl DeviceOps,
437 ) -> Result<Device, Errno>
438 where
439 L: LockEqualOrBefore<FileOpsCore>,
440 {
441 self.register_dyn_device_with_dir(
442 locked,
443 kernel_or_task,
444 name,
445 class,
446 build_device_directory,
447 dev_ops,
448 )
449 }
450
451 pub fn register_dyn_device_with_dir<'a, L>(
455 &self,
456 locked: &mut Locked<L>,
457 kernel_or_task: impl KernelOrTask<'a>,
458 name: &FsStr,
459 class: Class,
460 build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
461 dev_ops: impl DeviceOps,
462 ) -> Result<Device, Errno>
463 where
464 L: LockEqualOrBefore<FileOpsCore>,
465 {
466 self.register_dyn_device_with_devname(
467 locked,
468 kernel_or_task,
469 name,
470 name,
471 class,
472 build_directory,
473 dev_ops,
474 )
475 }
476
477 pub fn register_dyn_device_with_devname<'a, L>(
488 &self,
489 locked: &mut Locked<L>,
490 kernel_or_task: impl KernelOrTask<'a>,
491 name: &FsStr,
492 devname: &FsStr,
493 class: Class,
494 build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
495 dev_ops: impl DeviceOps,
496 ) -> Result<Device, Errno>
497 where
498 L: LockEqualOrBefore<FileOpsCore>,
499 {
500 let device_type = self.state.lock(locked.cast_locked()).dyn_chardev_allocator.allocate()?;
501 let metadata = DeviceMetadata::new(devname.into(), device_type, DeviceMode::Char);
502 Ok(self.register_device_with_dir(
503 locked,
504 kernel_or_task,
505 name,
506 metadata,
507 class,
508 build_directory,
509 dev_ops,
510 )?)
511 }
512
513 pub fn register_silent_dyn_device<'a, L>(
520 &self,
521 locked: &mut Locked<L>,
522 name: &FsStr,
523 dev_ops: impl DeviceOps,
524 ) -> Result<DeviceMetadata, Errno>
525 where
526 L: LockEqualOrBefore<FileOpsCore>,
527 {
528 let device_type = self.state.lock(locked.cast_locked()).dyn_chardev_allocator.allocate()?;
529 let metadata = DeviceMetadata::new(name.into(), device_type, DeviceMode::Char);
530 let entry = DeviceEntry::new(name.into(), dev_ops);
531 self.devices(locked, metadata.mode).register_minor(metadata.device_type, entry);
532 Ok(metadata)
533 }
534
535 pub fn add_device<'a, L>(
543 &self,
544 locked: &mut Locked<L>,
545 kernel_or_task: impl KernelOrTask<'a>,
546 name: &FsStr,
547 metadata: DeviceMetadata,
548 class: Class,
549 build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
550 ) -> Result<Device, Errno>
551 where
552 L: LockEqualOrBefore<FileOpsCore>,
553 {
554 self.devices(locked, metadata.mode)
555 .get(metadata.device_type)
556 .expect("device is registered");
557 let device = self.objects.create_device(name, Some(metadata), class, build_directory);
558
559 block_task_until(kernel_or_task, |kernel, event| {
560 Ok(self.notify_device(locked, kernel, device.clone(), event))
561 })?;
562 Ok(device)
563 }
564
565 pub fn add_net_device(&self, name: &FsStr) -> Device {
577 self.objects.create_device(name, None, self.objects.net_class(), build_device_directory)
578 }
579
580 pub fn remove_net_device(&self, device: Device) {
584 assert!(device.metadata.is_none());
585 self.objects.remove(&device);
586 }
587
588 pub fn add_numberless_device<L>(
595 &self,
596 _locked: &mut Locked<L>,
597 name: &FsStr,
598 class: Class,
599 build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
600 ) -> Device
601 where
602 L: LockEqualOrBefore<FileOpsCore>,
603 {
604 self.objects.create_device(name, None, class, build_directory)
605 }
606
607 pub fn remove_device<L>(
612 &self,
613 locked: &mut Locked<L>,
614 current_task: &CurrentTask,
615 device: Device,
616 ) where
617 L: LockEqualOrBefore<FileOpsCore>,
618 {
619 if let Some(metadata) = &device.metadata {
620 self.dispatch_uevent(locked, UEventAction::Remove, device.clone());
621
622 if let Err(err) = devtmpfs_remove_path(locked, current_task, metadata.devname.as_ref())
623 {
624 log_error!("Cannot remove device {:?} ({:?})", device, err);
625 }
626 }
627
628 self.objects.remove(&device);
629 }
630
631 pub fn list_major_devices<L>(
634 &self,
635 locked: &mut Locked<L>,
636 mode: DeviceMode,
637 ) -> Vec<(u32, FsString)>
638 where
639 L: LockEqualOrBefore<FileOpsCore>,
640 {
641 self.devices(locked, mode).list_major_devices()
642 }
643
644 pub fn list_minor_devices<L>(
646 &self,
647 locked: &mut Locked<L>,
648 mode: DeviceMode,
649 range: Range<DeviceType>,
650 ) -> Vec<(DeviceType, FsString)>
651 where
652 L: LockEqualOrBefore<FileOpsCore>,
653 {
654 self.devices(locked, mode).list_minor_devices(range)
655 }
656
657 fn devices<'a, L>(
659 &'a self,
660 locked: &'a mut Locked<L>,
661 mode: DeviceMode,
662 ) -> MappedMutexGuard<'a, RegisteredDevices>
663 where
664 L: LockEqualOrBefore<FileOpsCore>,
665 {
666 MutexGuard::map(self.state.lock(locked.cast_locked()), |state| match mode {
667 DeviceMode::Char => &mut state.char_devices,
668 DeviceMode::Block => &mut state.block_devices,
669 })
670 }
671
672 pub fn register_major<'a, L>(
678 &self,
679 locked: &mut Locked<L>,
680 name: FsString,
681 mode: DeviceMode,
682 major: u32,
683 dev_ops: impl DeviceOps,
684 ) -> Result<(), Errno>
685 where
686 L: LockEqualOrBefore<FileOpsCore>,
687 {
688 let entry = DeviceEntry::new(name, dev_ops);
689 self.devices(locked, mode).register_major(major, entry)
690 }
691
692 pub fn next_anonymous_dev_id<'a, L>(&self, locked: &mut Locked<L>) -> DeviceType
694 where
695 L: LockEqualOrBefore<FileOpsCore>,
696 {
697 let mut state = self.state.lock(locked.cast_locked());
698 let id = DeviceType::new(0, state.next_anon_minor);
699 state.next_anon_minor += 1;
700 id
701 }
702
703 pub fn register_listener<'a, L>(
707 &self,
708 locked: &mut Locked<L>,
709 listener: impl DeviceListener + 'static,
710 ) -> DeviceListenerKey
711 where
712 L: LockEqualOrBefore<FileOpsCore>,
713 {
714 let mut state = self.state.lock(locked.cast_locked());
715 let key = state.next_listener_id;
716 state.next_listener_id += 1;
717 state.listeners.insert(key, Box::new(listener));
718 key
719 }
720
721 pub fn unregister_listener<'a, L>(&self, locked: &mut Locked<L>, key: &DeviceListenerKey)
723 where
724 L: LockEqualOrBefore<FileOpsCore>,
725 {
726 self.state.lock(locked.cast_locked()).listeners.remove(key);
727 }
728
729 pub fn dispatch_uevent<'a, L>(
731 &self,
732 locked: &mut Locked<L>,
733 action: UEventAction,
734 device: Device,
735 ) where
736 L: LockEqualOrBefore<FileOpsCore>,
737 {
738 let mut state = self.state.lock(locked.cast_locked());
739 let event_id = state.next_event_id;
740 state.next_event_id += 1;
741 let context = UEventContext { seqnum: event_id };
742 for listener in state.listeners.values() {
743 listener.on_device_event(action, device.clone(), context);
744 }
745 }
746
747 pub fn open_device<L>(
751 &self,
752 locked: &mut Locked<L>,
753 current_task: &CurrentTask,
754 node: &NamespaceNode,
755 flags: OpenFlags,
756 device_type: DeviceType,
757 mode: DeviceMode,
758 ) -> Result<Box<dyn FileOps>, Errno>
759 where
760 L: LockEqualOrBefore<FileOpsCore>,
761 {
762 let locked = locked.cast_locked::<FileOpsCore>();
763 let dev_ops = self.devices(locked, mode).get(device_type)?;
764 dev_ops.open(locked, current_task, device_type, node, flags)
765 }
766
767 pub fn get_device<L>(
768 &self,
769 locked: &mut Locked<L>,
770 device_type: DeviceType,
771 mode: DeviceMode,
772 ) -> Result<DeviceHandle, Errno>
773 where
774 L: LockEqualOrBefore<FileOpsCore>,
775 {
776 self.devices(locked, mode).get(device_type)
777 }
778}
779
780impl Default for DeviceRegistry {
781 fn default() -> Self {
782 let misc_available = vec![DeviceType::new_range(MISC_MAJOR, MISC_DYNANIC_MINOR_RANGE)];
783 let dyn_available = DYN_MAJOR_RANGE
784 .map(|major| DeviceType::new_range(major, DeviceMode::Char.minor_range()))
785 .rev()
786 .collect();
787 let state = DeviceRegistryState {
788 char_devices: Default::default(),
789 block_devices: Default::default(),
790 misc_chardev_allocator: DeviceTypeAllocator::new(misc_available),
791 dyn_chardev_allocator: DeviceTypeAllocator::new(dyn_available),
792 next_anon_minor: 1,
793 listeners: Default::default(),
794 next_listener_id: 0,
795 next_event_id: 0,
796 };
797 Self { objects: Default::default(), state: OrderedMutex::new(state) }
798 }
799}
800
801struct DeviceTypeAllocator {
803 freelist: Vec<Range<DeviceType>>,
807}
808
809impl DeviceTypeAllocator {
810 fn new(mut available: Vec<Range<DeviceType>>) -> Self {
814 available.reverse();
815 Self { freelist: available }
816 }
817
818 fn allocate(&mut self) -> Result<DeviceType, Errno> {
822 let Some(range) = self.freelist.pop() else {
823 return error!(ENOMEM);
824 };
825 let allocated = range.start;
826 if let Some(next) = allocated.next_minor() {
827 if next < range.end {
828 self.freelist.push(next..range.end);
829 }
830 }
831 Ok(allocated)
832 }
833}
834
835fn block_task_until<'a, T, F>(kernel_or_task: impl KernelOrTask<'a>, f: F) -> Result<T, Errno>
838where
839 F: FnOnce(&Kernel, Option<Arc<InterruptibleEvent>>) -> Result<T, Errno>,
840{
841 let kernel = kernel_or_task.kernel();
842 match kernel_or_task.maybe_task() {
843 None => f(kernel, None),
844 Some(task) => {
845 let event = InterruptibleEvent::new();
846 let (_waiter, guard) = SimpleWaiter::new(&event);
847 let result = f(kernel, Some(event.clone()))?;
848 task.block_until(guard, zx::MonotonicInstant::INFINITE)?;
849 Ok(result)
850 }
851 }
852}
853
854#[cfg(test)]
855mod tests {
856 use super::*;
857 use crate::device::mem::DevNull;
858 use crate::testing::*;
859 use crate::vfs::*;
860 use starnix_uapi::device_type::{INPUT_MAJOR, MEM_MAJOR};
861
862 #[::fuchsia::test]
863 async fn registry_fails_to_add_duplicate_device() {
864 spawn_kernel_and_run(async |locked, _current_task| {
865 let registry = DeviceRegistry::default();
866 registry
867 .register_major(
868 locked,
869 "mem".into(),
870 DeviceMode::Char,
871 MEM_MAJOR,
872 simple_device_ops::<DevNull>,
873 )
874 .expect("registers once");
875 registry
876 .register_major(
877 locked,
878 "random".into(),
879 DeviceMode::Char,
880 123,
881 simple_device_ops::<DevNull>,
882 )
883 .expect("registers unique");
884 registry
885 .register_major(
886 locked,
887 "mem".into(),
888 DeviceMode::Char,
889 MEM_MAJOR,
890 simple_device_ops::<DevNull>,
891 )
892 .expect_err("fail to register duplicate");
893 })
894 .await;
895 }
896
897 #[::fuchsia::test]
898 async fn registry_opens_device() {
899 spawn_kernel_and_run(async |locked, current_task| {
900 let kernel = current_task.kernel();
901 let registry = DeviceRegistry::default();
902 registry
903 .register_major(
904 locked,
905 "mem".into(),
906 DeviceMode::Char,
907 MEM_MAJOR,
908 simple_device_ops::<DevNull>,
909 )
910 .expect("registers unique");
911
912 let fs = create_testfs(locked, &kernel);
913 let node = create_namespace_node_for_testing(&fs, PanickingFsNode);
914
915 assert!(
917 registry
918 .open_device(
919 locked,
920 ¤t_task,
921 &node,
922 OpenFlags::RDONLY,
923 DeviceType::NONE,
924 DeviceMode::Char
925 )
926 .is_err()
927 );
928
929 assert!(
931 registry
932 .open_device(
933 locked,
934 ¤t_task,
935 &node,
936 OpenFlags::RDONLY,
937 DeviceType::NULL,
938 DeviceMode::Block
939 )
940 .is_err()
941 );
942
943 let _ = registry
945 .open_device(
946 locked,
947 ¤t_task,
948 &node,
949 OpenFlags::RDONLY,
950 DeviceType::NULL,
951 DeviceMode::Char,
952 )
953 .expect("opens device");
954 })
955 .await;
956 }
957
958 #[::fuchsia::test]
959 async fn registry_dynamic_misc() {
960 spawn_kernel_and_run(async |locked, current_task| {
961 let kernel = current_task.kernel();
962 fn create_test_device(
963 _locked: &mut Locked<FileOpsCore>,
964 _current_task: &CurrentTask,
965 _id: DeviceType,
966 _node: &NamespaceNode,
967 _flags: OpenFlags,
968 ) -> Result<Box<dyn FileOps>, Errno> {
969 Ok(Box::new(PanickingFile))
970 }
971
972 let registry = &kernel.device_registry;
973 let device = registry
974 .register_dyn_device(
975 locked,
976 &*current_task,
977 "test-device".into(),
978 registry.objects.virtual_block_class(),
979 create_test_device,
980 )
981 .unwrap();
982 let device_type = device.metadata.expect("has metadata").device_type;
983 assert!(DYN_MAJOR_RANGE.contains(&device_type.major()));
984
985 let fs = create_testfs(locked, &kernel);
986 let node = create_namespace_node_for_testing(&fs, PanickingFsNode);
987 let _ = registry
988 .open_device(
989 locked,
990 ¤t_task,
991 &node,
992 OpenFlags::RDONLY,
993 device_type,
994 DeviceMode::Char,
995 )
996 .expect("opens device");
997 })
998 .await;
999 }
1000
1001 #[::fuchsia::test]
1002 async fn registery_add_class() {
1003 spawn_kernel_and_run(async |locked, current_task| {
1004 let kernel = current_task.kernel();
1005 let registry = &kernel.device_registry;
1006 registry
1007 .register_major(
1008 locked,
1009 "input".into(),
1010 DeviceMode::Char,
1011 INPUT_MAJOR,
1012 simple_device_ops::<DevNull>,
1013 )
1014 .expect("can register input");
1015
1016 let input_class = registry
1017 .objects
1018 .get_or_create_class("input".into(), registry.objects.virtual_bus());
1019 registry
1020 .add_device(
1021 locked,
1022 &*current_task,
1023 "mouse".into(),
1024 DeviceMetadata::new(
1025 "mouse".into(),
1026 DeviceType::new(INPUT_MAJOR, 0),
1027 DeviceMode::Char,
1028 ),
1029 input_class,
1030 build_device_directory,
1031 )
1032 .expect("add_device");
1033
1034 assert!(registry.objects.root.lookup("class/input/mouse".into()).is_some());
1035 })
1036 .await;
1037 }
1038
1039 #[::fuchsia::test]
1040 async fn registry_add_bus() {
1041 spawn_kernel_and_run(async |locked, current_task| {
1042 let kernel = current_task.kernel();
1043 let registry = &kernel.device_registry;
1044 registry
1045 .register_major(
1046 locked,
1047 "input".into(),
1048 DeviceMode::Char,
1049 INPUT_MAJOR,
1050 simple_device_ops::<DevNull>,
1051 )
1052 .expect("can register input");
1053
1054 let bus = registry.objects.get_or_create_bus("my-bus".into());
1055 let class = registry.objects.get_or_create_class("my-class".into(), bus);
1056 registry
1057 .add_device(
1058 locked,
1059 &*current_task,
1060 "my-device".into(),
1061 DeviceMetadata::new(
1062 "my-device".into(),
1063 DeviceType::new(INPUT_MAJOR, 0),
1064 DeviceMode::Char,
1065 ),
1066 class,
1067 build_device_directory,
1068 )
1069 .expect("add_device");
1070 assert!(registry.objects.root.lookup("bus/my-bus".into()).is_some());
1071 assert!(registry.objects.root.lookup("devices/my-bus/my-class".into()).is_some());
1072 assert!(
1073 registry.objects.root.lookup("devices/my-bus/my-class/my-device".into()).is_some()
1074 );
1075 })
1076 .await;
1077 }
1078
1079 #[::fuchsia::test]
1080 async fn registry_remove_device() {
1081 spawn_kernel_and_run(async |locked, current_task| {
1082 let kernel = current_task.kernel();
1083 let registry = &kernel.device_registry;
1084 registry
1085 .register_major(
1086 locked,
1087 "input".into(),
1088 DeviceMode::Char,
1089 INPUT_MAJOR,
1090 simple_device_ops::<DevNull>,
1091 )
1092 .expect("can register input");
1093
1094 let pci_bus = registry.objects.get_or_create_bus("pci".into());
1095 let input_class = registry.objects.get_or_create_class("input".into(), pci_bus);
1096 let mouse_dev = registry
1097 .add_device(
1098 locked,
1099 &*current_task,
1100 "mouse".into(),
1101 DeviceMetadata::new(
1102 "mouse".into(),
1103 DeviceType::new(INPUT_MAJOR, 0),
1104 DeviceMode::Char,
1105 ),
1106 input_class.clone(),
1107 build_device_directory,
1108 )
1109 .expect("add_device");
1110
1111 assert!(registry.objects.root.lookup("bus/pci/devices/mouse".into()).is_some());
1112 assert!(registry.objects.root.lookup("devices/pci/input/mouse".into()).is_some());
1113 assert!(registry.objects.root.lookup("class/input/mouse".into()).is_some());
1114
1115 registry.remove_device(locked, ¤t_task, mouse_dev);
1116
1117 assert!(registry.objects.root.lookup("bus/pci/devices/mouse".into()).is_none());
1118 assert!(registry.objects.root.lookup("devices/pci/input/mouse".into()).is_none());
1119 assert!(registry.objects.root.lookup("class/input/mouse".into()).is_none());
1120 })
1121 .await;
1122 }
1123
1124 #[::fuchsia::test]
1125 async fn registry_add_and_remove_numberless_device() {
1126 spawn_kernel_and_run(async |locked, current_task| {
1127 let kernel = current_task.kernel();
1128 let registry = &kernel.device_registry;
1129
1130 let cooling_device = registry.add_numberless_device(
1131 locked,
1132 "cooling_device0".into(),
1133 registry.objects.virtual_thermal_class(),
1134 build_device_directory,
1135 );
1136
1137 assert!(registry.objects.root.lookup("class/thermal/cooling_device0".into()).is_some());
1138 assert!(
1139 registry
1140 .objects
1141 .root
1142 .lookup("devices/virtual/thermal/cooling_device0".into())
1143 .is_some()
1144 );
1145
1146 registry.remove_device(locked, ¤t_task, cooling_device);
1147
1148 assert!(registry.objects.root.lookup("class/thermal/cooling_device0".into()).is_none());
1149 assert!(
1150 registry
1151 .objects
1152 .root
1153 .lookup("devices/virtual/thermal/cooling_device0".into())
1154 .is_none()
1155 );
1156 })
1157 .await;
1158 }
1159}