Branch data Line data Source code
1 : : /*
2 : : * LED Class Core
3 : : *
4 : : * Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu>
5 : : * Copyright (C) 2005-2007 Richard Purdie <rpurdie@openedhand.com>
6 : : *
7 : : * This program is free software; you can redistribute it and/or modify
8 : : * it under the terms of the GNU General Public License version 2 as
9 : : * published by the Free Software Foundation.
10 : : */
11 : :
12 : : #include <linux/module.h>
13 : : #include <linux/kernel.h>
14 : : #include <linux/init.h>
15 : : #include <linux/list.h>
16 : : #include <linux/spinlock.h>
17 : : #include <linux/device.h>
18 : : #include <linux/timer.h>
19 : : #include <linux/err.h>
20 : : #include <linux/ctype.h>
21 : : #include <linux/leds.h>
22 : : #include "leds.h"
23 : :
24 : : static struct class *leds_class;
25 : :
26 : : static void led_update_brightness(struct led_classdev *led_cdev)
27 : : {
28 [ # # # # ]: 0 : if (led_cdev->brightness_get)
29 : 0 : led_cdev->brightness = led_cdev->brightness_get(led_cdev);
30 : : }
31 : :
32 : 0 : static ssize_t brightness_show(struct device *dev,
33 : : struct device_attribute *attr, char *buf)
34 : : {
35 : 0 : struct led_classdev *led_cdev = dev_get_drvdata(dev);
36 : :
37 : : /* no lock needed for this */
38 : : led_update_brightness(led_cdev);
39 : :
40 : 0 : return sprintf(buf, "%u\n", led_cdev->brightness);
41 : : }
42 : :
43 : 0 : static ssize_t brightness_store(struct device *dev,
44 : : struct device_attribute *attr, const char *buf, size_t size)
45 : : {
46 : 0 : struct led_classdev *led_cdev = dev_get_drvdata(dev);
47 : : unsigned long state;
48 : : ssize_t ret = -EINVAL;
49 : :
50 : : ret = kstrtoul(buf, 10, &state);
51 [ # # ]: 0 : if (ret)
52 : : return ret;
53 : :
54 [ # # ]: 0 : if (state == LED_OFF)
55 : 0 : led_trigger_remove(led_cdev);
56 : 0 : __led_set_brightness(led_cdev, state);
57 : :
58 : 0 : return size;
59 : : }
60 : : static DEVICE_ATTR_RW(brightness);
61 : :
62 : 0 : static ssize_t led_max_brightness_show(struct device *dev,
63 : : struct device_attribute *attr, char *buf)
64 : : {
65 : 0 : struct led_classdev *led_cdev = dev_get_drvdata(dev);
66 : :
67 : 0 : return sprintf(buf, "%u\n", led_cdev->max_brightness);
68 : : }
69 : : static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL);
70 : :
71 : : #ifdef CONFIG_LEDS_TRIGGERS
72 : : static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
73 : : static struct attribute *led_trigger_attrs[] = {
74 : : &dev_attr_trigger.attr,
75 : : NULL,
76 : : };
77 : : static const struct attribute_group led_trigger_group = {
78 : : .attrs = led_trigger_attrs,
79 : : };
80 : : #endif
81 : :
82 : : static struct attribute *led_class_attrs[] = {
83 : : &dev_attr_brightness.attr,
84 : : &dev_attr_max_brightness.attr,
85 : : NULL,
86 : : };
87 : :
88 : : static const struct attribute_group led_group = {
89 : : .attrs = led_class_attrs,
90 : : };
91 : :
92 : : static const struct attribute_group *led_groups[] = {
93 : : &led_group,
94 : : #ifdef CONFIG_LEDS_TRIGGERS
95 : : &led_trigger_group,
96 : : #endif
97 : : NULL,
98 : : };
99 : :
100 : 0 : static void led_timer_function(unsigned long data)
101 : : {
102 : 0 : struct led_classdev *led_cdev = (void *)data;
103 : : unsigned long brightness;
104 : : unsigned long delay;
105 : :
106 [ # # ][ # # ]: 0 : if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
107 : : __led_set_brightness(led_cdev, LED_OFF);
108 : : return;
109 : : }
110 : :
111 [ # # ]: 0 : if (led_cdev->flags & LED_BLINK_ONESHOT_STOP) {
112 : 0 : led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
113 : 0 : return;
114 : : }
115 : :
116 : 0 : brightness = led_get_brightness(led_cdev);
117 [ # # ]: 0 : if (!brightness) {
118 : : /* Time to switch the LED on. */
119 : 0 : brightness = led_cdev->blink_brightness;
120 : : delay = led_cdev->blink_delay_on;
121 : : } else {
122 : : /* Store the current brightness value to be able
123 : : * to restore it when the delay_off period is over.
124 : : */
125 : 0 : led_cdev->blink_brightness = brightness;
126 : : brightness = LED_OFF;
127 : : delay = led_cdev->blink_delay_off;
128 : : }
129 : :
130 : : __led_set_brightness(led_cdev, brightness);
131 : :
132 : : /* Return in next iteration if led is in one-shot mode and we are in
133 : : * the final blink state so that the led is toggled each delay_on +
134 : : * delay_off milliseconds in worst case.
135 : : */
136 [ # # ]: 0 : if (led_cdev->flags & LED_BLINK_ONESHOT) {
137 [ # # ]: 0 : if (led_cdev->flags & LED_BLINK_INVERT) {
138 [ # # ]: 0 : if (brightness)
139 : 0 : led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
140 : : } else {
141 [ # # ]: 0 : if (!brightness)
142 : 0 : led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
143 : : }
144 : : }
145 : :
146 : 0 : mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
147 : : }
148 : :
149 : 0 : static void set_brightness_delayed(struct work_struct *ws)
150 : : {
151 : 0 : struct led_classdev *led_cdev =
152 : : container_of(ws, struct led_classdev, set_brightness_work);
153 : :
154 : 0 : led_stop_software_blink(led_cdev);
155 : :
156 : 0 : __led_set_brightness(led_cdev, led_cdev->delayed_set_value);
157 : 0 : }
158 : :
159 : : /**
160 : : * led_classdev_suspend - suspend an led_classdev.
161 : : * @led_cdev: the led_classdev to suspend.
162 : : */
163 : 0 : void led_classdev_suspend(struct led_classdev *led_cdev)
164 : : {
165 : 0 : led_cdev->flags |= LED_SUSPENDED;
166 : 0 : led_cdev->brightness_set(led_cdev, 0);
167 : 0 : }
168 : : EXPORT_SYMBOL_GPL(led_classdev_suspend);
169 : :
170 : : /**
171 : : * led_classdev_resume - resume an led_classdev.
172 : : * @led_cdev: the led_classdev to resume.
173 : : */
174 : 0 : void led_classdev_resume(struct led_classdev *led_cdev)
175 : : {
176 : 0 : led_cdev->brightness_set(led_cdev, led_cdev->brightness);
177 : 0 : led_cdev->flags &= ~LED_SUSPENDED;
178 : 0 : }
179 : : EXPORT_SYMBOL_GPL(led_classdev_resume);
180 : :
181 : 0 : static int led_suspend(struct device *dev)
182 : : {
183 : 0 : struct led_classdev *led_cdev = dev_get_drvdata(dev);
184 : :
185 [ # # ]: 0 : if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
186 : : led_classdev_suspend(led_cdev);
187 : :
188 : 0 : return 0;
189 : : }
190 : :
191 : 0 : static int led_resume(struct device *dev)
192 : : {
193 : 0 : struct led_classdev *led_cdev = dev_get_drvdata(dev);
194 : :
195 [ # # ]: 0 : if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
196 : : led_classdev_resume(led_cdev);
197 : :
198 : 0 : return 0;
199 : : }
200 : :
201 : : static const struct dev_pm_ops leds_class_dev_pm_ops = {
202 : : .suspend = led_suspend,
203 : : .resume = led_resume,
204 : : };
205 : :
206 : : /**
207 : : * led_classdev_register - register a new object of led_classdev class.
208 : : * @parent: The device to register.
209 : : * @led_cdev: the led_classdev structure for this device.
210 : : */
211 : 0 : int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
212 : : {
213 : 0 : led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
214 : : "%s", led_cdev->name);
215 [ # # ]: 0 : if (IS_ERR(led_cdev->dev))
216 : 0 : return PTR_ERR(led_cdev->dev);
217 : :
218 : : #ifdef CONFIG_LEDS_TRIGGERS
219 : 0 : init_rwsem(&led_cdev->trigger_lock);
220 : : #endif
221 : : /* add to the list of leds */
222 : 0 : down_write(&leds_list_lock);
223 : 0 : list_add_tail(&led_cdev->node, &leds_list);
224 : 0 : up_write(&leds_list_lock);
225 : :
226 [ # # ]: 0 : if (!led_cdev->max_brightness)
227 : 0 : led_cdev->max_brightness = LED_FULL;
228 : :
229 : : led_update_brightness(led_cdev);
230 : :
231 : 0 : INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed);
232 : :
233 : 0 : init_timer(&led_cdev->blink_timer);
234 : 0 : led_cdev->blink_timer.function = led_timer_function;
235 : 0 : led_cdev->blink_timer.data = (unsigned long)led_cdev;
236 : :
237 : : #ifdef CONFIG_LEDS_TRIGGERS
238 : 0 : led_trigger_set_default(led_cdev);
239 : : #endif
240 : :
241 : : dev_dbg(parent, "Registered led device: %s\n",
242 : : led_cdev->name);
243 : :
244 : 0 : return 0;
245 : : }
246 : : EXPORT_SYMBOL_GPL(led_classdev_register);
247 : :
248 : : /**
249 : : * led_classdev_unregister - unregisters a object of led_properties class.
250 : : * @led_cdev: the led device to unregister
251 : : *
252 : : * Unregisters a previously registered via led_classdev_register object.
253 : : */
254 : 0 : void led_classdev_unregister(struct led_classdev *led_cdev)
255 : : {
256 : : #ifdef CONFIG_LEDS_TRIGGERS
257 : 0 : down_write(&led_cdev->trigger_lock);
258 [ # # ]: 0 : if (led_cdev->trigger)
259 : 0 : led_trigger_set(led_cdev, NULL);
260 : 0 : up_write(&led_cdev->trigger_lock);
261 : : #endif
262 : :
263 : 0 : cancel_work_sync(&led_cdev->set_brightness_work);
264 : :
265 : : /* Stop blinking */
266 : 0 : led_stop_software_blink(led_cdev);
267 : 0 : led_set_brightness(led_cdev, LED_OFF);
268 : :
269 : 0 : device_unregister(led_cdev->dev);
270 : :
271 : 0 : down_write(&leds_list_lock);
272 : : list_del(&led_cdev->node);
273 : 0 : up_write(&leds_list_lock);
274 : 0 : }
275 : : EXPORT_SYMBOL_GPL(led_classdev_unregister);
276 : :
277 : 0 : static int __init leds_init(void)
278 : : {
279 : 0 : leds_class = class_create(THIS_MODULE, "leds");
280 [ # # ]: 0 : if (IS_ERR(leds_class))
281 : 0 : return PTR_ERR(leds_class);
282 : 0 : leds_class->pm = &leds_class_dev_pm_ops;
283 : 0 : leds_class->dev_groups = led_groups;
284 : 0 : return 0;
285 : : }
286 : :
287 : 0 : static void __exit leds_exit(void)
288 : : {
289 : 0 : class_destroy(leds_class);
290 : 0 : }
291 : :
292 : : subsys_initcall(leds_init);
293 : : module_exit(leds_exit);
294 : :
295 : : MODULE_AUTHOR("John Lenz, Richard Purdie");
296 : : MODULE_LICENSE("GPL");
297 : : MODULE_DESCRIPTION("LED Class Interface");
|