Linux driver learning record - character device driver

         Character device is the most basic type of device driver in Linux. Character device is a byte by byte. Read and write operations are carried out according to the byte stream, and the read and write data are in order. For example, common lighting, buttons, IIC, LCD, etc.

         Load / unload function

         Load / unload function module_init(XXX_init), module_exit(XXX_exit), where XXX_ Init and XXX_ Exit is a function that needs to be written by yourself. When the commands "insmod" and "remod" will load / unload the driver, the above two functions will be called. The template is as follows:

static int __init XXX_init(void)
{
    /*Content of entry function*/
    return 0;
}

static void __exit XXX_exit(void)
{
    /*Content of exit function*/
}

module_init(XXX_init);
module_exit(XXX_exit);

         Static represents a static function, which is different from ordinary functions: by adding the modifier static before the return type of the function, the function becomes a static function. This function can only be used in the file declaring it and cannot be called by other files. The definition and declaration of ordinary functions are extern al by default. The advantage is that functions with the same name can be defined in other files without conflict.

        __ init and__ exit modifier: a hint to the kernel that this function is only an initialization function. After initialization, it can be discarded to free memory; This function is only used for module unloading. It will be called only during unloading. Any other call will make an error.

         Registration and logout of character devices         

         Character device registration and logout, static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops) and static inline void unregister_chrdev(unsigned int major, const char *name).

  • major: the main device. Each device under Linux has a device number. Linux device numbers range from 0 to 4095. If there is a primary number, there is a secondary number. It will be said later.
  • Name: device name, string.
  • fops: structure file_ Pointer of type operations to the collection variable of the device operation function. The storage functions of this structure include read(), write(), open(), and so on.
static struct file_operations test_fops;

static int __int XXX_init(void)
{
    /*Register character device driver*/
    int temp = 0;
    temp = register_chrdev(200, "test", &test_fops)
    if(temp < 0)
    {
        printk("register error!");
    }
    return 0;
}

static void __exit XXX_exit(void)
{
    /*Unregister character device driver*/
    unregister_chrdev(200, "test");
}

         The above is the method of static allocation of equipment number, that is, specify a number as the equipment number during registration. But you don't know whether the device number has been used (you can view the device number used by the system through the command "cat /proc/devices"). The following describes the method of dynamically assigning a device number: apply for a device number before registering the device, and the system will automatically give you an unused device number to avoid conflict. Release the device number when uninstalling the driver. Dynamic allocation is recommended, although it may be more complex.

        Apply for and release device number function int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name) and void unregister_chrdev_region(dev_t from, unsigned count).

  • dev: the device number to which the application is saved.
  • baseminor: starting address of secondary equipment number. Generally 0
  • count: the number of equipment numbers to apply for.
  • Name: device name.
int major;
int minor;
dev_t devid;  //Device number structure, "guess it is a structure containing two unsigned int variables"

if(major) //If major is valid
{    
    devid = MKDEV(major, 0);  //Define the equipment number. The secondary equipment number is generally 0
    /*MKDEV Yes, convert the primary and secondary device numbers to dev_ Kernel functions of type T*/
    register_chrdev_region(devid, 1, "test"); //Application equipment No
}
else  //invalid
{
    alloc_chrdev_region(&devid, 0, 1, "test"); //Application equipment No
    major = MAJOR(devid);  //Get master number
    minor = MINOR(devid);  //Get secondary number
}

unregister_chrdev_region(devid, 1); //Log off the equipment number, simple

There is an additional function int register_chrdev_region(dev_t from, unsigned count, const char *name). If there are major and minor numbers, use this function to apply. (since there is a device number, why not directly call the function register_chrdev to register the device? I don't understand)

         Equipment specific operation function

        Device specific operation function, file_ The operations structure is a specific device operation function. Assuming that the test device here controls a buffer (memory), there must be read and write functions.

/*What you need to do to open the device*/
/*
inode: inode passed to driver
filp: The device file has a file structure called private_ The member variable of data is usually private when it is open ed_ Data points to the device structure.
*/
static int test_open (struct inode *inode, struct file *filp)
{
    /**/
    return 0;
}

/*Read from device*/
/*
filp: Device to open
buf:Data buffer returned to user space
cnt: Length of data to read
offt: Offset relative to the first address of the file
*/
static ssizet_t test_read (struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    /**/
    return 0;
}

/*Write device*/
static ssize_t test_write (struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    /**/
    return 0;
}

/*release device*/
static int test_release(struct inode *inode, struct file *filp)
{
    /**/
    return 0;
}

/*Device operation function structure*/
static sturct file_operations test_fops = {
    .owner = THIS_MODULE,
    .open = test_open,
    .read = test_read,
    .write = test_write,
    .release = test_release,
};

        Add information

         Add information. Finally, add LICENSE information and author information. Where LICENSE is required, otherwise compilation error occurs. Examples are as follows:

module_init(XXX_init);
module_exit(XXX_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("GaoXu");

Tags: Linux

Posted on Sat, 11 Sep 2021 23:33:10 -0400 by rheroux