Branch data Line data Source code
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/timer.h>
21 : : #include <linux/rwsem.h>
22 : : #include <linux/leds.h>
23 : : #include <linux/slab.h>
24 : : #include "leds.h"
25 : :
26 : : /*
27 : : * Nests outside led_cdev->trigger_lock
28 : : */
29 : : static DECLARE_RWSEM(triggers_list_lock);
30 : : static LIST_HEAD(trigger_list);
31 : :
32 : : /* Used by LED Class */
33 : :
34 : 0 : ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
35 : : const char *buf, size_t count)
36 : : {
37 : 0 : struct led_classdev *led_cdev = dev_get_drvdata(dev);
38 : : char trigger_name[TRIG_NAME_MAX];
39 : : struct led_trigger *trig;
40 : : size_t len;
41 : :
42 : 0 : trigger_name[sizeof(trigger_name) - 1] = '\0';
43 : 0 : strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
44 : 0 : len = strlen(trigger_name);
45 : :
46 [ # # ][ # # ]: 0 : if (len && trigger_name[len - 1] == '\n')
47 : 0 : trigger_name[len - 1] = '\0';
48 : :
49 [ # # ]: 0 : if (!strcmp(trigger_name, "none")) {
50 : 0 : led_trigger_remove(led_cdev);
51 : 0 : return count;
52 : : }
53 : :
54 : 0 : down_read(&triggers_list_lock);
55 [ # # ]: 0 : list_for_each_entry(trig, &trigger_list, next_trig) {
56 [ # # ]: 0 : if (!strcmp(trigger_name, trig->name)) {
57 : 0 : down_write(&led_cdev->trigger_lock);
58 : 0 : led_trigger_set(led_cdev, trig);
59 : 0 : up_write(&led_cdev->trigger_lock);
60 : :
61 : 0 : up_read(&triggers_list_lock);
62 : 0 : return count;
63 : : }
64 : : }
65 : 0 : up_read(&triggers_list_lock);
66 : :
67 : 0 : return -EINVAL;
68 : : }
69 : : EXPORT_SYMBOL_GPL(led_trigger_store);
70 : :
71 : 0 : ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
72 : : char *buf)
73 : : {
74 : 0 : struct led_classdev *led_cdev = dev_get_drvdata(dev);
75 : : struct led_trigger *trig;
76 : : int len = 0;
77 : :
78 : 0 : down_read(&triggers_list_lock);
79 : 0 : down_read(&led_cdev->trigger_lock);
80 : :
81 [ # # ]: 0 : if (!led_cdev->trigger)
82 : 0 : len += sprintf(buf+len, "[none] ");
83 : : else
84 : 0 : len += sprintf(buf+len, "none ");
85 : :
86 [ # # ]: 0 : list_for_each_entry(trig, &trigger_list, next_trig) {
87 [ # # ][ # # ]: 0 : if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
88 : : trig->name))
89 : 0 : len += sprintf(buf+len, "[%s] ", trig->name);
90 : : else
91 : 0 : len += sprintf(buf+len, "%s ", trig->name);
92 : : }
93 : 0 : up_read(&led_cdev->trigger_lock);
94 : 0 : up_read(&triggers_list_lock);
95 : :
96 : 0 : len += sprintf(len+buf, "\n");
97 : 0 : return len;
98 : : }
99 : : EXPORT_SYMBOL_GPL(led_trigger_show);
100 : :
101 : : /* Caller must ensure led_cdev->trigger_lock held */
102 : 0 : void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
103 : : {
104 : : unsigned long flags;
105 : : char *event = NULL;
106 : : char *envp[2];
107 : : const char *name;
108 : :
109 [ # # ]: 0 : name = trig ? trig->name : "none";
110 : 0 : event = kasprintf(GFP_KERNEL, "TRIGGER=%s", name);
111 : :
112 : : /* Remove any existing trigger */
113 [ # # ]: 0 : if (led_cdev->trigger) {
114 : 0 : write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
115 : : list_del(&led_cdev->trig_list);
116 : 0 : write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
117 : : flags);
118 : 0 : cancel_work_sync(&led_cdev->set_brightness_work);
119 : 0 : led_stop_software_blink(led_cdev);
120 [ # # ]: 0 : if (led_cdev->trigger->deactivate)
121 : 0 : led_cdev->trigger->deactivate(led_cdev);
122 : 0 : led_cdev->trigger = NULL;
123 : 0 : led_set_brightness(led_cdev, LED_OFF);
124 : : }
125 [ # # ]: 0 : if (trig) {
126 : 0 : write_lock_irqsave(&trig->leddev_list_lock, flags);
127 : 0 : list_add_tail(&led_cdev->trig_list, &trig->led_cdevs);
128 : 0 : write_unlock_irqrestore(&trig->leddev_list_lock, flags);
129 : 0 : led_cdev->trigger = trig;
130 [ # # ]: 0 : if (trig->activate)
131 : 0 : trig->activate(led_cdev);
132 : : }
133 : :
134 [ # # ]: 0 : if (event) {
135 : 0 : envp[0] = event;
136 : 0 : envp[1] = NULL;
137 : 0 : kobject_uevent_env(&led_cdev->dev->kobj, KOBJ_CHANGE, envp);
138 : 0 : kfree(event);
139 : : }
140 : 0 : }
141 : : EXPORT_SYMBOL_GPL(led_trigger_set);
142 : :
143 : 0 : void led_trigger_remove(struct led_classdev *led_cdev)
144 : : {
145 : 0 : down_write(&led_cdev->trigger_lock);
146 : 0 : led_trigger_set(led_cdev, NULL);
147 : 0 : up_write(&led_cdev->trigger_lock);
148 : 0 : }
149 : : EXPORT_SYMBOL_GPL(led_trigger_remove);
150 : :
151 : 0 : void led_trigger_set_default(struct led_classdev *led_cdev)
152 : : {
153 : : struct led_trigger *trig;
154 : :
155 [ # # ]: 0 : if (!led_cdev->default_trigger)
156 : 0 : return;
157 : :
158 : 0 : down_read(&triggers_list_lock);
159 : 0 : down_write(&led_cdev->trigger_lock);
160 [ # # ]: 0 : list_for_each_entry(trig, &trigger_list, next_trig) {
161 [ # # ]: 0 : if (!strcmp(led_cdev->default_trigger, trig->name))
162 : 0 : led_trigger_set(led_cdev, trig);
163 : : }
164 : 0 : up_write(&led_cdev->trigger_lock);
165 : 0 : up_read(&triggers_list_lock);
166 : : }
167 : : EXPORT_SYMBOL_GPL(led_trigger_set_default);
168 : :
169 : 0 : void led_trigger_rename_static(const char *name, struct led_trigger *trig)
170 : : {
171 : : /* new name must be on a temporary string to prevent races */
172 [ # # ]: 0 : BUG_ON(name == trig->name);
173 : :
174 : 0 : down_write(&triggers_list_lock);
175 : : /* this assumes that trig->name was originaly allocated to
176 : : * non constant storage */
177 : 0 : strcpy((char *)trig->name, name);
178 : 0 : up_write(&triggers_list_lock);
179 : 0 : }
180 : : EXPORT_SYMBOL_GPL(led_trigger_rename_static);
181 : :
182 : : /* LED Trigger Interface */
183 : :
184 : 0 : int led_trigger_register(struct led_trigger *trig)
185 : : {
186 : : struct led_classdev *led_cdev;
187 : : struct led_trigger *_trig;
188 : :
189 : 0 : rwlock_init(&trig->leddev_list_lock);
190 : 0 : INIT_LIST_HEAD(&trig->led_cdevs);
191 : :
192 : 0 : down_write(&triggers_list_lock);
193 : : /* Make sure the trigger's name isn't already in use */
194 [ # # ]: 0 : list_for_each_entry(_trig, &trigger_list, next_trig) {
195 [ # # ]: 0 : if (!strcmp(_trig->name, trig->name)) {
196 : 0 : up_write(&triggers_list_lock);
197 : 0 : return -EEXIST;
198 : : }
199 : : }
200 : : /* Add to the list of led triggers */
201 : 0 : list_add_tail(&trig->next_trig, &trigger_list);
202 : 0 : up_write(&triggers_list_lock);
203 : :
204 : : /* Register with any LEDs that have this as a default trigger */
205 : 0 : down_read(&leds_list_lock);
206 [ # # ]: 0 : list_for_each_entry(led_cdev, &leds_list, node) {
207 : 0 : down_write(&led_cdev->trigger_lock);
208 [ # # ][ # # ]: 0 : if (!led_cdev->trigger && led_cdev->default_trigger &&
[ # # ]
209 : 0 : !strcmp(led_cdev->default_trigger, trig->name))
210 : 0 : led_trigger_set(led_cdev, trig);
211 : 0 : up_write(&led_cdev->trigger_lock);
212 : : }
213 : 0 : up_read(&leds_list_lock);
214 : :
215 : 0 : return 0;
216 : : }
217 : : EXPORT_SYMBOL_GPL(led_trigger_register);
218 : :
219 : 0 : void led_trigger_unregister(struct led_trigger *trig)
220 : : {
221 : : struct led_classdev *led_cdev;
222 : :
223 : : /* Remove from the list of led triggers */
224 : 0 : down_write(&triggers_list_lock);
225 : : list_del(&trig->next_trig);
226 : 0 : up_write(&triggers_list_lock);
227 : :
228 : : /* Remove anyone actively using this trigger */
229 : 0 : down_read(&leds_list_lock);
230 [ # # ]: 0 : list_for_each_entry(led_cdev, &leds_list, node) {
231 : 0 : down_write(&led_cdev->trigger_lock);
232 [ # # ]: 0 : if (led_cdev->trigger == trig)
233 : 0 : led_trigger_set(led_cdev, NULL);
234 : 0 : up_write(&led_cdev->trigger_lock);
235 : : }
236 : 0 : up_read(&leds_list_lock);
237 : 0 : }
238 : : EXPORT_SYMBOL_GPL(led_trigger_unregister);
239 : :
240 : : /* Simple LED Tigger Interface */
241 : :
242 : 0 : void led_trigger_event(struct led_trigger *trig,
243 : : enum led_brightness brightness)
244 : : {
245 : : struct list_head *entry;
246 : :
247 [ + + ]: 11364664 : if (!trig)
248 : 11364741 : return;
249 : :
250 : 11364625 : read_lock(&trig->leddev_list_lock);
251 [ + + ]: 22729510 : list_for_each(entry, &trig->led_cdevs) {
252 : : struct led_classdev *led_cdev;
253 : :
254 : 11364897 : led_cdev = list_entry(entry, struct led_classdev, trig_list);
255 : 11364897 : led_set_brightness(led_cdev, brightness);
256 : : }
257 : : read_unlock(&trig->leddev_list_lock);
258 : : }
259 : : EXPORT_SYMBOL_GPL(led_trigger_event);
260 : :
261 : 0 : static void led_trigger_blink_setup(struct led_trigger *trig,
262 : : unsigned long *delay_on,
263 : : unsigned long *delay_off,
264 : : int oneshot,
265 : : int invert)
266 : : {
267 : : struct list_head *entry;
268 : :
269 [ # # ]: 0 : if (!trig)
270 : 0 : return;
271 : :
272 : 0 : read_lock(&trig->leddev_list_lock);
273 [ # # ]: 0 : list_for_each(entry, &trig->led_cdevs) {
274 : : struct led_classdev *led_cdev;
275 : :
276 : 0 : led_cdev = list_entry(entry, struct led_classdev, trig_list);
277 [ # # ]: 0 : if (oneshot)
278 : 0 : led_blink_set_oneshot(led_cdev, delay_on, delay_off,
279 : : invert);
280 : : else
281 : 0 : led_blink_set(led_cdev, delay_on, delay_off);
282 : : }
283 : : read_unlock(&trig->leddev_list_lock);
284 : : }
285 : :
286 : 0 : void led_trigger_blink(struct led_trigger *trig,
287 : : unsigned long *delay_on,
288 : : unsigned long *delay_off)
289 : : {
290 : 0 : led_trigger_blink_setup(trig, delay_on, delay_off, 0, 0);
291 : 0 : }
292 : : EXPORT_SYMBOL_GPL(led_trigger_blink);
293 : :
294 : 0 : void led_trigger_blink_oneshot(struct led_trigger *trig,
295 : : unsigned long *delay_on,
296 : : unsigned long *delay_off,
297 : : int invert)
298 : : {
299 : 0 : led_trigger_blink_setup(trig, delay_on, delay_off, 1, invert);
300 : 0 : }
301 : : EXPORT_SYMBOL_GPL(led_trigger_blink_oneshot);
302 : :
303 : 0 : void led_trigger_register_simple(const char *name, struct led_trigger **tp)
304 : : {
305 : : struct led_trigger *trig;
306 : : int err;
307 : :
308 : : trig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
309 : :
310 [ # # ]: 0 : if (trig) {
311 : 0 : trig->name = name;
312 : 0 : err = led_trigger_register(trig);
313 [ # # ]: 0 : if (err < 0) {
314 : 0 : kfree(trig);
315 : : trig = NULL;
316 : 0 : pr_warn("LED trigger %s failed to register (%d)\n",
317 : : name, err);
318 : : }
319 : : } else {
320 : 0 : pr_warn("LED trigger %s failed to register (no memory)\n",
321 : : name);
322 : : }
323 : 0 : *tp = trig;
324 : 0 : }
325 : : EXPORT_SYMBOL_GPL(led_trigger_register_simple);
326 : :
327 : 0 : void led_trigger_unregister_simple(struct led_trigger *trig)
328 : : {
329 [ # # ]: 0 : if (trig)
330 : 0 : led_trigger_unregister(trig);
331 : 0 : kfree(trig);
332 : 0 : }
333 : : EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
334 : :
335 : : MODULE_AUTHOR("Richard Purdie");
336 : : MODULE_LICENSE("GPL");
337 : : MODULE_DESCRIPTION("LED Triggers Core");
|