Skip to content

Linux LED Subsystem: Source Code Walkthrough

The previous article introduced how to use the Linux LED subsystem. This follow-up looks at the main kernel-side source files and how the framework is organized.

LED subsystem overview

The most useful files to inspect in the kernel tree are:

drivers/leds/led-class.c
drivers/leds/led-core.c
drivers/leds/led-triggers.c
include/linux/leds.h

1. LED device management

drivers/leds/led-class.c implements LED-device registration and removal. The central representation is struct led_classdev, which holds the LED name, current and maximum brightness, callbacks, trigger state, device pointer, and synchronization objects.

The important fields are:

  • name — used for the LED subdirectory under /sys/class/leds/.
  • brightness — current brightness, exposed through brightness.
  • max_brightness — maximum valid brightness.
  • brightness_set / brightness_set_blocking — driver callbacks that change brightness.
  • brightness_get — callback that reports brightness.
  • blink_set — optional hardware-accelerated blinking callback.
  • trigger — the trigger currently controlling the LED.

The framework initialization creates the leds device class and attaches the class power-management and attribute groups:

static int __init leds_init(void)
{
    leds_class = class_create("leds");
    if (IS_ERR(leds_class))
        return PTR_ERR(leds_class);

    leds_class->pm = &leds_class_dev_pm_ops;
    leds_class->dev_groups = led_groups;
    return 0;
}

After this initialization, LED drivers can register a led_classdev instance and appear under /sys/class/leds/. Registration is handled by led_classdev_register_ext(); led_classdev_unregister() removes a registered LED.

2. Core LED functionality

drivers/leds/led-core.c provides the common registration and management layer.

It connects a struct led_classdev with sysfs and provides the common routines behind brightness and LED-state changes. It also supports trigger registration and associates LED devices with power-management operations through the class pm hooks.

From user space, this is why a typical LED can be inspected and controlled with files such as:

/sys/class/leds/<name>/brightness
/sys/class/leds/<name>/max_brightness
/sys/class/leds/<name>/trigger

3. LED triggers

drivers/leds/led-triggers.c implements trigger registration and management. A trigger changes LED state in response to a condition or event: timers, activity indicators, audio status, heartbeats, and so on.

The key structure is conceptually:

struct led_trigger {
    const char *name;
    int (*activate)(struct led_classdev *led_cdev);
    void (*deactivate)(struct led_classdev *led_cdev);

    struct led_hw_trigger_type *trigger_type;
    rwlock_t leddev_list_lock;
    struct list_head led_cdevs;
    struct list_head next_trig;
    const struct attribute_group **groups;
};

A trigger is therefore an operating mode for an LED. The timer trigger, for example, makes it blink at a configured interval. A board-specific trigger might indicate a network link, audio activity, or a system state.

4. Responsibilities at a glance

File Main responsibility
led-class.c Registers/unregisters LED class devices; creates the LED class and sysfs-facing objects.
led-core.c Supplies common LED-device management, brightness handling, and framework support.
led-triggers.c Registers and controls triggers, including built-in trigger behavior.
include/linux/leds.h Declares the public LED framework interfaces and data structures.

Summary

The LED subsystem separates an LED driver's hardware callbacks from the common user-space interface. A driver describes its LED with led_classdev, the core exposes it in sysfs, and optional triggers give it event-driven behavior without each driver having to reimplement that logic.