• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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