目录
PCIe 实战实例来展示如何开发一个简单的 PCIe 设备驱动程序,该驱动程序将设备注册到内核,并实现基本的读写功能。我们将使用 Linux 内核提供的 PCIe API 来完成这个任务。
PCIe 实战实例:开发一个简单的 PCIe 设备驱动程序
目标
- 理解 PCIe 设备的基本结构和配置空间。
- 学习如何编写 PCIe 设备驱动程序。
- 实现设备的读写功能。
- 测试和调试驱动程序。
环境准备
- 硬件:一块支持 PCIe 的开发板或者虚拟机。
- 软件:Linux 操作系统(推荐 Ubuntu),因为 Linux 内核提供了丰富的 PCIe 支持。
- 开发工具:GCC 编译器、Make 工具、文本编辑器(如 Vim 或者 VSCode)。
步骤一:理解 PCIe 基本概念
- 物理层:PCIe 使用差分信号对传输数据,支持多种链路宽度(如 x1, x4, x8, x16)。
- 数据链路层:负责确保数据包的正确传输,包括错误检测和重传机制。
- 事务层:处理请求和响应消息,定义了读写操作的协议。
- 配置空间:每个 PCIe 设备都有一个唯一的配置空间,用于存储设备信息和控制寄存器。
步骤二:设置开发环境
- 安装操作系统:在开发板或虚拟机上安装 Ubuntu。
- 安装开发工具:打开终端并运行以下命令来安装必要的开发工具。 bash
深色版本
sudo apt-get update sudo apt-get install build-essential
步骤三:开发 PCIe 设备驱动程序
我们将开发一个简单的 PCIe 设备驱动程序,该驱动程序将设备注册到内核,并提供基本的读写功能。
-
创建项目文件夹:
bash深色版本
mkdir pcie_driver_example cd pcie_driver_example
-
编写驱动程序代码: 创建一个名为
cpcie_driver.c
的文件,并编写以下代码:深色版本
#include <linux/init.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/io.h> // 用于内存映射 #include <linux/fs.h> // 用于文件操作 #include <linux/cdev.h> // 用于字符设备 #include <linux/uaccess.h> // 用于用户空间访问 MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple PCIe driver with read/write support"); #define DEVICE_NAME "pcie_example" #define CLASS_NAME "pcie_class" static dev_t dev; static struct cdev c_dev; static struct class *cl; static struct pci_dev *pdev; static void __iomem *bar0; static struct pci_device_id pcie_dev_ids[] = { { PCI_DEVICE(0x1234, 0x5678) }, // 替换为你设备的Vendor ID和Device ID { 0, } // 表示列表结束 }; static int pcie_driver_open(struct inode *inode, struct file *file) { return 0; } static int pcie_driver_close(struct inode *inode, struct file *file) { return 0; } static ssize_t pcie_driver_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { unsigned int value = ioread32(bar0); if (copy_to_user(buf, &value, sizeof(unsigned int))) return -EFAULT; return sizeof(unsigned int); } static ssize_t pcie_driver_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { unsigned int value; if (count != sizeof(unsigned int)) return -EINVAL; if (copy_from_user(&value, buf, sizeof(unsigned int))) return -EFAULT; iowrite32(value, bar0); return sizeof(unsigned int); } static struct file_operations fops = { .owner = THIS_MODULE, .open = pcie_driver_open, .release = pcie_driver_close, .read = pcie_driver_read, .write = pcie_driver_write }; static int pcie_driver_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int ret; unsigned long bar0_start, bar0_len; // 请求PCI资源 ret = pci_enable_device(pdev); if (ret) { dev_err(&pdev->dev, "Failed to enable PCI device\n"); return ret; } // 获取BAR0地址和长度 bar0_start = pci_resource_start(pdev, 0); bar0_len = pci_resource_len(pdev, 0); // 映射BAR0到内存 bar0 = ioremap(bar0_start, bar0_len); if (!bar0) { dev_err(&pdev->dev, "Failed to map BAR0\n"); pci_disable_device(pdev); return -ENOMEM; } // 注册字符设备 alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME); cdev_init(&c_dev, &fops); cdev_add(&c_dev, dev, 1); cl = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(cl)) { unregister_chrdev_region(dev, 1); iounmap(bar0); pci_disable_device(pdev); return PTR_ERR(cl); } device_create(cl, NULL, dev, NULL, DEVICE_NAME); printk(KERN_INFO "PCIe Driver: Device Found and Mapped!\n"); return 0; } static void pcie_driver_remove(struct pci_dev *pdev) { device_destroy(cl, dev); class_destroy(cl); cdev_del(&c_dev); unregister_chrdev_region(dev, 1); iounmap(bar0); pci_disable_device(pdev); printk(KERN_INFO "PCIe Driver: Device Removed!\n"); } static struct pci_driver pcie_driver = { .name = "pcie_example_driver", .id_table = pcie_dev_ids, .probe = pcie_driver_probe, .remove = pcie_driver_remove, }; static int __init pcie_driver_init(void) { return pci_register_driver(&pcie_driver); } static void __exit pcie_driver_exit(void) { pci_unregister_driver(&pcie_driver); } module_init(pcie_driver_init); module_exit(pcie_driver_exit);
-
编写 Makefile: 在同一目录下创建一个名为
makefileMakefile
的文件,内容如下:深色版本
obj-m += pcie_driver.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
-
编译驱动程序: 在终端中运行以下命令来编译驱动程序:
bash深色版本
make
步骤四:测试和调试驱动程序
- 加载驱动程序: bash
深色版本
sudo insmod pcie_driver.ko
- 查看内核日志: bash
深色版本
查看输出确认驱动程序是否成功加载并且找到了设备。dmesg | tail
- 创建设备节点: 如果设备节点没有自动创建,可以手动创建: bash
深色版本
sudo mknod /dev/pcie_example c 240 0 sudo chmod 666 /dev/pcie_example
- 测试读写功能:
- 读取设备寄存器: bash
深色版本
cat /dev/pcie_example
- 写入设备寄存器: bash
深色版本
echo 12345678 > /dev/pcie_example
- 读取设备寄存器: bash
- 卸载驱动程序: bash
深色版本
sudo rmmod pcie_driver
步骤五:深入学习和实践
- 学习 PCIe 配置空间:了解如何通过配置空间访问设备属性。
- 实现 DMA 功能:学习如何使用 Direct Memory Access (DMA) 技术提高数据传输效率。
- 多设备支持:扩展驱动程序以支持多个相同类型的 PCIe 设备。
- 性能优化:研究如何优化驱动程序以减少延迟和提高吞吐量。
结语
通过以上步骤,你应该能够建立一个具有读写功能的 PCIe 设备驱动程序,并具备进一步探索 PCIe 技术的基础。PCIe 是一个复杂的领域,涉及到许多底层硬件知识和技术细节,因此持续的学习和实践是非常重要的。
标签:驱动程序,PCIe,--,driver,dev,PCIE,static,pcie From: https://blog.csdn.net/MHD0815/article/details/143441643