Linux 下的 LED 处理¶
最简单的形式,LED 类只允许从用户空间控制 LED。LED 出现在 /sys/class/leds/ 中。LED 的最大亮度在 max_brightness 文件中定义。brightness 文件将设置 LED 的亮度(取值范围为 0-max_brightness)。大多数 LED 没有硬件亮度支持,因此对于非零亮度设置只会点亮。
该类还引入了 LED 触发器的可选概念。触发器是基于内核的 LED 事件源。触发器可以是简单的,也可以是复杂的。简单触发器不可配置,旨在以最少的额外代码插入到现有子系统中。例如磁盘活动、nand 磁盘和 sharpsl 充电触发器。禁用 LED 触发器后,代码将进行优化。
复杂触发器虽然可用于所有 LED,但具有特定于 LED 的参数,并且基于每个 LED 工作。定时器触发器就是一个例子。定时器触发器会定期在 LED_OFF 和当前亮度设置之间更改 LED 亮度。可以通过 /sys/class/leds/<device>/delay_{on,off} 以毫秒为单位指定“开启”和“关闭”时间。您可以独立于定时器触发器更改 LED 的亮度值。但是,如果将亮度值设置为 LED_OFF,它也将禁用定时器触发器。
您可以以类似于选择 IO 调度器的方式更改触发器(通过 /sys/class/leds/<device>/trigger)。选择给定的触发器后,特定于触发器的参数可能会出现在 /sys/class/leds/<device> 中。
设计理念¶
基本设计理念是简单性。LED 是简单的设备,目标是保持少量代码,尽可能多地提供功能。在提出增强建议时,请牢记这一点。
LED 设备命名¶
当前的形式为
“设备名称:颜色:功能”
- 设备名称
它应该引用内核创建的唯一标识符,例如网络设备的 phyN 或输入设备的 inputN,而不是硬件;与产品和给定设备连接的总线相关的信息可以在 sysfs 中找到,并且可以使用 tools/leds 中的 get_led_device_info.sh 脚本检索;通常,此部分主要用于与某些其他设备相关的 LED。
- 颜色
来自头文件 include/dt-bindings/leds/common.h 的 LED_COLOR_ID_* 定义之一。
- 功能
来自头文件 include/dt-bindings/leds/common.h 的 LED_FUNCTION_* 定义之一。
如果需要的颜色或功能缺失,请向 linux-leds@vger.kernel.org 提交补丁。
可能需要为给定平台使用多个具有相同颜色和功能的 LED,但仅在序数上有所不同。在这种情况下,最好只在驱动程序中将预定义的 LED_FUNCTION_* 名称与所需的 “-N” 后缀连接起来。基于 fwnode 的驱动程序可以使用 function-enumerator 属性,然后 LED 核心将在 LED 类设备注册时自动处理连接。
LED 子系统还具有防止名称冲突的保护措施,当热插拔设备的驱动程序创建 LED 类设备并且它不提供唯一的设备名称部分时,可能会发生名称冲突。在这种情况下,数字后缀(例如 “_1”、“_2”、“_3” 等)将添加到请求的 LED 类设备名称中。
可能仍然存在一些 LED 类驱动程序使用供应商或产品名称作为设备名称,但这种方法现在已被弃用,因为它不传递任何附加值。产品信息可以在 sysfs 中的其他位置找到(请参阅 tools/leds/get_led_device_info.sh)。
正确 LED 名称的示例
“red:disk”
“white:flash”
“red:indicator”
“phy1:green:wlan”
“phy3::wlan”
“:kbd_backlight”
“input5::kbd_backlight”
“input3::numlock”
“input3::scrolllock”
“input3::capslock”
“mmc1::status”
“white:status”
get_led_device_info.sh 脚本可用于验证 LED 名称是否满足此处指出的要求。它执行 LED 类设备名称部分的验证,并在验证失败时给出该部分的预期值的提示。到目前为止,该脚本支持验证 LED 与以下类型的设备之间的关联
输入设备
符合 ieee80211 标准的 USB 设备
该脚本可以扩展。
有人呼吁将颜色等 LED 属性作为单独的 led 类属性导出。作为一种不会产生太多开销的解决方案,我建议将这些属性作为设备名称的一部分。上面的命名方案为进一步的属性留出了空间,如果需要的话。如果名称的某些部分不适用,只需将该部分留空即可。
亮度设置 API¶
LED 子系统核心公开以下用于设置亮度的 API
- led_set_brightness
保证不会休眠,传递 LED_OFF 会停止闪烁,
- led_set_brightness_sync
对于需要立即生效的用例 - 它可能会阻塞调用者访问设备寄存器所需的时间,并且可能会休眠,传递 LED_OFF 会停止硬件闪烁,如果启用了软件闪烁回退,则返回 -EBUSY。
LED 注册 API¶
想要注册供其他驱动程序/用户空间使用的 LED 类设备的驱动程序需要分配并填充 led_classdev 结构,然后调用 [devm_]led_classdev_register。如果使用非 devm 版本,驱动程序必须在其删除函数中调用 led_classdev_unregister,然后再释放 led_classdev 结构。
如果驱动程序可以检测到硬件发起的亮度更改,因此希望具有 brightness_hw_changed 属性,则必须在注册之前在 flags 中设置 LED_BRIGHT_HW_CHANGED 标志。在没有注册 LED_BRIGHT_HW_CHANGED 标志的 classdev 上调用 led_classdev_notify_brightness_hw_changed 是一个错误,将触发 WARN_ON。
LED 的硬件加速闪烁¶
可以对某些 LED 进行编程,使其在没有任何 CPU 干预的情况下闪烁。为了支持此功能,LED 驱动程序可以选择实现 blink_set() 函数(请参阅 <linux/leds.h>)。但是,要将 LED 设置为闪烁,最好使用 API 函数 led_blink_set(),因为它会检查并在必要时实现软件回退。
要关闭闪烁,请使用亮度值为 LED_OFF 的 API 函数 led_brightness_set(),该函数应停止可能需要闪烁的任何软件定时器。
如果调用 blink_set() 函数的参数为 *delay_on==0 && *delay_off==0,则该函数应选择一个用户友好的闪烁值。在这种情况下,驱动程序应通过 delay_on 和 delay_off 参数将选择的值返回给 leds 子系统。
使用 brightness_set() 回调函数将亮度设置为零应完全关闭 LED,并取消先前编程的任何硬件闪烁功能。
硬件驱动的 LED¶
可以对某些 LED 进行编程,使其由硬件驱动。这不仅限于闪烁,还包括自主关闭或开启。为了支持此功能,LED 需要实现各种其他操作,并需要声明对支持的触发器的特定支持。
通过硬件控制,我们指的是由硬件驱动的 LED。
LED 驱动程序必须定义以下值才能支持硬件控制
- hw_control_trigger
LED 在硬件控制模式下支持的唯一触发器名称。
- LED 驱动程序必须实现以下 API 才能支持硬件控制
- hw_control_is_supported
检查是否可以解析支持的触发器传递的标志,并在 LED 上激活硬件控制。
如果支持传递的标志掩码并且可以使用 hw_control_set() 设置,则返回 0。
如果不支持传递的标志掩码,则必须返回 -EOPNOTSUPP,在这种情况下,LED 触发器将使用软件回退。
如果发生任何其他错误(如设备未就绪或超时),则返回负错误。
- hw_control_set
激活硬件控制。LED 驱动程序将使用支持的触发器传递的标志,将它们解析为一组模式,并设置 LED 以按照请求的模式由硬件驱动。
通过 brightness_set 设置 LED_OFF 以停用硬件控制。
成功时返回 0,如果未能应用标志,则返回负错误号。
- hw_control_get
从已经处于硬件控制状态的 LED 获取活动模式,解析它们,并在标志中为支持的触发器设置当前活动标志。
成功时返回 0,如果未能解析初始模式,则返回负错误号。来自此函数的错误并非致命,因为设备可能处于所连接 LED 触发器不支持的初始状态。
- hw_control_get_device
返回与硬件控制中的 LED 驱动程序关联的设备。触发器可以使用它将此函数返回的设备与触发器配置的设备相匹配,作为闪烁事件的来源,并正确启用硬件控制。(例如,为特定 dev 配置为闪烁的网络设备触发器匹配从 get_device 返回的 dev 以设置硬件控制)
返回指向
struct device
的指针,如果当前未连接任何内容,则返回 NULL。
LED 驱动程序可以默认激活其他模式,以解决无法在支持的触发器上支持每个不同模式的问题。例如,将闪烁速度硬编码为某个间隔,如果未满足某些要求,则启用特殊功能(如绕过闪烁)。
触发器应首先检查 LED 驱动程序是否支持硬件控制 API,并检查是否支持触发器以验证硬件控制是否可行,使用 hw_control_is_supported 检查是否支持标志,最后才使用 hw_control_set 激活硬件控制。
触发器可以使用 hw_control_get 来检查 LED 是否已处于硬件控制状态并初始化它们的标志。
当 LED 处于硬件控制状态时,不可能进行软件闪烁,这样做会有效地禁用硬件控制。
已知问题¶
LED 触发器核心不能是模块,因为简单的触发器函数会导致噩梦般的依赖问题。与简单的触发器功能带来的好处相比,我认为这是一个小问题。LED 子系统的其余部分可以是模块化的。