Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

leds: delay led_set_brightness if stopping soft-blink

Delay execution of led_set_brightness() if need to stop soft-blink
timer.

This allows led_set_brightness to be called in hard-irq context even if
soft-blink was activated on that LED.

Signed-off-by: Fabio Baltieri <fabio.baltieri@gmail.com>
Cc: Pawel Moll <pawel.moll@arm.com>
Signed-off-by: Bryan Wu <bryan.wu@canonical.com>

authored by

Fabio Baltieri and committed by
Bryan Wu
d23a22a7 490dcee9

+37 -4
+15
drivers/leds/led-class.c
··· 124 124 mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay)); 125 125 } 126 126 127 + static void set_brightness_delayed(struct work_struct *ws) 128 + { 129 + struct led_classdev *led_cdev = 130 + container_of(ws, struct led_classdev, set_brightness_work); 131 + 132 + led_stop_software_blink(led_cdev); 133 + 134 + __led_set_brightness(led_cdev, led_cdev->delayed_set_value); 135 + } 136 + 127 137 /** 128 138 * led_classdev_suspend - suspend an led_classdev. 129 139 * @led_cdev: the led_classdev to suspend. ··· 201 191 202 192 led_update_brightness(led_cdev); 203 193 194 + INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed); 195 + 204 196 init_timer(&led_cdev->blink_timer); 205 197 led_cdev->blink_timer.function = led_timer_function; 206 198 led_cdev->blink_timer.data = (unsigned long)led_cdev; ··· 233 221 up_write(&led_cdev->trigger_lock); 234 222 #endif 235 223 224 + cancel_work_sync(&led_cdev->set_brightness_work); 225 + 236 226 /* Stop blinking */ 227 + led_stop_software_blink(led_cdev); 237 228 led_set_brightness(led_cdev, LED_OFF); 238 229 239 230 device_unregister(led_cdev->dev);
+13 -3
drivers/leds/led-core.c
··· 103 103 } 104 104 EXPORT_SYMBOL(led_blink_set_oneshot); 105 105 106 - void led_set_brightness(struct led_classdev *led_cdev, 107 - enum led_brightness brightness) 106 + void led_stop_software_blink(struct led_classdev *led_cdev) 108 107 { 109 - /* stop and clear soft-blink timer */ 110 108 del_timer_sync(&led_cdev->blink_timer); 111 109 led_cdev->blink_delay_on = 0; 112 110 led_cdev->blink_delay_off = 0; 111 + } 112 + EXPORT_SYMBOL_GPL(led_stop_software_blink); 113 + 114 + void led_set_brightness(struct led_classdev *led_cdev, 115 + enum led_brightness brightness) 116 + { 117 + /* delay brightness setting if need to stop soft-blink timer */ 118 + if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) { 119 + led_cdev->delayed_set_value = brightness; 120 + schedule_work(&led_cdev->set_brightness_work); 121 + return; 122 + } 113 123 114 124 __led_set_brightness(led_cdev, brightness); 115 125 }
+3 -1
drivers/leds/led-triggers.c
··· 109 109 list_del(&led_cdev->trig_list); 110 110 write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, 111 111 flags); 112 + cancel_work_sync(&led_cdev->set_brightness_work); 113 + led_stop_software_blink(led_cdev); 112 114 if (led_cdev->trigger->deactivate) 113 115 led_cdev->trigger->deactivate(led_cdev); 114 116 led_cdev->trigger = NULL; ··· 226 224 struct led_classdev *led_cdev; 227 225 228 226 led_cdev = list_entry(entry, struct led_classdev, trig_list); 229 - __led_set_brightness(led_cdev, brightness); 227 + led_set_brightness(led_cdev, brightness); 230 228 } 231 229 read_unlock(&trig->leddev_list_lock); 232 230 }
+2
drivers/leds/leds.h
··· 32 32 return led_cdev->brightness; 33 33 } 34 34 35 + void led_stop_software_blink(struct led_classdev *led_cdev); 36 + 35 37 extern struct rw_semaphore leds_list_lock; 36 38 extern struct list_head leds_list; 37 39
+4
include/linux/leds.h
··· 16 16 #include <linux/spinlock.h> 17 17 #include <linux/rwsem.h> 18 18 #include <linux/timer.h> 19 + #include <linux/workqueue.h> 19 20 20 21 struct device; 21 22 /* ··· 69 68 unsigned long blink_delay_on, blink_delay_off; 70 69 struct timer_list blink_timer; 71 70 int blink_brightness; 71 + 72 + struct work_struct set_brightness_work; 73 + int delayed_set_value; 72 74 73 75 #ifdef CONFIG_LEDS_TRIGGERS 74 76 /* Protects the trigger data below */