PCIe【9】Linux内核 PCI驱动
1. PCI驱动的架构层次
共分为4个核心层次:
-
PCI/PCIe核心层(
PCI Core
)-
driver/pci
目录,负责总线枚举、资源配置、设备发现和通用驱动框架。 -
功能:扫描PCIe拓扑结构、分配内存和中断资源、管理设备配置空间(如BAR、Capability结构)。
-
-
主机控制器驱动(
Host Controller Driver
)-
位于
drivers/pci/controller
或drivers/pci/host
目录,负责与硬件SoC
/芯片组的PCIe RC
交互。 -
功能:初始化Root Complex、处理总线扫描触发(如通过ACPI或设备树)。
-
-
PCIe设备驱动(
Device Driver
)-
用户编写的具体设备驱动,如网卡、GPU、量子密码卡驱动等。
-
关键操作:映射BAR空间、配置DMA、注册中断处理函数。
-
-
用户空间接口,提供
/sys/bus/pci
下的sysfs
节点。
2. PCI驱动的结构体
PCI层负责大部分的设备探测工作。当探测到一个PCI设备时,将通过pci_register_driver()
进行初始化。初始化主要包含以下工作:
- 启用设备
pci_enable_device()
- 从休眠状态切换到工作状态;此外,如果BIOS没有做,pci_enable_device()会分配设备的I/O和内存区域,以及分配一个IRQ
-
请求MMIO/IOP资源(Memory-Mapped I/O; I/O Port)
-
设置DMA掩码大小(对于流式和一致的DMA)
-
分配和初始化共享控制数据(
pci_allocate_coherent()
) -
访问设备配置空间(如果需要)
-
注册IRQ处理程序(
request_irq()
) -
初始化非PCI(即芯片的LAN/SCSI/等部分)
-
启用DMA/处理引擎
当使用完设备后,如果需要卸载模块,那采取以下步骤:
-
禁用设备产生的IRQ
-
释放IRQ(`free_irq())
-
停止所有DMA活动
-
释放DMA缓冲区(包括一致性和数据流式)
-
从其他子系统(例如scsi或netdev)上取消注册
-
释放MMIO/IOP资源
-
禁用设备
当驱动退出时,它只是调用pci_unregister_driver()
,PCI层会自动调用驱动处理的所有设备的移除钩子。
- __init 标记
含义:__init 是一个宏,它将一个函数标记为“仅在初始化期间使用”。
作用:
内存优化:被 __init 标记的函数(及其使用的数据,用 __initdata 标记)的代码会被编译器放置到一个特殊的内存段中(通常是 .init.text)。
初始化后释放:当内核启动完成(对于内建驱动)或模块加载成功(对于可加载模块)后,内核会认为这些初始化代码不再需要。此时,内核会释放这个 .init.text 段所占用的内存,将其归还给系统。这可以节省宝贵的内核内存空间。
使用场景:用于 module_init() 指定的初始化函数,以及所有只在初始化过程中被调用一次的函数。一旦初始化完成,这些函数的代码就不再需要了。
示例:
static int __init my_driver_init(void)
{
printk(KERN_INFO "My driver initializing...\n");
// 执行各种初始化操作,如注册设备、申请资源等
return 0; // 成功
}
module_init(my_driver_init); // 告诉内核哪个函数是初始化入口