diff --git a/Documentation/leds/leds-class.txt b/Documentation/leds/leds-class.txt index d406d98339b25abbe3d1f5f05c4ac7f9856d3e9c..44f5e6bccd978fbe2329cdb39af87494aaafc09c 100644 --- a/Documentation/leds/leds-class.txt +++ b/Documentation/leds/leds-class.txt @@ -74,8 +74,8 @@ blink_set() function (see ). To set an LED to blinking, however, it is better to use the API function led_blink_set(), as it will check and implement software fallback if necessary. -To turn off blinking again, use the API function led_brightness_set() -as that will not just set the LED brightness but also stop any software +To turn off blinking, use the API function led_brightness_set() +with brightness value LED_OFF, which should stop any software timers that may have been required for blinking. The blink_set() function should choose a user friendly blinking value diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index 3495d5d6547f65a6fe507f063dce397bc424496f..3bce44893021365a999724a9aff0cf17d462a95f 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -53,11 +53,12 @@ static void led_timer_function(unsigned long data) if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) { led_set_brightness_nosleep(led_cdev, LED_OFF); + led_cdev->flags &= ~LED_BLINK_SW; return; } if (led_cdev->flags & LED_BLINK_ONESHOT_STOP) { - led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP; + led_cdev->flags &= ~(LED_BLINK_ONESHOT_STOP | LED_BLINK_SW); return; } @@ -151,6 +152,7 @@ static void led_set_software_blink(struct led_classdev *led_cdev, return; } + led_cdev->flags |= LED_BLINK_SW; mod_timer(&led_cdev->blink_timer, jiffies + 1); } @@ -219,6 +221,7 @@ void led_stop_software_blink(struct led_classdev *led_cdev) del_timer_sync(&led_cdev->blink_timer); led_cdev->blink_delay_on = 0; led_cdev->blink_delay_off = 0; + led_cdev->flags &= ~LED_BLINK_SW; } EXPORT_SYMBOL_GPL(led_stop_software_blink); @@ -226,10 +229,10 @@ void led_set_brightness(struct led_classdev *led_cdev, enum led_brightness brightness) { /* - * In case blinking is on delay brightness setting + * If software blink is active, delay brightness setting * until the next timer tick. */ - if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) { + if (led_cdev->flags & LED_BLINK_SW) { /* * If we need to disable soft blinking delegate this to the * work queue task to avoid problems in case we are called diff --git a/drivers/leds/trigger/ledtrig-heartbeat.c b/drivers/leds/trigger/ledtrig-heartbeat.c index 410c39c62dc7f1c3bdfa3b5dd305bbef031aef25..c9f386213e9ef16ae006e2270e2615e4bb60253e 100644 --- a/drivers/leds/trigger/ledtrig-heartbeat.c +++ b/drivers/leds/trigger/ledtrig-heartbeat.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "../leds.h" static int panic_heartbeats; @@ -154,6 +155,30 @@ static struct led_trigger heartbeat_led_trigger = { .deactivate = heartbeat_trig_deactivate, }; +static int heartbeat_pm_notifier(struct notifier_block *nb, + unsigned long pm_event, void *unused) +{ + int rc; + + switch (pm_event) { + case PM_SUSPEND_PREPARE: + case PM_HIBERNATION_PREPARE: + case PM_RESTORE_PREPARE: + led_trigger_unregister(&heartbeat_led_trigger); + break; + case PM_POST_SUSPEND: + case PM_POST_HIBERNATION: + case PM_POST_RESTORE: + rc = led_trigger_register(&heartbeat_led_trigger); + if (rc) + pr_err("could not re-register heartbeat trigger\n"); + break; + default: + break; + } + return NOTIFY_DONE; +} + static int heartbeat_reboot_notifier(struct notifier_block *nb, unsigned long code, void *unused) { @@ -168,6 +193,10 @@ static int heartbeat_panic_notifier(struct notifier_block *nb, return NOTIFY_DONE; } +static struct notifier_block heartbeat_pm_nb = { + .notifier_call = heartbeat_pm_notifier, +}; + static struct notifier_block heartbeat_reboot_nb = { .notifier_call = heartbeat_reboot_notifier, }; @@ -184,12 +213,14 @@ static int __init heartbeat_trig_init(void) atomic_notifier_chain_register(&panic_notifier_list, &heartbeat_panic_nb); register_reboot_notifier(&heartbeat_reboot_nb); + register_pm_notifier(&heartbeat_pm_nb); } return rc; } static void __exit heartbeat_trig_exit(void) { + unregister_pm_notifier(&heartbeat_pm_nb); unregister_reboot_notifier(&heartbeat_reboot_nb); atomic_notifier_chain_unregister(&panic_notifier_list, &heartbeat_panic_nb); diff --git a/include/linux/leds.h b/include/linux/leds.h index d2b13066e78155ccacc072afcd75a2ae2bcb23fa..e5e7f2e80a54dd2f15737a3ef478287458641f6e 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -42,15 +42,16 @@ struct led_classdev { #define LED_UNREGISTERING (1 << 1) /* Upper 16 bits reflect control information */ #define LED_CORE_SUSPENDRESUME (1 << 16) -#define LED_BLINK_ONESHOT (1 << 17) -#define LED_BLINK_ONESHOT_STOP (1 << 18) -#define LED_BLINK_INVERT (1 << 19) -#define LED_BLINK_BRIGHTNESS_CHANGE (1 << 20) -#define LED_BLINK_DISABLE (1 << 21) -#define LED_SYSFS_DISABLE (1 << 22) -#define LED_DEV_CAP_FLASH (1 << 23) -#define LED_HW_PLUGGABLE (1 << 24) -#define LED_PANIC_INDICATOR (1 << 25) +#define LED_BLINK_SW (1 << 17) +#define LED_BLINK_ONESHOT (1 << 18) +#define LED_BLINK_ONESHOT_STOP (1 << 19) +#define LED_BLINK_INVERT (1 << 20) +#define LED_BLINK_BRIGHTNESS_CHANGE (1 << 21) +#define LED_BLINK_DISABLE (1 << 22) +#define LED_SYSFS_DISABLE (1 << 23) +#define LED_DEV_CAP_FLASH (1 << 24) +#define LED_HW_PLUGGABLE (1 << 25) +#define LED_PANIC_INDICATOR (1 << 26) /* Set LED brightness level * Must not sleep. Use brightness_set_blocking for drivers @@ -72,8 +73,8 @@ struct led_classdev { * and if both are zero then a sensible default should be chosen. * The call should adjust the timings in that case and if it can't * match the values specified exactly. - * Deactivate blinking again when the brightness is set to a fixed - * value via the brightness_set() callback. + * Deactivate blinking again when the brightness is set to LED_OFF + * via the brightness_set() callback. */ int (*blink_set)(struct led_classdev *led_cdev, unsigned long *delay_on,