drivers/leds/led-triggers.c: fix memory leak
[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         }
126 }
127 EXPORT_SYMBOL_GPL(led_trigger_set);
128
129 void led_trigger_remove(struct led_classdev *led_cdev)
130 {
131         down_write(&led_cdev->trigger_lock);
132         led_trigger_set(led_cdev, NULL);
133         up_write(&led_cdev->trigger_lock);
134 }
135 EXPORT_SYMBOL_GPL(led_trigger_remove);
136
137 void led_trigger_set_default(struct led_classdev *led_cdev)
138 {
139         struct led_trigger *trig;
140
141         if (!led_cdev->default_trigger)
142                 return;
143
144         down_read(&triggers_list_lock);
145         down_write(&led_cdev->trigger_lock);
146         list_for_each_entry(trig, &trigger_list, next_trig) {
147                 if (!strcmp(led_cdev->default_trigger, trig->name))
148                         led_trigger_set(led_cdev, trig);
149         }
150         up_write(&led_cdev->trigger_lock);
151         up_read(&triggers_list_lock);
152 }
153 EXPORT_SYMBOL_GPL(led_trigger_set_default);
154
155 /* LED Trigger Interface */
156
157 int led_trigger_register(struct led_trigger *trigger)
158 {
159         struct led_classdev *led_cdev;
160         struct led_trigger *trig;
161
162         rwlock_init(&trigger->leddev_list_lock);
163         INIT_LIST_HEAD(&trigger->led_cdevs);
164
165         down_write(&triggers_list_lock);
166         /* Make sure the trigger's name isn't already in use */
167         list_for_each_entry(trig, &trigger_list, next_trig) {
168                 if (!strcmp(trig->name, trigger->name)) {
169                         up_write(&triggers_list_lock);
170                         return -EEXIST;
171                 }
172         }
173         /* Add to the list of led triggers */
174         list_add_tail(&trigger->next_trig, &trigger_list);
175         up_write(&triggers_list_lock);
176
177         /* Register with any LEDs that have this as a default trigger */
178         down_read(&leds_list_lock);
179         list_for_each_entry(led_cdev, &leds_list, node) {
180                 down_write(&led_cdev->trigger_lock);
181                 if (!led_cdev->trigger && led_cdev->default_trigger &&
182                             !strcmp(led_cdev->default_trigger, trigger->name))
183                         led_trigger_set(led_cdev, trigger);
184                 up_write(&led_cdev->trigger_lock);
185         }
186         up_read(&leds_list_lock);
187
188         return 0;
189 }
190 EXPORT_SYMBOL_GPL(led_trigger_register);
191
192 void led_trigger_unregister(struct led_trigger *trigger)
193 {
194         struct led_classdev *led_cdev;
195
196         /* Remove from the list of led triggers */
197         down_write(&triggers_list_lock);
198         list_del(&trigger->next_trig);
199         up_write(&triggers_list_lock);
200
201         /* Remove anyone actively using this trigger */
202         down_read(&leds_list_lock);
203         list_for_each_entry(led_cdev, &leds_list, node) {
204                 down_write(&led_cdev->trigger_lock);
205                 if (led_cdev->trigger == trigger)
206                         led_trigger_set(led_cdev, NULL);
207                 up_write(&led_cdev->trigger_lock);
208         }
209         up_read(&leds_list_lock);
210 }
211 EXPORT_SYMBOL_GPL(led_trigger_unregister);
212
213 /* Simple LED Tigger Interface */
214
215 void led_trigger_event(struct led_trigger *trigger,
216                         enum led_brightness brightness)
217 {
218         struct list_head *entry;
219
220         if (!trigger)
221                 return;
222
223         read_lock(&trigger->leddev_list_lock);
224         list_for_each(entry, &trigger->led_cdevs) {
225                 struct led_classdev *led_cdev;
226
227                 led_cdev = list_entry(entry, struct led_classdev, trig_list);
228                 led_set_brightness(led_cdev, brightness);
229         }
230         read_unlock(&trigger->leddev_list_lock);
231 }
232 EXPORT_SYMBOL_GPL(led_trigger_event);
233
234 void led_trigger_blink(struct led_trigger *trigger,
235                        unsigned long *delay_on,
236                        unsigned long *delay_off)
237 {
238         struct list_head *entry;
239
240         if (!trigger)
241                 return;
242
243         read_lock(&trigger->leddev_list_lock);
244         list_for_each(entry, &trigger->led_cdevs) {
245                 struct led_classdev *led_cdev;
246
247                 led_cdev = list_entry(entry, struct led_classdev, trig_list);
248                 led_blink_set(led_cdev, delay_on, delay_off);
249         }
250         read_unlock(&trigger->leddev_list_lock);
251 }
252 EXPORT_SYMBOL_GPL(led_trigger_blink);
253
254 void led_trigger_register_simple(const char *name, struct led_trigger **tp)
255 {
256         struct led_trigger *trigger;
257         int err;
258
259         trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
260
261         if (trigger) {
262                 trigger->name = name;
263                 err = led_trigger_register(trigger);
264                 if (err < 0) {
265                         kfree(trigger);
266                         trigger = NULL;
267                         printk(KERN_WARNING "LED trigger %s failed to register"
268                                 " (%d)\n", name, err);
269                 }
270         } else
271                 printk(KERN_WARNING "LED trigger %s failed to register"
272                         " (no memory)\n", name);
273
274         *tp = trigger;
275 }
276 EXPORT_SYMBOL_GPL(led_trigger_register_simple);
277
278 void led_trigger_unregister_simple(struct led_trigger *trigger)
279 {
280         if (trigger)
281                 led_trigger_unregister(trigger);
282         kfree(trigger);
283 }
284 EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
285
286 MODULE_AUTHOR("Richard Purdie");
287 MODULE_LICENSE("GPL");
288 MODULE_DESCRIPTION("LED Triggers Core");