pcie对设备的枚举

kernel 中对pci设别的枚举flow如下:
acpi_pci_root_add->pci_acpi_scan_root->acpi_pci_root_create->pci_scan_child_bus->pci_scan_slot
这里的slot表示一个独立的PCI设备,PCI一般是由segment:bus:device:fn 这四部分组成
root@ubuntu:/sdf5# lspci
0002:80:00.0 PCI bridge: Device 19e5:1610 (rev 01)

例如这个里的segmemg是0002.bus是80,device 是00,fn是0

以一张PCI网卡为例上面有四个网卡,这四个网口只是fn不同,但是device是相等的,因此代表这张网卡代表一个slot.

    for (devfn = 0; devfn < 0x100; devfn += 8)
        pci_scan_slot(bus, devfn);

从这段code中也可以看出device是8 bit的,一个bus上最多只能有256 个device。
pci_scan_slot->pci_scan_single_device->pci_scan_device
static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
{
    struct pci_dev *dev;
    u32 l;

    if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000))
        return NULL;

    dev = pci_alloc_dev(bus);
    if (!dev)
        return NULL;

    dev->devfn = devfn;
    dev->vendor = l & 0xffff;
    dev->device = (l >> 16) & 0xffff;

    pci_set_of_node(dev);

    if (pci_setup_device(dev)) {
        pci_bus_put(dev->bus);
        kfree(dev);
        return NULL;
    }

    return dev;
}
在pci_scan_device 中是通过pci_bus_read_dev_vendor_id 来读取设备钱32 bit的数据也就是vendor_id 来判断设备是否存在,这个可以从pci endpoint的type 0 和type 1的header中看到。
bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
                int crs_timeout)
{
    int delay = 1;

    if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
        return false;

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

    /*
     * Configuration Request Retry Status.  Some root ports return the
     * actual device ID instead of the synthetic ID (0xFFFF) required
     * by the PCIe spec.  Ignore the device ID and only check for
     * (vendor id == 1).
     */
    while ((*l & 0xffff) == 0x0001) {
        if (!crs_timeout)
            return false;

        msleep(delay);
        delay *= 2;
        if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
            return false;
        /* Card hasn't responded in 60 seconds?  Must be stuck. */
        if (delay > crs_timeout) {
            printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not responding\n",
                   pci_domain_nr(bus), bus->number, PCI_SLOT(devfn),
                   PCI_FUNC(devfn));
            return false;
        }
    }

    return true;
}
pci_bus_read_dev_vendor_id 调用pci_bus_read_config_dword 来读32bit的数据,如果读到的数据不是下面这四种中的一种就算发现设备了
(*l == 0xffffffff || *l == 0x00000000 ||   *l == 0x0000ffff || *l == 0xffff0000)

while ((*l & 0xffff) == 0x0001) {
        if (!crs_timeout)
            return false;

        msleep(delay);
        delay *= 2;
        if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
            return false;
        /* Card hasn't responded in 60 seconds?  Must be stuck. */
        if (delay > crs_timeout) {
            printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not responding\n",
                   pci_domain_nr(bus), bus->number, PCI_SLOT(devfn),
                   PCI_FUNC(devfn));
            return false;
        }
    }
上面这段while循环是针对特殊的PCI设备,一般走不到,因此只要读的vendor id不是上面说到的四种,就算发现设备,就返回true了。

而pci_bus_read_config_dword 是一个宏,具体实现在pci/access.c 中
#define PCI_USER_READ_CONFIG(size, type)                    \
int pci_user_read_config_##size                        \
    (struct pci_dev *dev, int pos, type *val)            \
{                                    \
    int ret = PCIBIOS_SUCCESSFUL;                    \
    u32 data = -1;                            \
    if (PCI_##size##_BAD)                        \
        return -EINVAL;                        \
    raw_spin_lock_irq(&pci_lock);                \
    if (unlikely(dev->block_cfg_access))                \
        pci_wait_cfg(dev);                    \
    ret = dev->bus->ops->read(dev->bus, dev->devfn,            \
                    pos, sizeof(type), &data);    \
    raw_spin_unlock_irq(&pci_lock);                \
    *val = (type)data;                        \
    return pcibios_err_to_errno(ret);                \
}                                    \
EXPORT_SYMBOL_GPL(pci_user_read_config_##size);
可见最后还是调用bus->ops->read 来读取的。而这个ops的赋值在pcie的配置空间 已经讲过了

你可能感兴趣的:(Linux,源码分析)