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
 106        /* Remove any existing trigger */
 107        if (led_cdev->trigger) {
 108                write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
 109                list_del(&led_cdev->trig_list);
 110                write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
 111                        flags);
 112                if (led_cdev->trigger->deactivate)
 113                        led_cdev->trigger->deactivate(led_cdev);
 114                led_cdev->trigger = NULL;
 115                led_set_brightness(led_cdev, LED_OFF);
 116        }
 117        if (trig) {
 118                write_lock_irqsave(&trig->leddev_list_lock, flags);
 119                list_add_tail(&led_cdev->trig_list, &trig->led_cdevs);
 120                write_unlock_irqrestore(&trig->leddev_list_lock, flags);
 121                led_cdev->trigger = trig;
 122                if (trig->activate)
 123                        trig->activate(led_cdev);
 124        }
 125}
 126EXPORT_SYMBOL_GPL(led_trigger_set);
 127
 128void led_trigger_remove(struct led_classdev *led_cdev)
 129{
 130        down_write(&led_cdev->trigger_lock);
 131        led_trigger_set(led_cdev, NULL);
 132        up_write(&led_cdev->trigger_lock);
 133}
 134EXPORT_SYMBOL_GPL(led_trigger_remove);
 135
 136void led_trigger_set_default(struct led_classdev *led_cdev)
 137{
 138        struct led_trigger *trig;
 139
 140        if (!led_cdev->default_trigger)
 141                return;
 142
 143        down_read(&triggers_list_lock);
 144        down_write(&led_cdev->trigger_lock);
 145        list_for_each_entry(trig, &trigger_list, next_trig) {
 146                if (!strcmp(led_cdev->default_trigger, trig->name))
 147                        led_trigger_set(led_cdev, trig);
 148        }
 149        up_write(&led_cdev->trigger_lock);
 150        up_read(&triggers_list_lock);
 151}
 152EXPORT_SYMBOL_GPL(led_trigger_set_default);
 153
 154/* LED Trigger Interface */
 155
 156int led_trigger_register(struct led_trigger *trig)
 157{
 158        struct led_classdev *led_cdev;
 159        struct led_trigger *_trig;
 160
 161        rwlock_init(&trig->leddev_list_lock);
 162        INIT_LIST_HEAD(&trig->led_cdevs);
 163
 164        down_write(&triggers_list_lock);
 165        /* Make sure the trigger's name isn't already in use */
 166        list_for_each_entry(_trig, &trigger_list, next_trig) {
 167                if (!strcmp(_trig->name, trig->name)) {
 168                        up_write(&triggers_list_lock);
 169                        return -EEXIST;
 170                }
 171        }
 172        /* Add to the list of led triggers */
 173        list_add_tail(&trig->next_trig, &trigger_list);
 174        up_write(&triggers_list_lock);
 175
 176        /* Register with any LEDs that have this as a default trigger */
 177        down_read(&leds_list_lock);
 178        list_for_each_entry(led_cdev, &leds_list, node) {
 179                down_write(&led_cdev->trigger_lock);
 180                if (!led_cdev->trigger && led_cdev->default_trigger &&
 181                            !strcmp(led_cdev->default_trigger, trig->name))
 182                        led_trigger_set(led_cdev, trig);
 183                up_write(&led_cdev->trigger_lock);
 184        }
 185        up_read(&leds_list_lock);
 186
 187        return 0;
 188}
 189EXPORT_SYMBOL_GPL(led_trigger_register);
 190
 191void led_trigger_unregister(struct led_trigger *trig)
 192{
 193        struct led_classdev *led_cdev;
 194
 195        /* Remove from the list of led triggers */
 196        down_write(&triggers_list_lock);
 197        list_del(&trig->next_trig);
 198        up_write(&triggers_list_lock);
 199
 200        /* Remove anyone actively using this trigger */
 201        down_read(&leds_list_lock);
 202        list_for_each_entry(led_cdev, &leds_list, node) {
 203                down_write(&led_cdev->trigger_lock);
 204                if (led_cdev->trigger == trig)
 205                        led_trigger_set(led_cdev, NULL);
 206                up_write(&led_cdev->trigger_lock);
 207        }
 208        up_read(&leds_list_lock);
 209}
 210EXPORT_SYMBOL_GPL(led_trigger_unregister);
 211
 212/* Simple LED Tigger Interface */
 213
 214void led_trigger_event(struct led_trigger *trig,
 215                        enum led_brightness brightness)
 216{
 217        struct list_head *entry;
 218
 219        if (!trig)
 220                return;
 221
 222        read_lock(&trig->leddev_list_lock);
 223        list_for_each(entry, &trig->led_cdevs) {
 224                struct led_classdev *led_cdev;
 225
 226                led_cdev = list_entry(entry, struct led_classdev, trig_list);
 227                __led_set_brightness(led_cdev, brightness);
 228        }
 229        read_unlock(&trig->leddev_list_lock);
 230}
 231EXPORT_SYMBOL_GPL(led_trigger_event);
 232
 233static void led_trigger_blink_setup(struct led_trigger *trig,
 234                             unsigned long *delay_on,
 235                             unsigned long *delay_off,
 236                             int oneshot,
 237                             int invert)
 238{
 239        struct list_head *entry;
 240
 241        if (!trig)
 242                return;
 243
 244        read_lock(&trig->leddev_list_lock);
 245        list_for_each(entry, &trig->led_cdevs) {
 246                struct led_classdev *led_cdev;
 247
 248                led_cdev = list_entry(entry, struct led_classdev, trig_list);
 249                if (oneshot)
 250                        led_blink_set_oneshot(led_cdev, delay_on, delay_off,
 251                                              invert);
 252                else
 253                        led_blink_set(led_cdev, delay_on, delay_off);
 254        }
 255        read_unlock(&trig->leddev_list_lock);
 256}
 257
 258void led_trigger_blink(struct led_trigger *trig,
 259                       unsigned long *delay_on,
 260                       unsigned long *delay_off)
 261{
 262        led_trigger_blink_setup(trig, delay_on, delay_off, 0, 0);
 263}
 264EXPORT_SYMBOL_GPL(led_trigger_blink);
 265
 266void led_trigger_blink_oneshot(struct led_trigger *trig,
 267                               unsigned long *delay_on,
 268                               unsigned long *delay_off,
 269                               int invert)
 270{
 271        led_trigger_blink_setup(trig, delay_on, delay_off, 1, invert);
 272}
 273EXPORT_SYMBOL_GPL(led_trigger_blink_oneshot);
 274
 275void led_trigger_register_simple(const char *name, struct led_trigger **tp)
 276{
 277        struct led_trigger *trig;
 278        int err;
 279
 280        trig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
 281
 282        if (trig) {
 283                trig->name = name;
 284                err = led_trigger_register(trig);
 285                if (err < 0) {
 286                        kfree(trig);
 287                        trig = NULL;
 288                        printk(KERN_WARNING "LED trigger %s failed to register"
 289                                " (%d)\n", name, err);
 290                }
 291        } else
 292                printk(KERN_WARNING "LED trigger %s failed to register"
 293                        " (no memory)\n", name);
 294
 295        *tp = trig;
 296}
 297EXPORT_SYMBOL_GPL(led_trigger_register_simple);
 298
 299void led_trigger_unregister_simple(struct led_trigger *trig)
 300{
 301        if (trig)
 302                led_trigger_unregister(trig);
 303        kfree(trig);
 304}
 305EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
 306
 307MODULE_AUTHOR("Richard Purdie");
 308MODULE_LICENSE("GPL");
 309MODULE_DESCRIPTION("LED Triggers Core");
 310
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.