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 beenThe 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. quiteIn 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
3 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 Usedepmod
the command, which can be entered in the terminaldepmod -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 lightrmmod leddriver.ko