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