Skip to content

PCIe驱动实现

PCIe(Peripheral Component Interconnect Express)是一种高速串行计算机扩展总线标准,广泛应用于现代计算机系统中,用于连接显卡、网卡、存储控制器等各种硬件设备。Linux内核提供了完整的PCIe子系统框架,使得开发者可以方便地实现PCIe设备驱动。本章将详细介绍PCIe驱动的核心数据结构、API接口和实现细节。

PCIe驱动概述

PCIe总线的主要特点:

  1. 高速传输:支持多种通道宽度(x1, x2, x4, x8, x16, x32),单通道传输速率高达16 GT/s(PCIe 5.0)
  2. 点对点架构:每个设备都有独立的通信通道,避免总线竞争
  3. 分层协议:由物理层、数据链路层和事务层组成
  4. 热插拔支持:允许设备在系统运行时插入和拔出
  5. 电源管理:支持多种电源状态和电源管理机制
  6. 向后兼容:保持与传统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,
};

最佳实践

  1. 资源管理

    • 正确申请和释放设备资源
    • 使用devm_*函数族简化资源管理
    • 实现设备的正确引用计数
  2. 错误处理

    • 完善的错误检测和恢复机制
    • 适当的错误日志记录
    • 资源的正确回滚
  3. 性能优化

    • 使用MSI/MSI-X中断提高中断处理性能
    • 合理使用DMA减少CPU开销
    • 批量处理数据减少中断频率
    • 优化内存访问模式
  4. 调试支持

    • 提供详细的调试日志
    • 实现sysfs接口用于运行时配置和状态查询
    • 支持动态调试(dynamic debug)
  5. 安全性

    • 验证所有用户输入
    • 实现适当的访问控制
    • 防止DMA攻击
    • 保护设备寄存器免受恶意访问
  6. 兼容性

    • 支持不同的PCIe版本和特性
    • 处理设备ID和子系统ID的变化
    • 支持多种硬件配置
  7. 可维护性

    • 清晰的代码结构和注释
    • 模块化设计
    • 遵循Linux内核编码风格
    • 使用标准的内核API

总结

PCIe驱动实现主要包括以下步骤:

  1. 定义设备ID表和驱动结构体
  2. 实现设备探测和移除函数
  3. 配置设备资源(I/O、内存、中断)
  4. 实现中断处理和DMA操作
  5. 提供用户空间接口(sysfs、字符设备等)
  6. 实现高级功能(SR-IOV、热插拔、电源管理等)

通过Linux PCIe子系统提供的API接口,开发者可以方便地实现各种PCIe设备驱动。PCIe驱动的关键在于理解PCIe总线的架构和特性,正确管理设备资源,实现高效的中断处理和DMA操作,以及提供友好的用户空间接口。

随着PCIe技术的不断发展,新的特性如PCIe 5.0/6.0、CXL(Compute Express Link)等也在不断涌现,开发者需要持续关注PCIe技术的最新发展,以实现更加高效和功能丰富的PCIe设备驱动。