1extern crate alloc;
7
8use alloc::string::String;
9use alloc::vec::Vec;
10
11use super::config::{PciConfig, vendor};
12use super::device::PciDeviceInfo;
13use super::{PciAddress, PciBus};
14use crate::early_println;
15
16pub struct PciScanner<'a> {
20 config: PciConfig,
22 bus: &'a PciBus,
24}
25
26impl<'a> PciScanner<'a> {
27 pub fn new(bus: &'a PciBus) -> Self {
33 let config = PciConfig::new(bus.ecam_base());
34 Self { config, bus }
35 }
36
37 pub fn scan(&self) -> Vec<PciDeviceInfo> {
46 let mut devices = Vec::new();
47 let mut device_id_counter = 0;
48
49 early_println!("Scanning PCI bus...");
50
51 self.scan_bus(0, &mut devices, &mut device_id_counter);
53
54 early_println!("PCI scan complete: found {} devices", devices.len());
55
56 devices
57 }
58
59 fn scan_bus(&self, bus: u8, devices: &mut Vec<PciDeviceInfo>, id_counter: &mut usize) {
61 for device in 0..32 {
63 self.scan_device(bus, device, devices, id_counter);
64 }
65 }
66
67 fn scan_device(
69 &self,
70 bus: u8,
71 device: u8,
72 devices: &mut Vec<PciDeviceInfo>,
73 id_counter: &mut usize,
74 ) {
75 let addr = PciAddress::new(0, bus, device, 0);
76
77 let vendor_id = self.config.read_vendor_id(&addr);
79 if vendor_id == vendor::INVALID {
80 return; }
82
83 let header_type = self.config.read_header_type(&addr);
85 let is_multifunction = (header_type & 0x80) != 0;
86
87 if let Some(device_info) = self.probe_function(bus, device, 0, id_counter) {
89 devices.push(device_info);
90 }
91
92 if is_multifunction {
94 for function in 1..8 {
95 if let Some(device_info) = self.probe_function(bus, device, function, id_counter) {
96 devices.push(device_info);
97 }
98 }
99 }
100 }
101
102 fn probe_function(
104 &self,
105 bus: u8,
106 device: u8,
107 function: u8,
108 id_counter: &mut usize,
109 ) -> Option<PciDeviceInfo> {
110 let addr = PciAddress::new(0, bus, device, function);
111
112 let vendor_id = self.config.read_vendor_id(&addr);
116 if vendor_id == vendor::INVALID {
117 return None;
118 }
119
120 early_println!(
121 "PCI: Found device with vendor {:04x} at {:02x}:{:02x}.{}",
122 vendor_id,
123 bus,
124 device,
125 function
126 );
127
128 let device_id = self.config.read_device_id(&addr);
130 let class_code = self.config.read_class_code(&addr);
131 let revision = self
132 .config
133 .read_u8(&addr, super::config::offset::REVISION_ID);
134 let subsystem_vendor_id = self
135 .config
136 .read_u16(&addr, super::config::offset::SUBSYSTEM_VENDOR_ID);
137 let subsystem_id = self
138 .config
139 .read_u16(&addr, super::config::offset::SUBSYSTEM_ID);
140 let interrupt_line = self
141 .config
142 .read_u8(&addr, super::config::offset::INTERRUPT_LINE);
143 let interrupt_pin = self
144 .config
145 .read_u8(&addr, super::config::offset::INTERRUPT_PIN);
146
147 let name = Self::generate_device_name(vendor_id, device_id, bus, device, function);
151
152 let device_info = PciDeviceInfo::new(
153 addr,
154 vendor_id,
155 device_id,
156 class_code,
157 revision,
158 subsystem_vendor_id,
159 subsystem_id,
160 interrupt_line,
161 interrupt_pin,
162 name,
163 *id_counter,
164 );
165
166 *id_counter += 1;
167
168 early_println!(
169 "Found PCI device: {:04x}:{:04x} at {:02x}:{:02x}.{} (class: {:06x})",
170 vendor_id,
171 device_id,
172 bus,
173 device,
174 function,
175 class_code
176 );
177
178 Some(device_info)
179 }
180
181 fn generate_device_name(
186 _vendor_id: u16,
187 _device_id: u16,
188 _bus: u8,
189 _device: u8,
190 _function: u8,
191 ) -> &'static str {
192 match _vendor_id {
196 vendor::INTEL => "intel_pci_device",
197 vendor::AMD => "amd_pci_device",
198 vendor::NVIDIA => "nvidia_pci_device",
199 vendor::REDHAT => "virtio_pci_device",
200 _ => "pci_device",
201 }
202 }
203}
204
205impl PciBus {
206 pub fn scan(&self) {
211 let scanner = PciScanner::new(self);
212 let devices = scanner.scan();
213
214 for device in devices {
216 self.add_device(device);
217 }
218 }
219
220 pub fn scan_and_register(&self) {
225 use crate::device::manager::DeviceManager;
226
227 self.scan();
228
229 let devices = self.devices();
230 let device_manager = DeviceManager::get_manager();
231
232 early_println!(
233 "Registering {} PCI devices with DeviceManager",
234 devices.len()
235 );
236
237 for device in devices {
238 let device_name = String::from(device.name());
239 early_println!(
242 " - {} ({:04x}:{:04x})",
243 device_name,
244 device.vendor_id(),
245 device.device_id()
246 );
247 }
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254
255 #[test_case]
256 fn test_pci_scanner_creation() {
257 let bus = PciBus::new(0x3000_0000, 0x1000_0000);
258 let _scanner = PciScanner::new(&bus);
259 }
261
262 #[test_case]
263 fn test_device_name_generation() {
264 let name = PciScanner::generate_device_name(vendor::INTEL, 0x1234, 0, 0, 0);
265 assert_eq!(name, "intel_pci_device");
266
267 let name = PciScanner::generate_device_name(vendor::REDHAT, 0x1000, 0, 0, 0);
268 assert_eq!(name, "virtio_pci_device");
269
270 let name = PciScanner::generate_device_name(0x9999, 0x1234, 0, 0, 0);
271 assert_eq!(name, "pci_device");
272 }
273
274 #[test_case]
275 fn test_pci_real_device_discovery() {
276 use crate::device::fdt::FdtManager;
279 use crate::early_println;
280
281 early_println!("[PCI Test] Starting real PCI device discovery test");
282
283 let fdt_manager = unsafe { FdtManager::get_mut_manager() };
285 let fdt = fdt_manager.get_fdt();
286
287 if fdt.is_none() {
288 early_println!("[PCI Test] No FDT available, skipping test");
289 return;
290 }
291
292 let fdt = fdt.unwrap();
293
294 let mut pci_found = false;
296 let mut ecam_base = 0;
297 let mut ecam_size = 0;
298
299 for node_name in &["/soc/pci", "/soc/pcie", "/pci", "/pcie"] {
301 if let Some(pci_node) = fdt.find_node(node_name) {
302 early_println!("[PCI Test] Found PCI node: {}", node_name);
303
304 if let Some(reg) = pci_node.reg() {
306 for region in reg {
307 ecam_base = region.starting_address as usize;
308 if let Some(size) = region.size {
309 ecam_size = size;
310 pci_found = true;
311 early_println!(
312 "[PCI Test] ECAM base: {:#x}, size: {:#x}",
313 ecam_base,
314 ecam_size
315 );
316 break;
317 }
318 }
319 }
320 if pci_found {
321 break;
322 }
323 }
324 }
325
326 if !pci_found {
327 early_println!("[PCI Test] No PCI host bridge found in device tree");
328 early_println!("[PCI Test] This is expected if not running with PCI support");
329 return;
330 }
331
332 early_println!("[PCI Test] ✓ PCI host bridge detected in device tree");
340 early_println!(
341 "[PCI Test] ✓ ECAM configuration: base={:#x}, size={:#x}",
342 ecam_base,
343 ecam_size
344 );
345 early_println!(
346 "[PCI Test] Note: Actual device scanning requires ECAM virtual memory mapping"
347 );
348 early_println!("[PCI Test] Test passed: PCI infrastructure initialized successfully");
349
350 assert!(pci_found, "PCI host bridge should be detected");
353 }
354}