通过sysfs 读写pci的配置空间和rom空间

来源:互联网 发布:led电子屏编辑软件 编辑:程序博客网 时间:2024/06/06 05:48
在pci_bus_add_device中会调用pci_create_sysfs_dev_files 来在sys下面为这个dev的配置空间和rom 空间提供访问的接口
int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev)
{
    int retval;
    int rom_size;
    struct bin_attribute *attr;

    if (!sysfs_initialized)
        return -EACCES;
// 判断配置空间的size是否大于256,如果是256的话就认为是pcie设备,否则就是pci设备,为啥不用pci_is_pcie 这个函数判断呢?然后会创建config ,通过对这个config的read和write就可以对配置空间读和写
    if (pdev->cfg_size > PCI_CFG_SPACE_SIZE)
        retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
    else
        retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
    if (retval)
        goto err;

    retval = pci_create_resource_files(pdev);
    if (retval)
        goto err_config_file;

    /* If the device has a ROM, try to expose it in sysfs. */
//得到rom的size
    rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
//如果rom size不为0
    if (rom_size) {
        attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
        if (!attr) {
            retval = -ENOMEM;
            goto err_resource_files;
        }
        sysfs_bin_attr_init(attr);
        attr->size = rom_size;
//名称为rom,配置空间对应的name为config
        attr->attr.name = "rom";
        attr->attr.mode = S_IRUSR | S_IWUSR;
        attr->read = pci_read_rom;
        attr->write = pci_write_rom;
//通过sysfs_create_bin_file 生成入口,pdev->dev.kobj 表示一个目录
        retval = sysfs_create_bin_file(&pdev->dev.kobj, attr);
        if (retval) {
            kfree(attr);
            goto err_resource_files;
        }
        pdev->rom_attr = attr;
    }


err_config_file:
//如果失败的话,就调用sysfs_remove_bin_file 去掉接口
    if (pdev->cfg_size > PCI_CFG_SPACE_SIZE)
        sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
    else
        sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
err:
    return retval;
}
我们以pci_read_rom为例看看如果对rom来进行读操作
static ssize_t pci_read_rom(struct file *filp, struct kobject *kobj,
                struct bin_attribute *bin_attr, char *buf,
                loff_t off, size_t count)
{
    struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
    void __iomem *rom;
    size_t size;

    if (!pdev->rom_attr_enabled)
        return -EINVAL;
//通过pci_map_rom 将rom对应的地址映射为虚拟地址,这样就可以通过memcpy_fromio 直接操作
    rom = pci_map_rom(pdev, &size);    /* size starts out as PCI window size */
    if (!rom || !size)
        return -EIO;

    if (off >= size)
        count = 0;
    else {
        if (off + count > size)
            count = size - off;

        memcpy_fromio(buf, rom + off, count);
    }
//完成只有,unmap掉虚拟地址,防止别人误操作。
    pci_unmap_rom(pdev, rom);

    return count;
}

void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
{
    struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
    loff_t start;
    void __iomem *rom;

    /* assign the ROM an address if it doesn't have one */
    if (res->parent == NULL && pci_assign_resource(pdev, PCI_ROM_RESOURCE))
        return NULL;
//得到rom的起始地址和size
    start = pci_resource_start(pdev, PCI_ROM_RESOURCE);
    *size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
    if (*size == 0)
        return NULL;

    /* Enable ROM space decodes */
判断是否是能rom
int pci_enable_rom(struct pci_dev *pdev)
{
    struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
    struct pci_bus_region region;
    u32 rom_addr;

    if (!res->flags)
        return -1;

    /* Nothing to enable if we're using a shadow copy in RAM */
    if (res->flags & IORESOURCE_ROM_SHADOW)
        return 0;

    pcibios_resource_to_bus(pdev->bus, &region, res);
    pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
    rom_addr &= ~PCI_ROM_ADDRESS_MASK;
    rom_addr |= region.start | PCI_ROM_ADDRESS_ENABLE;
    pci_write_config_dword(pdev, pdev->rom_base_reg, rom_addr);
    return 0;
}
直接通过写寄存器的方式是能rom 操作.
    if (pci_enable_rom(pdev))
        return NULL;
//调用ioremap映射为虚拟地址.
    rom = ioremap(start, *size);
    if (!rom) {
        /* restore enable if ioremap fails */
        if (!(res->flags & IORESOURCE_ROM_ENABLE))
            pci_disable_rom(pdev);
        return NULL;
    }

//计算实际的rom size,也就是前面通过    *size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
得到的size不一定准确,以实际通过寄存器中的到的值为准.实际情况是这两个size 大部分情况下相等,因为最初PCI_ROM_RESOURCE中的size也是通过寄存器读到的。
    *size = pci_get_rom_size(pdev, rom, *size);
    return rom;
}

0 0
原创粉丝点击