1use super::io::{inl, outl};
9use crate::sync::SpinLock;
10use alloc::vec::Vec;
11use core::fmt;
12
13const CONFIG_ADDRESS: u16 = 0xCF8;
15const CONFIG_DATA: u16 = 0xCFC;
17
18static PCI_IO_LOCK: SpinLock<()> = SpinLock::new(());
23
24#[derive(Clone, Copy)]
25struct PciLineQuirk {
26 vendor_id: u16,
27 device_id: u16,
28}
29
30const PCI_IRQ_LINE_ZERO_IF_FF: &[PciLineQuirk] = &[
31 PciLineQuirk {
32 vendor_id: vendor::INTEL,
33 device_id: intel_eth::E1000_82540EM,
34 },
35 PciLineQuirk {
36 vendor_id: vendor::INTEL,
37 device_id: intel_eth::E1000_82545EM,
38 },
39 PciLineQuirk {
40 vendor_id: vendor::INTEL,
41 device_id: intel_eth::E1000E_82574L,
42 },
43 PciLineQuirk {
44 vendor_id: vendor::VIRTIO,
45 device_id: device::VIRTIO_NET,
46 },
47 PciLineQuirk {
48 vendor_id: vendor::VIRTIO,
49 device_id: device::VIRTIO_BLOCK,
50 },
51];
52
53pub mod vendor {
55 pub const VIRTIO: u16 = 0x1AF4;
56 pub const QEMU: u16 = 0x1234;
57 pub const INTEL: u16 = 0x8086;
58 pub const AMD: u16 = 0x1022;
59}
60
61pub mod device {
63 pub const VIRTIO_NET: u16 = 0x1000;
64 pub const VIRTIO_BLOCK: u16 = 0x1001;
65 pub const VIRTIO_CONSOLE: u16 = 0x1003;
66 pub const VIRTIO_RNG: u16 = 0x1005;
67 pub const VIRTIO_GPU: u16 = 0x1050;
68 pub const VIRTIO_INPUT: u16 = 0x1052;
69}
70
71pub mod class {
73 pub const MASS_STORAGE: u8 = 0x01;
74 pub const NETWORK: u8 = 0x02;
75}
76
77pub mod storage_subclass {
79 pub const SCSI: u8 = 0x00;
80 pub const IDE: u8 = 0x01;
81 pub const FLOPPY: u8 = 0x02;
82 pub const IPI: u8 = 0x03;
83 pub const RAID: u8 = 0x04;
84 pub const ATA: u8 = 0x05;
85 pub const SATA: u8 = 0x06;
86 pub const SAS: u8 = 0x07;
87 pub const NVM: u8 = 0x08;
88 pub const OTHER: u8 = 0x80;
89}
90
91pub mod sata_progif {
93 pub const AHCI: u8 = 0x01;
95 pub const VENDOR: u8 = 0x00;
97}
98
99pub mod net_subclass {
101 pub const ETHERNET: u8 = 0x00;
102 pub const OTHER: u8 = 0x80;
103}
104
105pub mod intel_eth {
107 pub const E1000_82540EM: u16 = 0x100E; pub const E1000_82545EM: u16 = 0x100F;
109 pub const E1000E_82574L: u16 = 0x10D3; pub const I210_AT: u16 = 0x1533;
111 pub const I350_AM2: u16 = 0x1521;
112 pub const I350_AM4: u16 = 0x1523;
113 pub const I217_LM: u16 = 0x153A;
114 pub const I211_AT: u16 = 0x1539;
115 pub const I219_LM: u16 = 0x15F9;
116 pub const I219_V: u16 = 0x15FA;
117 pub const I225_LM: u16 = 0x15F2;
118 pub const I225_V: u16 = 0x15F3;
119 pub const I226_LM: u16 = 0x125B;
120 pub const I226_V: u16 = 0x125C;
121}
122
123pub mod config {
125 pub const VENDOR_ID: u8 = 0x00;
126 pub const DEVICE_ID: u8 = 0x02;
127 pub const COMMAND: u8 = 0x04;
128 pub const STATUS: u8 = 0x06;
129 pub const REVISION_ID: u8 = 0x08;
130 pub const PROG_IF: u8 = 0x09;
131 pub const SUBCLASS: u8 = 0x0A;
132 pub const CLASS_CODE: u8 = 0x0B;
133 pub const CACHE_LINE_SIZE: u8 = 0x0C;
134 pub const LATENCY_TIMER: u8 = 0x0D;
135 pub const HEADER_TYPE: u8 = 0x0E;
136 pub const BIST: u8 = 0x0F;
137 pub const BAR0: u8 = 0x10;
138 pub const BAR1: u8 = 0x14;
139 pub const BAR2: u8 = 0x18;
140 pub const BAR3: u8 = 0x1C;
141 pub const BAR4: u8 = 0x20;
142 pub const BAR5: u8 = 0x24;
143 pub const CARDBUS_CIS: u8 = 0x28;
144 pub const SUBSYSTEM_VENDOR_ID: u8 = 0x2C;
145 pub const SUBSYSTEM_ID: u8 = 0x2E;
146 pub const ROM_BAR: u8 = 0x30;
147 pub const CAPABILITIES: u8 = 0x34;
148 pub const INTERRUPT_LINE: u8 = 0x3C;
149 pub const INTERRUPT_PIN: u8 = 0x3D;
150 pub const MIN_GNT: u8 = 0x3E;
151 pub const MAX_LAT: u8 = 0x3F;
152 pub const CAPABILITIES_PTR: u8 = 0x34;
153}
154
155pub mod cap_id {
157 pub const MSI: u8 = 0x05;
159 pub const MSIX: u8 = 0x11;
161}
162
163pub mod msi_cap {
165 pub const CONTROL: u8 = 0x02;
168 pub const ADDR_LOW: u8 = 0x04;
170 pub const ADDR_HIGH: u8 = 0x08;
172 pub const DATA: u8 = 0x08;
176}
177
178pub mod msi_ctrl {
180 pub const ENABLE: u16 = 1 << 0;
182 pub const MME_MASK: u16 = 0b111 << 4;
184 pub const MMC_MASK: u16 = 0b111 << 1;
186 pub const ADDR64: u16 = 1 << 7;
188 pub const PV_MASK: u16 = 1 << 8;
190}
191
192pub mod msix_cap {
194 pub const CONTROL: u8 = 0x02;
197 pub const TABLE: u8 = 0x04;
199 pub const PBA: u8 = 0x08;
201}
202
203pub mod msix_ctrl {
205 pub const ENABLE: u16 = 1 << 15;
207 pub const FUNC_MASK: u16 = 1 << 14;
209 pub const TABLE_SIZE_MASK: u16 = (1 << 11) - 1;
211}
212
213pub const MSI_ADDR_BASE: u32 = 0xFEE0_0000;
223pub const MSI_ADDR_DEST_SHIFT: u32 = 12;
224
225pub mod command {
227 pub const IO_SPACE: u16 = 1 << 0;
228 pub const MEMORY_SPACE: u16 = 1 << 1;
229 pub const BUS_MASTER: u16 = 1 << 2;
230 pub const SPECIAL_CYCLES: u16 = 1 << 3;
231 pub const MWI_ENABLE: u16 = 1 << 4;
232 pub const VGA_PALETTE_SNOOP: u16 = 1 << 5;
233 pub const PARITY_ERROR_RESPONSE: u16 = 1 << 6;
234 pub const STEPPING_CONTROL: u16 = 1 << 7;
235 pub const SERR_ENABLE: u16 = 1 << 8;
236 pub const FAST_BACK_TO_BACK: u16 = 1 << 9;
237 pub const INTERRUPT_DISABLE: u16 = 1 << 10;
238}
239
240#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242pub enum Bar {
243 Io { port: u16 },
244 Memory32 { addr: u32, prefetchable: bool },
245 Memory64 { addr: u64, prefetchable: bool },
246}
247
248#[derive(Clone, Copy, PartialEq, Eq)]
250pub struct PciAddress {
251 pub bus: u8,
252 pub device: u8,
253 pub function: u8,
254}
255
256impl fmt::Debug for PciAddress {
257 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258 write!(f, "{:02x}:{:02x}.{}", self.bus, self.device, self.function)
259 }
260}
261
262impl PciAddress {
263 pub const fn new(bus: u8, device: u8, function: u8) -> Self {
265 Self {
266 bus,
267 device,
268 function,
269 }
270 }
271
272 fn config_address(&self, offset: u8) -> u32 {
274 let bus = self.bus as u32;
275 let device = (self.device as u32) & 0x1F;
276 let function = (self.function as u32) & 0x07;
277 let offset = (offset as u32) & 0xFC;
278
279 0x8000_0000 | (bus << 16) | (device << 11) | (function << 8) | offset
280 }
281}
282
283#[derive(Clone, Copy)]
285pub struct PciDevice {
286 pub address: PciAddress,
287 pub vendor_id: u16,
288 pub device_id: u16,
289 pub class_code: u8,
290 pub subclass: u8,
291 pub prog_if: u8,
292 pub revision: u8,
293 pub header_type: u8,
294 pub interrupt_line: u8,
295 pub interrupt_pin: u8,
296}
297
298impl fmt::Debug for PciDevice {
299 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300 write!(
301 f,
302 "PciDevice({:?} ID {:04x}:{:04x} Class {:02x}:{:02x})",
303 self.address, self.vendor_id, self.device_id, self.class_code, self.subclass
304 )
305 }
306}
307
308impl PciDevice {
309 pub fn read_config_u8(&self, offset: u8) -> u8 {
311 let addr = self.address.config_address(offset & !0x03);
312 let shift = (offset & 0x03) * 8;
313
314 let _lock = PCI_IO_LOCK.lock();
315 unsafe {
316 outl(CONFIG_ADDRESS, addr);
317 ((inl(CONFIG_DATA) >> shift) & 0xFF) as u8
318 }
319 }
320
321 pub fn read_config_u16(&self, offset: u8) -> u16 {
323 let addr = self.address.config_address(offset & !0x03);
324 let shift = (offset & 0x02) * 8;
325
326 let _lock = PCI_IO_LOCK.lock();
327 unsafe {
328 outl(CONFIG_ADDRESS, addr);
329 ((inl(CONFIG_DATA) >> shift) & 0xFFFF) as u16
330 }
331 }
332
333 pub fn read_config_u32(&self, offset: u8) -> u32 {
335 let addr = self.address.config_address(offset);
336
337 let _lock = PCI_IO_LOCK.lock();
338 unsafe {
339 outl(CONFIG_ADDRESS, addr);
340 inl(CONFIG_DATA)
341 }
342 }
343
344 pub fn write_config_u8(&self, offset: u8, value: u8) {
346 let addr = self.address.config_address(offset & !0x03);
347 let shift = (offset & 0x03) * 8;
348
349 let _lock = PCI_IO_LOCK.lock();
350 unsafe {
351 outl(CONFIG_ADDRESS, addr);
352 let old = inl(CONFIG_DATA);
353 let mask = !(0xFF << shift);
354 let new = (old & mask) | ((value as u32) << shift);
355 outl(CONFIG_ADDRESS, addr);
356 outl(CONFIG_DATA, new);
357 }
358 }
359
360 pub fn write_config_u16(&self, offset: u8, value: u16) {
362 let addr = self.address.config_address(offset & !0x03);
363 let shift = (offset & 0x02) * 8;
364
365 let _lock = PCI_IO_LOCK.lock();
366 unsafe {
367 outl(CONFIG_ADDRESS, addr);
368 let old = inl(CONFIG_DATA);
369 let mask = !(0xFFFF << shift);
370 let new = (old & mask) | ((value as u32) << shift);
371 outl(CONFIG_ADDRESS, addr);
372 outl(CONFIG_DATA, new);
373 }
374 }
375
376 pub fn write_config_u32(&self, offset: u8, value: u32) {
378 let addr = self.address.config_address(offset);
379
380 let _lock = PCI_IO_LOCK.lock();
381 unsafe {
382 outl(CONFIG_ADDRESS, addr);
383 outl(CONFIG_DATA, value);
384 }
385 }
386
387 pub fn read_bar(&self, bar_index: u8) -> Option<Bar> {
389 if bar_index > 5 {
390 return None;
391 }
392
393 let offset = config::BAR0 + (bar_index * 4);
394 let bar_low = self.read_config_u32(offset);
395
396 if bar_low == 0 {
397 return None;
398 }
399
400 if bar_low & 0x1 != 0 {
402 let port = (bar_low & 0xFFFF_FFFC) as u16;
403 Some(Bar::Io { port })
404 } else {
405 let bar_type = (bar_low >> 1) & 0x3;
406 let prefetchable = (bar_low >> 3) & 0x1 != 0;
407
408 match bar_type {
409 0 => {
410 let addr = bar_low & 0xFFFF_FFF0;
411 Some(Bar::Memory32 { addr, prefetchable })
412 }
413 2 => {
414 if bar_index >= 5 {
415 return None;
416 }
417 let bar_high = self.read_config_u32(offset + 4);
418 let addr = ((bar_high as u64) << 32) | ((bar_low & 0xFFFF_FFF0) as u64);
419 Some(Bar::Memory64 { addr, prefetchable })
420 }
421 _ => None,
422 }
423 }
424 }
425
426 pub fn read_bar_raw(&self, bar_index: u8) -> Option<u64> {
428 match self.read_bar(bar_index) {
429 Some(Bar::Io { port }) => Some(port as u64),
430 Some(Bar::Memory32 { addr, .. }) => Some(addr as u64),
431 Some(Bar::Memory64 { addr, .. }) => Some(addr),
432 None => None,
433 }
434 }
435
436 pub fn enable_bus_master(&self) {
438 let mut cmd = self.read_config_u16(config::COMMAND);
439 cmd |= command::BUS_MASTER;
440 self.write_config_u16(config::COMMAND, cmd);
441 }
442
443 pub fn enable_memory_space(&self) {
445 let mut cmd = self.read_config_u16(config::COMMAND);
446 cmd |= command::MEMORY_SPACE;
447 self.write_config_u16(config::COMMAND, cmd);
448 }
449
450 pub fn enable_io_space(&self) {
452 let mut cmd = self.read_config_u16(config::COMMAND);
453 cmd |= command::IO_SPACE;
454 self.write_config_u16(config::COMMAND, cmd);
455 }
456}
457
458pub struct PciScanner {
487 bus_queue: [u8; 256],
488 queue_head: usize,
489 queue_tail: usize,
490 seen_buses: [bool; 256],
491 device: u8,
492 function: u8,
493 is_multi_function: bool,
496}
497
498impl PciScanner {
499 pub fn new() -> Self {
500 let mut s = Self {
501 bus_queue: [0u8; 256],
502 queue_head: 0,
503 queue_tail: 1,
504 seen_buses: [false; 256],
505 device: 0,
506 function: 0,
507 is_multi_function: false,
508 };
509 s.seen_buses[0] = true;
510 s
511 }
512
513 fn enqueue_bus(&mut self, bus: u8) {
514 if !self.seen_buses[bus as usize] && self.queue_tail < 256 {
515 self.seen_buses[bus as usize] = true;
516 self.bus_queue[self.queue_tail] = bus;
517 self.queue_tail += 1;
518 }
519 }
520
521 #[inline]
522 fn advance_to_next_device(&mut self) {
523 self.function = 0;
524 self.device += 1;
525 self.is_multi_function = false;
526 }
527}
528
529impl Iterator for PciScanner {
530 type Item = PciDevice;
531
532 fn next(&mut self) -> Option<Self::Item> {
533 loop {
534 if self.queue_head >= self.queue_tail {
536 return None;
537 }
538 let bus = self.bus_queue[self.queue_head];
539
540 if self.device >= 32 {
541 self.queue_head += 1;
542 self.device = 0;
543 self.function = 0;
544 self.is_multi_function = false;
545 continue;
546 }
547
548 let current_function = self.function;
549
550 if current_function == 0 {
552 let word00 = raw_config_read(bus, self.device, 0, 0x00);
555 let vendor_id = (word00 & 0xFFFF) as u16;
556 if is_absent_vendor(vendor_id) {
557 self.advance_to_next_device();
558 continue;
559 }
560
561 let Some(dev) = probe_from_word00(PciAddress::new(bus, self.device, 0), word00)
563 else {
564 self.advance_to_next_device();
565 continue;
566 };
567
568 self.is_multi_function = dev.header_type & 0x80 != 0;
570
571 if dev.header_type & 0x7F == 0x01 {
573 let secondary = raw_config_read_u8(bus, self.device, 0, 0x19);
574 self.enqueue_bus(secondary);
575 }
576
577 if self.is_multi_function {
580 self.function = 1;
581 } else {
582 self.advance_to_next_device();
583 }
584
585 return Some(dev);
586 }
587
588 debug_assert!(self.is_multi_function);
590
591 self.function += 1;
592 if self.function >= 8 {
593 self.advance_to_next_device();
594 }
595
596 let address = PciAddress::new(bus, self.device, current_function);
597 let Some(dev) = probe_device_full(address) else {
598 continue;
599 };
600
601 if dev.header_type & 0x7F == 0x01 {
602 let secondary = raw_config_read_u8(bus, self.device, current_function, 0x19);
603 self.enqueue_bus(secondary);
604 }
605
606 return Some(dev);
607 }
608 }
609}
610
611fn is_absent_vendor(vendor_id: u16) -> bool {
616 vendor_id == 0xFFFF || vendor_id == 0x0000
617}
618
619fn quirk_zero_irq_line(vendor_id: u16, device_id: u16, irq_line: u8) -> u8 {
620 if irq_line != 0xFF {
621 return irq_line;
622 }
623 if PCI_IRQ_LINE_ZERO_IF_FF
624 .iter()
625 .any(|q| q.vendor_id == vendor_id && q.device_id == device_id)
626 {
627 return 0;
628 }
629 0
630}
631
632fn valid_header_type(header_type: u8) -> bool {
633 matches!(header_type & 0x7F, 0x00..=0x02)
634}
635
636fn is_ghost_device(class_code: u8, subclass: u8, prog_if: u8) -> bool {
637 class_code == 0xFF && subclass == 0xFF && prog_if == 0xFF
638}
639
640#[inline]
643fn raw_config_read(bus: u8, device: u8, function: u8, offset: u8) -> u32 {
644 let addr = 0x8000_0000u32
645 | ((bus as u32) << 16)
646 | (((device as u32) & 0x1F) << 11)
647 | (((function as u32) & 0x07) << 8)
648 | ((offset as u32) & 0xFC);
649 let _lock = PCI_IO_LOCK.lock();
650 unsafe {
651 outl(CONFIG_ADDRESS, addr);
652 inl(CONFIG_DATA)
653 }
654}
655
656#[inline]
658fn raw_config_read_u8(bus: u8, device: u8, function: u8, offset: u8) -> u8 {
659 let dword = raw_config_read(bus, device, function, offset & !0x03);
660 let shift = (offset & 0x03) * 8;
661 ((dword >> shift) & 0xFF) as u8
662}
663
664fn probe_device_full(address: PciAddress) -> Option<PciDevice> {
669 let _lock = PCI_IO_LOCK.lock();
670
671 let word00 = unsafe {
672 outl(CONFIG_ADDRESS, address.config_address(0x00));
673 inl(CONFIG_DATA)
674 };
675 let vendor_id = (word00 & 0xFFFF) as u16;
676 if is_absent_vendor(vendor_id) {
677 return None;
678 }
679 let device_id = (word00 >> 16) as u16;
680 if device_id == 0xFFFF || device_id == 0x0000 {
681 return None;
682 }
683
684 let word08 = unsafe {
685 outl(CONFIG_ADDRESS, address.config_address(0x08));
686 inl(CONFIG_DATA)
687 };
688 let word0c = unsafe {
689 outl(CONFIG_ADDRESS, address.config_address(0x0C));
690 inl(CONFIG_DATA)
691 };
692
693 let header_type = ((word0c >> 16) & 0xFF) as u8;
694 if !valid_header_type(header_type) {
695 return None;
696 }
697
698 let class_code = ((word08 >> 24) & 0xFF) as u8;
699 let subclass = ((word08 >> 16) & 0xFF) as u8;
700 let prog_if = ((word08 >> 8) & 0xFF) as u8;
701 if is_ghost_device(class_code, subclass, prog_if) {
702 return None;
703 }
704
705 let word3c = unsafe {
706 outl(CONFIG_ADDRESS, address.config_address(0x3C));
707 inl(CONFIG_DATA)
708 };
709 let interrupt_line = quirk_zero_irq_line(vendor_id, device_id, (word3c & 0xFF) as u8);
710
711 Some(PciDevice {
712 address,
713 vendor_id,
714 device_id,
715 class_code,
716 subclass,
717 prog_if,
718 revision: (word08 & 0xFF) as u8,
719 header_type,
720 interrupt_line,
721 interrupt_pin: ((word3c >> 8) & 0xFF) as u8,
722 })
723}
724
725fn probe_from_word00(address: PciAddress, word00: u32) -> Option<PciDevice> {
728 let vendor_id = (word00 & 0xFFFF) as u16;
729 let device_id = (word00 >> 16) as u16;
730 if device_id == 0xFFFF || device_id == 0x0000 {
731 return None;
732 }
733
734 let _lock = PCI_IO_LOCK.lock();
735
736 let word08 = unsafe {
737 outl(CONFIG_ADDRESS, address.config_address(0x08));
738 inl(CONFIG_DATA)
739 };
740 let word0c = unsafe {
741 outl(CONFIG_ADDRESS, address.config_address(0x0C));
742 inl(CONFIG_DATA)
743 };
744
745 let header_type = ((word0c >> 16) & 0xFF) as u8;
746 if !valid_header_type(header_type) {
747 return None;
748 }
749
750 let class_code = ((word08 >> 24) & 0xFF) as u8;
751 let subclass = ((word08 >> 16) & 0xFF) as u8;
752 let prog_if = ((word08 >> 8) & 0xFF) as u8;
753 if is_ghost_device(class_code, subclass, prog_if) {
754 return None;
755 }
756
757 let word3c = unsafe {
758 outl(CONFIG_ADDRESS, address.config_address(0x3C));
759 inl(CONFIG_DATA)
760 };
761 let interrupt_line = quirk_zero_irq_line(vendor_id, device_id, (word3c & 0xFF) as u8);
762
763 Some(PciDevice {
764 address,
765 vendor_id,
766 device_id,
767 class_code,
768 subclass,
769 prog_if,
770 revision: (word08 & 0xFF) as u8,
771 header_type,
772 interrupt_line,
773 interrupt_pin: ((word3c >> 8) & 0xFF) as u8,
774 })
775}
776
777static PCI_DEVICE_CACHE: SpinLock<Option<Vec<PciDevice>>> = SpinLock::new(None);
797
798fn with_cache<R>(f: impl FnOnce(&[PciDevice]) -> R) -> R {
804 let mut cache = PCI_DEVICE_CACHE.lock();
805 if cache.is_none() {
806 let dummy = 0u64;
808 let rsp = &dummy as *const u64 as u64;
809 crate::serial_println!("[PCI] Scanning PCI bus, rsp={:#x}", rsp);
810 let devices: Vec<PciDevice> = PciScanner::new().collect();
811 crate::serial_println!("[PCI] PCI scan complete, found {} devices", devices.len());
812 for dev in &devices {
813 crate::serial_println!(
814 "[PCI] {:02x}:{:02x}.{:x} vendor={:04x} device={:04x} class={:02x}:{:02x}",
815 dev.address.bus,
816 dev.address.device,
817 dev.address.function,
818 dev.vendor_id,
819 dev.device_id,
820 dev.class_code,
821 dev.subclass
822 );
823 }
824 *cache = Some(devices);
825 }
826 f(cache.as_deref().unwrap_or(&[]))
827}
828
829pub fn find_device(vendor_id: u16, device_id: u16) -> Option<PciDevice> {
831 with_cache(|devs| {
832 devs.iter()
833 .copied()
834 .find(|dev| dev.vendor_id == vendor_id && dev.device_id == device_id)
835 })
836}
837
838pub fn find_virtio_devices() -> Vec<PciDevice> {
840 find_devices_by_vendor(vendor::VIRTIO)
841}
842
843pub fn find_virtio_device(device_id: u16) -> Option<PciDevice> {
845 find_device(vendor::VIRTIO, device_id)
846}
847
848pub fn all_devices() -> Vec<PciDevice> {
850 with_cache(|devs| devs.to_vec())
851}
852
853pub fn find_devices_by_vendor(vendor_id: u16) -> Vec<PciDevice> {
855 with_cache(|devs| {
856 devs.iter()
857 .copied()
858 .filter(|dev| dev.vendor_id == vendor_id)
859 .collect()
860 })
861}
862
863pub fn find_devices_by_class(class_code: u8, subclass: u8) -> Vec<PciDevice> {
865 with_cache(|devs| {
866 devs.iter()
867 .copied()
868 .filter(|dev| dev.class_code == class_code && dev.subclass == subclass)
869 .collect()
870 })
871}
872
873#[derive(Debug, Clone, Copy, Default)]
877pub struct ProbeCriteria {
878 pub vendor_id: Option<u16>,
879 pub device_id: Option<u16>,
880 pub class_code: Option<u8>,
881 pub subclass: Option<u8>,
882 pub prog_if: Option<u8>,
883}
884
885impl ProbeCriteria {
886 pub const fn any() -> Self {
887 Self {
888 vendor_id: None,
889 device_id: None,
890 class_code: None,
891 subclass: None,
892 prog_if: None,
893 }
894 }
895
896 fn matches(&self, dev: &PciDevice) -> bool {
897 if self.vendor_id.is_some_and(|v| dev.vendor_id != v) {
898 return false;
899 }
900 if self.device_id.is_some_and(|d| dev.device_id != d) {
901 return false;
902 }
903 if self.class_code.is_some_and(|c| dev.class_code != c) {
904 return false;
905 }
906 if self.subclass.is_some_and(|s| dev.subclass != s) {
907 return false;
908 }
909 if self.prog_if.is_some_and(|p| dev.prog_if != p) {
910 return false;
911 }
912 true
913 }
914}
915
916pub fn probe_all(criteria: ProbeCriteria) -> Vec<PciDevice> {
918 with_cache(|devs| {
919 devs.iter()
920 .copied()
921 .filter(|dev| criteria.matches(dev))
922 .collect()
923 })
924}
925
926pub fn probe_first(criteria: ProbeCriteria) -> Option<PciDevice> {
928 with_cache(|devs| devs.iter().copied().find(|dev| criteria.matches(dev)))
929}
930
931pub fn invalidate_cache() {
935 *PCI_DEVICE_CACHE.lock() = None;
936}