LED/GPIO/pinctrl子系统(1):基于GPIO的LED子系统概述
关键词:led、gpio、led trigger(heartbeat/netdev/timer)等等。
本系列文档以LED为起点,结合GPIO LED首先了解LED子系统;然后梳理背后的GPIO子系统;进而梳理GPIO子系统背后的pinctrl子系统。
LED子系统为设备提供指示灯、闪光灯等功能,对内核或用户程序运行状态提供指示信息。
下面分别从LED子系统框架、如何配置LED子系统、配置dts、相关sysfs,并以GPIO LED为例了解驱动和测试程序。
1 Linux LED子系统框架和API
1.1 LED子系统框架
如下图,基本将LED子系统分为4大部分,由底层到上层依次是:
- 硬件层:输出高低电平的GPIO,以及对应的LED灯。
- LED驱动:配置相关驱动芯片或者GPIO等,并将设备注册到platform bus等创建设备。
- LED核心层:处理用户空间请求,并将请求通过LED驱动注册的接口写入外设中。
- 用户层:通过对LED sysfs操作,达到开关、Trigger配置、亮度设置等。
1.2 LED子系统API
LED子系统对外提供的API主要分为两部分:
- LED驱动使用的注册注销等API。
- 创建LED Trigger的API。
struct led_classdev表示注册的一个LED设备,包含了对设备设置接口函数、设备属性、对应的Trigger、属性节点等:
struct led_classdev { const char *name; enum led_brightness brightness;--当前光强。 enum led_brightness max_brightness;--最大光强。 int flags; unsigned long work_flags; void (*brightness_set)(struct led_classdev *led_cdev, enum led_brightness brightness);--光强设置接口。 int (*brightness_set_blocking)(struct led_classdev *led_cdev, enum led_brightness brightness);--非阻塞式光强设置接口。 enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);--获取光强。 int (*blink_set)(struct led_classdev *led_cdev, unsigned long *delay_on, unsigned long *delay_off);--闪烁接口。 int (*pattern_set)(struct led_classdev *led_cdev, struct led_pattern *pattern, u32 len, int repeat);--LED模式设置。 int (*pattern_clear)(struct led_classdev *led_cdev); ... #ifdef CONFIG_LEDS_TRIGGERS ... struct led_trigger *trigger;--当前LED的Trigger。 ... #endif };
填充好struct led_classdev后,即可通过如下函数对LED注册和注销:
extern int led_classdev_register_ext(struct device *parent, struct led_classdev *led_cdev, struct led_init_data *init_data); static inline int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) { return led_classdev_register_ext(parent, led_cdev, NULL); } extern int devm_led_classdev_register_ext(struct device *parent, struct led_classdev *led_cdev, struct led_init_data *init_data); static inline int devm_led_classdev_register(struct device *parent, struct led_classdev *led_cdev) { return devm_led_classdev_register_ext(parent, led_cdev, NULL); } extern void led_classdev_unregister(struct led_classdev *led_cdev); extern void devm_led_classdev_unregister(struct device *parent, struct led_classdev *led_cdev);
struct led_trigger表示一个LED Trigger,通过如下函数可以注册和注销LED Trigger:
extern int led_trigger_register(struct led_trigger *trigger); extern void led_trigger_unregister(struct led_trigger *trigger); extern int devm_led_trigger_register(struct device *dev, struct led_trigger *trigger); extern void led_trigger_register_simple(const char *name, struct led_trigger **trigger); extern void led_trigger_unregister_simple(struct led_trigger *trigger);
2 LED menuconfig配置
对LED的配置,主要是配置LED驱动和需要支持的Trigger类型:
Device Drivers ->LED Support ->LED Class Support ->LED Support for GPIO connected LEDs--支持GPIO类型LED控制。 ->LED Trigger support--LED class所支持的Trigger类型,通过sysfs可以将内核时间通过LED表示。 ->LED Timer Trigger--定义off/on时间,周期性显示。 ->LED One-shot Trigger ->LED Disk Trigger--磁盘活动指示灯。 ->LED MTD (NAND/NOR) Trigger--NAND Flash活动指示灯。 ->LED Heartbeat Trigger--模拟心跳效果,LED闪烁或呼吸。 ->LED backlight Trigger ->LED CPU Trigger--根据CPU活动显示LED。 ->LED activity Trigger ->LED GPIO Trigger ->LED Default ON Trigger ->*** iptables trigger is under Netfilter config (LED target) ->LED Transient Trigger ->LED Camera Flash/Torch Trigger ->LED Panic Trigger ->LED Netdev Trigger ->LED Pattern Trigger--允许自定义LED显示模式。 ->Audio Mute LED Trigger
LED子系统涉及到的主要文件有:
drivers/leds/├── led-class.c--创建led_class类,并且定义LED通用属性brightness/max_brightness/trigger/brightness_hw_changed,以及LED设备的注册注销函数。 ├── led-core.c--LED核心,包括基本功能初始化、设备管理、闪烁、亮度等设置。 ├── leds-gpio.c--基于GPIO实现的LED驱动。 ├── leds.h--LED对外头文件。 ├── led-triggers.c--针对LED Trigger抽象出来的注册注销、设置、闪烁等功能函数。 ├── trigger │ ├── ledtrig-activity.c │ ├── ledtrig-audio.c │ ├── ledtrig-backlight.c │ ├── ledtrig-camera.c │ ├── ledtrig-cpu.c │ ├── ledtrig-default-on.c │ ├── ledtrig-disk.c │ ├── ledtrig-gpio.c │ ├── ledtrig-heartbeat.c│ ├── ledtrig-mtd.c │ ├── ledtrig-netdev.c │ ├── ledtrig-oneshot.c │ ├── ledtrig-panic.c │ ├── ledtrig-pattern.c │ ├── ledtrig-timer.c │ ├── ledtrig-transient.c
3 LED dts
编写GPIO LED dts:
leds { compatible = "gpio-leds";--使用的是LED GPIO驱动。 led1 { label = "sys-led";--LED名称。 gpios = <&gpioi 0 1>;--使用的GPIO节点。 linux,default-trigger = "heartbeat";--默认Trigger类型。 default-state = "on";--默认状态。 status = "okay";--设备是否生效。 }; led2 { label = "user-led"; gpios = <&gpiof 3 1>; linux,default-trigger = "none"; default-state = "on"; status = "okay"; }; beep {--beep其实可以作为类LED设备处理。 label = "beep"; gpios = <&gpioc 7 1>; default-state = "off"; }; };
运行中的DTS查看如下:
/sys/firmware/devicetree/base/leds/ |-- beep | |-- default-state | |-- gpios | |-- label | `-- name |-- compatible |-- led1 | |-- default-state | |-- gpios | |-- label | |-- linux,default-trigger | |-- name | `-- status |-- led2 | |-- default-state | |-- gpios | |-- label | |-- linux,default-trigger | |-- name | `-- status `-- name
更多节点说明参考《common.txt - Documentation/devicetree/bindings/leds/common.txt》。
4 LED GPIO驱动解析
LED GPIO驱动主要工作:从dts中获取每个子节点;根据获取到的信息初始化struct led_classdev;注册LED Class设备。
platform_driver
gpio_led_probe--和DTS中的gpio-leds匹配后进行probe。
gpio_leds_create
device_get_child_node_count/device_for_each_child_node--获取子节点数,并遍历子节点。
devm_fwnode_get_gpiod_from_child--获取LED dts子节点。
fwnode_property_read_string--获取DTS中LED配置。
create_gpio_led--填充struct led_classdev,并将其注册到LED Core中。将对LED设备的操作转化为对GPIO的操作。
devm_led_classdev_register_ext--注册LED Class设备。
gpiod_set_consumer_name--设置当前GPIO的消费者名称。
gpio_led_shutdown
gpio_led_set--关闭LED输出。
完成后就创建platform设备,并且在/sys/class/leds下创建对LED配置的sysfs。
5 sysfs
常规的LED sysfs节点主要包括:brightness/max_brightness/trigger。
/sys/devices/platform/leds/ |-- driver -> ../../../bus/platform/drivers/leds-gpio |-- driver_override |-- leds | |-- beep | | |-- brightness--设置当前LED亮度值。 | | |-- max_brightness--LED最大亮度值。 | | |-- trigger--显示或配置LED触发方式,其中[]表示当前选中的Trigger。 | |-- sys-led | | |-- brightness | | |-- max_brightness | | |-- trigger | `-- user-led | |-- brightness | |-- max_brightness | |-- trigger
对于timer的其他Trigger类型的LED,可能还有其他sysfs节点。
/sys/class/leds/user-led |-- brightness |-- delay_off--一次timer周期内,关闭持续时间。 |-- delay_on--一次timer周期内,打开持续时间。 |-- max_brightness|-- trigger
当trigger设置为timer后,设置delay_off和delay_on,LED则以此off/on时间间隔循环显示。
通过sysfs即可对LED进行测试。
6 其他
关于Camera闪光灯,需要打开:
Device Drivers -> LED Support -> LED Class Support
->LED Flash Class Support
关于Flash LED和其他Trigger的介绍参考《common.txt - Documentation/devicetree/bindings/leds/common.txt》。