linux内核中pcie驱动架构
一、PCIe驱动架构层次
Linux PCIe驱动分为四个核心层次:
-
PCI/PCIe核心层(PCI Core)
-
位于
drivers/pci
目录,负责总线枚举、资源配置、设备发现和通用驱动框架。 -
关键功能:扫描PCIe拓扑结构、分配内存和中断资源、管理设备配置空间(如BAR、Capability结构)。
-
核心数据结构:
struct pci_dev
(表示一个PCI设备)、struct pci_bus
(表示一条PCI总线)。
-
-
主机控制器驱动(Host Controller Driver)
-
位于
drivers/pci/controller
或drivers/pci/host
目录,负责与硬件SoC/芯片组的PCIe Root Complex交互。 -
示例驱动:Intel的
pcie-intel
、ARM的pcie-rcar
、国产芯片的定制控制器驱动。 -
功能:初始化Root Complex、处理总线扫描触发(如通过ACPI或设备树)。
-
-
PCIe设备驱动(Device Driver)
-
用户编写的具体设备驱动(如网卡、GPU、量子密码卡驱动)。
-
通过
struct pci_driver
注册驱动,实现probe()
、remove()
等回调。 -
关键操作:映射BAR空间、配置DMA、注册中断处理函数。
-
-
用户空间接口
-
提供
/sys/bus/pci
下的sysfs节点,或通过字符设备(如/dev/mycrypto
)暴露功能。 -
工具链:
lspci
、setpci
等命令直接访问配置空间。
-
二、总线枚举与设备发现
-
枚举流程
-
硬件初始化:BIOS/UEFI或Bootloader配置PCIe链路,内核启动后重新扫描。
-
拓扑构建:从Root Complex开始,递归扫描下游设备(Switch和Endpoint)。
-
资源配置:为每个设备分配内存地址(通过BAR)、中断号(MSI/MSI-X或INTx)。
-
-
配置空间访问
-
通过
pci_read_config_byte()
、pci_write_config_dword()
等API读写设备的配置空间。 -
关键字段:Vendor ID、Device ID、Class Code、BAR0-5、Capability列表(如PCIe Capability)。
-
三、驱动模型与设备匹配
-
驱动注册
-
设备驱动通过
pci_register_driver()
注册,示例代码:static struct pci_driver my_driver = { .name = "my_pcie_device", .id_table = my_pci_ids, // 设备ID列表 .probe = my_probe, .remove = my_remove, }; pci_register_driver(&my_driver);
-
-
设备匹配
-
基于
id_table
中的设备ID(Vendor ID + Device ID)或Class Code匹配设备。 -
匹配成功后,内核调用
probe()
函数初始化设备。
-
四、关键驱动开发步骤
-
资源映射
-
启用设备:
pci_enable_device()
。 -
申请BAR资源:
pci_request_regions()
。 -
映射BAR到内核地址空间:
pci_iomap()
。
-
-
中断处理
-
申请中断号:
pci_alloc_irq_vectors()
(支持MSI/MSI-X)。 -
注册中断处理函数:
request_irq()
。
-
-
DMA配置
-
设置DMA掩码:
dma_set_mask_and_coherent()
。 -
分配DMA缓冲区:
dma_alloc_coherent()
或使用流式DMA API。
-
五、代码结构与关键API
-
核心代码路径
-
总线管理:
drivers/pci/probe.c
、access.c
。 -
主机控制器驱动:
drivers/pci/controller/
。 -
设备驱动示例:
drivers/net/ethernet/
(如Intel网卡驱动)。
-
-
常用API
-
设备操作:
pci_read_config_*()
、pci_write_config_*()
。 -
资源管理:
pci_resource_start()
、pci_resource_len()
。 -
电源管理:
pci_set_power_state()
。
-
六、高级功能
-
SR-IOV虚拟化支持
-
通过
sysfs
或驱动代码创建Virtual Function(VF)。 -
示例:GPU或网卡的硬件虚拟化。
-
-
热插拔(Hotplug)
-
支持PCIe设备的热插拔,需实现
struct hotplug_slot_ops
。
-
-
ACPI/设备树集成
-
ACPI:解析
_DSM
方法配置设备。 -
设备树:在ARM架构中通过
pcie-controller
节点描述拓扑。
-
七、调试与工具
-
调试手段
-
dmesg
查看内核日志,定位probe()
失败或资源冲突。 -
通过
/sys/kernel/debug/pci/
查看设备详细信息。
-
-
用户工具
-
lspci -vv
:显示设备配置空间和Capability。 -
pcimem
:直接读写设备内存空间。
-
八、典型驱动示例(伪代码)
static int my_probe(struct pci_dev *pdev, const struct pci_device_id *id) {
// 启用设备
pci_enable_device(pdev);
// 申请BAR资源
pci_request_regions(pdev, "my_driver");
// 映射BAR0到内核虚拟地址
void __iomem *regs = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
// 配置DMA
dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
// 注册中断
int irq = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
request_irq(irq, my_isr, 0, "my_irq", dev);
// 创建设备节点
misc_register(&my_miscdev);
return 0;
}
static void my_remove(struct pci_dev *pdev) {
// 释放资源
pci_release_regions(pdev);
pci_disable_device(pdev);
}
九、架构总结
Linux PCIe驱动架构通过分层设计实现了硬件抽象与跨平台兼容性:
-
核心层统一管理总线与设备发现。
-
主机控制器驱动适配不同硬件平台。
-
设备驱动专注于功能实现,通过标准API与内核交互。
开发PCIe设备驱动时,需重点关注资源管理、中断处理、DMA配置及用户空间接口设计。