linux/drivers/leds/led-class.c
<<
>>
Prefs
   1/*
   2 * LED Class Core
   3 *
   4 * Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu>
   5 * Copyright (C) 2005-2007 Richard Purdie <rpurdie@openedhand.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11
  12#include <linux/ctype.h>
  13#include <linux/device.h>
  14#include <linux/err.h>
  15#include <linux/init.h>
  16#include <linux/kernel.h>
  17#include <linux/leds.h>
  18#include <linux/list.h>
  19#include <linux/module.h>
  20#include <linux/slab.h>
  21#include <linux/spinlock.h>
  22#include <linux/timer.h>
  23#include "leds.h"
  24
  25static struct class *leds_class;
  26
  27static 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
  32        /* no lock needed for this */
  33        led_update_brightness(led_cdev);
  34
  35        return sprintf(buf, "%u\n", led_cdev->brightness);
  36}
  37
  38static ssize_t brightness_store(struct device *dev,
  39                struct device_attribute *attr, const char *buf, size_t size)
  40{
  41        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  42        unsigned long state;
  43        ssize_t ret;
  44
  45        mutex_lock(&led_cdev->led_access);
  46
  47        if (led_sysfs_is_disabled(led_cdev)) {
  48                ret = -EBUSY;
  49                goto unlock;
  50        }
  51
  52        ret = kstrtoul(buf, 10, &state);
  53        if (ret)
  54                goto unlock;
  55
  56        if (state == LED_OFF)
  57                led_trigger_remove(led_cdev);
  58        led_set_brightness(led_cdev, state);
  59
  60        ret = size;
  61unlock:
  62        mutex_unlock(&led_cdev->led_access);
  63        return ret;
  64}
  65static DEVICE_ATTR_RW(brightness);
  66
  67static 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}
  74static DEVICE_ATTR_RO(max_brightness);
  75
  76#ifdef CONFIG_LEDS_TRIGGERS
  77static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
  78static struct attribute *led_trigger_attrs[] = {
  79        &dev_attr_trigger.attr,
  80        NULL,
  81};
  82static const struct attribute_group led_trigger_group = {
  83        .attrs = led_trigger_attrs,
  84};
  85#endif
  86
  87static struct attribute *led_class_attrs[] = {
  88        &dev_attr_brightness.attr,
  89        &dev_attr_max_brightness.attr,
  90        NULL,
  91};
  92
  93static const struct attribute_group led_group = {
  94        .attrs = led_class_attrs,
  95};
  96
  97static 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/**
 106 * led_classdev_suspend - suspend an led_classdev.
 107 * @led_cdev: the led_classdev to suspend.
 108 */
 109void led_classdev_suspend(struct led_classdev *led_cdev)
 110{
 111        led_cdev->flags |= LED_SUSPENDED;
 112        led_set_brightness_nopm(led_cdev, 0);
 113}
 114EXPORT_SYMBOL_GPL(led_classdev_suspend);
 115
 116/**
 117 * led_classdev_resume - resume an led_classdev.
 118 * @led_cdev: the led_classdev to resume.
 119 */
 120void led_classdev_resume(struct led_classdev *led_cdev)
 121{
 122        led_set_brightness_nopm(led_cdev, led_cdev->brightness);
 123
 124        if (led_cdev->flash_resume)
 125                led_cdev->flash_resume(led_cdev);
 126
 127        led_cdev->flags &= ~LED_SUSPENDED;
 128}
 129EXPORT_SYMBOL_GPL(led_classdev_resume);
 130
 131#ifdef CONFIG_PM_SLEEP
 132static int led_suspend(struct device *dev)
 133{
 134        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 135
 136        if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
 137                led_classdev_suspend(led_cdev);
 138
 139        return 0;
 140}
 141
 142static int led_resume(struct device *dev)
 143{
 144        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 145
 146        if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
 147                led_classdev_resume(led_cdev);
 148
 149        return 0;
 150}
 151#endif
 152
 153static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);
 154
 155static int match_name(struct device *dev, const void *data)
 156{
 157        if (!dev_name(dev))
 158                return 0;
 159        return !strcmp(dev_name(dev), (char *)data);
 160}
 161
 162static int led_classdev_next_name(const char *init_name, char *name,
 163                                  size_t len)
 164{
 165        unsigned int i = 0;
 166        int ret = 0;
 167        struct device *dev;
 168
 169        strlcpy(name, init_name, len);
 170
 171        while ((ret < len) &&
 172               (dev = class_find_device(leds_class, NULL, name, match_name))) {
 173                put_device(dev);
 174                ret = snprintf(name, len, "%s_%u", init_name, ++i);
 175        }
 176
 177        if (ret >= len)
 178                return -ENOMEM;
 179
 180        return i;
 181}
 182
 183/**
 184 * led_classdev_register - register a new object of led_classdev class.
 185 * @parent: The device to register.
 186 * @led_cdev: the led_classdev structure for this device.
 187 */
 188int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
 189{
 190        char name[64];
 191        int ret;
 192
 193        ret = led_classdev_next_name(led_cdev->name, name, sizeof(name));
 194        if (ret < 0)
 195                return ret;
 196
 197        led_cdev->dev = device_create_with_groups(leds_class, parent, 0,
 198                                led_cdev, led_cdev->groups, "%s", name);
 199        if (IS_ERR(led_cdev->dev))
 200                return PTR_ERR(led_cdev->dev);
 201
 202        if (ret)
 203                dev_warn(parent, "Led %s renamed to %s due to name collision",
 204                                led_cdev->name, dev_name(led_cdev->dev));
 205
 206#ifdef CONFIG_LEDS_TRIGGERS
 207        init_rwsem(&led_cdev->trigger_lock);
 208#endif
 209        mutex_init(&led_cdev->led_access);
 210        /* add to the list of leds */
 211        down_write(&leds_list_lock);
 212        list_add_tail(&led_cdev->node, &leds_list);
 213        up_write(&leds_list_lock);
 214
 215        if (!led_cdev->max_brightness)
 216                led_cdev->max_brightness = LED_FULL;
 217
 218        led_update_brightness(led_cdev);
 219
 220        led_init_core(led_cdev);
 221
 222#ifdef CONFIG_LEDS_TRIGGERS
 223        led_trigger_set_default(led_cdev);
 224#endif
 225
 226        dev_dbg(parent, "Registered led device: %s\n",
 227                        led_cdev->name);
 228
 229        return 0;
 230}
 231EXPORT_SYMBOL_GPL(led_classdev_register);
 232
 233/**
 234 * led_classdev_unregister - unregisters a object of led_properties class.
 235 * @led_cdev: the led device to unregister
 236 *
 237 * Unregisters a previously registered via led_classdev_register object.
 238 */
 239void led_classdev_unregister(struct led_classdev *led_cdev)
 240{
 241#ifdef CONFIG_LEDS_TRIGGERS
 242        down_write(&led_cdev->trigger_lock);
 243        if (led_cdev->trigger)
 244                led_trigger_set(led_cdev, NULL);
 245        up_write(&led_cdev->trigger_lock);
 246#endif
 247
 248        led_cdev->flags |= LED_UNREGISTERING;
 249
 250        /* Stop blinking */
 251        led_stop_software_blink(led_cdev);
 252
 253        led_set_brightness(led_cdev, LED_OFF);
 254
 255        flush_work(&led_cdev->set_brightness_work);
 256
 257        device_unregister(led_cdev->dev);
 258
 259        down_write(&leds_list_lock);
 260        list_del(&led_cdev->node);
 261        up_write(&leds_list_lock);
 262
 263        mutex_destroy(&led_cdev->led_access);
 264}
 265EXPORT_SYMBOL_GPL(led_classdev_unregister);
 266
 267static void devm_led_classdev_release(struct device *dev, void *res)
 268{
 269        led_classdev_unregister(*(struct led_classdev **)res);
 270}
 271
 272/**
 273 * devm_led_classdev_register - resource managed led_classdev_register()
 274 * @parent: The device to register.
 275 * @led_cdev: the led_classdev structure for this device.
 276 */
 277int devm_led_classdev_register(struct device *parent,
 278                               struct led_classdev *led_cdev)
 279{
 280        struct led_classdev **dr;
 281        int rc;
 282
 283        dr = devres_alloc(devm_led_classdev_release, sizeof(*dr), GFP_KERNEL);
 284        if (!dr)
 285                return -ENOMEM;
 286
 287        rc = led_classdev_register(parent, led_cdev);
 288        if (rc) {
 289                devres_free(dr);
 290                return rc;
 291        }
 292
 293        *dr = led_cdev;
 294        devres_add(parent, dr);
 295
 296        return 0;
 297}
 298EXPORT_SYMBOL_GPL(devm_led_classdev_register);
 299
 300static int devm_led_classdev_match(struct device *dev, void *res, void *data)
 301{
 302        struct led_cdev **p = res;
 303
 304        if (WARN_ON(!p || !*p))
 305                return 0;
 306
 307        return *p == data;
 308}
 309
 310/**
 311 * devm_led_classdev_unregister() - resource managed led_classdev_unregister()
 312 * @parent: The device to unregister.
 313 * @led_cdev: the led_classdev structure for this device.
 314 */
 315void devm_led_classdev_unregister(struct device *dev,
 316                                  struct led_classdev *led_cdev)
 317{
 318        WARN_ON(devres_release(dev,
 319                               devm_led_classdev_release,
 320                               devm_led_classdev_match, led_cdev));
 321}
 322EXPORT_SYMBOL_GPL(devm_led_classdev_unregister);
 323
 324static int __init leds_init(void)
 325{
 326        leds_class = class_create(THIS_MODULE, "leds");
 327        if (IS_ERR(leds_class))
 328                return PTR_ERR(leds_class);
 329        leds_class->pm = &leds_class_dev_pm_ops;
 330        leds_class->dev_groups = led_groups;
 331        return 0;
 332}
 333
 334static void __exit leds_exit(void)
 335{
 336        class_destroy(leds_class);
 337}
 338
 339subsys_initcall(leds_init);
 340module_exit(leds_exit);
 341
 342MODULE_AUTHOR("John Lenz, Richard Purdie");
 343MODULE_LICENSE("GPL");
 344MODULE_DESCRIPTION("LED Class Interface");
 345