led-class.c 17 KB

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