diff options
Diffstat (limited to 'drivers/leds')
-rw-r--r-- | drivers/leds/Kconfig | 8 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/led-class.c | 20 | ||||
-rw-r--r-- | drivers/leds/led-triggers.c | 239 | ||||
-rw-r--r-- | drivers/leds/leds.h | 13 |
5 files changed, 281 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 106f9aeca04f..a4d12ecaf6a5 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -14,5 +14,13 @@ config LEDS_CLASS | |||
14 | This option enables the led sysfs class in /sys/class/leds. You'll | 14 | This option enables the led sysfs class in /sys/class/leds. You'll |
15 | need this to do anything useful with LEDs. If unsure, say N. | 15 | need this to do anything useful with LEDs. If unsure, say N. |
16 | 16 | ||
17 | config LEDS_TRIGGERS | ||
18 | bool "LED Trigger support" | ||
19 | depends NEW_LEDS | ||
20 | help | ||
21 | This option enables trigger support for the leds class. | ||
22 | These triggers allow kernel events to drive the LEDs and can | ||
23 | be configured via sysfs. If unsure, say Y. | ||
24 | |||
17 | endmenu | 25 | endmenu |
18 | 26 | ||
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 8a62871437fa..a9d8becd6189 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -2,3 +2,4 @@ | |||
2 | # LED Core | 2 | # LED Core |
3 | obj-$(CONFIG_NEW_LEDS) += led-core.o | 3 | obj-$(CONFIG_NEW_LEDS) += led-core.o |
4 | obj-$(CONFIG_LEDS_CLASS) += led-class.o | 4 | obj-$(CONFIG_LEDS_CLASS) += led-class.o |
5 | obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o | ||
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 0292df4101ba..b0b5d05fadd6 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c | |||
@@ -54,6 +54,9 @@ static ssize_t led_brightness_store(struct class_device *dev, | |||
54 | 54 | ||
55 | static CLASS_DEVICE_ATTR(brightness, 0644, led_brightness_show, | 55 | static CLASS_DEVICE_ATTR(brightness, 0644, led_brightness_show, |
56 | led_brightness_store); | 56 | led_brightness_store); |
57 | #ifdef CONFIG_LEDS_TRIGGERS | ||
58 | static CLASS_DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); | ||
59 | #endif | ||
57 | 60 | ||
58 | /** | 61 | /** |
59 | * led_classdev_suspend - suspend an led_classdev. | 62 | * led_classdev_suspend - suspend an led_classdev. |
@@ -100,6 +103,15 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) | |||
100 | list_add_tail(&led_cdev->node, &leds_list); | 103 | list_add_tail(&led_cdev->node, &leds_list); |
101 | write_unlock(&leds_list_lock); | 104 | write_unlock(&leds_list_lock); |
102 | 105 | ||
106 | #ifdef CONFIG_LEDS_TRIGGERS | ||
107 | rwlock_init(&led_cdev->trigger_lock); | ||
108 | |||
109 | led_trigger_set_default(led_cdev); | ||
110 | |||
111 | class_device_create_file(led_cdev->class_dev, | ||
112 | &class_device_attr_trigger); | ||
113 | #endif | ||
114 | |||
103 | printk(KERN_INFO "Registered led device: %s\n", | 115 | printk(KERN_INFO "Registered led device: %s\n", |
104 | led_cdev->class_dev->class_id); | 116 | led_cdev->class_dev->class_id); |
105 | 117 | ||
@@ -117,6 +129,14 @@ void led_classdev_unregister(struct led_classdev *led_cdev) | |||
117 | { | 129 | { |
118 | class_device_remove_file(led_cdev->class_dev, | 130 | class_device_remove_file(led_cdev->class_dev, |
119 | &class_device_attr_brightness); | 131 | &class_device_attr_brightness); |
132 | #ifdef CONFIG_LEDS_TRIGGERS | ||
133 | class_device_remove_file(led_cdev->class_dev, | ||
134 | &class_device_attr_trigger); | ||
135 | write_lock(&led_cdev->trigger_lock); | ||
136 | if (led_cdev->trigger) | ||
137 | led_trigger_set(led_cdev, NULL); | ||
138 | write_unlock(&led_cdev->trigger_lock); | ||
139 | #endif | ||
120 | 140 | ||
121 | class_device_unregister(led_cdev->class_dev); | 141 | class_device_unregister(led_cdev->class_dev); |
122 | 142 | ||
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c new file mode 100644 index 000000000000..5e2cd8be1191 --- /dev/null +++ b/drivers/leds/led-triggers.c | |||
@@ -0,0 +1,239 @@ | |||
1 | /* | ||
2 | * LED Triggers Core | ||
3 | * | ||
4 | * Copyright 2005-2006 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/config.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/list.h> | ||
19 | #include <linux/spinlock.h> | ||
20 | #include <linux/device.h> | ||
21 | #include <linux/sysdev.h> | ||
22 | #include <linux/timer.h> | ||
23 | #include <linux/leds.h> | ||
24 | #include "leds.h" | ||
25 | |||
26 | /* | ||
27 | * Nests outside led_cdev->trigger_lock | ||
28 | */ | ||
29 | static rwlock_t triggers_list_lock = RW_LOCK_UNLOCKED; | ||
30 | static LIST_HEAD(trigger_list); | ||
31 | |||
32 | ssize_t led_trigger_store(struct class_device *dev, const char *buf, | ||
33 | size_t count) | ||
34 | { | ||
35 | struct led_classdev *led_cdev = class_get_devdata(dev); | ||
36 | char trigger_name[TRIG_NAME_MAX]; | ||
37 | struct led_trigger *trig; | ||
38 | size_t len; | ||
39 | |||
40 | trigger_name[sizeof(trigger_name) - 1] = '\0'; | ||
41 | strncpy(trigger_name, buf, sizeof(trigger_name) - 1); | ||
42 | len = strlen(trigger_name); | ||
43 | |||
44 | if (len && trigger_name[len - 1] == '\n') | ||
45 | trigger_name[len - 1] = '\0'; | ||
46 | |||
47 | if (!strcmp(trigger_name, "none")) { | ||
48 | write_lock(&led_cdev->trigger_lock); | ||
49 | led_trigger_set(led_cdev, NULL); | ||
50 | write_unlock(&led_cdev->trigger_lock); | ||
51 | return count; | ||
52 | } | ||
53 | |||
54 | read_lock(&triggers_list_lock); | ||
55 | list_for_each_entry(trig, &trigger_list, next_trig) { | ||
56 | if (!strcmp(trigger_name, trig->name)) { | ||
57 | write_lock(&led_cdev->trigger_lock); | ||
58 | led_trigger_set(led_cdev, trig); | ||
59 | write_unlock(&led_cdev->trigger_lock); | ||
60 | |||
61 | read_unlock(&triggers_list_lock); | ||
62 | return count; | ||
63 | } | ||
64 | } | ||
65 | read_unlock(&triggers_list_lock); | ||
66 | |||
67 | return -EINVAL; | ||
68 | } | ||
69 | |||
70 | |||
71 | ssize_t led_trigger_show(struct class_device *dev, char *buf) | ||
72 | { | ||
73 | struct led_classdev *led_cdev = class_get_devdata(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 | } | ||
130 | if (trigger) { | ||
131 | write_lock_irqsave(&trigger->leddev_list_lock, flags); | ||
132 | list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs); | ||
133 | write_unlock_irqrestore(&trigger->leddev_list_lock, flags); | ||
134 | if (trigger->activate) | ||
135 | trigger->activate(led_cdev); | ||
136 | } | ||
137 | led_cdev->trigger = trigger; | ||
138 | } | ||
139 | |||
140 | void led_trigger_set_default(struct led_classdev *led_cdev) | ||
141 | { | ||
142 | struct led_trigger *trig; | ||
143 | |||
144 | if (!led_cdev->default_trigger) | ||
145 | return; | ||
146 | |||
147 | read_lock(&triggers_list_lock); | ||
148 | write_lock(&led_cdev->trigger_lock); | ||
149 | list_for_each_entry(trig, &trigger_list, next_trig) { | ||
150 | if (!strcmp(led_cdev->default_trigger, trig->name)) | ||
151 | led_trigger_set(led_cdev, trig); | ||
152 | } | ||
153 | write_unlock(&led_cdev->trigger_lock); | ||
154 | read_unlock(&triggers_list_lock); | ||
155 | } | ||
156 | |||
157 | int led_trigger_register(struct led_trigger *trigger) | ||
158 | { | ||
159 | struct led_classdev *led_cdev; | ||
160 | |||
161 | rwlock_init(&trigger->leddev_list_lock); | ||
162 | INIT_LIST_HEAD(&trigger->led_cdevs); | ||
163 | |||
164 | /* Add to the list of led triggers */ | ||
165 | write_lock(&triggers_list_lock); | ||
166 | list_add_tail(&trigger->next_trig, &trigger_list); | ||
167 | write_unlock(&triggers_list_lock); | ||
168 | |||
169 | /* Register with any LEDs that have this as a default trigger */ | ||
170 | read_lock(&leds_list_lock); | ||
171 | list_for_each_entry(led_cdev, &leds_list, node) { | ||
172 | write_lock(&led_cdev->trigger_lock); | ||
173 | if (!led_cdev->trigger && led_cdev->default_trigger && | ||
174 | !strcmp(led_cdev->default_trigger, trigger->name)) | ||
175 | led_trigger_set(led_cdev, trigger); | ||
176 | write_unlock(&led_cdev->trigger_lock); | ||
177 | } | ||
178 | read_unlock(&leds_list_lock); | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | void led_trigger_register_simple(const char *name, struct led_trigger **tp) | ||
184 | { | ||
185 | struct led_trigger *trigger; | ||
186 | |||
187 | trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); | ||
188 | |||
189 | if (trigger) { | ||
190 | trigger->name = name; | ||
191 | led_trigger_register(trigger); | ||
192 | } | ||
193 | *tp = trigger; | ||
194 | } | ||
195 | |||
196 | void led_trigger_unregister(struct led_trigger *trigger) | ||
197 | { | ||
198 | struct led_classdev *led_cdev; | ||
199 | |||
200 | /* Remove from the list of led triggers */ | ||
201 | write_lock(&triggers_list_lock); | ||
202 | list_del(&trigger->next_trig); | ||
203 | write_unlock(&triggers_list_lock); | ||
204 | |||
205 | /* Remove anyone actively using this trigger */ | ||
206 | read_lock(&leds_list_lock); | ||
207 | list_for_each_entry(led_cdev, &leds_list, node) { | ||
208 | write_lock(&led_cdev->trigger_lock); | ||
209 | if (led_cdev->trigger == trigger) | ||
210 | led_trigger_set(led_cdev, NULL); | ||
211 | write_unlock(&led_cdev->trigger_lock); | ||
212 | } | ||
213 | read_unlock(&leds_list_lock); | ||
214 | } | ||
215 | |||
216 | void led_trigger_unregister_simple(struct led_trigger *trigger) | ||
217 | { | ||
218 | led_trigger_unregister(trigger); | ||
219 | kfree(trigger); | ||
220 | } | ||
221 | |||
222 | /* Used by LED Class */ | ||
223 | EXPORT_SYMBOL_GPL(led_trigger_set); | ||
224 | EXPORT_SYMBOL_GPL(led_trigger_set_default); | ||
225 | EXPORT_SYMBOL_GPL(led_trigger_show); | ||
226 | EXPORT_SYMBOL_GPL(led_trigger_store); | ||
227 | |||
228 | /* LED Trigger Interface */ | ||
229 | EXPORT_SYMBOL_GPL(led_trigger_register); | ||
230 | EXPORT_SYMBOL_GPL(led_trigger_unregister); | ||
231 | |||
232 | /* Simple LED Tigger Interface */ | ||
233 | EXPORT_SYMBOL_GPL(led_trigger_register_simple); | ||
234 | EXPORT_SYMBOL_GPL(led_trigger_unregister_simple); | ||
235 | EXPORT_SYMBOL_GPL(led_trigger_event); | ||
236 | |||
237 | MODULE_AUTHOR("Richard Purdie"); | ||
238 | MODULE_LICENSE("GPL"); | ||
239 | MODULE_DESCRIPTION("LED Triggers Core"); | ||
diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index d26ca2f7e722..a715c4ed93ff 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h | |||
@@ -28,4 +28,17 @@ static inline void led_set_brightness(struct led_classdev *led_cdev, | |||
28 | extern rwlock_t leds_list_lock; | 28 | extern rwlock_t leds_list_lock; |
29 | extern struct list_head leds_list; | 29 | extern struct list_head leds_list; |
30 | 30 | ||
31 | #ifdef CONFIG_LEDS_TRIGGERS | ||
32 | void led_trigger_set_default(struct led_classdev *led_cdev); | ||
33 | void led_trigger_set(struct led_classdev *led_cdev, | ||
34 | struct led_trigger *trigger); | ||
35 | #else | ||
36 | #define led_trigger_set_default(x) do {} while(0) | ||
37 | #define led_trigger_set(x, y) do {} while(0) | ||
38 | #endif | ||
39 | |||
40 | ssize_t led_trigger_store(struct class_device *dev, const char *buf, | ||
41 | size_t count); | ||
42 | ssize_t led_trigger_show(struct class_device *dev, char *buf); | ||
43 | |||
31 | #endif /* __LEDS_H_INCLUDED */ | 44 | #endif /* __LEDS_H_INCLUDED */ |