Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.32-rc3 237 lines 6.4 kB view raw
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");