pcil的模块研发demo

// PCIE 设备的读写demo 模块.
// 逐行分析代码的执行逻辑     使用这个提示词来让ai解释代码.

#include <linux/module.h>  
#include <linux/fs.h>
#include <linux/cdev.h>  //sudo apt install linux-source
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>

#define DEVICE_NAME "my_pcie_dev"
#define CLASS_NAME "pcie"

static int major_number;
static struct class* pcie_class = NULL;
static struct device* pcie_device = NULL;

// 设备私有数据结构
struct my_device_data {
    char buffer[256];
    int buffer_size;
};//最多存256的char,  buffer_size表示实际上存多少.










// 文件打开函数
static int my_open(struct inode *inode, struct file* file)
{
    struct my_device_data *dev_data;
    
    printk(KERN_INFO "PCIe device opened\n");
    
    // 分配设备私有数据
    dev_data = kmalloc(sizeof(struct my_device_data), GFP_KERNEL);//GFP_KERNEL 表示内存不够时候可以睡眠等待.
    if (!dev_data) {
        return -ENOMEM;
    }
    
    dev_data->buffer_size = 0;
    memset(dev_data->buffer, 0, sizeof(dev_data->buffer));
    
    // 将设备数据关联到文件结构
    file->private_data = dev_data;
    
    return 0;//返回0表示成功 
}

// 文件释放/关闭函数
static int my_release(struct inode *inode, struct file *file)
{
    struct my_device_data *dev_data = file->private_data;
    
    printk(KERN_INFO "PCIe device closed\n");
    
    // 释放设备私有数据
    if (dev_data) {
        kfree(dev_data);
        file->private_data = NULL;
    }
    
    return 0;
}

// 读操作函数 - 从设备读取数据到用户空间
static ssize_t my_read(struct file *file, char __user *user_buffer, 
                      size_t count, loff_t *offset)  // loff_t 后面t 表示底层是一个int类型.
{
    struct my_device_data *dev_data = file->private_data;
    int bytes_to_read;
    
    printk(KERN_INFO "Read operation requested, count=%zu, offset=%lld\n", 
           count, *offset); //内核打印命令  用dmeg查看.
    
    if (!dev_data) {
        return -ENODEV;
    }
    
    // 计算实际可读取的字节数
    if (*offset >= dev_data->buffer_size) {
        return 0; // 已到达缓冲区末尾, 读取的字符数是0.所以返回0表示读取的字符数.
    }
    
    bytes_to_read = dev_data->buffer_size - *offset;
    if (count < bytes_to_read) {
        bytes_to_read = count; 
    }
    
    // 将数据从内核空间复制到用户空间
    if (copy_to_user(user_buffer, 
                     dev_data->buffer + *offset, 
                     bytes_to_read)) {
        return -EFAULT; // 复制失败
    }
    
    *offset += bytes_to_read; // 更新偏移量
    printk(KERN_INFO "Successfully read %d bytes\n", bytes_to_read);
    
    return bytes_to_read;
}
// Linux 内核的标记;
    // __user: 指向用户空间的指针
    // __kernel: 指向内核空间的指针
    // __iomem: 指向 I/O 内存的指针


// 写操作函数 - 从用户空间写入数据到设备
static ssize_t my_write(struct file *file, const char __user *user_buffer, 
                       size_t count, loff_t *offset) // 所以user_buffer是一个指向用户空间的指针, 他的标记是用户空间.
{
    struct my_device_data *dev_data = file->private_data;
    int bytes_to_write;
    
    printk(KERN_INFO "Write operation requested, count=%zu\n", count);
    
    if (!dev_data) {
        return -ENODEV;
    }
    
    // 检查缓冲区边界, 计算可以写入的字节数
    if (*offset + count > sizeof(dev_data->buffer)) {
        bytes_to_write = sizeof(dev_data->buffer) - *offset;
        if (bytes_to_write <= 0) {
            printk(KERN_WARNING "Buffer full, cannot write more data\n");
            return -ENOSPC;
        }
    } else {
        bytes_to_write = count;
    }
    
    // 将数据从用户空间复制到内核空间   //复制函数一般是第一个变量是dst, 第二个是src,第三个是字节数. 因为目的地是未来要到的位置, 所以更重要, 所以写在第一个位置, 原始位置是老的位置不那么重要, 过时了马上, 所以写第二个位置. 
    if (copy_from_user(dev_data->buffer + *offset, 
                       user_buffer, 
                       bytes_to_write)) {
        return -EFAULT; // 复制失败
    }
    
    *offset += bytes_to_write;
    if (dev_data->buffer_size < *offset) {//如果offset过大,超过buffer最大值了.那么就用最大值定义offset即可.
        dev_data->buffer_size = *offset;
    }
    
    printk(KERN_INFO "Successfully wrote %d bytes\n", bytes_to_write);
    
    return bytes_to_write;
}

// ioctl 操作函数(可选)- 用于控制命令 //io control : 该函数通过传入设备相关请求码实现对硬件特性的控制
static long my_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct my_device_data *dev_data = file->private_data;
    
    printk(KERN_INFO "ioctl called: cmd=0x%x, arg=0x%lx\n", cmd, arg);
    
    switch (cmd) {
        case 1: // 清空缓冲区
            if (dev_data) {
                dev_data->buffer_size = 0;
                memset(dev_data->buffer, 0, sizeof(dev_data->buffer));
                printk(KERN_INFO "Buffer cleared\n");
            }
            break;
            
        case 2: // 获取缓冲区大小 //arg强制转化类型为用户空间的指针.//所以最后arg这个指针指向一个int, 他的值是buffer_size
            if (copy_to_user((void __user *)arg, 
                             &dev_data->buffer_size, 
                             sizeof(dev_data->buffer_size))) {
                return -EFAULT;
            }
            break;
            
        default:
            return -ENOTTY; // 不支持的命令
    }
    
    return 0;
}

// 定义文件操作结构体
static const struct file_operations fops = {
    .owner = THIS_MODULE,          // 指向当前模块
    .open = my_open,               // 打开文件时调用
    .release = my_release,         // 关闭文件时调用
    .read = my_read,               // 读取文件时调用
    .write = my_write,             // 写入文件时调用
    .unlocked_ioctl = my_ioctl,    // ioctl 操作时调用
    .llseek = no_llseek,           // 禁用 seek 操作(对于设备文件通常是这样)
};

// 模块初始化函数
static int __init pcie_module_init(void)
{
    // 注册字符设备,获取主设备号
    major_number = register_chrdev(0, DEVICE_NAME, &fops);
//传入 0 表示让内核自动分配一个可用的主设备号
//major_number :返回分配到的主设备号(如果参数中传入 0) 






    if (major_number < 0) {
        printk(KERN_ALERT "Failed to register device with error %d\n", major_number);
        return major_number;
    }
    
    printk(KERN_INFO "PCIe device registered with major number %d\n", major_number);
    
    // 创建设备类
    pcie_class = class_create(THIS_MODULE, CLASS_NAME); //在 /sys/class/pcie/ 目录下创建设备类目录
    if (IS_ERR(pcie_class)) {
        unregister_chrdev(major_number, DEVICE_NAME);
        printk(KERN_ALERT "Failed to create device class\n");
        return PTR_ERR(pcie_class);
    }
    
    // 创建设备节点
    pcie_device = device_create(pcie_class, NULL, 
                               MKDEV(major_number, 0), 
                               NULL, DEVICE_NAME); //device_create 函数自动在 /dev 目录创建设备文件
    if (IS_ERR(pcie_device)) {
        class_destroy(pcie_class);
        unregister_chrdev(major_number, DEVICE_NAME);
        printk(KERN_ALERT "Failed to create device\n");
        return PTR_ERR(pcie_device);
    }
    
    printk(KERN_INFO "PCIe device created successfully\n");
    return 0;
}

// 模块退出函数
static void __exit pcie_module_exit(void)
{
    device_destroy(pcie_class, MKDEV(major_number, 0));
    class_unregister(pcie_class);
    class_destroy(pcie_class);
    unregister_chrdev(major_number, DEVICE_NAME);
    printk(KERN_INFO "PCIe device driver unloaded\n");
}

module_init(pcie_module_init);
module_exit(pcie_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Qwen");
MODULE_DESCRIPTION("PCIe Device Driver with File Operations Example");
MODULE_VERSION("1.0");

posted on 2025-12-23 15:26  张博的博客  阅读(2)  评论(0)    收藏  举报

导航