| 1 | |
| 2 | |
| 3 | * Kernel Panic LED Trigger
|
| 4 | *
|
| 5 | * Copyright 2016 Ezequiel Garcia <[email protected]>
|
| 6 |
|
| 7 |
|
| 8 | #include <linux/kernel.h>
|
| 9 | #include <linux/init.h>
|
| 10 | #include <linux/notifier.h>
|
| 11 | #include <linux/panic_notifier.h>
|
| 12 | #include <linux/leds.h>
|
| 13 | #include "../leds.h"
|
| 14 |
|
| 15 | static struct led_trigger *trigger;
|
| 16 |
|
| 17 | |
| 18 | * This is called in a special context by the atomic panic
|
| 19 | * notifier. This means the trigger can be changed without
|
| 20 | * worrying about locking.
|
| 21 |
|
| 22 | static void led_trigger_set_panic(struct led_classdev *led_cdev)
|
| 23 | {
|
| 24 | if (led_cdev->trigger)
|
| 25 | list_del(&led_cdev->trig_list);
|
| 26 | list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
|
| 27 |
|
| 28 |
|
| 29 | led_cdev->blink_delay_on = 0;
|
| 30 | led_cdev->blink_delay_off = 0;
|
| 31 |
|
| 32 | led_cdev->trigger = trigger;
|
| 33 | }
|
| 34 |
|
| 35 | static int led_trigger_panic_notifier(struct notifier_block *nb,
|
| 36 | unsigned long code, void *unused)
|
| 37 | {
|
| 38 | struct led_classdev *led_cdev;
|
| 39 |
|
| 40 | list_for_each_entry(led_cdev, &leds_list, node)
|
| 41 | if (led_cdev->flags & LED_PANIC_INDICATOR)
|
| 42 | led_trigger_set_panic(led_cdev);
|
| 43 | return NOTIFY_DONE;
|
| 44 | }
|
| 45 |
|
| 46 | static struct notifier_block led_trigger_panic_nb = {
|
| 47 | .notifier_call = led_trigger_panic_notifier,
|
| 48 | };
|
| 49 |
|
| 50 | static long led_panic_blink(int state)
|
| 51 | {
|
| 52 | led_trigger_event(trigger, state ? LED_FULL : LED_OFF);
|
| 53 | return 0;
|
| 54 | }
|
| 55 |
|
| 56 | static int __init ledtrig_panic_init(void)
|
| 57 | {
|
| 58 | led_trigger_register_simple("panic", &trigger);
|
| 59 | if (!trigger)
|
| 60 | return -ENOMEM;
|
| 61 |
|
| 62 | atomic_notifier_chain_register(&panic_notifier_list,
|
| 63 | &led_trigger_panic_nb);
|
| 64 |
|
| 65 | panic_blink = led_panic_blink;
|
| 66 | return 0;
|
| 67 | }
|
| 68 | device_initcall(ledtrig_panic_init);
|