// 千问写的: // 1. 基本结构:PCIe 驱动注册 #include <linux/module.h> #include <linux/pci.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/uaccess.h> #define DEVICE_NAME "my_pcie_device" // 自定义设备私有数据 struct my_pcie_dev { struct pci_dev *pdev; void __iomem *bar0; // 映射的 BAR 空间 //__iomem 表示io内存地址空间, 用来区分, 区分普通内存地址空间和 I/O 内存地址空间 struct cdev cdev; }; static dev_t dev_num; static struct class *my_class; // 设备 ID 表(根据你的设备 Vendor ID 和 Device ID 修改) static const struct pci_device_id my_pcie_id_table[] = { { PCI_DEVICE(0x1234, 0x5678) }, // 替换为你的 VendorID 和 DeviceID { 0, } }; MODULE_DEVICE_TABLE(pci, my_pcie_id_table); // Probe 函数:设备被发现时调用 static int my_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct my_pcie_dev *my_dev; int ret; printk(KERN_INFO "PCIe device probed!\n"); // 启用设备 ret = pci_enable_device(pdev); //启用设备. if (ret) { printk(KERN_ERR "Cannot enable PCI device\n"); return ret; } // 请求 BAR 资源(假设使用 BAR0) if (!request_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0), DEVICE_NAME)) { printk(KERN_ERR "Cannot request memory region\n"); goto err_disable; } // 映射 BAR 到内核虚拟地址 my_dev = kzalloc(sizeof(*my_dev), GFP_KERNEL);// kzalloc申请一个数据长度的内存. // kzalloc 返回 void * 类型 // my_dev 是 struct my_pcie_dev * 类型 // C语言自动完成类型转换,无需显式转换 if (!my_dev) { ret = -ENOMEM; goto err_release; } my_dev->pdev = pdev; my_dev->bar0 = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); // ioremap: 输入物理设备地址(设备寄存器或者设备内存), 输出普通内存地址(malloc获得). 这两个地址做映射. if (!my_dev->bar0) { printk(KERN_ERR "Cannot map BAR0\n"); ret = -ENOMEM; goto err_free; } // 可选:设置 DMA(如果设备支持) // pci_set_master(pdev); // 保存驱动私有数据 pci_set_drvdata(pdev, my_dev); printk(KERN_INFO "PCIe device initialized successfully.\n"); return 0; err_free: kfree(my_dev); err_release: release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); err_disable: pci_disable_device(pdev); return ret; } // Remove 函数:设备被移除或驱动卸载时调用 static void my_pcie_remove(struct pci_dev *pdev) { struct my_pcie_dev *my_dev = pci_get_drvdata(pdev);//获取设备私有数据 if (my_dev) { if (my_dev->bar0) iounmap(my_dev->bar0);//解除内存映射 release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); kfree(my_dev); } pci_disable_device(pdev); printk(KERN_INFO "PCIe device removed.\n"); } // PCI 驱动结构体 static struct pci_driver my_pcie_driver = { .name = DEVICE_NAME, .id_table = my_pcie_id_table, .probe = my_pcie_probe, .remove = my_pcie_remove, }; // 模块初始化和退出 /** * my_pcie_init - PCIe驱动程序初始化函数 * * 该函数在模块加载时被调用,负责注册PCIe驱动程序。 * 通过pci_register_driver函数将驱动程序注册到内核PCI子系统中。 * * 返回值: * 0 - 驱动程序成功注册 * 非0 - 驱动程序注册失败,返回错误码 */ static int __init my_pcie_init(void) { int ret; ret = pci_register_driver(&my_pcie_driver); if (ret) { printk(KERN_ERR "Failed to register PCIe driver\n"); return ret; } printk(KERN_INFO "PCIe driver loaded.\n"); return 0; } static void __exit my_pcie_exit(void) { int ret; ret = pci_unregister_driver(&my_pcie_driver); if (ret) { printk(KERN_ERR "Failed to unregister PCIe driver: %d\n", ret); } else { printk(KERN_INFO "PCIe driver unloaded successfully.\n"); } } module_init(my_pcie_init); module_exit(my_pcie_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("A simple PCIe device driver");
// 下面是一个 C 语言示例程序,用于读取指定 PCIe 设备的 Max Payload Size (MPS) 和 Max Read Request Size (MRRS),这两个参数决定了设备能接收的最大 TLP(Transaction Layer Packet)数据大小。 // PCIe 接收缓冲区大小(Receive Buffer Size) // 最大有效载荷大小(Max Payload Size, MPS) // 最大读请求大小(Max Read Request Size, MRRS) 一个请求最大能让返回方返回多少. 可以返回很大,然后返回方每次返回一小块. 每次一小块的大小的上限就是MPS 所有现代系统通常支持至少 512B,高性能设备(如 NVMe SSD、GPU)常设为 4096B 以提升吞吐。 // 设备支持的 BAR(Base Address Register)大小 // DMA 传输的数据大小限制 #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <stdint.h> #include <string.h> // 解析 BDF 格式: domain:bus:device.function,例如 0000:01:00.0 int read_pcie_max_sizes(const char* bdf) { char path[256]; /// char里面的数据初始化的时候是乱的, 是内存里面的数据, 不会自动初始化为全\0. snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s/config", bdf); //字符串格式化 int fd = open(path, O_RDONLY); if (fd < 0) { perror("open config"); // // void perror(const char *str); // 当系统调用失败时,perror 会输出格式:str: 错误描述 return -1; } // PCIe 设备能力结构位于配置空间偏移 0x34(Capability Pointer) uint8_t cap_ptr; if (pread(fd, &cap_ptr, 1, 0x34) != 1) {//fd 偏移量34读取一个字节到&cap_ptr位置. perror("read capability pointer"); close(fd); return -1; } // 遍历能力列表,查找 PCIe Capability (ID = 0x10) while (cap_ptr != 0) { uint16_t cap_header; if (pread(fd, &cap_header, 2, cap_ptr) != 2) break; uint8_t cap_id = cap_header & 0xFF; uint8_t next_ptr = (cap_header >> 8) & 0xFF; if (cap_id == 0x10) { // PCIe Capability uint16_t dev_cap; if (pread(fd, &dev_cap, 2, cap_ptr + 4) == 2) { // Device Capabilities Register bits [2:0] = Max_Payload_Size Supported int max_payload_supported = dev_cap & 0x7; const char* payload_str[] = {"128", "256", "512", "1024", "2048", "4096"}; printf("Max Payload Size Supported: %s bytes\n", max_payload_supported < 6 ? payload_str[max_payload_supported] : "Reserved"); uint16_t dev_ctrl; if (pread(fd, &dev_ctrl, 2, cap_ptr + 8) == 2) { // Device Control Register bits [7:5] = Max_Payload_Size int mps = (dev_ctrl >> 5) & 0x7; printf("Current Max Payload Size (MPS): %s bytes\n", mps < 6 ? payload_str[mps] : "Reserved"); // Device Control Register bits [14:12] = Max_Read_Request_Size int mrrs = (dev_ctrl >> 12) & 0x7; printf("Max Read Request Size (MRRS): %s bytes\n", mrrs < 6 ? payload_str[mrrs] : "Reserved"); } } close(fd); return 0; } cap_ptr = next_ptr; } close(fd); fprintf(stderr, "PCIe capability not found.\n"); return -1; } int main(int argc, char *argv[]) { // if (argc != 2) { // fprintf(stderr, "Usage: %s <BDF> e.g., 0000:01:00.0\n", argv[0]); // return 1; // } argv[1]="245f:00:00.0"; argv[1]="c710:00:00.0"; return read_pcie_max_sizes(argv[1]) ? EXIT_FAILURE : EXIT_SUCCESS; }