美文网首页
Linux字符设备驱动的读写功能实现

Linux字符设备驱动的读写功能实现

作者: 二进制人类 | 来源:发表于2022-11-21 12:07 被阅读0次

    读写方法集的实现

    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); /* 用户调用read函数的回调函数 */
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); /* 用户调用write函数的回调函数 */ 
    

    读写函数

    /*
    功能:将用户空间from数据写入到内核空间to;
    参数:
        参数1:to指针,指向的是内核缓存空间的起始地址;
        参数2:from指针,指向的是用户缓存空间的起始地址;
        参数3:n表示拷贝数据的字节数; 
    返回值:
        成功返回拷贝的字节数;失败返回-EFAULT    
    */
    extern int copy_from_user(void *to, const void __user *from, int n);
    /*
    功能:将内核空间from数据写入到用户空间to中;
    参数:
        参数1:to指针,指向的用户缓存控件的起始地址;
        参数2:from指针,指向的是内核缓存空间的起始地址;
        参数3:n表示拷贝数据的字节数;
    返回值:
        成功返回拷贝的字节数;失败返回-EFAULT
    */
    extern int copy_to_user(void __user *to, const void *from, int n);
    

    实例

    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <asm/uaccess.h>
    
    #define KBUFSIZE 64
    
    static dev_t dev_num;/* dev_num存储申请成功的设备号 */
    static unsigned int count = 4;/* 次设备的数量 */
    static struct cdev *cdev_p;/* 字符设备注册结构体指针 */
    static struct class *class_p;
    static struct device *device_p;
    
    
    static unsigned char kernel_buf[KBUFSIZE];
    
    int my_char_driver_open(struct inode *inode_p, struct file *file_p)
    {
        dev_t my_dev;
    
        my_dev = inode_p->i_rdev;
        printk(KERN_INFO "my char driver open <%d:%d> success\n", MAJOR(my_dev), MINOR(my_dev));
    
        return 0;
    }
    
    int my_char_driver_release(struct inode *inode_p, struct file *file_p)
    {
        dev_t my_dev;
    
        my_dev = inode_p->i_rdev;
        printk(KERN_INFO "my char driver release <%d:%d> success\n", MAJOR(my_dev), MINOR(my_dev));
    
        return 0;
    }
    
    /* 提供给用户的read函数调用时,内核中的驱动调用read */
    ssize_t my_char_driver_read(struct file *file_p, char __user *buf, size_t size, loff_t *loff_p)
    {
        int ret;
    
        if (size < 0)
            size = 0;
    
        if (size > KBUFSIZE)
            size = KBUFSIZE;
    
        ret = copy_to_user(buf, kernel_buf, size);
        if (ret == -EFAULT)
            return ret;
    
        return size;
    }
    
    ssize_t  my_char_driver_write(struct file *file_p, const char __user *buf, size_t size, loff_t *loff_p)
    {
        int ret;
    
        if (size < 0)
            size = 0;
    
        if (size > KBUFSIZE)
            size = KBUFSIZE;
    
        ret = copy_from_user(kernel_buf, buf, size);
        if (ret == -EFAULT)
            return ret;
    
        printk(KERN_INFO "kernel_buf : %s\n", kernel_buf);
    
        return size;
    }
    
    const struct file_operations fops =
    {
        .owner = THIS_MODULE,
        .open = my_char_driver_open,
        .release = my_char_driver_release,
        .read = my_char_driver_read,
        .write = my_char_driver_write
    };
    
    /* 定义模块加载入口函数 */
    static int __init my_char_driver_init(void)
    {
        int ret = 0;
    
        /* 1. 申请设备号:手动申请(给定主设备号和起始次设备号)/动态申请(返回设备号) */
        ret = alloc_chrdev_region(&dev_num, 0, count, "chrdev");/* 动态申请设备号 */
        if (ret != 0)
        {
            goto err0;
        }
    
        /* 2. 注册设备到系统中,有系统统一调度管理 */
        /* 2.1 申请字符设备注册结构体空间*/
        cdev_p = cdev_alloc();
        if (cdev_p == NULL)
        {
            ret = - ENOMEM;
            goto err1;
        }
    
        /* 2.2 初始化字符数设备结构体空间中的操作方法集 */
        cdev_init(cdev_p, &fops);
    
        /* 2.3 添加字符设备结构体到系统中,由系统统一调度管理 */
        ret = cdev_add(cdev_p, dev_num, count);
        if (ret != 0)
        {
            goto err2;
        }
    
        /* 3. 创建设备节点(可选:一般都会加上) */
        /* 3.1 创建节点对象 */
        class_p =  class_create(THIS_MODULE, "mychar");
        if (IS_ERR(class_p))
        {
            ret = - ENOMEM;
            goto err3;
        }
    
        int i;
        for (i = 0; i < count; i++)  /* 由于驱动可以管理多个设备文件,需要循环创建设备文件 */
        {
            char drv_name[20] = {0};
            sprintf(drv_name, "mychar%d", i);
    
            /* 3.2 创建设备节点 */
            device_p = device_create(class_p, NULL, MKDEV(MAJOR(dev_num), i), NULL, drv_name);
            if (IS_ERR(device_p))
            {
                ret = - ENOMEM;
                goto err4;
            }
        }
    
        printk(KERN_INFO "my char driver : <major: %d, minor: %d> init success\n", MAJOR(dev_num), MINOR(dev_num));
        return 0;
    
    err4:
        /* 释放创建成功的设备节点 */
        for (i--; i >= 0; i--)
            device_destroy(class_p, MKDEV(MAJOR(dev_num), i));
    
        /* 释放设备节点对象 */
        class_destroy(class_p);
    err3:
    
    err2:
        /* 释放字符设备结构体空间 */
        cdev_del(cdev_p);
    err1:
        /* 释放设备号 */
        unregister_chrdev_region(dev_num, count);
    err0:
        return ret;
    }
    
    /* 定义模块卸载入口函数 */
    static void __exit my_char_driver_exit(void)
    {
        /* 释放设备节点 */
        int i;
        for (i = 0; i < count; i++)
            device_destroy(class_p, MKDEV(MAJOR(dev_num), i));
    
        /* 释放设备节点对象 */
        class_destroy(class_p);
    
        /* 释放字符设备结构体空间 */
        cdev_del(cdev_p);
    
        /* 释放设备号 */
        unregister_chrdev_region(dev_num, count);
        printk(KERN_INFO "my char driver exit success\n");
    }
    
    module_init(my_char_driver_init);/* 声明模块加载入口函数 */
    module_exit(my_char_driver_exit);/* 声明模块卸载入口函数 */
    MODULE_LICENSE("GPL");
    

    相关文章

      网友评论

          本文标题:Linux字符设备驱动的读写功能实现

          本文链接:https://www.haomeiwen.com/subject/bimttdtx.html