linux/drivers/leds/led-class-flash.c
<<
>>
Prefs
   1/*
   2 * LED Flash class interface
   3 *
   4 * Copyright (C) 2015 Samsung Electronics Co., Ltd.
   5 * Author: Jacek Anaszewski <j.anaszewski@samsung.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/device.h>
  13#include <linux/init.h>
  14#include <linux/led-class-flash.h>
  15#include <linux/leds.h>
  16#include <linux/module.h>
  17#include <linux/slab.h>
  18#include "leds.h"
  19
  20#define has_flash_op(fled_cdev, op)                             \
  21        (fled_cdev && fled_cdev->ops->op)
  22
  23#define call_flash_op(fled_cdev, op, args...)           \
  24        ((has_flash_op(fled_cdev, op)) ?                        \
  25                        (fled_cdev->ops->op(fled_cdev, args)) : \
  26                        -EINVAL)
  27
  28static const char * const led_flash_fault_names[] = {
  29        "led-over-voltage",
  30        "flash-timeout-exceeded",
  31        "controller-over-temperature",
  32        "controller-short-circuit",
  33        "led-power-supply-over-current",
  34        "indicator-led-fault",
  35        "led-under-voltage",
  36        "controller-under-voltage",
  37        "led-over-temperature",
  38};
  39
  40static ssize_t flash_brightness_store(struct device *dev,
  41                struct device_attribute *attr, const char *buf, size_t size)
  42{
  43        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  44        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
  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        ret = led_set_flash_brightness(fled_cdev, state);
  60        if (ret < 0)
  61                goto unlock;
  62
  63        ret = size;
  64unlock:
  65        mutex_unlock(&led_cdev->led_access);
  66        return ret;
  67}
  68
  69static ssize_t flash_brightness_show(struct device *dev,
  70                struct device_attribute *attr, char *buf)
  71{
  72        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  73        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
  74
  75        /* no lock needed for this */
  76        led_update_flash_brightness(fled_cdev);
  77
  78        return sprintf(buf, "%u\n", fled_cdev->brightness.val);
  79}
  80static DEVICE_ATTR_RW(flash_brightness);
  81
  82static ssize_t max_flash_brightness_show(struct device *dev,
  83                struct device_attribute *attr, char *buf)
  84{
  85        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  86        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
  87
  88        return sprintf(buf, "%u\n", fled_cdev->brightness.max);
  89}
  90static DEVICE_ATTR_RO(max_flash_brightness);
  91
  92static ssize_t flash_strobe_store(struct device *dev,
  93                struct device_attribute *attr, const char *buf, size_t size)
  94{
  95        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  96        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
  97        unsigned long state;
  98        ssize_t ret = -EINVAL;
  99
 100        mutex_lock(&led_cdev->led_access);
 101
 102        if (led_sysfs_is_disabled(led_cdev)) {
 103                ret = -EBUSY;
 104                goto unlock;
 105        }
 106
 107        ret = kstrtoul(buf, 10, &state);
 108        if (ret)
 109                goto unlock;
 110
 111        if (state > 1) {
 112                ret = -EINVAL;
 113                goto unlock;
 114        }
 115
 116        ret = led_set_flash_strobe(fled_cdev, state);
 117        if (ret < 0)
 118                goto unlock;
 119        ret = size;
 120unlock:
 121        mutex_unlock(&led_cdev->led_access);
 122        return ret;
 123}
 124
 125static ssize_t flash_strobe_show(struct device *dev,
 126                struct device_attribute *attr, char *buf)
 127{
 128        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 129        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 130        bool state;
 131        int ret;
 132
 133        /* no lock needed for this */
 134        ret = led_get_flash_strobe(fled_cdev, &state);
 135        if (ret < 0)
 136                return ret;
 137
 138        return sprintf(buf, "%u\n", state);
 139}
 140static DEVICE_ATTR_RW(flash_strobe);
 141
 142static ssize_t flash_timeout_store(struct device *dev,
 143                struct device_attribute *attr, const char *buf, size_t size)
 144{
 145        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 146        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 147        unsigned long flash_timeout;
 148        ssize_t ret;
 149
 150        mutex_lock(&led_cdev->led_access);
 151
 152        if (led_sysfs_is_disabled(led_cdev)) {
 153                ret = -EBUSY;
 154                goto unlock;
 155        }
 156
 157        ret = kstrtoul(buf, 10, &flash_timeout);
 158        if (ret)
 159                goto unlock;
 160
 161        ret = led_set_flash_timeout(fled_cdev, flash_timeout);
 162        if (ret < 0)
 163                goto unlock;
 164
 165        ret = size;
 166unlock:
 167        mutex_unlock(&led_cdev->led_access);
 168        return ret;
 169}
 170
 171static ssize_t flash_timeout_show(struct device *dev,
 172                struct device_attribute *attr, char *buf)
 173{
 174        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 175        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 176
 177        return sprintf(buf, "%u\n", fled_cdev->timeout.val);
 178}
 179static DEVICE_ATTR_RW(flash_timeout);
 180
 181static ssize_t max_flash_timeout_show(struct device *dev,
 182                struct device_attribute *attr, char *buf)
 183{
 184        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 185        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 186
 187        return sprintf(buf, "%u\n", fled_cdev->timeout.max);
 188}
 189static DEVICE_ATTR_RO(max_flash_timeout);
 190
 191static ssize_t flash_fault_show(struct device *dev,
 192                struct device_attribute *attr, char *buf)
 193{
 194        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 195        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 196        u32 fault, mask = 0x1;
 197        char *pbuf = buf;
 198        int i, ret, buf_len;
 199
 200        ret = led_get_flash_fault(fled_cdev, &fault);
 201        if (ret < 0)
 202                return -EINVAL;
 203
 204        *buf = '\0';
 205
 206        for (i = 0; i < LED_NUM_FLASH_FAULTS; ++i) {
 207                if (fault & mask) {
 208                        buf_len = sprintf(pbuf, "%s ",
 209                                          led_flash_fault_names[i]);
 210                        pbuf += buf_len;
 211                }
 212                mask <<= 1;
 213        }
 214
 215        return sprintf(buf, "%s\n", buf);
 216}
 217static DEVICE_ATTR_RO(flash_fault);
 218
 219static struct attribute *led_flash_strobe_attrs[] = {
 220        &dev_attr_flash_strobe.attr,
 221        NULL,
 222};
 223
 224static struct attribute *led_flash_timeout_attrs[] = {
 225        &dev_attr_flash_timeout.attr,
 226        &dev_attr_max_flash_timeout.attr,
 227        NULL,
 228};
 229
 230static struct attribute *led_flash_brightness_attrs[] = {
 231        &dev_attr_flash_brightness.attr,
 232        &dev_attr_max_flash_brightness.attr,
 233        NULL,
 234};
 235
 236static struct attribute *led_flash_fault_attrs[] = {
 237        &dev_attr_flash_fault.attr,
 238        NULL,
 239};
 240
 241static const struct attribute_group led_flash_strobe_group = {
 242        .attrs = led_flash_strobe_attrs,
 243};
 244
 245static const struct attribute_group led_flash_timeout_group = {
 246        .attrs = led_flash_timeout_attrs,
 247};
 248
 249static const struct attribute_group led_flash_brightness_group = {
 250        .attrs = led_flash_brightness_attrs,
 251};
 252
 253static const struct attribute_group led_flash_fault_group = {
 254        .attrs = led_flash_fault_attrs,
 255};
 256
 257static void led_flash_resume(struct led_classdev *led_cdev)
 258{
 259        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 260
 261        call_flash_op(fled_cdev, flash_brightness_set,
 262                                        fled_cdev->brightness.val);
 263        call_flash_op(fled_cdev, timeout_set, fled_cdev->timeout.val);
 264}
 265
 266static void led_flash_init_sysfs_groups(struct led_classdev_flash *fled_cdev)
 267{
 268        struct led_classdev *led_cdev = &fled_cdev->led_cdev;
 269        const struct led_flash_ops *ops = fled_cdev->ops;
 270        const struct attribute_group **flash_groups = fled_cdev->sysfs_groups;
 271
 272        int num_sysfs_groups = 0;
 273
 274        flash_groups[num_sysfs_groups++] = &led_flash_strobe_group;
 275
 276        if (ops->flash_brightness_set)
 277                flash_groups[num_sysfs_groups++] = &led_flash_brightness_group;
 278
 279        if (ops->timeout_set)
 280                flash_groups[num_sysfs_groups++] = &led_flash_timeout_group;
 281
 282        if (ops->fault_get)
 283                flash_groups[num_sysfs_groups++] = &led_flash_fault_group;
 284
 285        led_cdev->groups = flash_groups;
 286}
 287
 288int led_classdev_flash_register(struct device *parent,
 289                                struct led_classdev_flash *fled_cdev)
 290{
 291        struct led_classdev *led_cdev;
 292        const struct led_flash_ops *ops;
 293        int ret;
 294
 295        if (!fled_cdev)
 296                return -EINVAL;
 297
 298        led_cdev = &fled_cdev->led_cdev;
 299
 300        if (led_cdev->flags & LED_DEV_CAP_FLASH) {
 301                if (!led_cdev->brightness_set_blocking)
 302                        return -EINVAL;
 303
 304                ops = fled_cdev->ops;
 305                if (!ops || !ops->strobe_set)
 306                        return -EINVAL;
 307
 308                led_cdev->flash_resume = led_flash_resume;
 309
 310                /* Select the sysfs attributes to be created for the device */
 311                led_flash_init_sysfs_groups(fled_cdev);
 312        }
 313
 314        /* Register led class device */
 315        ret = led_classdev_register(parent, led_cdev);
 316        if (ret < 0)
 317                return ret;
 318
 319        return 0;
 320}
 321EXPORT_SYMBOL_GPL(led_classdev_flash_register);
 322
 323void led_classdev_flash_unregister(struct led_classdev_flash *fled_cdev)
 324{
 325        if (!fled_cdev)
 326                return;
 327
 328        led_classdev_unregister(&fled_cdev->led_cdev);
 329}
 330EXPORT_SYMBOL_GPL(led_classdev_flash_unregister);
 331
 332static void led_clamp_align(struct led_flash_setting *s)
 333{
 334        u32 v, offset;
 335
 336        v = s->val + s->step / 2;
 337        v = clamp(v, s->min, s->max);
 338        offset = v - s->min;
 339        offset = s->step * (offset / s->step);
 340        s->val = s->min + offset;
 341}
 342
 343int led_set_flash_timeout(struct led_classdev_flash *fled_cdev, u32 timeout)
 344{
 345        struct led_classdev *led_cdev = &fled_cdev->led_cdev;
 346        struct led_flash_setting *s = &fled_cdev->timeout;
 347
 348        s->val = timeout;
 349        led_clamp_align(s);
 350
 351        if (!(led_cdev->flags & LED_SUSPENDED))
 352                return call_flash_op(fled_cdev, timeout_set, s->val);
 353
 354        return 0;
 355}
 356EXPORT_SYMBOL_GPL(led_set_flash_timeout);
 357
 358int led_get_flash_fault(struct led_classdev_flash *fled_cdev, u32 *fault)
 359{
 360        return call_flash_op(fled_cdev, fault_get, fault);
 361}
 362EXPORT_SYMBOL_GPL(led_get_flash_fault);
 363
 364int led_set_flash_brightness(struct led_classdev_flash *fled_cdev,
 365                                u32 brightness)
 366{
 367        struct led_classdev *led_cdev = &fled_cdev->led_cdev;
 368        struct led_flash_setting *s = &fled_cdev->brightness;
 369
 370        s->val = brightness;
 371        led_clamp_align(s);
 372
 373        if (!(led_cdev->flags & LED_SUSPENDED))
 374                return call_flash_op(fled_cdev, flash_brightness_set, s->val);
 375
 376        return 0;
 377}
 378EXPORT_SYMBOL_GPL(led_set_flash_brightness);
 379
 380int led_update_flash_brightness(struct led_classdev_flash *fled_cdev)
 381{
 382        struct led_flash_setting *s = &fled_cdev->brightness;
 383        u32 brightness;
 384
 385        if (has_flash_op(fled_cdev, flash_brightness_get)) {
 386                int ret = call_flash_op(fled_cdev, flash_brightness_get,
 387                                                &brightness);
 388                if (ret < 0)
 389                        return ret;
 390
 391                s->val = brightness;
 392        }
 393
 394        return 0;
 395}
 396EXPORT_SYMBOL_GPL(led_update_flash_brightness);
 397
 398MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
 399MODULE_DESCRIPTION("LED Flash class interface");
 400MODULE_LICENSE("GPL v2");
 401