aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/leds/led-triggers.c
diff options
context:
space:
mode:
authorRichard Purdie <rpurdie@rpsys.net>2006-03-31 05:31:05 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-31 15:18:56 -0500
commitc3bc9956ec52fb2c70f29aa894d8eec766116584 (patch)
treec93aa56e6793cfa283d13a28cfa6c02a30f6c794 /drivers/leds/led-triggers.c
parentc72a1d608dd0eb3d553a08bfdf1c0041bebaa8a0 (diff)
[PATCH] LED: add LED trigger tupport
Add support for LED triggers to the LED subsystem. "Triggers" are events which change the state of an LED. Two kinds of trigger are available, simple ones which can be added to exising code with minimum disruption and complex ones for implementing new or more complex functionality. Signed-off-by: Richard Purdie <rpurdie@rpsys.net> Cc: Russell King <rmk@arm.linux.org.uk> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/leds/led-triggers.c')
-rw-r--r--drivers/leds/led-triggers.c239
1 files changed, 239 insertions, 0 deletions
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
new file mode 100644
index 000000000000..5e2cd8be1191
--- /dev/null
+++ b/drivers/leds/led-triggers.c
@@ -0,0 +1,239 @@
1/*
2 * LED Triggers Core
3 *
4 * Copyright 2005-2006 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/config.h>
15#include <linux/module.h>
16#include <linux/kernel.h>
17#include <linux/init.h>
18#include <linux/list.h>
19#include <linux/spinlock.h>
20#include <linux/device.h>
21#include <linux/sysdev.h>
22#include <linux/timer.h>
23#include <linux/leds.h>
24#include "leds.h"
25
26/*
27 * Nests outside led_cdev->trigger_lock
28 */
29static rwlock_t triggers_list_lock = RW_LOCK_UNLOCKED;
30static LIST_HEAD(trigger_list);
31
32ssize_t led_trigger_store(struct class_device *dev, const char *buf,
33 size_t count)
34{
35 struct led_classdev *led_cdev = class_get_devdata(dev);
36 char trigger_name[TRIG_NAME_MAX];
37 struct led_trigger *trig;
38 size_t len;
39
40 trigger_name[sizeof(trigger_name) - 1] = '\0';
41 strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
42 len = strlen(trigger_name);
43
44 if (len && trigger_name[len - 1] == '\n')
45 trigger_name[len - 1] = '\0';
46
47 if (!strcmp(trigger_name, "none")) {
48 write_lock(&led_cdev->trigger_lock);
49 led_trigger_set(led_cdev, NULL);
50 write_unlock(&led_cdev->trigger_lock);
51 return count;
52 }
53
54 read_lock(&triggers_list_lock);
55 list_for_each_entry(trig, &trigger_list, next_trig) {
56 if (!strcmp(trigger_name, trig->name)) {
57 write_lock(&led_cdev->trigger_lock);
58 led_trigger_set(led_cdev, trig);
59 write_unlock(&led_cdev->trigger_lock);
60
61 read_unlock(&triggers_list_lock);
62 return count;
63 }
64 }
65 read_unlock(&triggers_list_lock);
66
67 return -EINVAL;
68}
69
70
71ssize_t led_trigger_show(struct class_device *dev, char *buf)
72{
73 struct led_classdev *led_cdev = class_get_devdata(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
99void 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 */
118void 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 }
130 if (trigger) {
131 write_lock_irqsave(&trigger->leddev_list_lock, flags);
132 list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
133 write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
134 if (trigger->activate)
135 trigger->activate(led_cdev);
136 }
137 led_cdev->trigger = trigger;
138}
139
140void led_trigger_set_default(struct led_classdev *led_cdev)
141{
142 struct led_trigger *trig;
143
144 if (!led_cdev->default_trigger)
145 return;
146
147 read_lock(&triggers_list_lock);
148 write_lock(&led_cdev->trigger_lock);
149 list_for_each_entry(trig, &trigger_list, next_trig) {
150 if (!strcmp(led_cdev->default_trigger, trig->name))
151 led_trigger_set(led_cdev, trig);
152 }
153 write_unlock(&led_cdev->trigger_lock);
154 read_unlock(&triggers_list_lock);
155}
156
157int led_trigger_register(struct led_trigger *trigger)
158{
159 struct led_classdev *led_cdev;
160
161 rwlock_init(&trigger->leddev_list_lock);
162 INIT_LIST_HEAD(&trigger->led_cdevs);
163
164 /* Add to the list of led triggers */
165 write_lock(&triggers_list_lock);
166 list_add_tail(&trigger->next_trig, &trigger_list);
167 write_unlock(&triggers_list_lock);
168
169 /* Register with any LEDs that have this as a default trigger */
170 read_lock(&leds_list_lock);
171 list_for_each_entry(led_cdev, &leds_list, node) {
172 write_lock(&led_cdev->trigger_lock);
173 if (!led_cdev->trigger && led_cdev->default_trigger &&
174 !strcmp(led_cdev->default_trigger, trigger->name))
175 led_trigger_set(led_cdev, trigger);
176 write_unlock(&led_cdev->trigger_lock);
177 }
178 read_unlock(&leds_list_lock);
179
180 return 0;
181}
182
183void led_trigger_register_simple(const char *name, struct led_trigger **tp)
184{
185 struct led_trigger *trigger;
186
187 trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
188
189 if (trigger) {
190 trigger->name = name;
191 led_trigger_register(trigger);
192 }
193 *tp = trigger;
194}
195
196void led_trigger_unregister(struct led_trigger *trigger)
197{
198 struct led_classdev *led_cdev;
199
200 /* Remove from the list of led triggers */
201 write_lock(&triggers_list_lock);
202 list_del(&trigger->next_trig);
203 write_unlock(&triggers_list_lock);
204
205 /* Remove anyone actively using this trigger */
206 read_lock(&leds_list_lock);
207 list_for_each_entry(led_cdev, &leds_list, node) {
208 write_lock(&led_cdev->trigger_lock);
209 if (led_cdev->trigger == trigger)
210 led_trigger_set(led_cdev, NULL);
211 write_unlock(&led_cdev->trigger_lock);
212 }
213 read_unlock(&leds_list_lock);
214}
215
216void led_trigger_unregister_simple(struct led_trigger *trigger)
217{
218 led_trigger_unregister(trigger);
219 kfree(trigger);
220}
221
222/* Used by LED Class */
223EXPORT_SYMBOL_GPL(led_trigger_set);
224EXPORT_SYMBOL_GPL(led_trigger_set_default);
225EXPORT_SYMBOL_GPL(led_trigger_show);
226EXPORT_SYMBOL_GPL(led_trigger_store);
227
228/* LED Trigger Interface */
229EXPORT_SYMBOL_GPL(led_trigger_register);
230EXPORT_SYMBOL_GPL(led_trigger_unregister);
231
232/* Simple LED Tigger Interface */
233EXPORT_SYMBOL_GPL(led_trigger_register_simple);
234EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
235EXPORT_SYMBOL_GPL(led_trigger_event);
236
237MODULE_AUTHOR("Richard Purdie");
238MODULE_LICENSE("GPL");
239MODULE_DESCRIPTION("LED Triggers Core");