aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/leds/ledtrig-timer.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-11-11 17:05:21 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2010-11-12 10:55:32 -0500
commit5ada28bf76752e33dce3d807bf0dfbe6d1b943ad (patch)
tree03ce703dce3c5f5afad16a81556608700849d6c5 /drivers/leds/ledtrig-timer.c
parent52ca0e84b05595cf74f1ff772b3f9807256b1b27 (diff)
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>
Diffstat (limited to 'drivers/leds/ledtrig-timer.c')
-rw-r--r--drivers/leds/ledtrig-timer.c124
1 files changed, 10 insertions, 114 deletions
diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c
index 82b77bd482ff..b09bcbeade9c 100644
--- a/drivers/leds/ledtrig-timer.c
+++ b/drivers/leds/ledtrig-timer.c
@@ -12,73 +12,25 @@
12 */ 12 */
13 13
14#include <linux/module.h> 14#include <linux/module.h>
15#include <linux/jiffies.h>
16#include <linux/kernel.h> 15#include <linux/kernel.h>
17#include <linux/init.h> 16#include <linux/init.h>
18#include <linux/list.h>
19#include <linux/spinlock.h>
20#include <linux/device.h> 17#include <linux/device.h>
21#include <linux/sysdev.h>
22#include <linux/timer.h>
23#include <linux/ctype.h> 18#include <linux/ctype.h>
24#include <linux/leds.h> 19#include <linux/leds.h>
25#include <linux/slab.h>
26#include "leds.h" 20#include "leds.h"
27 21
28struct 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
37static 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
68static ssize_t led_delay_on_show(struct device *dev, 22static ssize_t led_delay_on_show(struct device *dev,
69 struct device_attribute *attr, char *buf) 23 struct device_attribute *attr, char *buf)
70{ 24{
71 struct led_classdev *led_cdev = dev_get_drvdata(dev); 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
77static ssize_t led_delay_on_store(struct device *dev, 30static ssize_t led_delay_on_store(struct device *dev,
78 struct device_attribute *attr, const char *buf, size_t size) 31 struct device_attribute *attr, const char *buf, size_t size)
79{ 32{
80 struct led_classdev *led_cdev = dev_get_drvdata(dev); 33 struct led_classdev *led_cdev = dev_get_drvdata(dev);
81 struct timer_trig_data *timer_data = led_cdev->trigger_data;
82 int ret = -EINVAL; 34 int ret = -EINVAL;
83 char *after; 35 char *after;
84 unsigned long state = simple_strtoul(buf, &after, 10); 36 unsigned long state = simple_strtoul(buf, &after, 10);
@@ -88,21 +40,7 @@ static ssize_t led_delay_on_store(struct device *dev,
88 count++; 40 count++;
89 41
90 if (count == size) { 42 if (count == size) {
91 if (timer_data->delay_on != state) { 43 led_blink_set(led_cdev, &state, &led_cdev->blink_delay_off);
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 }
106 ret = count; 44 ret = count;
107 } 45 }
108 46
@@ -113,16 +51,14 @@ static ssize_t led_delay_off_show(struct device *dev,
113 struct device_attribute *attr, char *buf) 51 struct device_attribute *attr, char *buf)
114{ 52{
115 struct led_classdev *led_cdev = dev_get_drvdata(dev); 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
121static ssize_t led_delay_off_store(struct device *dev, 58static ssize_t led_delay_off_store(struct device *dev,
122 struct device_attribute *attr, const char *buf, size_t size) 59 struct device_attribute *attr, const char *buf, size_t size)
123{ 60{
124 struct led_classdev *led_cdev = dev_get_drvdata(dev); 61 struct led_classdev *led_cdev = dev_get_drvdata(dev);
125 struct timer_trig_data *timer_data = led_cdev->trigger_data;
126 int ret = -EINVAL; 62 int ret = -EINVAL;
127 char *after; 63 char *after;
128 unsigned long state = simple_strtoul(buf, &after, 10); 64 unsigned long state = simple_strtoul(buf, &after, 10);
@@ -132,21 +68,7 @@ static ssize_t led_delay_off_store(struct device *dev,
132 count++; 68 count++;
133 69
134 if (count == size) { 70 if (count == size) {
135 if (timer_data->delay_off != state) { 71 led_blink_set(led_cdev, &led_cdev->blink_delay_on, &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 }
150 ret = count; 72 ret = count;
151 } 73 }
152 74
@@ -158,60 +80,34 @@ static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);
158 80
159static void timer_trig_activate(struct led_classdev *led_cdev) 81static void timer_trig_activate(struct led_classdev *led_cdev)
160{ 82{
161 struct timer_trig_data *timer_data;
162 int rc; 83 int rc;
163 84
164 timer_data = kzalloc(sizeof(struct timer_trig_data), GFP_KERNEL); 85 led_cdev->trigger_data = NULL;
165 if (!timer_data)
166 return;
167
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 86
177 rc = device_create_file(led_cdev->dev, &dev_attr_delay_on); 87 rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
178 if (rc) 88 if (rc)
179 goto err_out; 89 return;
180 rc = device_create_file(led_cdev->dev, &dev_attr_delay_off); 90 rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
181 if (rc) 91 if (rc)
182 goto err_out_delayon; 92 goto err_out_delayon;
183 93
184 /* If there is hardware support for blinking, start one 94 led_cdev->trigger_data = (void *)1;
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);
190 95
191 return; 96 return;
192 97
193err_out_delayon: 98err_out_delayon:
194 device_remove_file(led_cdev->dev, &dev_attr_delay_on); 99 device_remove_file(led_cdev->dev, &dev_attr_delay_on);
195err_out:
196 led_cdev->trigger_data = NULL;
197 kfree(timer_data);
198} 100}
199 101
200static void timer_trig_deactivate(struct led_classdev *led_cdev) 102static void timer_trig_deactivate(struct led_classdev *led_cdev)
201{ 103{
202 struct timer_trig_data *timer_data = led_cdev->trigger_data; 104 if (led_cdev->trigger_data) {
203 unsigned long on = 0, off = 0;
204
205 if (timer_data) {
206 device_remove_file(led_cdev->dev, &dev_attr_delay_on); 105 device_remove_file(led_cdev->dev, &dev_attr_delay_on);
207 device_remove_file(led_cdev->dev, &dev_attr_delay_off); 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 */ 109 /* Stop blinking */
213 if (led_cdev->blink_set) 110 led_brightness_set(led_cdev, LED_OFF);
214 led_cdev->blink_set(led_cdev, &on, &off);
215} 111}
216 112
217static struct led_trigger timer_led_trigger = { 113static struct led_trigger timer_led_trigger = {