The essence of character device driver registration and registration and cancellation steps, and the role of the struct inode/file structure

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 number

3. 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 members

Parameters: cdev: character device object pointer
fops: operation method pointer

3. 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 name

Return 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 space

void 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;
}