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