Linux new character device driver

Posted by phpwizard01 on Wed, 01 Dec 2021 03:59:08 +0100

1, Assign and release equipment numbers

Using register_ The chrdev function only needs to give a master device number when registering a character device, but this will cause two problems:

  • ① . it is necessary to determine which main equipment numbers are not used in advance.
  • ② . all secondary equipment numbers under a primary equipment number will be used. For example, if the primary equipment number of LED is set to 200, then all secondary equipment numbers in the range 0 ~ 1048575 (2 ^ 20-1) will be separated by one LED equipment. This is a waste of equipment number! An LED device must have only one primary device number and one secondary device number.

The best way to solve these two problems is to apply to the Linux kernel when you want to use the device number. You can apply for as many as you need, and the Linux kernel allocates the device number that the device can use. If the equipment number is not specified, the following function is used to apply for the equipment number:

int alloc_chrdev_region(dev_t*dev,unsigned baseminor,unsigned count,const char*name)

If the primary device number and secondary 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)

The parameter from is the starting equipment number to be applied for, that is, the given equipment number; The parameter count is the quantity to be applied, which is generally one; Parameter name is the device name.
After unregistering the character device, release the device number, whether through alloc_chrdev_region function or register_ chrdev_ The device number applied by the region function uniformly uses the following release functions:

void unregister_chrdev_region(dev_t from,unsigned count)

Under the new character device driver, the example code of device number allocation is as follows:

int major;/*Main equipment No*/
int minor;/*Secondary equipment No*/
dev_t devid;/*Equipment number*/
if (major) {/*Defines the master equipment number*/
devid=MKDEV(major,0);/*0 is selected for most drive secondary equipment numbers*/
register_chrdev_region(devid,1,"test");
} else {/*No device number defined*/
alloc_chrdev_region(&devid,0,1,"test");/*Application equipment No*/
major = MAJOR(devid);/*Gets the master device number of the assigned number*/
minor=MINOR(devid);/*Get the secondary device number of the assigned number*/
  • Lines 1 to 3 define the primary / secondary device number variables major and minor, and the device number variable devid.
  • In line 5, judge whether the main device number major is valid. If the main device number is generally given in the linux driver, it means that the device number of the device has been determined, because the secondary device numbers basically choose 0, which is a conventional provision in the development of Linux driver.
  • In line 6, if major is valid, use MKDEV to construct the equipment number, and select 0 for the secondary equipment number.
  • Line 7, use register_chrdev_region function to register the device number.
  • In lines 9 to 11, if major is invalid, it means that there is no given device number. In this case, alloc is used_ chrdev_ Region function to apply for the device number. After the equipment number application is successful, major and MINOR are used to extract the main equipment number and secondary equipment number.

To log off the equipment number, use the following code:

unregister_chrdev_region(devid, 1);/*Logout equipment number*/

2, New character device registration method

1. Character device structure
In Linux, cdev structure is used to represent a character device. The definition of cdev structure in include/linux/cdev.h file 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;
}

There are two important member variables in cdev: ops and dev, which are the character device file operation function set file_operations and device number dev_t. Before writing a character device driver, you need to define a cdev structure variable, which represents a character device, as shown below:

struct cdev test_cdev;

2,cdev_init function
After you define the cdev variable, you use cdev_init function initializes it, cdev_ The init function prototype is as follows:

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

The parameter cdev is the cdev structure variable to be initialized, and the parameter fops is the set of character device file operation functions. Using cdev_ The example code of init function initializing cdev variable is as follows:

struct cdev testcdev;
/*Device operation function*/
static struct file_operations test_fops = {
.owner=THIS_MODULE,
/*Other specific initial items*/
};
testcdev.owner=THIS_MODULE;
cdev_init(&testcdev,&test_fops);/*Initialize cdev structure variable*/

3,cdev_add function
cdev_ The add function is used to add a character device (CDEV structure variable) to the Linux system. First, use CDEV_ The init function initializes the CDEV structure variable, and then uses CDEV_ The add function adds this character device to the Linux system. cdev_ The prototype of the add function is as follows:

int cdev_add(struct cdev *p,dev_t dev,unsigned count)

Parameter p points to the character device to be added (cdev structure variable). Parameter dev is the device number used by the device, and parameter count is the number of devices to be added. The complete example code is as follows. Add the cdev add function, as shown below:

struct cdev testcdev;
/*Device operation function*/
static struct file_operations test_fops = {
.owner=THIS_MODULE,
/*Other specific initial items*/
};
testcdev.owner = THIS_MODULE;
cdev_init(&testcdev,&test_fops);/*Initialize cdev structure variable*/
cdev_add(&testcdev,devid,1);/*Add character device*/

4,cdev_del function
Be sure to use CDEV when uninstalling the driver_ Del function deletes the corresponding character device from the Linux kernel, CDEV_ The prototype of del function is as follows:

void cdev_del(struct cdev *p)

Parameter p is the character device to be deleted. If you want to delete a character device, refer to the following code:

cdev_del(&testcdev);/*Delete cdev*/

cdev_del and unregister_chrdev_ The combined function of the two functions of region is equivalent to unregister_chrdev function.

3, Automatically create device nodes

1. mdev mechanism
Udev is a user program. Under Linux, udev can create and delete device files. Udev can detect the state of hardware devices in the system and create or delete device files according to the state of hardware devices in the system. For example, after successfully loading the driver module with the modprobe command, the corresponding device node file will be automatically created in the / dev directory. After unloading the driver module with the rmmod command, the device node file in the / dev directory will be deleted. When using busybox to build the root file system, busybox will create a simplified version of udev, mdev. Therefore, mdev is used in embedded Linux to automatically create and delete device node files. The hot plug events in the Linux system are also managed by mdev. In the / etc/init.d/rcS file, the following statement:

echo/sbin/mdev>/proc/sys/kernel/hotplug

2. Create and delete classes
The work of automatically creating device nodes is completed in the entry function of the driver, generally in cdev_add the code related to automatically creating the device node after the add function. First, create a class class. Class is a structure defined in the file include/linux/device.h. class_create is a class creation function, class_create is a macro definition, 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)

According to the above code, the macro class_ After expanding create, the contents are as follows:

struct class *class_create(struct module*owner,const char*name)

class_create has two parameters. The parameter owner is usually THIS_MODULE, parameter name is the class name. The return value is a pointer to the structure class, that is, the created class.
When unloading the driver, you need to delete the class. The class deletion function is class_destroy, the prototype function is as follows:

void class_destroy(struct class*cls);

The parameter cls is the class to be deleted.

3. Create device
Using device_ The create function creates a device under the class, device_ The prototype of the create function is as follows:

struct device *device_create(struct class *class,
struct device *parent,
dev_t devt,
void *drvdata,
const char *fmt,...)

device_create is a variable parameter function.

  • The parameter class is the class to be created by the device;
  • The parameter parent is the parent device, which is generally NULL, that is, there is no parent device;
  • The parameter devt is the device number;
  • The parameter drvdata is some data that may be used by the device, which is generally NULL;
  • The parameter fmt is the device name. If fmt=xxx is set, the device file / dev/xxx will be generated.
  • The return value is the created device.

Similarly, when unloading the driver, you need to delete the created device, and the device deletion function is device_destroy, the prototype function is as follows:

void device_destroy(struct class*class,dev_t devt)

The parameter class is the class of the device to be deleted, and the parameter devt is the device number to be deleted.

Reference template:

struct class *class;/*class*/
struct device *device; /*equipment*/
dev_t devid;/*Equipment number*/

/*Drive entry function*/
static int _init led_init(void)
{
/*Create class*/
	class = class _ create ( THIS _ MODULE , " xxx " ) ;
/*Create device*/
	device = device _ create ( class , NULL , devid , NULL , " xxx " ) ;
return 0;
}
/*Drive exit function*/
static void_exit led_exit(void)
{
/*Delete device*/
	device_destroy(newchrled.class,newchrled.devid);
/*Delete class*/
	class_destroy(newchrled.class);
}
module_init(led_init);
module_exit(led_exit);

That's all for the new Linux character device driver!!!

Topics: Linux Embedded system