Appearance
PCIe驱动实现
PCIe(Peripheral Component Interconnect Express)是一种高速串行计算机扩展总线标准,广泛应用于现代计算机系统中,用于连接显卡、网卡、存储控制器等各种硬件设备。Linux内核提供了完整的PCIe子系统框架,使得开发者可以方便地实现PCIe设备驱动。本章将详细介绍PCIe驱动的核心数据结构、API接口和实现细节。
PCIe驱动概述
PCIe总线的主要特点:
- 高速传输:支持多种通道宽度(x1, x2, x4, x8, x16, x32),单通道传输速率高达16 GT/s(PCIe 5.0)
- 点对点架构:每个设备都有独立的通信通道,避免总线竞争
- 分层协议:由物理层、数据链路层和事务层组成
- 热插拔支持:允许设备在系统运行时插入和拔出
- 电源管理:支持多种电源状态和电源管理机制
- 向后兼容:保持与传统PCI设备的软件兼容性
Linux PCIe子系统架构:
- PCI核心:提供核心功能和API接口
- PCI总线驱动:负责管理PCIe总线硬件和设备枚举
- PCI设备驱动:实现具体设备的功能
- PCI配置空间:用于设备配置和状态查询
- PCI中断管理:处理设备中断
- PCI内存管理:管理设备内存和DMA操作
核心数据结构
1. PCI设备结构体
struct pci_dev是PCI设备的核心结构体:
c
struct pci_dev {
struct list_head bus_list; // 总线列表
struct pci_bus *bus; // 所属总线
struct pci_bus *subordinate; // 下属总线
void *sysdata; // 总线特定数据
struct proc_dir_entry *procent; // procfs入口
unsigned int devfn; // 设备和功能号
unsigned short vendor; // 厂商ID
unsigned short device; // 设备ID
unsigned short subsystem_vendor; // 子系统厂商ID
unsigned short subsystem_device; // 子系统设备ID
unsigned int class; // 设备类
u8 hdr_type; // 头部类型
u8 rom_base_reg; // ROM基址寄存器
struct pci_driver *driver; // 驱动指针
char name[80]; // 设备名称
int irq; // 中断号
struct resource resource[DEVICE_COUNT_RESOURCE]; // 资源数组
unsigned long dma_mask; // DMA掩码
// ... 其他字段
};2. PCI驱动结构体
struct pci_driver定义了PCI驱动的操作函数和设备ID表:
c
struct pci_driver {
struct list_head node; // 驱动列表
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); // 恢复函数
void (*shutdown)(struct pci_dev *dev); // 关闭函数
int (*sriov_configure)(struct pci_dev *dev, int num_vfs); // SR-IOV配置函数
struct device_driver driver; // 设备驱动
// ... 其他字段
};3. PCI设备ID表
struct pci_device_id定义了驱动支持的设备ID:
c
struct pci_device_id {
__u32 vendor; // 厂商ID
__u32 device; // 设备ID
__u32 subvendor; // 子系统厂商ID
__u32 subdevice; // 子系统设备ID
__u32 class; // 设备类
__u32 class_mask; // 设备类掩码
kernel_ulong_t driver_data; // 驱动私有数据
};4. PCI资源结构体
struct resource定义了设备的资源(内存、I/O端口等):
c
struct resource {
resource_size_t start; // 资源起始地址
resource_size_t end; // 资源结束地址
const char *name; // 资源名称
unsigned long flags; // 资源标志
struct resource *parent, *sibling, *child; // 资源树关系
};5. PCI总线结构体
struct pci_bus定义了PCI总线的信息:
c
struct pci_bus {
struct list_head node; // 总线列表
struct pci_bus *parent; // 父总线
struct list_head children; // 子总线列表
struct list_head devices; // 设备列表
struct pci_ops *ops; // 总线操作函数
struct resource *resource[PCI_BRIDGE_RESOURCES]; // 总线资源
struct pci_dev *self; // 桥设备
unsigned char number; // 总线号
unsigned char primary; // 主总线号
unsigned char secondary; // 次总线号
unsigned char subordinate; // 下属总线号
// ... 其他字段
};核心API接口
1. PCI驱动注册与注销
c
// 注册PCI驱动
int pci_register_driver(struct pci_driver *drv);
// 注销PCI驱动
void pci_unregister_driver(struct pci_driver *drv);
// 辅助宏定义
#define module_pci_driver(__pci_driver) \
module_driver(__pci_driver, pci_register_driver, pci_unregister_driver)2. PCI设备配置空间访问
c
// 读取配置空间字节
int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val);
// 读取配置空间字
int pci_read_config_word(struct pci_dev *dev, int where, u16 *val);
// 读取配置空间双字
int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);
// 写入配置空间字节
int pci_write_config_byte(struct pci_dev *dev, int where, u8 val);
// 写入配置空间字
int pci_write_config_word(struct pci_dev *dev, int where, u16 val);
// 写入配置空间双字
int pci_write_config_dword(struct pci_dev *dev, int where, u32 val);3. PCI设备资源管理
c
// 启用设备总线主控
int pci_set_master(struct pci_dev *dev);
// 禁用设备总线主控
void pci_clear_master(struct pci_dev *dev);
// 启用设备
int pci_enable_device(struct pci_dev *dev);
// 启用设备(带唤醒支持)
int pci_enable_device_wake(struct pci_dev *dev, pm_message_t state);
// 禁用设备
void pci_disable_device(struct pci_dev *dev);
// 申请设备I/O资源
int pci_request_regions(struct pci_dev *pdev, const char *res_name);
// 释放设备I/O资源
void pci_release_regions(struct pci_dev *pdev);
// 映射设备内存资源
void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen);
// 取消映射设备内存资源
void pci_iounmap(struct pci_dev *dev, void __iomem *addr);4. PCI中断管理
c
// 获取设备中断号
int pci_irq_vector(struct pci_dev *dev, unsigned int nr);
// 申请中断
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev);
// 释放中断
void free_irq(unsigned int irq, void *dev);
// 启用MSI中断
int pci_enable_msi(struct pci_dev *dev);
// 启用MSI-X中断
int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec);
// 禁用MSI中断
void pci_disable_msi(struct pci_dev *dev);
// 禁用MSI-X中断
void pci_disable_msix(struct pci_dev *dev);5. PCI DMA管理
c
// 设置DMA掩码
int pci_set_dma_mask(struct pci_dev *dev, u64 mask);
// 设置一致性DMA掩码
int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
// 分配一致性DMA内存
void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
dma_addr_t *dma_handle);
// 释放一致性DMA内存
void pci_free_consistent(struct pci_dev *hwdev, size_t size,
void *vaddr, dma_addr_t dma_handle);
// 分配流式DMA内存
dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size,
int direction);
// 取消映射流式DMA内存
void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr,
size_t size, int direction);
// 映射DMA内存数组
int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents,
int direction);
// 取消映射DMA内存数组
void pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, int nents,
int direction);PCIe设备驱动实现示例
1. 驱动模块初始化
c
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
// 设备ID表
static const struct pci_device_id pcie_ids[] = {
{ PCI_DEVICE(0x1234, 0x5678), }, // 厂商ID: 0x1234, 设备ID: 0x5678
{ 0, }
};
MODULE_DEVICE_TABLE(pci, pcie_ids);
// PCIe设备私有数据结构体
struct pcie_dev_priv {
struct pci_dev *dev; // PCI设备指针
void __iomem *bar0; // BAR0内存映射
void __iomem *bar1; // BAR1内存映射
int irq; // 中断号
void *dma_buffer; // DMA缓冲区虚拟地址
dma_addr_t dma_handle; // DMA缓冲区物理地址
size_t dma_size; // DMA缓冲区大小
struct work_struct irq_work; // 中断工作队列
};2. 设备探测与移除函数
c
// 设备探测函数
static int pcie_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct pcie_dev_priv *priv;
int ret;
// 启用设备
ret = pci_enable_device(dev);
if (ret) {
dev_err(&dev->dev, "Failed to enable PCI device\n");
return ret;
}
// 设置设备DMA掩码
ret = pci_set_dma_mask(dev, DMA_BIT_MASK(64));
if (ret) {
ret = pci_set_dma_mask(dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(&dev->dev, "Failed to set DMA mask\n");
goto disable_device;
}
}
ret = pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(64));
if (ret) {
ret = pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(&dev->dev, "Failed to set consistent DMA mask\n");
goto disable_device;
}
}
// 分配私有数据
priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
goto disable_device;
}
priv->dev = dev;
pci_set_drvdata(dev, priv);
// 启用总线主控
pci_set_master(dev);
// 申请设备I/O资源
ret = pci_request_regions(dev, "pcie_example");
if (ret) {
dev_err(&dev->dev, "Failed to request regions\n");
goto clear_master;
}
// 映射BAR0内存
priv->bar0 = pci_iomap(dev, 0, 0);
if (!priv->bar0) {
dev_err(&dev->dev, "Failed to map BAR0\n");
ret = -ENOMEM;
goto release_regions;
}
// 映射BAR1内存
priv->bar1 = pci_iomap(dev, 1, 0);
if (!priv->bar1) {
dev_err(&dev->dev, "Failed to map BAR1\n");
ret = -ENOMEM;
goto unmap_bar0;
}
// 获取中断号
priv->irq = dev->irq;
if (!priv->irq) {
dev_err(&dev->dev, "No IRQ assigned\n");
ret = -ENODEV;
goto unmap_bar1;
}
// 分配DMA缓冲区
priv->dma_size = 4096;
priv->dma_buffer = pci_alloc_consistent(dev, priv->dma_size, &priv->dma_handle);
if (!priv->dma_buffer) {
dev_err(&dev->dev, "Failed to allocate DMA buffer\n");
ret = -ENOMEM;
goto unmap_bar1;
}
// 初始化工作队列
INIT_WORK(&priv->irq_work, pcie_irq_work_handler);
// 申请中断
ret = request_irq(priv->irq, pcie_irq_handler, IRQF_SHARED, "pcie_example", priv);
if (ret) {
dev_err(&dev->dev, "Failed to request IRQ %d\n", priv->irq);
goto free_dma_buffer;
}
// 设备初始化
pcie_hw_init(priv);
dev_info(&dev->dev, "PCIe device initialized successfully\n");
dev_info(&dev->dev, "BAR0 mapped to %p\n", priv->bar0);
dev_info(&dev->dev, "BAR1 mapped to %p\n", priv->bar1);
dev_info(&dev->dev, "Using IRQ %d\n", priv->irq);
dev_info(&dev->dev, "DMA buffer: virt=%p, phys=0x%llx, size=%zu\n",
priv->dma_buffer, (unsigned long long)priv->dma_handle, priv->dma_size);
return 0;
free_dma_buffer:
pci_free_consistent(dev, priv->dma_size, priv->dma_buffer, priv->dma_handle);
unmap_bar1:
pci_iounmap(dev, priv->bar1);
unmap_bar0:
pci_iounmap(dev, priv->bar0);
release_regions:
pci_release_regions(dev);
clear_master:
pci_clear_master(dev);
disable_device:
pci_disable_device(dev);
return ret;
}
// 设备移除函数
static void pcie_remove(struct pci_dev *dev)
{
struct pcie_dev_priv *priv = pci_get_drvdata(dev);
// 释放中断
free_irq(priv->irq, priv);
// 取消DMA缓冲区
pci_free_consistent(dev, priv->dma_size, priv->dma_buffer, priv->dma_handle);
// 取消内存映射
pci_iounmap(dev, priv->bar1);
pci_iounmap(dev, priv->bar0);
// 释放I/O资源
pci_release_regions(dev);
// 禁用总线主控
pci_clear_master(dev);
// 禁用设备
pci_disable_device(dev);
dev_info(&dev->dev, "PCIe device removed\n");
}3. 设备硬件初始化
c
// 设备硬件初始化
static void pcie_hw_init(struct pcie_dev_priv *priv)
{
// 配置设备寄存器
iowrite32(0x00000001, priv->bar0 + 0x00); // 启用设备
iowrite32(0x0000000F, priv->bar0 + 0x04); // 启用所有中断
iowrite32(priv->dma_handle, priv->bar0 + 0x08); // 设置DMA地址
iowrite32(priv->dma_size, priv->bar0 + 0x0C); // 设置DMA大小
// 初始化设备状态
iowrite32(0x00000000, priv->bar1 + 0x00); // 清除状态寄存器
iowrite32(0x00000000, priv->bar1 + 0x04); // 清除控制寄存器
}4. 中断处理函数
c
// 中断处理函数
static irqreturn_t pcie_irq_handler(int irq, void *dev_id)
{
struct pcie_dev_priv *priv = dev_id;
u32 status;
// 读取中断状态
status = ioread32(priv->bar1 + 0x00);
if (!status) {
return IRQ_NONE; // 不是我们的中断
}
// 清除中断
iowrite32(status, priv->bar1 + 0x00);
// 调度工作队列处理中断
schedule_work(&priv->irq_work);
return IRQ_HANDLED;
}
// 中断工作队列处理函数
static void pcie_irq_work_handler(struct work_struct *work)
{
struct pcie_dev_priv *priv = container_of(work, struct pcie_dev_priv, irq_work);
u32 status;
// 重新读取状态寄存器确认中断
status = ioread32(priv->bar1 + 0x00);
if (!status) {
return;
}
// 处理不同类型的中断
if (status & 0x01) {
dev_info(&priv->dev->dev, "DMA completion interrupt received\n");
// 处理DMA完成中断
pcie_handle_dma_complete(priv);
}
if (status & 0x02) {
dev_info(&priv->dev->dev, "Error interrupt received\n");
// 处理错误中断
pcie_handle_error(priv);
}
if (status & 0x04) {
dev_info(&priv->dev->dev, "Command interrupt received\n");
// 处理命令中断
pcie_handle_command(priv);
}
if (status & 0x08) {
dev_info(&priv->dev->dev, "Status change interrupt received\n");
// 处理状态变化中断
pcie_handle_status_change(priv);
}
}5. DMA操作处理
c
// 处理DMA完成中断
static void pcie_handle_dma_complete(struct pcie_dev_priv *priv)
{
u32 dma_status = ioread32(priv->bar0 + 0x10);
dev_info(&priv->dev->dev, "DMA completed with status 0x%08x\n", dma_status);
// 检查DMA状态
if (dma_status & 0x01) {
dev_info(&priv->dev->dev, "DMA transfer succeeded\n");
// 处理接收到的数据
pcie_process_dma_data(priv);
} else {
dev_err(&priv->dev->dev, "DMA transfer failed\n");
// 处理DMA错误
pcie_handle_dma_error(priv);
}
}
// 处理DMA数据
static void pcie_process_dma_data(struct pcie_dev_priv *priv)
{
// 从DMA缓冲区读取数据
u32 *data = (u32 *)priv->dma_buffer;
u32 length = priv->dma_size / sizeof(u32);
dev_info(&priv->dev->dev, "Processing %zu bytes of DMA data\n", priv->dma_size);
// 打印前16字节数据(示例)
for (int i = 0; i < min(length, 4U); i++) {
dev_info(&priv->dev->dev, "data[%d] = 0x%08x\n", i, data[i]);
}
}
// 启动DMA传输
static int pcie_start_dma_transfer(struct pcie_dev_priv *priv, size_t size)
{
if (size > priv->dma_size) {
dev_err(&priv->dev->dev, "DMA transfer size %zu exceeds buffer size %zu\n",
size, priv->dma_size);
return -EINVAL;
}
// 准备DMA传输
iowrite32(size, priv->bar0 + 0x14); // 设置传输大小
iowrite32(0x01, priv->bar0 + 0x18); // 启动DMA传输
dev_info(&priv->dev->dev, "Started DMA transfer of %zu bytes\n", size);
return 0;
}6. 驱动结构体定义
c
// PCI驱动结构体
static struct pci_driver pcie_driver = {
.name = "pcie_example",
.id_table = pcie_ids,
.probe = pcie_probe,
.remove = pcie_remove,
};
// 模块初始化和退出
module_pci_driver(pcie_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux Driver Developer");
MODULE_DESCRIPTION("PCIe Example Driver");
MODULE_VERSION("1.0");PCIe中断处理
1. MSI/MSI-X中断
MSI(Message Signaled Interrupts)和MSI-X是PCIe的高级中断机制,相比传统的INTx中断具有更高的性能和灵活性:
c
// 使用MSI中断的探测函数
static int pcie_probe_msi(struct pci_dev *dev, const struct pci_device_id *id)
{
struct pcie_dev_priv *priv;
int ret;
// ... 其他初始化代码 ...
// 启用MSI中断
ret = pci_enable_msi(dev);
if (ret) {
dev_err(&dev->dev, "Failed to enable MSI: %d\n", ret);
goto error;
}
priv->irq = dev->irq;
// ... 申请中断等代码 ...
return 0;
// ... 错误处理代码 ...
}
// 使用MSI-X中断的探测函数
static int pcie_probe_msix(struct pci_dev *dev, const struct pci_device_id *id)
{
struct pcie_dev_priv *priv;
struct msix_entry entries[2];
int ret;
// ... 其他初始化代码 ...
// 配置MSI-X中断向量
entries[0].entry = 0;
entries[0].vector = -1;
entries[1].entry = 1;
entries[1].vector = -1;
// 启用MSI-X中断
ret = pci_enable_msix(dev, entries, 2);
if (ret) {
dev_err(&dev->dev, "Failed to enable MSI-X: %d\n", ret);
goto error;
}
// 申请第一个中断向量
ret = request_irq(entries[0].vector, pcie_irq_handler0, 0, "pcie_example0", priv);
if (ret) {
dev_err(&dev->dev, "Failed to request MSI-X vector 0: %d\n", ret);
goto disable_msix;
}
// 申请第二个中断向量
ret = request_irq(entries[1].vector, pcie_irq_handler1, 0, "pcie_example1", priv);
if (ret) {
dev_err(&dev->dev, "Failed to request MSI-X vector 1: %d\n", ret);
goto free_irq0;
}
// ... 其他代码 ...
return 0;
free_irq0:
free_irq(entries[0].vector, priv);
disable_msix:
pci_disable_msix(dev);
// ... 其他错误处理代码 ...
}2. 中断亲和性
对于多核系统,可以设置中断亲和性来控制中断在哪个CPU核心上处理:
c
// 设置中断亲和性
static void pcie_set_irq_affinity(struct pcie_dev_priv *priv)
{
struct cpumask mask;
// 设置中断只在CPU 0上处理
cpumask_clear(&mask);
cpumask_set_cpu(0, &mask);
// 应用中断亲和性
if (irq_set_affinity(priv->irq, &mask)) {
dev_warn(&priv->dev->dev, "Failed to set IRQ affinity\n");
} else {
dev_info(&priv->dev->dev, "IRQ %d affinity set to CPU 0\n", priv->irq);
}
}PCIe内存映射与DMA
1. BAR内存映射
PCIe设备通常有多个BAR(Base Address Register)用于内存映射:
c
// 映射所有可用的BAR
static void pcie_map_bars(struct pcie_dev_priv *priv)
{
struct pci_dev *dev = priv->dev;
int bar;
for (bar = 0; bar < 6; bar++) {
// 检查BAR是否存在且为内存类型
if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) {
resource_size_t start = pci_resource_start(dev, bar);
resource_size_t end = pci_resource_end(dev, bar);
resource_size_t size = resource_size(&dev->resource[bar]);
void __iomem *addr = pci_iomap(dev, bar, size);
if (addr) {
dev_info(&dev->dev, "BAR%d: 0x%llx-0x%llx (%zu bytes) mapped to %p\n",
bar, (unsigned long long)start, (unsigned long long)end,
size, addr);
// 根据BAR号保存映射地址
switch (bar) {
case 0:
priv->bar0 = addr;
break;
case 1:
priv->bar1 = addr;
break;
case 2:
priv->bar2 = addr;
break;
// ... 其他BAR处理 ...
}
} else {
dev_err(&dev->dev, "Failed to map BAR%d\n", bar);
}
}
}
}2. 流式DMA操作
流式DMA用于临时数据传输:
c
// 流式DMA传输示例
static int pcie_streaming_dma_example(struct pcie_dev_priv *priv, void *data, size_t size)
{
dma_addr_t dma_addr;
int ret = 0;
// 映射内存用于DMA传输
dma_addr = dma_map_single(&priv->dev->dev, data, size, DMA_TO_DEVICE);
if (dma_mapping_error(&priv->dev->dev, dma_addr)) {
dev_err(&priv->dev->dev, "Failed to map streaming DMA\n");
return -ENOMEM;
}
// 配置设备DMA地址和大小
iowrite32(dma_addr, priv->bar0 + 0x20);
iowrite32(size, priv->bar0 + 0x24);
// 启动DMA传输
iowrite32(0x01, priv->bar0 + 0x28);
// 等待DMA传输完成
while (!(ioread32(priv->bar1 + 0x00) & 0x01)) {
msleep(1);
}
// 检查DMA状态
if (ioread32(priv->bar0 + 0x30) & 0x01) {
dev_info(&priv->dev->dev, "Streaming DMA transfer succeeded\n");
} else {
dev_err(&priv->dev->dev, "Streaming DMA transfer failed\n");
ret = -EIO;
}
// 清除中断
iowrite32(0x01, priv->bar1 + 0x00);
// 取消DMA映射
dma_unmap_single(&priv->dev->dev, dma_addr, size, DMA_TO_DEVICE);
return ret;
}PCIe设备的用户空间接口
1. 使用sysfs接口
c
// sysfs显示设备信息
static ssize_t pcie_show_info(struct device *dev, struct device_attribute *attr, char *buf)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct pcie_dev_priv *priv = pci_get_drvdata(pci_dev);
return sprintf(buf, "Vendor ID: 0x%04x\n"
"Device ID: 0x%04x\n"
"BAR0: %p\n"
"BAR1: %p\n"
"IRQ: %d\n"
"DMA Buffer: virt=%p, phys=0x%llx\n",
pci_dev->vendor, pci_dev->device, priv->bar0, priv->bar1,
priv->irq, priv->dma_buffer, (unsigned long long)priv->dma_handle);
}
// sysfs属性定义
static DEVICE_ATTR(info, 0444, pcie_show_info, NULL);
// sysfs写入控制
static ssize_t pcie_store_control(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct pcie_dev_priv *priv = pci_get_drvdata(pci_dev);
u32 value;
if (kstrtou32(buf, 16, &value)) {
return -EINVAL;
}
// 写入控制寄存器
iowrite32(value, priv->bar0 + 0x40);
dev_info(dev, "Control register set to 0x%08x\n", value);
return count;
}
// sysfs属性定义
static DEVICE_ATTR(control, 0644, NULL, pcie_store_control);
// sysfs属性组
static struct attribute *pcie_attrs[] = {
&dev_attr_info.attr,
&dev_attr_control.attr,
NULL,
};
ATTRIBUTE_GROUPS(pcie);
// 在驱动结构体中添加sysfs支持
static struct pci_driver pcie_driver = {
.name = "pcie_example",
.id_table = pcie_ids,
.probe = pcie_probe,
.remove = pcie_remove,
.driver = {
.dev_groups = pcie_groups,
},
};2. 使用字符设备接口
c
// 文件操作结构体
static const struct file_operations pcie_fops = {
.owner = THIS_MODULE,
.open = pcie_open,
.release = pcie_release,
.read = pcie_read,
.write = pcie_write,
.unlocked_ioctl = pcie_ioctl,
.mmap = pcie_mmap,
};
// 字符设备结构体
static struct cdev pcie_cdev;
static dev_t pcie_devno;
static struct class *pcie_class;
// 初始化字符设备
static int pcie_init_chrdev(void)
{
int ret;
// 分配设备号
ret = alloc_chrdev_region(&pcie_devno, 0, 1, "pcie_example");
if (ret) {
return ret;
}
// 初始化字符设备
cdev_init(&pcie_cdev, &pcie_fops);
pcie_cdev.owner = THIS_MODULE;
// 添加字符设备到系统
ret = cdev_add(&pcie_cdev, pcie_devno, 1);
if (ret) {
unregister_chrdev_region(pcie_devno, 1);
return ret;
}
// 创建类
pcie_class = class_create(THIS_MODULE, "pcie_example");
if (IS_ERR(pcie_class)) {
cdev_del(&pcie_cdev);
unregister_chrdev_region(pcie_devno, 1);
return PTR_ERR(pcie_class);
}
// 创建设备节点
device_create(pcie_class, NULL, pcie_devno, NULL, "pcie_example");
return 0;
}
// 清理字符设备
static void pcie_cleanup_chrdev(void)
{
device_destroy(pcie_class, pcie_devno);
class_destroy(pcie_class);
cdev_del(&pcie_cdev);
unregister_chrdev_region(pcie_devno, 1);
}高级PCIe功能
1. SR-IOV支持
SR-IOV(Single Root I/O Virtualization)允许一个物理PCIe设备虚拟化为多个虚拟功能(VF):
c
// SR-IOV配置函数
static int pcie_sriov_configure(struct pci_dev *dev, int num_vfs)
{
struct pcie_dev_priv *priv = pci_get_drvdata(dev);
int ret;
dev_info(&dev->dev, "SR-IOV configure: %d VFs\n", num_vfs);
// 配置SR-IOV
ret = pci_enable_sriov(dev, num_vfs);
if (ret) {
dev_err(&dev->dev, "Failed to enable SR-IOV: %d\n", ret);
return ret;
}
// 配置虚拟功能
if (num_vfs > 0) {
priv->num_vfs = num_vfs;
// 初始化虚拟功能
pcie_init_vfs(priv);
} else {
// 清理虚拟功能
pcie_cleanup_vfs(priv);
priv->num_vfs = 0;
}
return 0;
}
// 在驱动结构体中添加SR-IOV支持
static struct pci_driver pcie_driver = {
.name = "pcie_example",
.id_table = pcie_ids,
.probe = pcie_probe,
.remove = pcie_remove,
.sriov_configure = pcie_sriov_configure,
};2. PCIe热插拔
PCIe热插拔允许在系统运行时插入和拔出设备:
c
// 热插拔事件处理
static int pcie_hotplug_event(struct notifier_block *nb, unsigned long action,
void *data)
{
struct pci_bus *bus = data;
switch (action) {
case PCIEHP_HP_EJECTION:
dev_info(NULL, "PCIe hotplug ejection event on bus %d\n", bus->number);
break;
case PCIEHP_HP_INSERTION:
dev_info(NULL, "PCIe hotplug insertion event on bus %d\n", bus->number);
break;
case PCIEHP_HP_POWERON:
dev_info(NULL, "PCIe hotplug power on event on bus %d\n", bus->number);
break;
case PCIEHP_HP_POWEROFF:
dev_info(NULL, "PCIe hotplug power off event on bus %d\n", bus->number);
break;
default:
dev_info(NULL, "Unknown PCIe hotplug event %lu on bus %d\n", action, bus->number);
break;
}
return NOTIFY_OK;
}
// 热插拔通知块
static struct notifier_block pcie_hotplug_notifier = {
.notifier_call = pcie_hotplug_event,
};
// 注册热插拔通知
static int __init pcie_hotplug_init(void)
{
return pciehp_register_notifier(&pcie_hotplug_notifier);
}
// 注销热插拔通知
static void __exit pcie_hotplug_exit(void)
{
pciehp_unregister_notifier(&pcie_hotplug_notifier);
}3. PCIe电源管理
PCIe支持多种电源管理状态:
c
// 设备挂起函数
static int pcie_suspend(struct pci_dev *dev, pm_message_t state)
{
struct pcie_dev_priv *priv = pci_get_drvdata(dev);
// 保存设备状态
priv->saved_bar0 = ioread32(priv->bar0 + 0x00);
priv->saved_bar1 = ioread32(priv->bar1 + 0x00);
// 禁用中断
free_irq(priv->irq, priv);
// 禁用设备
pci_disable_device(dev);
// 设置设备电源状态
pci_set_power_state(dev, pci_choose_state(dev, state));
dev_info(&dev->dev, "PCIe device suspended\n");
return 0;
}
// 设备恢复函数
static int pcie_resume(struct pci_dev *dev)
{
struct pcie_dev_priv *priv = pci_get_drvdata(dev);
int ret;
// 恢复设备电源状态
pci_set_power_state(dev, PCI_D0);
// 启用设备
ret = pci_enable_device(dev);
if (ret) {
dev_err(&dev->dev, "Failed to enable device on resume\n");
return ret;
}
// 恢复设备寄存器
iowrite32(priv->saved_bar0, priv->bar0 + 0x00);
iowrite32(priv->saved_bar1, priv->bar1 + 0x00);
// 重新申请中断
ret = request_irq(priv->irq, pcie_irq_handler, IRQF_SHARED, "pcie_example", priv);
if (ret) {
dev_err(&dev->dev, "Failed to request IRQ on resume\n");
return ret;
}
// 重新初始化设备
pcie_hw_init(priv);
dev_info(&dev->dev, "PCIe device resumed\n");
return 0;
}
// 在驱动结构体中添加电源管理支持
static struct pci_driver pcie_driver = {
.name = "pcie_example",
.id_table = pcie_ids,
.probe = pcie_probe,
.remove = pcie_remove,
.suspend = pcie_suspend,
.resume = pcie_resume,
};最佳实践
资源管理:
- 正确申请和释放设备资源
- 使用devm_*函数族简化资源管理
- 实现设备的正确引用计数
错误处理:
- 完善的错误检测和恢复机制
- 适当的错误日志记录
- 资源的正确回滚
性能优化:
- 使用MSI/MSI-X中断提高中断处理性能
- 合理使用DMA减少CPU开销
- 批量处理数据减少中断频率
- 优化内存访问模式
调试支持:
- 提供详细的调试日志
- 实现sysfs接口用于运行时配置和状态查询
- 支持动态调试(dynamic debug)
安全性:
- 验证所有用户输入
- 实现适当的访问控制
- 防止DMA攻击
- 保护设备寄存器免受恶意访问
兼容性:
- 支持不同的PCIe版本和特性
- 处理设备ID和子系统ID的变化
- 支持多种硬件配置
可维护性:
- 清晰的代码结构和注释
- 模块化设计
- 遵循Linux内核编码风格
- 使用标准的内核API
总结
PCIe驱动实现主要包括以下步骤:
- 定义设备ID表和驱动结构体
- 实现设备探测和移除函数
- 配置设备资源(I/O、内存、中断)
- 实现中断处理和DMA操作
- 提供用户空间接口(sysfs、字符设备等)
- 实现高级功能(SR-IOV、热插拔、电源管理等)
通过Linux PCIe子系统提供的API接口,开发者可以方便地实现各种PCIe设备驱动。PCIe驱动的关键在于理解PCIe总线的架构和特性,正确管理设备资源,实现高效的中断处理和DMA操作,以及提供友好的用户空间接口。
随着PCIe技术的不断发展,新的特性如PCIe 5.0/6.0、CXL(Compute Express Link)等也在不断涌现,开发者需要持续关注PCIe技术的最新发展,以实现更加高效和功能丰富的PCIe设备驱动。