diff options
Diffstat (limited to 'drivers/leds/led-triggers.c')
-rw-r--r-- | drivers/leds/led-triggers.c | 239 |
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 | */ | ||
29 | static rwlock_t triggers_list_lock = RW_LOCK_UNLOCKED; | ||
30 | static LIST_HEAD(trigger_list); | ||
31 | |||
32 | ssize_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 | |||
71 | ssize_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 | |||
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 | } | ||
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 | |||
140 | void 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 | |||
157 | int 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 | |||
183 | void 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 | |||
196 | void 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 | |||
216 | void led_trigger_unregister_simple(struct led_trigger *trigger) | ||
217 | { | ||
218 | led_trigger_unregister(trigger); | ||
219 | kfree(trigger); | ||
220 | } | ||
221 | |||
222 | /* Used by LED Class */ | ||
223 | EXPORT_SYMBOL_GPL(led_trigger_set); | ||
224 | EXPORT_SYMBOL_GPL(led_trigger_set_default); | ||
225 | EXPORT_SYMBOL_GPL(led_trigger_show); | ||
226 | EXPORT_SYMBOL_GPL(led_trigger_store); | ||
227 | |||
228 | /* LED Trigger Interface */ | ||
229 | EXPORT_SYMBOL_GPL(led_trigger_register); | ||
230 | EXPORT_SYMBOL_GPL(led_trigger_unregister); | ||
231 | |||
232 | /* Simple LED Tigger Interface */ | ||
233 | EXPORT_SYMBOL_GPL(led_trigger_register_simple); | ||
234 | EXPORT_SYMBOL_GPL(led_trigger_unregister_simple); | ||
235 | EXPORT_SYMBOL_GPL(led_trigger_event); | ||
236 | |||
237 | MODULE_AUTHOR("Richard Purdie"); | ||
238 | MODULE_LICENSE("GPL"); | ||
239 | MODULE_DESCRIPTION("LED Triggers Core"); | ||