linux/drivers/leds/led-triggers.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * LED Triggers Core
   4 *
   5 * Copyright 2005-2007 Openedhand Ltd.
   6 *
   7 * Author: Richard Purdie <rpurdie@openedhand.com>
   8 */
   9
  10#include <linux/export.h>
  11#include <linux/kernel.h>
  12#include <linux/list.h>
  13#include <linux/spinlock.h>
  14#include <linux/device.h>
  15#include <linux/timer.h>
  16#include <linux/rwsem.h>
  17#include <linux/leds.h>
  18#include <linux/slab.h>
  19#include <linux/mm.h>
  20#include "leds.h"
  21
  22/*
  23 * Nests outside led_cdev->trigger_lock
  24 */
  25static DECLARE_RWSEM(triggers_list_lock);
  26LIST_HEAD(trigger_list);
  27
  28 /* Used by LED Class */
  29
  30static inline bool
  31trigger_relevant(struct led_classdev *led_cdev, struct led_trigger *trig)
  32{
  33        return !trig->trigger_type || trig->trigger_type == led_cdev->trigger_type;
  34}
  35
  36ssize_t led_trigger_write(struct file *filp, struct kobject *kobj,
  37                          struct bin_attribute *bin_attr, char *buf,
  38                          loff_t pos, size_t count)
  39{
  40        struct device *dev = kobj_to_dev(kobj);
  41        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  42        struct led_trigger *trig;
  43        int ret = count;
  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        if (sysfs_streq(buf, "none")) {
  53                led_trigger_remove(led_cdev);
  54                goto unlock;
  55        }
  56
  57        down_read(&triggers_list_lock);
  58        list_for_each_entry(trig, &trigger_list, next_trig) {
  59                if (sysfs_streq(buf, trig->name) && trigger_relevant(led_cdev, trig)) {
  60                        down_write(&led_cdev->trigger_lock);
  61                        led_trigger_set(led_cdev, trig);
  62                        up_write(&led_cdev->trigger_lock);
  63
  64                        up_read(&triggers_list_lock);
  65                        goto unlock;
  66                }
  67        }
  68        /* we come here only if buf matches no trigger */
  69        ret = -EINVAL;
  70        up_read(&triggers_list_lock);
  71
  72unlock:
  73        mutex_unlock(&led_cdev->led_access);
  74        return ret;
  75}
  76EXPORT_SYMBOL_GPL(led_trigger_write);
  77
  78__printf(3, 4)
  79static int led_trigger_snprintf(char *buf, ssize_t size, const char *fmt, ...)
  80{
  81        va_list args;
  82        int i;
  83
  84        va_start(args, fmt);
  85        if (size <= 0)
  86                i = vsnprintf(NULL, 0, fmt, args);
  87        else
  88                i = vscnprintf(buf, size, fmt, args);
  89        va_end(args);
  90
  91        return i;
  92}
  93
  94static int led_trigger_format(char *buf, size_t size,
  95                              struct led_classdev *led_cdev)
  96{
  97        struct led_trigger *trig;
  98        int len = led_trigger_snprintf(buf, size, "%s",
  99                                       led_cdev->trigger ? "none" : "[none]");
 100
 101        list_for_each_entry(trig, &trigger_list, next_trig) {
 102                bool hit;
 103
 104                if (!trigger_relevant(led_cdev, trig))
 105                        continue;
 106
 107                hit = led_cdev->trigger && !strcmp(led_cdev->trigger->name, trig->name);
 108
 109                len += led_trigger_snprintf(buf + len, size - len,
 110                                            " %s%s%s", hit ? "[" : "",
 111                                            trig->name, hit ? "]" : "");
 112        }
 113
 114        len += led_trigger_snprintf(buf + len, size - len, "\n");
 115
 116        return len;
 117}
 118
 119/*
 120 * It was stupid to create 10000 cpu triggers, but we are stuck with it now.
 121 * Don't make that mistake again. We work around it here by creating binary
 122 * attribute, which is not limited by length. This is _not_ good design, do not
 123 * copy it.
 124 */
 125ssize_t led_trigger_read(struct file *filp, struct kobject *kobj,
 126                        struct bin_attribute *attr, char *buf,
 127                        loff_t pos, size_t count)
 128{
 129        struct device *dev = kobj_to_dev(kobj);
 130        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 131        void *data;
 132        int len;
 133
 134        down_read(&triggers_list_lock);
 135        down_read(&led_cdev->trigger_lock);
 136
 137        len = led_trigger_format(NULL, 0, led_cdev);
 138        data = kvmalloc(len + 1, GFP_KERNEL);
 139        if (!data) {
 140                up_read(&led_cdev->trigger_lock);
 141                up_read(&triggers_list_lock);
 142                return -ENOMEM;
 143        }
 144        len = led_trigger_format(data, len + 1, led_cdev);
 145
 146        up_read(&led_cdev->trigger_lock);
 147        up_read(&triggers_list_lock);
 148
 149        len = memory_read_from_buffer(buf, count, &pos, data, len);
 150
 151        kvfree(data);
 152
 153        return len;
 154}
 155EXPORT_SYMBOL_GPL(led_trigger_read);
 156
 157/* Caller must ensure led_cdev->trigger_lock held */
 158int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
 159{
 160        unsigned long flags;
 161        char *event = NULL;
 162        char *envp[2];
 163        const char *name;
 164        int ret;
 165
 166        if (!led_cdev->trigger && !trig)
 167                return 0;
 168
 169        name = trig ? trig->name : "none";
 170        event = kasprintf(GFP_KERNEL, "TRIGGER=%s", name);
 171
 172        /* Remove any existing trigger */
 173        if (led_cdev->trigger) {
 174                write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
 175                list_del(&led_cdev->trig_list);
 176                write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
 177                        flags);
 178                cancel_work_sync(&led_cdev->set_brightness_work);
 179                led_stop_software_blink(led_cdev);
 180                if (led_cdev->trigger->deactivate)
 181                        led_cdev->trigger->deactivate(led_cdev);
 182                device_remove_groups(led_cdev->dev, led_cdev->trigger->groups);
 183                led_cdev->trigger = NULL;
 184                led_cdev->trigger_data = NULL;
 185                led_cdev->activated = false;
 186                led_set_brightness(led_cdev, LED_OFF);
 187        }
 188        if (trig) {
 189                write_lock_irqsave(&trig->leddev_list_lock, flags);
 190                list_add_tail(&led_cdev->trig_list, &trig->led_cdevs);
 191                write_unlock_irqrestore(&trig->leddev_list_lock, flags);
 192                led_cdev->trigger = trig;
 193
 194                if (trig->activate)
 195                        ret = trig->activate(led_cdev);
 196                else
 197                        ret = 0;
 198
 199                if (ret)
 200                        goto err_activate;
 201
 202                ret = device_add_groups(led_cdev->dev, trig->groups);
 203                if (ret) {
 204                        dev_err(led_cdev->dev, "Failed to add trigger attributes\n");
 205                        goto err_add_groups;
 206                }
 207        }
 208
 209        if (event) {
 210                envp[0] = event;
 211                envp[1] = NULL;
 212                if (kobject_uevent_env(&led_cdev->dev->kobj, KOBJ_CHANGE, envp))
 213                        dev_err(led_cdev->dev,
 214                                "%s: Error sending uevent\n", __func__);
 215                kfree(event);
 216        }
 217
 218        return 0;
 219
 220err_add_groups:
 221
 222        if (trig->deactivate)
 223                trig->deactivate(led_cdev);
 224err_activate:
 225
 226        write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
 227        list_del(&led_cdev->trig_list);
 228        write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, flags);
 229        led_cdev->trigger = NULL;
 230        led_cdev->trigger_data = NULL;
 231        led_set_brightness(led_cdev, LED_OFF);
 232        kfree(event);
 233
 234        return ret;
 235}
 236EXPORT_SYMBOL_GPL(led_trigger_set);
 237
 238void led_trigger_remove(struct led_classdev *led_cdev)
 239{
 240        down_write(&led_cdev->trigger_lock);
 241        led_trigger_set(led_cdev, NULL);
 242        up_write(&led_cdev->trigger_lock);
 243}
 244EXPORT_SYMBOL_GPL(led_trigger_remove);
 245
 246void led_trigger_set_default(struct led_classdev *led_cdev)
 247{
 248        struct led_trigger *trig;
 249
 250        if (!led_cdev->default_trigger)
 251                return;
 252
 253        down_read(&triggers_list_lock);
 254        down_write(&led_cdev->trigger_lock);
 255        list_for_each_entry(trig, &trigger_list, next_trig) {
 256                if (!strcmp(led_cdev->default_trigger, trig->name) &&
 257                    trigger_relevant(led_cdev, trig)) {
 258                        led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER;
 259                        led_trigger_set(led_cdev, trig);
 260                        break;
 261                }
 262        }
 263        up_write(&led_cdev->trigger_lock);
 264        up_read(&triggers_list_lock);
 265}
 266EXPORT_SYMBOL_GPL(led_trigger_set_default);
 267
 268void led_trigger_rename_static(const char *name, struct led_trigger *trig)
 269{
 270        /* new name must be on a temporary string to prevent races */
 271        BUG_ON(name == trig->name);
 272
 273        down_write(&triggers_list_lock);
 274        /* this assumes that trig->name was originaly allocated to
 275         * non constant storage */
 276        strcpy((char *)trig->name, name);
 277        up_write(&triggers_list_lock);
 278}
 279EXPORT_SYMBOL_GPL(led_trigger_rename_static);
 280
 281/* LED Trigger Interface */
 282
 283int led_trigger_register(struct led_trigger *trig)
 284{
 285        struct led_classdev *led_cdev;
 286        struct led_trigger *_trig;
 287
 288        rwlock_init(&trig->leddev_list_lock);
 289        INIT_LIST_HEAD(&trig->led_cdevs);
 290
 291        down_write(&triggers_list_lock);
 292        /* Make sure the trigger's name isn't already in use */
 293        list_for_each_entry(_trig, &trigger_list, next_trig) {
 294                if (!strcmp(_trig->name, trig->name) &&
 295                    (trig->trigger_type == _trig->trigger_type ||
 296                     !trig->trigger_type || !_trig->trigger_type)) {
 297                        up_write(&triggers_list_lock);
 298                        return -EEXIST;
 299                }
 300        }
 301        /* Add to the list of led triggers */
 302        list_add_tail(&trig->next_trig, &trigger_list);
 303        up_write(&triggers_list_lock);
 304
 305        /* Register with any LEDs that have this as a default trigger */
 306        down_read(&leds_list_lock);
 307        list_for_each_entry(led_cdev, &leds_list, node) {
 308                down_write(&led_cdev->trigger_lock);
 309                if (!led_cdev->trigger && led_cdev->default_trigger &&
 310                    !strcmp(led_cdev->default_trigger, trig->name) &&
 311                    trigger_relevant(led_cdev, trig)) {
 312                        led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER;
 313                        led_trigger_set(led_cdev, trig);
 314                }
 315                up_write(&led_cdev->trigger_lock);
 316        }
 317        up_read(&leds_list_lock);
 318
 319        return 0;
 320}
 321EXPORT_SYMBOL_GPL(led_trigger_register);
 322
 323void led_trigger_unregister(struct led_trigger *trig)
 324{
 325        struct led_classdev *led_cdev;
 326
 327        if (list_empty_careful(&trig->next_trig))
 328                return;
 329
 330        /* Remove from the list of led triggers */
 331        down_write(&triggers_list_lock);
 332        list_del_init(&trig->next_trig);
 333        up_write(&triggers_list_lock);
 334
 335        /* Remove anyone actively using this trigger */
 336        down_read(&leds_list_lock);
 337        list_for_each_entry(led_cdev, &leds_list, node) {
 338                down_write(&led_cdev->trigger_lock);
 339                if (led_cdev->trigger == trig)
 340                        led_trigger_set(led_cdev, NULL);
 341                up_write(&led_cdev->trigger_lock);
 342        }
 343        up_read(&leds_list_lock);
 344}
 345EXPORT_SYMBOL_GPL(led_trigger_unregister);
 346
 347static void devm_led_trigger_release(struct device *dev, void *res)
 348{
 349        led_trigger_unregister(*(struct led_trigger **)res);
 350}
 351
 352int devm_led_trigger_register(struct device *dev,
 353                              struct led_trigger *trig)
 354{
 355        struct led_trigger **dr;
 356        int rc;
 357
 358        dr = devres_alloc(devm_led_trigger_release, sizeof(*dr),
 359                          GFP_KERNEL);
 360        if (!dr)
 361                return -ENOMEM;
 362
 363        *dr = trig;
 364
 365        rc = led_trigger_register(trig);
 366        if (rc)
 367                devres_free(dr);
 368        else
 369                devres_add(dev, dr);
 370
 371        return rc;
 372}
 373EXPORT_SYMBOL_GPL(devm_led_trigger_register);
 374
 375/* Simple LED Trigger Interface */
 376
 377void led_trigger_event(struct led_trigger *trig,
 378                        enum led_brightness brightness)
 379{
 380        struct led_classdev *led_cdev;
 381
 382        if (!trig)
 383                return;
 384
 385        read_lock(&trig->leddev_list_lock);
 386        list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list)
 387                led_set_brightness(led_cdev, brightness);
 388        read_unlock(&trig->leddev_list_lock);
 389}
 390EXPORT_SYMBOL_GPL(led_trigger_event);
 391
 392static void led_trigger_blink_setup(struct led_trigger *trig,
 393                             unsigned long *delay_on,
 394                             unsigned long *delay_off,
 395                             int oneshot,
 396                             int invert)
 397{
 398        struct led_classdev *led_cdev;
 399
 400        if (!trig)
 401                return;
 402
 403        read_lock(&trig->leddev_list_lock);
 404        list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) {
 405                if (oneshot)
 406                        led_blink_set_oneshot(led_cdev, delay_on, delay_off,
 407                                              invert);
 408                else
 409                        led_blink_set(led_cdev, delay_on, delay_off);
 410        }
 411        read_unlock(&trig->leddev_list_lock);
 412}
 413
 414void led_trigger_blink(struct led_trigger *trig,
 415                       unsigned long *delay_on,
 416                       unsigned long *delay_off)
 417{
 418        led_trigger_blink_setup(trig, delay_on, delay_off, 0, 0);
 419}
 420EXPORT_SYMBOL_GPL(led_trigger_blink);
 421
 422void led_trigger_blink_oneshot(struct led_trigger *trig,
 423                               unsigned long *delay_on,
 424                               unsigned long *delay_off,
 425                               int invert)
 426{
 427        led_trigger_blink_setup(trig, delay_on, delay_off, 1, invert);
 428}
 429EXPORT_SYMBOL_GPL(led_trigger_blink_oneshot);
 430
 431void led_trigger_register_simple(const char *name, struct led_trigger **tp)
 432{
 433        struct led_trigger *trig;
 434        int err;
 435
 436        trig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
 437
 438        if (trig) {
 439                trig->name = name;
 440                err = led_trigger_register(trig);
 441                if (err < 0) {
 442                        kfree(trig);
 443                        trig = NULL;
 444                        pr_warn("LED trigger %s failed to register (%d)\n",
 445                                name, err);
 446                }
 447        } else {
 448                pr_warn("LED trigger %s failed to register (no memory)\n",
 449                        name);
 450        }
 451        *tp = trig;
 452}
 453EXPORT_SYMBOL_GPL(led_trigger_register_simple);
 454
 455void led_trigger_unregister_simple(struct led_trigger *trig)
 456{
 457        if (trig)
 458                led_trigger_unregister(trig);
 459        kfree(trig);
 460}
 461EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
 462