1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * LED Class Core
4 *
5 * Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu>
6 * Copyright (C) 2005-2007 Richard Purdie <rpurdie@openedhand.com>
7 */
8
9 #include <linux/ctype.h>
10 #include <linux/device.h>
11 #include <linux/err.h>
12 #include <linux/init.h>
13 #include <linux/kernel.h>
14 #include <linux/leds.h>
15 #include <linux/list.h>
16 #include <linux/module.h>
17 #include <linux/property.h>
18 #include <linux/slab.h>
19 #include <linux/spinlock.h>
20 #include <linux/timer.h>
21 #include <uapi/linux/uleds.h>
22 #include <linux/of.h>
23 #include "leds.h"
24
25 static DEFINE_MUTEX(leds_lookup_lock);
26 static LIST_HEAD(leds_lookup_list);
27
brightness_show(struct device * dev,struct device_attribute * attr,char * buf)28 static ssize_t brightness_show(struct device *dev,
29 struct device_attribute *attr, char *buf)
30 {
31 struct led_classdev *led_cdev = dev_get_drvdata(dev);
32 unsigned int brightness;
33
34 mutex_lock(&led_cdev->led_access);
35 led_update_brightness(led_cdev);
36 brightness = led_cdev->brightness;
37 mutex_unlock(&led_cdev->led_access);
38
39 return sprintf(buf, "%u\n", brightness);
40 }
41
brightness_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)42 static ssize_t brightness_store(struct device *dev,
43 struct device_attribute *attr, const char *buf, size_t size)
44 {
45 struct led_classdev *led_cdev = dev_get_drvdata(dev);
46 unsigned long state;
47 ssize_t ret;
48
49 mutex_lock(&led_cdev->led_access);
50
51 if (led_sysfs_is_disabled(led_cdev)) {
52 ret = -EBUSY;
53 goto unlock;
54 }
55
56 ret = kstrtoul(buf, 10, &state);
57 if (ret)
58 goto unlock;
59
60 if (state == LED_OFF)
61 led_trigger_remove(led_cdev);
62 /* flush out any request to disable blinking */
63 flush_work(&led_cdev->set_brightness_work);
64 led_set_brightness(led_cdev, state);
65 flush_work(&led_cdev->set_brightness_work);
66
67 ret = size;
68 unlock:
69 mutex_unlock(&led_cdev->led_access);
70 return ret;
71 }
72 static DEVICE_ATTR_RW(brightness);
73
max_brightness_show(struct device * dev,struct device_attribute * attr,char * buf)74 static ssize_t max_brightness_show(struct device *dev,
75 struct device_attribute *attr, char *buf)
76 {
77 struct led_classdev *led_cdev = dev_get_drvdata(dev);
78 unsigned int max_brightness;
79
80 mutex_lock(&led_cdev->led_access);
81 max_brightness = led_cdev->max_brightness;
82 mutex_unlock(&led_cdev->led_access);
83
84 return sprintf(buf, "%u\n", max_brightness);
85 }
86 static DEVICE_ATTR_RO(max_brightness);
87
88 #ifdef CONFIG_LEDS_TRIGGERS
89 static BIN_ATTR(trigger, 0644, led_trigger_read, led_trigger_write, 0);
90 static struct bin_attribute *led_trigger_bin_attrs[] = {
91 &bin_attr_trigger,
92 NULL,
93 };
94 static const struct attribute_group led_trigger_group = {
95 .bin_attrs = led_trigger_bin_attrs,
96 };
97 #endif
98
99 static struct attribute *led_class_attrs[] = {
100 &dev_attr_brightness.attr,
101 &dev_attr_max_brightness.attr,
102 NULL,
103 };
104
105 static const struct attribute_group led_group = {
106 .attrs = led_class_attrs,
107 };
108
109 static const struct attribute_group *led_groups[] = {
110 &led_group,
111 #ifdef CONFIG_LEDS_TRIGGERS
112 &led_trigger_group,
113 #endif
114 NULL,
115 };
116
117 #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
brightness_hw_changed_show(struct device * dev,struct device_attribute * attr,char * buf)118 static ssize_t brightness_hw_changed_show(struct device *dev,
119 struct device_attribute *attr, char *buf)
120 {
121 struct led_classdev *led_cdev = dev_get_drvdata(dev);
122
123 if (led_cdev->brightness_hw_changed == -1)
124 return -ENODATA;
125
126 return sprintf(buf, "%u\n", led_cdev->brightness_hw_changed);
127 }
128
129 static DEVICE_ATTR_RO(brightness_hw_changed);
130
led_add_brightness_hw_changed(struct led_classdev * led_cdev)131 static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
132 {
133 struct device *dev = led_cdev->dev;
134 int ret;
135
136 ret = device_create_file(dev, &dev_attr_brightness_hw_changed);
137 if (ret) {
138 dev_err(dev, "Error creating brightness_hw_changed\n");
139 return ret;
140 }
141
142 led_cdev->brightness_hw_changed_kn =
143 sysfs_get_dirent(dev->kobj.sd, "brightness_hw_changed");
144 if (!led_cdev->brightness_hw_changed_kn) {
145 dev_err(dev, "Error getting brightness_hw_changed kn\n");
146 device_remove_file(dev, &dev_attr_brightness_hw_changed);
147 return -ENXIO;
148 }
149
150 return 0;
151 }
152
led_remove_brightness_hw_changed(struct led_classdev * led_cdev)153 static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
154 {
155 sysfs_put(led_cdev->brightness_hw_changed_kn);
156 device_remove_file(led_cdev->dev, &dev_attr_brightness_hw_changed);
157 }
158
led_classdev_notify_brightness_hw_changed(struct led_classdev * led_cdev,unsigned int brightness)159 void led_classdev_notify_brightness_hw_changed(struct led_classdev *led_cdev, unsigned int brightness)
160 {
161 if (WARN_ON(!led_cdev->brightness_hw_changed_kn))
162 return;
163
164 led_cdev->brightness_hw_changed = brightness;
165 sysfs_notify_dirent(led_cdev->brightness_hw_changed_kn);
166 }
167 EXPORT_SYMBOL_GPL(led_classdev_notify_brightness_hw_changed);
168 #else
led_add_brightness_hw_changed(struct led_classdev * led_cdev)169 static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
170 {
171 return 0;
172 }
led_remove_brightness_hw_changed(struct led_classdev * led_cdev)173 static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
174 {
175 }
176 #endif
177
178 /**
179 * led_classdev_suspend - suspend an led_classdev.
180 * @led_cdev: the led_classdev to suspend.
181 */
led_classdev_suspend(struct led_classdev * led_cdev)182 void led_classdev_suspend(struct led_classdev *led_cdev)
183 {
184 led_cdev->flags |= LED_SUSPENDED;
185 led_set_brightness_nopm(led_cdev, 0);
186 flush_work(&led_cdev->set_brightness_work);
187 }
188 EXPORT_SYMBOL_GPL(led_classdev_suspend);
189
190 /**
191 * led_classdev_resume - resume an led_classdev.
192 * @led_cdev: the led_classdev to resume.
193 */
led_classdev_resume(struct led_classdev * led_cdev)194 void led_classdev_resume(struct led_classdev *led_cdev)
195 {
196 led_set_brightness_nopm(led_cdev, led_cdev->brightness);
197
198 if (led_cdev->flash_resume)
199 led_cdev->flash_resume(led_cdev);
200
201 led_cdev->flags &= ~LED_SUSPENDED;
202 }
203 EXPORT_SYMBOL_GPL(led_classdev_resume);
204
205 #ifdef CONFIG_PM_SLEEP
led_suspend(struct device * dev)206 static int led_suspend(struct device *dev)
207 {
208 struct led_classdev *led_cdev = dev_get_drvdata(dev);
209
210 if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
211 led_classdev_suspend(led_cdev);
212
213 return 0;
214 }
215
led_resume(struct device * dev)216 static int led_resume(struct device *dev)
217 {
218 struct led_classdev *led_cdev = dev_get_drvdata(dev);
219
220 if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
221 led_classdev_resume(led_cdev);
222
223 return 0;
224 }
225 #endif
226
227 static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);
228
led_module_get(struct device * led_dev)229 static struct led_classdev *led_module_get(struct device *led_dev)
230 {
231 struct led_classdev *led_cdev;
232
233 if (!led_dev)
234 return ERR_PTR(-EPROBE_DEFER);
235
236 led_cdev = dev_get_drvdata(led_dev);
237
238 if (!try_module_get(led_cdev->dev->parent->driver->owner)) {
239 put_device(led_cdev->dev);
240 return ERR_PTR(-ENODEV);
241 }
242
243 return led_cdev;
244 }
245
246 static const struct class leds_class = {
247 .name = "leds",
248 .dev_groups = led_groups,
249 .pm = &leds_class_dev_pm_ops,
250 };
251
252 /**
253 * of_led_get() - request a LED device via the LED framework
254 * @np: device node to get the LED device from
255 * @index: the index of the LED
256 *
257 * Returns the LED device parsed from the phandle specified in the "leds"
258 * property of a device tree node or a negative error-code on failure.
259 */
of_led_get(struct device_node * np,int index)260 struct led_classdev *of_led_get(struct device_node *np, int index)
261 {
262 struct device *led_dev;
263 struct device_node *led_node;
264
265 led_node = of_parse_phandle(np, "leds", index);
266 if (!led_node)
267 return ERR_PTR(-ENOENT);
268
269 led_dev = class_find_device_by_of_node(&leds_class, led_node);
270 of_node_put(led_node);
271
272 return led_module_get(led_dev);
273 }
274 EXPORT_SYMBOL_GPL(of_led_get);
275
276 /**
277 * led_put() - release a LED device
278 * @led_cdev: LED device
279 */
led_put(struct led_classdev * led_cdev)280 void led_put(struct led_classdev *led_cdev)
281 {
282 module_put(led_cdev->dev->parent->driver->owner);
283 put_device(led_cdev->dev);
284 }
285 EXPORT_SYMBOL_GPL(led_put);
286
devm_led_release(struct device * dev,void * res)287 static void devm_led_release(struct device *dev, void *res)
288 {
289 struct led_classdev **p = res;
290
291 led_put(*p);
292 }
293
__devm_led_get(struct device * dev,struct led_classdev * led)294 static struct led_classdev *__devm_led_get(struct device *dev, struct led_classdev *led)
295 {
296 struct led_classdev **dr;
297
298 dr = devres_alloc(devm_led_release, sizeof(struct led_classdev *), GFP_KERNEL);
299 if (!dr) {
300 led_put(led);
301 return ERR_PTR(-ENOMEM);
302 }
303
304 *dr = led;
305 devres_add(dev, dr);
306
307 return led;
308 }
309
310 /**
311 * devm_of_led_get - Resource-managed request of a LED device
312 * @dev: LED consumer
313 * @index: index of the LED to obtain in the consumer
314 *
315 * The device node of the device is parse to find the request LED device.
316 * The LED device returned from this function is automatically released
317 * on driver detach.
318 *
319 * @return a pointer to a LED device or ERR_PTR(errno) on failure.
320 */
devm_of_led_get(struct device * dev,int index)321 struct led_classdev *__must_check devm_of_led_get(struct device *dev,
322 int index)
323 {
324 struct led_classdev *led;
325
326 if (!dev)
327 return ERR_PTR(-EINVAL);
328
329 led = of_led_get(dev->of_node, index);
330 if (IS_ERR(led))
331 return led;
332
333 return __devm_led_get(dev, led);
334 }
335 EXPORT_SYMBOL_GPL(devm_of_led_get);
336
337 /**
338 * led_get() - request a LED device via the LED framework
339 * @dev: device for which to get the LED device
340 * @con_id: name of the LED from the device's point of view
341 *
342 * @return a pointer to a LED device or ERR_PTR(errno) on failure.
343 */
led_get(struct device * dev,char * con_id)344 struct led_classdev *led_get(struct device *dev, char *con_id)
345 {
346 struct led_lookup_data *lookup;
347 const char *provider = NULL;
348 struct device *led_dev;
349
350 mutex_lock(&leds_lookup_lock);
351 list_for_each_entry(lookup, &leds_lookup_list, list) {
352 if (!strcmp(lookup->dev_id, dev_name(dev)) &&
353 !strcmp(lookup->con_id, con_id)) {
354 provider = kstrdup_const(lookup->provider, GFP_KERNEL);
355 break;
356 }
357 }
358 mutex_unlock(&leds_lookup_lock);
359
360 if (!provider)
361 return ERR_PTR(-ENOENT);
362
363 led_dev = class_find_device_by_name(&leds_class, provider);
364 kfree_const(provider);
365
366 return led_module_get(led_dev);
367 }
368 EXPORT_SYMBOL_GPL(led_get);
369
370 /**
371 * devm_led_get() - request a LED device via the LED framework
372 * @dev: device for which to get the LED device
373 * @con_id: name of the LED from the device's point of view
374 *
375 * The LED device returned from this function is automatically released
376 * on driver detach.
377 *
378 * @return a pointer to a LED device or ERR_PTR(errno) on failure.
379 */
devm_led_get(struct device * dev,char * con_id)380 struct led_classdev *devm_led_get(struct device *dev, char *con_id)
381 {
382 struct led_classdev *led;
383
384 led = led_get(dev, con_id);
385 if (IS_ERR(led))
386 return led;
387
388 return __devm_led_get(dev, led);
389 }
390 EXPORT_SYMBOL_GPL(devm_led_get);
391
392 /**
393 * led_add_lookup() - Add a LED lookup table entry
394 * @led_lookup: the lookup table entry to add
395 *
396 * Add a LED lookup table entry. On systems without devicetree the lookup table
397 * is used by led_get() to find LEDs.
398 */
led_add_lookup(struct led_lookup_data * led_lookup)399 void led_add_lookup(struct led_lookup_data *led_lookup)
400 {
401 mutex_lock(&leds_lookup_lock);
402 list_add_tail(&led_lookup->list, &leds_lookup_list);
403 mutex_unlock(&leds_lookup_lock);
404 }
405 EXPORT_SYMBOL_GPL(led_add_lookup);
406
407 /**
408 * led_remove_lookup() - Remove a LED lookup table entry
409 * @led_lookup: the lookup table entry to remove
410 */
led_remove_lookup(struct led_lookup_data * led_lookup)411 void led_remove_lookup(struct led_lookup_data *led_lookup)
412 {
413 mutex_lock(&leds_lookup_lock);
414 list_del(&led_lookup->list);
415 mutex_unlock(&leds_lookup_lock);
416 }
417 EXPORT_SYMBOL_GPL(led_remove_lookup);
418
419 /**
420 * devm_of_led_get_optional - Resource-managed request of an optional LED device
421 * @dev: LED consumer
422 * @index: index of the LED to obtain in the consumer
423 *
424 * The device node of the device is parsed to find the requested LED device.
425 * The LED device returned from this function is automatically released
426 * on driver detach.
427 *
428 * @return a pointer to a LED device, ERR_PTR(errno) on failure and NULL if the
429 * led was not found.
430 */
devm_of_led_get_optional(struct device * dev,int index)431 struct led_classdev *__must_check devm_of_led_get_optional(struct device *dev,
432 int index)
433 {
434 struct led_classdev *led;
435
436 led = devm_of_led_get(dev, index);
437 if (IS_ERR(led) && PTR_ERR(led) == -ENOENT)
438 return NULL;
439
440 return led;
441 }
442 EXPORT_SYMBOL_GPL(devm_of_led_get_optional);
443
led_classdev_next_name(const char * init_name,char * name,size_t len)444 static int led_classdev_next_name(const char *init_name, char *name,
445 size_t len)
446 {
447 unsigned int i = 0;
448 int ret = 0;
449 struct device *dev;
450
451 strscpy(name, init_name, len);
452
453 while ((ret < len) &&
454 (dev = class_find_device_by_name(&leds_class, name))) {
455 put_device(dev);
456 ret = snprintf(name, len, "%s_%u", init_name, ++i);
457 }
458
459 if (ret >= len)
460 return -ENOMEM;
461
462 return i;
463 }
464
465 /**
466 * led_classdev_register_ext - register a new object of led_classdev class
467 * with init data.
468 *
469 * @parent: parent of LED device
470 * @led_cdev: the led_classdev structure for this device.
471 * @init_data: LED class device initialization data
472 */
led_classdev_register_ext(struct device * parent,struct led_classdev * led_cdev,struct led_init_data * init_data)473 int led_classdev_register_ext(struct device *parent,
474 struct led_classdev *led_cdev,
475 struct led_init_data *init_data)
476 {
477 char composed_name[LED_MAX_NAME_SIZE];
478 char final_name[LED_MAX_NAME_SIZE];
479 const char *proposed_name = composed_name;
480 int ret;
481
482 if (init_data) {
483 if (init_data->devname_mandatory && !init_data->devicename) {
484 dev_err(parent, "Mandatory device name is missing");
485 return -EINVAL;
486 }
487 ret = led_compose_name(parent, init_data, composed_name);
488 if (ret < 0)
489 return ret;
490
491 if (init_data->fwnode) {
492 fwnode_property_read_string(init_data->fwnode,
493 "linux,default-trigger",
494 &led_cdev->default_trigger);
495
496 if (fwnode_property_present(init_data->fwnode,
497 "retain-state-shutdown"))
498 led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN;
499
500 fwnode_property_read_u32(init_data->fwnode,
501 "max-brightness",
502 &led_cdev->max_brightness);
503
504 if (fwnode_property_present(init_data->fwnode, "color"))
505 fwnode_property_read_u32(init_data->fwnode, "color",
506 &led_cdev->color);
507 }
508 } else {
509 proposed_name = led_cdev->name;
510 }
511
512 ret = led_classdev_next_name(proposed_name, final_name, sizeof(final_name));
513 if (ret < 0)
514 return ret;
515
516 if (led_cdev->color >= LED_COLOR_ID_MAX)
517 dev_warn(parent, "LED %s color identifier out of range\n", final_name);
518
519 mutex_init(&led_cdev->led_access);
520 mutex_lock(&led_cdev->led_access);
521 led_cdev->dev = device_create_with_groups(&leds_class, parent, 0,
522 led_cdev, led_cdev->groups, "%s", final_name);
523 if (IS_ERR(led_cdev->dev)) {
524 mutex_unlock(&led_cdev->led_access);
525 return PTR_ERR(led_cdev->dev);
526 }
527 if (init_data && init_data->fwnode)
528 device_set_node(led_cdev->dev, init_data->fwnode);
529
530 if (ret)
531 dev_warn(parent, "Led %s renamed to %s due to name collision",
532 proposed_name, dev_name(led_cdev->dev));
533
534 if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
535 ret = led_add_brightness_hw_changed(led_cdev);
536 if (ret) {
537 device_unregister(led_cdev->dev);
538 led_cdev->dev = NULL;
539 mutex_unlock(&led_cdev->led_access);
540 return ret;
541 }
542 }
543
544 led_cdev->work_flags = 0;
545 #ifdef CONFIG_LEDS_TRIGGERS
546 init_rwsem(&led_cdev->trigger_lock);
547 #endif
548 #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
549 led_cdev->brightness_hw_changed = -1;
550 #endif
551 /* add to the list of leds */
552 down_write(&leds_list_lock);
553 list_add_tail(&led_cdev->node, &leds_list);
554 up_write(&leds_list_lock);
555
556 if (!led_cdev->max_brightness)
557 led_cdev->max_brightness = LED_FULL;
558
559 led_update_brightness(led_cdev);
560
561 led_init_core(led_cdev);
562
563 #ifdef CONFIG_LEDS_TRIGGERS
564 led_trigger_set_default(led_cdev);
565 #endif
566
567 mutex_unlock(&led_cdev->led_access);
568
569 dev_dbg(parent, "Registered led device: %s\n",
570 led_cdev->name);
571
572 return 0;
573 }
574 EXPORT_SYMBOL_GPL(led_classdev_register_ext);
575
576 /**
577 * led_classdev_unregister - unregisters a object of led_properties class.
578 * @led_cdev: the led device to unregister
579 *
580 * Unregisters a previously registered via led_classdev_register object.
581 */
led_classdev_unregister(struct led_classdev * led_cdev)582 void led_classdev_unregister(struct led_classdev *led_cdev)
583 {
584 if (IS_ERR_OR_NULL(led_cdev->dev))
585 return;
586
587 #ifdef CONFIG_LEDS_TRIGGERS
588 down_write(&led_cdev->trigger_lock);
589 if (led_cdev->trigger)
590 led_trigger_set(led_cdev, NULL);
591 up_write(&led_cdev->trigger_lock);
592 #endif
593
594 led_cdev->flags |= LED_UNREGISTERING;
595
596 /* Stop blinking */
597 led_stop_software_blink(led_cdev);
598
599 if (!(led_cdev->flags & LED_RETAIN_AT_SHUTDOWN))
600 led_set_brightness(led_cdev, LED_OFF);
601
602 flush_work(&led_cdev->set_brightness_work);
603
604 if (led_cdev->flags & LED_BRIGHT_HW_CHANGED)
605 led_remove_brightness_hw_changed(led_cdev);
606
607 device_unregister(led_cdev->dev);
608
609 down_write(&leds_list_lock);
610 list_del(&led_cdev->node);
611 up_write(&leds_list_lock);
612
613 mutex_destroy(&led_cdev->led_access);
614 }
615 EXPORT_SYMBOL_GPL(led_classdev_unregister);
616
devm_led_classdev_release(struct device * dev,void * res)617 static void devm_led_classdev_release(struct device *dev, void *res)
618 {
619 led_classdev_unregister(*(struct led_classdev **)res);
620 }
621
622 /**
623 * devm_led_classdev_register_ext - resource managed led_classdev_register_ext()
624 *
625 * @parent: parent of LED device
626 * @led_cdev: the led_classdev structure for this device.
627 * @init_data: LED class device initialization data
628 */
devm_led_classdev_register_ext(struct device * parent,struct led_classdev * led_cdev,struct led_init_data * init_data)629 int devm_led_classdev_register_ext(struct device *parent,
630 struct led_classdev *led_cdev,
631 struct led_init_data *init_data)
632 {
633 struct led_classdev **dr;
634 int rc;
635
636 dr = devres_alloc(devm_led_classdev_release, sizeof(*dr), GFP_KERNEL);
637 if (!dr)
638 return -ENOMEM;
639
640 rc = led_classdev_register_ext(parent, led_cdev, init_data);
641 if (rc) {
642 devres_free(dr);
643 return rc;
644 }
645
646 *dr = led_cdev;
647 devres_add(parent, dr);
648
649 return 0;
650 }
651 EXPORT_SYMBOL_GPL(devm_led_classdev_register_ext);
652
devm_led_classdev_match(struct device * dev,void * res,void * data)653 static int devm_led_classdev_match(struct device *dev, void *res, void *data)
654 {
655 struct led_classdev **p = res;
656
657 if (WARN_ON(!p || !*p))
658 return 0;
659
660 return *p == data;
661 }
662
663 /**
664 * devm_led_classdev_unregister() - resource managed led_classdev_unregister()
665 * @dev: The device to unregister.
666 * @led_cdev: the led_classdev structure for this device.
667 */
devm_led_classdev_unregister(struct device * dev,struct led_classdev * led_cdev)668 void devm_led_classdev_unregister(struct device *dev,
669 struct led_classdev *led_cdev)
670 {
671 WARN_ON(devres_release(dev,
672 devm_led_classdev_release,
673 devm_led_classdev_match, led_cdev));
674 }
675 EXPORT_SYMBOL_GPL(devm_led_classdev_unregister);
676
leds_init(void)677 static int __init leds_init(void)
678 {
679 return class_register(&leds_class);
680 }
681
leds_exit(void)682 static void __exit leds_exit(void)
683 {
684 class_unregister(&leds_class);
685 }
686
687 subsys_initcall(leds_init);
688 module_exit(leds_exit);
689
690 MODULE_AUTHOR("John Lenz, Richard Purdie");
691 MODULE_LICENSE("GPL");
692 MODULE_DESCRIPTION("LED Class Interface");
693