Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Documentation/ABI/testing/sysfs-class-led-driver-hid-asus
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
What: /sys/class/leds/<led>/aura_mode
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: read/write
ASUS Aura topology mode on hid-asus Dynamic Lighting nodes.
One of "auto", "unified", or "split". Writing selects the
stored mode; reading shows the stored value with the active
choice in brackets (e.g. "[auto] unified split").

auto resolves to split: keyboard and lightbar nodes (when
present) accept effect/direct writes independently, and the
global node returns -EBUSY. unified inverts that: only the
global node is writable. Direct RGB may be backed by HID
LampArray (Usage Page 0x59) when Aura 0xBC cannot address the
lightbar independently; firmware animations stay on Aura 0xb3.
151 changes: 151 additions & 0 deletions Documentation/ABI/testing/sysfs-class-leds-dynamic
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
What: /sys/class/leds/<led>/direction
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: read/write
Animation propagation direction. Outputs or accepts one of
``left``, ``right``, ``up``, ``down``, ``clockwise``,
``counter_clockwise``. Only visible when the driver supports
directional animations.

What: /sys/class/leds/<led>/direction_index
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: read
Space-separated list of supported animation directions.
Only visible when the driver supports directional animations.

What: /sys/class/leds/<led>/direct_buffer
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: write-only (binary)
Raw packed RGB stream (3 bytes per LED: R, G, B in sequence).
Writing to this node transmits direct per-key or matrix frame
data bypassing hardware effect generators. The complete write
must total exactly (led_count * 3) bytes; kernfs may deliver
that payload in PAGE_SIZE chunks starting at offset 0. Only
visible when the driver implements direct RGB streaming.

What: /sys/class/leds/<led>/effect
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: read/write
Currently selected hardware or driver-synthesized animation
effect. Reading outputs the effect name. Writing a name from
``effect_index`` selects that effect. Names are defined by the
driver, not by a global enum; userspace must read
``effect_index``. Any active trigger is automatically detached
upon switching effects.

What: /sys/class/leds/<led>/effect_index
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: read
Space-separated list of animation effect names supported by
this LED. The vocabulary is defined by the driver. Lighting
off is not an effect; use ``enabled``.

What: /sys/class/leds/<led>/effects_palette
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: read/write
Multi-color stacked palette used by multi-color animation
effects. Reading outputs space-separated 24-bit hex colors
(``#RRGGBB``). Writing accepts a space-separated sequence of
hex triplets. The number of entries must not exceed
``max_palette_entries``. Color of a single-color LED remains
on the standard LED ``brightness`` / ``multi_intensity`` nodes.
Only visible when the driver supports programmable palettes.

What: /sys/class/leds/<led>/enabled
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: read/write
Lighting engine on/off as ``true`` or ``false``. Independent
from ``effect``: disabling turns the lights off without
forgetting the selected effect. Only visible when the driver
implements a dedicated enable callback.

What: /sys/class/leds/<led>/enabled_index
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: read
Space-separated list of values accepted by ``enabled``
(``false true``). Only visible when the driver implements a
dedicated enable callback.

What: /sys/class/leds/<led>/frame
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: write-only (binary)
Raw binary sink for monochrome pixel displays (e.g. AniMe
Matrix) or segment lighting strips. A write smaller than
PAGE_SIZE at offset 0 is forwarded as one frame. A larger
write may arrive in PAGE_SIZE chunks and is forwarded only
when those chunks fill the advertised size contiguously from
offset 0. Any other offset is rejected. Writes larger than
the advertised size are rejected.

What: /sys/class/leds/<led>/led_count
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: read
Total number of individual, addressable LEDs in this zone.

What: /sys/class/leds/<led>/matrix_dimensions
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: read
Width and height of 2D matrix layouts formatted as two
space-separated integers (``<width> <height>``). This
attribute is only visible when the driver publishes non-zero
matrix dimensions for the zone.

What: /sys/class/leds/<led>/max_palette_entries
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: read
Maximum number of palette entries accepted by
``effects_palette``. Only visible when the driver supports
programmable palettes.

What: /sys/class/leds/<led>/power_states
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: read/write
Space-separated list of currently enabled persistence power
states. Writing a space-separated list of state names replaces
the active bitmask (an empty list clears all enabled states).
Only visible on devices supporting power state configuration.

What: /sys/class/leds/<led>/power_states_index
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: read
Space-separated list of system power states supported for
lighting persistence (``boot``, ``awake``, ``sleep``,
``shutdown``). Only visible on devices supporting power state
configuration.

What: /sys/class/leds/<led>/speed
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: read/write
Current animation speed level (integer within ``speed_range``).
Only visible when the driver supports adjustable speed.

What: /sys/class/leds/<led>/speed_range
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: read
Minimum and maximum animation speed level accepted by
``speed``. Formatted as ``<min>-<max>``. Only visible when the
driver supports adjustable speed.

What: /sys/class/leds/<led>/zone_type
Date: September 2026
Contact: Marco Scardovi <scardracs@disroot.org>
Description: read
Driver-defined string describing the physical topology of
this lighting zone. Example values include ``generic``,
``keyboard``, ``keyboard_per_key``, ``matrix_2d``,
``segment_strip``, ``logo``, ``lightbar``, and ``global``.
1 change: 1 addition & 0 deletions Documentation/leds/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ LEDs
leds-class
leds-class-flash
leds-class-multicolor
leds-class-dynamic
ledtrig-oneshot
ledtrig-transient
ledtrig-usbport
Expand Down
216 changes: 216 additions & 0 deletions Documentation/leds/leds-class-dynamic.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
.. SPDX-License-Identifier: GPL-2.0

======================================
Dynamic Lighting LED class under Linux
======================================

Author: Marco Scardovi <scardracs@disroot.org>

Description
===========
The Dynamic Lighting LED class provides a standardized sysfs interface for
complex, addressable illumination hardware such as per-key RGB keyboard
matrices, 2D LED matrix displays, addressable segment strips, and chassis
lightbars.

The class can either wrap a new LED class device or attach onto an LED that
the vendor driver already registered (including ``LED_MULTI_COLOR``). Color
stays on the standard ``brightness`` / ``multi_intensity`` nodes. Dynamic
Lighting adds optional effect, speed, enable, palette, and binary frame
attributes without requiring hidraw or a rewrite of the existing LED
registration.

Directory Layout Example
========================
The following examples use ``<led>`` as a placeholder for a Dynamic Lighting
LED class device name. Optional attributes are omitted when the driver does
not implement the matching callback.

.. code-block:: console

# ls -l /sys/class/leds/<led>/
-rw-r--r-- 1 root root 4096 Sep 4 17:00 brightness
-r--r--r-- 1 root root 4096 Sep 4 17:00 max_brightness
-r--r--r-- 1 root root 4096 Sep 4 17:00 zone_type
-r--r--r-- 1 root root 4096 Sep 4 17:00 led_count
-r--r--r-- 1 root root 4096 Sep 4 17:00 effect_index
-rw-r--r-- 1 root root 4096 Sep 4 17:00 effect
-rw-r--r-- 1 root root 4096 Sep 4 17:00 enabled
-r--r--r-- 1 root root 4096 Sep 4 17:00 enabled_index
-r--r--r-- 1 root root 4096 Sep 4 17:00 speed_range
-rw-r--r-- 1 root root 4096 Sep 4 17:00 speed
-r--r--r-- 1 root root 4096 Sep 4 17:00 direction_index
-rw-r--r-- 1 root root 4096 Sep 4 17:00 direction
-r--r--r-- 1 root root 4096 Sep 4 17:00 max_palette_entries
-rw-r--r-- 1 root root 4096 Sep 4 17:00 effects_palette
-r--r--r-- 1 root root 4096 Sep 4 17:00 power_states_index
-rw-r--r-- 1 root root 4096 Sep 4 17:00 power_states
--w------- 1 root root 504 Sep 4 17:00 direct_buffer

Attaching to an existing LED
============================
Drivers that already expose a LED can publish Dynamic Lighting as extra
attributes on that same directory. Keep the existing color path, point
``host`` at the registered LED, and supply only the ops the hardware
already implements:

.. code-block:: c

static const struct led_dynamic_ops my_ops = {
.set_effect = my_set_effect,
.set_speed = my_set_speed,
.set_enabled = my_set_enabled,
};

ldev->host = existing_led_cdev;
ldev->ops = &my_ops;
ldev->effects = my_effects;
ldev->num_effects = ARRAY_SIZE(my_effects);
led_classdev_dynamic_register(dev, ldev);

``led_dynamic_fill_effects()`` can compact an existing capability bitmask
into a name table. Effect names are defined by the driver; userspace must
read ``effect_index``.

Sysfs Attributes
================

``zone_type`` (read-only)
Driver-defined string describing the physical topology of the zone.
Example values include ``generic``, ``keyboard``, ``keyboard_per_key``,
``matrix_2d``, ``segment_strip``, ``logo``, ``lightbar``, or ``global``.

``led_count`` (read-only)
Total number of individually addressable LEDs in this zone.

``matrix_dimensions`` (read-only)
Width and height for 2D matrix layouts formatted as ``<width> <height>``.
Only visible when the driver publishes non-zero matrix dimensions.

``effect_index`` (read-only)
Space-separated list of animation effect names supported by this LED.
Names are defined by the driver.

``effect`` (read/write)
Currently selected animation effect. Writing a name from ``effect_index``
switches the mode. Any active trigger is automatically detached upon
effect change.

``enabled`` (read/write)
Lighting engine on/off (``true`` / ``false``). Independent from
``effect``. Only visible when the driver implements ``set_enabled``.

``enabled_index`` (read-only)
Values accepted by ``enabled`` (``false true``). Only visible when the
driver implements ``set_enabled``.

``speed_range`` (read-only)
Minimum and maximum effect animation speed accepted by ``speed``,
formatted as ``<min>-<max>``. Only visible when the hardware supports
adjustable speed.

``speed`` (read/write)
Current effect animation speed (within ``speed_range``). Only visible when
the hardware supports adjustable speed.

``direction_index`` (read-only)
Space-separated list of directions accepted by ``direction``. Only
visible when directional effects are supported.

``direction`` (read/write)
Animation propagation direction: ``left``, ``right``, ``up``, ``down``,
``clockwise``, or ``counter_clockwise``. Only visible when directional
effects are supported.

``max_palette_entries`` (read-only)
Maximum number of palette entries accepted by ``effects_palette``. Only
visible when programmable palettes are supported.

``effects_palette`` (read/write)
Space-separated list of 24-bit RGB hex colors (e.g. ``#ff0000 #00ff00``).
Up to ``max_palette_entries`` colors can be defined. Single-LED color
continues to use ``brightness`` / ``multi_intensity``.

``power_states_index`` (read-only)
List of platform power states supported for illumination persistence
(``boot``, ``awake``, ``sleep``, ``shutdown``).

``power_states`` (read/write)
Currently active persistence states. Writing a space-separated list of
state names replaces the active state bitmask.

``direct_buffer`` (write-only, binary)
Raw binary sink for streaming per-key RGB frames. Each LED requires 3 bytes
in sequence (R, G, B). The complete write must total ``led_count * 3``
bytes; kernfs may deliver that payload in ``PAGE_SIZE`` chunks starting at
offset 0. Enables efficient high-rate streaming for visualizers and canvas
sinks.

``frame`` (write-only, binary)
Raw binary sink for monochrome display chunks (e.g. 2D pixel matrices) or
segmented lighting bars. A write smaller than ``PAGE_SIZE`` at offset 0 is
forwarded as one frame. A larger write may arrive in ``PAGE_SIZE`` chunks
and is forwarded only when those chunks fill the advertised size
contiguously from offset 0 (at most 65536 bytes). Any other offset is
rejected.

Locking Hierarchy & Invariants
==============================
To prevent kernel deadlocks between LED triggers, sysfs handlers, and bus
transfers, the subsystem enforces the following lock order:

1. Acquire outer mutex: ``mutex_lock(&cdev->led_access)``.
2. If the operation replaces trigger-driven output, disengage/remove the active
LED trigger via ``led_trigger_remove(cdev)``.
3. Acquire internal mutex: ``mutex_lock(&ldev->lock)``.
4. Validate inputs, update state, and dispatch driver callbacks.
5. Release internal mutex: ``mutex_unlock(&ldev->lock)``.
6. Release outer mutex: ``mutex_unlock(&cdev->led_access)``.

Driver callbacks must not persist class-owned fields (``current_effect``,
``speed``, ``enabled``, ``palette``, ``active_power_states``) on failure; the core
writes those fields only after a successful callback. ``brightness_set_blocking``
is not called with ``ldev->lock`` held and must take it if it mutates the
same state.

When Dynamic Lighting is attached to an existing LED, ``cdev`` in the lock
order is that host LED (``led_dynamic_cdev()``), not the unused embedded
``ldev->cdev``.

``frame`` writes larger than the driver-advertised ``max_frame_size`` (capped at
65536 bytes) are rejected. Empty writes are rejected.

Examples
========

Selecting an effect and speed advertised by the device:
-------------------------------------------------------
.. code-block:: console

# cat /sys/class/leds/<led>/effect_index
# echo <effect> > /sys/class/leds/<led>/effect
# echo 1 > /sys/class/leds/<led>/speed

Turning lighting off without changing the selected effect:
----------------------------------------------------------
.. code-block:: console

# echo false > /sys/class/leds/<led>/enabled

Configuring a custom 3-color palette:
-------------------------------------
.. code-block:: console

# echo "#ff0000 #00ff00 #0000ff" > /sys/class/leds/<led>/effects_palette

Enabling illumination during boot and awake states:
---------------------------------------------------
.. code-block:: console

# echo "boot awake" > /sys/class/leds/<led>/power_states

Streaming a direct RGB frame (for a 168-LED device, 504 bytes):
----------------------------------------------------------------
.. code-block:: console

# dd if=/dev/urandom of=/sys/class/leds/<led>/direct_buffer bs=504 count=1
Loading
Loading