美文网首页
Linux字符设备节点创建

Linux字符设备节点创建

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

    mknod命令创建节点(手动创建)

    #1. 节点创建命令的格式
    mknod NAME TYPE MAJOR MINOR
        #NAME:节点文件的名称
        #TYPE:节点文件的类型
        #    c    字符设备文件
        #    b    块设备文件
        #MAJOR    主设备号
        #MINOR    次设备号
    #2. 实例:创建一个字符设备文件节点:名称为(/dev/mychar0),主设备号为243,次设备为0
        mknod /dev/mychar0 c 243 0
    

    代码实现节点的创建(自动创建)

    /**
     * [device_create 创建设备节点文件,并注册到系统中(根文件系统)]
     * @param  class   [指向的该设备对应的结构体指针]
     * @param  parent  [默认可以使用NULL表示]
     * @param  devt    [是创建设备节点对应的设备号(包含主设备号和次设备号)]
     * @param  drvdata [给传递数据参数;默认使用NULL表示]
     * @param  fmt     [可变参数,表示设备节点的名称]
     * @return         [成功返回设备节点的指针;识别返回使用ERR_PTR()能够查看的错误信息]
     */
    struct device *device_create(struct class *class, struct device *parent,
         dev_t devt, void *drvdata, const char *fmt, ...);
    /*
    参数:
        class 指针对象的创建
        owner:使用THIS_MODULE
        name:对象名称
    返回值:
        成功返回struct class 结构体指针对象(设备节点对象);失败返回NULL
    */
    #define class_create(owner, name)
    

    错误处理函数

    static inline void * __must_check ERR_PTR(long error)    /* 将long类型数据转换为指针数据 */
    {
    return (void *) error;        
    }
    
    static inline long __must_check PTR_ERR(const void *ptr)    /* 将指针数据转换为long类型数据 */
    {
    return (long) ptr;
    }
    
    static inline long __must_check IS_ERR(const void *ptr)    /* 判断错误是否指向 */
    {
    return IS_ERR_VALUE((unsigned long)ptr);
    }
    

    释放

    /*
    设备节点的释放
    参数:
        cls表示节点对象指针;
        dev_t:设备节点号
    */
    extern void device_destroy(struct class *cls, dev_t devt);
    /*
    设备节点对象的释放
    cls指针指向的对象就是需要释放的对象
    */
    extern void class_destroy(struct class *cls);
    

    实例

    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    
    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;
    
    int my_char_driver_open(struct inode *inode_p, struct file *file_p)
    {
        printk(KERN_INFO "my char driver open success\n");
        return 0;
    }
    
    int my_char_driver_release(struct inode *inode_p, struct file *file_p)
    {
        printk(KERN_INFO "my char driver release success\n");
        return 0;
    }
    
    const struct file_operations fops =
    {
        .owner = THIS_MODULE,
        .open = my_char_driver_open,
        .release = my_char_driver_release
    };
    
    /* 定义模块加载入口函数 */
    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/dyuttdtx.html