您的位置:首页 > 其它

pcie对设备的枚举

2017-03-25 09:34 417 查看
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的配置空间 已经讲过了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: