Blame view

drivers/leds/led-triggers.c 7.28 KB
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
1
2
3
  /*
   * LED Triggers Core
   *
f8a7c6fe1   Richard Purdie   leds: Convert fro...
4
   * Copyright 2005-2007 Openedhand Ltd.
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
5
6
7
8
9
10
11
12
   *
   * Author: Richard Purdie <rpurdie@openedhand.com>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   */
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
13
14
15
16
17
18
  #include <linux/module.h>
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/list.h>
  #include <linux/spinlock.h>
  #include <linux/device.h>
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
19
  #include <linux/timer.h>
dc47206e5   Richard Purdie   leds: Fix led tri...
20
  #include <linux/rwsem.h>
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
21
  #include <linux/leds.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
22
  #include <linux/slab.h>
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
23
24
25
26
27
  #include "leds.h"
  
  /*
   * Nests outside led_cdev->trigger_lock
   */
dc47206e5   Richard Purdie   leds: Fix led tri...
28
  static DECLARE_RWSEM(triggers_list_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
29
  static LIST_HEAD(trigger_list);
4d404fd5c   Németh Márton   leds: Cleanup var...
30
   /* Used by LED Class */
f8a7c6fe1   Richard Purdie   leds: Convert fro...
31
32
  ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
  		const char *buf, size_t count)
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
33
  {
f8a7c6fe1   Richard Purdie   leds: Convert fro...
34
  	struct led_classdev *led_cdev = dev_get_drvdata(dev);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
35
36
37
38
39
40
41
42
43
44
45
46
47
  	char trigger_name[TRIG_NAME_MAX];
  	struct led_trigger *trig;
  	size_t len;
  
  	trigger_name[sizeof(trigger_name) - 1] = '\0';
  	strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
  	len = strlen(trigger_name);
  
  	if (len && trigger_name[len - 1] == '
  ')
  		trigger_name[len - 1] = '\0';
  
  	if (!strcmp(trigger_name, "none")) {
0013b23d6   Németh Márton   leds: disable tri...
48
  		led_trigger_remove(led_cdev);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
49
50
  		return count;
  	}
dc47206e5   Richard Purdie   leds: Fix led tri...
51
  	down_read(&triggers_list_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
52
53
  	list_for_each_entry(trig, &trigger_list, next_trig) {
  		if (!strcmp(trigger_name, trig->name)) {
dc47206e5   Richard Purdie   leds: Fix led tri...
54
  			down_write(&led_cdev->trigger_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
55
  			led_trigger_set(led_cdev, trig);
dc47206e5   Richard Purdie   leds: Fix led tri...
56
  			up_write(&led_cdev->trigger_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
57

dc47206e5   Richard Purdie   leds: Fix led tri...
58
  			up_read(&triggers_list_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
59
60
61
  			return count;
  		}
  	}
dc47206e5   Richard Purdie   leds: Fix led tri...
62
  	up_read(&triggers_list_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
63
64
65
  
  	return -EINVAL;
  }
4d404fd5c   Németh Márton   leds: Cleanup var...
66
  EXPORT_SYMBOL_GPL(led_trigger_store);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
67

f8a7c6fe1   Richard Purdie   leds: Convert fro...
68
69
  ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
  		char *buf)
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
70
  {
f8a7c6fe1   Richard Purdie   leds: Convert fro...
71
  	struct led_classdev *led_cdev = dev_get_drvdata(dev);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
72
73
  	struct led_trigger *trig;
  	int len = 0;
dc47206e5   Richard Purdie   leds: Fix led tri...
74
75
  	down_read(&triggers_list_lock);
  	down_read(&led_cdev->trigger_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
76
77
78
79
80
81
82
83
84
85
86
87
88
  
  	if (!led_cdev->trigger)
  		len += sprintf(buf+len, "[none] ");
  	else
  		len += sprintf(buf+len, "none ");
  
  	list_for_each_entry(trig, &trigger_list, next_trig) {
  		if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
  							trig->name))
  			len += sprintf(buf+len, "[%s] ", trig->name);
  		else
  			len += sprintf(buf+len, "%s ", trig->name);
  	}
dc47206e5   Richard Purdie   leds: Fix led tri...
89
90
  	up_read(&led_cdev->trigger_lock);
  	up_read(&triggers_list_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
91
92
93
94
95
  
  	len += sprintf(len+buf, "
  ");
  	return len;
  }
4d404fd5c   Németh Márton   leds: Cleanup var...
96
  EXPORT_SYMBOL_GPL(led_trigger_show);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
97
98
99
100
101
102
103
104
105
106
  
  /* Caller must ensure led_cdev->trigger_lock held */
  void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
  {
  	unsigned long flags;
  
  	/* Remove any existing trigger */
  	if (led_cdev->trigger) {
  		write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
  		list_del(&led_cdev->trig_list);
4d404fd5c   Németh Márton   leds: Cleanup var...
107
108
  		write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
  			flags);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
109
110
  		if (led_cdev->trigger->deactivate)
  			led_cdev->trigger->deactivate(led_cdev);
fe3025b55   Dmitry Eremin-Solenikov   leds: Ensure led-...
111
  		led_cdev->trigger = NULL;
5ada28bf7   Johannes Berg   led-class: always...
112
  		led_brightness_set(led_cdev, LED_OFF);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
113
114
115
116
117
  	}
  	if (trigger) {
  		write_lock_irqsave(&trigger->leddev_list_lock, flags);
  		list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
  		write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
fe3025b55   Dmitry Eremin-Solenikov   leds: Ensure led-...
118
  		led_cdev->trigger = trigger;
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
119
120
121
  		if (trigger->activate)
  			trigger->activate(led_cdev);
  	}
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
122
  }
4d404fd5c   Németh Márton   leds: Cleanup var...
123
  EXPORT_SYMBOL_GPL(led_trigger_set);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
124

0013b23d6   Németh Márton   leds: disable tri...
125
126
127
128
129
130
  void led_trigger_remove(struct led_classdev *led_cdev)
  {
  	down_write(&led_cdev->trigger_lock);
  	led_trigger_set(led_cdev, NULL);
  	up_write(&led_cdev->trigger_lock);
  }
4d404fd5c   Németh Márton   leds: Cleanup var...
131
  EXPORT_SYMBOL_GPL(led_trigger_remove);
0013b23d6   Németh Márton   leds: disable tri...
132

c3bc9956e   Richard Purdie   [PATCH] LED: add ...
133
134
135
136
137
138
  void led_trigger_set_default(struct led_classdev *led_cdev)
  {
  	struct led_trigger *trig;
  
  	if (!led_cdev->default_trigger)
  		return;
dc47206e5   Richard Purdie   leds: Fix led tri...
139
140
  	down_read(&triggers_list_lock);
  	down_write(&led_cdev->trigger_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
141
142
143
144
  	list_for_each_entry(trig, &trigger_list, next_trig) {
  		if (!strcmp(led_cdev->default_trigger, trig->name))
  			led_trigger_set(led_cdev, trig);
  	}
dc47206e5   Richard Purdie   leds: Fix led tri...
145
146
  	up_write(&led_cdev->trigger_lock);
  	up_read(&triggers_list_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
147
  }
4d404fd5c   Németh Márton   leds: Cleanup var...
148
149
150
  EXPORT_SYMBOL_GPL(led_trigger_set_default);
  
  /* LED Trigger Interface */
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
151
152
153
154
  
  int led_trigger_register(struct led_trigger *trigger)
  {
  	struct led_classdev *led_cdev;
700c6ea22   Adam Nielsen   leds: Prevent mul...
155
  	struct led_trigger *trig;
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
156
157
158
  
  	rwlock_init(&trigger->leddev_list_lock);
  	INIT_LIST_HEAD(&trigger->led_cdevs);
dc47206e5   Richard Purdie   leds: Fix led tri...
159
  	down_write(&triggers_list_lock);
700c6ea22   Adam Nielsen   leds: Prevent mul...
160
161
162
163
164
165
166
167
  	/* Make sure the trigger's name isn't already in use */
  	list_for_each_entry(trig, &trigger_list, next_trig) {
  		if (!strcmp(trig->name, trigger->name)) {
  			up_write(&triggers_list_lock);
  			return -EEXIST;
  		}
  	}
  	/* Add to the list of led triggers */
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
168
  	list_add_tail(&trigger->next_trig, &trigger_list);
dc47206e5   Richard Purdie   leds: Fix led tri...
169
  	up_write(&triggers_list_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
170
171
  
  	/* Register with any LEDs that have this as a default trigger */
72f8da329   Richard Purdie   leds: Fix leds_li...
172
  	down_read(&leds_list_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
173
  	list_for_each_entry(led_cdev, &leds_list, node) {
dc47206e5   Richard Purdie   leds: Fix led tri...
174
  		down_write(&led_cdev->trigger_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
175
176
177
  		if (!led_cdev->trigger && led_cdev->default_trigger &&
  			    !strcmp(led_cdev->default_trigger, trigger->name))
  			led_trigger_set(led_cdev, trigger);
dc47206e5   Richard Purdie   leds: Fix led tri...
178
  		up_write(&led_cdev->trigger_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
179
  	}
72f8da329   Richard Purdie   leds: Fix leds_li...
180
  	up_read(&leds_list_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
181
182
183
  
  	return 0;
  }
4d404fd5c   Németh Márton   leds: Cleanup var...
184
  EXPORT_SYMBOL_GPL(led_trigger_register);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
185
186
187
188
189
190
  
  void led_trigger_unregister(struct led_trigger *trigger)
  {
  	struct led_classdev *led_cdev;
  
  	/* Remove from the list of led triggers */
dc47206e5   Richard Purdie   leds: Fix led tri...
191
  	down_write(&triggers_list_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
192
  	list_del(&trigger->next_trig);
dc47206e5   Richard Purdie   leds: Fix led tri...
193
  	up_write(&triggers_list_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
194
195
  
  	/* Remove anyone actively using this trigger */
72f8da329   Richard Purdie   leds: Fix leds_li...
196
  	down_read(&leds_list_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
197
  	list_for_each_entry(led_cdev, &leds_list, node) {
dc47206e5   Richard Purdie   leds: Fix led tri...
198
  		down_write(&led_cdev->trigger_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
199
200
  		if (led_cdev->trigger == trigger)
  			led_trigger_set(led_cdev, NULL);
dc47206e5   Richard Purdie   leds: Fix led tri...
201
  		up_write(&led_cdev->trigger_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
202
  	}
72f8da329   Richard Purdie   leds: Fix leds_li...
203
  	up_read(&leds_list_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
204
  }
4d404fd5c   Németh Márton   leds: Cleanup var...
205
  EXPORT_SYMBOL_GPL(led_trigger_unregister);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
206

4d404fd5c   Németh Márton   leds: Cleanup var...
207
208
209
210
  /* Simple LED Tigger Interface */
  
  void led_trigger_event(struct led_trigger *trigger,
  			enum led_brightness brightness)
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
211
  {
4d404fd5c   Németh Márton   leds: Cleanup var...
212
213
214
215
216
217
218
219
220
221
222
223
224
  	struct list_head *entry;
  
  	if (!trigger)
  		return;
  
  	read_lock(&trigger->leddev_list_lock);
  	list_for_each(entry, &trigger->led_cdevs) {
  		struct led_classdev *led_cdev;
  
  		led_cdev = list_entry(entry, struct led_classdev, trig_list);
  		led_set_brightness(led_cdev, brightness);
  	}
  	read_unlock(&trigger->leddev_list_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
225
  }
4d404fd5c   Németh Márton   leds: Cleanup var...
226
  EXPORT_SYMBOL_GPL(led_trigger_event);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
227

0b9536c95   Vasily Khoruzhick   leds: Add ability...
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  void led_trigger_blink(struct led_trigger *trigger,
  		       unsigned long *delay_on,
  		       unsigned long *delay_off)
  {
  	struct list_head *entry;
  
  	if (!trigger)
  		return;
  
  	read_lock(&trigger->leddev_list_lock);
  	list_for_each(entry, &trigger->led_cdevs) {
  		struct led_classdev *led_cdev;
  
  		led_cdev = list_entry(entry, struct led_classdev, trig_list);
  		led_blink_set(led_cdev, delay_on, delay_off);
  	}
  	read_unlock(&trigger->leddev_list_lock);
  }
  EXPORT_SYMBOL_GPL(led_trigger_blink);
4d404fd5c   Németh Márton   leds: Cleanup var...
247
248
249
250
  void led_trigger_register_simple(const char *name, struct led_trigger **tp)
  {
  	struct led_trigger *trigger;
  	int err;
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
251

4d404fd5c   Németh Márton   leds: Cleanup var...
252
  	trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
253

4d404fd5c   Németh Márton   leds: Cleanup var...
254
255
256
  	if (trigger) {
  		trigger->name = name;
  		err = led_trigger_register(trigger);
cba4c2ac0   Masakazu Mokuno   drivers/leds/led-...
257
258
259
  		if (err < 0) {
  			kfree(trigger);
  			trigger = NULL;
4d404fd5c   Németh Márton   leds: Cleanup var...
260
261
262
  			printk(KERN_WARNING "LED trigger %s failed to register"
  				" (%d)
  ", name, err);
cba4c2ac0   Masakazu Mokuno   drivers/leds/led-...
263
  		}
4d404fd5c   Németh Márton   leds: Cleanup var...
264
265
266
267
268
269
270
  	} else
  		printk(KERN_WARNING "LED trigger %s failed to register"
  			" (no memory)
  ", name);
  
  	*tp = trigger;
  }
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
271
  EXPORT_SYMBOL_GPL(led_trigger_register_simple);
4d404fd5c   Németh Márton   leds: Cleanup var...
272
273
274
275
276
277
278
  
  void led_trigger_unregister_simple(struct led_trigger *trigger)
  {
  	if (trigger)
  		led_trigger_unregister(trigger);
  	kfree(trigger);
  }
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
279
  EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
280
281
282
283
  
  MODULE_AUTHOR("Richard Purdie");
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("LED Triggers Core");