• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/leds/
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/sysdev.h>
21#include <linux/timer.h>
22#include <linux/rwsem.h>
23#include <linux/leds.h>
24#include <linux/slab.h>
25#include "leds.h"
26
27/*
28 * Nests outside led_cdev->trigger_lock
29 */
30static DECLARE_RWSEM(triggers_list_lock);
31static LIST_HEAD(trigger_list);
32
33 /* Used by LED Class */
34
35ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
36		const char *buf, size_t count)
37{
38	struct led_classdev *led_cdev = dev_get_drvdata(dev);
39	char trigger_name[TRIG_NAME_MAX];
40	struct led_trigger *trig;
41	size_t len;
42
43	trigger_name[sizeof(trigger_name) - 1] = '\0';
44	strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
45	len = strlen(trigger_name);
46
47	if (len && trigger_name[len - 1] == '\n')
48		trigger_name[len - 1] = '\0';
49
50	if (!strcmp(trigger_name, "none")) {
51		led_trigger_remove(led_cdev);
52		return count;
53	}
54
55	down_read(&triggers_list_lock);
56	list_for_each_entry(trig, &trigger_list, next_trig) {
57		if (!strcmp(trigger_name, trig->name)) {
58			down_write(&led_cdev->trigger_lock);
59			led_trigger_set(led_cdev, trig);
60			up_write(&led_cdev->trigger_lock);
61
62			up_read(&triggers_list_lock);
63			return count;
64		}
65	}
66	up_read(&triggers_list_lock);
67
68	return -EINVAL;
69}
70EXPORT_SYMBOL_GPL(led_trigger_store);
71
72ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
73		char *buf)
74{
75	struct led_classdev *led_cdev = dev_get_drvdata(dev);
76	struct led_trigger *trig;
77	int len = 0;
78
79	down_read(&triggers_list_lock);
80	down_read(&led_cdev->trigger_lock);
81
82	if (!led_cdev->trigger)
83		len += sprintf(buf+len, "[none] ");
84	else
85		len += sprintf(buf+len, "none ");
86
87	list_for_each_entry(trig, &trigger_list, next_trig) {
88		if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
89							trig->name))
90			len += sprintf(buf+len, "[%s] ", trig->name);
91		else
92			len += sprintf(buf+len, "%s ", trig->name);
93	}
94	up_read(&led_cdev->trigger_lock);
95	up_read(&triggers_list_lock);
96
97	len += sprintf(len+buf, "\n");
98	return len;
99}
100EXPORT_SYMBOL_GPL(led_trigger_show);
101
102/* Caller must ensure led_cdev->trigger_lock held */
103void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
104{
105	unsigned long flags;
106
107	/* Remove any existing trigger */
108	if (led_cdev->trigger) {
109		write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
110		list_del(&led_cdev->trig_list);
111		write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
112			flags);
113		if (led_cdev->trigger->deactivate)
114			led_cdev->trigger->deactivate(led_cdev);
115		led_cdev->trigger = NULL;
116		led_set_brightness(led_cdev, LED_OFF);
117	}
118	if (trigger) {
119		write_lock_irqsave(&trigger->leddev_list_lock, flags);
120		list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
121		write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
122		led_cdev->trigger = trigger;
123		if (trigger->activate)
124			trigger->activate(led_cdev);
125	}
126}
127EXPORT_SYMBOL_GPL(led_trigger_set);
128
129void led_trigger_remove(struct led_classdev *led_cdev)
130{
131	down_write(&led_cdev->trigger_lock);
132	led_trigger_set(led_cdev, NULL);
133	up_write(&led_cdev->trigger_lock);
134}
135EXPORT_SYMBOL_GPL(led_trigger_remove);
136
137void led_trigger_set_default(struct led_classdev *led_cdev)
138{
139	struct led_trigger *trig;
140
141	if (!led_cdev->default_trigger)
142		return;
143
144	down_read(&triggers_list_lock);
145	down_write(&led_cdev->trigger_lock);
146	list_for_each_entry(trig, &trigger_list, next_trig) {
147		if (!strcmp(led_cdev->default_trigger, trig->name))
148			led_trigger_set(led_cdev, trig);
149	}
150	up_write(&led_cdev->trigger_lock);
151	up_read(&triggers_list_lock);
152}
153EXPORT_SYMBOL_GPL(led_trigger_set_default);
154
155/* LED Trigger Interface */
156
157int led_trigger_register(struct led_trigger *trigger)
158{
159	struct led_classdev *led_cdev;
160	struct led_trigger *trig;
161
162	rwlock_init(&trigger->leddev_list_lock);
163	INIT_LIST_HEAD(&trigger->led_cdevs);
164
165	down_write(&triggers_list_lock);
166	/* Make sure the trigger's name isn't already in use */
167	list_for_each_entry(trig, &trigger_list, next_trig) {
168		if (!strcmp(trig->name, trigger->name)) {
169			up_write(&triggers_list_lock);
170			return -EEXIST;
171		}
172	}
173	/* Add to the list of led triggers */
174	list_add_tail(&trigger->next_trig, &trigger_list);
175	up_write(&triggers_list_lock);
176
177	/* Register with any LEDs that have this as a default trigger */
178	down_read(&leds_list_lock);
179	list_for_each_entry(led_cdev, &leds_list, node) {
180		down_write(&led_cdev->trigger_lock);
181		if (!led_cdev->trigger && led_cdev->default_trigger &&
182			    !strcmp(led_cdev->default_trigger, trigger->name))
183			led_trigger_set(led_cdev, trigger);
184		up_write(&led_cdev->trigger_lock);
185	}
186	up_read(&leds_list_lock);
187
188	return 0;
189}
190EXPORT_SYMBOL_GPL(led_trigger_register);
191
192void led_trigger_unregister(struct led_trigger *trigger)
193{
194	struct led_classdev *led_cdev;
195
196	/* Remove from the list of led triggers */
197	down_write(&triggers_list_lock);
198	list_del(&trigger->next_trig);
199	up_write(&triggers_list_lock);
200
201	/* Remove anyone actively using this trigger */
202	down_read(&leds_list_lock);
203	list_for_each_entry(led_cdev, &leds_list, node) {
204		down_write(&led_cdev->trigger_lock);
205		if (led_cdev->trigger == trigger)
206			led_trigger_set(led_cdev, NULL);
207		up_write(&led_cdev->trigger_lock);
208	}
209	up_read(&leds_list_lock);
210}
211EXPORT_SYMBOL_GPL(led_trigger_unregister);
212
213/* Simple LED Tigger Interface */
214
215void led_trigger_event(struct led_trigger *trigger,
216			enum led_brightness brightness)
217{
218	struct list_head *entry;
219
220	if (!trigger)
221		return;
222
223	read_lock(&trigger->leddev_list_lock);
224	list_for_each(entry, &trigger->led_cdevs) {
225		struct led_classdev *led_cdev;
226
227		led_cdev = list_entry(entry, struct led_classdev, trig_list);
228		led_set_brightness(led_cdev, brightness);
229	}
230	read_unlock(&trigger->leddev_list_lock);
231}
232EXPORT_SYMBOL_GPL(led_trigger_event);
233
234void led_trigger_register_simple(const char *name, struct led_trigger **tp)
235{
236	struct led_trigger *trigger;
237	int err;
238
239	trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
240
241	if (trigger) {
242		trigger->name = name;
243		err = led_trigger_register(trigger);
244		if (err < 0)
245			printk(KERN_WARNING "LED trigger %s failed to register"
246				" (%d)\n", name, err);
247	} else
248		printk(KERN_WARNING "LED trigger %s failed to register"
249			" (no memory)\n", name);
250
251	*tp = trigger;
252}
253EXPORT_SYMBOL_GPL(led_trigger_register_simple);
254
255void led_trigger_unregister_simple(struct led_trigger *trigger)
256{
257	if (trigger)
258		led_trigger_unregister(trigger);
259	kfree(trigger);
260}
261EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
262
263MODULE_AUTHOR("Richard Purdie");
264MODULE_LICENSE("GPL");
265MODULE_DESCRIPTION("LED Triggers Core");
266