Linux Embedded Development Record (2) Device Driver

        In the previous section, we mainly explained how to compile a driver. In this section, we mainly explain the process that drives development.

        1. Why do you need a driver?

        A device driver, as the name suggests, is a device that needs to be used. In Linux, the application layer cannot directly manipulate the hardware device, so we need a program that specifically manipulates the device to provide it for the application layer to call. Such a program is called driver. Well, it is actually a program that needs to deal with equipment.

        2. What is the process that drives development?

        For drivers before version 2.6, we basically don’t need to pay attention to them now. We mainly focus on drivers after version 2.6. We mainly divide it into four processes. The following mainly takes the character device driver as an example.

        Before talking about drivers, let's first look at how our application layer uses a device. The basic steps are to open a device first, open("/dev/led"); and then call write, read, ioctl and other methods to operate the device. Finally, use close() to turn off the device when we are finished. Well, for the application layer, what does it need to pay most attention to? It is this node under dev. Where did this node come from? There are two ways to generate it, one is to use the udev device management method in the driver, and the other is to use the mknod instruction. If the device driver does not support automatic creation of device nodes, it needs to be created by the user of the driver. Assume that the name of this device is led, the major device number is 240, and the minor device number is 0. The command to create a device node is #mknod /dev/led c 240 0. In this way, a node /led is generated under /dev/. How to create device nodes using udev? It mainly calls two fixed functions: first, class_create() to create a class for the device, and then call device_create again to create the corresponding device node for the device. The following is the code for a udev to create a node.

//Create the led_class directory under /sys/class.

led_class = class_create(THIS_MODULE,"led_class");

//Create /dev/led%d node file.

device_create(led_class,NULL,devno,NULL,"led","%d",MINOR(devno));

A devno MINOR (devno) appears here. What are these? Let’s go back and see how we use mknod to create a node. When using mknod to create a node, we need to know three parameters, name, and major device number. , minor device number. The name LED is easy for us to guess, so there are only two left, one major device number and one minor device number. So who is the master? devno is the main device, and MINOR(devno) finds its minor device number based on the major device number.

From the above, can we easily find that after a driver is completed, what things does it need to provide us? The first major device number, the second minor device number, the third device name, and the fourth interface for operating the device. Well, the driver is that simple. If you complete the above four questions, then your driver is ready! Okay, let's see if these four questions are completed below, and our driver will be completed.

 1. Conventional character device driver.

1. Secondary device number

static int minor = 0; // We set its minor device number to 0

2. Main device number

dev_t  devno;

ret = alloc_chrdev_region(&devno,minor,1,"led"); //Get the major device number from the system.

major = MAJOR(devno);//Get the major device number

minor = MINOR(devno); //Get the minor device number

3. Operating device interface.

struct file_operations led_cdev_fops = {

        .owner = THIS_MODULE,

        .read = led_cdev_read,

        .write = led_cdev_write,

        .open = led_cdev_open,

        .ioctl = led_cdev_ioctl

}; //Device operation interface

struct cdev *led_dev; //cdev data structure

led_dev = cdev_alloc(); //Allocate led_dev structure.

cdev_init(led_dev,&led_cdev_fops ); //Initialize the led_dev structure. Note that this structure is associated with device operations.

cdev_add(led_dev,devno,1); //Add led_dev to the system. Note that devno is introduced here, which associates the operation with the major device number. ,

4. Create a device under /dev/

led_class = class_create(THIS_MODULE,"led_class");

device_create(led_class,NULL,devno,NULL,"led",NULL); 

At this point, a conventional device driver is completed. Load it into module_init();, compile it using the corresponding compilation tool, put it into the Arm board, and then it can be applied.

 2. Character driver of device driver model.

        The device driver model is mainly the management of devices, drivers and buses by the kernel. And expose these management information to the application layer, such as the udev creation device we mentioned above, it is part of the device driver model. It exposes /dev/led to users. Let users understand and use it. Management is carried out in the Linux kernel through kobject, kset, and subsys. Driver writing can ignore the specific implementation of these management mechanisms, but we need to know what information it exposes to users and what this information represents. The following mainly describes the information it mainly exposes and the relationship between this information.

1. Driver model information is exposed through the sysfs file system. 1. The superior-subordinate relationship of the device driver model is realized through the parent directory and subdirectory of sysfs. 2. The horizontal relationship of the device driver model is realized through the directory symbolic link of sysfs. 3. The attributes of the driver model are realized through the file contents of the sysfs file system. 4. The kobject in the device driver model data structure corresponds to the directory in the sysfs file system, and the struct attribute member in the data structure corresponds to the file in the sysfs file system. The sysfs file system is mounted under /sys.

2. Device, driver, bus. When a new device or driver is added to the bus, the match method of the bus will be called to find a matching driver or device for the device or driver.

3. Class is a high-level abstraction in the Linux device driver model, providing user space with a high-level view of the device. In sysfs, classes are generally placed in the /sys/class directory. In a class subsystem, information can be exported to user space, and user space can interact with the kernel through this information. The most typical one is udev we talked about earlier. udev is a user space program that creates device nodes based on the dev files in the ./sys/class directory. It can be understood that the nodes in the class directory have nodes under ./dev.

4. The actual file node is created in /sys/devices/. The devices appearing in other directories, such as device classes and subdirectories below the bus, use symbolic links to point to files in the /sys/devices/ directory.

3. Character drivers for platform devices and drivers.

        Platform device abstracts the two core concepts of platform_device and platform_driver. Another important concept related to this is resource.

        struct resource{

                resource_size_t start; //The starting address of the resource on the CPU

                resource_size_t end; //The physical end address of the resource on the CPU

                const char *name; //The name of the resource

                unsigned long flags; //Resource flags

                struct resource *parent, *sibing,*child; //The father, brother and child resources of the resource

        }; //For resources, we should pay attention to its structure and see what content the resources contain.

A device can occupy multiple resources.

1. Platform equipment. Not any device can be abstracted into a platform_device. A platform_device is a device that appears as an independent entity in the system. One thing these devices have in common is that the CPU can directly access them through the bus. First, let's take a look at what a platform device contains.

 strut platform_device{

        const char *name; //Device name

        int id; //device ID

        struct device dev; //Device data structure

        u32 num_resources; //Number of resources

        struct resources *resources; //Device resources

        const struct platform_device_id *id_entry; //Device ID entry

        struct pdev_archdata archdata; //architecture related data

};

        name is the name of the device, used to match and bind with platform_driver, and resource is used to describe the device's resources, such as address, IRQ, etc. For a device, we mainly focus on its name and resources.

2. Platform driver. platform_driver is the encapsulation of device_driver and provides the probe and remove methods of the driver. Methods such as shutdown and suspend related to power management are also provided.

struct platform_driver{

        int (*probe)(struct platform_device *)    // probe 方法

        int (*remove)(struct platform_device *) // remove 方法

        void (*shutdown)(struct platform_device *);  //shutdown 方法

        int (*suspend)(struct platform_device*, pm_message_t state); //suspend 方法

        int (*resume)(struct platform_device *); //resume 方法

        struct device_drive driver; //device driver

        const struct platform_device_id *id_table; //Device ID table

};

3. The difference between ordinary drivers and platform drivers. The difference between ordinary drivers and platform drivers lies in the changes in the framework structure and the changes in the locations of resource application and release. 1. Resource application, device registration, etc. are moved from the module initialization part of the ordinary character driver to the probe method of the platform driver. 2. Cancellation of equipment. Resource release and other aspects have been moved from the ordinary character-driven module exit code to the platform-driven remove method. 3. The platform driver also adds resource definition and initialization, platform device and driver definition and initialization.

4. Platform driver example:

       1. Platform equipment.

        For platform devices, we only need to do three things: 1. What resources does this device have? 2. This device data structure. 3. Register platform equipment.

        (1) Define resources.

        static struct resource led_resource[]={

        [0]={

                .start = GPIO_LED_PIN,

                .end = GPIO_LED_PIN,

                .flags = IORESOURCE_IO,

                },

        };

        (2), device data structure

        static struct platform_device *led_platform_device = {

                .name = "led", //The name in the driver must be the same as this name

                .id = -1,

                .num_resources = ARRAY_SIZE(led_resource); 

                .resource = led_resource,

                

                

        };

        (3) Register platform equipment.

        platform_device_register(&lef_platform_device);

        Two platform drivers.

        For platform drivers, in addition to the four steps required for ordinary drivers, we also need to obtain resources from the platform device and then register the platform driver.

        1. Obtain resources from platform devices.

        struct resource *res_io;

        res_io = platform_get_resource(pdev,IORESOURCE_IO,0);

        int led_io = res_io.start; //Get the GPIO pin.

        2. Define the secondary device number

       static int minor = 0;

        3. Main device number

        ret = alloc_chrdrv_region(&devno,minor,1,"led");

        major = MAJOR(devno);

       4. Operating device interface.

        struct cdev *led_dev; //cdev data structure

        struct file_operations led_fops = {

                .owner = THIS_MODULE,

                .open = led_open, //led_open is a function that I need to implement myself, usually to make the hardware available

                .write = led_write,

                .read = led_read,

                .ioctl = led_ioctl

        };

        led_dev = cdev_alloc(); //

        cdev_init(led_dev,&led_fops); //Initialization, note that the operation function is added at this location

        cdev_add(led_dev,devno,1); //Add LED to the system

        5. Create a device under /dev.

        led_class = class_create(THIS_MODULE,"led_class");

        device_create(led_class,deno,NULL,"led");

        6. Register to the platform driver.

        struct platform_driver led_platform_driver = {

                .probe = led_probe,

                .remove = led_remove,

                .drive={

                        .name = "led",

                        .owner = THIS_MODULE,

                }

        };

        platform_driver_register(&led_platform_driver);

 4. About the character driver of the device tree.

        The character driver of the device tree is the latest process in driver development to date. How is it different from ordinary device drivers and platform device drivers? The biggest difference is that there is an additional device tree used to describe the hardware. Looking back, let's see where we describe the hardware in ordinary device drivers and platform drivers. In ordinary device drivers, we initialize and describe the hardware information during initialization. In platform drivers, we put the hardware resource information in the platform device. So what does this hardware information look like in the driver of the device tree? First, let's look at what the device tree finally looks like after entering the kernel. When we trace the Linux source code, we will find that after the device tree is parsed, of_device_add() finally links the device_node node generated by the corresponding device tree node into the dev.of_node of platform_device. That's right. In the end, each device in the device tree is parsed into a platform device. The system has already written the platform device for us by reading the device tree, so we only need to implement the device driver. The rest is just the same as writing the platform device driver. To obtain hardware information in the driver, the of function is called.

Guess you like

Origin blog.csdn.net/dreamliweiming/article/details/126990229