PCI SYSFS
我们在调试时最长在PCI SYSFS目录下进行调试,这个目录是怎么生成的?
/sys/bus/pci/devices/0000:26:00.0$ ls
aer_dev_correctable broken_parity_status current_link_speed dma_mask_bits iommu_group max_link_speed numa_node reset revision sriov_offset subsystem vendor
aer_dev_fatal class current_link_width driver_override irq max_link_width power resource rom sriov_stride subsystem_device
aer_dev_nonfatal config d3cold_allowed enable local_cpulist modalias remove resource0 sriov_drivers_autoprobe sriov_totalvfs subsystem_vendor
ari_enabled consistent_dma_mask_bits device iommu local_cpus msi_bus rescan resource2 sriov_numvfs sriov_vf_device uevent
/sys/bus/pci目录在 pci_driver_init 生成并注册
相关宏
// linux-5.7.14/include/linux/device/bus.h
#define BUS_ATTR_RW(_name) \
struct bus_attribute bus_attr_##_name = __ATTR_RW(_name)
#define BUS_ATTR_RO(_name) \
struct bus_attribute bus_attr_##_name = __ATTR_RO(_name)
#define BUS_ATTR_WO(_name) \
struct bus_attribute bus_attr_##_name = __ATTR_WO(_name)
// linux-5.7.14/include/linux/device.h
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
#define DEVICE_ATTR_PREALLOC(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = \
__ATTR_PREALLOC(_name, _mode, _show, _store)
#define DEVICE_ATTR_RW(_name) \
struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
#define DEVICE_ATTR_RO(_name) \
struct device_attribute dev_attr_##_name = __ATTR_RO(_name)
#define DEVICE_ATTR_WO(_name) \
struct device_attribute dev_attr_##_name = __ATTR_WO(_name)
#define DEVICE_ULONG_ATTR(_name, _mode, _var) \
struct dev_ext_attribute dev_attr_##_name = \
{ __ATTR(_name, _mode, device_show_ulong, device_store_ulong), &(_var) }
#define DEVICE_INT_ATTR(_name, _mode, _var) \
struct dev_ext_attribute dev_attr_##_name = \
{ __ATTR(_name, _mode, device_show_int, device_store_int), &(_var) }
#define DEVICE_BOOL_ATTR(_name, _mode, _var) \
struct dev_ext_attribute dev_attr_##_name = \
{ __ATTR(_name, _mode, device_show_bool, device_store_bool), &(_var) }
#define DEVICE_ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = \
__ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store)
// linux-5.7.14/include/linux/sysfs.h
#define __ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), \
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
.show = _show, \
.store = _store, \
}
#define __ATTR_PREALLOC(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), \
.mode = SYSFS_PREALLOC | VERIFY_OCTAL_PERMISSIONS(_mode) },\
.show = _show, \
.store = _store, \
}
#define __ATTR_RO(_name) { \
.attr = { .name = __stringify(_name), .mode = 0444 }, \
.show = _name##_show, \
}
#define __ATTR_RO_MODE(_name, _mode) { \
.attr = { .name = __stringify(_name), \
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
.show = _name##_show, \
}
#define __ATTR_WO(_name) { \
.attr = { .name = __stringify(_name), .mode = 0200 }, \
.store = _name##_store, \
}
#define __ATTR_RW(_name) __ATTR(_name, 0644, _name##_show, _name##_store)
#define __ATTR_NULL { .attr = { .name = NULL } }
// linux-5.7.14/drivers/pci/pci-driver.c
struct bus_type pci_bus_type = {
.name = "pci",
......
.dev_groups = pci_dev_groups,
.bus_groups = pci_bus_groups,
.drv_groups = pci_drv_groups,
.....
};
pci_driver_init
bus_register(&pcie_port_bus_type);
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
bus_add_groups(bus, bus->bus_groups);
sysfs_create_groups(&bus->p->subsys.kobj, groups); // 参考bus操作接口
// linux-5.7.14/drivers/pci/pci-sysfs.c
pci_bus_add_device
pci_create_sysfs_dev_files(dev);
sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr); // config接口
sysfs_create_bin_file(&pdev->dev.kobj, attr); // if (rom_size) ;option Rom接口
bus操作接口
rescan 重新扫描所有PCI设备
// linux-5.7.14/drivers/pci/pci-sysfs.c
recan属性:WO,
ssize_t rescan_store(struct bus_type *bus, const char *buf, size_t count);
static BUS_ATTR_WO(rescan); ===>
struct bus_attribute bus_attr_rescan = {
.attr = { .rescan = __stringify(rescan), .mode = 0200 }, \
.store = rescan_store,
};
static struct attribute *pci_bus_attrs[] = {
&bus_attr_rescan.attr,
NULL,
};
static const struct attribute_group pci_bus_group = {
.attrs = pci_bus_attrs,
};
const struct attribute_group *pci_bus_groups[] = {
&pci_bus_group,
NULL,
};
device接口
提供了一大波device的操作接口,太多,不一一列举。
/* show configuration fields */
#define pci_config_attr(field, format_string) \
static ssize_t \
field##_show(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct pci_dev *pdev; \
\
pdev = to_pci_dev(dev); \
return sprintf(buf, format_string, pdev->field); \
} \
static DEVICE_ATTR_RO(field)
pci_config_attr(vendor, "0x%04x\n"); // 一大波show接口
pci_config_attr(device, "0x%04x\n");
pci_config_attr(subsystem_vendor, "0x%04x\n");
pci_config_attr(subsystem_device, "0x%04x\n");
pci_config_attr(revision, "0x%02x\n");
pci_config_attr(class, "0x%06x\n");
pci_config_attr(irq, "%u\n");
static DEVICE_ATTR_RW(enable); // enable接口
static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
char *buf)
static ssize_t enable_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
static struct attribute *pci_dev_attrs[] = {
&dev_attr_resource.attr,
&dev_attr_vendor.attr,
&dev_attr_device.attr,
&dev_attr_subsystem_vendor.attr,
&dev_attr_subsystem_device.attr,
&dev_attr_revision.attr,
&dev_attr_class.attr,
&dev_attr_irq.attr,
&dev_attr_local_cpus.attr,
&dev_attr_local_cpulist.attr,
&dev_attr_modalias.attr,
#ifdef CONFIG_NUMA
&dev_attr_numa_node.attr,
#endif
&dev_attr_dma_mask_bits.attr,
&dev_attr_consistent_dma_mask_bits.attr,
&dev_attr_enable.attr,
&dev_attr_broken_parity_status.attr,
&dev_attr_msi_bus.attr,
#if defined(CONFIG_PM) && defined(CONFIG_ACPI)
&dev_attr_d3cold_allowed.attr,
#endif
#ifdef CONFIG_OF
&dev_attr_devspec.attr,
#endif
&dev_attr_driver_override.attr,
&dev_attr_ari_enabled.attr,
NULL,
};
static const struct attribute_group pci_dev_group = {
.attrs = pci_dev_attrs,
};
const struct attribute_group *pci_dev_groups[] = {
&pci_dev_group,
NULL,
};
pci_setup_device(pci设备初始化)
PCI是怎么获取配置空间大小的 ???pdev->cfg_size 在什么地方填充?
pci_scan_device
pci_setup_device
pci_setup_device
hdr_type = pci_hdr_type(dev); // 读取头部信息
set_pcie_port_type
pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); // PCIE一定有0x10的ID配置
__pci_bus_find_cap_start
// 读取pci配置空间的状态位,判断是否capabilities list位是否使能
pci_bus_read_config_word(bus, devfn, PCI_STATUS, &status);
// 如果没使能,那配置空间也就0x00-0x3f
// 如果使能,返回PCI_CAPABILITY_LIST 0x34,也就是capability指针位置
__pci_find_next_cap //
pci_bus_read_config_byte(bus, devfn, pos, &pos); // 读取指针,寻找next
....// 开始遍历capability,寻找ID符合的。也就是PCI_CAP_ID_EXP
pci_cfg_space_size // 获取配置空间大小
// 桥设备暂时不考虑
if (pci_is_pcie(dev)) // 根据前边获取到的信息,判断是否是pcie设备
return pci_cfg_space_size_ext(dev); // 这里边就是尝试读取下0x100以后的数据,