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}
153
154pub mod command {
156 pub const IO_SPACE: u16 = 1 << 0;
157 pub const MEMORY_SPACE: u16 = 1 << 1;
158 pub const BUS_MASTER: u16 = 1 << 2;
159 pub const SPECIAL_CYCLES: u16 = 1 << 3;
160 pub const MWI_ENABLE: u16 = 1 << 4;
161 pub const VGA_PALETTE_SNOOP: u16 = 1 << 5;
162 pub const PARITY_ERROR_RESPONSE: u16 = 1 << 6;
163 pub const STEPPING_CONTROL: u16 = 1 << 7;
164 pub const SERR_ENABLE: u16 = 1 << 8;
165 pub const FAST_BACK_TO_BACK: u16 = 1 << 9;
166 pub const INTERRUPT_DISABLE: u16 = 1 << 10;
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171pub enum Bar {
172 Io { port: u16 },
173 Memory32 { addr: u32, prefetchable: bool },
174 Memory64 { addr: u64, prefetchable: bool },
175}
176
177#[derive(Clone, Copy, PartialEq, Eq)]
179pub struct PciAddress {
180 pub bus: u8,
181 pub device: u8,
182 pub function: u8,
183}
184
185impl fmt::Debug for PciAddress {
186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187 write!(f, "{:02x}:{:02x}.{}", self.bus, self.device, self.function)
188 }
189}
190
191impl PciAddress {
192 pub const fn new(bus: u8, device: u8, function: u8) -> Self {
194 Self {
195 bus,
196 device,
197 function,
198 }
199 }
200
201 fn config_address(&self, offset: u8) -> u32 {
203 let bus = self.bus as u32;
204 let device = (self.device as u32) & 0x1F;
205 let function = (self.function as u32) & 0x07;
206 let offset = (offset as u32) & 0xFC;
207
208 0x8000_0000 | (bus << 16) | (device << 11) | (function << 8) | offset
209 }
210}
211
212#[derive(Clone, Copy)]
214pub struct PciDevice {
215 pub address: PciAddress,
216 pub vendor_id: u16,
217 pub device_id: u16,
218 pub class_code: u8,
219 pub subclass: u8,
220 pub prog_if: u8,
221 pub revision: u8,
222 pub header_type: u8,
223 pub interrupt_line: u8,
224 pub interrupt_pin: u8,
225}
226
227impl fmt::Debug for PciDevice {
228 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229 write!(
230 f,
231 "PciDevice({:?} ID {:04x}:{:04x} Class {:02x}:{:02x})",
232 self.address, self.vendor_id, self.device_id, self.class_code, self.subclass
233 )
234 }
235}
236
237impl PciDevice {
238 pub fn read_config_u8(&self, offset: u8) -> u8 {
240 let addr = self.address.config_address(offset & !0x03);
241 let shift = (offset & 0x03) * 8;
242
243 let _lock = PCI_IO_LOCK.lock();
244 unsafe {
245 outl(CONFIG_ADDRESS, addr);
246 ((inl(CONFIG_DATA) >> shift) & 0xFF) as u8
247 }
248 }
249
250 pub fn read_config_u16(&self, offset: u8) -> u16 {
252 let addr = self.address.config_address(offset & !0x03);
253 let shift = (offset & 0x02) * 8;
254
255 let _lock = PCI_IO_LOCK.lock();
256 unsafe {
257 outl(CONFIG_ADDRESS, addr);
258 ((inl(CONFIG_DATA) >> shift) & 0xFFFF) as u16
259 }
260 }
261
262 pub fn read_config_u32(&self, offset: u8) -> u32 {
264 let addr = self.address.config_address(offset);
265
266 let _lock = PCI_IO_LOCK.lock();
267 unsafe {
268 outl(CONFIG_ADDRESS, addr);
269 inl(CONFIG_DATA)
270 }
271 }
272
273 pub fn write_config_u8(&self, offset: u8, value: u8) {
275 let addr = self.address.config_address(offset & !0x03);
276 let shift = (offset & 0x03) * 8;
277
278 let _lock = PCI_IO_LOCK.lock();
279 unsafe {
280 outl(CONFIG_ADDRESS, addr);
281 let old = inl(CONFIG_DATA);
282 let mask = !(0xFF << shift);
283 let new = (old & mask) | ((value as u32) << shift);
284 outl(CONFIG_ADDRESS, addr);
285 outl(CONFIG_DATA, new);
286 }
287 }
288
289 pub fn write_config_u16(&self, offset: u8, value: u16) {
291 let addr = self.address.config_address(offset & !0x03);
292 let shift = (offset & 0x02) * 8;
293
294 let _lock = PCI_IO_LOCK.lock();
295 unsafe {
296 outl(CONFIG_ADDRESS, addr);
297 let old = inl(CONFIG_DATA);
298 let mask = !(0xFFFF << shift);
299 let new = (old & mask) | ((value as u32) << shift);
300 outl(CONFIG_ADDRESS, addr);
301 outl(CONFIG_DATA, new);
302 }
303 }
304
305 pub fn write_config_u32(&self, offset: u8, value: u32) {
307 let addr = self.address.config_address(offset);
308
309 let _lock = PCI_IO_LOCK.lock();
310 unsafe {
311 outl(CONFIG_ADDRESS, addr);
312 outl(CONFIG_DATA, value);
313 }
314 }
315
316 pub fn read_bar(&self, bar_index: u8) -> Option<Bar> {
318 if bar_index > 5 {
319 return None;
320 }
321
322 let offset = config::BAR0 + (bar_index * 4);
323 let bar_low = self.read_config_u32(offset);
324
325 if bar_low == 0 {
326 return None;
327 }
328
329 if bar_low & 0x1 != 0 {
331 let port = (bar_low & 0xFFFF_FFFC) as u16;
332 Some(Bar::Io { port })
333 } else {
334 let bar_type = (bar_low >> 1) & 0x3;
335 let prefetchable = (bar_low >> 3) & 0x1 != 0;
336
337 match bar_type {
338 0 => {
339 let addr = bar_low & 0xFFFF_FFF0;
340 Some(Bar::Memory32 { addr, prefetchable })
341 }
342 2 => {
343 if bar_index >= 5 {
344 return None;
345 }
346 let bar_high = self.read_config_u32(offset + 4);
347 let addr = ((bar_high as u64) << 32) | ((bar_low & 0xFFFF_FFF0) as u64);
348 Some(Bar::Memory64 { addr, prefetchable })
349 }
350 _ => None,
351 }
352 }
353 }
354
355 pub fn read_bar_raw(&self, bar_index: u8) -> Option<u64> {
357 match self.read_bar(bar_index) {
358 Some(Bar::Io { port }) => Some(port as u64),
359 Some(Bar::Memory32 { addr, .. }) => Some(addr as u64),
360 Some(Bar::Memory64 { addr, .. }) => Some(addr),
361 None => None,
362 }
363 }
364
365 pub fn enable_bus_master(&self) {
367 let mut cmd = self.read_config_u16(config::COMMAND);
368 cmd |= command::BUS_MASTER;
369 self.write_config_u16(config::COMMAND, cmd);
370 }
371
372 pub fn enable_memory_space(&self) {
374 let mut cmd = self.read_config_u16(config::COMMAND);
375 cmd |= command::MEMORY_SPACE;
376 self.write_config_u16(config::COMMAND, cmd);
377 }
378
379 pub fn enable_io_space(&self) {
381 let mut cmd = self.read_config_u16(config::COMMAND);
382 cmd |= command::IO_SPACE;
383 self.write_config_u16(config::COMMAND, cmd);
384 }
385}
386
387pub struct PciScanner {
416 bus_queue: [u8; 256],
417 queue_head: usize,
418 queue_tail: usize,
419 seen_buses: [bool; 256],
420 device: u8,
421 function: u8,
422 is_multi_function: bool,
425}
426
427impl PciScanner {
428 pub fn new() -> Self {
429 let mut s = Self {
430 bus_queue: [0u8; 256],
431 queue_head: 0,
432 queue_tail: 1,
433 seen_buses: [false; 256],
434 device: 0,
435 function: 0,
436 is_multi_function: false,
437 };
438 s.seen_buses[0] = true;
439 s
440 }
441
442 fn enqueue_bus(&mut self, bus: u8) {
443 if !self.seen_buses[bus as usize] && self.queue_tail < 256 {
444 self.seen_buses[bus as usize] = true;
445 self.bus_queue[self.queue_tail] = bus;
446 self.queue_tail += 1;
447 }
448 }
449
450 #[inline]
451 fn advance_to_next_device(&mut self) {
452 self.function = 0;
453 self.device += 1;
454 self.is_multi_function = false;
455 }
456}
457
458impl Iterator for PciScanner {
459 type Item = PciDevice;
460
461 fn next(&mut self) -> Option<Self::Item> {
462 loop {
463 if self.queue_head >= self.queue_tail {
465 return None;
466 }
467 let bus = self.bus_queue[self.queue_head];
468
469 if self.device >= 32 {
470 self.queue_head += 1;
471 self.device = 0;
472 self.function = 0;
473 self.is_multi_function = false;
474 continue;
475 }
476
477 let current_function = self.function;
478
479 if current_function == 0 {
481 let word00 = raw_config_read(bus, self.device, 0, 0x00);
484 let vendor_id = (word00 & 0xFFFF) as u16;
485 if is_absent_vendor(vendor_id) {
486 self.advance_to_next_device();
487 continue;
488 }
489
490 let Some(dev) = probe_from_word00(PciAddress::new(bus, self.device, 0), word00)
492 else {
493 self.advance_to_next_device();
494 continue;
495 };
496
497 self.is_multi_function = dev.header_type & 0x80 != 0;
499
500 if dev.header_type & 0x7F == 0x01 {
502 let secondary = raw_config_read_u8(bus, self.device, 0, 0x19);
503 self.enqueue_bus(secondary);
504 }
505
506 if self.is_multi_function {
509 self.function = 1;
510 } else {
511 self.advance_to_next_device();
512 }
513
514 return Some(dev);
515 }
516
517 debug_assert!(self.is_multi_function);
519
520 self.function += 1;
521 if self.function >= 8 {
522 self.advance_to_next_device();
523 }
524
525 let address = PciAddress::new(bus, self.device, current_function);
526 let Some(dev) = probe_device_full(address) else {
527 continue;
528 };
529
530 if dev.header_type & 0x7F == 0x01 {
531 let secondary = raw_config_read_u8(bus, self.device, current_function, 0x19);
532 self.enqueue_bus(secondary);
533 }
534
535 return Some(dev);
536 }
537 }
538}
539
540fn is_absent_vendor(vendor_id: u16) -> bool {
545 vendor_id == 0xFFFF || vendor_id == 0x0000
546}
547
548fn quirk_zero_irq_line(vendor_id: u16, device_id: u16, irq_line: u8) -> u8 {
549 if irq_line != 0xFF {
550 return irq_line;
551 }
552 if PCI_IRQ_LINE_ZERO_IF_FF
553 .iter()
554 .any(|q| q.vendor_id == vendor_id && q.device_id == device_id)
555 {
556 return 0;
557 }
558 0
559}
560
561fn valid_header_type(header_type: u8) -> bool {
562 matches!(header_type & 0x7F, 0x00..=0x02)
563}
564
565fn is_ghost_device(class_code: u8, subclass: u8, prog_if: u8) -> bool {
566 class_code == 0xFF && subclass == 0xFF && prog_if == 0xFF
567}
568
569#[inline]
572fn raw_config_read(bus: u8, device: u8, function: u8, offset: u8) -> u32 {
573 let addr = 0x8000_0000u32
574 | ((bus as u32) << 16)
575 | (((device as u32) & 0x1F) << 11)
576 | (((function as u32) & 0x07) << 8)
577 | ((offset as u32) & 0xFC);
578 let _lock = PCI_IO_LOCK.lock();
579 unsafe {
580 outl(CONFIG_ADDRESS, addr);
581 inl(CONFIG_DATA)
582 }
583}
584
585#[inline]
587fn raw_config_read_u8(bus: u8, device: u8, function: u8, offset: u8) -> u8 {
588 let dword = raw_config_read(bus, device, function, offset & !0x03);
589 let shift = (offset & 0x03) * 8;
590 ((dword >> shift) & 0xFF) as u8
591}
592
593fn probe_device_full(address: PciAddress) -> Option<PciDevice> {
598 let _lock = PCI_IO_LOCK.lock();
599
600 let word00 = unsafe {
601 outl(CONFIG_ADDRESS, address.config_address(0x00));
602 inl(CONFIG_DATA)
603 };
604 let vendor_id = (word00 & 0xFFFF) as u16;
605 if is_absent_vendor(vendor_id) {
606 return None;
607 }
608 let device_id = (word00 >> 16) as u16;
609 if device_id == 0xFFFF || device_id == 0x0000 {
610 return None;
611 }
612
613 let word08 = unsafe {
614 outl(CONFIG_ADDRESS, address.config_address(0x08));
615 inl(CONFIG_DATA)
616 };
617 let word0c = unsafe {
618 outl(CONFIG_ADDRESS, address.config_address(0x0C));
619 inl(CONFIG_DATA)
620 };
621
622 let header_type = ((word0c >> 16) & 0xFF) as u8;
623 if !valid_header_type(header_type) {
624 return None;
625 }
626
627 let class_code = ((word08 >> 24) & 0xFF) as u8;
628 let subclass = ((word08 >> 16) & 0xFF) as u8;
629 let prog_if = ((word08 >> 8) & 0xFF) as u8;
630 if is_ghost_device(class_code, subclass, prog_if) {
631 return None;
632 }
633
634 let word3c = unsafe {
635 outl(CONFIG_ADDRESS, address.config_address(0x3C));
636 inl(CONFIG_DATA)
637 };
638 let interrupt_line = quirk_zero_irq_line(vendor_id, device_id, (word3c & 0xFF) as u8);
639
640 Some(PciDevice {
641 address,
642 vendor_id,
643 device_id,
644 class_code,
645 subclass,
646 prog_if,
647 revision: (word08 & 0xFF) as u8,
648 header_type,
649 interrupt_line,
650 interrupt_pin: ((word3c >> 8) & 0xFF) as u8,
651 })
652}
653
654fn probe_from_word00(address: PciAddress, word00: u32) -> Option<PciDevice> {
657 let vendor_id = (word00 & 0xFFFF) as u16;
658 let device_id = (word00 >> 16) as u16;
659 if device_id == 0xFFFF || device_id == 0x0000 {
660 return None;
661 }
662
663 let _lock = PCI_IO_LOCK.lock();
664
665 let word08 = unsafe {
666 outl(CONFIG_ADDRESS, address.config_address(0x08));
667 inl(CONFIG_DATA)
668 };
669 let word0c = unsafe {
670 outl(CONFIG_ADDRESS, address.config_address(0x0C));
671 inl(CONFIG_DATA)
672 };
673
674 let header_type = ((word0c >> 16) & 0xFF) as u8;
675 if !valid_header_type(header_type) {
676 return None;
677 }
678
679 let class_code = ((word08 >> 24) & 0xFF) as u8;
680 let subclass = ((word08 >> 16) & 0xFF) as u8;
681 let prog_if = ((word08 >> 8) & 0xFF) as u8;
682 if is_ghost_device(class_code, subclass, prog_if) {
683 return None;
684 }
685
686 let word3c = unsafe {
687 outl(CONFIG_ADDRESS, address.config_address(0x3C));
688 inl(CONFIG_DATA)
689 };
690 let interrupt_line = quirk_zero_irq_line(vendor_id, device_id, (word3c & 0xFF) as u8);
691
692 Some(PciDevice {
693 address,
694 vendor_id,
695 device_id,
696 class_code,
697 subclass,
698 prog_if,
699 revision: (word08 & 0xFF) as u8,
700 header_type,
701 interrupt_line,
702 interrupt_pin: ((word3c >> 8) & 0xFF) as u8,
703 })
704}
705
706static PCI_DEVICE_CACHE: SpinLock<Option<Vec<PciDevice>>> = SpinLock::new(None);
716
717fn with_cache<R>(f: impl FnOnce(&[PciDevice]) -> R) -> R {
723 let mut cache = PCI_DEVICE_CACHE.lock();
724 if cache.is_none() {
725 let dummy = 0u64;
727 let rsp = &dummy as *const u64 as u64;
728 crate::serial_println!("[PCI] Scanning PCI bus, rsp={:#x}", rsp);
729 let devices: Vec<PciDevice> = PciScanner::new().collect();
730 crate::serial_println!("[PCI] PCI scan complete, found {} devices", devices.len());
731 for dev in &devices {
732 crate::serial_println!(
733 "[PCI] {:02x}:{:02x}.{:x} vendor={:04x} device={:04x} class={:02x}:{:02x}",
734 dev.address.bus,
735 dev.address.device,
736 dev.address.function,
737 dev.vendor_id,
738 dev.device_id,
739 dev.class_code,
740 dev.subclass
741 );
742 }
743 *cache = Some(devices);
744 }
745 f(cache.as_deref().unwrap_or(&[]))
746}
747
748pub fn find_device(vendor_id: u16, device_id: u16) -> Option<PciDevice> {
750 with_cache(|devs| {
751 devs.iter()
752 .copied()
753 .find(|dev| dev.vendor_id == vendor_id && dev.device_id == device_id)
754 })
755}
756
757pub fn find_virtio_devices() -> Vec<PciDevice> {
759 find_devices_by_vendor(vendor::VIRTIO)
760}
761
762pub fn find_virtio_device(device_id: u16) -> Option<PciDevice> {
764 find_device(vendor::VIRTIO, device_id)
765}
766
767pub fn all_devices() -> Vec<PciDevice> {
769 with_cache(|devs| devs.to_vec())
770}
771
772pub fn find_devices_by_vendor(vendor_id: u16) -> Vec<PciDevice> {
774 with_cache(|devs| {
775 devs.iter()
776 .copied()
777 .filter(|dev| dev.vendor_id == vendor_id)
778 .collect()
779 })
780}
781
782pub fn find_devices_by_class(class_code: u8, subclass: u8) -> Vec<PciDevice> {
784 with_cache(|devs| {
785 devs.iter()
786 .copied()
787 .filter(|dev| dev.class_code == class_code && dev.subclass == subclass)
788 .collect()
789 })
790}
791
792#[derive(Debug, Clone, Copy, Default)]
796pub struct ProbeCriteria {
797 pub vendor_id: Option<u16>,
798 pub device_id: Option<u16>,
799 pub class_code: Option<u8>,
800 pub subclass: Option<u8>,
801 pub prog_if: Option<u8>,
802}
803
804impl ProbeCriteria {
805 pub const fn any() -> Self {
806 Self {
807 vendor_id: None,
808 device_id: None,
809 class_code: None,
810 subclass: None,
811 prog_if: None,
812 }
813 }
814
815 fn matches(&self, dev: &PciDevice) -> bool {
816 if self.vendor_id.is_some_and(|v| dev.vendor_id != v) {
817 return false;
818 }
819 if self.device_id.is_some_and(|d| dev.device_id != d) {
820 return false;
821 }
822 if self.class_code.is_some_and(|c| dev.class_code != c) {
823 return false;
824 }
825 if self.subclass.is_some_and(|s| dev.subclass != s) {
826 return false;
827 }
828 if self.prog_if.is_some_and(|p| dev.prog_if != p) {
829 return false;
830 }
831 true
832 }
833}
834
835pub fn probe_all(criteria: ProbeCriteria) -> Vec<PciDevice> {
837 with_cache(|devs| {
838 devs.iter()
839 .copied()
840 .filter(|dev| criteria.matches(dev))
841 .collect()
842 })
843}
844
845pub fn probe_first(criteria: ProbeCriteria) -> Option<PciDevice> {
847 with_cache(|devs| devs.iter().copied().find(|dev| criteria.matches(dev)))
848}
849
850pub fn invalidate_cache() {
854 *PCI_DEVICE_CACHE.lock() = None;
855}