Driver development–implementation process of udev mechanism for automatically creating device nodes

1. Understanding udev

udev: A mechanism for automatically creating device nodes. The logic of creating device nodes is in user space.

2. Analysis of the process of creating device nodes by udev mechanism

3. Directory information creation and deletion functions

#include<linux/device.h>
1. Submit directory information upwards
struct class * class_create(struct module *owner,const char *name);
Function: Apply for a device class and initialize it, submit directory information upwards
parameter:
Parameter 1: owner: a module pointer pointing to the current kernel module itself, fill in THIS_MODULE
Parameter 2: name: directory name submitted upwards
Return value: The first address of the applied struct class object space is returned successfully, and the error code pointer is returned if failed.
    /*
    4K space will be reserved at the top level of the kernel space. When the struct class function call fails, the function will return a pointer to this 4K space.
    bool __must_check IS_ERR(__force const void *ptr)
    Function: Determine whether the pointer points to the 4K reserved space
    Parameter: pointer to be judged
    Return value: Returns logical true if pointing to 4K reserved space, otherwise returns logical false
     long __must_check PTR_ERR(__force const void *ptr)
     Function: Get the error code through the error code pointer
     ex:struct class_create *cls=struct class_create(THIS_MODULE,"mycdev");
     if(IS_ERR(cls))
     {
         printk("Failed to submit directory upward\
");
         return -PRT_ERR(cls);
     }
    */
2. Destroy the directory
void class_destroy(struct class *cls)
Function: Destroy directory information
Parameters: cls: pointer to class object
Return value: None

4. Node information creation and deletion functions

1. Submit node information upwards
struct device *device_create(struct class *class, struct device *parent,
                 dev_t devt, void *drvdata, const char *fmt, ...)
Function: Create a device object and submit device node information upwards
parameter:
Parameter 1: cls: the class object pointer when submitting the directory upwards
Parameter 2: parent: The address of the previous node of the currently applied object. If you don’t know, fill in NULL.
Parameter 3: devt: device number major device number <<20 | minor device number
        /*
            MKDEV (major device number, minor device number): Get the device number based on the major device number and minor device number
            MAJOR(dev): Get the major device number based on the device number
            MINOR(dev): Get the minor device number based on the device number
        */
Parameter 4: dridata: the private data of the device object applied for, fill in NULL
Parameter 5: fmt: device node name submitted upwards
...: variable length parameters
Return value: Successfully returns the first address of the device object applied for. Failure returns an error code pointer, pointing to the 4K reserved space.

2. Destroy device node information
void device_destroy(struct class *class, dev_t devt)
Function: Destroy device node information
parameter:
class: The class object pointer obtained when submitting the directory upwards
devt: The device number submitted when submitting device node information upwards
Return value: None

5. Automatically create device node instances

1. Driver

#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/io.h>
#include<linux/device.h>
#include"head.h"

int major;
char kbuf[128]={0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\
",__FILE__,__func__,__LINE__);
    return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
    printk("%s:%s:%d\
",__FILE__,__func__,__LINE__);
     unsigned long ret;
    //Read copy to user space
    if(size>sizeof(kbuf))//The size that the user space expects to read cannot be satisfied by the kernel, then give the maximum size supported by the kernel.
        size=sizeof(kbuf);
    ret=copy_to_user(ubuf,kbuf,size);
    if(ret)//copy failed
    {
        printk("copy_to_user filed\
");
        return ret;
    }
    return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{
    unsigned long ret;
    //Read data from user space
    if(size>sizeof(kbuf))//The size that the user space expects to read cannot be satisfied by the kernel, then give the maximum size supported by the kernel.
        size=sizeof(kbuf);
    ret=copy_from_user(kbuf,ubuf,size);
    if(ret)//copy failed
    {
        printk("copy_to_user filed\
");
        return ret;
    }
    switch(kbuf[0]){
        case '1'://LED1
            if(kbuf[1]=='0')//Turn off the light
                vir_led1->ODR & amp;= (~(1<<10));
            else//turn on the light
                vir_led1->ODR |= 1<<10;
            break;
        case '2'://LED2
            if(kbuf[1]=='0')//Turn off the light
                vir_led2->ODR & amp;= (~(1<<10));
            else//turn on the light
                vir_led2->ODR |= 1<<10;
            break;
        case '3'://LED3
            if(kbuf[1]=='0')//Turn off the light
                vir_led3->ODR & amp;= (~(1<<8));
            else//turn on the light
                vir_led3->ODR |= 1<<8;
            break;
    }
    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,
    .read=mycdev_read,
    .write=mycdev_write,
    .release=mycdev_close,
};

int all_led_init(void)
{
    //Register address mapping
    vir_led1=ioremap(PHY_LED1_ADDR,sizeof(gpio_t));
    if(vir_led1==NULL)
    {
        printk("ioremap filed:%d\
",__LINE__);
        return -ENOMEM;
    }
     vir_led2=ioremap(PHY_LED2_ADDR,sizeof(gpio_t));
    if(vir_led2==NULL)
    {
        printk("ioremap filed:%d\
",__LINE__);
        return -ENOMEM;
    }
     vir_led3=vir_led1;
    vir_rcc=ioremap(PHY_RCC_ADDR,4);
    if(vir_rcc==NULL)
    {
        printk("ioremap filed:%d\
",__LINE__);
        return -ENOMEM;
    }
    printk("Physical address mapping successful\
");
    //Initialization of register
    //rcc
    (*vir_rcc) |= (0X3<<4);
    //led1
    vir_led1->MODER & amp;= (~(3<<20));
    vir_led1->MODER |= (1<<20);
    vir_led1->ODR & amp;= (~(1<<10));
    //led2
    vir_led2->MODER & amp;= (~(3<<20));
    vir_led2->MODER |= (1<<20);
    vir_led2->ODR & amp;= (~(1<<10));
    //led3
    vir_led3->MODER & amp;= (~(3<<16));
    vir_led1->MODER |= (1<<16);
    vir_led1->ODR & amp;= (~(1<<8));
    printk("Register initialization successful\
");

    return 0;
}

static int __init mycdev_init(void)
{
    //Character device driver registration
    major=register_chrdev(0,"mychrdev", & amp;fops);
    if(major<0)
    {
        printk("Character device driver registration failed\
");
        return major;
    }
    printk("Character device driver registration successful: major=%d\
",major);
    
    //Register mapping and initialization
    all_led_init();
    //Submit directory upward
    cls=class_create(THIS_MODULE,"mychrdev");
    if(IS_ERR(cls))
    {
        printk("Failed to submit directory upward\
");
        return -PTR_ERR(cls);
    }
    printk("Submit directory upward successfully\
");
    //Submit device node information upwards
    int i;
    for(i=0;i<3;i + + )
    {
        dev=device_create(cls,NULL,MKDEV(major,i),NULL,"mychrdev%d",i);
        if(IS_ERR(dev))
        {
            printk("Failed to submit device node information upward\
");
            return -PTR_ERR(dev);
        }
    }
    printk("Submit device node information upward successfully\
");


    return 0;
}
static void __exit mycdev_exit(void)
{
    /*Destroy device node information*/
    int i;
    for(i=0;i<3;i + + )
    {
        device_destroy(cls,MKDEV(major,i));
    }
    //Destroy directory information
    class_destroy(cls);
    //Cancel address mapping
    iounmap(vir_led1);
    iounmap(vir_led2);
    iounmap(vir_rcc);
    
   
    //Unregister the character device driver
    unregister_chrdev(major,"mychrdev");


}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

2. Application

#include<stdlib.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
#include<string.h>


int main(int argc, char const *argv[])
{
    char buf[128]={0};
    int fd=open("/dev/mychrdev0",O_RDWR);
    if(fd<0)
    {
        printf("Failed to open device file\
");
        exit(-1);
    }
    while(1)
    {
        //read from terminal
        printf("Please enter two characters\
");
        printf("First character: 1(LED1) 2(LED2) 3(LED3)\
");
        printf("Second character: 0 (lights off) 1 (lights on)\
");
        printf("Please enter >");
        fgets(buf,sizeof(buf),stdin);
        buf[strlen(buf)-1]='\0';
        //Write to device file
        write(fd,buf,sizeof(buf));
    }

    
    close(fd);

    return 0;
}

3.Header file

#ifndef __HEAD_H__
#define __HEAD_H__
typedef struct{
    unsigned int MODER;
    unsigned int OTYPER;
    unsigned int OSPEEDR;
    unsigned int PUPDR;
    unsigned int IDR;
    unsigned int ODR;
}gpio_t;
#define PHY_LED1_ADDR 0X50006000
#define PHY_LED2_ADDR 0X50007000
#define PHY_LED3_ADDR 0X50006000
#define PHY_RCC_ADDR 0X50000A28
#endif