• Linux设备文件自动生成


    第一种是使用mknod手工创建:# mknod <devfilename> <devtype> <major> <minor>

    第二种是自动创建设备节点:利用udev(mdev)来实现设备文件的自动创建,首先应保证支持udev(mdev),由busybox配置。

          具体udev相关知识这里不详细阐述,可以移步Linux 文件系统与设备文件系统 —— udev 设备文件系统,这里主要讲使用方法。     

        在驱动用加入对udev 的支持主要做的就是:在驱动初始化的代码里调用class_create(...)为该设备创建一个class,再为每个设备调用device_create(...)创建对应的设备

        内核中定义的struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用 device_create(…)函数来在/dev目录下创建相应的设备节点。

         这样,加载模块的时候,用户空间中的udev会自动响应 device_create()函数,去/sysfs下寻找对应的类从而创建设备节点。 

    下面是两个函数的解析:

    支持字符设备文件自动生成
    linux/device.h

    struct class *class_create(struct module *owner, const char *name);
    /*
      功能:在/sys/class目录下创建一个目录,目录名是name指定的
      参数:
        struct module *owner - THIS_MODULE
        const char *name - 设备名
      返回值:
        成功:class指针
        失败: - bool IS_ERR(const void *ptr)  判断是否出错
             long PTR_ERR(const void *ptr)  转换错误码
    */
    void class_destroy(struct class *cls);
    /*
      功能:删除class指针指向的目录
      参数:
        struct class *cls - class指针
    */
    struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);
    /* 
      功能:
        在class指针指向的目录下再创建一个目录,目录名由const char *fmt, ...指出、并导出设备信息(dev_t)
      参数:
        struct class *cls - class指针
        struct device *parent - 父对象,NULL
        dev_t devt - 设备号
        void *drvdata - 驱动私有数据
        const char *fmt, ... - fmt是目录名字符串格式,...就是不定参数
      返回值:
        成功 - device指针
        失败 - bool IS_ERR(const void *ptr)  判断是否出错
           long PTR_ERR(const void *ptr)   转换错误码
    */
    void device_destroy(struct class *cls, dev_t devt);
    /*
      功能:删除device_create创建的目录
      参数:
        struct class *cls - class指针
        dev_t devt - 设备号
    */

    创建次序
    struct class *class_create(struct module *owner, const char *name);
    struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);
    删除次序
    void device_destroy(struct class *cls, dev_t devt);
    void class_destroy(struct class *cls);

    实例:

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/cdev.h>
    #include <linux/fs.h>
    #include <linux/errno.h>
    #include <asm/current.h>
    #include <linux/sched.h>
    #include <linux/device.h>
    
    MODULE_LICENSE("GPL");
    
    static struct class *cls = NULL;
    
    static int major = 0;
    static int minor = 0;
    const  int count = 6;
    
    #define DEVNAME    "demo"
    
    static struct cdev *demop = NULL;
    
    //打开设备
    static int demo_open(struct inode *inode, struct file *filp)
    {
        //get command and pid
        printk(KERN_INFO "%s : %s : %d
    ", __FILE__, __func__, __LINE__);return 0;
    }
    
    //关闭设备
    static int demo_release(struct inode *inode, struct file *filp)
    {
        //get major and minor from inode
        printk(KERN_INFO "%s : %s : %d
    ", __FILE__, __func__, __LINE__);
        return 0;
    }
    
    static struct file_operations fops = {
        .owner    = THIS_MODULE,
        .open    = demo_open,
        .release= demo_release,
    };
    
    static int __init demo_init(void)
    {
        dev_t devnum;
        int ret, i;
        struct device *devp = NULL;
    
        //1. alloc cdev obj
        demop = cdev_alloc();
        if(NULL == demop){
            return -ENOMEM;
        }
        //2. init cdev obj
        cdev_init(demop, &fops);
    
        ret = alloc_chrdev_region(&devnum, minor, count, DEVNAME);
        if(ret){
            goto ERR_STEP;
        }
        major = MAJOR(devnum);
    
        //3. register cdev obj
        ret = cdev_add(demop, devnum, count);
        if(ret){
            goto ERR_STEP1;
        }
        cls = class_create(THIS_MODULE, DEVNAME);
        if(IS_ERR(cls)){
            ret = PTR_ERR(cls);
            goto ERR_STEP1;
        }
        for(i = minor; i < (count+minor); i++){
            devp = device_create(cls, NULL, MKDEV(major, i), NULL, "%s%d", DEVNAME, i);
            if(IS_ERR(devp)){
                ret = PTR_ERR(devp);
                goto ERR_STEP2;
            }
        }
        return 0;
    
    ERR_STEP2:
        for(--i; i >= minor; i--){
            device_destroy(cls, MKDEV(major, i));
        }
        class_destroy(cls);
    
    ERR_STEP1:
        unregister_chrdev_region(devnum, count);
    
    ERR_STEP:
        cdev_del(demop);
    
        //get command and pid
        printk(KERN_INFO "%s : %s : %d - fail.
    ", __FILE__, __func__, __LINE__);
        return ret;
    }
    
    static void __exit demo_exit(void)
    {
        int i;
        //get command and pid
        printk(KERN_INFO "%s : %s : %d - leave.
    ", __FILE__, __func__, __LINE__);
    
        for(i=minor; i < (count+minor); i++){
            device_destroy(cls, MKDEV(major, i));
        }
        class_destroy(cls);
    
        unregister_chrdev_region(MKDEV(major, minor), count);
    
        cdev_del(demop);
    }
    
    module_init(demo_init);
    module_exit(demo_exit);

    下面可以看几个class几个名字的对应关系:

  • 相关阅读:
    C# 动态创建SQL数据库(一)
    在Winform中菜单动态添加“最近使用文件”
    转(C# 类似右键菜单弹出窗体)
    为什么不能用Abort退出线程
    C# GDI绘制波形图
    转(C# 实现生产者消费者队列)
    为字段设置初始值
    闲话资源管理
    正确使用 new 修饰符
    减少装箱与拆箱
  • 原文地址:https://www.cnblogs.com/chen-farsight/p/6154941.html
Copyright © 2020-2023  润新知