目录

  • Linux 内核自带LED 驱动使能
  • Linux 内核自带LED 驱动简介
    • LED 灯驱动框架分析
    • module_platform_driver 函数简析
    • gpio_led_probe 函数简析
  • 设备树节点编写
  • 运行测试

前面我们都是自己编写LED 灯驱动,其实像LED 灯这样非常基础的设备驱动,Linux 内核已经集成了。Linux 内核的LED 灯驱动采用platform 框架,因此我们只需要按照要求在设备树文件中添加相应的LED 节点即可,本章我们就来学习如何使用Linux 内核自带的LED 驱动来驱动I.MX6U-ALPHA 开发板上的LED0。

Linux 内核自带LED 驱动使能

上一章节我们编写基于设备树的platform LED 灯驱动,其实Linux 内核已经自带了LED 灯
驱动,要使用Linux 内核自带的LED 灯驱动首先得先配置Linux 内核,使能自带的LED 灯驱
动,输入如下命令打开Linux 配置菜单:

make menuconfig

按照如下路径打开LED 驱动配置项:

-> Device Drivers-> LED Support (NEW_LEDS [=y])->LED Support for GPIO connected LEDs

按照上述路径,选择“LED Support for GPIO connected LEDs”,将其编译进Linux 内核,也即是在此选项上按下“Y”键,使此选项前面变为“<*>”,如图56.1.1 所示:

在“LED Support for GPIO connected LEDs”上按下‘?’可以打开此选项的帮助信息,如图56.1.2 所示:

从图56.1.2 可以看出,把Linux 内部自带的LED 灯驱动编译进内核以后,
CONFIG_LEDS_GPIO 就会等于‘y’,Linux 会根据CONFIG_LEDS_GPIO 的值来选择如何编译LED 灯驱动,如果为‘y’就将其编译进Linux 内核。

配置好Linux 内核以后退出配置界面,打开.config 文件,会找到“CONFIG_LEDS_GPIO=y”这一行,如图56.1.3 所示:

重新编译Linux 内核,然后使用新编译出来的zImage 镜像启动开发板。

Linux 内核自带LED 驱动简介

LED 灯驱动框架分析

LED 灯驱动文件为/drivers/leds/leds-gpio.c,大家可以打开/drivers/leds/Makefile 这个文件,找到如下所示内容:

2 # LED Core
3 obj-$(CONFIG_NEW_LEDS) += led-core.o
.....
23 obj-$(CONFIG_LEDS_GPIO_REGISTER) += leds-gpio-register.o
24 obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
25 obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o
......

第24 行,如果定义了CONFIG_LEDS_GPIO 的话就会编译leds-gpio.c 这个文件,在上一小节我们选择将LED 驱动编译进Linux 内核,在.config 文件中就会有“CONFIG_LEDS_GPIO=y”
这一行,因此leds-gpio.c 驱动文件就会被编译。
接下来我们看一下leds-gpio.c 这个驱动文件,找到如下所示内容:

236 static const struct of_device_id of_gpio_leds_match[] = {237 { .compatible = "gpio-leds", },
238 {},
239 };
......
290 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 },
297 };
298
299 module_platform_driver(gpio_led_driver);

第236~239 行,LED 驱动的匹配表,此表只有一个匹配项,compatible 内容为“gpio-leds”,因此设备树中的LED 灯设备节点的compatible 属性值也要为“gpio-leds”,否则设备和驱动匹配不成功,驱动就没法工作。

第290~296 行,platform_driver 驱动结构体变量,可以看出,Linux 内核自带的LED 驱动采用了platform 框架。第291 行可以看出probe 函数为gpio_led_probe,因此当驱动和设备匹配成功以后gpio_led_probe 函数就会执行。从294 行可以看出,驱动名字为“leds-gpio”,因此会在/sys/bus/platform/drivers 目录下存在一个名为“leds-gpio”的文件,如图56.2.1.1 所示:

第299 行通过module_platform_driver 函数向Linux 内核注册gpio_led_driver 这个platform驱动。

module_platform_driver 函数简析

在上一小节中我们知道LED 驱动会采用module_platform_driver 函数向Linux 内核注册platform 驱动,其实在Linux 内核中会大量采用module_platform_driver 来完成向Linux 内核注册platform 驱动的操作。module_platform_driver 定义在include/linux/platform_device.h 文件中,
为一个宏,定义如下:

221 #define module_platform_driver(__platform_driver) \
222 module_driver(__platform_driver, platform_driver_register, \
223 platform_driver_unregister)

可以看出,module_platform_driver 依赖module_driver,module_driver 也是一个宏,定义在include/linux/device.h 文件中,内容如下:

1260 #define module_driver(__driver, __register, __unregister, ...) \
1261 static int __init __driver##_init(void) \
1262 { \
1263 return __register(&(__driver) , ##__VA_ARGS__); \
1264 } \
1265 module_init(__driver##_init); \
1266 static void __exit __driver##_exit(void) \
1267 { \
1268 __unregister(&(__driver) , ##__VA_ARGS__); \
1269 } \
1270 module_exit(__driver##_exit);

借助示例代码56.2.2.1 和示例代码56.2.2.2,将:

module_platform_driver(gpio_led_driver)

展开以后就是:

static int __init gpio_led_driver_init(void)
{return platform_driver_register (&(gpio_led_driver));
}
module_init(gpio_led_driver_init);
static void __exit gpio_led_driver_exit(void)
{platform_driver_unregister (&(gpio_led_driver) );
}
module_exit(gpio_led_driver_exit);

上面的代码不就是标准的注册和删除platform 驱动吗?因此module_platform_driver 函数的功能就是完成platform 驱动的注册和删除。

gpio_led_probe 函数简析

当驱动和设备匹配以后gpio_led_probe 函数就会执行,此函数主要是从设备树中获取LED灯的GPIO 信息,缩减后的函数内容如下所示:

243 static int gpio_led_probe(struct platform_device *pdev)
244 {245 struct gpio_led_platform_data *pdata =
dev_get_platdata(&pdev->dev);
246 struct gpio_leds_priv *priv;
247 int i, ret = 0;
248
249 if (pdata && pdata->num_leds) { /* 非设备树方式*/
/* 获取platform_device信息*/
......
268 } else { /* 采用设备树*/
269 priv = gpio_leds_create(pdev);
270 if (IS_ERR(priv))
271 return PTR_ERR(priv);
272 }
273
274 platform_set_drvdata(pdev, priv);
275
276 return 0;
277 }

第269~271 行,如果使用设备树的话,使用gpio_leds_create 函数从设备树中提取设备信息,获取到的LED 灯GPIO 信息保存在返回值中,gpio_leds_create 函数内容如下:

167 static struct gpio_leds_priv *gpio_leds_create(struct
platform_device *pdev)
168 {169 struct device *dev = &pdev->dev;
170 struct fwnode_handle *child;
171 struct gpio_leds_priv *priv;
172 int count, ret;
173 struct device_node *np;
174
175 count = device_get_child_node_count(dev);
176 if (!count)
177 return ERR_PTR(-ENODEV);
178
179 priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count),
GFP_KERNEL);
180 if (!priv)
181 return ERR_PTR(-ENOMEM);
182
183 device_for_each_child_node(dev, child) {184 struct gpio_led led = {};
185 const char *state = NULL;
186
187 led.gpiod = devm_get_gpiod_from_child(dev, NULL, child);
188 if (IS_ERR(led.gpiod)) {189 fwnode_handle_put(child);
190 ret = PTR_ERR(led.gpiod);
191 goto err;
192 }
193
194 np = of_node(child);
195
196 if (fwnode_property_present(child, "label")) {197 fwnode_property_read_string(child, "label", &led.name);
198 } else {199 if (IS_ENABLED(CONFIG_OF) && !led.name && np)
200 led.name = np->name;
201 if (!led.name)
202 return ERR_PTR(-EINVAL);
203 }

第175 行,调用device_get_child_node_count 函数统计子节点数量,一般在在设备树中创建一个节点表示LED 灯,然后在这个节点下面为每个LED 灯创建一个子节点。因此子节点数量也是LED 灯的数量。
第183 行,遍历每个子节点,获取每个子节点的信息。
第187 行,获取LED 灯所使用的GPIO 信息。
第196~197 行,读取子节点label 属性值,因为使用label 属性作为LED 的名字。
第204~205 行,获取“linux,default-trigger”属性值,可以通过此属性设置某个LED 灯在Linux 系统中的默认功能,比如作为系统心跳指示灯等等。
第207~215 行,获取“default-state”属性值,也就是LED 灯的默认状态属性。
第220 行,调用create_gpio_led 函数创建LED 相关的io,其实就是设置LED 所使用的io为输出之类的。create_gpio_led 函数主要是初始化led_dat 这个gpio_led_data 结构体类型变量,led_dat 保存了LED 的操作函数等内容。

关于gpio_led_probe 函数就分析到这里,gpio_led_probe 函数主要功能就是获取LED 灯的设备信息,然后根据这些信息来初始化对应的IO,设置为输出等。

设备树节点编写

打开文档Documentation/devicetree/bindings/leds/leds-gpio.txt,此文档详细的讲解了Linux 自带驱动对应的设备树节点该如何编写,我们在编写设备节点的时候要注意以下几点:
①、创建一个节点表示LED 灯设备,比如dtsleds,如果板子上有多个LED 灯的话每个LED灯都作为dtsleds 的子节点。
②、dtsleds 节点的compatible 属性值一定要为“gpio-leds”。
③、设置label 属性,此属性为可选,每个子节点都有一个label 属性,label 属性一般表示LED 灯的名字,比如以颜色区分的话就是red、green 等等。
④、每个子节点必须要设置gpios 属性值,表示此LED 所使用的GPIO 引脚!
⑤、可以设置“linux,default-trigger”属性值,也就是设置LED 灯的默认功能,可以查阅Documentation/devicetree/bindings/leds/common.txt 这个文档来查看可选功能,比如:
backlight:LED 灯作为背光。
default-on:LED 灯打开
heartbeat:LED 灯作为心跳指示灯,可以作为系统运行提示灯。
ide-disk:LED 灯作为硬盘活动指示灯。
timer:LED 灯周期性闪烁,由定时器驱动,闪烁频率可以修改
⑥、可以设置“default-state”属性值,可以设置为on、off 或keep,为on 的时候LED 灯默认打开,为off 的话LED 灯默认关闭,为keep 的话LED 灯保持当前模式。
根据上述几条要求在imx6ull-alientek-emmc.dts 中添加如下所示LED 灯设备节点:

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 };

因为I.MX6U-ALPHA 开发板只有一个LED0,因此在dtsleds 这个节点下只有一个子节点led0,LED0 名字为red,默认关闭。修改完成以后保存并重新编译设备树,然后用新的设备树启动开发板。

运行测试

用新的zImage 和imx6ull-alientek-emmc.dtb 启动开发板,启动以后查看
/sys/bus/platform/devices/dtsleds 这个目录是否存在,如果存在的话就如到此目录中,如图56.4.1所示:

进入到leds 目录中,此目录中的内容如图56.4.2 所示:

从图56.4.2 可以看出,在leds 目录下有一个名为“red”子目录,这个子目录的名字就是我们在设备树中第5 行设置的label 属性值。
我们的设置究竟有没有用,最终是要通过测试才能知道的,首先查看一下系统中有没有“sys/class/leds/red/brightness”这个文件,如果有的话就输入如下命令打开RED 这个LED 灯:

echo 1 > /sys/class/leds/red/brightness //打开LED0

关闭RED 这个LED 灯的命令如下:

echo 0 > /sys/class/leds/red/brightness //关闭LED0

如果能正常的打开和关闭LED 灯话就说明我们Linux 内核自带的LED 灯驱动工作正常。
我们一般会使用一个LED 灯作为系统指示灯,系统运行正常的话这个LED 指示灯就会一闪一闪的。里我们设置LED0 作为系统指示灯,在dtsleds 这个设备节点中加入“linux,default-trigger”属性信息即可,属性值为“heartbeat”,修改完以后的dtsleds 节点内容如下:

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 };

第7 行,设置LED0 为heartbeat。
第8 行,默认打开LED0。
重新编译设备树并且使用新的设备树启动Linux 系统,启动以后LED0 就会闪烁,作为系统心跳指示灯,表示系统正在运行。

Linux 自带的LED 灯驱动实验相关推荐

  1. Linux 自带的 LED 灯驱动实验

    目录 一.配置内核 二.设备树节点编写 1.确定compatible 属性值 2.编写节点 三.验证 测试 其实 Linux 内核已经自带了 LED 灯驱动,要使用 Linux 内核自带的 LED 灯 ...

  2. linux自带的LED灯驱动实验

    文章目录 一.linux内核自带LED驱动使能 二.linux内核自带LED驱动简介 1.LED灯驱动框架分析 2.module_platform_driver函数解析 3.gpio_led_prob ...

  3. STM32MP157驱动开发——Linux自带的LED灯驱动

    STM32MP157驱动开发--Linux自带的LED灯驱动 0.前言 一.Linux 内核自带 LED 驱动使能 二.驱动简介 1.LED灯驱动框架分析 2.module_platform_driv ...

  4. Linux 自带的 LED 灯驱动

    1 Linux 内核自带 LED 驱动使能 Linux 内核已经自带了 LED 灯驱动,要使用 Linux 内核自带的 LED 灯驱动首先得先配置 Linux 内核,使能自带的 LED 灯驱动,输入如 ...

  5. Linux 内核自带的 LED 灯驱动

    系列文章 I.MX6ULL 手册查找使用方法 实战点亮LED(寄存器版) I.MX6ULL 手册查找使用方法 实战点亮LED(固件库版本) linux 字符设备驱动实战 linux LED设备驱动文件 ...

  6. ARM裸机开发——Linux环境搭建和LED灯闪烁实验

    写在前面  本文为学校开展的嵌入式系统设计课程,本文主要是记录课程中的相关作业与学习记录,本课程采用了S3C2440A嵌入式系统开发板,由于主要以学校开展课程为主,本文内容可能有一定的课程资料辅助以及 ...

  7. 2022-10-20 Linux自带LED灯驱动gpio-leds 心跳呼吸灯heartbeat、定时器timer的使用

    一.测试环境:Android 9 系统 二.驱动文件在\kernel\drivers\leds\leds-gpio.c 三.dts的官方配置文档 kernel\Documentation\device ...

  8. Linux驱动开发学习笔记【12】:Linux自带LED灯驱动

    目录 一.内核自带LED驱动使能 二.内核自带LED驱动分析 三.内核自带LED驱动使用 一.内核自带LED驱动使能 在Linux内核中,已经自带了LED灯的驱动程序,使用的就是platform平台驱 ...

  9. Linux 设备树下的 platform 驱动实验基于正点原子IMX6ULL开发板

    1 设备树下的 platform 驱动简介 platform 驱动框架分为总线.设备和驱动,其中总线不需要我们这些驱动程序员去管理,这个是 Linux 内核提供的,我们在编写驱动的时候只要关注于设备和 ...

最新文章

  1. 转乱码UTF8和UTF-8网页编码
  2. 交叉熵比平方损失函数的优点
  3. css选择器(css Selectors)的语法分析
  4. 湖南省第6届程序大赛第3题 数字整除
  5. 影响数千万APP的安卓APP“寄生兽”漏洞技术分析
  6. 网页爬虫python代码_《用python写网络爬虫》完整版+源码
  7. Google提出的新型激活函数:Swish
  8. 人工智能云计算大数据物联网
  9. springboot的war和jar包
  10. idea创建springboot项目+mybatis_Spring Boot + MyBatis 多模块项目搭建教程
  11. python画一条水平直线(matplotlib)
  12. 二级缓存失效_缓存核心技术:缓存穿透、缓存并发、缓存失效之思路变迁
  13. C# webservice调用方法总结
  14. paip.提升性能---jvm java 工具使用.
  15. 2012年波兰乌克兰欧洲杯完全赛程(06.09-07.02)
  16. 解决Linux内核问题实用技巧之-dev/mem的新玩法
  17. linux如何使用ntfs格式的优盘
  18. cin gt gt n是c语言中的什么,c++中cinna是什么意思
  19. C++ 数组array与vector的比较
  20. 香鸡排三部曲:完结篇

热门文章

  1. 【设计模式】单例模式 Singleton Pattern
  2. IBM TS3200 Drive故障处理方案
  3. 移动web开发框架之我见
  4. matlab常用函数——软件常用函数
  5. 华为服务器故障灯不开机_华为服务器日常维护及故障处理介绍V.ppt
  6. C++的字符串分割函数
  7. matlab emf 读取,20140219-Emf_Demo EMF 矢量图 可以读取和保存EMF 的封闭类 非常实用 matlab 238万源代码下载- www.pudn.com...
  8. k8s部署tomcat及web应用_k8s部署tomcat的yaml文件
  9. 如何学习streamdecoder类_2019年终巨献:一份拿下了阿里、网易、滴滴等大厂offer的学习笔记...
  10. markdown 语法_Markdown 基本语法