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

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


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

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

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

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

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

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

        设置好compatible属性的值。

2.注意兼容属性

       关键字: of_match_table

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驱动

二、硬件原理图分析

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

三、试验程序编写

1.修改设备树文件

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

2.platform驱动程序编写

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 函数里面完成。

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 函数中
完成。

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

匹配表
 

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。

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 驱动。

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

obj-m := leddriver.o

输入如下命令编译出驱动模块文件:
make -j32
编译成功以后就会生成一个名为“ leddriver.o”的驱动模块文件。

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

2.运行测试

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驱动开发相关知识,主要包括相关的基础知识、硬件原理图以及驱动开发的过程。其中驱动开发作为本节的重点内容。


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

你可能感兴趣的:(嵌入式Linux驱动开发学习,linux,学习,arm开发,嵌入式硬件,arm)