先思考一个问题,系统配置PCI设备的时候,需要指明Bus Device Function Register构成的地址,但是系统如何知道主板上有多少总线,设备呢?整个系统的PCI拓扑需要系统去枚举遍历,使用的深度优先遍历算法,详见PCI的6.6.2章节。
下面以Linux内核代码分析来加深PCI配置的理解。按照参考3的描述,pci相关的初始化应该是如下顺序。
pcibus_class_init()
pci_driver_init()
pci_arch_init()
acpi_pci_init()
pci_acpi_init()
pci_legacy_init()
pcibios_assign_resources()
pci_init()
pci_proc_init()
pci_sysfs_init()
函数 | 作用 |
---|---|
pcibus_class_init | 就是向sys文件系统注册pci_bus class,完成后创建了/sys/class/pci_bus目录 |
pci_driver_init | pci_driver_init向sys文件系统注册pci bus,此后创建了/sys/bus/pci 目录及文件 |
pci_arch_init | 根据内核menuconfig配置,设置raw_pci_ops或者pci_bios_access,指定pci配置读写函数 |
acpi_pci_init | 向sys文件系统注册acpi_pci_bus bus,设置pci_platform_pm为acpi_pci_platform_pm 电源管理相关的设置 |
pci_acpi_init | pci acpi的一些中断函数指针设置,中断资源申请 |
pci_legacy_init | 完成总线的枚举和设备的发现,完成PCI桥的配置等 |
pcibios_assign_resources | |
pci_proc_init | proc目录下创建pci相关信息,呈现给用户态 |
pci_sysfs_init | sys文件系统pci部分创建 |
static struct class pcibus_class = {
.name = "pci_bus",
.dev_release = &release_pcibus_dev,
.dev_groups = pcibus_groups,
};
static int __init pcibus_class_init(void)
{
return class_register(&pcibus_class);
}
pcibus_class_init就是向sys文件系统注册pci_bus class,完成后创建了/sys/class/pci_bus目录。
struct bus_type pci_bus_type = {
.name = "pci",
.match = pci_bus_match,
.uevent = pci_uevent,
.probe = pci_device_probe,
.remove = pci_device_remove,
.shutdown = pci_device_shutdown,
.dev_groups = pci_dev_groups,
.bus_groups = pci_bus_groups,
.drv_groups = pci_drv_groups,
.pm = PCI_PM_OPS_PTR,
};
EXPORT_SYMBOL(pci_bus_type);
static int __init pci_driver_init(void)
{
return bus_register(&pci_bus_type);//注册pci bus,创建/sys/bus/pci 目录及文件
}
postcore_initcall(pci_driver_init);
/**
* bus_register - register a driver-core subsystem
* @bus: bus to register
*
* Once we have that, we register the bus with the kobject
* infrastructure, then register the children subsystems it has:
* the devices and drivers that belong to the subsystem.
*/
int bus_register(struct bus_type *bus)
{
int retval;
struct subsys_private *priv;
struct lock_class_key *key = &bus->lock_key;
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->bus = bus;
bus->p = priv;
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
priv->subsys.kobj.kset = bus_kset;
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys);
if (retval)
goto out;
retval = bus_create_file(bus, &bus_attr_uevent);
if (retval)
goto bus_uevent_fail;
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);//创建/sys/bus/pci/devices
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);//创建/sys/bus/pci/drivers
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
INIT_LIST_HEAD(&priv->interfaces);
__mutex_init(&priv->mutex, "subsys mutex", key);
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
retval = bus_add_groups(bus, bus->bus_groups);
if (retval)
goto bus_