diff options
author | Ezequiel Garcia <ezequiel@vanguardiasur.com.ar> | 2016-04-28 19:03:38 -0300 |
---|---|---|
committer | Jacek Anaszewski <j.anaszewski@samsung.com> | 2016-05-06 10:22:09 +0200 |
commit | ba93cdce5bbe6929fd7486f87c985598ded8f451 (patch) | |
tree | e9bedbf17709dceafabbcd753a3d6cc97c36d6fb /drivers/leds/trigger/ledtrig-panic.c | |
parent | f15c65afddbe16a832cfe3f311588c7e34a4efbf (diff) | |
download | linux-rt-ba93cdce5bbe6929fd7486f87c985598ded8f451.tar.gz |
leds: triggers: Allow to switch the trigger to "panic" on a kernel panic
This commit adds a new led_cdev flag LED_PANIC_INDICATOR, which
allows to mark a specific LED to be switched to the "panic"
trigger, on a kernel panic.
This is useful to allow the user to assign a regular trigger
to a given LED, and still blink that LED on a kernel panic.
Signed-off-by: Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
Reviewed-by: Matthias Brugger <mbrugger@suse.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Diffstat (limited to 'drivers/leds/trigger/ledtrig-panic.c')
-rw-r--r-- | drivers/leds/trigger/ledtrig-panic.c | 47 |
1 files changed, 47 insertions, 0 deletions
diff --git a/drivers/leds/trigger/ledtrig-panic.c b/drivers/leds/trigger/ledtrig-panic.c index 627b350c5ec3..d735526b9db4 100644 --- a/drivers/leds/trigger/ledtrig-panic.c +++ b/drivers/leds/trigger/ledtrig-panic.c @@ -11,10 +11,54 @@ #include <linux/kernel.h> #include <linux/init.h> +#include <linux/notifier.h> #include <linux/leds.h> +#include "../leds.h" static struct led_trigger *trigger; +/* + * This is called in a special context by the atomic panic + * notifier. This means the trigger can be changed without + * worrying about locking. + */ +static void led_trigger_set_panic(struct led_classdev *led_cdev) +{ + struct led_trigger *trig; + + list_for_each_entry(trig, &trigger_list, next_trig) { + if (strcmp("panic", trig->name)) + continue; + if (led_cdev->trigger) + list_del(&led_cdev->trig_list); + list_add_tail(&led_cdev->trig_list, &trig->led_cdevs); + + /* Avoid the delayed blink path */ + led_cdev->blink_delay_on = 0; + led_cdev->blink_delay_off = 0; + + led_cdev->trigger = trig; + if (trig->activate) + trig->activate(led_cdev); + break; + } +} + +static int led_trigger_panic_notifier(struct notifier_block *nb, + unsigned long code, void *unused) +{ + struct led_classdev *led_cdev; + + list_for_each_entry(led_cdev, &leds_list, node) + if (led_cdev->flags & LED_PANIC_INDICATOR) + led_trigger_set_panic(led_cdev); + return NOTIFY_DONE; +} + +static struct notifier_block led_trigger_panic_nb = { + .notifier_call = led_trigger_panic_notifier, +}; + static long led_panic_blink(int state) { led_trigger_event(trigger, state ? LED_FULL : LED_OFF); @@ -23,6 +67,9 @@ static long led_panic_blink(int state) static int __init ledtrig_panic_init(void) { + atomic_notifier_chain_register(&panic_notifier_list, + &led_trigger_panic_nb); + led_trigger_register_simple("panic", &trigger); panic_blink = led_panic_blink; return 0; |