pcie linux 驱动开发 介绍
1.Linux PCIe驱动开发简介
Linux PCIe驱动开发是一个复杂的主题,但我可以为您提供一个详细的概述和基本步骤。以下是 Linux PCIe 驱动开发的主要方面:
1. 基础知识
- 了解 PCIe 总线架构和协议
- 熟悉 Linux 内核模块开发
- 了解设备树(Device Tree)概念(对于嵌入式系统)
2. 驱动框架
Linux 内核提供了 PCI 子系统,简化了 PCIe 设备驱动的开发。主要使用以下结构和函数:
- struct pci_driver:描述 PCI 驱动程序
- struct pci_device_id:定义驱动程序支持的设备
- pci_register_driver():注册 PCI 驱动程序
- pci_unregister_driver():注销 PCI 驱动程序
3. 驱动程序开发步骤
a. 创建驱动程序结构:
```c
static struct pci_driver my_pcie_driver = {
.name = "my_pcie_driver",
.id_table = my_pcie_ids,
.probe = my_pcie_probe,
.remove = my_pcie_remove,
};
```
b. 定义支持的设备 ID:
```c
static const struct pci_device_id my_pcie_ids[] = {
{ PCI_DEVICE(VENDOR_ID, DEVICE_ID) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, my_pcie_ids);
```
c. 实现 probe 函数:
```c
static int my_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
// 启用设备
if (pci_enable_device(pdev))
return -EIO;
// 请求 I/O 和内存资源
if (pci_request_regions(pdev, "my_pcie_driver"))
goto err_disable_device;
// 映射 BAR 地址
void __iomem *mmio = pci_iomap(pdev, 0, 0);
if (!mmio)
goto err_release_regions;
// 其他初始化操作...
return 0;
err_release_regions:
pci_release_regions(pdev);
err_disable_device:
pci_disable_device(pdev);
return -EIO;
}
```
d. 实现 remove 函数:
```c
static void my_pcie_remove(struct pci_dev *pdev)
{
// 取消映射 BAR 地址
pci_iounmap(pdev, mmio);
// 释放 I/O 和内存资源
pci_release_regions(pdev);
// 禁用设备
pci_disable_device(pdev);
}
```
e. 模块初始化和清理:
```c
static int __init my_pcie_init(void)
{
return pci_register_driver(&my_pcie_driver);
}
static void __exit my_pcie_exit(void)
{
pci_unregister_driver(&my_pcie_driver);
}
module_init(my_pcie_init);
module_exit(my_pcie_exit);
```
4. DMA 操作
对于需要 DMA 的设备,您需要:
- 使用 dma_alloc_coherent() 分配 DMA 缓冲区
- 设置 DMA 掩码(pci_set_dma_mask())
- 使用 pci_set_master() 启用总线主控
5. 中断处理
- 使用 request_irq() 注册中断处理程序
- 在 probe 函数中设置 MSI 或 MSI-X 中断(如果支持)
6. 电源管理
实现 suspend 和 resume 回调函数,以支持系统休眠和唤醒。
7. 调试技巧
- 使用 printk() 输出调试信息
- 利用 /sys/kernel/debug/ 下的调试接口
- 使用 KGDB 进行内核调试
8. 编译和测试
- 编写 Makefile 用于编译驱动程序
- 使用 insmod 和 rmmod 命令加载和卸载驱动
- 通过 /sys/bus/pci/devices/ 检查设备状态
9. 高级主题
- MSI 和 MSI-X 中断
- PCIe 配置空间访问
- 热插拔支持
- SR-IOV 虚拟化
开发 PCIe 驱动程序需要深入了解 Linux 内核和 PCIe 规范。建议仔细阅读 Linux 内核文档,特别是 Documentation/PCI/ 目录下的文件,以获取更多详细信息和最佳实践。
2 linux pcie开发举例
使用字符设备接口来实现通信,这是一种常用且灵活的方法。以下是完整的PCIe驱动程序代码,包括与应用程序通信的部分:
```c
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#define DRIVER_NAME "my_pcie_driver"
#define DEVICE_NAME "my_pcie_dev"
struct my_pcie_dev {
struct pci_dev *pdev;
void __iomem *mmio_base;
resource_size_t mmio_len;
struct cdev cdev;
dev_t dev_num;
struct device *device;
};
static int my_pcie_open(struct inode *inode, struct file *file)
{
struct my_pcie_dev *dev = container_of(inode->i_cdev, struct my_pcie_dev, cdev);
file->private_data = dev;
return 0;
}
static int my_pcie_release(struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t my_pcie_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
struct my_pcie_dev *dev = file->private_data;
u32 value;
if (*offset >= sizeof(u32))
return 0;
value = ioread32(dev->mmio_base);
if (copy_to_user(buf, &value, sizeof(u32)))
return -EFAULT;
*offset += sizeof(u32);
return sizeof(u32);
}
static ssize_t my_pcie_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
struct my_pcie_dev *dev = file->private_data;
u32 value;
if (count != sizeof(u32))
return -EINVAL;
if (copy_from_user(&value, buf, sizeof(u32)))
return -EFAULT;
iowrite32(value, dev->mmio_base);
return sizeof(u32);
}
static const struct file_operations my_pcie_fops = {
.owner = THIS_MODULE,
.open = my_pcie_open,
.release = my_pcie_release,
.read = my_pcie_read,
.write = my_pcie_write,
};
static int my_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct my_pcie_dev *my_dev;
int err;
my_dev = devm_kzalloc(&pdev->dev, sizeof(*my_dev), GFP_KERNEL);
if (!my_dev)
return -ENOMEM;
my_dev->pdev = pdev;
err = pcim_enable_device(pdev);
if (err) {
dev_err(&pdev->dev, "Failed to enable PCI device\n");
return err;
}
err = pcim_iomap_regions(pdev, BIT(0), DRIVER_NAME);
if (err) {
dev_err(&pdev->dev, "Failed to request MMIO region\n");
return err;
}
my_dev->mmio_base = pcim_iomap_table(pdev)[0];
my_dev->mmio_len = pci_resource_len(pdev, 0);
pci_set_drvdata(pdev, my_dev);
// 创建字符设备
err = alloc_chrdev_region(&my_dev->dev_num, 0, 1, DEVICE_NAME);
if (err) {
dev_err(&pdev->dev, "Failed to allocate device number\n");
return err;
}
cdev_init(&my_dev->cdev, &my_pcie_fops);
err = cdev_add(&my_dev->cdev, my_dev->dev_num, 1);
if (err) {
dev_err(&pdev->dev, "Failed to add character device\n");
unregister_chrdev_region(my_dev->dev_num, 1);
return err;
}
my_dev->device = device_create(THIS_MODULE, NULL, my_dev->dev_num, NULL, DEVICE_NAME);
if (IS_ERR(my_dev->device)) {
dev_err(&pdev->dev, "Failed to create device\n");
cdev_del(&my_dev->cdev);
unregister_chrdev_region(my_dev->dev_num, 1);
return PTR_ERR(my_dev->device);
}
dev_info(&pdev->dev, "PCIe device probed successfully\n");
return 0;
}
static void my_pcie_remove(struct pci_dev *pdev)
{
struct my_pcie_dev *my_dev = pci_get_drvdata(pdev);
device_destroy(THIS_MODULE, my_dev->dev_num);
cdev_del(&my_dev->cdev);
unregister_chrdev_region(my_dev->dev_num, 1);
dev_info(&pdev->dev, "PCIe device removed\n");
}
static struct pci_device_id my_pcie_ids[] = {
{ PCI_DEVICE(0x1234, 0x5678) }, // 替换为实际的 Vendor ID 和 Device ID
{ 0, }
};
MODULE_DEVICE_TABLE(pci, my_pcie_ids);
static struct pci_driver my_pcie_driver = {
.name = DRIVER_NAME,
.id_table = my_pcie_ids,
.probe = my_pcie_probe,
.remove = my_pcie_remove,
};
module_pci_driver(my_pcie_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("PCIe driver with character device interface");
```
这个驱动程序创建了一个字符设备接口,允许应用程序通过文件操作与PCIe设备进行通信。现在,让我们创建一个简单的应用程序来演示如何与这个驱动程序通信:
```c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdint.h>
#define DEVICE_FILE "/dev/my_pcie_dev"
int main()
{
int fd;
uint32_t value_to_write = 0x12345678;
uint32_t value_read = 0;
// 打开设备文件
fd = open(DEVICE_FILE, O_RDWR);
if (fd < 0) {
perror("Failed to open the device");
return -1;
}
// 写入数据
if (write(fd, &value_to_write, sizeof(value_to_write)) != sizeof(value_to_write)) {
perror("Failed to write to the device");
close(fd);
return -1;
}
printf("Wrote value: 0x%08x\n", value_to_write);
// 读取数据
if (read(fd, &value_read, sizeof(value_read)) != sizeof(value_read)) {
perror("Failed to read from the device");
close(fd);
return -1;
}
printf("Read value: 0x%08x\n", value_read);
// 关闭设备文件
close(fd);
return 0;
}
```
这个应用程序执行以下操作:
1. 打开PCIe设备的字符设备文件。
2. 向设备写入一个32位的值。
3. 从设备读取一个32位的值。
4. 关闭设备文件。
要使用这个系统:
1. 编译并加载PCIe驱动程序。
2. 编译应用程序: `gcc -o pcie_app pcie_app.c`
3. 运行应用程序: `./pcie_app`
这个示例展示了基本的读写操作。根据你的具体需求,你可能需要添加更多的功能,如:
- 使用 ioctl 来执行特定的设备操作。
- 实现中断处理来支持异步事件。
- 添加 mmap 支持,允许直接映射设备内存到用户空间。
- 实现 DMA 传输以提高数据传输效率。
记住,在实际应用中,你需要根据具体的PCIe设备规格来调整驱动程序的行为,例如正确设置 BAR 地址,处理设备特定的寄存器等。
更多推荐


所有评论(0)