// 千问写的:
// 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;
}