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, 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#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
34pub enum DeviceMode {
35 Char,
37
38 Block,
40}
41
42impl DeviceMode {
43 fn minor_count(&self) -> u32 {
45 match self {
46 Self::Char => CHRDEV_MINOR_MAX,
47 Self::Block => BLKDEV_MINOR_MAX,
48 }
49 }
50
51 pub fn minor_range(&self) -> Range<u32> {
53 0..self.minor_count()
54 }
55}
56
57pub trait DeviceOps: DynClone + Send + Sync + AsAny + 'static {
58 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
74impl<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
103pub 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
114pub type DeviceListenerKey = u64;
117
118pub 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
143struct DeviceEntry {
145 name: FsString,
149
150 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#[derive(Default)]
164struct RegisteredDevices {
165 majors: BTreeMap<u32, DeviceEntry>,
174
175 minors: BTreeMap<DeviceId, DeviceEntry>,
179}
180
181impl RegisteredDevices {
182 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 fn register_minor(&mut self, devt: DeviceId, entry: DeviceEntry) {
198 self.minors.insert(devt, entry);
199 }
200
201 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 fn list_major_devices(&self) -> Vec<(u32, FsString)> {
219 self.majors.iter().map(|(major, entry)| (*major, entry.name.clone())).collect()
220 }
221
222 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
228pub struct DeviceRegistry {
237 pub objects: KObjectStore,
239
240 state: OrderedMutex<DeviceRegistryState, starnix_sync::DeviceRegistryState>,
242}
243struct DeviceRegistryState {
244 char_devices: RegisteredDevices,
246
247 block_devices: RegisteredDevices,
249
250 misc_chardev_allocator: DeviceIdAllocator,
254
255 dyn_chardev_allocator: DeviceIdAllocator,
259
260 next_anon_minor: u32,
262
263 listeners: BTreeMap<u64, Box<dyn DeviceListener>>,
268
269 next_listener_id: u64,
271
272 next_event_id: u64,
274}
275
276impl DeviceRegistry {
277 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 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 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 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 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 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 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 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 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 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 pub fn remove_net_device(&self, device: Device) {
577 assert!(device.metadata.is_none());
578 self.objects.remove(&device);
579 }
580
581 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 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 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 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 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 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 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 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 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 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 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
796struct DeviceIdAllocator {
798 freelist: Vec<Range<DeviceId>>,
802}
803
804impl DeviceIdAllocator {
805 fn new(mut available: Vec<Range<DeviceId>>) -> Self {
809 available.reverse();
810 Self { freelist: available }
811 }
812
813 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 assert!(
893 registry
894 .open_device(
895 locked,
896 ¤t_task,
897 &node,
898 OpenFlags::RDONLY,
899 DeviceId::NONE,
900 DeviceMode::Char
901 )
902 .is_err()
903 );
904
905 assert!(
907 registry
908 .open_device(
909 locked,
910 ¤t_task,
911 &node,
912 OpenFlags::RDONLY,
913 DeviceId::NULL,
914 DeviceMode::Block
915 )
916 .is_err()
917 );
918
919 let _ = registry
921 .open_device(
922 locked,
923 ¤t_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 ¤t_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, ¤t_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, ¤t_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}