Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长
学习嵌入式Linux驱动开发整整30天了。今天简单做一个小结。因为之前的主要工作是做ARM的裸机开发,所以接触Linux以后感觉很多东西都变了。不仅仅包括相关知识点的归纳,更重要的是思维方式的转变。做裸机开发的时候,在很多时候是要弄清楚最底层的逻辑才能进行代码的开发。而Linux则更像是站在一个更高的维度去思考,很细枝末叶的东西是不需要考虑的。相对裸机开发的维度来说,裸机开发后做Linux驱动开发更有点降维打击的感觉。但并不是说Linux驱动开发简单,而是说裸机开发对底层的要求可能会更高一些。以上仅仅是个人的一点自我感受。
本节笔记主要学习Linux自带的LED驱动试验。主要内容包括驱动使能、简介、设备树节点编写及运行测试。其中驱动简介又包括LED灯驱动框架分析、module_platform_driver函数简介和gpio_led_probe函数简析。
一、Linux内核自带的LED灯驱动使能
需要配置Linux内核,使能自带的LED驱动。使用make menuconfig打开Linux配置菜单。
按照如下路径打开LED驱动配置项:
Device Drivers
LED Support(NEW_LEDS=[y])
LED Support for GPIO connected LEDs. 在此选项上按下" Y"键,使此选项前面变为" <*>",
重新编译 Linux 内核,然后使用新编译出来的 zImage 镜像启动开发板。
二、Linux内核自带LED驱动简介
1、LED灯驱动框架分析
obj - $ ( CONFIG_LEDS_GPIO ) += leds - gpio . o
cpp
236 static const struct of_device_id of_gpio_leds_match[] = {
237 { .compatible = "gpio-leds", },
238 {},
239 };
LED 驱动的匹配表,此表只有一个匹配项, compatible 内容为" gpio-leds",因此设备树中的 LED 灯设备节点的 compatible 属性值也要为" gpio-leds",否则设备和驱动匹配不成功,驱动就没法工作。
cpp
static struct platform_driver gpio_led_driver = {
291 .probe = gpio_led_probe,
292 .remove = gpio_led_remove,
293 .driver = {
294 .name = "leds-gpio",
295 .of_match_table = of_gpio_leds_match,
296 },
platform_driver 驱动结构体变量,可以看出, Linux 内核自带的 LED 驱动采用了 platform 框架。
cpp
module_platform_driver(gpio_led_driver);
通过 module_platform_driver 函数向 Linux 内核注册 gpio_led_driver 这个 platform
驱动。
2、module_platform_driver函数简介
module_platform_driver 定义在 include/linux/platform_device.h 文件中为一个宏,
module_platform_driver 依赖module_driver, module_driver 也是一个宏。
module_platform_driver 函数的功能就是完成 platform 驱动的注册和删除。
3、gpio_led_probe函数简析
cpp
269 priv = gpio_leds_create(pdev);
270 if (IS_ERR(priv))
271 return PTR_ERR(priv);
如果使用设备树的话,使用 gpio_leds_create 函数从设备树中提取设备信息,获取到的 LED 灯 GPIO 信息保存在返回值中。
在gpio_leds_create 函数中,调用 device_get_child_node_count 函数统计子节点数量,一般在在设备树中创建一个节点表示 LED 灯,然后在这个节点下面为每个 LED 灯创建一个子节点。因此子节点数量也是 LED 灯的数量。
cpp
device_for_each_child_node(dev, child) {
遍历每个子节点,获取每个子节点的信息。
cpp
led.gpiod = devm_get_gpiod_from_child(dev, NULL, child);
获取 LED 灯所使用的 GPIO 信息。
cpp
196 if (fwnode_property_present(child, "label")) {
197 fwnode_property_read_string(child, "label", &led.name);
读取子节点 label 属性值,因为使用 label 属性作为 LED 的名字。
cpp
204 fwnode_property_read_string(child, "linux,default-trigger",
205 &led.default_trigger);
获取" linux,default-trigger"属性值,可以通过此属性设置某个 LED 灯在Linux 系统中的默认功能,比如作为系统心跳指示灯等等。
cpp
207 if (!fwnode_property_read_string(child, "default-state",
208 &state)) {
209 if (!strcmp(state, "keep"))
210 led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
211 else if (!strcmp(state, "on"))
212 led.default_state = LEDS_GPIO_DEFSTATE_ON;
213 else
214 led.default_state = LEDS_GPIO_DEFSTATE_OFF;
215 }
获取" default-state"属性值,也就是 LED 灯的默认状态属性。
cpp
220 ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],
221 dev, NULL);
调用 create_gpio_led 函数创建 LED 相关的 io,其实就是设置 LED 所使用的 io为输出之类的。 create_gpio_led 函数主要是初始化 led_dat这个 gpio_led_data 结构体类型变量,led_dat 保存了 LED 的操作函数等内容。
gpio_led_probe 函数主要功能就是获取 LED 灯的设备信息,然后根据这些信息来初始化对应的 IO,设置为输出等。
三、设备树节点编写
在编写设备节点的时候要注意以下几点:
①、创建一个节点表示 LED 灯设备,比如 dtsleds,如果板子上有多个 LED 灯的话每个 LED
灯都作为 dtsleds 的子节点。
②、 dtsleds 节点的 compatible 属性值一定要为" gpio-leds"。
③、设置 label 属性,此属性为可选,每个子节点都有一个 label 属性, label 属性一般表示
LED 灯的名字,比如以颜色区分的话就是 red、 green 等等。
④、每个子节点必须要设置gpios 属性值,表示此 LED 所使用的 GPIO 引脚!
⑤、可以设置" linux,default-trigger "属性值,也就是设置 LED 灯的默认功能,比如:
backlight : LED 灯作为背光。
default-on : LED 灯打开
heartbeat : LED 灯作为心跳指示灯,可以作为系统运行提示灯。
ide-disk : LED 灯作为硬盘活动指示灯。
timer : LED 灯周期性闪烁,由定时器驱动,闪烁频率可以修改
⑥、可以设置" default-state "属性值,可以设置为 on、 off 或 keep,为 on 的时候 LED 灯默
认打开,为 off 的话 LED 灯默认关闭,为 keep 的话 LED 灯保持当前模式。
cpp
1 dtsleds {
2 compatible = "gpio-leds";
3 4
led0 {
5 label = "red";
6 gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
7 default-state = "off";
8 };
9 };
四、运行测试
启 动 开 发 板 , 启 动 以 后 查 看/sys/bus/platform/devices/dtsleds 这个目录是否存在。
进入到 leds 目录中,此目录中的内容如图所示:
在 leds 目录下有一个名为" red"子目录,这个子目录的名字就是我们在设备树中第 5 行设置的 label属性值。
输入如下命令打开 RED 这个 LED 灯:
echo 1 > /sys/class/leds/red/brightness //打开 LED0
关闭 RED 这个 LED 灯的命令如下:
echo 0 > /sys/class/leds/red/brightness //关闭 LED0
系统运行指示灯:
cpp
1 dtsleds {
2 compatible = "gpio-leds";
3 4
led0 {
5 label = "red";
6 gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
7 linux,default-trigger = "heartbeat";
8 default-state = "on";
9 };
10 };
设置 LED0 作为系统指示灯,在 dtsleds 这个设备节点中加入" linux,default-trigge r"属性信息即可,属性值为" heartbeat"。
五、总结
本节笔记主要内容包括驱动使能、简介、设备树节点编写及运行测试。其中驱动简介又包括LED灯驱动框架分析、module_platform_driver函数简介和gpio_led_probe函数简析。
本文为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。