xref: /openbmc/linux/drivers/leds/led-triggers.c (revision c21b37f6)
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/leds.h>
23 #include "leds.h"
24 
25 /*
26  * Nests outside led_cdev->trigger_lock
27  */
28 static DEFINE_RWLOCK(triggers_list_lock);
29 static LIST_HEAD(trigger_list);
30 
31 ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
32 		const char *buf, size_t count)
33 {
34 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
35 	char trigger_name[TRIG_NAME_MAX];
36 	struct led_trigger *trig;
37 	size_t len;
38 
39 	trigger_name[sizeof(trigger_name) - 1] = '\0';
40 	strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
41 	len = strlen(trigger_name);
42 
43 	if (len && trigger_name[len - 1] == '\n')
44 		trigger_name[len - 1] = '\0';
45 
46 	if (!strcmp(trigger_name, "none")) {
47 		write_lock(&led_cdev->trigger_lock);
48 		led_trigger_set(led_cdev, NULL);
49 		write_unlock(&led_cdev->trigger_lock);
50 		return count;
51 	}
52 
53 	read_lock(&triggers_list_lock);
54 	list_for_each_entry(trig, &trigger_list, next_trig) {
55 		if (!strcmp(trigger_name, trig->name)) {
56 			write_lock(&led_cdev->trigger_lock);
57 			led_trigger_set(led_cdev, trig);
58 			write_unlock(&led_cdev->trigger_lock);
59 
60 			read_unlock(&triggers_list_lock);
61 			return count;
62 		}
63 	}
64 	read_unlock(&triggers_list_lock);
65 
66 	return -EINVAL;
67 }
68 
69 
70 ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
71 		char *buf)
72 {
73 	struct led_classdev *led_cdev = dev_get_drvdata(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 		led_set_brightness(led_cdev, LED_OFF);
130 	}
131 	if (trigger) {
132 		write_lock_irqsave(&trigger->leddev_list_lock, flags);
133 		list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
134 		write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
135 		if (trigger->activate)
136 			trigger->activate(led_cdev);
137 	}
138 	led_cdev->trigger = trigger;
139 }
140 
141 void led_trigger_set_default(struct led_classdev *led_cdev)
142 {
143 	struct led_trigger *trig;
144 
145 	if (!led_cdev->default_trigger)
146 		return;
147 
148 	read_lock(&triggers_list_lock);
149 	write_lock(&led_cdev->trigger_lock);
150 	list_for_each_entry(trig, &trigger_list, next_trig) {
151 		if (!strcmp(led_cdev->default_trigger, trig->name))
152 			led_trigger_set(led_cdev, trig);
153 	}
154 	write_unlock(&led_cdev->trigger_lock);
155 	read_unlock(&triggers_list_lock);
156 }
157 
158 int led_trigger_register(struct led_trigger *trigger)
159 {
160 	struct led_classdev *led_cdev;
161 
162 	rwlock_init(&trigger->leddev_list_lock);
163 	INIT_LIST_HEAD(&trigger->led_cdevs);
164 
165 	/* Add to the list of led triggers */
166 	write_lock(&triggers_list_lock);
167 	list_add_tail(&trigger->next_trig, &trigger_list);
168 	write_unlock(&triggers_list_lock);
169 
170 	/* Register with any LEDs that have this as a default trigger */
171 	read_lock(&leds_list_lock);
172 	list_for_each_entry(led_cdev, &leds_list, node) {
173 		write_lock(&led_cdev->trigger_lock);
174 		if (!led_cdev->trigger && led_cdev->default_trigger &&
175 			    !strcmp(led_cdev->default_trigger, trigger->name))
176 			led_trigger_set(led_cdev, trigger);
177 		write_unlock(&led_cdev->trigger_lock);
178 	}
179 	read_unlock(&leds_list_lock);
180 
181 	return 0;
182 }
183 
184 void led_trigger_register_simple(const char *name, struct led_trigger **tp)
185 {
186 	struct led_trigger *trigger;
187 	int err;
188 
189 	trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
190 
191 	if (trigger) {
192 		trigger->name = name;
193 		err = led_trigger_register(trigger);
194 		if (err < 0)
195 			printk(KERN_WARNING "LED trigger %s failed to register"
196 				" (%d)\n", name, err);
197 	} else
198 		printk(KERN_WARNING "LED trigger %s failed to register"
199 			" (no memory)\n", name);
200 
201 	*tp = trigger;
202 }
203 
204 void led_trigger_unregister(struct led_trigger *trigger)
205 {
206 	struct led_classdev *led_cdev;
207 
208 	/* Remove from the list of led triggers */
209 	write_lock(&triggers_list_lock);
210 	list_del(&trigger->next_trig);
211 	write_unlock(&triggers_list_lock);
212 
213 	/* Remove anyone actively using this trigger */
214 	read_lock(&leds_list_lock);
215 	list_for_each_entry(led_cdev, &leds_list, node) {
216 		write_lock(&led_cdev->trigger_lock);
217 		if (led_cdev->trigger == trigger)
218 			led_trigger_set(led_cdev, NULL);
219 		write_unlock(&led_cdev->trigger_lock);
220 	}
221 	read_unlock(&leds_list_lock);
222 }
223 
224 void led_trigger_unregister_simple(struct led_trigger *trigger)
225 {
226 	if (trigger)
227 		led_trigger_unregister(trigger);
228 	kfree(trigger);
229 }
230 
231 /* Used by LED Class */
232 EXPORT_SYMBOL_GPL(led_trigger_set);
233 EXPORT_SYMBOL_GPL(led_trigger_set_default);
234 EXPORT_SYMBOL_GPL(led_trigger_show);
235 EXPORT_SYMBOL_GPL(led_trigger_store);
236 
237 /* LED Trigger Interface */
238 EXPORT_SYMBOL_GPL(led_trigger_register);
239 EXPORT_SYMBOL_GPL(led_trigger_unregister);
240 
241 /* Simple LED Tigger Interface */
242 EXPORT_SYMBOL_GPL(led_trigger_register_simple);
243 EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
244 EXPORT_SYMBOL_GPL(led_trigger_event);
245 
246 MODULE_AUTHOR("Richard Purdie");
247 MODULE_LICENSE("GPL");
248 MODULE_DESCRIPTION("LED Triggers Core");
249