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 led_classdev *led_cdev;
246 : :
247 [ + + ]: 17326378 : if (!trig)
248 : 17343561 : return;
249 : :
250 : 17306884 : read_lock(&trig->leddev_list_lock);
251 [ + + ]: 34597193 : list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list)
252 : 17309324 : led_set_brightness(led_cdev, brightness);
253 : : read_unlock(&trig->leddev_list_lock);
254 : : }
255 : : EXPORT_SYMBOL_GPL(led_trigger_event);
256 : :
257 : 0 : static void led_trigger_blink_setup(struct led_trigger *trig,
258 : : unsigned long *delay_on,
259 : : unsigned long *delay_off,
260 : : int oneshot,
261 : : int invert)
262 : : {
263 : : struct led_classdev *led_cdev;
264 : :
265 [ # # ]: 0 : if (!trig)
266 : 0 : return;
267 : :
268 : 0 : read_lock(&trig->leddev_list_lock);
269 [ # # ]: 0 : list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) {
270 [ # # ]: 0 : if (oneshot)
271 : 0 : led_blink_set_oneshot(led_cdev, delay_on, delay_off,
272 : : invert);
273 : : else
274 : 0 : led_blink_set(led_cdev, delay_on, delay_off);
275 : : }
276 : : read_unlock(&trig->leddev_list_lock);
277 : : }
278 : :
279 : 0 : void led_trigger_blink(struct led_trigger *trig,
280 : : unsigned long *delay_on,
281 : : unsigned long *delay_off)
282 : : {
283 : 0 : led_trigger_blink_setup(trig, delay_on, delay_off, 0, 0);
284 : 0 : }
285 : : EXPORT_SYMBOL_GPL(led_trigger_blink);
286 : :
287 : 0 : void led_trigger_blink_oneshot(struct led_trigger *trig,
288 : : unsigned long *delay_on,
289 : : unsigned long *delay_off,
290 : : int invert)
291 : : {
292 : 0 : led_trigger_blink_setup(trig, delay_on, delay_off, 1, invert);
293 : 0 : }
294 : : EXPORT_SYMBOL_GPL(led_trigger_blink_oneshot);
295 : :
296 : 0 : void led_trigger_register_simple(const char *name, struct led_trigger **tp)
297 : : {
298 : : struct led_trigger *trig;
299 : : int err;
300 : :
301 : : trig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
302 : :
303 [ # # ]: 0 : if (trig) {
304 : 0 : trig->name = name;
305 : 0 : err = led_trigger_register(trig);
306 [ # # ]: 0 : if (err < 0) {
307 : 0 : kfree(trig);
308 : : trig = NULL;
309 : 0 : pr_warn("LED trigger %s failed to register (%d)\n",
310 : : name, err);
311 : : }
312 : : } else {
313 : 0 : pr_warn("LED trigger %s failed to register (no memory)\n",
314 : : name);
315 : : }
316 : 0 : *tp = trig;
317 : 0 : }
318 : : EXPORT_SYMBOL_GPL(led_trigger_register_simple);
319 : :
320 : 0 : void led_trigger_unregister_simple(struct led_trigger *trig)
321 : : {
322 [ # # ]: 0 : if (trig)
323 : 0 : led_trigger_unregister(trig);
324 : 0 : kfree(trig);
325 : 0 : }
326 : : EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
327 : :
328 : : MODULE_AUTHOR("Richard Purdie");
329 : : MODULE_LICENSE("GPL");
330 : : MODULE_DESCRIPTION("LED Triggers Core");
|