pcie驱动

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

posted on 2025-12-24 14:36  张博的博客  阅读(3)  评论(0)    收藏  举报

导航