Blame view

drivers/leds/led-class.c 7.69 KB
c72a1d608   Richard Purdie   [PATCH] LED: add ...
1
2
3
4
  /*
   * LED Class Core
   *
   * Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu>
f8a7c6fe1   Richard Purdie   leds: Convert fro...
5
   * Copyright (C) 2005-2007 Richard Purdie <rpurdie@openedhand.com>
c72a1d608   Richard Purdie   [PATCH] LED: add ...
6
7
8
9
10
   *
   * 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.
   */
c72a1d608   Richard Purdie   [PATCH] LED: add ...
11
12
13
14
15
16
  #include <linux/module.h>
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/list.h>
  #include <linux/spinlock.h>
  #include <linux/device.h>
c72a1d608   Richard Purdie   [PATCH] LED: add ...
17
18
  #include <linux/timer.h>
  #include <linux/err.h>
3dc7b82ea   Richard Purdie   [PATCH] LED: Fix ...
19
  #include <linux/ctype.h>
c72a1d608   Richard Purdie   [PATCH] LED: add ...
20
21
22
23
  #include <linux/leds.h>
  #include "leds.h"
  
  static struct class *leds_class;
29d76dfa2   Henrique de Moraes Holschuh   leds: Add support...
24
25
26
27
28
  static void led_update_brightness(struct led_classdev *led_cdev)
  {
  	if (led_cdev->brightness_get)
  		led_cdev->brightness = led_cdev->brightness_get(led_cdev);
  }
f8a7c6fe1   Richard Purdie   leds: Convert fro...
29
30
  static ssize_t led_brightness_show(struct device *dev, 
  		struct device_attribute *attr, char *buf)
c72a1d608   Richard Purdie   [PATCH] LED: add ...
31
  {
f8a7c6fe1   Richard Purdie   leds: Convert fro...
32
  	struct led_classdev *led_cdev = dev_get_drvdata(dev);
c72a1d608   Richard Purdie   [PATCH] LED: add ...
33
34
  
  	/* no lock needed for this */
29d76dfa2   Henrique de Moraes Holschuh   leds: Add support...
35
  	led_update_brightness(led_cdev);
c72a1d608   Richard Purdie   [PATCH] LED: add ...
36

dd8e5a203   Sven Wegener   leds: Remove unee...
37
38
  	return sprintf(buf, "%u
  ", led_cdev->brightness);
c72a1d608   Richard Purdie   [PATCH] LED: add ...
39
  }
f8a7c6fe1   Richard Purdie   leds: Convert fro...
40
41
  static ssize_t led_brightness_store(struct device *dev,
  		struct device_attribute *attr, const char *buf, size_t size)
c72a1d608   Richard Purdie   [PATCH] LED: add ...
42
  {
f8a7c6fe1   Richard Purdie   leds: Convert fro...
43
  	struct led_classdev *led_cdev = dev_get_drvdata(dev);
c72a1d608   Richard Purdie   [PATCH] LED: add ...
44
45
46
  	ssize_t ret = -EINVAL;
  	char *after;
  	unsigned long state = simple_strtoul(buf, &after, 10);
3dc7b82ea   Richard Purdie   [PATCH] LED: Fix ...
47
  	size_t count = after - buf;
c72a1d608   Richard Purdie   [PATCH] LED: add ...
48

e7d2860b6   André Goddard Rosa   tree-wide: conver...
49
  	if (isspace(*after))
3dc7b82ea   Richard Purdie   [PATCH] LED: Fix ...
50
51
52
53
  		count++;
  
  	if (count == size) {
  		ret = count;
0013b23d6   Németh Márton   leds: disable tri...
54
55
56
  
  		if (state == LED_OFF)
  			led_trigger_remove(led_cdev);
c72a1d608   Richard Purdie   [PATCH] LED: add ...
57
58
59
60
61
  		led_set_brightness(led_cdev, state);
  	}
  
  	return ret;
  }
1bd465e6b   Guennadi Liakhovetski   leds: allow led-d...
62
63
64
65
66
67
68
69
  static ssize_t led_max_brightness_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	struct led_classdev *led_cdev = dev_get_drvdata(dev);
  
  	return sprintf(buf, "%u
  ", led_cdev->max_brightness);
  }
14b5d6dd4   Florian Fainelli   leds: Fix race be...
70
71
  static struct device_attribute led_class_attrs[] = {
  	__ATTR(brightness, 0644, led_brightness_show, led_brightness_store),
569762ef3   Axel Lin   leds: led-class: ...
72
  	__ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
73
  #ifdef CONFIG_LEDS_TRIGGERS
14b5d6dd4   Florian Fainelli   leds: Fix race be...
74
  	__ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
75
  #endif
14b5d6dd4   Florian Fainelli   leds: Fix race be...
76
77
  	__ATTR_NULL,
  };
c72a1d608   Richard Purdie   [PATCH] LED: add ...
78

5ada28bf7   Johannes Berg   led-class: always...
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  static void led_timer_function(unsigned long data)
  {
  	struct led_classdev *led_cdev = (void *)data;
  	unsigned long brightness;
  	unsigned long delay;
  
  	if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
  		led_set_brightness(led_cdev, LED_OFF);
  		return;
  	}
  
  	brightness = led_get_brightness(led_cdev);
  	if (!brightness) {
  		/* Time to switch the LED on. */
  		brightness = led_cdev->blink_brightness;
  		delay = led_cdev->blink_delay_on;
  	} else {
  		/* Store the current brightness value to be able
  		 * to restore it when the delay_off period is over.
  		 */
  		led_cdev->blink_brightness = brightness;
  		brightness = LED_OFF;
  		delay = led_cdev->blink_delay_off;
  	}
  
  	led_set_brightness(led_cdev, brightness);
  
  	mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
  }
  
  static void led_stop_software_blink(struct led_classdev *led_cdev)
  {
  	/* deactivate previous settings */
  	del_timer_sync(&led_cdev->blink_timer);
  	led_cdev->blink_delay_on = 0;
  	led_cdev->blink_delay_off = 0;
  }
  
  static void led_set_software_blink(struct led_classdev *led_cdev,
  				   unsigned long delay_on,
  				   unsigned long delay_off)
  {
  	int current_brightness;
  
  	current_brightness = led_get_brightness(led_cdev);
  	if (current_brightness)
  		led_cdev->blink_brightness = current_brightness;
  	if (!led_cdev->blink_brightness)
  		led_cdev->blink_brightness = led_cdev->max_brightness;
fff26f814   Esben Haabendal   leds: support aut...
128
129
  	if (led_get_trigger_data(led_cdev) &&
  	    delay_on == led_cdev->blink_delay_on &&
5ada28bf7   Johannes Berg   led-class: always...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
  	    delay_off == led_cdev->blink_delay_off)
  		return;
  
  	led_stop_software_blink(led_cdev);
  
  	led_cdev->blink_delay_on = delay_on;
  	led_cdev->blink_delay_off = delay_off;
  
  	/* never on - don't blink */
  	if (!delay_on)
  		return;
  
  	/* never off - just set to brightness */
  	if (!delay_off) {
  		led_set_brightness(led_cdev, led_cdev->blink_brightness);
  		return;
  	}
  
  	mod_timer(&led_cdev->blink_timer, jiffies + 1);
  }
c72a1d608   Richard Purdie   [PATCH] LED: add ...
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
  /**
   * led_classdev_suspend - suspend an led_classdev.
   * @led_cdev: the led_classdev to suspend.
   */
  void led_classdev_suspend(struct led_classdev *led_cdev)
  {
  	led_cdev->flags |= LED_SUSPENDED;
  	led_cdev->brightness_set(led_cdev, 0);
  }
  EXPORT_SYMBOL_GPL(led_classdev_suspend);
  
  /**
   * led_classdev_resume - resume an led_classdev.
   * @led_cdev: the led_classdev to resume.
   */
  void led_classdev_resume(struct led_classdev *led_cdev)
  {
  	led_cdev->brightness_set(led_cdev, led_cdev->brightness);
  	led_cdev->flags &= ~LED_SUSPENDED;
  }
  EXPORT_SYMBOL_GPL(led_classdev_resume);
859cb7f2a   Richard Purdie   leds: Add suspend...
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
  static int led_suspend(struct device *dev, pm_message_t state)
  {
  	struct led_classdev *led_cdev = dev_get_drvdata(dev);
  
  	if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
  		led_classdev_suspend(led_cdev);
  
  	return 0;
  }
  
  static int led_resume(struct device *dev)
  {
  	struct led_classdev *led_cdev = dev_get_drvdata(dev);
  
  	if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
  		led_classdev_resume(led_cdev);
  
  	return 0;
  }
c72a1d608   Richard Purdie   [PATCH] LED: add ...
190
191
  /**
   * led_classdev_register - register a new object of led_classdev class.
ff8649aff   Sven Wegener   leds: Fixup kdoc ...
192
   * @parent: The device to register.
c72a1d608   Richard Purdie   [PATCH] LED: add ...
193
194
195
196
   * @led_cdev: the led_classdev structure for this device.
   */
  int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
  {
a9b12619f   Greg Kroah-Hartman   device create: mi...
197
198
  	led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
  				      "%s", led_cdev->name);
801678c5a   Hirofumi Nakagawa   Remove duplicated...
199
  	if (IS_ERR(led_cdev->dev))
f8a7c6fe1   Richard Purdie   leds: Convert fro...
200
  		return PTR_ERR(led_cdev->dev);
c72a1d608   Richard Purdie   [PATCH] LED: add ...
201

270c3957d   Richard Purdie   leds: Fix trigger...
202
203
204
  #ifdef CONFIG_LEDS_TRIGGERS
  	init_rwsem(&led_cdev->trigger_lock);
  #endif
c72a1d608   Richard Purdie   [PATCH] LED: add ...
205
  	/* add to the list of leds */
72f8da329   Richard Purdie   leds: Fix leds_li...
206
  	down_write(&leds_list_lock);
c72a1d608   Richard Purdie   [PATCH] LED: add ...
207
  	list_add_tail(&led_cdev->node, &leds_list);
72f8da329   Richard Purdie   leds: Fix leds_li...
208
  	up_write(&leds_list_lock);
c72a1d608   Richard Purdie   [PATCH] LED: add ...
209

1bd465e6b   Guennadi Liakhovetski   leds: allow led-d...
210
211
  	if (!led_cdev->max_brightness)
  		led_cdev->max_brightness = LED_FULL;
29d76dfa2   Henrique de Moraes Holschuh   leds: Add support...
212
  	led_update_brightness(led_cdev);
5ada28bf7   Johannes Berg   led-class: always...
213
214
215
  	init_timer(&led_cdev->blink_timer);
  	led_cdev->blink_timer.function = led_timer_function;
  	led_cdev->blink_timer.data = (unsigned long)led_cdev;
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
216
  #ifdef CONFIG_LEDS_TRIGGERS
12fda1681   Jeff Garzik   [PATCH] drivers/l...
217
  	led_trigger_set_default(led_cdev);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
218
  #endif
bb9b6ef70   H Hartley Sweeten   leds: led-class.c...
219
220
  	printk(KERN_DEBUG "Registered led device: %s
  ",
f8a7c6fe1   Richard Purdie   leds: Convert fro...
221
  			led_cdev->name);
c72a1d608   Richard Purdie   [PATCH] LED: add ...
222
223
224
225
226
227
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(led_classdev_register);
  
  /**
0266a4589   Qinghuang Feng   leds: Fix leds-cl...
228
   * led_classdev_unregister - unregisters a object of led_properties class.
70d63ccc7   Henrik Kretzschmar   kerneldoc-typo in...
229
   * @led_cdev: the led device to unregister
c72a1d608   Richard Purdie   [PATCH] LED: add ...
230
231
232
   *
   * Unregisters a previously registered via led_classdev_register object.
   */
b844eba29   Rafael J. Wysocki   PM: Remove destro...
233
  void led_classdev_unregister(struct led_classdev *led_cdev)
c72a1d608   Richard Purdie   [PATCH] LED: add ...
234
  {
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
235
  #ifdef CONFIG_LEDS_TRIGGERS
dc47206e5   Richard Purdie   leds: Fix led tri...
236
  	down_write(&led_cdev->trigger_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
237
238
  	if (led_cdev->trigger)
  		led_trigger_set(led_cdev, NULL);
dc47206e5   Richard Purdie   leds: Fix led tri...
239
  	up_write(&led_cdev->trigger_lock);
c3bc9956e   Richard Purdie   [PATCH] LED: add ...
240
  #endif
c72a1d608   Richard Purdie   [PATCH] LED: add ...
241

5ada28bf7   Johannes Berg   led-class: always...
242
243
  	/* Stop blinking */
  	led_brightness_set(led_cdev, LED_OFF);
b844eba29   Rafael J. Wysocki   PM: Remove destro...
244
  	device_unregister(led_cdev->dev);
c72a1d608   Richard Purdie   [PATCH] LED: add ...
245

72f8da329   Richard Purdie   leds: Fix leds_li...
246
  	down_write(&leds_list_lock);
c72a1d608   Richard Purdie   [PATCH] LED: add ...
247
  	list_del(&led_cdev->node);
72f8da329   Richard Purdie   leds: Fix leds_li...
248
  	up_write(&leds_list_lock);
c72a1d608   Richard Purdie   [PATCH] LED: add ...
249
  }
b844eba29   Rafael J. Wysocki   PM: Remove destro...
250
  EXPORT_SYMBOL_GPL(led_classdev_unregister);
c72a1d608   Richard Purdie   [PATCH] LED: add ...
251

5ada28bf7   Johannes Berg   led-class: always...
252
253
254
255
  void led_blink_set(struct led_classdev *led_cdev,
  		   unsigned long *delay_on,
  		   unsigned long *delay_off)
  {
488bc35bf   Antonio Ospite   leds: turn the bl...
256
  	del_timer_sync(&led_cdev->blink_timer);
5ada28bf7   Johannes Berg   led-class: always...
257
  	if (led_cdev->blink_set &&
cb871513f   Johan Hovold   Revert "leds: sav...
258
  	    !led_cdev->blink_set(led_cdev, delay_on, delay_off))
5ada28bf7   Johannes Berg   led-class: always...
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
  		return;
  
  	/* blink with 1 Hz as default if nothing specified */
  	if (!*delay_on && !*delay_off)
  		*delay_on = *delay_off = 500;
  
  	led_set_software_blink(led_cdev, *delay_on, *delay_off);
  }
  EXPORT_SYMBOL(led_blink_set);
  
  void led_brightness_set(struct led_classdev *led_cdev,
  			enum led_brightness brightness)
  {
  	led_stop_software_blink(led_cdev);
  	led_cdev->brightness_set(led_cdev, brightness);
  }
  EXPORT_SYMBOL(led_brightness_set);
c72a1d608   Richard Purdie   [PATCH] LED: add ...
276
277
278
279
280
  static int __init leds_init(void)
  {
  	leds_class = class_create(THIS_MODULE, "leds");
  	if (IS_ERR(leds_class))
  		return PTR_ERR(leds_class);
859cb7f2a   Richard Purdie   leds: Add suspend...
281
282
  	leds_class->suspend = led_suspend;
  	leds_class->resume = led_resume;
14b5d6dd4   Florian Fainelli   leds: Fix race be...
283
  	leds_class->dev_attrs = led_class_attrs;
c72a1d608   Richard Purdie   [PATCH] LED: add ...
284
285
286
287
288
289
290
291
292
293
294
295
296
297
  	return 0;
  }
  
  static void __exit leds_exit(void)
  {
  	class_destroy(leds_class);
  }
  
  subsys_initcall(leds_init);
  module_exit(leds_exit);
  
  MODULE_AUTHOR("John Lenz, Richard Purdie");
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("LED Class Interface");