美文网首页
Linux字符设备注册

Linux字符设备注册

作者: 二进制人类 | 来源:发表于2022-11-03 09:28 被阅读0次

    结构体原型

    struct cdev { 
        struct kobject kobj;                  //kobject对象
        struct module *owner;                 //THIS_MODULE,类似于C++中的this指针
        const struct file_operations *ops;    //操作方法集
        struct list_head list; 
        dev_t dev;                            //设备号(U32 32bit无符号整型)
        unsigned int count;                   //设备计数
    };
    

    结构体空间开辟

    struct cdev *cdev_alloc(void)
    

    结构体空间初始化

    /*
    参数:
        cdev设备结构体指针,初始化设备结构体指针;
        fops:设备的操作方法集;
    PS:为了保证内核空间的安全性,所以需要使用初始化函数实现
    */
    void cdev_init(struct cdev *cdev, const struct file_operations *fops)
    

    操作方法集的结构体

    struct file_operations {
    /* 一般传THIS_MODULE */
    struct module *owner;                
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    int (*readdir) (struct file *, void *, filldir_t);
    unsigned int (*poll) (struct file *, struct poll_table_struct *);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, loff_t, loff_t, int datasync);
    int (*aio_fsync) (struct kiocb *, int datasync);
    int (*fasync) (int, struct file *, int);
    int (*lock) (struct file *, int, struct file_lock *);
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
    int (*check_flags)(int);
    int (*flock) (struct file *, int, struct file_lock *);
    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
    int (*setlease)(struct file *, long, struct file_lock **);
    long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len);
    };
    

    机构体注册

    /**
     * [cdev_add 将字符设备结构体添加到内核系统的驱动管理单元(链表)中,由内核进行统一调度]
     * @param  p     [指向的字符设备注册结构体]
     * @param  dev   [字符设备号]
     * @param  count [字符设备个数]
     * @return       [ 成功返回0,失败返回错误号]
     */
    int cdev_add(struct cdev *p, dev_t dev, unsigned count)
    

    字符设备移除

    /*将字符设备结构体从内核中移除,并释放结构体空间*/
    void cdev_del(struct cdev *p)
    

    实例

    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/types.h>
    #include <linux/kdev_t.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    
    MODULE_LICENSE("GPL");
    
    
    /* 定义字符设备的操作方法集 */
    int my_open(struct inode *inodep, struct file *filep)
    {
        printk(KERN_INFO "%s\n", __func__);
    
        return 0;
    }
    
    int my_release(struct inode *inodep, struct file *filep)
    {
        printk(KERN_INFO "%s\n", __func__);
    
        return 0;
    }
    
    static dev_t dev;/* 定义设备号变量 */
    static unsigned int count = 1;
    
    /* 定义字符设备结构体指针 */
    struct cdev *cdevp;
    /* 定义字符设备的操作方法集结构体 */
    struct file_operations fops =
    {
        .owner = THIS_MODULE,
        .open = my_open,
        .release = my_release
    };
    
    
    
    /* 定义模块加载函数 */
    static int __init my_module_init(void)
    {
        int ret;
    
        /* 申请设备编号:采用动态申请方式 */
        ret = alloc_chrdev_region(&dev, 0, count, "mychar");
        if (ret != 0)
        {
            printk(KERN_ERR "register_chrdev_region: ret = %d\n", ret);
            return ret;
        }
        printk(KERN_INFO "register_chrdev_region major:%d,minor:%d\n", MAJOR(dev), MINOR(dev));
    
        /* 设备注册:将设备添加到内核驱动管理链表中,然后由系统进行统一调度 */
        /* 开辟字符设备结构体空间 */
        cdevp = cdev_alloc();
        if (cdevp == NULL)
        {
            ret = -ENOMEM;
            goto mychar_err0;
        }
    
        /* 初始化结构体成员 */
        cdev_init(cdevp, &fops);
    
        /* 将字符设备结构体cdev添加到system中,由内核统一调度 */
        ret = cdev_add(cdevp, dev, count);
        if (ret < 0)
        {
            goto mychar_err1;
        }
    
        printk(KERN_INFO "cdev register success\n");
    
        return 0;
    
    mychar_err1:
        /* 释放字符设备结构体 */
        cdev_del(cdevp);
    mychar_err0:
        /* 释放设备号 */
        unregister_chrdev_region(dev, count);
        return ret;
    }
    
    /* 定义模块卸载函数 */
    static void __exit my_module_exit(void)
    {
        /* 释放字符设备结构体 */
        cdev_del(cdevp);
    
        /* 释放设备号 */
        unregister_chrdev_region(dev, count);
    }
    
    /* 声明模块的加载入口 */
    module_init(my_module_init);
    /* 声明模块的卸载入口 */
    module_exit(my_module_exit);
    

    相关文章

      网友评论

          本文标题:Linux字符设备注册

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