Linux设备驱动之pci设备的枚举

.write = pci_conf1_write, };

这个结构其实就是pci设备配置空间操作的接口.

五:pci设备的枚举过程 返回到pci_legacy_init()中 static int __init pci_legacy_init(void) {

……

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

pci_bus_add_devices(pci_root_bus); …… }

Pci设备的枚举过程是由pcibios_scan_root()完成的.在这里调用是以0为参数.说明是从根总线起开始枚举.

pcibios_scan_root()代码如下:

struct pci_bus * __devinit pcibios_scan_root(int busnum) {

struct pci_bus *bus = NULL; struct pci_sysdata *sd;

dmi_check_system(pciprobe_dmi_table);

while ((bus = pci_find_next_bus(bus)) != NULL) { if (bus->number == busnum) { /* Already scanned */ return bus; } }

/* Allocate per-root-bus (not per bus) arch-specific data. * TODO: leak; this memory is never freed. * It's arguable whether it's worth the trouble to care. */

sd = kzalloc(sizeof(*sd), GFP_KERNEL); if (!sd) {

printk(KERN_ERR %um); return NULL; }

printk(KERN_DEBUG \

return pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd);

5

}

先在pci_root_buses中判断是否存在这个根总线对应的总线号.如果存在,说明这条总线已经遍历过了,直接退出. Pci_root_ops这是定义的pci设备配置空间的操作.在没有选择CONFIG_PCI_MMCONFIG的情况下,它的操作都会转入我们在上面的分析的,ram_pci_ops中.这个过程非常简单,可以自行分析. 然后,流程转入pci_scan_bus_parented().代码如下:

struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent, int bus, struct pci_ops *ops, void *sysdata) {

struct pci_bus *b;

b = pci_create_bus(parent, bus, ops, sysdata); if (b)

b->subordinate = pci_scan_child_bus(b); return b; }

在pci_create_bus()中,为对应总线号构建pci_bus,然后将其挂入到pci_root_buses链表.该函数代码比较简单,请自行分析.然后,调用然后pci_scan_child_bus枚举该总线下的所有设备.pci_bus->subordinate表示下流总线的最大总线号.pci_sacn_child_bus()代码如下:

unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) {

unsigned int devfn, pass, max = bus->secondary; struct pci_dev *dev;

pr_debug(\>number);

/* Go find them, Rover! */

//按功能号扫描设备号对应的pci 设备 for (devfn = 0; devfn < 0x100; devfn += 8) pci_scan_slot(bus, devfn); /*

* After performing arch-dependent fixup of the bus, look behind * all PCI-to-PCI bridges on this bus. */

pr_debug(%umber); pcibios_fixup_bus(bus); for (pass=0; pass < 2; pass++)

list_for_each_entry(dev, &bus->devices, bus_list) {

if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||

6

dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) max = pci_scan_bridge(bus, dev, max, pass); } /*

* We've scanned the bus and so we know all about what's on * the other side of any bridges that may be on this bus plus * any devices. *

* Return how far we've got finding sub-buses. */

pr_debug(\ pci_domain_nr(bus), bus->number, max); return max; }

这节的难点就是在这个地方了,从我们之前分析的pci设备配置空间的读写方式可得知.对特定总线.下面最多个32个设备号.每个设备号又对应8 个功能号.我们可以将设备号和功能号放到一起,即占8~15位.在这面的代码中.对每个设备号调用pci_scan_slot()去扫描它下面的8个功能号对应的设备.总而言之,把该总线下面的所有设备都要枚举完.

pci_scan_slot()代码如下:

nt pci_scan_slot(struct pci_bus *bus, int devfn) {

int func, nr = 0; int scan_all_fns;

scan_all_fns = pcibios_scan_all_fns(bus, devfn);

for (func = 0; func < 8; func++, devfn++) { struct pci_dev *dev;

dev = pci_scan_single_device(bus, devfn); if (dev) { nr++;

/*

* If this is a single function device, * don't scan past the first function. */

if (!dev->multifunction) { if (func > 0) {

dev->multifunction = 1; } else { break;

7

} } } else {

if (func == 0 && !scan_all_fns) break; } } return nr; }

对其它的每个设备都会调用pci_scan_single_device().如果是单功能设备(dev->multifunction == 0).则只要判断它的第一个功能号可以了,不需要判断之后功能号对应的设备. Pci_scan_single_device()代码如下:

struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn) {

struct pci_dev *dev;

dev = pci_scan_device(bus, devfn); if (!dev)

return NULL;

//将pci_dev加至pci_bus->devices pci_device_add(dev, bus);

return dev; }

对每个设备,都会调用pci_scan_device()执行扫描的过程,如果该设备存在,就会将该设备加入到所属总线的devices链表上.这是在pci_device_add()函数中完成的,这个函数比较简单.这里不做详细分析.我们把注意力集中到pci_scan_device(),这函数有点长,分段分析如下:

static struct pci_dev * __devinit

pci_scan_device(struct pci_bus *bus, int devfn) {

struct pci_dev *dev; u32 l; u8 hdr_type; int delay = 1;

if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l)) return NULL;

/* some broken boards return 0 or ~0 if a slot is empty: */ if (l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) return NULL;

8

联系客服:779662525#qq.com(#替换为@)