i.MX6ULL(20) linux platform device driver

The Linux system should consider the reusability of the driver, because

 

Therefore, a software idea such as driver separation and layering was proposed. Under this idea, the most frequently dealt with in the future was born.
Platform device driver, also called platform device driver.

1 Separation and layering of Linux drivers

1.1 Drive Separation and Separation

For a mature, large, and complex operating system such as Linux , code reusability is very important, otherwise it would be
There will be a lot of meaningless repetitive code in the Linux kernel. Especially the driver, because the driver takes up the Linux
The bulk of the kernel code, if the driver is not managed and the repeated code is allowed to increase arbitrarily, it will not take long
The number of files in the Linux kernel is unacceptably large.
If there are three platforms A , B and C , all of these three platforms ( here the platform refers to SOC) have the MPU6050 .
A six-axis sensor with an I2C interface, according to the idea when we wrote the bare-metal I2C driver, each platform has an MPU6050
driver, so the simplest driver framework written is shown in Figure 54.1.1 :

Each platform has a host driver and device driver, the host driver must be
Yes, after all, different platforms have different I2C controllers. But there is no need to write one for each platform for the device driver on the right
The best practice is that each platform's I2C controller provides a unified interface
( also called host driver ) , each device only provides one driver ( device driver ) , and each device passes through a unified I2C
interface driver to access

There must be many kinds of actual I2C driver devices, not just the MPU6050 , so the actual driver architecture is shown in the figure
54.1.1.3 shows:

This is the separation of the driver, that is, the separation of the host driver and the device driver.

In the actual driver development, the general I2C host controller driver has been
The semiconductor manufacturer has written it, and the device driver is generally written by the device manufacturer. We only need to provide the device information.
For example, for an I2C device, provide which I2C interface the device is connected to, what is the speed of the I2C , and so on. quite
In order to separate the device information from the device driver, the driver uses standard methods to obtain device information ( such as obtaining device information from the device tree) , and then initializes the device according to the obtained device information.
This is equivalent to that the driver is only responsible for the driver, and the device is only responsible for the device, just find a way to match the two.
This is the bus (bus) , driver (driver) and device (device) model in Linux , which is often referred to as driver separation. The bus is the master of driver and device information, responsible for connecting the two, as shown in Figure 54.1.1.4:
When we register a driver with the system, the bus will look in the device on the right to see if there is a match
equipment, if any, to link the two. Similarly, when a device is registered with the system, the bus will
Look in the driver on the left to see if there is a matching device, and if so, link it . A large number of drivers in the Linux kernel
The programs all use the bus, driver and device modes. The platform driver we will focus on later is the product of this idea.
thing.

1.2 Layering of drivers

Drivers under Linux are often layered, and the purpose of layering is to process different content at different layers. Taking the input ( input subsystem, which will be explained in detail in a special chapter later) that is often used in other books or materials as an example, briefly introduce the layering of the driver.
The input subsystem is responsible for managing all input-related drivers, including keyboard, mouse, touch, etc. The bottom layer is the original device driver, which is responsible for obtaining the original value of the input device, and reporting the obtained input events to the input core layer . The input core layer handles various IO models and provides a collection of file_operations operations
When we write the input device driver, we only need to handle the reporting of input events. As for how to handle these reported input events, that is for the upper layer to consider, and we don't care.

2 Introduction to platform driver model

Earlier we talked about the separation of device drivers, and led to the bus (bus) , driver (driver) and device (device) models, such as
Such as I2C , SPI , USB and other buses. However, some peripherals in the SOC do not have the concept of the bus, but they must use the bus
What about wires, drivers, and device models? In order to solve this problem, Linux proposes the platform virtual bus, corresponding
There are platform_driver and platform_device .

2.1 platform bus

The Linux system kernel uses the bus_type structure to represent the bus, which is defined in the file include/linux/device.h ,
The contents of the bus_type structure are as follows:
 struct bus_type {
 	const char *name; /* 总线名字 */
 	const char *dev_name; 
 	struct device *dev_root;
 	struct device_attribute *dev_attrs;
 	const struct attribute_group **bus_groups; /* 总线属性 */
 	const struct attribute_group **dev_groups; /* 设备属性 */
 	const struct attribute_group **drv_groups; /* 驱动属性 */
 
 	int (*match)(struct device *dev, struct device_driver *drv);
 	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
	 int (*probe)(struct device *dev);
 	int (*remove)(struct device *dev);
 	void (*shutdown)(struct device *dev);
 
 	int (*online)(struct device *dev);
 	int (*offline)(struct device *dev);
	int (*suspend)(struct device *dev, pm_message_t state);
	int (*resume)(struct device *dev);
	const struct dev_pm_ops *pm;
	const struct iommu_ops *iommu_ops;
	struct subsys_private *p;
	struct lock_class_key lock_key;
 };
The bus uses the match function to find the corresponding driver according to the registered device.
To move, or to find the corresponding device according to the registered driver, so each bus must implement this function. The match function has
Two parameters: dev and drv , these two parameters are device and device_driver types, that is, the device and the driver.

2.1.1 platform_bus_type inherits bus_type

struct bus_type platform_bus_type = {
     .name = "platform",
     .dev_groups = platform_dev_groups,
     .match = platform_match,
     .uevent = platform_uevent,
     .pm = &platform_dev_pm_ops,
 };
platform_bus_type is the platform platform bus, where platform_match is the matching function. let's see
How to match the driver and the device, the platform_match function is defined in the file drivers/base/platform.c , the function
The content of the number is as follows:
static int platform_match(struct device *dev,
struct device_driver *drv)
 {
     struct platform_device *pdev = to_platform_device(dev);
     struct platform_driver *pdrv = to_platform_driver(drv);
 
     /*When driver_override is set,only bind to the matching driver*/
     if (pdev->driver_override)
         return !strcmp(pdev->driver_override, drv->name);
 
     /* Attempt an OF style match first */
     if (of_driver_match_device(dev, drv))
         return 1;

     /* Then try ACPI style match */
     if (acpi_driver_match_device(dev, drv))
         return 1;
    if (pdrv->id_table)
         return platform_match_id(pdrv->id_table, pdev) != NULL;
 
      /* fall-back to driver name match */
     return (strcmp(pdev->name, drv->name) == 0);
   }

2.1.2 Driver and device matching

4 ways to match
1.  OF type matching, that is, the matching method adopted by the device tree,
The of_driver_match_device function is defined in the file include/linux/of_device.h . device_driver structure ( indicating
device driver ) has a member variable named of_match_table , which holds the compatible matching table of the driver.
The compatible attribute of each device node in the device tree will be compared with all members in the of_match_table table, check if
Whether there are the same entries, if yes, it means that the device matches this driver, after the device and driver match successfully, the probe function
will execute.
2.  ACPI matching method.
3.  id_table matches, each platform_driver structure has an id_table
Member variables, as the name suggests, store a lot of id information. These id information stores the driver supported by this platformd driver
Action type.
4. If the id_table of the third matching method does not exist, directly compare the driver and
Check the name field of the device to see if they are equal. If they are equal, the match is successful.
For the version number of Linux that supports device tree, the general device driver supports both device tree and no device tree for compatibility.
matching method. That is to say, the first matching method generally exists, and only one of the third and fourth matching methods is required.
The fourth method is used the most, which is to directly compare the name fields of the driver and the device. After all, this method is the simplest.

2.2 platform driver

The platform_driver structure represents the platform driver, and this structure is defined in the file
In include/linux/platform_device.h , the content is as follows:
 struct platform_driver {
     int (*probe)(struct platform_device *);
     int (*remove)(struct platform_device *);
     void (*shutdown)(struct platform_device *);
     int (*suspend)(struct platform_device *, pm_message_t state);
     int (*resume)(struct platform_device *);
     struct device_driver driver;
     const struct platform_device_id *id_table;
     bool prevent_deferred_probe;
 };
The probe function, when the driver and the device are successfully matched, the probe function will be executed, a very important function! !
Generally, the driver provider will write it. If you want to write a brand new driver, then the probe needs to be implemented by yourself.
The driver member is the device_driver structure variable, which is widely used in the Linux kernel.
Thinking, device_driver is equivalent to the base class, providing the most basic driver framework. plaform_driver inherits this base class,
Then on this basis, some unique member variables are added.
The id_table table, which is what we used when we explained the platform bus matching drivers and devices in the previous section
The third method, id_table is a table ( that is, an array ) , and the type of each element is platform_device_id ,
The content of the platform_device_id structure is as follows:
 struct platform_device_id {
     char name[PLATFORM_NAME_SIZE];
     kernel_ulong_t driver_data;
 };
The device_driver structure is defined in include/linux/device.h , and the content of the device_driver structure is as follows:
示例代码 54.2.2.3 device_driver 结构体
 struct device_driver {
     const char *name;
     struct bus_type *bus;
 
     struct module *owner;
     const char *mod_name; /* used for built-in modules */
 
     bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
 
     const struct of_device_id *of_match_table;
     const struct acpi_device_id *acpi_match_table;

     int (*probe) (struct device *dev);
     int (*remove) (struct device *dev);
     void (*shutdown) (struct device *dev);
     int (*suspend) (struct device *dev, pm_message_t state);
     int (*resume) (struct device *dev);
     const struct attribute_group **groups;

     const struct dev_pm_ops *pm;

     struct driver_private *p;
 };
of_match_table is the matching table used by the driver when using the device tree. It is also an array. Each match
All items are of_device_id structure type,
compatible is very important, because for the device tree, it is through the compatible attribute of the device node
Comparing the property value with the compatible member variable of each item in of_match_table , if there is an equal, it means the device
Matched successfully with this driver.
When writing a platform driver, first define a platform_driver structure variable, and then implement the structure
Each member variable in is focused on implementing the matching method and the probe function. After the driver and device are successfully matched, probe
The function will be executed, and the specific driver program is written in the probe function, such as character device driver and so on.
int platform_driver_register (struct platform_driver *driver)
platform_driver_unregister(struct platform_driver *drv)
The platform driver framework is as follows
The platform driver is still a traditional character device driver, block device driver or network device driver.
A "platform" skin is put on, the purpose is to use the driver model of bus, driver and device to realize the distribution of drivers.
Separation and stratification.
xxx_of_match matching table, if you use the device tree, you will use this matching table to drive and device
match. Line 51 sets a matching item whose compatible value is " xxx-gpio ", so when in the device tree
When the compatible attribute value of the device node is " xxx-gpio ", the device will match the driver. Line 52 is a
flag, the last occurrence of the of_device_id table must be empty.
Lines 58 ~65 , define a platform_driver structure variable xxx_driver , indicating the platform driver, lines 59~62
The line sets the two attributes of the device_driver member variable name and of_match_table in paltform_driver . in
The name attribute is used for traditional driver and device matching, that is, to check whether the name fields of the driver and the device are the same.
The of_match_table attribute is used for driver and device checks under the device tree. For a complete driver, it is necessary to provide
There are two matching methods, device tree and no device tree.

2.3 platform equipment

The platform_device structure represents the platform device, here we should pay attention, if the kernel supports the device tree
Do not use platform_device to describe the device anymore.
示例代码 54.2.3.1 platform_device 结构体代码段
 struct platform_device {
     const char *name; 
     int id; 
     bool id_auto;
     struct device dev;
     u32 num_resources; 
     struct resource *resource;

     const struct platform_device_id *id_entry;
     char *driver_override; /* Driver name to force a match */

     /* MFD cell pointer */
     struct mfd_cell *mfd_cell;

     /* arch specific additions */
     struct pdev_archdata archdata;
 };

 struct resource {
     resource_size_t start;
     resource_size_t end;
     const char *name;
     unsigned long flags;
     struct resource *parent, *sibling, *child;
 };
Line 23 , name represents the device name, which must be the same as the name field of the platform driver used , otherwise set
The device cannot match the corresponding driver. For example, the name field of the corresponding platform driver is " xxx-gpio ", then the name
The field should also be set to " xxx-gpio ".
On line 27 , num_resources indicates the number of resources, which is generally the size of the resource resource on line 28 .
In line 28 , resource represents resources, that is, device information, such as peripheral registers, etc. The Linux kernel uses resources
Structures represent resources.
start and end indicate the start and end information of the resource, respectively. For resources of the memory class, they indicate the start and end of the memory
address, name indicates the resource name, flags indicates the resource type, and the optional resource types are defined in the file
Include/linux/ioport.h ,
In previous versions of Linux that did not support device trees , users need to write platform_device variables to describe device information,
Then use the platform_device_register function to register the device information into the Linux kernel. The prototype of this function is as follows:
int platform_device_register(struct platform_device *pdev)
If the platform is no longer used, the corresponding platform can be unregistered through the platform_device_unregister function
device, the prototype of the platform_device_unregister function is as follows:
​​​​​​​
void platform_device_unregister(struct platform_device *pdev)

The platform device information framework is as follows: 

示例代码 54.2.3.4 platform 设备框架
 /* 寄存器地址定义*/
 #define PERIPH1_REGISTER_BASE (0X20000000) /* 外设 1 寄存器首地址 */ 
 #define PERIPH2_REGISTER_BASE (0X020E0068) /* 外设 2 寄存器首地址 */
 #define REGISTER_LENGTH 4

 /* 资源 */
 static struct resource xxx_resources[] = {
 [0] = {
 .start = PERIPH1_REGISTER_BASE,
 .end = (PERIPH1_REGISTER_BASE + REGISTER_LENGTH - 1),
 .flags = IORESOURCE_MEM,
 }, 
 [1] = {
 .start = PERIPH2_REGISTER_BASE,
 .end = (PERIPH2_REGISTER_BASE + REGISTER_LENGTH - 1),
 .flags = IORESOURCE_MEM,
 },
 };

 /* platform 设备结构体 */
 static struct platform_device xxxdevice = {
 .name = "xxx-gpio",
 .id = -1,
 .num_resources = ARRAY_SIZE(xxx_resources),
 .resource = xxx_resources,
 };
 
 /* 设备模块加载 */
static int __init xxxdevice_init(void)
 {
 return platform_device_register(&xxxdevice);
 }

 /* 设备模块注销 */
 static void __exit xxx_resourcesdevice_exit(void)
 {
 platform_device_unregister(&xxxdevice);
 }

 module_init(xxxdevice_init);
 module_exit(xxxdevice_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("zuozhongkai");
Compared with the device tree, it is much more complicated. When the Linux kernel supports the device
After the tree is prepared, the user does not need to manually register the platform device. Because the device information is placed in the device tree to describe,
When the Linux kernel starts, it will read the device information from the device tree, and then organize it into platform_device form
Routines under the non-device tree are ignored

Introduction to the platform driver under the device tree

The platform driver framework is divided into bus, device and driver. The bus does not need to be managed by us driver programmers.
One is provided by the Linux kernel. We only need to focus on the specific implementation of the device and the driver when writing the driver. in or not
Under the Linux kernel with a device tree , we need to write and register platform_device and platform_driver separately, representing
Table devices and drivers. When using the device tree, the description of the device is placed in the device tree, so platform_device is
We don't need to write it, we just need to implement platform_driver . When writing a platform driver based on the device tree
When moving, we need to pay attention to the following points:
1. Create a device node in the device tree
There is no doubt that you must first create a device node in the device tree to describe the device information. The key point is to set the compatible
The value of the attribute, because the platform bus needs to match the driver through the compatible attribute value of the device node! Remember this.
For example, we can write a device node as shown below to describe the LED device we will use in the experiments in this chapter :
示例代码 55.1.1 gpioled 设备节点
 gpioled {
     #address-cells = <1>;
     #size-cells = <1>;
     compatible = "atkalpha-gpioled";
     pinctrl-names = "default";
     pinctrl-0 = <&pinctrl_led>;
     led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
     status = "okay";
 };
2. Pay attention to compatibility attributes when writing platform drivers
When using the device tree, the platform driver will use of_match_table to
Save the compatibility value, which means which devices this driver is compatible with. Therefore, of_match_table will be particularly important, such as this
The platform_driver in the platform driver of the routine can be set as follows:
示例代码 55.1.2 of_match_table 匹配表的设置
 static const struct of_device_id leds_of_match[] = {
     { .compatible = "atkalpha-gpioled" }, /* 兼容属性 */
     { /* Sentinel */ }
 };
 
 MODULE_DEVICE_TABLE(of, leds_of_match); //声明一下 leds_of_match 这个设备匹配表。
 
static struct platform_driver leds_platform_driver = {
     .driver = {
         .name = "imx6ul-led",
         .of_match_table = leds_of_match,
     },
     .probe = leds_probe,
     .remove = leds_remove,
 };
Declare the device matching table leds_of_match through MODULE_DEVICE_TABLE .
3. Write platform driver
The platform driver based on the device tree is basically the same as the platform driver without the device tree in the previous chapter.
After the device is successfully matched, the probe function will be executed. We need to execute the character device driver in the probe function,
When the driver module is logged out, the remove function will be executed, all of which are similar

3.1 Hardware schematic analysis

We only use the LED light on the IMX6U-ALPHA development board for the experiment in this chapter, so refer to 8.3 for the experimental hardware schematic diagram
Just a section.
​​​​​​​

3.2  Experimental program writing

The routine path corresponding to this experiment is: development board CD -> 2 , Linux driver routine -> 18_dtsplatform .
In this chapter, we write a platform driver based on the device tree , so we need to add a device node in the device tree, and then
We only need to write the platform driver.
Lines 183 ~186 , the matching table, describes what kind of devices this driver matches. Line 184 adds a value
The compatible attribute value of "atkalpha-gpioled" , when the compatible attribute value of a device node in the device tree is also
" atkalpha-gpioled " will match this driver.
Lines 120 ~164 , the probe function of the platform driver , when the device node in the device tree matches the driver successfully
This function will be executed in the future, and all the work done in the driver loading function is now completed in the probe function.
Lines 189~196 , the platform_driver driver structure, line 191 sets the name of the platform driver to " imx6ul
led ", therefore, when the driver is successfully loaded, there will be a file named " imx6u" in the /sys/bus/platform/drivers/ directory
led " file. Line 192 sets of_match_table to the above led_of_match .
示例代码 55.3.2.1 leddriver.c 文件代码段
1 #include <linux/types.h>
2 #include <linux/kernel.h>
3 #include <linux/delay.h>
4 #include <linux/ide.h>
5 #include <linux/init.h>
6 #include <linux/module.h>
7 #include <linux/errno.h>
8 #include <linux/gpio.h>
9 #include <linux/cdev.h>
10 #include <linux/device.h>
11 #include <linux/of_gpio.h>
12 #include <linux/semaphore.h>
13 #include <linux/timer.h>
14 #include <linux/irq.h>
15 #include <linux/wait.h>
16 #include <linux/poll.h>
17 #include <linux/fs.h>
18 #include <linux/fcntl.h>
19 #include <linux/platform_device.h>
20 #include <asm/mach/map.h>
21 #include <asm/uaccess.h>
22 #include <asm/io.h>
23 /***************************************************************
24 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
25 文件名 : leddriver.c
26 作者 : 左忠凯
27 版本 : V1.0
28 描述 : 设备树下的 platform 驱动
29 其他 : 无
30 论坛 : www.openedv.com
31 日志 : 初版 V1.0 2019/8/13 左忠凯创建
32 ***************************************************************/
33 #define LEDDEV_CNT 1 /* 设备号长度 */
34 #define LEDDEV_NAME "dtsplatled" /* 设备名字 */
35 #define LEDOFF 0
36 #define LEDON 1
37 
38 /* leddev 设备结构体 */
39 struct leddev_dev{
40     dev_t devid; /* 设备号 */
41     struct cdev cdev; /* cdev */
42     struct class *class; /* 类 */
43     struct device *device; /* 设备 */
44     int major; /* 主设备号 */ 
45     struct device_node *node; /* LED 设备节点 */
46     int led0; /* LED 灯 GPIO 标号 */
47 };
48 
49 struct leddev_dev leddev; /* led 设备 */
50
51 /*
52 * @description : LED 打开/关闭
53 * @param - sta : LEDON(0) 打开 LED,LEDOFF(1) 关闭 LED
54 * @return : 无
55 */
56 void led0_switch(u8 sta)
57 {
58 if (sta == LEDON )
59     gpio_set_value(leddev.led0, 0);
60 else if (sta == LEDOFF)
61     gpio_set_value(leddev.led0, 1);
62 }
63 
64 /*
65 * @description : 打开设备
66 * @param – inode : 传递给驱动的 inode
67 * @param - filp : 设备文件,file 结构体有个叫做 private_data 的成员变量
68 * 一般在 open 的时候将 private_data 指向设备结构体。
69 * @return : 0 成功;其他 失败
70 */
71 static int led_open(struct inode *inode, struct file *filp)
72 {
73     filp->private_data = &leddev; /* 设置私有数据 */
74     return 0;
75 }
76 
77 /*
78 * @description : 向设备写数据
79 * @param - filp : 设备文件,表示打开的文件描述符
80 * @param - buf : 要写给设备写入的数据
81 * @param - cnt : 要写入的数据长度
82 * @param – offt : 相对于文件首地址的偏移
83 * @return : 写入的字节数,如果为负值,表示写入失败
84 */
85 static ssize_t led_write(struct file *filp, const char __user *buf,
    size_t cnt, loff_t *offt)
86 {
87     int retvalue;
88     unsigned char databuf[2];
89     unsigned char ledstat;
90 
91     retvalue = copy_from_user(databuf, buf, cnt);
92     if(retvalue < 0) {
93 
94         printk("kernel write failed!\r\n");
95         return -EFAULT;
96     }
97 
98     ledstat = databuf[0];
99     if (ledstat == LEDON) {
100         led0_switch(LEDON);
101     } else if (ledstat == LEDOFF) {
102         led0_switch(LEDOFF);
103     }
104     return 0;
105 }
106
107 /* 设备操作函数 */
108 static struct file_operations led_fops = {
109     .owner = THIS_MODULE,
110     .open = led_open,
111     .write = led_write,
112 };
113
114 /*
115 * @description : flatform 驱动的 probe 函数,当驱动与
116 * 设备匹配以后此函数就会执行
117 * @param - dev : platform 设备
118 * @return : 0,成功;其他负值,失败
119 */
120 static int led_probe(struct platform_device *dev)
121 { 
122     printk("led driver and device was matched!\r\n");
123     /* 1、设置设备号 */
124     if (leddev.major) {
125         leddev.devid = MKDEV(leddev.major, 0);
126         register_chrdev_region(leddev.devid, LEDDEV_CNT,LEDDEV_NAME);
127         } else {
128         alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT,LEDDEV_NAME);
129         leddev.major = MAJOR(leddev.devid);
130     }
131
132     /* 2、注册设备 */
133     cdev_init(&leddev.cdev, &led_fops);
134     cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);
135
136     /* 3、创建类 */
137     leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);
138     if (IS_ERR(leddev.class)) {
139         return PTR_ERR(leddev.class);
140     }
141
142     /* 4、创建设备 */
143     leddev.device = device_create(leddev.class, NULL, leddev.devid,NULL,LEDDEV_NAME);
144     if (IS_ERR(leddev.device)) {
145         return PTR_ERR(leddev.device);
146     }
147
148     /* 5、初始化 IO */ 
149     leddev.node = of_find_node_by_path("/gpioled");
150     if (leddev.node == NULL){
151         printk("gpioled node nost find!\r\n");
152         return -EINVAL;
153     }
154 
155     leddev.led0 = of_get_named_gpio(leddev.node, "led-gpio", 0);
156     if (leddev.led0 < 0) {
157         printk("can't get led-gpio\r\n");
158     return -EINVAL;
159      }
160
161     gpio_request(leddev.led0, "led0");
162     gpio_direction_output(leddev.led0, 1); /*设置为输出,默认高电平 */
163     return 0;
164    }
165
166 /*
167 * @description : remove 函数,移除 platform 驱动的时候此函数会执行
168 * @param - dev : platform 设备
169 * @return : 0,成功;其他负值,失败
170 */
171 static int led_remove(struct platform_device *dev)
172 {
173     gpio_set_value(leddev.led0, 1); /* 卸载驱动的时候关闭 LED */
174
175     cdev_del(&leddev.cdev); /* 删除 cdev */
176     unregister_chrdev_region(leddev.devid, LEDDEV_CNT);
177     device_destroy(leddev.class, leddev.devid);
178     class_destroy(leddev.class);
179     return 0;
180 }
181
182 /* 匹配列表 */
183 static const struct of_device_id led_of_match[] = {
184     { .compatible = "atkalpha-gpioled" },
185     { /* Sentinel */ }
186 };
187
188 /* platform 驱动结构体 */
189 static struct platform_driver led_driver = {
190     .driver = {
191         .name = "imx6ul-led", /* 驱动名字,用于和设备匹配 */
192         .of_match_table = led_of_match, /* 设备树匹配表 */
193     },
194     .probe = led_probe,
195     .remove = led_remove,
196 };
197 
198 /*
199 * @description : 驱动模块加载函数
200 * @param : 无
201 * @return : 无
202 */
203 static int __init leddriver_init(void)
204 {
205     return platform_driver_register(&led_driver);
206 }
207
208 /*
209 * @description : 驱动模块卸载函数
210 * @param : 无
211 * @return : 无
212 */
213 static void __exit leddriver_exit(void)
214 {
215     platform_driver_unregister(&led_driver);
216 }
217
218 module_init(leddriver_init);
219 module_exit(leddriver_exit);
220 MODULE_LICENSE("GPL");
221 MODULE_AUTHOR("zuozhongkai");

3.3 Write test APP

To test the APP , just use ledApp.c written in Section 54.4.2 of the previous chapter .

3.4 Running tests

Copy the compiled leddriver.ko to the rootfs/lib/modules/4.1.15 directory, restart the development board, and enter
Enter the directory lib/modules/4.1.15 , and enter the following command to load the driver module leddriver.ko .
depmod //This command needs to be run when loading the driver for the first time  Use depmod the command, which can be entered in the terminal depmod -a , which will analyze all available modules and generate a dependency graph. (you can leave it blank)
modprobe leddriver.ko // load driver module (including dependencies)
After the driver module is loaded, go to the /sys/bus/platform/drivers/ directory to check whether the driver exists. We are in
Set the name field of led_driver (platform_driver type ) in leddriver.c to " imx6ul-led ", so it will be in
There is a file named " imx6ul-led " in the /sys/bus/platform/drivers/ directory, and the result is shown in Figure 55.4.2.1 :
There are also led device files in the /sys/bus/platform/devices/ directory, that is, gpioled in the device tree
nodes, as shown in Figure 55.4.2.2 :
Both the driver and the module exist, and when the driver and the device are successfully matched, a line of statements will be output as shown in Figure 55.4.2.3
./ledApp /dev/dtsplatled 1
// Turn on the LED light
./ledApp /dev/dtsplatled 0
// Turn off the LED light
​​​​​​​
rmmod leddriver.ko

​​​​​​​

Guess you like

Origin blog.csdn.net/TyearLin/article/details/131949862
Recommended