在《Linux设备模型初始化》一节介绍了由__define_initcall引入的几个初始化宏,PCI初始化过程就是由这些宏控制的,下面我们介绍PCI的初始化。
首先就是优先级最高的postcore_initcall(pcibus_class_init),pcibus_class_init函数是PCI子系统第一个执行的初始化函数。在sys/class中注册pci_bus目录。
static int __init pcibus_class_init(void)
{
return class_register(&pcibus_class);
}
pcibus_class定义如下
static struct class pcibus_class = {
.name = "pci_bus",
.release = &release_pcibus_dev,
};
pcibus_class_init就是将pcibus_class注册到/sys/class目录下,创建/sys/class/pci_bus目录
然后subsys_initcall(pci_legacy_init)决定接下来会调用pci_legacy_init
static int __init pci_legacy_init(void)
if (pcibios_scanned++)
return 0;
pci_root_bus = pcibios_scan_root(0);
//最开始的时候,pci_root_buses为空,所以pci_find_next_bus返回空
while ((bus = pci_find_next_bus(bus)) != NULL)
///* 如果现有的根总线编号与传参数相等,说明已经扫描过该根总线,退出 */
if (bus->number == busnum) {
/* Already scanned */
return bus;
}
return pci_scan_bus(busnum, &pci_root_ops, NULL); //busnum=0
return pci_scan_bus_parented(NULL, bus, ops, sysdata);
首先增加pcibios_scanned的值,如果增加前的值不为0,就说明已经初始胡过一次了,就直接退出,否则调用pcibios_scan_root从0号pci线去扫描总线,具体的扫描操作是由pci_scan_bus_parented执行的。
struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent, int bus, struct pci_ops *ops, void *sysdata)
{
/* 为根总线分配一个pci_bus描述符 */
b = pci_alloc_bus();
dev = kmalloc(sizeof(*dev), GFP_KERNEL);
b->sysdata = sysdata;
b->ops = ops;
/* pci_find_bus查找成功,说明通过另外一个桥设备可以到达总线,这样放弃扫描 */
if (pci_find_bus(pci_domain_nr(b), bus))
goto err_out;
/* 将根总线链接入pci_root_buses链表 */
list_add_tail(&b->node, &pci_root_buses);
/* 初始化设备,并注册到系统。 */
memset(dev, 0, sizeof(*dev));
dev->parent = parent;
dev->release = pci_release_bus_bridge_dev;
sprintf(dev->bus_id, "pci%04x:%02x", pci_domain_nr(b), bus);
// 如果dev的父设备为空,所以会把dev注册到/dev/devices目录下,否则就会注册到父设备目录下,对于根总线,就是创建/sys/devices/pci0000:00目录
device_register(dev);
b->bridge = get_device(dev);
/* 初始化类设备描述符,并添加到系统 */
b->class_dev.class = &pcibus_class;
sprintf(b->class_dev.class_id, "%04x:%02x", pci_domain_nr(b), bus);
// 将类设备注册到/sys/class/pci_bus/目录下,也就是创建/sys/class/pci_bus/0000:00目录
class_device_register(&b->class_dev);
/*
在PCI总线目录下创建属性文件,关于class_device_attr_cpuaffinity的定义
参考CLASS_DEVICE_ATTR(cpuaffinity, S_IRUGO, pci_bus_show_cpuaffinity, NULL);
*/
class_device_create_file(&b->class_dev, &class_device_attr_cpuaffinity);
/* Create legacy_io and legacy_mem files for this bus */
/* 在PCI总线目录下创建二进制属性文件 */
pci_create_legacy_files(b);
// 在类设备目录下创建名为“bridge”的链接文件,链接到对应的桥设备目录下
sysfs_create_link(&b->class_dev.kobj, &b->bridge->kobj, "bridge");
/* 初始化总线与次总线编号 */
b->number = b->secondary = bus;
/* 设置端口和内存资源,以后分配资源时使用 */
b->resource[0] = &ioport_resource;
b->resource[1] = &iomem_resource;
/* 扫描根总线 */
b->subordinate = pci_scan_child_bus(b);
pci_bus_add_devices(b);
return b;
pci_scan_bus_parented的主要内容如下:
(1)Linux把总线也看做设备,所以将代表根总线的设备注册到/dev/devices目录下,创建创建/sys/devices/pci0000:00目录。
(2)设置根总线的桥设备就是当前总线设备。
(3) 将代表根总线的类设备注册到/sys/class/pci_bus/目录下,也就是创建/sys/class/pci_bus/0000:00目录。
(4)在类设备目录下创建名为“bridge”的链接文件,链接到对应的桥设备目录下。
(5)调用pci_scan_child_bus去扫描根总线。
(6)调用pci_bus_add_devices将扫描到的设备插入到全局PCI设备列表中,并加入到sysfs和procfs中。
unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)
{
/**
* 扫描当前PCI总线的所有设备,如果设备是PCI总线,会递归调用本函数。
* 并将设备加入到对应总线的设备队列中。
* 每个PCI总线最多有32个设备,每个设备最多有8个function,因此循环0x100次。
*/
for (devfn = 0; devfn < 0x100; devfn += 8)
pci_scan_slot(bus, devfn);
/**
* pcibios_fixup_bus的主要目的是为一些PCI设备中的errata提供work-around.
* 同时还会调用一个重要的函数pci_read_bridge_base函数。
* pci_read_bridge_base函数会根据寄存存器的值初始化PCI所管理的地址空间。
*/
pcibios_fixup_bus(bus);
/**
* 调用pci_scan_bridge函数处理当前PCI总线上所挂接的PCI桥。
*/
for (pass=0; pass < 2; pass++)
list_for_each_entry(dev, &bus->devices, bus_list) {
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
/**
* pass为0处理"已经完成枚举"的PCI桥。
* pass为1处理"尚未完成枚举"的PCI桥。
* 对x86来说,BIOS已经预先对总线进行枚举。
*/
max = pci_scan_bridge(bus, dev, max, pass);
}
return max;
}
pci_scan_child_bus首先调用pci_scan_slot去扫描挂载到该当前总线的上的设备,然后调用pci_scan_bridge去递归的扫描桥接的其他PCI总线。
int __devinit pci_scan_slot(struct pci_bus *bus, int devfn)
/**
* 每个PCI设备最多可能有8个function。
*/
for (func = 0; func < 8; func++, devfn++) {
struct pci_dev *dev;
/**
* 配置设备。
* pci_scan_single_device调用pci_scan_device对PCI设备的配置寄存器进行读写操作,侧重于对PCI设备进行硬件层面的初始化操作。
* 调用pci_device_add进行软件方面的初始化。
*/
dev = pci_scan_single_device(bus, devfn);
dev = pci_scan_device(bus, devfn);
pci_fixup_device(pci_fixup_header, dev);
/* 将设备添加到全局链表,以及总线设备链表 */
INIT_LIST_HEAD(&dev->global_list);
list_add_tail(&dev->bus_list, &bus->devices);
nr++;
return nr;
pci_scan_slot会调用pci_scan_single_device去扫描该总线上功能号为devfn的设备,pci_scan_single_device会继续调用pci_scan_device去扫描设备,然后将扫描到的设备利用bus_list添加到bus->devices的链表上。
所以最终扫描指定功能号的设备是否存在就是由pci_scan_device函数去完成的,接下来我们看一下该函数
static struct pci_dev * __devinit
pci_scan_device(struct pci_bus *bus, int devfn)
{
/* 读取总线上给定设备/功能号的配置空间中厂商ID和设备ID */
if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))
return NULL;
/* 某些主板会返回全0或全1表示设备不存在 */
if (l == 0xffffffff || l == 0x00000000 ||
l == 0x0000ffff || l == 0xffff0000)
return NULL;
/**
* 读取VENDOR_ID和HEADER_TYPE寄存器,并对设备进行初始化。
*/
while (l == 0xffff0001) {/* 0xffff0001状态表示配置重试状态 */
/* 读取配置空间失败,退出 */
if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))
return NULL;
}
/* 厂商和设备ID读取成功,这里再读取配置头 */
if (pci_bus_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type))
return NULL;
/**
* 分配pci_dev并对其进行初始化。
*/
dev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL);
if (!dev)
return NULL;
memset(dev, 0, sizeof(struct pci_dev));
dev->bus = bus;
dev->sysdata = bus->sysdata;
dev->dev.parent = bus->bridge;
dev->dev.bus = &pci_bus_type;
dev->devfn = devfn;
dev->hdr_type = hdr_type & 0x7f;
dev->multifunction = !!(hdr_type & 0x80);
dev->vendor = l & 0xffff;
dev->device = (l >> 16) & 0xffff;
dev->cfg_size = pci_cfg_space_size(dev);
dev->dma_mask = 0xffffffff;
/**
* pci_setup_device对设备进行真正的初始化,在该函数里会设备pci设备的名字,例如0000:00:00:0
*/
if (pci_setup_device(dev) < 0) {
kfree(dev);
return NULL;
}
/* 初始化PCI设备的内嵌设备描述符 */
device_initialize(&dev->dev);
kobj_set_kset_s(dev, devices_subsys);
dev->dev.release = pci_release_dev;
pci_dev_get(dev);
pci_name_device(dev);
dev->dev.dma_mask = &dev->dma_mask;
dev->dev.coherent_dma_mask = 0xffffffffull;
return dev;
PCI总线扫描结束后,即pci_scan_child_bus调用结束后,会执行pci_bus_add_devices函数去注册扫描到的设备。
void __devinit pci_bus_add_devices(struct pci_bus *bus)
{
// 遍历该总线上的设备
list_for_each_entry(dev, &bus->devices, bus_list) {
if (!list_empty(&dev->global_list))
continue;
/**
* 将当前PCI总线上的所有PCI设备相关信息加入到proc和sysfs文件系统中。
*/
pci_bus_add_device(dev);
// PCI设备被注册到/sys/devices/pci0000:00目录下
device_add(&dev->dev);
list_add_tail(&dev->global_list, &pci_devices);
pci_proc_attach_device(dev);
pci_create_sysfs_dev_files(dev);
}
list_for_each_entry(dev, &bus->devices, bus_list) {
if (dev->subordinate && list_empty(&dev->subordinate->node)) {
spin_lock(&pci_bus_lock);
list_add_tail(&dev->subordinate->node, &dev->bus->children);
spin_unlock(&pci_bus_lock);
/**
* 递归调用,将PCI总线上的所有PCI子桥加入proc和sysfs文件系统。
*/
pci_bus_add_devices(dev->subordinate);
sysfs_create_link(&dev->subordinate->class_dev.kobj, &dev->dev.kobj, "bridge");
}
}