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/sysdev.h>
  21#include <linux/timer.h>
  22#include <linux/rwsem.h>
  23#include <linux/leds.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 *trigger)
 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 (trigger) {
 118                write_lock_irqsave(&trigger->leddev_list_lock, flags);
 119                list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
 120                write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
 121                led_cdev->trigger = trigger;
 122                if (trigger->activate)
 123                        trigger->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 *trigger)
 157{
 158        struct led_classdev *led_cdev;
 159        struct led_trigger *trig;
 160
 161        rwlock_init(&trigger->leddev_list_lock);
 162        INIT_LIST_HEAD(&trigger->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, trigger->name)) {
 168                        up_write(&triggers_list_lock);
 169                        return -EEXIST;
 170                }
 171        }
 172        /* Add to the list of led triggers */
 173        list_add_tail(&trigger->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, trigger->name))
 182                        led_trigger_set(led_cdev, trigger);
 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 *trigger)
 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(&trigger->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 == trigger)
 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 *trigger,
 215                        enum led_brightness brightness)
 216{
 217        struct list_head *entry;
 218
 219        if (!trigger)
 220                return;
 221
 222        read_lock(&trigger->leddev_list_lock);
 223        list_for_each(entry, &trigger->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(&trigger->leddev_list_lock);
 230}
 231EXPORT_SYMBOL_GPL(led_trigger_event);
 232
 233void led_trigger_register_simple(const char *name, struct led_trigger **tp)
 234{
 235        struct led_trigger *trigger;
 236        int err;
 237
 238        trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
 239
 240        if (trigger) {
 241                trigger->name = name;
 242                err = led_trigger_register(trigger);
 243                if (err < 0)
 244                        printk(KERN_WARNING "LED trigger %s failed to register"
 245                                " (%d)\n", name, err);
 246        } else
 247                printk(KERN_WARNING "LED trigger %s failed to register"
 248                        " (no memory)\n", name);
 249
 250        *tp = trigger;
 251}
 252EXPORT_SYMBOL_GPL(led_trigger_register_simple);
 253
 254void led_trigger_unregister_simple(struct led_trigger *trigger)
 255{
 256        if (trigger)
 257                led_trigger_unregister(trigger);
 258        kfree(trigger);
 259}
 260EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
 261
 262MODULE_AUTHOR("Richard Purdie");
 263MODULE_LICENSE("GPL");
 264MODULE_DESCRIPTION("LED Triggers Core");
 265