linux/drivers/leds/trigger/ledtrig-transient.c
<<
>>
Prefs
   1/*
   2 * LED Kernel Transient Trigger
   3 *
   4 * Copyright (C) 2012 Shuah Khan <shuahkhan@gmail.com>
   5 *
   6 * Based on Richard Purdie's ledtrig-timer.c and Atsushi Nemoto's
   7 * ledtrig-heartbeat.c
   8 * Design and use-case input from Jonas Bonn <jonas@southpole.se> and
   9 * Neil Brown <neilb@suse.de>
  10 *
  11 * This program is free software; you can redistribute it and/or modify
  12 * it under the terms of the GNU General Public License version 2 as
  13 * published by the Free Software Foundation.
  14 *
  15 */
  16/*
  17 * Transient trigger allows one shot timer activation. Please refer to
  18 * Documentation/leds/ledtrig-transient.txt for details
  19*/
  20
  21#include <linux/module.h>
  22#include <linux/kernel.h>
  23#include <linux/init.h>
  24#include <linux/device.h>
  25#include <linux/slab.h>
  26#include <linux/timer.h>
  27#include <linux/leds.h>
  28#include "../leds.h"
  29
  30struct transient_trig_data {
  31        int activate;
  32        int state;
  33        int restore_state;
  34        unsigned long duration;
  35        struct timer_list timer;
  36};
  37
  38static void transient_timer_function(unsigned long data)
  39{
  40        struct led_classdev *led_cdev = (struct led_classdev *) data;
  41        struct transient_trig_data *transient_data = led_cdev->trigger_data;
  42
  43        transient_data->activate = 0;
  44        __led_set_brightness(led_cdev, transient_data->restore_state);
  45}
  46
  47static ssize_t transient_activate_show(struct device *dev,
  48                struct device_attribute *attr, char *buf)
  49{
  50        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  51        struct transient_trig_data *transient_data = led_cdev->trigger_data;
  52
  53        return sprintf(buf, "%d\n", transient_data->activate);
  54}
  55
  56static ssize_t transient_activate_store(struct device *dev,
  57                struct device_attribute *attr, const char *buf, size_t size)
  58{
  59        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  60        struct transient_trig_data *transient_data = led_cdev->trigger_data;
  61        unsigned long state;
  62        ssize_t ret;
  63
  64        ret = kstrtoul(buf, 10, &state);
  65        if (ret)
  66                return ret;
  67
  68        if (state != 1 && state != 0)
  69                return -EINVAL;
  70
  71        /* cancel the running timer */
  72        if (state == 0 && transient_data->activate == 1) {
  73                del_timer(&transient_data->timer);
  74                transient_data->activate = state;
  75                __led_set_brightness(led_cdev, transient_data->restore_state);
  76                return size;
  77        }
  78
  79        /* start timer if there is no active timer */
  80        if (state == 1 && transient_data->activate == 0 &&
  81            transient_data->duration != 0) {
  82                transient_data->activate = state;
  83                __led_set_brightness(led_cdev, transient_data->state);
  84                transient_data->restore_state =
  85                    (transient_data->state == LED_FULL) ? LED_OFF : LED_FULL;
  86                mod_timer(&transient_data->timer,
  87                          jiffies + transient_data->duration);
  88        }
  89
  90        /* state == 0 && transient_data->activate == 0
  91                timer is not active - just return */
  92        /* state == 1 && transient_data->activate == 1
  93                timer is already active - just return */
  94
  95        return size;
  96}
  97
  98static ssize_t transient_duration_show(struct device *dev,
  99                struct device_attribute *attr, char *buf)
 100{
 101        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 102        struct transient_trig_data *transient_data = led_cdev->trigger_data;
 103
 104        return sprintf(buf, "%lu\n", transient_data->duration);
 105}
 106
 107static ssize_t transient_duration_store(struct device *dev,
 108                struct device_attribute *attr, const char *buf, size_t size)
 109{
 110        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 111        struct transient_trig_data *transient_data = led_cdev->trigger_data;
 112        unsigned long state;
 113        ssize_t ret;
 114
 115        ret = kstrtoul(buf, 10, &state);
 116        if (ret)
 117                return ret;
 118
 119        transient_data->duration = state;
 120        return size;
 121}
 122
 123static ssize_t transient_state_show(struct device *dev,
 124                struct device_attribute *attr, char *buf)
 125{
 126        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 127        struct transient_trig_data *transient_data = led_cdev->trigger_data;
 128        int state;
 129
 130        state = (transient_data->state == LED_FULL) ? 1 : 0;
 131        return sprintf(buf, "%d\n", state);
 132}
 133
 134static ssize_t transient_state_store(struct device *dev,
 135                struct device_attribute *attr, const char *buf, size_t size)
 136{
 137        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 138        struct transient_trig_data *transient_data = led_cdev->trigger_data;
 139        unsigned long state;
 140        ssize_t ret;
 141
 142        ret = kstrtoul(buf, 10, &state);
 143        if (ret)
 144                return ret;
 145
 146        if (state != 1 && state != 0)
 147                return -EINVAL;
 148
 149        transient_data->state = (state == 1) ? LED_FULL : LED_OFF;
 150        return size;
 151}
 152
 153static DEVICE_ATTR(activate, 0644, transient_activate_show,
 154                   transient_activate_store);
 155static DEVICE_ATTR(duration, 0644, transient_duration_show,
 156                   transient_duration_store);
 157static DEVICE_ATTR(state, 0644, transient_state_show, transient_state_store);
 158
 159static void transient_trig_activate(struct led_classdev *led_cdev)
 160{
 161        int rc;
 162        struct transient_trig_data *tdata;
 163
 164        tdata = kzalloc(sizeof(struct transient_trig_data), GFP_KERNEL);
 165        if (!tdata) {
 166                dev_err(led_cdev->dev,
 167                        "unable to allocate transient trigger\n");
 168                return;
 169        }
 170        led_cdev->trigger_data = tdata;
 171
 172        rc = device_create_file(led_cdev->dev, &dev_attr_activate);
 173        if (rc)
 174                goto err_out;
 175
 176        rc = device_create_file(led_cdev->dev, &dev_attr_duration);
 177        if (rc)
 178                goto err_out_duration;
 179
 180        rc = device_create_file(led_cdev->dev, &dev_attr_state);
 181        if (rc)
 182                goto err_out_state;
 183
 184        setup_timer(&tdata->timer, transient_timer_function,
 185                    (unsigned long) led_cdev);
 186        led_cdev->activated = true;
 187
 188        return;
 189
 190err_out_state:
 191        device_remove_file(led_cdev->dev, &dev_attr_duration);
 192err_out_duration:
 193        device_remove_file(led_cdev->dev, &dev_attr_activate);
 194err_out:
 195        dev_err(led_cdev->dev, "unable to register transient trigger\n");
 196        led_cdev->trigger_data = NULL;
 197        kfree(tdata);
 198}
 199
 200static void transient_trig_deactivate(struct led_classdev *led_cdev)
 201{
 202        struct transient_trig_data *transient_data = led_cdev->trigger_data;
 203
 204        if (led_cdev->activated) {
 205                del_timer_sync(&transient_data->timer);
 206                __led_set_brightness(led_cdev, transient_data->restore_state);
 207                device_remove_file(led_cdev->dev, &dev_attr_activate);
 208                device_remove_file(led_cdev->dev, &dev_attr_duration);
 209                device_remove_file(led_cdev->dev, &dev_attr_state);
 210                led_cdev->trigger_data = NULL;
 211                led_cdev->activated = false;
 212                kfree(transient_data);
 213        }
 214}
 215
 216static struct led_trigger transient_trigger = {
 217        .name     = "transient",
 218        .activate = transient_trig_activate,
 219        .deactivate = transient_trig_deactivate,
 220};
 221
 222static int __init transient_trig_init(void)
 223{
 224        return led_trigger_register(&transient_trigger);
 225}
 226
 227static void __exit transient_trig_exit(void)
 228{
 229        led_trigger_unregister(&transient_trigger);
 230}
 231
 232module_init(transient_trig_init);
 233module_exit(transient_trig_exit);
 234
 235MODULE_AUTHOR("Shuah Khan <shuahkhan@gmail.com>");
 236MODULE_DESCRIPTION("Transient LED trigger");
 237MODULE_LICENSE("GPL");
 238