diff options
Diffstat (limited to 'drivers/leds/ledtrig-timer.c')
-rw-r--r-- | drivers/leds/ledtrig-timer.c | 124 |
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 | ||
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 | static ssize_t led_delay_on_show(struct device *dev, | 22 | static 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 | ||
77 | static ssize_t led_delay_on_store(struct device *dev, | 30 | static 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 | ||
121 | static ssize_t led_delay_off_store(struct device *dev, | 58 | static 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 | ||
159 | static void timer_trig_activate(struct led_classdev *led_cdev) | 81 | static 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 | ||
193 | err_out_delayon: | 98 | err_out_delayon: |
194 | device_remove_file(led_cdev->dev, &dev_attr_delay_on); | 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 | static void timer_trig_deactivate(struct led_classdev *led_cdev) | 102 | static 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 | ||
217 | static struct led_trigger timer_led_trigger = { | 113 | static struct led_trigger timer_led_trigger = { |