Commit 5ada28bf76752e33dce3d807bf0dfbe6d1b943ad

Authored by Johannes Berg
Committed by Linus Torvalds
1 parent 52ca0e84b0

led-class: always implement blinking

Currently, blinking LEDs can be awkward because it is not guaranteed that
all LEDs implement blinking.  The trigger that wants it to blink then
needs to implement its own timer solution.

Rather than require that, add led_blink_set() API that triggers can use.
This function will attempt to use hw blinking, but if that fails
implements a timer for it.  To stop blinking again, brightness_set() also
needs to be wrapped into API that will stop the software blink.

As a result of this, the timer trigger becomes a very trivial one, and
hopefully we can finally see triggers using blinking as well because it's
always easy to use.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Acked-by: Richard Purdie <rpurdie@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 7 changed files with 171 additions and 133 deletions Side-by-side Diff

Documentation/leds-class.txt
... ... @@ -60,15 +60,18 @@
60 60  
61 61 Some LEDs can be programmed to blink without any CPU interaction. To
62 62 support this feature, a LED driver can optionally implement the
63   -blink_set() function (see <linux/leds.h>). If implemented, triggers can
64   -attempt to use it before falling back to software timers. The blink_set()
65   -function should return 0 if the blink setting is supported, or -EINVAL
66   -otherwise, which means that LED blinking will be handled by software.
  63 +blink_set() function (see <linux/leds.h>). To set an LED to blinking,
  64 +however, it is better to use use the API function led_blink_set(),
  65 +as it will check and implement software fallback if necessary.
67 66  
68   -The blink_set() function should choose a user friendly blinking
69   -value if it is called with *delay_on==0 && *delay_off==0 parameters. In
70   -this case the driver should give back the chosen value through delay_on
71   -and delay_off parameters to the leds subsystem.
  67 +To turn off blinking again, use the API function led_brightness_set()
  68 +as that will not just set the LED brightness but also stop any software
  69 +timers that may have been required for blinking.
  70 +
  71 +The blink_set() function should choose a user friendly blinking value
  72 +if it is called with *delay_on==0 && *delay_off==0 parameters. In this
  73 +case the driver should give back the chosen value through delay_on and
  74 +delay_off parameters to the leds subsystem.
72 75  
73 76 Setting the brightness to zero with brightness_set() callback function
74 77 should completely turn off the LED and cancel the previously programmed
drivers/leds/Kconfig
... ... @@ -10,7 +10,7 @@
10 10 if NEW_LEDS
11 11  
12 12 config LEDS_CLASS
13   - tristate "LED Class Support"
  13 + bool "LED Class Support"
14 14 help
15 15 This option enables the led sysfs class in /sys/class/leds. You'll
16 16 need this to do anything useful with LEDs. If unsure, say N.
drivers/leds/led-class.c
... ... @@ -81,6 +81,79 @@
81 81 __ATTR_NULL,
82 82 };
83 83  
  84 +static void led_timer_function(unsigned long data)
  85 +{
  86 + struct led_classdev *led_cdev = (void *)data;
  87 + unsigned long brightness;
  88 + unsigned long delay;
  89 +
  90 + if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
  91 + led_set_brightness(led_cdev, LED_OFF);
  92 + return;
  93 + }
  94 +
  95 + brightness = led_get_brightness(led_cdev);
  96 + if (!brightness) {
  97 + /* Time to switch the LED on. */
  98 + brightness = led_cdev->blink_brightness;
  99 + delay = led_cdev->blink_delay_on;
  100 + } else {
  101 + /* Store the current brightness value to be able
  102 + * to restore it when the delay_off period is over.
  103 + */
  104 + led_cdev->blink_brightness = brightness;
  105 + brightness = LED_OFF;
  106 + delay = led_cdev->blink_delay_off;
  107 + }
  108 +
  109 + led_set_brightness(led_cdev, brightness);
  110 +
  111 + mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
  112 +}
  113 +
  114 +static void led_stop_software_blink(struct led_classdev *led_cdev)
  115 +{
  116 + /* deactivate previous settings */
  117 + del_timer_sync(&led_cdev->blink_timer);
  118 + led_cdev->blink_delay_on = 0;
  119 + led_cdev->blink_delay_off = 0;
  120 +}
  121 +
  122 +static void led_set_software_blink(struct led_classdev *led_cdev,
  123 + unsigned long delay_on,
  124 + unsigned long delay_off)
  125 +{
  126 + int current_brightness;
  127 +
  128 + current_brightness = led_get_brightness(led_cdev);
  129 + if (current_brightness)
  130 + led_cdev->blink_brightness = current_brightness;
  131 + if (!led_cdev->blink_brightness)
  132 + led_cdev->blink_brightness = led_cdev->max_brightness;
  133 +
  134 + if (delay_on == led_cdev->blink_delay_on &&
  135 + delay_off == led_cdev->blink_delay_off)
  136 + return;
  137 +
  138 + led_stop_software_blink(led_cdev);
  139 +
  140 + led_cdev->blink_delay_on = delay_on;
  141 + led_cdev->blink_delay_off = delay_off;
  142 +
  143 + /* never on - don't blink */
  144 + if (!delay_on)
  145 + return;
  146 +
  147 + /* never off - just set to brightness */
  148 + if (!delay_off) {
  149 + led_set_brightness(led_cdev, led_cdev->blink_brightness);
  150 + return;
  151 + }
  152 +
  153 + mod_timer(&led_cdev->blink_timer, jiffies + 1);
  154 +}
  155 +
  156 +
84 157 /**
85 158 * led_classdev_suspend - suspend an led_classdev.
86 159 * @led_cdev: the led_classdev to suspend.
... ... @@ -148,6 +221,10 @@
148 221  
149 222 led_update_brightness(led_cdev);
150 223  
  224 + init_timer(&led_cdev->blink_timer);
  225 + led_cdev->blink_timer.function = led_timer_function;
  226 + led_cdev->blink_timer.data = (unsigned long)led_cdev;
  227 +
151 228 #ifdef CONFIG_LEDS_TRIGGERS
152 229 led_trigger_set_default(led_cdev);
153 230 #endif
... ... @@ -157,7 +234,6 @@
157 234  
158 235 return 0;
159 236 }
160   -
161 237 EXPORT_SYMBOL_GPL(led_classdev_register);
162 238  
163 239 /**
... ... @@ -175,6 +251,9 @@
175 251 up_write(&led_cdev->trigger_lock);
176 252 #endif
177 253  
  254 + /* Stop blinking */
  255 + led_brightness_set(led_cdev, LED_OFF);
  256 +
178 257 device_unregister(led_cdev->dev);
179 258  
180 259 down_write(&leds_list_lock);
... ... @@ -182,6 +261,30 @@
182 261 up_write(&leds_list_lock);
183 262 }
184 263 EXPORT_SYMBOL_GPL(led_classdev_unregister);
  264 +
  265 +void led_blink_set(struct led_classdev *led_cdev,
  266 + unsigned long *delay_on,
  267 + unsigned long *delay_off)
  268 +{
  269 + if (led_cdev->blink_set &&
  270 + led_cdev->blink_set(led_cdev, delay_on, delay_off))
  271 + return;
  272 +
  273 + /* blink with 1 Hz as default if nothing specified */
  274 + if (!*delay_on && !*delay_off)
  275 + *delay_on = *delay_off = 500;
  276 +
  277 + led_set_software_blink(led_cdev, *delay_on, *delay_off);
  278 +}
  279 +EXPORT_SYMBOL(led_blink_set);
  280 +
  281 +void led_brightness_set(struct led_classdev *led_cdev,
  282 + enum led_brightness brightness)
  283 +{
  284 + led_stop_software_blink(led_cdev);
  285 + led_cdev->brightness_set(led_cdev, brightness);
  286 +}
  287 +EXPORT_SYMBOL(led_brightness_set);
185 288  
186 289 static int __init leds_init(void)
187 290 {
drivers/leds/led-triggers.c
... ... @@ -113,7 +113,7 @@
113 113 if (led_cdev->trigger->deactivate)
114 114 led_cdev->trigger->deactivate(led_cdev);
115 115 led_cdev->trigger = NULL;
116   - led_set_brightness(led_cdev, LED_OFF);
  116 + led_brightness_set(led_cdev, LED_OFF);
117 117 }
118 118 if (trigger) {
119 119 write_lock_irqsave(&trigger->leddev_list_lock, flags);
drivers/leds/ledtrig-timer.c
... ... @@ -12,73 +12,25 @@
12 12 */
13 13  
14 14 #include <linux/module.h>
15   -#include <linux/jiffies.h>
16 15 #include <linux/kernel.h>
17 16 #include <linux/init.h>
18   -#include <linux/list.h>
19   -#include <linux/spinlock.h>
20 17 #include <linux/device.h>
21   -#include <linux/sysdev.h>
22   -#include <linux/timer.h>
23 18 #include <linux/ctype.h>
24 19 #include <linux/leds.h>
25   -#include <linux/slab.h>
26 20 #include "leds.h"
27 21  
28   -struct timer_trig_data {
29   - int brightness_on; /* LED brightness during "on" period.
30   - * (LED_OFF < brightness_on <= LED_FULL)
31   - */
32   - unsigned long delay_on; /* milliseconds on */
33   - unsigned long delay_off; /* milliseconds off */
34   - struct timer_list timer;
35   -};
36   -
37   -static void led_timer_function(unsigned long data)
38   -{
39   - struct led_classdev *led_cdev = (struct led_classdev *) data;
40   - struct timer_trig_data *timer_data = led_cdev->trigger_data;
41   - unsigned long brightness;
42   - unsigned long delay;
43   -
44   - if (!timer_data->delay_on || !timer_data->delay_off) {
45   - led_set_brightness(led_cdev, LED_OFF);
46   - return;
47   - }
48   -
49   - brightness = led_get_brightness(led_cdev);
50   - if (!brightness) {
51   - /* Time to switch the LED on. */
52   - brightness = timer_data->brightness_on;
53   - delay = timer_data->delay_on;
54   - } else {
55   - /* Store the current brightness value to be able
56   - * to restore it when the delay_off period is over.
57   - */
58   - timer_data->brightness_on = brightness;
59   - brightness = LED_OFF;
60   - delay = timer_data->delay_off;
61   - }
62   -
63   - led_set_brightness(led_cdev, brightness);
64   -
65   - mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay));
66   -}
67   -
68 22 static ssize_t led_delay_on_show(struct device *dev,
69 23 struct device_attribute *attr, char *buf)
70 24 {
71 25 struct led_classdev *led_cdev = dev_get_drvdata(dev);
72   - struct timer_trig_data *timer_data = led_cdev->trigger_data;
73 26  
74   - return sprintf(buf, "%lu\n", timer_data->delay_on);
  27 + return sprintf(buf, "%lu\n", led_cdev->blink_delay_on);
75 28 }
76 29  
77 30 static ssize_t led_delay_on_store(struct device *dev,
78 31 struct device_attribute *attr, const char *buf, size_t size)
79 32 {
80 33 struct led_classdev *led_cdev = dev_get_drvdata(dev);
81   - struct timer_trig_data *timer_data = led_cdev->trigger_data;
82 34 int ret = -EINVAL;
83 35 char *after;
84 36 unsigned long state = simple_strtoul(buf, &after, 10);
... ... @@ -88,21 +40,7 @@
88 40 count++;
89 41  
90 42 if (count == size) {
91   - if (timer_data->delay_on != state) {
92   - /* the new value differs from the previous */
93   - timer_data->delay_on = state;
94   -
95   - /* deactivate previous settings */
96   - del_timer_sync(&timer_data->timer);
97   -
98   - /* try to activate hardware acceleration, if any */
99   - if (!led_cdev->blink_set ||
100   - led_cdev->blink_set(led_cdev,
101   - &timer_data->delay_on, &timer_data->delay_off)) {
102   - /* no hardware acceleration, blink via timer */
103   - mod_timer(&timer_data->timer, jiffies + 1);
104   - }
105   - }
  43 + led_blink_set(led_cdev, &state, &led_cdev->blink_delay_off);
106 44 ret = count;
107 45 }
108 46  
109 47  
110 48  
... ... @@ -113,16 +51,14 @@
113 51 struct device_attribute *attr, char *buf)
114 52 {
115 53 struct led_classdev *led_cdev = dev_get_drvdata(dev);
116   - struct timer_trig_data *timer_data = led_cdev->trigger_data;
117 54  
118   - return sprintf(buf, "%lu\n", timer_data->delay_off);
  55 + return sprintf(buf, "%lu\n", led_cdev->blink_delay_off);
119 56 }
120 57  
121 58 static ssize_t led_delay_off_store(struct device *dev,
122 59 struct device_attribute *attr, const char *buf, size_t size)
123 60 {
124 61 struct led_classdev *led_cdev = dev_get_drvdata(dev);
125   - struct timer_trig_data *timer_data = led_cdev->trigger_data;
126 62 int ret = -EINVAL;
127 63 char *after;
128 64 unsigned long state = simple_strtoul(buf, &after, 10);
... ... @@ -132,21 +68,7 @@
132 68 count++;
133 69  
134 70 if (count == size) {
135   - if (timer_data->delay_off != state) {
136   - /* the new value differs from the previous */
137   - timer_data->delay_off = state;
138   -
139   - /* deactivate previous settings */
140   - del_timer_sync(&timer_data->timer);
141   -
142   - /* try to activate hardware acceleration, if any */
143   - if (!led_cdev->blink_set ||
144   - led_cdev->blink_set(led_cdev,
145   - &timer_data->delay_on, &timer_data->delay_off)) {
146   - /* no hardware acceleration, blink via timer */
147   - mod_timer(&timer_data->timer, jiffies + 1);
148   - }
149   - }
  71 + led_blink_set(led_cdev, &led_cdev->blink_delay_on, &state);
150 72 ret = count;
151 73 }
152 74  
153 75  
154 76  
155 77  
156 78  
157 79  
158 80  
159 81  
160 82  
... ... @@ -158,60 +80,34 @@
158 80  
159 81 static void timer_trig_activate(struct led_classdev *led_cdev)
160 82 {
161   - struct timer_trig_data *timer_data;
162 83 int rc;
163 84  
164   - timer_data = kzalloc(sizeof(struct timer_trig_data), GFP_KERNEL);
165   - if (!timer_data)
166   - return;
  85 + led_cdev->trigger_data = NULL;
167 86  
168   - timer_data->brightness_on = led_get_brightness(led_cdev);
169   - if (timer_data->brightness_on == LED_OFF)
170   - timer_data->brightness_on = led_cdev->max_brightness;
171   - led_cdev->trigger_data = timer_data;
172   -
173   - init_timer(&timer_data->timer);
174   - timer_data->timer.function = led_timer_function;
175   - timer_data->timer.data = (unsigned long) led_cdev;
176   -
177 87 rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
178 88 if (rc)
179   - goto err_out;
  89 + return;
180 90 rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
181 91 if (rc)
182 92 goto err_out_delayon;
183 93  
184   - /* If there is hardware support for blinking, start one
185   - * user friendly blink rate chosen by the driver.
186   - */
187   - if (led_cdev->blink_set)
188   - led_cdev->blink_set(led_cdev,
189   - &timer_data->delay_on, &timer_data->delay_off);
  94 + led_cdev->trigger_data = (void *)1;
190 95  
191 96 return;
192 97  
193 98 err_out_delayon:
194 99 device_remove_file(led_cdev->dev, &dev_attr_delay_on);
195   -err_out:
196   - led_cdev->trigger_data = NULL;
197   - kfree(timer_data);
198 100 }
199 101  
200 102 static void timer_trig_deactivate(struct led_classdev *led_cdev)
201 103 {
202   - struct timer_trig_data *timer_data = led_cdev->trigger_data;
203   - unsigned long on = 0, off = 0;
204   -
205   - if (timer_data) {
  104 + if (led_cdev->trigger_data) {
206 105 device_remove_file(led_cdev->dev, &dev_attr_delay_on);
207 106 device_remove_file(led_cdev->dev, &dev_attr_delay_off);
208   - del_timer_sync(&timer_data->timer);
209   - kfree(timer_data);
210 107 }
211 108  
212   - /* If there is hardware support for blinking, stop it */
213   - if (led_cdev->blink_set)
214   - led_cdev->blink_set(led_cdev, &on, &off);
  109 + /* Stop blinking */
  110 + led_brightness_set(led_cdev, LED_OFF);
215 111 }
216 112  
217 113 static struct led_trigger timer_led_trigger = {
drivers/net/wireless/rt2x00/Kconfig
... ... @@ -221,9 +221,6 @@
221 221 boolean
222 222 default y if (RT2X00_LIB=y && LEDS_CLASS=y) || (RT2X00_LIB=m && LEDS_CLASS!=n)
223 223  
224   -comment "rt2x00 leds support disabled due to modularized LEDS_CLASS and built-in rt2x00"
225   - depends on RT2X00_LIB=y && LEDS_CLASS=m
226   -
227 224 config RT2X00_LIB_DEBUGFS
228 225 bool "Ralink debugfs support"
229 226 depends on RT2X00_LIB && MAC80211_DEBUGFS
include/linux/leds.h
... ... @@ -15,6 +15,7 @@
15 15 #include <linux/list.h>
16 16 #include <linux/spinlock.h>
17 17 #include <linux/rwsem.h>
  18 +#include <linux/timer.h>
18 19  
19 20 struct device;
20 21 /*
... ... @@ -45,10 +46,14 @@
45 46 /* Get LED brightness level */
46 47 enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
47 48  
48   - /* Activate hardware accelerated blink, delays are in
49   - * miliseconds and if none is provided then a sensible default
50   - * should be chosen. The call can adjust the timings if it can't
51   - * match the values specified exactly. */
  49 + /*
  50 + * Activate hardware accelerated blink, delays are in milliseconds
  51 + * and if both are zero then a sensible default should be chosen.
  52 + * The call should adjust the timings in that case and if it can't
  53 + * match the values specified exactly.
  54 + * Deactivate blinking again when the brightness is set to a fixed
  55 + * value via the brightness_set() callback.
  56 + */
52 57 int (*blink_set)(struct led_classdev *led_cdev,
53 58 unsigned long *delay_on,
54 59 unsigned long *delay_off);
... ... @@ -57,6 +62,10 @@
57 62 struct list_head node; /* LED Device list */
58 63 const char *default_trigger; /* Trigger to use */
59 64  
  65 + unsigned long blink_delay_on, blink_delay_off;
  66 + struct timer_list blink_timer;
  67 + int blink_brightness;
  68 +
60 69 #ifdef CONFIG_LEDS_TRIGGERS
61 70 /* Protects the trigger data below */
62 71 struct rw_semaphore trigger_lock;
... ... @@ -72,6 +81,36 @@
72 81 extern void led_classdev_unregister(struct led_classdev *led_cdev);
73 82 extern void led_classdev_suspend(struct led_classdev *led_cdev);
74 83 extern void led_classdev_resume(struct led_classdev *led_cdev);
  84 +
  85 +/**
  86 + * led_blink_set - set blinking with software fallback
  87 + * @led_cdev: the LED to start blinking
  88 + * @delay_on: the time it should be on (in ms)
  89 + * @delay_off: the time it should ble off (in ms)
  90 + *
  91 + * This function makes the LED blink, attempting to use the
  92 + * hardware acceleration if possible, but falling back to
  93 + * software blinking if there is no hardware blinking or if
  94 + * the LED refuses the passed values.
  95 + *
  96 + * Note that if software blinking is active, simply calling
  97 + * led_cdev->brightness_set() will not stop the blinking,
  98 + * use led_classdev_brightness_set() instead.
  99 + */
  100 +extern void led_blink_set(struct led_classdev *led_cdev,
  101 + unsigned long *delay_on,
  102 + unsigned long *delay_off);
  103 +/**
  104 + * led_brightness_set - set LED brightness
  105 + * @led_cdev: the LED to set
  106 + * @brightness: the brightness to set it to
  107 + *
  108 + * Set an LED's brightness, and, if necessary, cancel the
  109 + * software blink timer that implements blinking when the
  110 + * hardware doesn't.
  111 + */
  112 +extern void led_brightness_set(struct led_classdev *led_cdev,
  113 + enum led_brightness brightness);
75 114  
76 115 /*
77 116 * LED Triggers