Linux device driver (4)--create device number and device node

Reference blog:

Character device driver - use alloc_chrdev_region+cdev to register device driver

Linux driver device_create creates character device files - Programmer Sought

1. Allocate and release device numbers

When we wrote the character drivers of chrdevbase and led, we needed to manually create device nodes through mknod. And there are many problems as follows:

1) We need to determine in advance which major device numbers are not used

2) All the minor device numbers under a major device number will be used. For example, if the major device number of LED is now set to 200, then all the minor device numbers in the range of 0~2^20-1 will be divided into one LED device. gone. This is a waste of secondary device numbers. An LED device must have only one major device number and one minor device number.

Now the new character device driver no longer uses the register_chrdev and unregister_chrdev functions, but uses the new character device driver recommended by the linux kernel to create API functions.

2. Application for equipment number

The solution to the two problems in 1 is that we apply to the linux kernel when we use the device number, apply for as many as we need, and the linux kernel allocates the device number that can be used by the device. If you do not specify the device number, you can Use the following function to apply for a device number:

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,                                                                                                                                     
                        const char *name);
 
参数说明:
dev :alloc_chrdev_region函数向内核申请下来的设备号
baseminor :次设备号的起始
count: 申请次设备号的个数
name :执行 cat /proc/devices显示的名称                     

If the major device number and minor device number of the device are given, use the following function to register the device number:

int register_chrdev_region(dev_t from, unsigned count, const char *name)

参数说明:
from:申请的起始设备号,也就是给定的设备号
count:申请的数量,一般为1
name:名字

一般是给定主设备号,然后使用MKDEV构建完整的dev_t,一般次设备号选择0

Use the unregister_chrdev_region function when uninstalling. The function prototype is as follows:

void unregister_chrdev_region(dev_t from, unsigned count);

参数说明:
from:设备号
count:设备号个数

The sample code is as follows:

int major;
int minor;
dev_t devid;

if (major) {
    devid = MKDEV(major, 0);                    // 如果major有效的话就使用MKDEV来构建设备号,次设备选择0
    register_chrdev_region(devid, 1, "test");    // 注册设备号
} else {
    alloc_chrdev_region(&dev, 0, 1, "test");    // 申请设备号
    major = MAJOR(devid);                        // 获取分配设备号的主设备号
    minor = MINOR(devid);                        // 获取分配设备号的次设备号
}

3. cdev structure and related functions

The cdev structure binds the set of device operation functions and the device number.

cdev represents a character device, and the cdev structure is as follows:

struct cdev {
        struct kobject kobj;
        struct module *owner;
        const struct file_operations *ops;    // 设备操作函数集合
        struct list_head list;
        dev_t dev;                // 设备号
        unsigned int count;
} __randomize_layout;

cdev_init function

void cdev_init(struct cdev *, const struct file_operations *);

参数说明
第一个参数是cdev的结构体变量,第二个参数就是fops的字符设备文件操作函数集合

After the cdev structure is defined, it needs to be initialized with the cdev_init function. Namely: bind the character device and the set of operation functions of the character device .

After cdev_init initializes cdev, use cdev_add to add to the linux kernel

int cdev_add(struct cdev *, dev_t, unsigned);

参数说明:
第一个参数是cdev的指针,第二个参数是设备号,第三个参数是要添加的设备个数

cdev_del function: When uninstalling the driver, you must use the cdev_del function to delete the corresponding character device from the linux kernel. The function prototype is as follows:

cdev_del(&testdev);

The combined functions of cdev_del and unregister_chrdev_region are equivalent to the unregister_chrdev function.

4. Automatically create device nodes

When using busybox to build the root file system, busybox will create a simplified version of udev -- mdev (the functions of the two are the same) , so we use mdev to realize the automatic creation and deletion of device node files in embedded linux. The hot plug event in the linux system is also managed by mdev.

Its main function is to manage device nodes under the /dev directory. It is also used to replace devfs and hotplug functionality, which means it handles /dev directory and all userspace behavior when adding/removing hardware.

After the 2.6 kernel, the udev mechanism was introduced to replace devfs. The udev mechanism provides hot-plug management, and can automatically create /dev/xxx device files when loading the driver.

The following analyzes if the automatic creation and deletion of file nodes is realized through mdev:

1. Create and delete device classes

The class to which the devices in the system belong is described by the structure struct class object, which is used to represent a certain type of device ( abstracted into a collection of devices ), which is an abstract body of a group of devices with common attributes and functions, similar to oriented the concept of a class in an object;

The work of automatically creating device nodes is completed in the entry function of the driver. Generally, the code related to automatically creating device nodes is added after the cdev_add function. First, create a class class, class is a structure, defined in include/linux/device.h, class_create is a class creation function, class_create is a macro definition, the content is as follows:

#define class_create(owner, name)               \
({                                              \
        static struct lock_class_key __key;     \
        __class_create(owner, name, &__key);    \                                                                                                                                                           
})

struct class *__class_create(struct module *owner, const char *name,
                             struct lock_class_key *key)

宏class_create展开以后内容如下:
struct class *__class_create(struct module *owner, const char *name)   

参数说明:
owner:THIS_MODULE
name:类名字

返回值:
指向结构体class的指针,也就是创建的类                          

Delete the function class_destroy of the class, the function prototype is as follows:

void class_destroy(struct class *cls);

参数说明:
cls就是要删除的类
struct class
{
     const char*                    name;           //类名
     struct module*                 owner;         //所属模块
     struct subsystem              subsys;        //所属子系统subsystem
     struct list_head              children;      //属于该class类型的所有子类组成的链表;
     struct list_head              devices;       //属于该class类型的所有设备class_device组成的链表;
     struct list_head              interfaces;    //类接口class_interface链表
     struct semaphore              sem;             /* locks both the children and interfaces lists */
     struct class_attribute*        class_attrs;     //类属性
     struct class_device_attribute* class_dev_attrs; //类设备属性
     int (*uevent)(struct class_device* dev, char** envp, int num_envp, char* buffer, int buffer_size);
     void (*release)(struct class_device* dev);
     void (*class_release)(struct class* class);
};
int class_register(struct class *cls):类注册;
void class_unregister(struct class *cls):类注销;

2. Create and delete devices

After the device class is created, nodes cannot be automatically created. We also need to create a device under this class. Use the device_create function to create a device under the class. The prototype of the device_create function is:

struct device *device_create(struct class *class, struct device *parent,
                             dev_t devt, void *drvdata, const char *fmt, ...) 
{
        va_list vargs;
        struct device *dev;                                                                                                                                                                                 

        va_start(vargs, fmt);
        dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
        va_end(vargs);
        return dev; 
}

参数说明:
class:在哪一个设备类下创建,该设备依附的类
parent:父设备,没有即为NULL,一般为NULL
devt:设备号
drvdata:可能会使用的一些数据,一般为NULL
fmt:设备名字,fmt = xxx,则会生成/dev/xxx这个设备文件

返回值:
创建好的设备

device_create can automatically create device files is dependent on the udev application. udev is a tool that can dynamically update device files according to the status of hardware devices in the system, including the creation and deletion of device files. Device files are usually placed in the /dev directory. After using udev, only the devices that actually exist in the system are included in the /dev directory.

void device_destroy(struct class *class, dev_t devt)

参数说明:
class:设备依附的设备类
devt:设备号

5. Test demo

【Test only】

#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched/signal.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/parport.h>
#include <linux/ctype.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/major.h>
#include <linux/ppdev.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/compat.h>

#define NEWCHRDEVBASE_NAME "newchrdevbase"

struct newchrdevbase_dev {
        struct cdev cdev;        // 字符设备
        dev_t devid;    // 设备号
        struct class *class;        // 类
        struct device *device;        // 设备
        int major;        // 主设备号
        int minor;        // 次设备号
};

static struct newchrdevbase_dev newchrdevbase;

static int newchrdevbase_open(struct inode *inode, struct file *file)
{
        printk("newchrdevbase open \n");        
}

static int chrdevbase_release(struct inode *inode, struct file *file)
{
        printk("newchrdevbase close \n");
}

static struct file_operations newchrdevbase_fops = {
        .owner = THIS_MODULE;
        .open = newchrdevbase_open,
        .release = newchrdevbase_release,
};

static int __init newchrdevbase_init(void)
{
        int ret = 0;
        
        printk("newchrdevbase__init \n");
        if (newchrdevbase.major) {        // 给定主设备号
                newchrdevbase.devid = MKDEV(newchrdevbase.major, 0);
                ret = register_chrdev_region(newchrdevbase.devid, 1, NEWCHRDEVBASE_NAME);
        } else {                        // 没有给定主设备号
                ret = alloc_chrdev_region(&newchrdevbase.devid, 0, 1, NEWCHRDEVBASE_NAME);
                newchrdevbase.major = MAJOR(newchrdevbase.devid);
                newchrdevbase.minor = MINOR(newchrdevbase.devid);
        }

        if (ret < 0) {
                printk("newchrdevbase register failed \n");
                return -1;
        }

        newchrdevbase.cdev.owner = THIS_MODULE;

        cdev_init(&newchrdevbase.cdev , &newchrdevbase_fops);
        cdev_add(&newchrdevbase.cdev , newchrdevbase.devid, 1);
        
        newchrdevbase.class = class_create(THIS_MODULE, "newchrdevbase");
        if(IS_ERR(newchrdevbase.class)) {        // IS_ERR判断指针是否合法,合法返回0
                return PTR_ERR(newchrdevbase.class);        // PTR_ERR() 将一个错误指针强转为一个错误码,并作为函数的返回值使用, ERR_PTR() 将一个错误码强转为一个错误指针,并作为函数的返回值使用
        }

        newchrdevbase.device = device_create(newchrdevbase.class, NULL, newchrdevbase.devid, NULL, "newchrdevbase");
        if (IS_ERR(newchrdevbase.device)) {
                return PTR_ERR(newchrdevbase.device);
        }

        return 0;
}

static void __exit newchrdevbase_exit(void)
{
        // 删除字符设备
        cdev_del(&newchrdevbase.cdev);

        // 注销设备号
        unregister_chrdev_region(newchrdevbase.devid, 1);

        // 删除设备
        device_destroy(newchrdevbase.class, newchrdevbase.devid);

        // 删除设备类
        class_destroy(newchrdevbase.class);

        printk("newchrdevbase__exit end\n");
}

module_init(chrdevbase__init);
module_exit(chrdevbase__exit);

After the above test demo module is loaded into the kernel, we don't need to manually set the /dev/xxx device node through the mknod command.

and

You can search for newchrdevbase in the /sys/class directory, that is, /sys/class/newchrdevbase .

You can search for newchrdevbase in the /dev directory, that is, /dev/newchrdevbase .

おすすめ

転載: blog.csdn.net/qq_58550520/article/details/129169235