1. The essence of character device registration and registration and cancellation steps
The essence of character device driver registration
As long as a certain information exists on the operating system, there must be an object describing this information in the operating system. The character device driver is registered into the kernel, and there must be a character device driver object in the kernel to save the current character device driver information.
Character device driver object structure analysis struct cdev
struct dev{
struct kobject kobj; //Object of kernel base class
struct module *owner; //Pointer to this character device driver object THIS_MODULE
const struct file_operations *ops; //Operation method structure
dev_t dev; //Start value of device number
unsigned int countl; //number of devices
};
Character device driver registration and cancellation process
/************************Registration process****************/
1. Apply for a character device driver object
2. Initialize and assign values to the character device driver object
3. Apply to the kernel for the device number and specified number of device resources.
4. Register the character device driver object into the kernel
/************************Logout process******************/
1. Unregister the character device object
2. Release the requested device number3. Release character device driver object space
Character device driver registration and cancellation related API
*********************Registration process******************
#include
1. Apply for character device driver object
a:struct cdev cdev;
b: struct cdev *cdev = cdev_alloc();
/*
struct cdev *cdev_alloc(void)
Function: Apply for a struct cdev object from the kernel
Return value: Returns the first address of the object space applied for successfully, returns NULL if failed.
2. Initialization of character device driver object
void cdev_init(struct cdev *cdev, const struct file_operation *fops)
Function: Implement partial initialization of character device driver object membersParameters: cdev: character device object pointer
fops: operation method pointer3. Apply to the kernel for the device number and specified number of device resources.
3.1 Static application for device number
int register_chrdev_region(dev_t from, unsigned count, const char *name)Parameters: from: the starting value of the device number to be applied for
count: the number of device resources to be applied for
name: driver name
3.2 Dynamically apply for device number
int alloc_chrdev_region(dev_t *dev, unsigned baseminod, unsigned count, const char *name)
Parameters: dev: the first address of the space where the applied device number is saved.
baseminor: the starting value of the minor device number to be applied for
count: the number of requested device resources
name: driver nameReturn value: 0 is returned on success, error code is returned on failure
4. Register character device driver object
int cdev_add(struct cdev *p, dev_t dev, unsigned cout)Parameters: p: character device driver object pointer
dev: the starting value of the requested device number
count: the number of requested device resources/******************************Logout process****************** ******************/
1. Unregister the character device driver object
void cdev_del(struct cdev *p)
Parameters: character device driver object pointer
2. Release the requested device number
void unregister_chrdev_region(dev_t from, unsigned count)Parameters: from: starting value of device number
count: the number of device resources
3. Release character device driver object spacevoid kfree(struct cdev *p)
Parameters: p: character device driver object pointer
Character device driver object distribution registration and cancellation example
driver code
#include <linux/init.h> #include <linux/module.h> static int __init mycdev_init(void) { //1. Apply for an object space cdev_alloc //2.Initialize object cdev_init //3. Apply for device number register_chrdev_region()/alloc_chrdev_region() //4. Register driver object cdev_add //5. Submit the directory upward class_create //6. Submit device node information upward device_create return 0; } static void __exit mycdev_exit(void) { //1. Destroy device node information //2. Destroy the directory //3. Unregister the character device driver object //4. Release the device number //5. Release the applied character device driver object space } module_init(mycdev_init); module_exit(mycdev_exit); MODULE_LICENSE("GPL");
application layer code
#include <linux/init.h> #include <linux/module.h> #include<linux/fs.h> #include<linux/device.h> #include<linux/cdev.h> #include<linux/slab.h> struct cdev *cdev; unsigned int major=0; unsigned int minor=0; dev_t devno; struct class *cls; struct device *dev; int mycdev_open(struct inode *inode, struct file *file) { printk("%s:%s:%d\\ ", __FILE__, __func__, __LINE__); return 0; } long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { printk("%s:%s:%d\\ ", __FILE__, __func__, __LINE__); return 0; } int mycdev_close(struct inode *inode, struct file *file) { printk("%s:%s:%d\\ ", __FILE__, __func__, __LINE__); return 0; } //Define operation method structure variables and assign values struct file_operations fops = { .open = mycdev_open, .unlocked_ioctl = mycdev_ioctl, .release = mycdev_close, }; static int __init mycdev_init(void) { //1. Apply for an object space cdev_alloc int ret; cdev= cdev_alloc(); if(cdev==NULL) { printk("Failed to apply for character device driver object\\ "); ret=-EFAULT; goto out1; } printk("Character device driver object application successful\\ "); //2.Initialize object cdev_init cdev_init(cdev, & amp;fops); //3. Apply for device number register_chrdev_region()/alloc_chrdev_region() if(major==0)//Dynamic application { ret=alloc_chrdev_region( & amp;devno,minor,3,"mychrdev"); if(ret) { printk("Dynamic application for device number failed\\ "); goto out2; } major=MAJOR(devno);//Get the major device number based on the device number minor=MINOR(devno);//Get the minor device number based on the device number } else { ret=register_chrdev_region(MKDEV(major,minor),3,"mychrdev"); if(ret) { printk("Static specification of device number failed\\ "); goto out2; } } printk("Device number application successful\\ "); //4. Register driver object cdev_add ret=cdev_add(cdev,MKDEV(major,minor),3); if(ret) { printk("Failed to register character device driver object\\ "); goto out3; } printk("Registration of character device driver object successful\\ "); //5. Submit the directory upward class_create cls=class_create(THIS_MODULE,"mychrdev"); if(IS_ERR(cls)) { printk("Failed to submit directory upward\\ "); goto out4; } printk("Submit directory upward successfully\\ "); //6. Submit device node information upward device_create int i; for(i=0;i<3;i + + ) { dev=device_create(cls,NULL,MKDEV(major,i),NULL,"mycdev%d",i); if(IS_ERR(dev)) { printk("Failed to submit device node upward\\ "); goto out5; } } printk("Submitting device node information upward successfully\\ "); return 0; out5: //Release the successfully submitted node information for(--i;i>=0;i--) { device_destroy(cls,MKDEV(major,i)); } //Destroy directory class_destroy(cls); out4: cdev_del(cdev); out3: unregister_chrdev_region(MKDEV(major,minor),3); out2: kfree(cdev); out1: return ret; } static void __exit mycdev_exit(void) { //1. Destroy device node information int i; for(i=0;i<3;i + + ) { device_destroy(cls,MKDEV(major,i)); } //2. Destroy the directory class_destroy(cls); //3. Unregister the character device driver object cdev_del(cdev); //4. Release the device number unregister_chrdev_region(MKDEV(major,minor),3); //5. Release the applied character device driver object space kfree(cdev); } module_init(mycdev_init); module_exit(mycdev_exit); MODULE_LICENSE("GPL");
2. The role of struct inode/file structure
The role of struct inode structure
The role of 1.inode
As long as the file is stored in the operating system, there will be a struct inode structure object in the operating system kernel to save the relevant information of the current file. Each file has its own specific identifier called the inode number. The inode number is also the file The index number of the corresponding inode structure.
struct inode{
umode_t i_mode; //File permissions
unsigned short i_opflags;
kuid_t i_uid; //User ID of the file
kuid_t i_gid; //Group ID
unsigned int i_flags;
dev_t i_rdev; //device number
union{
struct block_device *i_bdev; //Block device
struct cdeb *i_cdv; //Character device
};
void *i_private; //Private data of the current file, which can be used for function value transfer
};
2. How to transfer the open function to the driver according to the file path
The role of struct file structure
1. The role of file descriptors
When we open a file in a process, the open function will return a non-negative integer after successful opening. This integer is the file descriptor. Up to 1024 file descriptors can be allocated in a process (the file descriptor is essentially an array subscript 0-1023). Each process will have its own independent set of file descriptors, and the existence of file descriptors depends on the process. So if you want to know the role of the file descriptor, you need to know the location of the file descriptor in the process.
As long as the process exists in the operating system, there must be a struct task_struct object in the operating system kernel to save relevant information about the process.
struct task_struct {
volatile long state; //process status
int on_cpu; //Identify which cpu the process is executed on
int prio; //process priority
pid_t pid; //process number
struct task_struct __rcl *real_parent; //parent process
struct files_struct *files; //Open file-related structure pointer
};
In the struct task_struct object, there is an open file-related structure pointer
struct files_struct {
struct file __rcu *fd_array[NR_OPEN_DEFULT]; //array of structure pointers, which stores the address of the struct file object (the struct file object stores information about the open file)
}; //The file descriptor is the subscript of this array
struct file structure function
When we open a file in the process, a structure space of type struct file will be applied for in the kernel. This space saves the open file information. The address of this struct file object will be saved to an array fd_array in the kernel, and the file descriptor is the subscript of the corresponding position in this array.
struct file {
struct path f_path; //File path
struct inode *f_inode;
const struct file_operations *f_op; //Operation method structure
unsigned int f_flags; //The second parameter of the open function is assigned to f_flags
fmode_t f_mode;/Permissions for open files
void *private_date; //Private data, which can realize data transfer between functions
};
How does the system call function find the corresponding operation method in the driver through the file descriptor
The process of applying for fd file descriptor and generating struct file structure
Complete the binding of device files and specific devices in the driver
method 1:
1. Devices belonging to the same type have different minor device numbers. Different device files can be bound to specific devices.
2. The parameters in the open method are struct inode* and struct file* structure object pointers.
3. There is a member device number i_rdev in the struct inode object. The minor device number can be obtained through int min = MINOR(inode->i_rdev.
4. Assign the obtained minor device number min to the private data member of the struct file object (void *private, which can be used as data transfer between functions), file->private = (void *)min
5. There is also a struct file* object pointer in the ioctl/read and other operation methods, int min = (int)(file->private); By judging the private data members, we can judge which specific device the device file corresponds to.
Method 2: There is a struct file* object pointer in the ioctl/read and other operation methods. There is a struct inode* object pointer in the struct file object. There is a member device number i_rdev in the struct inode object. You can pass int min = MINOR(inode ->i_rdev obtains the minor device number.
int min = MINOR(file->inode->i_rdev);
code example
int mycdev_open(struct inode *inode, struct file *file) { int min=MINOR(inode->i_rdev);//Get the minor device number of the opened file file->private_data= (void *)min; printk("%s:%s:%d\\ ", __FILE__, __func__, __LINE__); return 0; } long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int min=(int)file->private_data;//Get the minor device number of the file switch(min) { case 0://operate LED1 switch(cmd) { case LED_ON://turn on the light break; case LED_OFF: //Turn off the lights break; } break; case 1://operate LED2 switch(cmd) { case LED_ON://turn on the light break; case LED_OFF: //Turn off the lights break; } break; case 2://operate LED3 switch(cmd) { case LED_ON://turn on the light break; case LED_OFF: //Turn off the lights break; } break; } printk("%s:%s:%d\\ ", __FILE__, __func__, __LINE__); return 0; }