Linux LED Subsystem
21 09 2011 LEDs…Everyone likes that! Those little shiny electronic devices are mountedon any well-made electronic equipment to indicate at a glance itsworking status. They tell you when your network has activity, when yourlaptop battery is empty, when your hard-drive is working, when youramplifier is overloading… they may even light up your bedroom!
In embedded systems the proper design of the front panel, with theright LED illuminated icons, is an essential feature and if you arefamiliar with network troubleshooting you can understand why!
Well-made devices should have a panel that instantly gives you an idea of what’s working and what’s not just by looking at it.
If you are using Linux as your kernel on a SoC design, you’ll be glad to know that it has an entire subsystem dedicated to LEDs!
In this post I’ll show how you to check if your system has somecontrollable LEDs, and how to use that from userspace applications andkernel drivers.
Host Drivers
Kernel controllable LEDs are commonly found on embedded systems,where the CPU usually has some LEDs controlled by its GPIO pins or bysome external peripheral, such as I2C/SPI port expander.
There are many LED-enabled device drivers in the kernel sources,some of which are dedicated drivers, such as the one for the LEDs in PC Engines ALIX boards, while others are just drivers for devices with integrated LEDs, such as the one for RaLink wireless network cards.
Just run a search on the kernel code for the led_classdev_register function to see if you find something to play with.
The most common driver you’ll find in modern ARM-based SoC is “leds-gpio“, which is used to control LEDs connected to GPIO of any gpiolib enabled CPU, including most ARM SoC.
Registering a GPIO with the leds-gpio driver is easy, take a look at this code from the nslu2-setup.c driver:
01 | #include <linux/leds.h> |
03 | static struct gpio_led nslu2_led_pins[] = { |
05 | .name = "nslu2:green:ready" , |
06 | .gpio = NSLU2_LED_GRN_GPIO, |
11 | static struct gpio_led_platform_data nslu2_led_data = { |
12 | .num_leds = ARRAY_SIZE(nslu2_led_pins), |
13 | .leds = nslu2_led_pins, |
16 | static struct platform_device nslu2_leds = { |
19 | .dev.platform_data = &nslu2_led_data, |
22 | static struct platform_device *nslu2_devices[] __initdata = { |
On these platforms, when all the structures are in place, if youwant to add a new GPIO controlled LED just add the appropriatestructure with the name, pin and properties and light it up! If it’snot working, check that all the necessary modules are compiled in andbe sure that your pin is configured as a GPIO, since in many CPU youhave to do that explicitly.
If you’re a lucky owner of an embedded PowerPC board, you might like to know that the leds-gpio driver is openfirmware enabled!
Check out this configuration for the MPC8315E-RDB board:
02 | compatible = "gpio-leds"; |
04 | gpios = <&mcu_pio 0 0>; |
08 | gpios = <&mcu_pio 1 0>; |
09 | linux,default-trigger = "ide-disk"; |
Please note that while the platform driver registers as “leds-gpio”, the openfirmware one matches with “gpio-leds”.
Also, when you are naming your LEDs, you might want to follow the convention suggested in the official documentation, which tells you to name your led as: “devicename:colour:function”.
Controlling LEDs from Userspace
So, you have your kernel configured and the system booted. Now what?
First, be sure that the kernel registered the LEDs, as in:
balto@balto-mpc:~# dmesg | grep "led device"
Registered led device: pwr
Registered led device: hdd
Now, go to the “/sys/class/leds/” directory and look at the content:
balto@balto-mpc:~# cd /sys/class/leds/hdd
balto@balto-mpc:/sys/class/leds/hdd# ls
brightness device max_brightness power subsystem trigger
uevent
You should see what’s coming: to turn on the LED, just use:
balto-mpc:/sys/class/leds/hdd# echo 1 > brightness
and to turn it off use:
balto-mpc:/sys/class/leds/hdd# echo 0 > brightness
Also note that, as suggested by the content of the“actual_brightness” file, the brightness parameter goes from 0 to 255,and on some platforms you may have a PWM controlled LED with variablebrightness.
Now, what’s that “trigger” file for?
Triggers
Triggers are the API used to link a LED to an event in kernel space.
That’s the idea: the platform registers some LED device, the driversregister some LED trigger, you configure the device to use a certaintrigger and the led will be controlled from the appropriate driver.
To see the list of the available triggers, just read the “trigger” file:
balto@balto-mpc:/sys/class/leds/hdd# cat trigger
[none] nand-disk timer heartbeat gpio rfkill0 phy0rx
phy0tx phy0assoc phy0radio
you see that the trigger is disabled. To associate the LED with a trigger, just write its name in the “trigger” file:
balto@balto-mpc:/sys/class/leds/hdd# echo heartbeat > trigger
balto@balto-mpc:/sys/class/leds/hdd# cat trigger
none nand-disk timer [heartbeat] gpio rfkill0 phy0rx
phy0tx phy0assoc phy0radio
Now the LED should pulse like an heart with a period proportional to the system load average.
When you change trigger, check again the content of the LED directory, as many triggers also register some parameter files.
Hacking the kernel to add new triggers is pretty easy and good fun, just take some time and read a couple of trigger source files to see how they work!
Also, you can find the official documentation in the kernel’s “Documentation/leds” directory.
Happy blinkin’!