在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, ®ion, 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;
}
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, ®ion, 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;
}