Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[linux-2.6] / 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/leds.h>
23 #include "leds.h"
24
25 /*
26  * Nests outside led_cdev->trigger_lock
27  */
28 static DEFINE_RWLOCK(triggers_list_lock);
29 static LIST_HEAD(trigger_list);
30
31 ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
32                 const char *buf, size_t count)
33 {
34         struct led_classdev *led_cdev = dev_get_drvdata(dev);
35         char trigger_name[TRIG_NAME_MAX];
36         struct led_trigger *trig;
37         size_t len;
38
39         trigger_name[sizeof(trigger_name) - 1] = '\0';
40         strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
41         len = strlen(trigger_name);
42
43         if (len && trigger_name[len - 1] == '\n')
44                 trigger_name[len - 1] = '\0';
45
46         if (!strcmp(trigger_name, "none")) {
47                 write_lock(&led_cdev->trigger_lock);
48                 led_trigger_set(led_cdev, NULL);
49                 write_unlock(&led_cdev->trigger_lock);
50                 return count;
51         }
52
53         read_lock(&triggers_list_lock);
54         list_for_each_entry(trig, &trigger_list, next_trig) {
55                 if (!strcmp(trigger_name, trig->name)) {
56                         write_lock(&led_cdev->trigger_lock);
57                         led_trigger_set(led_cdev, trig);
58                         write_unlock(&led_cdev->trigger_lock);
59
60                         read_unlock(&triggers_list_lock);
61                         return count;
62                 }
63         }
64         read_unlock(&triggers_list_lock);
65
66         return -EINVAL;
67 }
68
69
70 ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
71                 char *buf)
72 {
73         struct led_classdev *led_cdev = dev_get_drvdata(dev);
74         struct led_trigger *trig;
75         int len = 0;
76
77         read_lock(&triggers_list_lock);
78         read_lock(&led_cdev->trigger_lock);
79
80         if (!led_cdev->trigger)
81                 len += sprintf(buf+len, "[none] ");
82         else
83                 len += sprintf(buf+len, "none ");
84
85         list_for_each_entry(trig, &trigger_list, next_trig) {
86                 if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
87                                                         trig->name))
88                         len += sprintf(buf+len, "[%s] ", trig->name);
89                 else
90                         len += sprintf(buf+len, "%s ", trig->name);
91         }
92         read_unlock(&led_cdev->trigger_lock);
93         read_unlock(&triggers_list_lock);
94
95         len += sprintf(len+buf, "\n");
96         return len;
97 }
98
99 void led_trigger_event(struct led_trigger *trigger,
100                         enum led_brightness brightness)
101 {
102         struct list_head *entry;
103
104         if (!trigger)
105                 return;
106
107         read_lock(&trigger->leddev_list_lock);
108         list_for_each(entry, &trigger->led_cdevs) {
109                 struct led_classdev *led_cdev;
110
111                 led_cdev = list_entry(entry, struct led_classdev, trig_list);
112                 led_set_brightness(led_cdev, brightness);
113         }
114         read_unlock(&trigger->leddev_list_lock);
115 }
116
117 /* Caller must ensure led_cdev->trigger_lock held */
118 void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
119 {
120         unsigned long flags;
121
122         /* Remove any existing trigger */
123         if (led_cdev->trigger) {
124                 write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
125                 list_del(&led_cdev->trig_list);
126                 write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, flags);
127                 if (led_cdev->trigger->deactivate)
128                         led_cdev->trigger->deactivate(led_cdev);
129                 led_set_brightness(led_cdev, LED_OFF);
130         }
131         if (trigger) {
132                 write_lock_irqsave(&trigger->leddev_list_lock, flags);
133                 list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
134                 write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
135                 if (trigger->activate)
136                         trigger->activate(led_cdev);
137         }
138         led_cdev->trigger = trigger;
139 }
140
141 void led_trigger_set_default(struct led_classdev *led_cdev)
142 {
143         struct led_trigger *trig;
144
145         if (!led_cdev->default_trigger)
146                 return;
147
148         read_lock(&triggers_list_lock);
149         write_lock(&led_cdev->trigger_lock);
150         list_for_each_entry(trig, &trigger_list, next_trig) {
151                 if (!strcmp(led_cdev->default_trigger, trig->name))
152                         led_trigger_set(led_cdev, trig);
153         }
154         write_unlock(&led_cdev->trigger_lock);
155         read_unlock(&triggers_list_lock);
156 }
157
158 int led_trigger_register(struct led_trigger *trigger)
159 {
160         struct led_classdev *led_cdev;
161
162         rwlock_init(&trigger->leddev_list_lock);
163         INIT_LIST_HEAD(&trigger->led_cdevs);
164
165         /* Add to the list of led triggers */
166         write_lock(&triggers_list_lock);
167         list_add_tail(&trigger->next_trig, &trigger_list);
168         write_unlock(&triggers_list_lock);
169
170         /* Register with any LEDs that have this as a default trigger */
171         read_lock(&leds_list_lock);
172         list_for_each_entry(led_cdev, &leds_list, node) {
173                 write_lock(&led_cdev->trigger_lock);
174                 if (!led_cdev->trigger && led_cdev->default_trigger &&
175                             !strcmp(led_cdev->default_trigger, trigger->name))
176                         led_trigger_set(led_cdev, trigger);
177                 write_unlock(&led_cdev->trigger_lock);
178         }
179         read_unlock(&leds_list_lock);
180
181         return 0;
182 }
183
184 void led_trigger_register_simple(const char *name, struct led_trigger **tp)
185 {
186         struct led_trigger *trigger;
187         int err;
188
189         trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
190
191         if (trigger) {
192                 trigger->name = name;
193                 err = led_trigger_register(trigger);
194                 if (err < 0)
195                         printk(KERN_WARNING "LED trigger %s failed to register"
196                                 " (%d)\n", name, err);
197         } else
198                 printk(KERN_WARNING "LED trigger %s failed to register"
199                         " (no memory)\n", name);
200
201         *tp = trigger;
202 }
203
204 void led_trigger_unregister(struct led_trigger *trigger)
205 {
206         struct led_classdev *led_cdev;
207
208         /* Remove from the list of led triggers */
209         write_lock(&triggers_list_lock);
210         list_del(&trigger->next_trig);
211         write_unlock(&triggers_list_lock);
212
213         /* Remove anyone actively using this trigger */
214         read_lock(&leds_list_lock);
215         list_for_each_entry(led_cdev, &leds_list, node) {
216                 write_lock(&led_cdev->trigger_lock);
217                 if (led_cdev->trigger == trigger)
218                         led_trigger_set(led_cdev, NULL);
219                 write_unlock(&led_cdev->trigger_lock);
220         }
221         read_unlock(&leds_list_lock);
222 }
223
224 void led_trigger_unregister_simple(struct led_trigger *trigger)
225 {
226         if (trigger)
227                 led_trigger_unregister(trigger);
228         kfree(trigger);
229 }
230
231 /* Used by LED Class */
232 EXPORT_SYMBOL_GPL(led_trigger_set);
233 EXPORT_SYMBOL_GPL(led_trigger_set_default);
234 EXPORT_SYMBOL_GPL(led_trigger_show);
235 EXPORT_SYMBOL_GPL(led_trigger_store);
236
237 /* LED Trigger Interface */
238 EXPORT_SYMBOL_GPL(led_trigger_register);
239 EXPORT_SYMBOL_GPL(led_trigger_unregister);
240
241 /* Simple LED Tigger Interface */
242 EXPORT_SYMBOL_GPL(led_trigger_register_simple);
243 EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
244 EXPORT_SYMBOL_GPL(led_trigger_event);
245
246 MODULE_AUTHOR("Richard Purdie");
247 MODULE_LICENSE("GPL");
248 MODULE_DESCRIPTION("LED Triggers Core");