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}