root/drivers/leds/led-class.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. brightness_show
  2. brightness_store
  3. max_brightness_show
  4. brightness_hw_changed_show
  5. led_add_brightness_hw_changed
  6. led_remove_brightness_hw_changed
  7. led_classdev_notify_brightness_hw_changed
  8. led_add_brightness_hw_changed
  9. led_remove_brightness_hw_changed
  10. led_classdev_suspend
  11. led_classdev_resume
  12. led_suspend
  13. led_resume
  14. led_classdev_next_name
  15. led_classdev_register_ext
  16. led_classdev_unregister
  17. devm_led_classdev_release
  18. devm_led_classdev_register_ext
  19. devm_led_classdev_match
  20. devm_led_classdev_unregister
  21. leds_init
  22. leds_exit

   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 "leds.h"
  23 
  24 static struct class *leds_class;
  25 
  26 static ssize_t brightness_show(struct device *dev,
  27                 struct device_attribute *attr, char *buf)
  28 {
  29         struct led_classdev *led_cdev = dev_get_drvdata(dev);
  30 
  31         /* no lock needed for this */
  32         led_update_brightness(led_cdev);
  33 
  34         return sprintf(buf, "%u\n", led_cdev->brightness);
  35 }
  36 
  37 static ssize_t brightness_store(struct device *dev,
  38                 struct device_attribute *attr, const char *buf, size_t size)
  39 {
  40         struct led_classdev *led_cdev = dev_get_drvdata(dev);
  41         unsigned long state;
  42         ssize_t ret;
  43 
  44         mutex_lock(&led_cdev->led_access);
  45 
  46         if (led_sysfs_is_disabled(led_cdev)) {
  47                 ret = -EBUSY;
  48                 goto unlock;
  49         }
  50 
  51         ret = kstrtoul(buf, 10, &state);
  52         if (ret)
  53                 goto unlock;
  54 
  55         if (state == LED_OFF)
  56                 led_trigger_remove(led_cdev);
  57         led_set_brightness(led_cdev, state);
  58         flush_work(&led_cdev->set_brightness_work);
  59 
  60         ret = size;
  61 unlock:
  62         mutex_unlock(&led_cdev->led_access);
  63         return ret;
  64 }
  65 static DEVICE_ATTR_RW(brightness);
  66 
  67 static ssize_t max_brightness_show(struct device *dev,
  68                 struct device_attribute *attr, char *buf)
  69 {
  70         struct led_classdev *led_cdev = dev_get_drvdata(dev);
  71 
  72         return sprintf(buf, "%u\n", led_cdev->max_brightness);
  73 }
  74 static DEVICE_ATTR_RO(max_brightness);
  75 
  76 #ifdef CONFIG_LEDS_TRIGGERS
  77 static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
  78 static struct attribute *led_trigger_attrs[] = {
  79         &dev_attr_trigger.attr,
  80         NULL,
  81 };
  82 static const struct attribute_group led_trigger_group = {
  83         .attrs = led_trigger_attrs,
  84 };
  85 #endif
  86 
  87 static struct attribute *led_class_attrs[] = {
  88         &dev_attr_brightness.attr,
  89         &dev_attr_max_brightness.attr,
  90         NULL,
  91 };
  92 
  93 static const struct attribute_group led_group = {
  94         .attrs = led_class_attrs,
  95 };
  96 
  97 static const struct attribute_group *led_groups[] = {
  98         &led_group,
  99 #ifdef CONFIG_LEDS_TRIGGERS
 100         &led_trigger_group,
 101 #endif
 102         NULL,
 103 };
 104 
 105 #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
 106 static ssize_t brightness_hw_changed_show(struct device *dev,
 107                 struct device_attribute *attr, char *buf)
 108 {
 109         struct led_classdev *led_cdev = dev_get_drvdata(dev);
 110 
 111         if (led_cdev->brightness_hw_changed == -1)
 112                 return -ENODATA;
 113 
 114         return sprintf(buf, "%u\n", led_cdev->brightness_hw_changed);
 115 }
 116 
 117 static DEVICE_ATTR_RO(brightness_hw_changed);
 118 
 119 static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
 120 {
 121         struct device *dev = led_cdev->dev;
 122         int ret;
 123 
 124         ret = device_create_file(dev, &dev_attr_brightness_hw_changed);
 125         if (ret) {
 126                 dev_err(dev, "Error creating brightness_hw_changed\n");
 127                 return ret;
 128         }
 129 
 130         led_cdev->brightness_hw_changed_kn =
 131                 sysfs_get_dirent(dev->kobj.sd, "brightness_hw_changed");
 132         if (!led_cdev->brightness_hw_changed_kn) {
 133                 dev_err(dev, "Error getting brightness_hw_changed kn\n");
 134                 device_remove_file(dev, &dev_attr_brightness_hw_changed);
 135                 return -ENXIO;
 136         }
 137 
 138         return 0;
 139 }
 140 
 141 static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
 142 {
 143         sysfs_put(led_cdev->brightness_hw_changed_kn);
 144         device_remove_file(led_cdev->dev, &dev_attr_brightness_hw_changed);
 145 }
 146 
 147 void led_classdev_notify_brightness_hw_changed(struct led_classdev *led_cdev,
 148                                                enum led_brightness brightness)
 149 {
 150         if (WARN_ON(!led_cdev->brightness_hw_changed_kn))
 151                 return;
 152 
 153         led_cdev->brightness_hw_changed = brightness;
 154         sysfs_notify_dirent(led_cdev->brightness_hw_changed_kn);
 155 }
 156 EXPORT_SYMBOL_GPL(led_classdev_notify_brightness_hw_changed);
 157 #else
 158 static int led_add_brightness_hw_changed(struct led_classdev *led_cdev)
 159 {
 160         return 0;
 161 }
 162 static void led_remove_brightness_hw_changed(struct led_classdev *led_cdev)
 163 {
 164 }
 165 #endif
 166 
 167 /**
 168  * led_classdev_suspend - suspend an led_classdev.
 169  * @led_cdev: the led_classdev to suspend.
 170  */
 171 void led_classdev_suspend(struct led_classdev *led_cdev)
 172 {
 173         led_cdev->flags |= LED_SUSPENDED;
 174         led_set_brightness_nopm(led_cdev, 0);
 175 }
 176 EXPORT_SYMBOL_GPL(led_classdev_suspend);
 177 
 178 /**
 179  * led_classdev_resume - resume an led_classdev.
 180  * @led_cdev: the led_classdev to resume.
 181  */
 182 void led_classdev_resume(struct led_classdev *led_cdev)
 183 {
 184         led_set_brightness_nopm(led_cdev, led_cdev->brightness);
 185 
 186         if (led_cdev->flash_resume)
 187                 led_cdev->flash_resume(led_cdev);
 188 
 189         led_cdev->flags &= ~LED_SUSPENDED;
 190 }
 191 EXPORT_SYMBOL_GPL(led_classdev_resume);
 192 
 193 #ifdef CONFIG_PM_SLEEP
 194 static int led_suspend(struct device *dev)
 195 {
 196         struct led_classdev *led_cdev = dev_get_drvdata(dev);
 197 
 198         if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
 199                 led_classdev_suspend(led_cdev);
 200 
 201         return 0;
 202 }
 203 
 204 static int led_resume(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_resume(led_cdev);
 210 
 211         return 0;
 212 }
 213 #endif
 214 
 215 static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);
 216 
 217 static int led_classdev_next_name(const char *init_name, char *name,
 218                                   size_t len)
 219 {
 220         unsigned int i = 0;
 221         int ret = 0;
 222         struct device *dev;
 223 
 224         strlcpy(name, init_name, len);
 225 
 226         while ((ret < len) &&
 227                (dev = class_find_device_by_name(leds_class, name))) {
 228                 put_device(dev);
 229                 ret = snprintf(name, len, "%s_%u", init_name, ++i);
 230         }
 231 
 232         if (ret >= len)
 233                 return -ENOMEM;
 234 
 235         return i;
 236 }
 237 
 238 /**
 239  * led_classdev_register_ext - register a new object of led_classdev class
 240  *                             with init data.
 241  *
 242  * @parent: parent of LED device
 243  * @led_cdev: the led_classdev structure for this device.
 244  * @init_data: LED class device initialization data
 245  */
 246 int led_classdev_register_ext(struct device *parent,
 247                               struct led_classdev *led_cdev,
 248                               struct led_init_data *init_data)
 249 {
 250         char composed_name[LED_MAX_NAME_SIZE];
 251         char final_name[LED_MAX_NAME_SIZE];
 252         const char *proposed_name = composed_name;
 253         int ret;
 254 
 255         if (init_data) {
 256                 if (init_data->devname_mandatory && !init_data->devicename) {
 257                         dev_err(parent, "Mandatory device name is missing");
 258                         return -EINVAL;
 259                 }
 260                 ret = led_compose_name(parent, init_data, composed_name);
 261                 if (ret < 0)
 262                         return ret;
 263         } else {
 264                 proposed_name = led_cdev->name;
 265         }
 266 
 267         ret = led_classdev_next_name(proposed_name, final_name, sizeof(final_name));
 268         if (ret < 0)
 269                 return ret;
 270 
 271         mutex_init(&led_cdev->led_access);
 272         mutex_lock(&led_cdev->led_access);
 273         led_cdev->dev = device_create_with_groups(leds_class, parent, 0,
 274                                 led_cdev, led_cdev->groups, "%s", final_name);
 275         if (IS_ERR(led_cdev->dev)) {
 276                 mutex_unlock(&led_cdev->led_access);
 277                 return PTR_ERR(led_cdev->dev);
 278         }
 279         if (init_data && init_data->fwnode)
 280                 led_cdev->dev->fwnode = init_data->fwnode;
 281 
 282         if (ret)
 283                 dev_warn(parent, "Led %s renamed to %s due to name collision",
 284                                 proposed_name, dev_name(led_cdev->dev));
 285 
 286         if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
 287                 ret = led_add_brightness_hw_changed(led_cdev);
 288                 if (ret) {
 289                         device_unregister(led_cdev->dev);
 290                         led_cdev->dev = NULL;
 291                         mutex_unlock(&led_cdev->led_access);
 292                         return ret;
 293                 }
 294         }
 295 
 296         led_cdev->work_flags = 0;
 297 #ifdef CONFIG_LEDS_TRIGGERS
 298         init_rwsem(&led_cdev->trigger_lock);
 299 #endif
 300 #ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
 301         led_cdev->brightness_hw_changed = -1;
 302 #endif
 303         /* add to the list of leds */
 304         down_write(&leds_list_lock);
 305         list_add_tail(&led_cdev->node, &leds_list);
 306         up_write(&leds_list_lock);
 307 
 308         if (!led_cdev->max_brightness)
 309                 led_cdev->max_brightness = LED_FULL;
 310 
 311         led_update_brightness(led_cdev);
 312 
 313         led_init_core(led_cdev);
 314 
 315 #ifdef CONFIG_LEDS_TRIGGERS
 316         led_trigger_set_default(led_cdev);
 317 #endif
 318 
 319         mutex_unlock(&led_cdev->led_access);
 320 
 321         dev_dbg(parent, "Registered led device: %s\n",
 322                         led_cdev->name);
 323 
 324         return 0;
 325 }
 326 EXPORT_SYMBOL_GPL(led_classdev_register_ext);
 327 
 328 /**
 329  * led_classdev_unregister - unregisters a object of led_properties class.
 330  * @led_cdev: the led device to unregister
 331  *
 332  * Unregisters a previously registered via led_classdev_register object.
 333  */
 334 void led_classdev_unregister(struct led_classdev *led_cdev)
 335 {
 336         if (IS_ERR_OR_NULL(led_cdev->dev))
 337                 return;
 338 
 339 #ifdef CONFIG_LEDS_TRIGGERS
 340         down_write(&led_cdev->trigger_lock);
 341         if (led_cdev->trigger)
 342                 led_trigger_set(led_cdev, NULL);
 343         up_write(&led_cdev->trigger_lock);
 344 #endif
 345 
 346         led_cdev->flags |= LED_UNREGISTERING;
 347 
 348         /* Stop blinking */
 349         led_stop_software_blink(led_cdev);
 350 
 351         led_set_brightness(led_cdev, LED_OFF);
 352 
 353         flush_work(&led_cdev->set_brightness_work);
 354 
 355         if (led_cdev->flags & LED_BRIGHT_HW_CHANGED)
 356                 led_remove_brightness_hw_changed(led_cdev);
 357 
 358         device_unregister(led_cdev->dev);
 359 
 360         down_write(&leds_list_lock);
 361         list_del(&led_cdev->node);
 362         up_write(&leds_list_lock);
 363 
 364         mutex_destroy(&led_cdev->led_access);
 365 }
 366 EXPORT_SYMBOL_GPL(led_classdev_unregister);
 367 
 368 static void devm_led_classdev_release(struct device *dev, void *res)
 369 {
 370         led_classdev_unregister(*(struct led_classdev **)res);
 371 }
 372 
 373 /**
 374  * devm_led_classdev_register_ext - resource managed led_classdev_register_ext()
 375  *
 376  * @parent: parent of LED device
 377  * @led_cdev: the led_classdev structure for this device.
 378  * @init_data: LED class device initialization data
 379  */
 380 int devm_led_classdev_register_ext(struct device *parent,
 381                                    struct led_classdev *led_cdev,
 382                                    struct led_init_data *init_data)
 383 {
 384         struct led_classdev **dr;
 385         int rc;
 386 
 387         dr = devres_alloc(devm_led_classdev_release, sizeof(*dr), GFP_KERNEL);
 388         if (!dr)
 389                 return -ENOMEM;
 390 
 391         rc = led_classdev_register_ext(parent, led_cdev, init_data);
 392         if (rc) {
 393                 devres_free(dr);
 394                 return rc;
 395         }
 396 
 397         *dr = led_cdev;
 398         devres_add(parent, dr);
 399 
 400         return 0;
 401 }
 402 EXPORT_SYMBOL_GPL(devm_led_classdev_register_ext);
 403 
 404 static int devm_led_classdev_match(struct device *dev, void *res, void *data)
 405 {
 406         struct led_cdev **p = res;
 407 
 408         if (WARN_ON(!p || !*p))
 409                 return 0;
 410 
 411         return *p == data;
 412 }
 413 
 414 /**
 415  * devm_led_classdev_unregister() - resource managed led_classdev_unregister()
 416  * @parent: The device to unregister.
 417  * @led_cdev: the led_classdev structure for this device.
 418  */
 419 void devm_led_classdev_unregister(struct device *dev,
 420                                   struct led_classdev *led_cdev)
 421 {
 422         WARN_ON(devres_release(dev,
 423                                devm_led_classdev_release,
 424                                devm_led_classdev_match, led_cdev));
 425 }
 426 EXPORT_SYMBOL_GPL(devm_led_classdev_unregister);
 427 
 428 static int __init leds_init(void)
 429 {
 430         leds_class = class_create(THIS_MODULE, "leds");
 431         if (IS_ERR(leds_class))
 432                 return PTR_ERR(leds_class);
 433         leds_class->pm = &leds_class_dev_pm_ops;
 434         leds_class->dev_groups = led_groups;
 435         return 0;
 436 }
 437 
 438 static void __exit leds_exit(void)
 439 {
 440         class_destroy(leds_class);
 441 }
 442 
 443 subsys_initcall(leds_init);
 444 module_exit(leds_exit);
 445 
 446 MODULE_AUTHOR("John Lenz, Richard Purdie");
 447 MODULE_LICENSE("GPL");
 448 MODULE_DESCRIPTION("LED Class Interface");

/* [<][>][^][v][top][bottom][index][help] */