Merge branch 'stable-3.2' into pandora-3.2
[pandora-kernel.git] / drivers / leds / led-triggers.c
1 /*
2  * LED Triggers Core
3  *
4  * Copyright 2005-2007 Openedhand Ltd.
5  *
6  * Author: Richard Purdie <rpurdie@openedhand.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  *
12  */
13
14 #include <linux/module.h>
15 #include <linux/kernel.h>
16 #include <linux/init.h>
17 #include <linux/list.h>
18 #include <linux/spinlock.h>
19 #include <linux/device.h>
20 #include <linux/sysdev.h>
21 #include <linux/timer.h>
22 #include <linux/rwsem.h>
23 #include <linux/leds.h>
24 #include <linux/slab.h>
25 #include "leds.h"
26
27 /*
28  * Nests outside led_cdev->trigger_lock
29  */
30 static DECLARE_RWSEM(triggers_list_lock);
31 static LIST_HEAD(trigger_list);
32
33  /* Used by LED Class */
34
35 ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
36                 const char *buf, size_t count)
37 {
38         struct led_classdev *led_cdev = dev_get_drvdata(dev);
39         char trigger_name[TRIG_NAME_MAX];
40         struct led_trigger *trig;
41         size_t len;
42
43         trigger_name[sizeof(trigger_name) - 1] = '\0';
44         strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
45         len = strlen(trigger_name);
46
47         if (len && trigger_name[len - 1] == '\n')
48                 trigger_name[len - 1] = '\0';
49
50         if (!strcmp(trigger_name, "none")) {
51                 led_trigger_remove(led_cdev);
52                 return count;
53         }
54
55         down_read(&triggers_list_lock);
56         list_for_each_entry(trig, &trigger_list, next_trig) {
57                 if (!strcmp(trigger_name, trig->name)) {
58                         down_write(&led_cdev->trigger_lock);
59                         led_trigger_set(led_cdev, trig);
60                         up_write(&led_cdev->trigger_lock);
61
62                         up_read(&triggers_list_lock);
63                         return count;
64                 }
65         }
66         up_read(&triggers_list_lock);
67
68         return -EINVAL;
69 }
70 EXPORT_SYMBOL_GPL(led_trigger_store);
71
72 ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
73                 char *buf)
74 {
75         struct led_classdev *led_cdev = dev_get_drvdata(dev);
76         struct led_trigger *trig;
77         int len = 0;
78
79         down_read(&triggers_list_lock);
80         down_read(&led_cdev->trigger_lock);
81
82         if (!led_cdev->trigger)
83                 len += sprintf(buf+len, "[none] ");
84         else
85                 len += sprintf(buf+len, "none ");
86
87         list_for_each_entry(trig, &trigger_list, next_trig) {
88                 if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
89                                                         trig->name))
90                         len += sprintf(buf+len, "[%s] ", trig->name);
91                 else
92                         len += sprintf(buf+len, "%s ", trig->name);
93         }
94         up_read(&led_cdev->trigger_lock);
95         up_read(&triggers_list_lock);
96
97         len += sprintf(len+buf, "\n");
98         return len;
99 }
100 EXPORT_SYMBOL_GPL(led_trigger_show);
101
102 /* Caller must ensure led_cdev->trigger_lock held */
103 void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
104 {
105         unsigned long flags;
106
107         /* Remove any existing trigger */
108         if (led_cdev->trigger) {
109                 write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
110                 list_del(&led_cdev->trig_list);
111                 write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
112                         flags);
113                 if (led_cdev->trigger->deactivate)
114                         led_cdev->trigger->deactivate(led_cdev);
115                 led_cdev->trigger = NULL;
116                 led_brightness_set(led_cdev, LED_OFF);
117         }
118         if (trigger) {
119                 write_lock_irqsave(&trigger->leddev_list_lock, flags);
120                 list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
121                 write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
122                 led_cdev->trigger = trigger;
123                 if (trigger->activate)
124                         trigger->activate(led_cdev);
125                 else
126                         led_set_brightness(led_cdev, trigger->prev_brightness);
127         }
128 }
129 EXPORT_SYMBOL_GPL(led_trigger_set);
130
131 void led_trigger_remove(struct led_classdev *led_cdev)
132 {
133         down_write(&led_cdev->trigger_lock);
134         led_trigger_set(led_cdev, NULL);
135         up_write(&led_cdev->trigger_lock);
136 }
137 EXPORT_SYMBOL_GPL(led_trigger_remove);
138
139 void led_trigger_set_default(struct led_classdev *led_cdev)
140 {
141         struct led_trigger *trig;
142
143         if (!led_cdev->default_trigger)
144                 return;
145
146         down_read(&triggers_list_lock);
147         down_write(&led_cdev->trigger_lock);
148         list_for_each_entry(trig, &trigger_list, next_trig) {
149                 if (!strcmp(led_cdev->default_trigger, trig->name))
150                         led_trigger_set(led_cdev, trig);
151         }
152         up_write(&led_cdev->trigger_lock);
153         up_read(&triggers_list_lock);
154 }
155 EXPORT_SYMBOL_GPL(led_trigger_set_default);
156
157 /* LED Trigger Interface */
158
159 int led_trigger_register(struct led_trigger *trigger)
160 {
161         struct led_classdev *led_cdev;
162         struct led_trigger *trig;
163
164         rwlock_init(&trigger->leddev_list_lock);
165         INIT_LIST_HEAD(&trigger->led_cdevs);
166
167         down_write(&triggers_list_lock);
168         /* Make sure the trigger's name isn't already in use */
169         list_for_each_entry(trig, &trigger_list, next_trig) {
170                 if (!strcmp(trig->name, trigger->name)) {
171                         up_write(&triggers_list_lock);
172                         return -EEXIST;
173                 }
174         }
175         /* Add to the list of led triggers */
176         list_add_tail(&trigger->next_trig, &trigger_list);
177         up_write(&triggers_list_lock);
178
179         /* Register with any LEDs that have this as a default trigger */
180         down_read(&leds_list_lock);
181         list_for_each_entry(led_cdev, &leds_list, node) {
182                 down_write(&led_cdev->trigger_lock);
183                 if (!led_cdev->trigger && led_cdev->default_trigger &&
184                             !strcmp(led_cdev->default_trigger, trigger->name))
185                         led_trigger_set(led_cdev, trigger);
186                 up_write(&led_cdev->trigger_lock);
187         }
188         up_read(&leds_list_lock);
189
190         return 0;
191 }
192 EXPORT_SYMBOL_GPL(led_trigger_register);
193
194 void led_trigger_unregister(struct led_trigger *trigger)
195 {
196         struct led_classdev *led_cdev;
197
198         /* Remove from the list of led triggers */
199         down_write(&triggers_list_lock);
200         list_del(&trigger->next_trig);
201         up_write(&triggers_list_lock);
202
203         /* Remove anyone actively using this trigger */
204         down_read(&leds_list_lock);
205         list_for_each_entry(led_cdev, &leds_list, node) {
206                 down_write(&led_cdev->trigger_lock);
207                 if (led_cdev->trigger == trigger)
208                         led_trigger_set(led_cdev, NULL);
209                 up_write(&led_cdev->trigger_lock);
210         }
211         up_read(&leds_list_lock);
212 }
213 EXPORT_SYMBOL_GPL(led_trigger_unregister);
214
215 /* Simple LED Tigger Interface */
216
217 void led_trigger_event(struct led_trigger *trigger,
218                         enum led_brightness brightness)
219 {
220         struct list_head *entry;
221
222         if (!trigger)
223                 return;
224
225         read_lock(&trigger->leddev_list_lock);
226         list_for_each(entry, &trigger->led_cdevs) {
227                 struct led_classdev *led_cdev;
228
229                 led_cdev = list_entry(entry, struct led_classdev, trig_list);
230                 led_set_brightness(led_cdev, brightness);
231         }
232         trigger->prev_brightness = brightness;
233         read_unlock(&trigger->leddev_list_lock);
234 }
235 EXPORT_SYMBOL_GPL(led_trigger_event);
236
237 void led_trigger_blink(struct led_trigger *trigger,
238                        unsigned long *delay_on,
239                        unsigned long *delay_off)
240 {
241         struct list_head *entry;
242
243         if (!trigger)
244                 return;
245
246         read_lock(&trigger->leddev_list_lock);
247         list_for_each(entry, &trigger->led_cdevs) {
248                 struct led_classdev *led_cdev;
249
250                 led_cdev = list_entry(entry, struct led_classdev, trig_list);
251                 led_blink_set(led_cdev, delay_on, delay_off);
252         }
253         read_unlock(&trigger->leddev_list_lock);
254 }
255 EXPORT_SYMBOL_GPL(led_trigger_blink);
256
257 void led_trigger_register_simple(const char *name, struct led_trigger **tp)
258 {
259         struct led_trigger *trigger;
260         int err;
261
262         trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
263
264         if (trigger) {
265                 trigger->name = name;
266                 err = led_trigger_register(trigger);
267                 if (err < 0) {
268                         kfree(trigger);
269                         trigger = NULL;
270                         printk(KERN_WARNING "LED trigger %s failed to register"
271                                 " (%d)\n", name, err);
272                 }
273         } else
274                 printk(KERN_WARNING "LED trigger %s failed to register"
275                         " (no memory)\n", name);
276
277         *tp = trigger;
278 }
279 EXPORT_SYMBOL_GPL(led_trigger_register_simple);
280
281 void led_trigger_unregister_simple(struct led_trigger *trigger)
282 {
283         if (trigger)
284                 led_trigger_unregister(trigger);
285         kfree(trigger);
286 }
287 EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
288
289 MODULE_AUTHOR("Richard Purdie");
290 MODULE_LICENSE("GPL");
291 MODULE_DESCRIPTION("LED Triggers Core");