Linux设备驱动之pci设备的枚举(2)

2019-04-14 11:59

}

先在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

/* Configuration request Retry Status */ while (l == 0xffff0001) { msleep(delay); delay *= 2;

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

/* Card hasn't responded in 60 seconds? Must be stuck. */ if (delay > 60 * 1000) {

printk(KERN_WARNING \ \ bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn)); return NULL; } }

从配置空间中读取该设备对应的vendor id和device id.如果读出来的值,有一个是空的,则说明该功能号对应的设备不存在,或者是配置非法.

如果读出来的是0xffff0001.则需要重新读一次,如果重读次数过多,也会退出

if (pci_bus_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type)) return NULL;

dev = alloc_pci_dev(); if (!dev)

return NULL;

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->error_state = pci_channel_io_normal; set_pcie_port_type(dev);

/* Assume 32-bit PCI; let 64-bit PCI cards (which are far rarer) set this higher, assuming the system even supports it. */ dev->dma_mask = 0xffffffff;

接着,将不同类型设备的共同头部配置读出来,然后赋值给pci_dev的相应成员.这里有个特别要值得注意的地方:

9

dev->dev.bus = &pci_bus_type.即将pci_dev里面封装的device结构的bus设置为了pci_bus_type.这个是很核心的一个步骤.我们先将它放到这里,之后的再来详细分析

特别的, HEADER_TYPE的最高位为0,表示该设备是一个单功能设备

if (pci_setup_device(dev) < 0) { kfree(dev); return NULL; }

return dev; }

最后,流程就会转入到pci_setup_deivce()对特定类型的设备配置都行读取操作了.代码如下: static int pci_setup_device(struct pci_dev * dev) {

u32 class;

sprintf(pci_name(dev), \ dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));

pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); dev->revision = class & 0xff;

class >>= 8; /* upper 3 bytes */ dev->class = class; class >>= 8;

pr_debug(\ dev->vendor, dev->device, class, dev->hdr_type);

/* \

dev->current_state = PCI_UNKNOWN;

/* Early fixups, before probing the BARs */ pci_fixup_device(pci_fixup_early, dev); class = dev->class >> 8;

switch (dev->hdr_type) { /* header type */

case PCI_HEADER_TYPE_NORMAL: /* standard header */ if (class == PCI_CLASS_BRIDGE_PCI) goto bad; pci_read_irq(dev);

pci_read_bases(dev, 6, PCI_ROM_ADDRESS);

pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor); pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device);

10


Linux设备驱动之pci设备的枚举(2).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:完整的工程结算单书(市政)

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: