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
 169/* LED Trigger Interface */
 170
 171int led_trigger_register(struct led_trigger *trig)
 172{
 173        struct led_classdev *led_cdev;
 174        struct led_trigger *_trig;
 175
 176        rwlock_init(&trig->leddev_list_lock);
 177        INIT_LIST_HEAD(&trig->led_cdevs);
 178
 179        down_write(&triggers_list_lock);
 180        /* Make sure the trigger's name isn't already in use */
 181        list_for_each_entry(_trig, &trigger_list, next_trig) {
 182                if (!strcmp(_trig->name, trig->name)) {
 183                        up_write(&triggers_list_lock);
 184                        return -EEXIST;
 185                }
 186        }
 187        /* Add to the list of led triggers */
 188        list_add_tail(&trig->next_trig, &trigger_list);
 189        up_write(&triggers_list_lock);
 190
 191        /* Register with any LEDs that have this as a default trigger */
 192        down_read(&leds_list_lock);
 193        list_for_each_entry(led_cdev, &leds_list, node) {
 194                down_write(&led_cdev->trigger_lock);
 195                if (!led_cdev->trigger && led_cdev->default_trigger &&
 196                            !strcmp(led_cdev->default_trigger, trig->name))
 197                        led_trigger_set(led_cdev, trig);
 198                up_write(&led_cdev->trigger_lock);
 199        }
 200        up_read(&leds_list_lock);
 201
 202        return 0;
 203}
 204EXPORT_SYMBOL_GPL(led_trigger_register);
 205
 206void led_trigger_unregister(struct led_trigger *trig)
 207{
 208        struct led_classdev *led_cdev;
 209
 210        /* Remove from the list of led triggers */
 211        down_write(&triggers_list_lock);
 212        list_del(&trig->next_trig);
 213        up_write(&triggers_list_lock);
 214
 215        /* Remove anyone actively using this trigger */
 216        down_read(&leds_list_lock);
 217        list_for_each_entry(led_cdev, &leds_list, node) {
 218                down_write(&led_cdev->trigger_lock);
 219                if (led_cdev->trigger == trig)
 220                        led_trigger_set(led_cdev, NULL);
 221                up_write(&led_cdev->trigger_lock);
 222        }
 223        up_read(&leds_list_lock);
 224}
 225EXPORT_SYMBOL_GPL(led_trigger_unregister);
 226
 227/* Simple LED Tigger Interface */
 228
 229void led_trigger_event(struct led_trigger *trig,
 230                        enum led_brightness brightness)
 231{
 232        struct list_head *entry;
 233
 234        if (!trig)
 235                return;
 236
 237        read_lock(&trig->leddev_list_lock);
 238        list_for_each(entry, &trig->led_cdevs) {
 239                struct led_classdev *led_cdev;
 240
 241                led_cdev = list_entry(entry, struct led_classdev, trig_list);
 242                led_set_brightness(led_cdev, brightness);
 243        }
 244        read_unlock(&trig->leddev_list_lock);
 245}
 246EXPORT_SYMBOL_GPL(led_trigger_event);
 247
 248static void led_trigger_blink_setup(struct led_trigger *trig,
 249                             unsigned long *delay_on,
 250                             unsigned long *delay_off,
 251                             int oneshot,
 252                             int invert)
 253{
 254        struct list_head *entry;
 255
 256        if (!trig)
 257                return;
 258
 259        read_lock(&trig->leddev_list_lock);
 260        list_for_each(entry, &trig->led_cdevs) {
 261                struct led_classdev *led_cdev;
 262
 263                led_cdev = list_entry(entry, struct led_classdev, trig_list);
 264                if (oneshot)
 265                        led_blink_set_oneshot(led_cdev, delay_on, delay_off,
 266                                              invert);
 267                else
 268                        led_blink_set(led_cdev, delay_on, delay_off);
 269        }
 270        read_unlock(&trig->leddev_list_lock);
 271}
 272
 273void led_trigger_blink(struct led_trigger *trig,
 274                       unsigned long *delay_on,
 275                       unsigned long *delay_off)
 276{
 277        led_trigger_blink_setup(trig, delay_on, delay_off, 0, 0);
 278}
 279EXPORT_SYMBOL_GPL(led_trigger_blink);
 280
 281void led_trigger_blink_oneshot(struct led_trigger *trig,
 282                               unsigned long *delay_on,
 283                               unsigned long *delay_off,
 284                               int invert)
 285{
 286        led_trigger_blink_setup(trig, delay_on, delay_off, 1, invert);
 287}
 288EXPORT_SYMBOL_GPL(led_trigger_blink_oneshot);
 289
 290void led_trigger_register_simple(const char *name, struct led_trigger **tp)
 291{
 292        struct led_trigger *trig;
 293        int err;
 294
 295        trig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
 296
 297        if (trig) {
 298                trig->name = name;
 299                err = led_trigger_register(trig);
 300                if (err < 0) {
 301                        kfree(trig);
 302                        trig = NULL;
 303                        printk(KERN_WARNING "LED trigger %s failed to register"
 304                                " (%d)\n", name, err);
 305                }
 306        } else
 307                printk(KERN_WARNING "LED trigger %s failed to register"
 308                        " (no memory)\n", name);
 309
 310        *tp = trig;
 311}
 312EXPORT_SYMBOL_GPL(led_trigger_register_simple);
 313
 314void led_trigger_unregister_simple(struct led_trigger *trig)
 315{
 316        if (trig)
 317                led_trigger_unregister(trig);
 318        kfree(trig);
 319}
 320EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
 321
 322MODULE_AUTHOR("Richard Purdie");
 323MODULE_LICENSE("GPL");
 324MODULE_DESCRIPTION("LED Triggers Core");
 325
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.