linux/drivers/leds/led-triggers.c
<<
>>
Prefs
   1/*
   2 * LED Triggers Core
   3 *
   4 * Copyright 2005-2007 Openedhand Ltd.
   5 *
   6 * Author: Richard Purdie <rpurdie@openedhand.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 *
  12 */
  13
  14#include <linux/module.h>
  15#include <linux/kernel.h>
  16#include <linux/init.h>
  17#include <linux/list.h>
  18#include <linux/spinlock.h>
  19#include <linux/device.h>
  20#include <linux/timer.h>
  21#include <linux/rwsem.h>
  22#include <linux/leds.h>
  23#include <linux/slab.h>
  24#include "leds.h"
  25
  26/*
  27 * Nests outside led_cdev->trigger_lock
  28 */
  29static DECLARE_RWSEM(triggers_list_lock);
  30static LIST_HEAD(trigger_list);
  31
  32 /* Used by LED Class */
  33
  34ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
  35                const char *buf, size_t count)
  36{
  37        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  38        char trigger_name[TRIG_NAME_MAX];
  39        struct led_trigger *trig;
  40        size_t len;
  41
  42        trigger_name[sizeof(trigger_name) - 1] = '\0';
  43        strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
  44        len = strlen(trigger_name);
  45
  46        if (len && trigger_name[len - 1] == '\n')
  47                trigger_name[len - 1] = '\0';
  48
  49        if (!strcmp(trigger_name, "none")) {
  50                led_trigger_remove(led_cdev);
  51                return count;
  52        }
  53
  54        down_read(&triggers_list_lock);
  55        list_for_each_entry(trig, &trigger_list, next_trig) {
  56                if (!strcmp(trigger_name, trig->name)) {
  57                        down_write(&led_cdev->trigger_lock);
  58                        led_trigger_set(led_cdev, trig);
  59                        up_write(&led_cdev->trigger_lock);
  60
  61                        up_read(&triggers_list_lock);
  62                        return count;
  63                }
  64        }
  65        up_read(&triggers_list_lock);
  66
  67        return -EINVAL;
  68}
  69EXPORT_SYMBOL_GPL(led_trigger_store);
  70
  71ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
  72                char *buf)
  73{
  74        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  75        struct led_trigger *trig;
  76        int len = 0;
  77
  78        down_read(&triggers_list_lock);
  79        down_read(&led_cdev->trigger_lock);
  80
  81        if (!led_cdev->trigger)
  82                len += sprintf(buf+len, "[none] ");
  83        else
  84                len += sprintf(buf+len, "none ");
  85
  86        list_for_each_entry(trig, &trigger_list, next_trig) {
  87                if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
  88                                                        trig->name))
  89                        len += sprintf(buf+len, "[%s] ", trig->name);
  90                else
  91                        len += sprintf(buf+len, "%s ", trig->name);
  92        }
  93        up_read(&led_cdev->trigger_lock);
  94        up_read(&triggers_list_lock);
  95
  96        len += sprintf(len+buf, "\n");
  97        return len;
  98}
  99EXPORT_SYMBOL_GPL(led_trigger_show);
 100
 101/* Caller must ensure led_cdev->trigger_lock held */
 102void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
 103{
 104        unsigned long flags;
 105        char *event = NULL;
 106        char *envp[2];
 107        const char *name;
 108
 109        name = trig ? trig->name : "none";
 110        event = kasprintf(GFP_KERNEL, "TRIGGER=%s", name);
 111
 112        /* Remove any existing trigger */
 113        if (led_cdev->trigger) {
 114                write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
 115                list_del(&led_cdev->trig_list);
 116                write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
 117                        flags);
 118                cancel_work_sync(&led_cdev->set_brightness_work);
 119                led_stop_software_blink(led_cdev);
 120                if (led_cdev->trigger->deactivate)
 121                        led_cdev->trigger->deactivate(led_cdev);
 122                led_cdev->trigger = NULL;
 123                led_set_brightness(led_cdev, LED_OFF);
 124        }
 125        if (trig) {
 126                write_lock_irqsave(&trig->leddev_list_lock, flags);
 127                list_add_tail(&led_cdev->trig_list, &trig->led_cdevs);
 128                write_unlock_irqrestore(&trig->leddev_list_lock, flags);
 129                led_cdev->trigger = trig;
 130                if (trig->activate)
 131                        trig->activate(led_cdev);
 132        }
 133
 134        if (event) {
 135                envp[0] = event;
 136                envp[1] = NULL;
 137                kobject_uevent_env(&led_cdev->dev->kobj, KOBJ_CHANGE, envp);
 138                kfree(event);
 139        }
 140}
 141EXPORT_SYMBOL_GPL(led_trigger_set);
 142
 143void led_trigger_remove(struct led_classdev *led_cdev)
 144{
 145        down_write(&led_cdev->trigger_lock);
 146        led_trigger_set(led_cdev, NULL);
 147        up_write(&led_cdev->trigger_lock);
 148}
 149EXPORT_SYMBOL_GPL(led_trigger_remove);
 150
 151void led_trigger_set_default(struct led_classdev *led_cdev)
 152{
 153        struct led_trigger *trig;
 154
 155        if (!led_cdev->default_trigger)
 156                return;
 157
 158        down_read(&triggers_list_lock);
 159        down_write(&led_cdev->trigger_lock);
 160        list_for_each_entry(trig, &trigger_list, next_trig) {
 161                if (!strcmp(led_cdev->default_trigger, trig->name))
 162                        led_trigger_set(led_cdev, trig);
 163        }
 164        up_write(&led_cdev->trigger_lock);
 165        up_read(&triggers_list_lock);
 166}
 167EXPORT_SYMBOL_GPL(led_trigger_set_default);
 168
 169void led_trigger_rename_static(const char *name, struct led_trigger *trig)
 170{
 171        /* new name must be on a temporary string to prevent races */
 172        BUG_ON(name == trig->name);
 173
 174        down_write(&triggers_list_lock);
 175        /* this assumes that trig->name was originaly allocated to
 176         * non constant storage */
 177        strcpy((char *)trig->name, name);
 178        up_write(&triggers_list_lock);
 179}
 180EXPORT_SYMBOL_GPL(led_trigger_rename_static);
 181
 182/* LED Trigger Interface */
 183
 184int led_trigger_register(struct led_trigger *trig)
 185{
 186        struct led_classdev *led_cdev;
 187        struct led_trigger *_trig;
 188
 189        rwlock_init(&trig->leddev_list_lock);
 190        INIT_LIST_HEAD(&trig->led_cdevs);
 191
 192        down_write(&triggers_list_lock);
 193        /* Make sure the trigger's name isn't already in use */
 194        list_for_each_entry(_trig, &trigger_list, next_trig) {
 195                if (!strcmp(_trig->name, trig->name)) {
 196                        up_write(&triggers_list_lock);
 197                        return -EEXIST;
 198                }
 199        }
 200        /* Add to the list of led triggers */
 201        list_add_tail(&trig->next_trig, &trigger_list);
 202        up_write(&triggers_list_lock);
 203
 204        /* Register with any LEDs that have this as a default trigger */
 205        down_read(&leds_list_lock);
 206        list_for_each_entry(led_cdev, &leds_list, node) {
 207                down_write(&led_cdev->trigger_lock);
 208                if (!led_cdev->trigger && led_cdev->default_trigger &&
 209                            !strcmp(led_cdev->default_trigger, trig->name))
 210                        led_trigger_set(led_cdev, trig);
 211                up_write(&led_cdev->trigger_lock);
 212        }
 213        up_read(&leds_list_lock);
 214
 215        return 0;
 216}
 217EXPORT_SYMBOL_GPL(led_trigger_register);
 218
 219void led_trigger_unregister(struct led_trigger *trig)
 220{
 221        struct led_classdev *led_cdev;
 222
 223        /* Remove from the list of led triggers */
 224        down_write(&triggers_list_lock);
 225        list_del(&trig->next_trig);
 226        up_write(&triggers_list_lock);
 227
 228        /* Remove anyone actively using this trigger */
 229        down_read(&leds_list_lock);
 230        list_for_each_entry(led_cdev, &leds_list, node) {
 231                down_write(&led_cdev->trigger_lock);
 232                if (led_cdev->trigger == trig)
 233                        led_trigger_set(led_cdev, NULL);
 234                up_write(&led_cdev->trigger_lock);
 235        }
 236        up_read(&leds_list_lock);
 237}
 238EXPORT_SYMBOL_GPL(led_trigger_unregister);
 239
 240/* Simple LED Tigger Interface */
 241
 242void led_trigger_event(struct led_trigger *trig,
 243                        enum led_brightness brightness)
 244{
 245        struct list_head *entry;
 246
 247        if (!trig)
 248                return;
 249
 250        read_lock(&trig->leddev_list_lock);
 251        list_for_each(entry, &trig->led_cdevs) {
 252                struct led_classdev *led_cdev;
 253
 254                led_cdev = list_entry(entry, struct led_classdev, trig_list);
 255                led_set_brightness(led_cdev, brightness);
 256        }
 257        read_unlock(&trig->leddev_list_lock);
 258}
 259EXPORT_SYMBOL_GPL(led_trigger_event);
 260
 261static void led_trigger_blink_setup(struct led_trigger *trig,
 262                             unsigned long *delay_on,
 263                             unsigned long *delay_off,
 264                             int oneshot,
 265                             int invert)
 266{
 267        struct list_head *entry;
 268
 269        if (!trig)
 270                return;
 271
 272        read_lock(&trig->leddev_list_lock);
 273        list_for_each(entry, &trig->led_cdevs) {
 274                struct led_classdev *led_cdev;
 275
 276                led_cdev = list_entry(entry, struct led_classdev, trig_list);
 277                if (oneshot)
 278                        led_blink_set_oneshot(led_cdev, delay_on, delay_off,
 279                                              invert);
 280                else
 281                        led_blink_set(led_cdev, delay_on, delay_off);
 282        }
 283        read_unlock(&trig->leddev_list_lock);
 284}
 285
 286void led_trigger_blink(struct led_trigger *trig,
 287                       unsigned long *delay_on,
 288                       unsigned long *delay_off)
 289{
 290        led_trigger_blink_setup(trig, delay_on, delay_off, 0, 0);
 291}
 292EXPORT_SYMBOL_GPL(led_trigger_blink);
 293
 294void led_trigger_blink_oneshot(struct led_trigger *trig,
 295                               unsigned long *delay_on,
 296                               unsigned long *delay_off,
 297                               int invert)
 298{
 299        led_trigger_blink_setup(trig, delay_on, delay_off, 1, invert);
 300}
 301EXPORT_SYMBOL_GPL(led_trigger_blink_oneshot);
 302
 303void led_trigger_register_simple(const char *name, struct led_trigger **tp)
 304{
 305        struct led_trigger *trig;
 306        int err;
 307
 308        trig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
 309
 310        if (trig) {
 311                trig->name = name;
 312                err = led_trigger_register(trig);
 313                if (err < 0) {
 314                        kfree(trig);
 315                        trig = NULL;
 316                        pr_warn("LED trigger %s failed to register (%d)\n",
 317                                name, err);
 318                }
 319        } else {
 320                pr_warn("LED trigger %s failed to register (no memory)\n",
 321                        name);
 322        }
 323        *tp = trig;
 324}
 325EXPORT_SYMBOL_GPL(led_trigger_register_simple);
 326
 327void led_trigger_unregister_simple(struct led_trigger *trig)
 328{
 329        if (trig)
 330                led_trigger_unregister(trig);
 331        kfree(trig);
 332}
 333EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
 334
 335MODULE_AUTHOR("Richard Purdie");
 336MODULE_LICENSE("GPL");
 337MODULE_DESCRIPTION("LED Triggers Core");
 338