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