Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/leds/led-triggers.c
15109 views
1
/*
2
* LED Triggers Core
3
*
4
* Copyright 2005-2007 Openedhand Ltd.
5
*
6
* Author: Richard Purdie <[email protected]>
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
*/
30
static DECLARE_RWSEM(triggers_list_lock);
31
static LIST_HEAD(trigger_list);
32
33
/* Used by LED Class */
34
35
ssize_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
}
70
EXPORT_SYMBOL_GPL(led_trigger_store);
71
72
ssize_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
}
100
EXPORT_SYMBOL_GPL(led_trigger_show);
101
102
/* Caller must ensure led_cdev->trigger_lock held */
103
void 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_brightness_set(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
}
127
EXPORT_SYMBOL_GPL(led_trigger_set);
128
129
void 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
}
135
EXPORT_SYMBOL_GPL(led_trigger_remove);
136
137
void 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
}
153
EXPORT_SYMBOL_GPL(led_trigger_set_default);
154
155
/* LED Trigger Interface */
156
157
int 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
}
190
EXPORT_SYMBOL_GPL(led_trigger_register);
191
192
void 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
}
211
EXPORT_SYMBOL_GPL(led_trigger_unregister);
212
213
/* Simple LED Tigger Interface */
214
215
void 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
}
232
EXPORT_SYMBOL_GPL(led_trigger_event);
233
234
void led_trigger_blink(struct led_trigger *trigger,
235
unsigned long *delay_on,
236
unsigned long *delay_off)
237
{
238
struct list_head *entry;
239
240
if (!trigger)
241
return;
242
243
read_lock(&trigger->leddev_list_lock);
244
list_for_each(entry, &trigger->led_cdevs) {
245
struct led_classdev *led_cdev;
246
247
led_cdev = list_entry(entry, struct led_classdev, trig_list);
248
led_blink_set(led_cdev, delay_on, delay_off);
249
}
250
read_unlock(&trigger->leddev_list_lock);
251
}
252
EXPORT_SYMBOL_GPL(led_trigger_blink);
253
254
void led_trigger_register_simple(const char *name, struct led_trigger **tp)
255
{
256
struct led_trigger *trigger;
257
int err;
258
259
trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
260
261
if (trigger) {
262
trigger->name = name;
263
err = led_trigger_register(trigger);
264
if (err < 0)
265
printk(KERN_WARNING "LED trigger %s failed to register"
266
" (%d)\n", name, err);
267
} else
268
printk(KERN_WARNING "LED trigger %s failed to register"
269
" (no memory)\n", name);
270
271
*tp = trigger;
272
}
273
EXPORT_SYMBOL_GPL(led_trigger_register_simple);
274
275
void led_trigger_unregister_simple(struct led_trigger *trigger)
276
{
277
if (trigger)
278
led_trigger_unregister(trigger);
279
kfree(trigger);
280
}
281
EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
282
283
MODULE_AUTHOR("Richard Purdie");
284
MODULE_LICENSE("GPL");
285
MODULE_DESCRIPTION("LED Triggers Core");
286
287