Linux设备驱动之pci设备的枚举 下载本文

if (pcibios_assign_all_busses())

/* Temporarily disable forwarding of the configuration cycles on all bridges in this bus segment to avoid possible conflicts in the second pass between two bridges programmed with overlapping bus ranges. */

pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses & ~0xffffff); goto out; }

/* Clear errors */ //往状态寄存器全写1

pci_write_config_word(dev, PCI_STATUS, 0xffff);

/* Prevent assigning a bus number that already exists. * This can happen when a bridge is hot-plugged */ //要处理的总线编号是否已经存在了 if (pci_find_bus(pci_domain_nr(bus), max+1)) goto out;

child = pci_add_new_bus(bus, dev, ++max); buses = (buses & 0xff000000)

| ((unsigned int)(child->primary) << 0) | ((unsigned int)(child->secondary) << 8) | ((unsigned int)(child->subordinate) << 16);

/*

* yenta.c forces a secondary latency timer of 176. * Copy that behaviour here. */

if (is_cardbus) {

buses &= ~0xff000000;

buses |= CARDBUS_LATENCY_TIMER << 24; }

pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);

if (!is_cardbus) {

child->bridge_ctl = bctl;

pci_fixup_parent_subordinate_busnr(child, max); /* Now we can scan all subordinate buses... */

17

max = pci_scan_child_bus(child);

pci_fixup_parent_subordinate_busnr(child, max); } else {

for (i=0; i

while (parent->parent) {

if ((!pcibios_assign_all_busses()) && (parent->subordinate > max) && (parent->subordinate <= max+i)) { j = 1; }

parent = parent->parent; } if (j) { i /= 2; break; } } max += i;

pci_fixup_parent_subordinate_busnr(child, max); }

child->subordinate = max;

pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); }

sprintf(child->name, (is_cardbus ? \

/* Has only triggered on CardBus, fixup is in yenta_socket */ while (bus->parent) {

if ((child->subordinate > bus->subordinate) || (child->number > bus->subordinate) || (child->number < bus->number) || (child->subordinate < bus->number)) { pr_debug(\ \ child->number, child->subordinate, (bus->number > child->subordinate && bus->subordinate < child->number) ? \

bus->self->transparent ? \ bus->number, bus->subordinate);

18

}

bus = bus->parent; } out:

pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bctl);

return max; }

忽略有关cardbus总线相关的部份。PCI_PRIMARY_BUS寄存器中的值的含义为:从低位到高位分别为:主总线号,次总线号,子层最大线号。各占两位。如果从PCI_PRIMARY_BUS取出来的值,次总线号和子层最大线号有意义。说明该pci-bridge是被bios处理过的。 这里为什么没有判断主总线号的值呢?这是因为总主线号可能为零,如根总线。而次总线是每枚举到一个pci-bridge就会增1。

如果该pci bridge是被bios处理过的,那直接构造一个pci_bus(pci_add_new_bus()).再递归枚举这个pci_bus下的设备就可以了。

相反,如果该pci-bridge没有被bios处理过,那就需要我们手动去处理了。这时,为它分配一个可能的总线号。然后将总线号写入PCI_PRIMARY_BUS寄存器。再构造一个pci_bus.递归枚举其下的设备。 最后的一个while()循环是打印出一些DEBUG信息。不需要理会

TODO:在上述代码红色标识部份。如果是没有由bios处理过的pci-bridge.有效设置只是次总线号和下层最大总线号。难道不需要设置主总线号么?

特别注意.在遍其次级总线下层pci时.此时还不知道下层最大总号是多少.所以将pci_bus->subordinate赋值为了0xFF.即其下的所有设备都可以透过这个pci-bridge(参考pci_alloc_child_bus()中的处理).然后等下层的子总线遍历完了之后,再来确定子总线的最大总线号,将其更新至pci_bus->subordinate.

对于上次主总线号的疑问:其实这个总主线号在访问下层总线是不会被使用到的,因为在配置的时候,只会比较pci_bridge的次总线号和下层最大总线号.如果总线号落在这个区间中间.将其透传到下一层总线.否则,忽略这个请求.

递归完成之后,pci总线上的所有信息都被找到了。所有pci_bus被存放在pci_root_buses为根的倒立树中.而总线上对应的pci 设备存放在pci_bus->device链表中.

返回到我们开篇时的初始化函数pci_legacy_init().这个函数还剩下一部份.代码如下: static int __init pci_legacy_init(void) {

if (!raw_pci_ops) {

printk(\ return 0; }

if (pcibios_scanned++) return 0;

printk(\ pci_root_bus = pcibios_scan_root(0); if (pci_root_bus)

pci_bus_add_devices(pci_root_bus);

19

pcibios_fixup_peer_bridges();

return 0; }

如果总线遍历成功,就会转入pci_bus_add_devices().代码片段如下: void pci_bus_add_devices(struct pci_bus *bus) {

…… ……

pci_bus_add_device(dev) …… …… }

该函数会遍历总线上的device链表.然后对每个设备调用pci_bus_add_device().代码如下: int pci_bus_add_device(struct pci_dev *dev) {

int retval;

retval = device_add(&dev->dev); if (retval)

return retval;

down_write(&pci_bus_sem);

list_add_tail(&dev->global_list, &pci_devices); up_write(&pci_bus_sem);

pci_proc_attach_device(dev); pci_create_sysfs_dev_files(dev); return 0; }

从上面的代码可以看出.它先将设备添加,然后再将设备挂载到全局的pci_devices链表上. 这样顺着pci_devices就可以找到所有的设备信息了.

另外,对于pci_dev的初始化,我们之前曾强调过.初始化代码片段如下: static struct pci_dev * __devinit

pci_scan_device(struct pci_bus *bus, int devfn) {

…… ……

dev->dev.parent = bus->bridge; dev->dev.bus = &pci_bus_type; …… …… }

即该设备是属于pci_bus_type总线的.事实上,我们编写的pci驱动程序,也是基于pci_bus_type.这样,就可以在添加

20

驱动的时候,就可以匹配想要的设备了.

在这里,特别注意一下,经过这里的枚举,只是枚举完了第一条根总线.那其它的根总线是在什么地方被枚举的呢? 在下一节里,再来分析这个问题. 六:小结

在linux的pci架构中,大量运用了深度优先遍历算法.这是由pci总线结构所决定的.经过这一章的分析过后,我们应该对pci架构有了一定的了解.同时在这一章还留下了一个问题.到下一节再来进行分析.

21