本帖最后由 robe.zhang 于 2021-6-1 10:02 编辑
【ALINXAXU2CGB试用】LED 小灯 linux 驱动源码分析
LED linux 驱动源码位于 drivers\leds目录中,跳了核心层和部分trigger代码分析以下: led-core.c #核心层 led-class.c #类注册 led-class-flash.c # 调用核心层实现的 led flash部分,源码用的比较少 led-triggers.c # triger 的 api uleds.c #用户空间部分 trigger/ #led 触发方式 ledtrig-heartbeat.c # timer phase 实现的 ledtrig-timer.c # software blink 实现,timer 实现 ledtrig-pattern.c # timer 实现,patten保存整个数组,一个接着一个执行
看 led-core.c 文件 EXPORT_SYMBOL_GPL(led_compose_name); # 打印名字,其中调用了led_parse_fwnode_props函数 led_parse_fwnode_props(dev, fwnode, &props); # 解析设备树 EXPORT_SYMBOL_GPL(led_sysfs_enable); EXPORT_SYMBOL_GPL(led_sysfs_disable); #开关 sysfs EXPORT_SYMBOL_GPL(led_get_default_pattern); # 解析 pattern属性 EXPORT_SYMBOL_GPL(led_update_brightness); # 更新 brightness值 EXPORT_SYMBOL_GPL(led_set_brightness_sync); EXPORT_SYMBOL_GPL(led_set_brightness_nosleep); EXPORT_SYMBOL_GPL(led_set_brightness_nopm); EXPORT_SYMBOL_GPL(led_set_brightness); # 各种方式设置 brightness,这里就是具体实现,先看同步开关 LED 的代码: 设置 brightness,调用 __led_set_brightness_blocking,继续追踪: __led_set_brightness_blocking 掉用了 led_cdev 的一个函数 看 led_cdev 结构体第 97 行,就是这个函数开关LED 小灯的,先标记为robe_1 函数,稍后能看到这个函数的具体实现 EXPORT_SYMBOL_GPL(led_stop_software_blink); # 软件实现的一个 blink功能 EXPORT_SYMBOL_GPL(led_blink_set); 看 software_blink 实现,设置 delay_on, delay_off 两个时间,然后使用timer 实现,详细代码稍后能看到实现,此处标记robe_2 代码 EXPORT_SYMBOL_GPL(led_blink_set_oneshot); # 软实现的 oneshot功能
EXPORT_SYMBOL_GPL(led_init_core); # 核心初始化部分,这个函数有必要看一下 初始化了一个 work 一个 timer Work 中根据flags 实现点亮或者闪灯: Timer 中检查设置 flags,设置时间,设置下一次timer,一次接着一次不停的闪闪闪就是这样实现的。这也就是 robe_2 代码的具体实现之一 再看led-class.c 代码: 创建了一个类,实现了 pm 电源管理,dev_groups: EXPORT_SYMBOL_GPL(devm_led_classdev_unregister); EXPORT_SYMBOL_GPL(devm_led_classdev_register_ext); EXPORT_SYMBOL_GPL(led_classdev_unregister); EXPORT_SYMBOL_GPL(led_classdev_register_ext); # 注册,注销 led_classdev EXPORT_SYMBOL_GPL(led_classdev_resume); EXPORT_SYMBOL_GPL(led_classdev_suspend); EXPORT_SYMBOL_GPL(led_classdev_notify_brightness_hw_changed); # 电源管理 dev_groups
看一个重点函数 led_classdev_register_ext ,273行创建led_class 类的dev,313行初始化 timer 和 delay_work,316行led_trigger_set_default,注册过程就做了这三件事。led_trigger_set_default 函数记作 robe_3 代码,看完trigger 时候具体分析看他做了什么
再看 led-triggers.c 代码: EXPORT_SYMBOL_GPL(led_trigger_unregister_simple); EXPORT_SYMBOL_GPL(led_trigger_register_simple); # 注册注销 EXPORT_SYMBOL_GPL(led_trigger_blink_oneshot); EXPORT_SYMBOL_GPL(led_trigger_blink); EXPORT_SYMBOL_GPL(led_trigger_event); # blink 方式,event EXPORT_SYMBOL_GPL(devm_led_trigger_register); EXPORT_SYMBOL_GPL(led_trigger_unregister); EXPORT_SYMBOL_GPL(led_trigger_register); # 注册注销 EXPORT_SYMBOL_GPL(led_trigger_rename_static); EXPORT_SYMBOL_GPL(led_trigger_set_default); EXPORT_SYMBOL_GPL(led_trigger_remove); EXPORT_SYMBOL_GPL(led_trigger_set); # 设置 tigger 方式 EXPORT_SYMBOL_GPL(led_trigger_show); EXPORT_SYMBOL_GPL(led_trigger_store); 看 led_trigger_register,243行添加tigger,253 行遍历led_dev 找到匹配的trigger,设置trigger 看 trigger 是如何设置的,led有 trigger 删掉,没有添加,调用 trig->activate(led_cdev),给系统发事件。这个trig->activate(led_cdev) 记作robe_4 代码,稍后看具体实现 再看 led_trigger_set_default,也就是 robe_3 代码:调用了led_trigger_set,回到了上面,不在赘述 看 trigger\ledtrig-timer.c 源码: 注册 timer_led_trigger,内部的 timer_trig_activate 函数就是 robe_4 代码的具体实现: 看这个函数:pattern 初始化,冲刷 work,设置 blink 调用 led_blink_setup led_blink_setup 调用 led_set_software_blink实现,上面说过。 最后看leds-gpio.c 源码:也就是 leds-gpio 驱动: Probe代码:逐个 led 解析后创建注册led_dev 设备,详细看278行的函数 278行函数:84行,86行,就是 brightness_set 和 brightness_set_blocking 函数,也就是robe_1 函数。一堆初始化,然后devm_led_classdev_register 注册 led_classdev, 注册函数层层调用,最后调用了 led_classdev_register_ext,这个函数上面说过。 Trigger active 之后,led小灯就开始blink 了.
LED小灯驱动框架和驱动全部分析完了,led 小灯的工作过程,大概就这个情况,源码分析的比较笼统,细节可能不是很精确,但是不影响理解驱动框架,理解到这个程度能称的上骨灰级用户,碰到所有 led 小灯驱动问题都可以得心应手从容应对,稍后适配板子上的四个LED小灯
|