kernel/device/pci/
device.rs

1//! PCI device information.
2//!
3//! This module defines the PCI device information structure that represents
4//! a PCI device discovered on the system.
5
6extern crate alloc;
7
8use alloc::vec::Vec;
9use core::any::Any;
10
11use super::PciAddress;
12use crate::device::{DeviceInfo, DeviceType};
13
14/// PCI device class codes
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum PciClass {
17    /// Unclassified device
18    Unclassified = 0x00,
19    /// Mass storage controller
20    MassStorage = 0x01,
21    /// Network controller
22    Network = 0x02,
23    /// Display controller
24    Display = 0x03,
25    /// Multimedia controller
26    Multimedia = 0x04,
27    /// Memory controller
28    Memory = 0x05,
29    /// Bridge device
30    Bridge = 0x06,
31    /// Simple communication controller
32    Communication = 0x07,
33    /// Base system peripheral
34    SystemPeripheral = 0x08,
35    /// Input device controller
36    Input = 0x09,
37    /// Docking station
38    DockingStation = 0x0A,
39    /// Processor
40    Processor = 0x0B,
41    /// Serial bus controller
42    SerialBus = 0x0C,
43    /// Wireless controller
44    Wireless = 0x0D,
45    /// Intelligent controller
46    IntelligentIO = 0x0E,
47    /// Satellite communication controller
48    Satellite = 0x0F,
49    /// Encryption controller
50    Encryption = 0x10,
51    /// Signal processing controller
52    SignalProcessing = 0x11,
53    /// Processing accelerator
54    Accelerator = 0x12,
55    /// Non-essential instrumentation
56    Instrumentation = 0x13,
57    /// Unknown class
58    Unknown = 0xFF,
59}
60
61impl From<u8> for PciClass {
62    fn from(value: u8) -> Self {
63        match value {
64            0x00 => PciClass::Unclassified,
65            0x01 => PciClass::MassStorage,
66            0x02 => PciClass::Network,
67            0x03 => PciClass::Display,
68            0x04 => PciClass::Multimedia,
69            0x05 => PciClass::Memory,
70            0x06 => PciClass::Bridge,
71            0x07 => PciClass::Communication,
72            0x08 => PciClass::SystemPeripheral,
73            0x09 => PciClass::Input,
74            0x0A => PciClass::DockingStation,
75            0x0B => PciClass::Processor,
76            0x0C => PciClass::SerialBus,
77            0x0D => PciClass::Wireless,
78            0x0E => PciClass::IntelligentIO,
79            0x0F => PciClass::Satellite,
80            0x10 => PciClass::Encryption,
81            0x11 => PciClass::SignalProcessing,
82            0x12 => PciClass::Accelerator,
83            0x13 => PciClass::Instrumentation,
84            _ => PciClass::Unknown,
85        }
86    }
87}
88
89/// PCI device information
90///
91/// Contains all relevant information about a discovered PCI device.
92#[derive(Debug, Clone)]
93pub struct PciDeviceInfo {
94    /// PCI address (bus, device, function)
95    address: PciAddress,
96    /// Vendor ID
97    vendor_id: u16,
98    /// Device ID
99    device_id: u16,
100    /// Class code (base class, subclass, interface)
101    class_code: u32,
102    /// Revision ID
103    revision: u8,
104    /// Subsystem vendor ID
105    subsystem_vendor_id: u16,
106    /// Subsystem ID
107    subsystem_id: u16,
108    /// Interrupt line
109    interrupt_line: u8,
110    /// Interrupt pin
111    interrupt_pin: u8,
112    /// Device name (generated from vendor/device ID)
113    name: &'static str,
114    /// Unique device ID in the system
115    id: usize,
116}
117
118impl PciDeviceInfo {
119    /// Create a new PCI device information structure
120    ///
121    /// # Arguments
122    ///
123    /// * `address` - PCI address
124    /// * `vendor_id` - Vendor ID
125    /// * `device_id` - Device ID
126    /// * `class_code` - Class code
127    /// * `revision` - Revision ID
128    /// * `subsystem_vendor_id` - Subsystem vendor ID
129    /// * `subsystem_id` - Subsystem ID
130    /// * `interrupt_line` - Interrupt line
131    /// * `interrupt_pin` - Interrupt pin
132    /// * `name` - Device name
133    /// * `id` - Unique device ID
134    #[allow(clippy::too_many_arguments)]
135    pub fn new(
136        address: PciAddress,
137        vendor_id: u16,
138        device_id: u16,
139        class_code: u32,
140        revision: u8,
141        subsystem_vendor_id: u16,
142        subsystem_id: u16,
143        interrupt_line: u8,
144        interrupt_pin: u8,
145        name: &'static str,
146        id: usize,
147    ) -> Self {
148        Self {
149            address,
150            vendor_id,
151            device_id,
152            class_code,
153            revision,
154            subsystem_vendor_id,
155            subsystem_id,
156            interrupt_line,
157            interrupt_pin,
158            name,
159            id,
160        }
161    }
162
163    /// Get the PCI address
164    pub fn address(&self) -> PciAddress {
165        self.address
166    }
167
168    /// Get the vendor ID
169    pub fn vendor_id(&self) -> u16 {
170        self.vendor_id
171    }
172
173    /// Get the device ID
174    pub fn device_id(&self) -> u16 {
175        self.device_id
176    }
177
178    /// Get the class code
179    pub fn class_code(&self) -> u32 {
180        self.class_code
181    }
182
183    /// Get the base class
184    pub fn base_class(&self) -> u8 {
185        ((self.class_code >> 16) & 0xFF) as u8
186    }
187
188    /// Get the subclass
189    pub fn subclass(&self) -> u8 {
190        ((self.class_code >> 8) & 0xFF) as u8
191    }
192
193    /// Get the interface
194    pub fn interface(&self) -> u8 {
195        (self.class_code & 0xFF) as u8
196    }
197
198    /// Get the PCI class
199    pub fn pci_class(&self) -> PciClass {
200        PciClass::from(self.base_class())
201    }
202
203    /// Get the revision ID
204    pub fn revision(&self) -> u8 {
205        self.revision
206    }
207
208    /// Get the subsystem vendor ID
209    pub fn subsystem_vendor_id(&self) -> u16 {
210        self.subsystem_vendor_id
211    }
212
213    /// Get the subsystem ID
214    pub fn subsystem_id(&self) -> u16 {
215        self.subsystem_id
216    }
217
218    /// Get the interrupt line
219    pub fn interrupt_line(&self) -> u8 {
220        self.interrupt_line
221    }
222
223    /// Get the interrupt pin
224    pub fn interrupt_pin(&self) -> u8 {
225        self.interrupt_pin
226    }
227
228    /// Check if device matches vendor and device ID
229    pub fn matches(&self, vendor_id: u16, device_id: u16) -> bool {
230        self.vendor_id == vendor_id && self.device_id == device_id
231    }
232
233    /// Check if device matches class code
234    pub fn matches_class(&self, base_class: u8, subclass: Option<u8>) -> bool {
235        if self.base_class() != base_class {
236            return false;
237        }
238        if let Some(sc) = subclass {
239            return self.subclass() == sc;
240        }
241        true
242    }
243
244    /// Convert to DeviceType based on PCI class
245    pub fn to_device_type(&self) -> DeviceType {
246        match self.pci_class() {
247            PciClass::MassStorage => DeviceType::Block,
248            PciClass::Network => DeviceType::Network,
249            PciClass::Display => DeviceType::Graphics,
250            _ => DeviceType::Generic,
251        }
252    }
253
254    /// Get the device name
255    pub fn name(&self) -> &'static str {
256        self.name
257    }
258
259    /// Get the device ID
260    pub fn id(&self) -> usize {
261        self.id
262    }
263}
264
265impl DeviceInfo for PciDeviceInfo {
266    fn name(&self) -> &'static str {
267        self.name
268    }
269
270    fn id(&self) -> usize {
271        self.id
272    }
273
274    fn compatible(&self) -> Vec<&'static str> {
275        // PCI devices use vendor:device ID matching rather than string-based
276        // compatibility matching. The PciDeviceDriver uses its own matches_device()
277        // method with PciDeviceId structures for matching, so this returns an empty
278        // vector. This is intentional and does not break driver matching for PCI devices.
279        Vec::new()
280    }
281
282    fn as_any(&self) -> &dyn Any {
283        self
284    }
285}
286
287#[cfg(test)]
288mod tests {
289    use super::*;
290
291    #[test_case]
292    fn test_pci_device_info_creation() {
293        let addr = PciAddress::new(0, 0, 1, 0);
294        let device = PciDeviceInfo::new(
295            addr,
296            0x8086, // Intel
297            0x1234,
298            0x020000, // Network controller
299            0x01,
300            0x0000,
301            0x0000,
302            0x0B,
303            0x01,
304            "pci_device",
305            1,
306        );
307
308        assert_eq!(device.vendor_id(), 0x8086);
309        assert_eq!(device.device_id(), 0x1234);
310        assert_eq!(device.base_class(), 0x02);
311        assert_eq!(device.pci_class(), PciClass::Network);
312        assert_eq!(device.to_device_type(), DeviceType::Network);
313    }
314
315    #[test_case]
316    fn test_pci_device_matching() {
317        let addr = PciAddress::new(0, 0, 1, 0);
318        let device = PciDeviceInfo::new(
319            addr,
320            0x8086,
321            0x1234,
322            0x030000, // Display controller
323            0x01,
324            0x0000,
325            0x0000,
326            0x0B,
327            0x01,
328            "pci_device",
329            1,
330        );
331
332        assert!(device.matches(0x8086, 0x1234));
333        assert!(!device.matches(0x8086, 0x5678));
334        assert!(device.matches_class(0x03, None));
335        assert!(device.matches_class(0x03, Some(0x00)));
336        assert!(!device.matches_class(0x02, None));
337    }
338
339    #[test_case]
340    fn test_pci_class_conversion() {
341        assert_eq!(PciClass::from(0x01), PciClass::MassStorage);
342        assert_eq!(PciClass::from(0x02), PciClass::Network);
343        assert_eq!(PciClass::from(0x03), PciClass::Display);
344        assert_eq!(PciClass::from(0xFF), PciClass::Unknown);
345    }
346}