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 struct class *leds_class;
26
brightness_show(struct device * dev,struct device_attribute * attr,char * buf)27 static ssize_t brightness_show(struct device *dev,
28 struct device_attribute *attr, char *buf)
29 {
30 struct led_classdev *led_cdev = dev_get_drvdata(dev);
31 unsigned int brightness;
32
33 mutex_lock(&led_cdev->led_access);
34 led_update_brightness(led_cdev);
35 brightness = led_cdev->brightness;
36 mutex_unlock(&led_cdev->led_access);
37
38 return sprintf(buf, "%u\n", brightness);
39 }
40
brightness_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)41 static ssize_t brightness_store(struct device *dev,
42 struct device_attribute *attr, const char *buf, size_t size)
43 {
44 struct led_classdev *led_cdev = dev_get_drvdata(dev);
45 unsigned long state;
46 ssize_t ret;
47
48 mutex_lock(&led_cdev->led_access);
49
50 if (led_sysfs_is_disabled(led_cdev)) {
51 ret = -EBUSY;
52 goto unlock;
53 }
54
55 ret = kstrtoul(buf, 10, &state);
56 if (ret)
57 goto unlock;
58
59 if (state == LED_OFF)
60 led_trigger_remove(led_cdev);
61 led_set_brightness(led_cdev, state);
62 flush_work(&led_cdev->set_brightness_work);
63
64 ret = size;
65 unlock:
66 mutex_unlock(&led_cdev->led_access);
67 return ret;
68 }
69 static DEVICE_ATTR_RW(brightness);
70
max_brightness_show(struct device * dev,struct device_attribute * attr,char * buf)71 static ssize_t max_brightness_show(struct device *dev,
72 struct device_attribute *attr, char *buf)
73 {
74 struct led_classdev *led_cdev = dev_get_drvdata(dev);
75 unsigned int max_brightness;
76
77 mutex_lock(&led_cdev->led_access);
78 max_brightness = led_cdev->max_brightness;
79 mutex_unlock(&led_cdev->led_access);
80
81 return sprintf(buf, "%u\n", max_brightness);
82 }
83 static DEVICE_ATTR_RO(max_brightness);
84
85 #ifdef CONFIG_LEDS_TRIGGERS
86 static BIN_ATTR(trigger, 0644, led_trigger_read, led_trigger_write, 0);
87 static struct bin_attribute *led_trigger_bin_attrs[] = {
88 &bin_attr_trigger,
89 NULL,
90 };
91 static const struct attribute_group led_trigger_group = {
92 .bin_attrs = led_trigger_bin_attrs,
93 };
94 #endif
95
96 static struct attribute *led_class_attrs[] = {
97 &dev_attr_brightness.attr,
98 &dev_attr_max_brightness.attr,
99 NULL,
100 };
101
102 static const struct attribute_group led_group = {
103 .attrs = led_class_attrs,
104 };
105
106 static const struct attribute_group *led_groups[] = {
107 &led_group,
108 #ifdef CONFIG_LEDS_TRIGGERS
109 &led_trigger_group,
110 #endif
111 NULL,
112 };
113
114 #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
brightness_hw_changed_show(struct device * dev,struct device_attribute * attr,char * buf)115 static ssize_t brightness_hw_changed_show(struct device *dev,
116 struct device_attribute *attr, char *buf)
117 {
118 struct led_classdev *led_cdev = dev_get_drvdata(dev);
119
120 if (led_cdev->brightness_hw_changed == -1)
121 return -ENODATA;
122
123 return sprintf(buf, "%u\n", led_cdev->brightness_hw_changed);
124 }
125
126 static DEVICE_ATTR_RO(brightness_hw_changed);
127
led_add_brightness_hw_changed(struct led_classdev * led_cdev)128 static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
129 {
130 struct device *dev = led_cdev->dev;
131 int ret;
132
133 ret = device_create_file(dev, &dev_attr_brightness_hw_changed);
134 if (ret) {
135 dev_err(dev, "Error creating brightness_hw_changed\n");
136 return ret;
137 }
138
139 led_cdev->brightness_hw_changed_kn =
140 sysfs_get_dirent(dev->kobj.sd, "brightness_hw_changed");
141 if (!led_cdev->brightness_hw_changed_kn) {
142 dev_err(dev, "Error getting brightness_hw_changed kn\n");
143 device_remove_file(dev, &dev_attr_brightness_hw_changed);
144 return -ENXIO;
145 }
146
147 return 0;
148 }
149
led_remove_brightness_hw_changed(struct led_classdev * led_cdev)150 static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
151 {
152 sysfs_put(led_cdev->brightness_hw_changed_kn);
153 device_remove_file(led_cdev->dev, &dev_attr_brightness_hw_changed);
154 }
155
led_classdev_notify_brightness_hw_changed(struct led_classdev * led_cdev,enum led_brightness brightness)156 void led_classdev_notify_brightness_hw_changed(struct led_classdev *led_cdev,
157 enum led_brightness brightness)
158 {
159 if (WARN_ON(!led_cdev->brightness_hw_changed_kn))
160 return;
161
162 led_cdev->brightness_hw_changed = brightness;
163 sysfs_notify_dirent(led_cdev->brightness_hw_changed_kn);
164 }
165 EXPORT_SYMBOL_GPL(led_classdev_notify_brightness_hw_changed);
166 #else
led_add_brightness_hw_changed(struct led_classdev * led_cdev)167 static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
168 {
169 return 0;
170 }
led_remove_brightness_hw_changed(struct led_classdev * led_cdev)171 static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
172 {
173 }
174 #endif
175
176 /**
177 * led_classdev_suspend - suspend an led_classdev.
178 * @led_cdev: the led_classdev to suspend.
179 */
led_classdev_suspend(struct led_classdev * led_cdev)180 void led_classdev_suspend(struct led_classdev *led_cdev)
181 {
182 led_cdev->flags |= LED_SUSPENDED;
183 led_set_brightness_nopm(led_cdev, 0);
184 flush_work(&led_cdev->set_brightness_work);
185 }
186 EXPORT_SYMBOL_GPL(led_classdev_suspend);
187
188 /**
189 * led_classdev_resume - resume an led_classdev.
190 * @led_cdev: the led_classdev to resume.
191 */
led_classdev_resume(struct led_classdev * led_cdev)192 void led_classdev_resume(struct led_classdev *led_cdev)
193 {
194 led_set_brightness_nopm(led_cdev, led_cdev->brightness);
195
196 if (led_cdev->flash_resume)
197 led_cdev->flash_resume(led_cdev);
198
199 led_cdev->flags &= ~LED_SUSPENDED;
200 }
201 EXPORT_SYMBOL_GPL(led_classdev_resume);
202
203 #ifdef CONFIG_PM_SLEEP
led_suspend(struct device * dev)204 static int led_suspend(struct device *dev)
205 {
206 struct led_classdev *led_cdev = dev_get_drvdata(dev);
207
208 if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
209 led_classdev_suspend(led_cdev);
210
211 return 0;
212 }
213
led_resume(struct device * dev)214 static int led_resume(struct device *dev)
215 {
216 struct led_classdev *led_cdev = dev_get_drvdata(dev);
217
218 if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
219 led_classdev_resume(led_cdev);
220
221 return 0;
222 }
223 #endif
224
225 static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);
226
227 /**
228 * of_led_get() - request a LED device via the LED framework
229 * @np: device node to get the LED device from
230 * @index: the index of the LED
231 *
232 * Returns the LED device parsed from the phandle specified in the "leds"
233 * property of a device tree node or a negative error-code on failure.
234 */
of_led_get(struct device_node * np,int index)235 struct led_classdev *of_led_get(struct device_node *np, int index)
236 {
237 struct device *led_dev;
238 struct led_classdev *led_cdev;
239 struct device_node *led_node;
240
241 led_node = of_parse_phandle(np, "leds", index);
242 if (!led_node)
243 return ERR_PTR(-ENOENT);
244
245 led_dev = class_find_device_by_of_node(leds_class, led_node);
246 of_node_put(led_node);
247 put_device(led_dev);
248
249 if (!led_dev)
250 return ERR_PTR(-EPROBE_DEFER);
251
252 led_cdev = dev_get_drvdata(led_dev);
253
254 if (!try_module_get(led_cdev->dev->parent->driver->owner)) {
255 put_device(led_cdev->dev);
256 return ERR_PTR(-ENODEV);
257 }
258
259 return led_cdev;
260 }
261 EXPORT_SYMBOL_GPL(of_led_get);
262
263 /**
264 * led_put() - release a LED device
265 * @led_cdev: LED device
266 */
led_put(struct led_classdev * led_cdev)267 void led_put(struct led_classdev *led_cdev)
268 {
269 module_put(led_cdev->dev->parent->driver->owner);
270 put_device(led_cdev->dev);
271 }
272 EXPORT_SYMBOL_GPL(led_put);
273
devm_led_release(struct device * dev,void * res)274 static void devm_led_release(struct device *dev, void *res)
275 {
276 struct led_classdev **p = res;
277
278 led_put(*p);
279 }
280
281 /**
282 * devm_of_led_get - Resource-managed request of a LED device
283 * @dev: LED consumer
284 * @index: index of the LED to obtain in the consumer
285 *
286 * The device node of the device is parse to find the request LED device.
287 * The LED device returned from this function is automatically released
288 * on driver detach.
289 *
290 * @return a pointer to a LED device or ERR_PTR(errno) on failure.
291 */
devm_of_led_get(struct device * dev,int index)292 struct led_classdev *__must_check devm_of_led_get(struct device *dev,
293 int index)
294 {
295 struct led_classdev *led;
296 struct led_classdev **dr;
297
298 if (!dev)
299 return ERR_PTR(-EINVAL);
300
301 led = of_led_get(dev->of_node, index);
302 if (IS_ERR(led))
303 return led;
304
305 dr = devres_alloc(devm_led_release, sizeof(struct led_classdev *),
306 GFP_KERNEL);
307 if (!dr) {
308 led_put(led);
309 return ERR_PTR(-ENOMEM);
310 }
311
312 *dr = led;
313 devres_add(dev, dr);
314
315 return led;
316 }
317 EXPORT_SYMBOL_GPL(devm_of_led_get);
318
led_classdev_next_name(const char * init_name,char * name,size_t len)319 static int led_classdev_next_name(const char *init_name, char *name,
320 size_t len)
321 {
322 unsigned int i = 0;
323 int ret = 0;
324 struct device *dev;
325
326 strlcpy(name, init_name, len);
327
328 while ((ret < len) &&
329 (dev = class_find_device_by_name(leds_class, name))) {
330 put_device(dev);
331 ret = snprintf(name, len, "%s_%u", init_name, ++i);
332 }
333
334 if (ret >= len)
335 return -ENOMEM;
336
337 return i;
338 }
339
340 /**
341 * led_classdev_register_ext - register a new object of led_classdev class
342 * with init data.
343 *
344 * @parent: parent of LED device
345 * @led_cdev: the led_classdev structure for this device.
346 * @init_data: LED class device initialization data
347 */
led_classdev_register_ext(struct device * parent,struct led_classdev * led_cdev,struct led_init_data * init_data)348 int led_classdev_register_ext(struct device *parent,
349 struct led_classdev *led_cdev,
350 struct led_init_data *init_data)
351 {
352 char composed_name[LED_MAX_NAME_SIZE];
353 char final_name[LED_MAX_NAME_SIZE];
354 const char *proposed_name = composed_name;
355 int ret;
356
357 if (init_data) {
358 if (init_data->devname_mandatory && !init_data->devicename) {
359 dev_err(parent, "Mandatory device name is missing");
360 return -EINVAL;
361 }
362 ret = led_compose_name(parent, init_data, composed_name);
363 if (ret < 0)
364 return ret;
365
366 if (init_data->fwnode)
367 fwnode_property_read_string(init_data->fwnode,
368 "linux,default-trigger",
369 &led_cdev->default_trigger);
370 } else {
371 proposed_name = led_cdev->name;
372 }
373
374 ret = led_classdev_next_name(proposed_name, final_name, sizeof(final_name));
375 if (ret < 0)
376 return ret;
377
378 mutex_init(&led_cdev->led_access);
379 mutex_lock(&led_cdev->led_access);
380 led_cdev->dev = device_create_with_groups(leds_class, parent, 0,
381 led_cdev, led_cdev->groups, "%s", final_name);
382 if (IS_ERR(led_cdev->dev)) {
383 mutex_unlock(&led_cdev->led_access);
384 return PTR_ERR(led_cdev->dev);
385 }
386 if (init_data && init_data->fwnode) {
387 led_cdev->dev->fwnode = init_data->fwnode;
388 led_cdev->dev->of_node = to_of_node(init_data->fwnode);
389 }
390
391 if (ret)
392 dev_warn(parent, "Led %s renamed to %s due to name collision",
393 proposed_name, dev_name(led_cdev->dev));
394
395 if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
396 ret = led_add_brightness_hw_changed(led_cdev);
397 if (ret) {
398 device_unregister(led_cdev->dev);
399 led_cdev->dev = NULL;
400 mutex_unlock(&led_cdev->led_access);
401 return ret;
402 }
403 }
404
405 led_cdev->work_flags = 0;
406 #ifdef CONFIG_LEDS_TRIGGERS
407 init_rwsem(&led_cdev->trigger_lock);
408 #endif
409 #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
410 led_cdev->brightness_hw_changed = -1;
411 #endif
412 /* add to the list of leds */
413 down_write(&leds_list_lock);
414 list_add_tail(&led_cdev->node, &leds_list);
415 up_write(&leds_list_lock);
416
417 if (!led_cdev->max_brightness)
418 led_cdev->max_brightness = LED_FULL;
419
420 led_update_brightness(led_cdev);
421
422 led_init_core(led_cdev);
423
424 #ifdef CONFIG_LEDS_TRIGGERS
425 led_trigger_set_default(led_cdev);
426 #endif
427
428 mutex_unlock(&led_cdev->led_access);
429
430 dev_dbg(parent, "Registered led device: %s\n",
431 led_cdev->name);
432
433 return 0;
434 }
435 EXPORT_SYMBOL_GPL(led_classdev_register_ext);
436
437 /**
438 * led_classdev_unregister - unregisters a object of led_properties class.
439 * @led_cdev: the led device to unregister
440 *
441 * Unregisters a previously registered via led_classdev_register object.
442 */
led_classdev_unregister(struct led_classdev * led_cdev)443 void led_classdev_unregister(struct led_classdev *led_cdev)
444 {
445 if (IS_ERR_OR_NULL(led_cdev->dev))
446 return;
447
448 #ifdef CONFIG_LEDS_TRIGGERS
449 down_write(&led_cdev->trigger_lock);
450 if (led_cdev->trigger)
451 led_trigger_set(led_cdev, NULL);
452 up_write(&led_cdev->trigger_lock);
453 #endif
454
455 led_cdev->flags |= LED_UNREGISTERING;
456
457 /* Stop blinking */
458 led_stop_software_blink(led_cdev);
459
460 led_set_brightness(led_cdev, LED_OFF);
461
462 flush_work(&led_cdev->set_brightness_work);
463
464 if (led_cdev->flags & LED_BRIGHT_HW_CHANGED)
465 led_remove_brightness_hw_changed(led_cdev);
466
467 device_unregister(led_cdev->dev);
468
469 down_write(&leds_list_lock);
470 list_del(&led_cdev->node);
471 up_write(&leds_list_lock);
472
473 mutex_destroy(&led_cdev->led_access);
474 }
475 EXPORT_SYMBOL_GPL(led_classdev_unregister);
476
devm_led_classdev_release(struct device * dev,void * res)477 static void devm_led_classdev_release(struct device *dev, void *res)
478 {
479 led_classdev_unregister(*(struct led_classdev **)res);
480 }
481
482 /**
483 * devm_led_classdev_register_ext - resource managed led_classdev_register_ext()
484 *
485 * @parent: parent of LED device
486 * @led_cdev: the led_classdev structure for this device.
487 * @init_data: LED class device initialization data
488 */
devm_led_classdev_register_ext(struct device * parent,struct led_classdev * led_cdev,struct led_init_data * init_data)489 int devm_led_classdev_register_ext(struct device *parent,
490 struct led_classdev *led_cdev,
491 struct led_init_data *init_data)
492 {
493 struct led_classdev **dr;
494 int rc;
495
496 dr = devres_alloc(devm_led_classdev_release, sizeof(*dr), GFP_KERNEL);
497 if (!dr)
498 return -ENOMEM;
499
500 rc = led_classdev_register_ext(parent, led_cdev, init_data);
501 if (rc) {
502 devres_free(dr);
503 return rc;
504 }
505
506 *dr = led_cdev;
507 devres_add(parent, dr);
508
509 return 0;
510 }
511 EXPORT_SYMBOL_GPL(devm_led_classdev_register_ext);
512
devm_led_classdev_match(struct device * dev,void * res,void * data)513 static int devm_led_classdev_match(struct device *dev, void *res, void *data)
514 {
515 struct led_classdev **p = res;
516
517 if (WARN_ON(!p || !*p))
518 return 0;
519
520 return *p == data;
521 }
522
523 /**
524 * devm_led_classdev_unregister() - resource managed led_classdev_unregister()
525 * @parent: The device to unregister.
526 * @led_cdev: the led_classdev structure for this device.
527 */
devm_led_classdev_unregister(struct device * dev,struct led_classdev * led_cdev)528 void devm_led_classdev_unregister(struct device *dev,
529 struct led_classdev *led_cdev)
530 {
531 WARN_ON(devres_release(dev,
532 devm_led_classdev_release,
533 devm_led_classdev_match, led_cdev));
534 }
535 EXPORT_SYMBOL_GPL(devm_led_classdev_unregister);
536
leds_init(void)537 static int __init leds_init(void)
538 {
539 leds_class = class_create(THIS_MODULE, "leds");
540 if (IS_ERR(leds_class))
541 return PTR_ERR(leds_class);
542 leds_class->pm = &leds_class_dev_pm_ops;
543 leds_class->dev_groups = led_groups;
544 return 0;
545 }
546
leds_exit(void)547 static void __exit leds_exit(void)
548 {
549 class_destroy(leds_class);
550 }
551
552 subsys_initcall(leds_init);
553 module_exit(leds_exit);
554
555 MODULE_AUTHOR("John Lenz, Richard Purdie");
556 MODULE_LICENSE("GPL");
557 MODULE_DESCRIPTION("LED Class Interface");
558