Linux 下的 LED 处理

最简单的形式,LED 类只允许从用户空间控制 LED。 LED 出现在 /sys/class/leds/ 中。 LED 的最大亮度在 max_brightness 文件中定义。 brightness 文件将设置 LED 的亮度(取值范围为 0-max_brightness)。 大多数 LED 没有硬件亮度支持,因此对于非零亮度设置只会打开。

该类还引入了 LED 触发器的可选概念。 触发器是基于内核的 led 事件源。 触发器可以是简单的,也可以是复杂的。 简单的触发器是不可配置的,旨在以最少的附加代码插入到现有的子系统中。 示例包括 disk-activity、nand-disk 和 sharpsl-charge 触发器。 禁用 led 触发器后,代码会进行优化。

虽然复杂触发器可用于所有 LED,但它们具有 LED 特定的参数,并且基于每个 LED 工作。 定时器触发器就是一个例子。 定时器触发器将定期在 LED_OFF 和当前亮度设置之间更改 LED 亮度。 “on” 和 “off” 时间可以通过 /sys/class/leds/<device>/delay_{on,off} 以毫秒为单位指定。 您可以独立于定时器触发器更改 LED 的亮度值。 但是,如果将亮度值设置为 LED_OFF,它也会禁用定时器触发器。

您可以通过类似于选择 IO 调度器的方式(通过 /sys/class/leds/<device>/trigger)来更改触发器。 一旦选择了给定的触发器,触发器特定的参数可能会出现在 /sys/class/leds/<device> 中。

设计理念

底层的设计理念是简单。 LED 是简单的设备,目标是保持少量代码,提供尽可能多的功能。 请记住这一点,在提出增强功能时。

LED 设备命名

目前的形式是

“devicename:color:function”

  • devicename

    它应该引用内核创建的唯一标识符,例如网络设备的 phyN 或输入设备的 inputN,而不是硬件;与产品和给定设备连接的总线相关的信息在 sysfs 中可用,可以使用 tools/leds 中的 get_led_device_info.sh 脚本检索;通常,这部分主要用于与其它设备关联的LED。

  • color

    来自头文件 include/dt-bindings/leds/common.h 的 LED_COLOR_ID_* 定义之一。

  • function

    来自头文件 include/dt-bindings/leds/common.h 的 LED_FUNCTION_* 定义之一。

如果需要的颜色或功能缺失,请提交补丁至 linux-leds@vger.kernel.org

给定平台可能需要多个具有相同颜色和功能的 LED,仅序号不同。 在这种情况下,最好直接在驱动程序中将预定义的 LED_FUNCTION_* 名称与所需的“-N”后缀连接。 基于 fwnode 的驱动程序可以使用 function-enumerator 属性,然后 LED 核心将在 LED 类设备注册时自动处理连接。

LED 子系统还具有针对名称冲突的保护,当 LED 类设备由热插拔设备的驱动程序创建并且它不提供唯一的 devicename 部分时,可能会发生名称冲突。 在这种情况下,数值后缀(例如“_1”、“_2”、“_3”等)将添加到请求的 LED 类设备名称。

可能仍然存在使用供应商或产品名称作为 devicename 的 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 类 devicename 部分的验证,并在验证失败时提供有关该部分预期值的提示。 到目前为止,该脚本支持 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 属性,则必须在注册之前在标志中设置 LED_BRIGHT_HW_CHANGED 标志。 在未注册 LED_BRIGHT_HW_CHANGED 标志的 classdev 上调用 led_classdev_notify_brightness_hw_changed 是一个错误,并且会触发 WARN_ON。

硬件驱动的 LED

某些 LED 可以被编程为由硬件驱动。 这不仅限于闪烁,还包括自主关闭或打开。 为了支持此功能,LED 需要实现各种附加操作,并且需要声明对支持的触发器的特定支持。

通过 hw 控制,我们指的是由硬件驱动的 LED。

LED 驱动程序必须定义以下值才能支持 hw 控制

  • hw_control_trigger

    LED 在 hw 控制模式下支持的唯一触发器名称。

LED 驱动程序必须实现以下 API 才能支持 hw 控制
  • hw_control_is_supported

    检查是否可以解析支持的触发器传递的标志,并在 LED 上激活 hw 控制。

    如果支持传递的标志掩码并且可以使用 hw_control_set() 设置,则返回 0。

    如果不支持传递的标志掩码,则必须返回 -EOPNOTSUPP,LED 触发器在这种情况下将使用软件回退。

    如果发生任何其他错误,例如设备未准备好或超时,则返回负错误。

  • hw_control_set

    激活 hw 控制。 LED 驱动程序将使用支持的触发器传递的标志,将它们解析为一组模式,并按照请求的模式设置由硬件驱动的 LED。

    通过 brightness_set 设置 LED_OFF 以停用 hw 控制。

    成功时返回 0,未能应用标志时返回负错误号。

  • hw_control_get

    从已经在 hw 控制中的 LED 获取活动模式,解析它们并在标志中设置支持的触发器的当前活动标志。

    成功时返回 0,未能解析初始模式时返回负错误号。 来自此函数的错误不是致命的,因为设备可能处于连接的 LED 触发器不支持的初始状态。

  • hw_control_get_device

    返回与 hw 控制中的 LED 驱动程序关联的设备。 触发器可以使用它来将此函数返回的设备与为触发器配置的设备匹配,作为闪烁事件的来源,并正确启用 hw 控制。(例如,配置为为特定 dev 闪烁的网络设备触发器匹配来自 get_device 的返回的 dev 以设置 hw 控制)

    返回指向 struct device 的指针,如果当前未连接任何设备,则返回 NULL。

LED 驱动程序可以默认激活其他模式,以解决无法在支持的触发器上支持每种不同模式的问题。 例如,将闪烁速度硬编码为固定间隔,如果未满足某些要求,则启用特殊功能(例如绕过闪烁)。

触发器应首先检查 LED 驱动程序是否支持 hw 控制 API,并检查是否支持触发器以验证 hw 控制是否可能,使用 hw_control_is_supported 检查是否支持标志,并且仅在最后使用 hw_control_set 激活 hw 控制。

触发器可以使用 hw_control_get 检查 LED 是否已经在 hw 控制中并初始化其标志。

当 LED 处于 hw 控制中时,无法进行软件闪烁,这样做会有效地禁用 hw 控制。

已知问题

LED 触发器核心不能是模块,因为简单的触发器函数会导致噩梦般的依赖关系问题。 与简单触发器功能带来的好处相比,我认为这是一个小问题。 LED 子系统的其余部分可以是模块化的。