Linux学习第29天:设备树下的 platform 驱动编写:举步维艰,那就脚踏实地,一步一个脚印,慢慢来。

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长


同志们,学习到现在,是不是有点懵逼的感觉。感觉该学的都学了,又感觉啥都没学会。就这样懵逼的状态进入到下一节的学习。这就是题目中我讲的举步维艰的状态了吧。起码这是我学习到现在的一个状态吧。但是既然下决心要攻克它,就不要轻言放弃。因为黎明往往就在黑夜的后面。这也是学习一种技能必须要经历的一个过程。脚底实地,按照计划,一节一节来,争取尽可能多的吸收每天学到的新的知识点。就这样一步一个脚印的走下去,不急躁,不冒进,相信终会有柳暗花明的一天。一起加油,同志们!!!

本篇笔记主要学习设备树下platform驱动开发相关知识,主要包括相关的基础知识、硬件原理图以及驱动开发的过程。其中驱动开发的过程有设备树的修改、驱动开发及APP开发、编译及运行测试。其中驱动开发作为本节的重点内容。

一、设备树下的platform驱动简介

编写设备树下的platform驱动时应注意的问题:

1.在设备树中创建设备节点

设置好compatible属性的值。

2.注意兼容属性

关键字: of_match_table

cpp 复制代码
1 static const struct of_device_id leds_of_match[] = {
2 { .compatible = "atkalpha-gpioled" }, /* 兼容属性 */
3 { /* Sentinel */ }
4 };
5
6 MODULE_DEVICE_TABLE(of, leds_of_match);
7
8 static struct platform_driver leds_platform_driver = {
9 .driver = {
10 .name = "imx6ul-led",
11 .of_match_table = leds_of_match,
12 },

第 1~4 行,of_device_id表,也就是驱动的兼容表,是一个数组,每个数组元素为 of_device_id类型。每个数组元素都是一个兼容属性,表示兼容的设备,一个驱动可以跟多个设备匹配。

第 3 行是一个空元素,在编写 of_device_id 的时候最后一个元素一定要为空

第 6 行, 通过MODULE_DEVICE_TABLE 声明一下 leds_of_match 这个设备匹配表。

第 11 行,设置 platform_driver 中的 of_match_table 匹配表为上面创建的 leds_of_match。

3.编写platform驱动

二、硬件原理图分析

三、试验程序编写

1.修改设备树文件

cpp 复制代码
1 pinctrl_led: ledgrp {
2 fsl,pins = <
3 MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0 /* LED0 */
4 >;
5 };

2.platform驱动程序编写

cpp 复制代码
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 }

platform 驱动的 probe函数,当设备树中的设备节点与驱动之间匹配成功以后此函数就会执行,原来在驱动加载函数里面做的工作现在全部放到 probe 函数里面完成。

cpp 复制代码
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 */

remove 函数,当卸载 platform 驱动的时候此函数就会执行。在此函数里面

释放内存、注销字符设备等,也就是将原来驱动卸载函数里面的工作全部都放到 remove 函数中

完成。

cpp 复制代码
182 /* 匹配列表 */
183 static const struct of_device_id led_of_match[] = {
184 { .compatible = "atkalpha-gpioled" },
185 { /* Sentinel */ }
186 };

匹配表

cpp 复制代码
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 };

platform_driver 驱动结构体, 191 行设置这个 platform 驱动的名字为" imx6ulled",因此,当驱动加载成功以后就会在/sys/bus/platform/drivers/目录下存在一个名为" imx6uled"的文件。 第 192 行设置 of_match_table 为上面的 led_of_match。

cpp 复制代码
198 /*
199 * @description : 驱动模块加载函数
200 * @param : 无
201 * @return : 无
202 */
203 static int __init leddriver_init(void)
204 {
205 return platform_driver_register(&led_driver);
206 }

驱动模块加载函数,在此函数里面通过 platform_driver_register 向 Linux 内

核注册 led_driver 驱动。

cpp 复制代码
208 /*
209 * @description : 驱动模块卸载函数
210 * @param : 无
211 * @return : 无
212 */
213 static void __exit leddriver_exit(void)
214 {
215 platform_driver_unregister(&led_driver);
216 }
217

驱动模块卸载函数,在此函数里面通过platform_driver_unregister从 Linux内核卸载 led_driver 驱动。

3.编写测试APP

和上次使用同样的即可。

四、运行测试

1.编译驱动程序和测试APP

cpp 复制代码
obj-m := leddriver.o

输入如下命令编译出驱动模块文件:
make -j32

编译成功以后就会生成一个名为" leddriver.o"的驱动模块文件。

测试 APP 直接使用上一章的 ledApp 这个测试软件即可。

2.运行测试

cpp 复制代码
depmod //第一次加载驱动的时候需要运行此命令
modprobe leddriver.ko //加载驱动模块

驱动模块加载完成以后到/sys/bus/platform/drivers/目录下查看驱动是否存在,我们在leddriver.c 中设置 led_driver (platform_driver 类型)的 name 字段为" imx6ul-led",因此会在

/sys/bus/platform/drivers/目录下存在名为" imx6ul-led"这个文件,结果如图 所示:

同理,在/sys/bus/platform/devices/目录下也存在 led 的设备文件,也就是设备树中 gpioled 这个节点,如图 所示:

驱动和模块都存在,当驱动和设备匹配成功以后就会输出如图所示一行语句:

驱动和设备匹配成功以后就可以测试 LED 灯驱动了,输入如下命令打开 LED 灯:
./ledApp /dev/dtsplatled 1 //打开 LED 灯

在输入如下命令关闭 LED 灯:
./ledApp /dev/dtsplatled 0 //关闭 LED 灯

观察一下 LED 灯能否打开和关闭,如果可以的话就说明驱动工作正常,如果要卸载驱动的

话输入如下命令即可:
rmmod leddriver.ko

五、总结

本篇笔记主要学习设备树下platform驱动开发相关知识,主要包括相关的基础知识、硬件原理图以及驱动开发的过程。其中驱动开发作为本节的重点内容。


本文为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。

相关推荐
周周记笔记1 分钟前
【元器件专题】用阻抗等效分析法来分析开关电路
单片机·嵌入式硬件
printfLILEI8 分钟前
php中的类与对象以及反序列化
linux·开发语言·php
leoFY12313 分钟前
STM32H750配置LAN PHY芯片LAN8742
网络·stm32·嵌入式硬件
NagatoYukee14 分钟前
Spring Security基础部分学习
java·学习·spring
iCxhust14 分钟前
如何利用iret修改cs ip
汇编·单片机·嵌入式硬件·微机原理·8088单板机
米小葱22 分钟前
【学习笔记】cmake
笔记·学习
叠叠乐1 小时前
redmi k90 pro max 强解BL,刷海外rom, 并刷入sukisu ultra
linux
m0_377108141 小时前
stm32平衡车
stm32·单片机·嵌入式硬件
辰海Coding2 小时前
MiniSpring框架学习-分解 Dispatcher
java·学习·spring·架构
初夏睡觉2 小时前
数据结构学习之~二叉堆 (P3378 【模版】堆)
数据结构·c++·学习