Hi,
I was reading through the PCI.C (direct configuration mechanism 1), and was wondering if I missed something - it looks like "main()" only scans a single PCI bus. Often there are more devices behind a PCI->PCI bridge, and sometimes there are multiple PCI host controllers (typically found in servers - it's reported as a multi-function host controller at bus=0, dev=0).
For example:
Code: Select all
Host_bus
|
|__Bus 0: Host_controller #1
| |__Bus 0, Dev 0
| | |_Bus 0, Dev 0, Func 0: Host bridge controller #1
| | |_Bus 0, Dev 0, Func 0: Host bridge controller #2
| |__Bus 0, Dev 1, Func 0: Video card
| |__Bus 0, Dev 2, Func 0: PCI to LPC Bridge
|
|__Bus 1: Host controller #2
|__Bus 1, Dev 0: PCI to PCI bridge
| |__Bus 2, Dev 0: Ethernet adapter
| |__Bus 2, Dev 1, Func 0: Mixer
| |__Bus 2, Dev 1, Func 1: MIDI
| |__Bus 2, Dev 1, Func 2: Digitized sound
| |__Bus 2, Dev 1, Func 3: Joystick
|__Bus 1, Dev 0: SCSI adapter
|__Bus 1, Dev 2: Multifunction/sound
|__Bus 1, Dev 2, Func 0: Mixer
|__Bus 1, Dev 2, Func 1: MIDI
|__Bus 1, Dev 2, Func 2: Digitized sound
|__Bus 1, Dev 2, Func 3: Joystick
The algorithm I use goes something like:
Code: Select all
int highest_host_bus = 0;
main() {
scan_PCI_bus(0);
scan_ISA_bus();
find_PS2_devices();
find_IDE_devices();
find_FDC_devices();
}
scan_PCI_bus(bus) {
int device;
for(device = 0; device < MAXdevice; device++) {
scanDevice(bus, device);
}
}
scanDevice(bus, device) {
int function = 0;
if( vendorID_isn't_sane() ) return;
if( multi_function_device() ) {
for(;function < MAXfunction; function++) {
scanFunction(bus, device, function);
}
} else {
scanFunction(bus, device, function);
}
}
scanFunction(bus, device, function) {
get_all_PCI_config_space_data();
if(class_type == bus_device) {
if(sub_class_type == host_bridge) {
if(function != 0) {
highest_host_bus++;
scan_PCI_bus(highest_host_bus);
}
} else if(sub_class_type == PCItoPCI_bridge) {
scan_PCI_bus(secondary_bus);
} else if(sub_class_type == PCItoLPC_bridge) {
set_LPC_bridge(bus, device, function);
}
} else if(class_type == serial_controller) {
if(sub_class_type == USB_controller) {
if(interface_type != USB_device) {
scan_PCI_USB_bus(bus, device, function);
}
}
}
}
I actually build a "device tree" that will be used to keep track of what hardware is connected to what, mostly for power management (for e.g. so I don't send a PCI bridge to sleep and then try to use a device that's connected through it).
This device tree is used for all hardware (CPU's, memory, PCI, USB, PCMCIA, LPC, PS/2, FDC, IDE, etc). Each node in the tree contains details on what it's connected to, if it needs a device driver (and the message port ID if it already has a device driver), resources it uses, it's power mode (full, standby, sleeping, off, etc) and a few other things.
The OS builds this device tree during boot, but will need to update it from time to time (e.g. hot plug devices). It also displays this device tree after detection - see
http://bcos.hopto.org/screens.html (second picture down). This is from Bochs, which doesn't support much PCI (unfortunately I can't show screen shots direct from my servers, etc). I've only done the code to detect PCI and PS/2 devices so far, but you can imagine what it'll look like eventually...
I added the ISA/Plug & Play device scanning to the algorithm above for a reason - I do device detection in this order to prevent detecting the same device twice, and so I know where the device actually is connected for power management. For e.g. ATA/IDE controller detected as both PCI and legacy ISA, or USB keyboard/mouse detected as both USB and PS/2. Generally the PCI and/or USB drivers will disable legacy emulation before the ISA and/or PS2 devices are scanned.
Cheers,
Brendan