linux/drivers/leds/ledtrig-timer.c
<<
>>
Prefs
   1/*
   2 * LED Kernel Timer Trigger
   3 *
   4 * Copyright 2005-2006 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/jiffies.h>
  16#include <linux/kernel.h>
  17#include <linux/init.h>
  18#include <linux/list.h>
  19#include <linux/spinlock.h>
  20#include <linux/device.h>
  21#include <linux/sysdev.h>
  22#include <linux/timer.h>
  23#include <linux/ctype.h>
  24#include <linux/leds.h>
  25#include "leds.h"
  26
  27struct timer_trig_data {
  28        int brightness_on;              /* LED brightness during "on" period.
  29                                         * (LED_OFF < brightness_on <= LED_FULL)
  30                                         */
  31        unsigned long delay_on;         /* milliseconds on */
  32        unsigned long delay_off;        /* milliseconds off */
  33        struct timer_list timer;
  34};
  35
  36static void led_timer_function(unsigned long data)
  37{
  38        struct led_classdev *led_cdev = (struct led_classdev *) data;
  39        struct timer_trig_data *timer_data = led_cdev->trigger_data;
  40        unsigned long brightness;
  41        unsigned long delay;
  42
  43        if (!timer_data->delay_on || !timer_data->delay_off) {
  44                led_set_brightness(led_cdev, LED_OFF);
  45                return;
  46        }
  47
  48        brightness = led_get_brightness(led_cdev);
  49        if (!brightness) {
  50                /* Time to switch the LED on. */
  51                brightness = timer_data->brightness_on;
  52                delay = timer_data->delay_on;
  53        } else {
  54                /* Store the current brightness value to be able
  55                 * to restore it when the delay_off period is over.
  56                 */
  57                timer_data->brightness_on = brightness;
  58                brightness = LED_OFF;
  59                delay = timer_data->delay_off;
  60        }
  61
  62        led_set_brightness(led_cdev, brightness);
  63
  64        mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay));
  65}
  66
  67static ssize_t led_delay_on_show(struct device *dev,
  68                struct device_attribute *attr, char *buf)
  69{
  70        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  71        struct timer_trig_data *timer_data = led_cdev->trigger_data;
  72
  73        return sprintf(buf, "%lu\n", timer_data->delay_on);
  74}
  75
  76static ssize_t led_delay_on_store(struct device *dev,
  77                struct device_attribute *attr, const char *buf, size_t size)
  78{
  79        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  80        struct timer_trig_data *timer_data = led_cdev->trigger_data;
  81        int ret = -EINVAL;
  82        char *after;
  83        unsigned long state = simple_strtoul(buf, &after, 10);
  84        size_t count = after - buf;
  85
  86        if (*after && isspace(*after))
  87                count++;
  88
  89        if (count == size) {
  90                if (timer_data->delay_on != state) {
  91                        /* the new value differs from the previous */
  92                        timer_data->delay_on = state;
  93
  94                        /* deactivate previous settings */
  95                        del_timer_sync(&timer_data->timer);
  96
  97                        /* try to activate hardware acceleration, if any */
  98                        if (!led_cdev->blink_set ||
  99                            led_cdev->blink_set(led_cdev,
 100                              &timer_data->delay_on, &timer_data->delay_off)) {
 101                                /* no hardware acceleration, blink via timer */
 102                                mod_timer(&timer_data->timer, jiffies + 1);
 103                        }
 104                }
 105                ret = count;
 106        }
 107
 108        return ret;
 109}
 110
 111static ssize_t led_delay_off_show(struct device *dev,
 112                struct device_attribute *attr, char *buf)
 113{
 114        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 115        struct timer_trig_data *timer_data = led_cdev->trigger_data;
 116
 117        return sprintf(buf, "%lu\n", timer_data->delay_off);
 118}
 119
 120static ssize_t led_delay_off_store(struct device *dev,
 121                struct device_attribute *attr, const char *buf, size_t size)
 122{
 123        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 124        struct timer_trig_data *timer_data = led_cdev->trigger_data;
 125        int ret = -EINVAL;
 126        char *after;
 127        unsigned long state = simple_strtoul(buf, &after, 10);
 128        size_t count = after - buf;
 129
 130        if (*after && isspace(*after))
 131                count++;
 132
 133        if (count == size) {
 134                if (timer_data->delay_off != state) {
 135                        /* the new value differs from the previous */
 136                        timer_data->delay_off = state;
 137
 138                        /* deactivate previous settings */
 139                        del_timer_sync(&timer_data->timer);
 140
 141                        /* try to activate hardware acceleration, if any */
 142                        if (!led_cdev->blink_set ||
 143                            led_cdev->blink_set(led_cdev,
 144                              &timer_data->delay_on, &timer_data->delay_off)) {
 145                                /* no hardware acceleration, blink via timer */
 146                                mod_timer(&timer_data->timer, jiffies + 1);
 147                        }
 148                }
 149                ret = count;
 150        }
 151
 152        return ret;
 153}
 154
 155static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
 156static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);
 157
 158static void timer_trig_activate(struct led_classdev *led_cdev)
 159{
 160        struct timer_trig_data *timer_data;
 161        int rc;
 162
 163        timer_data = kzalloc(sizeof(struct timer_trig_data), GFP_KERNEL);
 164        if (!timer_data)
 165                return;
 166
 167        timer_data->brightness_on = led_get_brightness(led_cdev);
 168        if (timer_data->brightness_on == LED_OFF)
 169                timer_data->brightness_on = led_cdev->max_brightness;
 170        led_cdev->trigger_data = timer_data;
 171
 172        init_timer(&timer_data->timer);
 173        timer_data->timer.function = led_timer_function;
 174        timer_data->timer.data = (unsigned long) led_cdev;
 175
 176        rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
 177        if (rc)
 178                goto err_out;
 179        rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
 180        if (rc)
 181                goto err_out_delayon;
 182
 183        /* If there is hardware support for blinking, start one
 184         * user friendly blink rate chosen by the driver.
 185         */
 186        if (led_cdev->blink_set)
 187                led_cdev->blink_set(led_cdev,
 188                        &timer_data->delay_on, &timer_data->delay_off);
 189
 190        return;
 191
 192err_out_delayon:
 193        device_remove_file(led_cdev->dev, &dev_attr_delay_on);
 194err_out:
 195        led_cdev->trigger_data = NULL;
 196        kfree(timer_data);
 197}
 198
 199static void timer_trig_deactivate(struct led_classdev *led_cdev)
 200{
 201        struct timer_trig_data *timer_data = led_cdev->trigger_data;
 202        unsigned long on = 0, off = 0;
 203
 204        if (timer_data) {
 205                device_remove_file(led_cdev->dev, &dev_attr_delay_on);
 206                device_remove_file(led_cdev->dev, &dev_attr_delay_off);
 207                del_timer_sync(&timer_data->timer);
 208                kfree(timer_data);
 209        }
 210
 211        /* If there is hardware support for blinking, stop it */
 212        if (led_cdev->blink_set)
 213                led_cdev->blink_set(led_cdev, &on, &off);
 214}
 215
 216static struct led_trigger timer_led_trigger = {
 217        .name     = "timer",
 218        .activate = timer_trig_activate,
 219        .deactivate = timer_trig_deactivate,
 220};
 221
 222static int __init timer_trig_init(void)
 223{
 224        return led_trigger_register(&timer_led_trigger);
 225}
 226
 227static void __exit timer_trig_exit(void)
 228{
 229        led_trigger_unregister(&timer_led_trigger);
 230}
 231
 232module_init(timer_trig_init);
 233module_exit(timer_trig_exit);
 234
 235MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
 236MODULE_DESCRIPTION("Timer LED trigger");
 237MODULE_LICENSE("GPL");
 238