Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/leds/led-class.c
15109 views
1
/*
2
* LED Class Core
3
*
4
* Copyright (C) 2005 John Lenz <[email protected]>
5
* Copyright (C) 2005-2007 Richard Purdie <[email protected]>
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/sysdev.h>
19
#include <linux/timer.h>
20
#include <linux/err.h>
21
#include <linux/ctype.h>
22
#include <linux/leds.h>
23
#include "leds.h"
24
25
static struct class *leds_class;
26
27
static void led_update_brightness(struct led_classdev *led_cdev)
28
{
29
if (led_cdev->brightness_get)
30
led_cdev->brightness = led_cdev->brightness_get(led_cdev);
31
}
32
33
static ssize_t led_brightness_show(struct device *dev,
34
struct device_attribute *attr, char *buf)
35
{
36
struct led_classdev *led_cdev = dev_get_drvdata(dev);
37
38
/* no lock needed for this */
39
led_update_brightness(led_cdev);
40
41
return sprintf(buf, "%u\n", led_cdev->brightness);
42
}
43
44
static ssize_t led_brightness_store(struct device *dev,
45
struct device_attribute *attr, const char *buf, size_t size)
46
{
47
struct led_classdev *led_cdev = dev_get_drvdata(dev);
48
ssize_t ret = -EINVAL;
49
char *after;
50
unsigned long state = simple_strtoul(buf, &after, 10);
51
size_t count = after - buf;
52
53
if (isspace(*after))
54
count++;
55
56
if (count == size) {
57
ret = count;
58
59
if (state == LED_OFF)
60
led_trigger_remove(led_cdev);
61
led_set_brightness(led_cdev, state);
62
}
63
64
return ret;
65
}
66
67
static ssize_t led_max_brightness_show(struct device *dev,
68
struct device_attribute *attr, char *buf)
69
{
70
struct led_classdev *led_cdev = dev_get_drvdata(dev);
71
72
return sprintf(buf, "%u\n", led_cdev->max_brightness);
73
}
74
75
static struct device_attribute led_class_attrs[] = {
76
__ATTR(brightness, 0644, led_brightness_show, led_brightness_store),
77
__ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
78
#ifdef CONFIG_LEDS_TRIGGERS
79
__ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
80
#endif
81
__ATTR_NULL,
82
};
83
84
static void led_timer_function(unsigned long data)
85
{
86
struct led_classdev *led_cdev = (void *)data;
87
unsigned long brightness;
88
unsigned long delay;
89
90
if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
91
led_set_brightness(led_cdev, LED_OFF);
92
return;
93
}
94
95
brightness = led_get_brightness(led_cdev);
96
if (!brightness) {
97
/* Time to switch the LED on. */
98
brightness = led_cdev->blink_brightness;
99
delay = led_cdev->blink_delay_on;
100
} else {
101
/* Store the current brightness value to be able
102
* to restore it when the delay_off period is over.
103
*/
104
led_cdev->blink_brightness = brightness;
105
brightness = LED_OFF;
106
delay = led_cdev->blink_delay_off;
107
}
108
109
led_set_brightness(led_cdev, brightness);
110
111
mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
112
}
113
114
static void led_stop_software_blink(struct led_classdev *led_cdev)
115
{
116
/* deactivate previous settings */
117
del_timer_sync(&led_cdev->blink_timer);
118
led_cdev->blink_delay_on = 0;
119
led_cdev->blink_delay_off = 0;
120
}
121
122
static void led_set_software_blink(struct led_classdev *led_cdev,
123
unsigned long delay_on,
124
unsigned long delay_off)
125
{
126
int current_brightness;
127
128
current_brightness = led_get_brightness(led_cdev);
129
if (current_brightness)
130
led_cdev->blink_brightness = current_brightness;
131
if (!led_cdev->blink_brightness)
132
led_cdev->blink_brightness = led_cdev->max_brightness;
133
134
if (led_get_trigger_data(led_cdev) &&
135
delay_on == led_cdev->blink_delay_on &&
136
delay_off == led_cdev->blink_delay_off)
137
return;
138
139
led_stop_software_blink(led_cdev);
140
141
led_cdev->blink_delay_on = delay_on;
142
led_cdev->blink_delay_off = delay_off;
143
144
/* never on - don't blink */
145
if (!delay_on)
146
return;
147
148
/* never off - just set to brightness */
149
if (!delay_off) {
150
led_set_brightness(led_cdev, led_cdev->blink_brightness);
151
return;
152
}
153
154
mod_timer(&led_cdev->blink_timer, jiffies + 1);
155
}
156
157
158
/**
159
* led_classdev_suspend - suspend an led_classdev.
160
* @led_cdev: the led_classdev to suspend.
161
*/
162
void led_classdev_suspend(struct led_classdev *led_cdev)
163
{
164
led_cdev->flags |= LED_SUSPENDED;
165
led_cdev->brightness_set(led_cdev, 0);
166
}
167
EXPORT_SYMBOL_GPL(led_classdev_suspend);
168
169
/**
170
* led_classdev_resume - resume an led_classdev.
171
* @led_cdev: the led_classdev to resume.
172
*/
173
void led_classdev_resume(struct led_classdev *led_cdev)
174
{
175
led_cdev->brightness_set(led_cdev, led_cdev->brightness);
176
led_cdev->flags &= ~LED_SUSPENDED;
177
}
178
EXPORT_SYMBOL_GPL(led_classdev_resume);
179
180
static int led_suspend(struct device *dev, pm_message_t state)
181
{
182
struct led_classdev *led_cdev = dev_get_drvdata(dev);
183
184
if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
185
led_classdev_suspend(led_cdev);
186
187
return 0;
188
}
189
190
static int led_resume(struct device *dev)
191
{
192
struct led_classdev *led_cdev = dev_get_drvdata(dev);
193
194
if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
195
led_classdev_resume(led_cdev);
196
197
return 0;
198
}
199
200
/**
201
* led_classdev_register - register a new object of led_classdev class.
202
* @parent: The device to register.
203
* @led_cdev: the led_classdev structure for this device.
204
*/
205
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
206
{
207
led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
208
"%s", led_cdev->name);
209
if (IS_ERR(led_cdev->dev))
210
return PTR_ERR(led_cdev->dev);
211
212
#ifdef CONFIG_LEDS_TRIGGERS
213
init_rwsem(&led_cdev->trigger_lock);
214
#endif
215
/* add to the list of leds */
216
down_write(&leds_list_lock);
217
list_add_tail(&led_cdev->node, &leds_list);
218
up_write(&leds_list_lock);
219
220
if (!led_cdev->max_brightness)
221
led_cdev->max_brightness = LED_FULL;
222
223
led_update_brightness(led_cdev);
224
225
init_timer(&led_cdev->blink_timer);
226
led_cdev->blink_timer.function = led_timer_function;
227
led_cdev->blink_timer.data = (unsigned long)led_cdev;
228
229
#ifdef CONFIG_LEDS_TRIGGERS
230
led_trigger_set_default(led_cdev);
231
#endif
232
233
printk(KERN_DEBUG "Registered led device: %s\n",
234
led_cdev->name);
235
236
return 0;
237
}
238
EXPORT_SYMBOL_GPL(led_classdev_register);
239
240
/**
241
* led_classdev_unregister - unregisters a object of led_properties class.
242
* @led_cdev: the led device to unregister
243
*
244
* Unregisters a previously registered via led_classdev_register object.
245
*/
246
void led_classdev_unregister(struct led_classdev *led_cdev)
247
{
248
#ifdef CONFIG_LEDS_TRIGGERS
249
down_write(&led_cdev->trigger_lock);
250
if (led_cdev->trigger)
251
led_trigger_set(led_cdev, NULL);
252
up_write(&led_cdev->trigger_lock);
253
#endif
254
255
/* Stop blinking */
256
led_brightness_set(led_cdev, LED_OFF);
257
258
device_unregister(led_cdev->dev);
259
260
down_write(&leds_list_lock);
261
list_del(&led_cdev->node);
262
up_write(&leds_list_lock);
263
}
264
EXPORT_SYMBOL_GPL(led_classdev_unregister);
265
266
void led_blink_set(struct led_classdev *led_cdev,
267
unsigned long *delay_on,
268
unsigned long *delay_off)
269
{
270
if (led_cdev->blink_set &&
271
!led_cdev->blink_set(led_cdev, delay_on, delay_off))
272
return;
273
274
/* blink with 1 Hz as default if nothing specified */
275
if (!*delay_on && !*delay_off)
276
*delay_on = *delay_off = 500;
277
278
led_set_software_blink(led_cdev, *delay_on, *delay_off);
279
}
280
EXPORT_SYMBOL(led_blink_set);
281
282
void led_brightness_set(struct led_classdev *led_cdev,
283
enum led_brightness brightness)
284
{
285
led_stop_software_blink(led_cdev);
286
led_cdev->brightness_set(led_cdev, brightness);
287
}
288
EXPORT_SYMBOL(led_brightness_set);
289
290
static int __init leds_init(void)
291
{
292
leds_class = class_create(THIS_MODULE, "leds");
293
if (IS_ERR(leds_class))
294
return PTR_ERR(leds_class);
295
leds_class->suspend = led_suspend;
296
leds_class->resume = led_resume;
297
leds_class->dev_attrs = led_class_attrs;
298
return 0;
299
}
300
301
static void __exit leds_exit(void)
302
{
303
class_destroy(leds_class);
304
}
305
306
subsys_initcall(leds_init);
307
module_exit(leds_exit);
308
309
MODULE_AUTHOR("John Lenz, Richard Purdie");
310
MODULE_LICENSE("GPL");
311
MODULE_DESCRIPTION("LED Class Interface");
312
313