4. Linux内核PCIe子系统:pci_bus、pci_dev数据结构,内核PCIe驱动框架概述

PCIe子系统是Linux内核中负责管理PCI/PCIe设备的核心基础设施。理解其核心数据结构pci_bus与pci_dev,以及驱动框架的层次关系,是进行PCIe驱动开发和调试的前提。本章将深入剖析这两个关键结构体,并概述内核PCIe驱动的整体架构。

4.1 核心数据结构:pci_bus 与 pci_dev

内核使用两个核心结构体来抽象PCIe总线与设备:struct pci_bus 表示一条PCIe总线,struct pci_dev 表示一个PCIe设备(包括桥设备)。两者通过链表和指针形成树状拓扑。

4.1.1 struct pci_bus — 总线节点

pci_bus 结构体描述了一条PCIe总线的属性,包括其父总线、子总线、挂接的设备链表、总线号范围、桥设备指针等。其关键字段如下:

字段 类型 说明
parent struct pci_bus * 指向父总线(上游总线)
children struct list_head 子总线链表头
devices struct list_head 挂接在此总线上的设备链表头
number unsigned char 本总线号(主号)
primary unsigned char 桥设备上游总线号
secondary unsigned char 桥设备下游总线号(本总线号)
subordinate unsigned char 桥设备可达的最大子总线号
self struct pci_dev * 指向代表此总线桥设备的pci_dev(根总线为NULL)
resource struct resource * 总线上的资源(桥窗口)数组
ops struct pci_ops * 总线访问操作函数集(配置空间读写)
调试要点: 在调试PCIe枚举问题时,常通过 /sys/devices/pci0000:00/ 下的目录结构观察总线拓扑,或使用 lspci -t 查看树形关系。内核中可通过 list_for_each_entry 遍历 pci_bus->devices 链表来枚举设备。

4.1.2 struct pci_dev — 设备节点

pci_dev 是内核中代表一个PCIe设备的核心结构体,包含了设备的配置空间信息、资源、驱动绑定状态、电源管理等全部属性。其关键字段分组如下:

分类 字段 说明
标识 vendor / device 厂商ID / 设备ID
subsystem_vendor / subsystem_device 子系统厂商ID / 子系统设备ID
class 类别寄存器(基类+子类+编程接口)
devfn 设备号(总线上的设备+功能号)
拓扑 bus 指向所在 pci_bus
slot 指向 pci_slot(物理槽位)
dev 内嵌的通用设备结构体(用于驱动模型)
资源 resource[DEVICE_COUNT_RESOURCE] 设备资源数组(BAR、ROM等)
irq 中断号(INTx或MSI/MSI-X)
msi_enabled / msix_enabled MSI/MSI-X使能标志
驱动 driver 指向绑定的 pci_driver
pci_driver 驱动自定义数据指针
电源 current_state 当前电源状态(D0-D3hot)
关键关系: 每个 pci_dev 通过 dev->kobj 暴露在 /sys/bus/pci/devices/ 下。调试时可通过 cat /sys/bus/pci/devices/0000:01:00.0/config 读取原始配置空间。

4.1.3 总线-设备拓扑关系SVG

下图展示了 pci_bus 与 pci_dev 之间的树状拓扑关系,以及桥设备如何连接上下游总线:

PCIe总线-设备拓扑关系图 pci_bus 0 (Root) pci_dev (Host Bridge) pci_dev (Endpoint) pci_dev (PCI Bridge) pci_bus 1 (Secondary) pci_dev (Function 0) pci_dev (Function 1) pci_dev (Endpoint) 拓扑关系说明 每个 pci_bus 通过 self 指针指向其桥设备 pci_dev;每个 pci_dev 通过 bus 指针指向所在 pci_bus。

4.2 内核PCIe驱动框架概述

Linux内核的PCIe驱动框架采用分层设计,从上到下依次为:设备驱动层、PCI核心层、总线访问层。这种分层结构使得驱动开发者只需关注设备特定的逻辑,而无需关心底层配置空间访问的细节。

4.2.1 框架层次结构

  1. 设备驱动层 (pci_driver):由开发者实现,通过 pci_register_driver() 注册。核心回调包括 probe()、remove()、suspend()、resume()。
  2. PCI核心层 (PCI Core):管理设备枚举、资源分配、电源管理、sysfs 接口、pci_dev 生命周期。提供 pci_read_config_*() / pci_write_config_*() 等API。
  3. 总线访问层 (pci_ops):与硬件平台相关的配置空间访问方法,通过 pci_bus->ops 调用。x86使用 pci_conf1 / pci_conf2 或 MMCONFIG;ARM64使用 ECAM。

4.2.2 驱动注册与匹配流程

PCIe驱动的标准注册流程如下:

// 定义 pci_device_id 表(用于匹配设备)
static const struct pci_device_id my_pci_ids[] = {
    { PCI_DEVICE(0x10EE, 0x1234) },  // 厂商ID 0x10EE, 设备ID 0x1234
    { 0, }
};
MODULE_DEVICE_TABLE(pci, my_pci_ids);

// probe 回调:设备匹配成功时调用
static int my_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
    int ret;
    // 1. 使能设备
    ret = pci_enable_device(pdev);
    if (ret)
        return ret;
    // 2. 请求MMIO/I/O资源
    ret = pci_request_regions(pdev, "my_driver");
    if (ret)
        goto err_disable;
    // 3. 设置DMA掩码
    ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
    if (ret)
        goto err_release;
    // 4. 读取BAR0地址并ioremap
    resource_size_t bar0_start = pci_resource_start(pdev, 0);
    resource_size_t bar0_len   = pci_resource_len(pdev, 0);
    void __iomem *regs = ioremap(bar0_start, bar0_len);
    // ... 初始化硬件 ...
    return 0;

err_release:
    pci_release_regions(pdev);
err_disable:
    pci_disable_device(pdev);
    return ret;
}

// remove 回调:设备移除或驱动卸载时调用
static void my_pci_remove(struct pci_dev *pdev)
{
    // ... 清理资源 ...
    pci_release_regions(pdev);
    pci_disable_device(pdev);
}

// 定义 pci_driver 结构体
static struct pci_driver my_pci_driver = {
    .name     = "my_pcie_driver",
    .id_table = my_pci_ids,
    .probe    = my_pci_probe,
    .remove   = my_pci_remove,
};

// 模块入口/出口
static int __init my_init(void)
{
    return pci_register_driver(&my_pci_driver);
}
static void __exit my_exit(void)
{
    pci_unregister_driver(&my_pci_driver);
}
module_init(my_init);
module_exit(my_exit);
常见错误: 在 probe() 中忘记调用 pci_enable_device() 会导致后续配置空间读写失败;忘记调用 pci_request_regions() 会导致资源冲突。调试时可通过 /proc/iomem 检查BAR地址是否已被正确分配。

4.2.3 PCIe驱动框架架构SVG

下图展示了PCIe驱动框架的三层架构及数据流:

Linux内核PCIe驱动框架架构图 设备驱动层 (pci_driver) probe() / remove() / suspend() / resume() / pci_device_id 匹配 PCI核心层 (PCI Core) 设备枚举 / 资源分配 / 电源管理 / sysfs接口 pci_read_config_* / pci_write_config_* / pci_iomap 总线访问层 (pci_ops) x86: MMCONFIG / pci_conf1 | ARM64: ECAM | 其他: 自定义 PCIe硬件 (配置空间 / MMIO / DMA) 数据流方向

4.3 关键API与调试接口

下表总结了PCIe驱动开发中最常用的内核API及其用途:

API 所属层次 用途
pci_register_driver() PCI Core 注册pci_driver到内核
pci_enable_device() PCI Core 使能设备(设置Command寄存器)
pci_request_regions() PCI Core 请求设备的I/O和MMIO资源
pci_iomap() PCI Core 将BAR映射到CPU虚拟地址空间
pci_read_config_dword() PCI Core 读取32位配置空间寄存器
pci_write_config_dword() PCI Core 写入32位配置空间寄存器
pci_set_master() PCI Core 设置设备为总线主控(Bus Master)
dma_set_mask() DMA API 设置DMA地址掩码
调试技巧: 使用 lspci -vvv -s 01:00.0 查看设备完整配置空间;使用 setpci 工具直接读写配置空间寄存器进行验证。内核中可通过 pci_find_device() 或 pci_get_device() 按VID/DID查找设备。

4.4 本章小结

  • pci_bus 是总线节点,通过 parent/children/devices 链表形成树状拓扑,self 指向桥设备。
  • pci_dev 是设备节点,包含完整的配置空间信息、资源数组、中断和电源状态。
  • PCIe驱动框架分为三层:设备驱动层(pci_driver)、PCI核心层(枚举/资源/API)、总线访问层(pci_ops 硬件相关)。
  • 标准驱动流程:注册驱动 → 匹配设备 → probe()中使能设备、请求资源、ioremap → remove()中释放资源。
  • 调试时善用 lspci、setpci、/proc/iomem、/sys/bus/pci/devices/ 等接口。