4. Linux PCIe子系统架构
Linux内核的PCIe子系统是一个高度层次化的软件栈,负责管理从设备枚举、资源分配到驱动绑定和数据传输的全生命周期。理解其层次结构和核心数据结构,是开发RC(Root Complex)和EP(Endpoint)驱动的基础。
4.1 PCIe子系统层次结构
PCIe子系统在Linux内核中分为三个主要层次:总线层、设备层和驱动层。每一层都通过特定的数据结构抽象,并通过内核提供的API进行交互。
层次概览: 总线层(pci_bus)管理拓扑和配置空间访问;设备层(pci_dev)代表一个具体的PCIe功能;驱动层(pci_driver)实现设备的具体操作逻辑。
4.2 核心数据结构详解
PCIe子系统的核心由三个数据结构支撑:pci_dev、pci_bus和pci_driver。它们分别对应设备、总线和驱动,构成了整个PCIe驱动框架的基石。
4.2.1 pci_dev:PCIe设备抽象
pci_dev是内核中代表一个PCIe功能(Function)的核心结构体。无论是RC端的桥设备,还是EP端的端点设备,最终都会被枚举为一个pci_dev实例。
| 字段 | 类型 | 说明 |
|---|---|---|
| vendor | unsigned int | 厂商ID(如0x8086为Intel) |
| device | unsigned int | 设备ID |
| class | unsigned int | 设备类别(如0x060400为PCI桥) |
| irq | unsigned int | 中断号(INTx或MSI/MSI-X) |
| resource[] | struct resource | 6个BAR空间及ROM资源描述 |
| bus | struct pci_bus * | 指向所属总线的指针 |
| dev | struct device | 内核通用设备,用于驱动模型 |
| cfg_size | int | 配置空间大小(256B或4KB) |
关键点: 在RC模式下,
pci_dev代表的是PCIe桥或下游端口;在EP模式下,pci_dev代表端点自身。驱动通过pci_dev访问BAR空间和配置寄存器。
4.2.2 pci_bus:PCIe总线拓扑
pci_bus结构体描述了一条PCIe总线,包括其父总线、子总线以及挂载在该总线上的所有设备。它构成了PCIe树形拓扑的节点。
| 字段 | 类型 | 说明 |
|---|---|---|
| parent | struct pci_bus * | 父总线(上游总线) |
| children | struct list_head | 子总线链表 |
| devices | struct list_head | 挂载在此总线上的设备链表 |
| self | struct pci_dev * | 指向此总线的桥设备(若为根总线则为NULL) |
| number | unsigned char | 总线编号(由BIOS或内核分配) |
| ops | struct pci_ops * | 配置空间访问操作函数集 |
| busn_res | struct resource | 此总线的子总线编号范围 |
4.2.3 pci_driver:PCIe驱动模型
pci_driver是驱动开发者最常接触的结构体。它通过内核的驱动模型与pci_dev进行匹配和绑定。
struct pci_driver {
const char *name; // 驱动名称
const struct pci_device_id *id_table; // 支持的设备ID表
int (*probe)(struct pci_dev *dev, const struct pci_device_id *id);
void (*remove)(struct pci_dev *dev);
int (*suspend)(struct pci_dev *dev, pm_message_t state);
int (*resume)(struct pci_dev *dev);
// ... 其他回调
};
| 回调函数 | 触发时机 | 典型操作 |
|---|---|---|
| probe() | 设备枚举完成,驱动匹配成功时 | 启用设备、分配资源、注册中断、初始化硬件 |
| remove() | 设备热拔出或驱动卸载时 | 释放资源、注销中断、关闭设备 |
| suspend() | 系统进入低功耗状态时 | 保存设备状态、停止DMA |
| resume() | 系统从低功耗状态恢复时 | 恢复设备状态、重新初始化 |
RC与EP驱动差异: 在RC模式下,
probe()通常用于初始化桥设备或下游端口,并可能触发子总线的扫描;在EP模式下,probe()专注于端点自身的功能初始化,如配置BAR空间和DMA引擎。
4.3 数据结构之间的协作关系
三个核心数据结构通过内核驱动模型紧密协作,形成完整的PCIe设备管理闭环:
- 枚举阶段: 内核通过
pci_bus的ops访问配置空间,发现设备后创建pci_dev实例,并将其挂载到对应总线的devices链表。 - 匹配阶段: 内核遍历已注册的
pci_driver,将id_table与pci_dev的vendor/device/class进行匹配。 - 绑定阶段: 匹配成功后,内核调用
pci_driver的probe()回调,传入pci_dev指针,驱动开始初始化硬件。 - 运行阶段: 驱动通过
pci_dev的resource[]访问BAR空间,通过irq注册中断处理函数。
4.4 关键API与使用模式
在驱动开发中,以下API是操作这三个核心数据结构的常用入口:
| API | 所属层 | 功能 |
|---|---|---|
pci_get_device() | 设备层 | 按vendor/device查找pci_dev |
pci_enable_device() | 设备层 | 启用设备(使能I/O和内存空间) |
pci_request_regions() | 设备层 | 申请BAR资源 |
pci_iomap() | 设备层 | 将BAR映射到CPU虚拟地址空间 |
pci_register_driver() | 驱动层 | 注册pci_driver到内核 |
pci_bus_read_config_*() | 总线层 | 通过pci_bus读取配置空间 |
RC驱动提示: 在RC模式下,经常需要遍历
pci_bus的children和devices链表来发现下游设备,并使用pci_bus_alloc_resource()分配总线资源。