kernel/device/manager.rs
1//! # Device Manager Module
2//!
3//! This module provides functionality for managing hardware devices in the kernel.
4//!
5//! ## Overview
6//!
7//! The device manager is responsible for:
8//! - Tracking available device drivers with priority-based initialization
9//! - Device discovery and initialization through FDT
10//! - Managing device information and lifecycle
11//!
12//! ## Key Components
13//!
14//! - `DeviceManager`: The main device management system that handles all devices and drivers
15//! - `DriverPriority`: Priority levels for controlling driver initialization order
16//!
17//! ## Device Discovery
18//!
19//! Devices are discovered through the Flattened Device Tree (FDT). The manager:
20//! 1. Parses the device tree
21//! 2. Matches compatible devices with registered drivers based on priority
22//! 3. Probes devices with appropriate drivers in priority order
23//!
24//! ## Usage
25//!
26//! The device manager is implemented as a global singleton that can be accessed via:
27//! - `DeviceManager::get_manager()` - Shared access (thread-safe via internal Mutex)
28//!
29//! ### Example: Registering a device driver
30//!
31//! ```
32//! use crate::device::manager::{DeviceManager, DriverPriority};
33//!
34//! // Create a new device driver
35//! let my_driver = Box::new(MyDeviceDriver::new());
36//!
37//! // Register with the device manager at Core priority
38//! DeviceManager::get_manager().register_driver(my_driver, DriverPriority::Core);
39//! ```
40
41extern crate alloc;
42
43use core::sync::atomic::AtomicUsize;
44use core::sync::atomic::Ordering;
45
46use alloc::boxed::Box;
47use alloc::collections::btree_map::BTreeMap;
48use alloc::string::String;
49use alloc::sync::Arc;
50use alloc::vec::Vec;
51use spin::mutex::Mutex;
52
53use crate::device::platform::PlatformDeviceInfo;
54use crate::device::platform::resource::PlatformDeviceResource;
55use crate::device::platform::resource::PlatformDeviceResourceType;
56use crate::early_println;
57
58use super::Device;
59use super::DeviceDriver;
60use super::DeviceInfo;
61use crate::DeviceSource;
62
63/// Simplified shared device type
64pub type SharedDevice = Arc<dyn Device>;
65
66/// Driver priority levels for initialization order
67#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
68pub enum DriverPriority {
69 /// Critical infrastructure drivers (interrupt controllers, memory controllers)
70 Critical = 0,
71 /// Core system drivers (timers, basic I/O)
72 Core = 1,
73 /// Standard device drivers (network, storage)
74 Standard = 2,
75 /// Late initialization drivers (filesystems, user interface)
76 Late = 3,
77}
78
79impl DriverPriority {
80 /// Get all priority levels in order
81 pub fn all() -> &'static [DriverPriority] {
82 &[
83 DriverPriority::Critical,
84 DriverPriority::Core,
85 DriverPriority::Standard,
86 DriverPriority::Late,
87 ]
88 }
89
90 /// Get a human-readable description of the priority level
91 pub fn description(&self) -> &'static str {
92 match self {
93 DriverPriority::Critical => "Critical Infrastructure",
94 DriverPriority::Core => "Core System",
95 DriverPriority::Standard => "Standard Devices",
96 DriverPriority::Late => "Late Initialization",
97 }
98 }
99}
100
101static MANAGER: DeviceManager = DeviceManager::new();
102
103/// DeviceManager
104///
105/// This struct is the main device management system.
106/// It handles all devices and drivers with priority-based initialization.
107///
108/// # Fields
109/// - `devices`: A mutex-protected map of all registered devices by ID.
110/// - `device_by_name`: A mutex-protected map of devices by name.
111/// - `name_to_id`: A mutex-protected map from device name to device ID.
112/// - `drivers`: A mutex-protected map of device drivers organized by priority.
113/// - `next_device_id`: Atomic counter for generating unique device IDs.
114pub struct DeviceManager {
115 /* Devices stored by ID */
116 devices: Mutex<BTreeMap<usize, SharedDevice>>,
117 /* Devices stored by name */
118 device_by_name: Mutex<BTreeMap<String, SharedDevice>>,
119 /* Name to ID mapping */
120 name_to_id: Mutex<BTreeMap<String, usize>>,
121 /* Device drivers organized by priority */
122 drivers: Mutex<BTreeMap<DriverPriority, Vec<Box<dyn DeviceDriver>>>>,
123 /* Next device ID to assign */
124 next_device_id: AtomicUsize,
125}
126
127impl DeviceManager {
128 const fn new() -> Self {
129 DeviceManager {
130 devices: Mutex::new(BTreeMap::new()),
131 device_by_name: Mutex::new(BTreeMap::new()),
132 name_to_id: Mutex::new(BTreeMap::new()),
133 drivers: Mutex::new(BTreeMap::new()),
134 next_device_id: AtomicUsize::new(1), // Start from 1, reserve 0 for invalid
135 }
136 }
137
138 pub fn get_manager() -> &'static DeviceManager {
139 &MANAGER
140 }
141
142 /// Register a device with the manager
143 ///
144 /// # Arguments
145 /// * `device`: The device to register.
146 ///
147 /// # Returns
148 /// * The id of the registered device.
149 ///
150 /// # Example
151 ///
152 /// ```rust
153 /// let device = Arc::new(MyDevice::new());
154 /// let id = DeviceManager::get_manager().register_device(device);
155 /// ```
156 ///
157 pub fn register_device(&self, device: Arc<dyn Device>) -> usize {
158 let mut devices = self.devices.lock();
159 let id = self.next_device_id.fetch_add(1, Ordering::SeqCst);
160 devices.insert(id, device);
161 id
162 }
163
164 /// Register a device with the manager by name
165 ///
166 /// # Arguments
167 /// * `name`: The name of the device.
168 /// * `device`: The device to register.
169 ///
170 /// # Returns
171 /// * The id of the registered device.
172 ///
173 pub fn register_device_with_name(&self, name: String, device: Arc<dyn Device>) -> usize {
174 let mut devices = self.devices.lock();
175 let mut device_by_name = self.device_by_name.lock();
176 let mut name_to_id = self.name_to_id.lock();
177
178 let id = self.next_device_id.fetch_add(1, Ordering::SeqCst);
179 devices.insert(id, device.clone());
180 device_by_name.insert(name.clone(), device);
181 name_to_id.insert(name, id);
182 id
183 }
184
185 /// Get a device by ID
186 ///
187 /// # Arguments
188 /// * `id`: The id of the device to get.
189 ///
190 /// # Returns
191 /// * The device if found, or None if not found.
192 ///
193 pub fn get_device(&self, id: usize) -> Option<SharedDevice> {
194 let devices = self.devices.lock();
195 devices.get(&id).cloned()
196 }
197
198 /// Get a device by name
199 ///
200 /// # Arguments
201 /// * `name`: The name of the device to get.
202 ///
203 /// # Returns
204 /// * The device if found, or None if not found.
205 ///
206 pub fn get_device_by_name(&self, name: &str) -> Option<SharedDevice> {
207 let device_by_name = self.device_by_name.lock();
208 device_by_name.get(name).cloned()
209 }
210
211 /// Get a device ID by name
212 ///
213 /// # Arguments
214 /// * `name`: The name of the device to find.
215 ///
216 /// # Returns
217 /// * The device ID if found, or None if not found.
218 ///
219 pub fn get_device_id_by_name(&self, name: &str) -> Option<usize> {
220 let name_to_id = self.name_to_id.lock();
221 name_to_id.get(name).cloned()
222 }
223
224 /// Get the number of devices
225 ///
226 /// # Returns
227 ///
228 /// The number of devices.
229 ///
230 pub fn get_devices_count(&self) -> usize {
231 let devices = self.devices.lock();
232 devices.len()
233 }
234
235 /// Get the first device of a specific type
236 ///
237 /// # Arguments
238 /// * `device_type`: The device type to find.
239 ///
240 /// # Returns
241 /// * The first device ID of the specified type, or None if not found.
242 ///
243 pub fn get_first_device_by_type(&self, device_type: super::DeviceType) -> Option<usize> {
244 let devices = self.devices.lock();
245 for (id, device) in devices.iter() {
246 if device.device_type() == device_type {
247 return Some(*id);
248 }
249 }
250 None
251 }
252
253 /// Get all devices registered by name
254 ///
255 /// Returns an iterator over (name, device) pairs for all devices
256 /// that were registered with explicit names.
257 ///
258 /// # Returns
259 ///
260 /// Vector of (name, device) tuples
261 pub fn get_named_devices(&self) -> Vec<(String, SharedDevice)> {
262 let device_by_name = self.device_by_name.lock();
263 device_by_name
264 .iter()
265 .map(|(name, device)| (name.clone(), device.clone()))
266 .collect()
267 }
268
269 pub fn borrow_drivers(&self) -> &Mutex<BTreeMap<DriverPriority, Vec<Box<dyn DeviceDriver>>>> {
270 &self.drivers
271 }
272
273 /// Populates devices from the FDT (Flattened Device Tree).
274 ///
275 /// This function searches for the `/soc` node in the FDT and iterates through its children.
276 /// For each child node, it checks if there is a compatible driver registered.
277 /// If a matching driver is found, it probes the device using the driver's `probe` method.
278 /// If the probe is successful, the device is registered with the driver.
279 ///
280 /// # Deprecated
281 /// Use `populate_devices_from_source` with `DeviceSource::Fdt` instead.
282 pub fn populate_devices(&self) {
283 use super::fdt::FdtManager;
284
285 let fdt_manager = unsafe { FdtManager::get_mut_manager() };
286 let fdt = fdt_manager.get_fdt();
287 if fdt.is_none() {
288 early_println!("FDT not initialized");
289 return;
290 }
291
292 self.populate_devices_from_fdt(None);
293 }
294
295 /// Populate devices using a specific device source
296 ///
297 /// # Arguments
298 ///
299 /// * `device_source` - The source of device information (FDT, UEFI, ACPI, etc.)
300 /// * `priorities` - Optional slice of priority levels to use. If None, uses all priorities in order.
301 pub fn populate_devices_from_source(
302 &self,
303 device_source: &DeviceSource,
304 priorities: Option<&[DriverPriority]>,
305 ) {
306 match device_source {
307 DeviceSource::Fdt(_addr) => {
308 early_println!("Populating devices from FDT...");
309 self.populate_devices_from_fdt(priorities);
310 }
311 DeviceSource::Uefi => {
312 early_println!("Populating devices from UEFI...");
313 self.populate_devices_from_uefi(priorities);
314 }
315 DeviceSource::Acpi => {
316 early_println!("Populating devices from ACPI...");
317 self.populate_devices_from_acpi(priorities);
318 }
319 DeviceSource::None => {
320 early_println!("No device source available - skipping device population");
321 }
322 }
323 }
324
325 /// Populate devices from FDT
326 fn populate_devices_from_fdt(&self, priorities: Option<&[DriverPriority]>) {
327 use super::fdt::FdtManager;
328
329 let fdt_manager = unsafe { FdtManager::get_mut_manager() };
330 let fdt = fdt_manager.get_fdt();
331 if fdt.is_none() {
332 early_println!("FDT not initialized");
333 return;
334 }
335 let fdt = fdt.unwrap();
336
337 let priority_list = priorities.unwrap_or(DriverPriority::all());
338
339 // Process each priority level separately to reduce stack depth
340 for &priority in priority_list {
341 self.process_priority_level(fdt, priority);
342 }
343 }
344
345 /// Process devices for a single priority level - reduces stack nesting
346 fn process_priority_level(&self, fdt: &fdt::Fdt, priority: DriverPriority) {
347 early_println!(
348 "Populating devices with {} drivers from FDT...",
349 priority.description()
350 );
351
352 // Try /soc node first (RISC-V virt), then fall back to root node (AArch64 virt)
353 let parent_node = if let Some(soc) = fdt.find_node("/soc") {
354 Some(soc)
355 } else {
356 // For AArch64 virt and other platforms where devices are at root level
357 fdt.find_node("/")
358 };
359
360 let parent_node = match parent_node {
361 Some(node) => node,
362 None => {
363 early_println!("No device tree root found");
364 return;
365 }
366 };
367
368 let mut idx = 0;
369
370 // Process each child node separately to reduce stack usage
371 for child in parent_node.children() {
372 self.process_single_device_node(child, priority, &mut idx);
373 }
374 }
375
376 /// Process a single device node with minimal stack usage
377 fn process_single_device_node(
378 &self,
379 child: fdt::node::FdtNode,
380 priority: DriverPriority,
381 idx: &mut usize,
382 ) {
383 let compatible = child.compatible();
384 if compatible.is_none() {
385 return;
386 }
387
388 // Minimize stack usage by not collecting all compatible strings at once
389 let compatible_iter = compatible.unwrap().all();
390
391 // Check if we have any drivers for this priority level
392 let has_drivers = {
393 let drivers = self.drivers.lock();
394 drivers
395 .get(&priority)
396 .map_or(false, |list| !list.is_empty())
397 };
398
399 if !has_drivers {
400 return;
401 }
402
403 // Build resources separately to reduce stack usage
404 let resources = self.build_minimal_resources(&child);
405
406 // Try to match with drivers
407 let compatible_vec: alloc::vec::Vec<&str> = compatible_iter.collect();
408 self.try_match_and_probe_device(child, priority, idx, compatible_vec, resources);
409 }
410
411 /// Build device resources with minimal stack allocation
412 fn build_minimal_resources(
413 &self,
414 child: &fdt::node::FdtNode,
415 ) -> alloc::vec::Vec<PlatformDeviceResource> {
416 let mut resources = alloc::vec::Vec::new();
417
418 // Add memory regions
419 if let Some(regions) = child.reg() {
420 for region in regions {
421 let res = PlatformDeviceResource {
422 res_type: PlatformDeviceResourceType::MEM,
423 start: region.starting_address as usize,
424 end: region.starting_address as usize + region.size.unwrap() - 1,
425 irq_metadata: None, // No IRQ metadata for memory regions
426 };
427 resources.push(res);
428 }
429 }
430
431 // Add IRQs
432 if let Some(irqs) = child.interrupts() {
433 // Standard path: fdt-rs successfully parsed interrupts
434 for irq in irqs {
435 let res = PlatformDeviceResource {
436 res_type: PlatformDeviceResourceType::IRQ,
437 start: irq,
438 end: irq,
439 irq_metadata: None, // No metadata when fdt-rs handles it
440 };
441 resources.push(res);
442 }
443 } else if let Some(prop) = child.property("interrupts") {
444 // Fallback: Parse raw interrupts property when fdt-rs fails
445 // This preserves interrupt controller metadata for later translation
446 let value = prop.value;
447
448 // Detect cell format based on property length
449 let cell_size = if value.len() % 12 == 0 {
450 3 // 3-cell format (e.g., ARM GIC: <type, number, flags>)
451 } else if value.len() % 8 == 0 {
452 2 // 2-cell format
453 } else if value.len() % 4 == 0 {
454 1 // 1-cell format (just interrupt number)
455 } else {
456 return resources; // Unknown format, skip
457 };
458
459 let num_irqs = value.len() / (cell_size * 4);
460
461 for i in 0..num_irqs {
462 let offset = i * cell_size * 4;
463
464 let (irq_num, metadata) = match cell_size {
465 3 => {
466 // 3-cell format: <type, number, flags>
467 let irq_type = u32::from_be_bytes([
468 value[offset],
469 value[offset + 1],
470 value[offset + 2],
471 value[offset + 3],
472 ]);
473 let irq_number = u32::from_be_bytes([
474 value[offset + 4],
475 value[offset + 5],
476 value[offset + 6],
477 value[offset + 7],
478 ]);
479 let irq_flags = u32::from_be_bytes([
480 value[offset + 8],
481 value[offset + 9],
482 value[offset + 10],
483 value[offset + 11],
484 ]);
485
486 // Store raw number, let interrupt controller translate
487 (
488 irq_number as usize,
489 Some(crate::device::platform::resource::IrqMetadata {
490 irq_type,
491 irq_number,
492 irq_flags,
493 }),
494 )
495 }
496 2 => {
497 // 2-cell format: <number, flags>
498 let irq_number = u32::from_be_bytes([
499 value[offset],
500 value[offset + 1],
501 value[offset + 2],
502 value[offset + 3],
503 ]);
504 let irq_flags = u32::from_be_bytes([
505 value[offset + 4],
506 value[offset + 5],
507 value[offset + 6],
508 value[offset + 7],
509 ]);
510
511 (
512 irq_number as usize,
513 Some(crate::device::platform::resource::IrqMetadata {
514 irq_type: 0, // No type in 2-cell format
515 irq_number,
516 irq_flags,
517 }),
518 )
519 }
520 1 => {
521 // 1-cell format: just interrupt number
522 let irq_number = u32::from_be_bytes([
523 value[offset],
524 value[offset + 1],
525 value[offset + 2],
526 value[offset + 3],
527 ]);
528
529 (irq_number as usize, None)
530 }
531 _ => unreachable!(),
532 };
533
534 let res = PlatformDeviceResource {
535 res_type: PlatformDeviceResourceType::IRQ,
536 start: irq_num,
537 end: irq_num,
538 irq_metadata: metadata,
539 };
540 resources.push(res);
541 }
542 }
543
544 resources
545 }
546
547 /// Try to match device with drivers and probe if successful
548 fn try_match_and_probe_device(
549 &self,
550 child: fdt::node::FdtNode,
551 priority: DriverPriority,
552 idx: &mut usize,
553 compatible: alloc::vec::Vec<&str>,
554 resources: alloc::vec::Vec<PlatformDeviceResource>,
555 ) {
556 let drivers = self.drivers.lock();
557 if let Some(driver_list) = drivers.get(&priority) {
558 for driver in driver_list.iter() {
559 if driver
560 .match_table()
561 .iter()
562 .any(|&c| compatible.contains(&c))
563 {
564 // Convert borrowed strings to static strings (FDT data is actually static)
565 // This is safe because FDT is loaded at boot and remains in memory
566 let static_name: &'static str = unsafe { core::mem::transmute(child.name) };
567 let static_compatible: alloc::vec::Vec<&'static str> = compatible
568 .into_iter()
569 .map(|s| unsafe { core::mem::transmute(s) })
570 .collect();
571
572 let device = alloc::boxed::Box::new(PlatformDeviceInfo::new(
573 static_name,
574 *idx,
575 static_compatible,
576 resources,
577 ));
578
579 match driver.probe(&*device) {
580 Ok(_) => {
581 early_println!(
582 "Successfully probed {} device: {}",
583 priority.description(),
584 device.name()
585 );
586 *idx += 1;
587 }
588 Err(e) => {
589 early_println!(
590 "Failed to probe {} device {}: {}",
591 priority.description(),
592 device.name(),
593 e
594 );
595 }
596 }
597 break; // Found matching driver, move to next device
598 }
599 }
600 }
601 }
602
603 /// Populate devices from UEFI (stub implementation)
604 ///
605 /// # Arguments
606 ///
607 /// * `priorities` - Optional slice of priority levels to use. If None, uses all priorities in order.
608 ///
609 /// # Note
610 ///
611 /// This is currently a stub implementation. UEFI device discovery will be implemented
612 /// when UEFI boot support is added.
613 fn populate_devices_from_uefi(&self, _priorities: Option<&[DriverPriority]>) {
614 early_println!("UEFI device discovery not yet implemented");
615 // TODO: Implement UEFI device discovery
616 // - Enumerate UEFI protocols
617 // - Create PlatformDeviceInfo from UEFI device handles
618 // - Probe devices with matching drivers
619 }
620
621 /// Populate devices from ACPI (stub implementation)
622 ///
623 /// # Arguments
624 ///
625 /// * `priorities` - Optional slice of priority levels to use. If None, uses all priorities in order.
626 ///
627 /// # Note
628 ///
629 /// This is currently a stub implementation. ACPI device discovery will be implemented
630 /// when x86 support is added.
631 fn populate_devices_from_acpi(&self, _priorities: Option<&[DriverPriority]>) {
632 early_println!("ACPI device discovery not yet implemented");
633 // TODO: Implement ACPI device discovery
634 // - Parse ACPI tables (DSDT, etc.)
635 // - Create PlatformDeviceInfo from ACPI device objects
636 // - Probe devices with matching drivers
637 }
638
639 /// Populate devices using drivers of specific priority levels
640 ///
641 /// # Arguments
642 ///
643 /// * `priorities` - Optional slice of priority levels to use. If None, uses all priorities in order.
644 ///
645 /// # Deprecated
646 /// Use `populate_devices_from_source` instead.
647 pub fn populate_devices_by_priority(&self, priorities: Option<&[DriverPriority]>) {
648 self.populate_devices_from_fdt(priorities);
649 }
650
651 /// Registers a device driver with the device manager.
652 ///
653 /// This function takes a boxed device driver and adds it to the list of registered drivers
654 /// at the specified priority level.
655 ///
656 /// # Arguments
657 ///
658 /// * `driver` - A boxed device driver that implements the `DeviceDriver` trait.
659 /// * `priority` - The priority level for this driver.
660 ///
661 /// # Example
662 ///
663 /// ```rust
664 /// let driver = Box::new(MyDeviceDriver::new());
665 /// DeviceManager::get_manager().register_driver(driver, DriverPriority::Standard);
666 /// ```
667 pub fn register_driver(&self, driver: Box<dyn DeviceDriver>, priority: DriverPriority) {
668 let mut drivers = self.drivers.lock();
669 drivers
670 .entry(priority)
671 .or_insert_with(Vec::new)
672 .push(driver);
673 }
674
675 /// Registers a device driver with default Standard priority.
676 ///
677 /// This is a convenience method for backward compatibility.
678 ///
679 /// # Arguments
680 ///
681 /// * `driver` - A boxed device driver that implements the `DeviceDriver` trait.
682 pub fn register_driver_default(&self, driver: Box<dyn DeviceDriver>) {
683 self.register_driver(driver, DriverPriority::Standard);
684 }
685
686 /// Clear all devices and reset the manager state (for testing only)
687 ///
688 /// This method is only available in test builds and should only be used
689 /// for unit testing to ensure test isolation.
690 #[cfg(test)]
691 pub fn clear_for_test(&self) {
692 let mut devices = self.devices.lock();
693 let mut device_by_name = self.device_by_name.lock();
694 let mut name_to_id = self.name_to_id.lock();
695
696 devices.clear();
697 device_by_name.clear();
698 name_to_id.clear();
699 self.next_device_id.store(1, Ordering::SeqCst); // Start from 1, reserve 0 for invalid
700 }
701}
702
703#[cfg(test)]
704mod tests {
705 use super::*;
706 use crate::device::{GenericDevice, platform::*};
707 use alloc::vec;
708
709 #[cfg(target_arch = "riscv64")]
710 #[test_case]
711 fn test_populate_driver() {
712 static mut TEST_RESULT: bool = false;
713 fn probe_fn(_device: &PlatformDeviceInfo) -> Result<(), &'static str> {
714 unsafe {
715 TEST_RESULT = true;
716 }
717 Ok(())
718 }
719
720 let driver = Box::new(PlatformDeviceDriver::new(
721 "test",
722 probe_fn,
723 |_device| Ok(()),
724 vec!["sifive,test0"],
725 ));
726 let manager = DeviceManager::new();
727 manager.register_driver(driver, DriverPriority::Standard);
728
729 manager.populate_devices();
730 let result = unsafe { TEST_RESULT };
731 assert_eq!(result, true);
732 }
733
734 #[test_case]
735 fn test_get_device_from_manager() {
736 let device = Arc::new(GenericDevice::new("test"));
737 let manager = DeviceManager::new();
738 let id = manager.register_device(device);
739 let retrieved_device = manager.get_device(id);
740 assert!(retrieved_device.is_some());
741 let retrieved_device = retrieved_device.unwrap();
742 assert_eq!(retrieved_device.name(), "test");
743 }
744
745 #[test_case]
746 fn test_get_device_by_name() {
747 let device = Arc::new(GenericDevice::new("test_named"));
748 let manager = DeviceManager::new();
749 let _id = manager.register_device_with_name("test_device".into(), device);
750 let retrieved_device = manager.get_device_by_name("test_device");
751 assert!(retrieved_device.is_some());
752 let retrieved_device = retrieved_device.unwrap();
753 assert_eq!(retrieved_device.name(), "test_named");
754 }
755
756 #[test_case]
757 fn test_get_first_device_by_type() {
758 let device1 = Arc::new(GenericDevice::new("test_char"));
759 let device2 = Arc::new(GenericDevice::new("test_block"));
760 let manager = DeviceManager::new();
761 let _id1 = manager.register_device(device1);
762 let _id2 = manager.register_device(device2);
763
764 let char_device_id = manager.get_first_device_by_type(crate::device::DeviceType::Generic);
765 assert!(char_device_id.is_some());
766 let char_device_id = char_device_id.unwrap();
767 let char_device = manager.get_device(char_device_id).unwrap();
768 assert_eq!(char_device.name(), "test_char");
769 }
770
771 #[test_case]
772 fn test_get_device_out_of_bounds() {
773 let manager = DeviceManager::new();
774 let device = manager.get_device(999);
775 assert!(device.is_none());
776 }
777
778 #[test_case]
779 fn test_get_device_by_name_not_found() {
780 let manager = DeviceManager::new();
781 let device = manager.get_device_by_name("non_existent");
782 assert!(device.is_none());
783 }
784}