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_async(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_async(led_cdev, 76 transient_data->restore_state); 77 return size; 78 } 79 80 /* start timer if there is no active timer */ 81 if (state == 1 && transient_data->activate == 0 && 82 transient_data->duration != 0) { 83 transient_data->activate = state; 84 led_set_brightness_async(led_cdev, transient_data->state); 85 transient_data->restore_state = 86 (transient_data->state == LED_FULL) ? LED_OFF : LED_FULL; 87 mod_timer(&transient_data->timer, 88 jiffies + transient_data->duration); 89 } 90 91 /* state == 0 && transient_data->activate == 0 92 timer is not active - just return */ 93 /* state == 1 && transient_data->activate == 1 94 timer is already active - just return */ 95 96 return size; 97} 98 99static ssize_t transient_duration_show(struct device *dev, 100 struct device_attribute *attr, char *buf) 101{ 102 struct led_classdev *led_cdev = dev_get_drvdata(dev); 103 struct transient_trig_data *transient_data = led_cdev->trigger_data; 104 105 return sprintf(buf, "%lu\n", transient_data->duration); 106} 107 108static ssize_t transient_duration_store(struct device *dev, 109 struct device_attribute *attr, const char *buf, size_t size) 110{ 111 struct led_classdev *led_cdev = dev_get_drvdata(dev); 112 struct transient_trig_data *transient_data = led_cdev->trigger_data; 113 unsigned long state; 114 ssize_t ret; 115 116 ret = kstrtoul(buf, 10, &state); 117 if (ret) 118 return ret; 119 120 transient_data->duration = state; 121 return size; 122} 123 124static ssize_t transient_state_show(struct device *dev, 125 struct device_attribute *attr, char *buf) 126{ 127 struct led_classdev *led_cdev = dev_get_drvdata(dev); 128 struct transient_trig_data *transient_data = led_cdev->trigger_data; 129 int state; 130 131 state = (transient_data->state == LED_FULL) ? 1 : 0; 132 return sprintf(buf, "%d\n", state); 133} 134 135static ssize_t transient_state_store(struct device *dev, 136 struct device_attribute *attr, const char *buf, size_t size) 137{ 138 struct led_classdev *led_cdev = dev_get_drvdata(dev); 139 struct transient_trig_data *transient_data = led_cdev->trigger_data; 140 unsigned long state; 141 ssize_t ret; 142 143 ret = kstrtoul(buf, 10, &state); 144 if (ret) 145 return ret; 146 147 if (state != 1 && state != 0) 148 return -EINVAL; 149 150 transient_data->state = (state == 1) ? LED_FULL : LED_OFF; 151 return size; 152} 153 154static DEVICE_ATTR(activate, 0644, transient_activate_show, 155 transient_activate_store); 156static DEVICE_ATTR(duration, 0644, transient_duration_show, 157 transient_duration_store); 158static DEVICE_ATTR(state, 0644, transient_state_show, transient_state_store); 159 160static void transient_trig_activate(struct led_classdev *led_cdev) 161{ 162 int rc; 163 struct transient_trig_data *tdata; 164 165 tdata = kzalloc(sizeof(struct transient_trig_data), GFP_KERNEL); 166 if (!tdata) { 167 dev_err(led_cdev->dev, 168 "unable to allocate transient trigger\n"); 169 return; 170 } 171 led_cdev->trigger_data = tdata; 172 173 rc = device_create_file(led_cdev->dev, &dev_attr_activate); 174 if (rc) 175 goto err_out; 176 177 rc = device_create_file(led_cdev->dev, &dev_attr_duration); 178 if (rc) 179 goto err_out_duration; 180 181 rc = device_create_file(led_cdev->dev, &dev_attr_state); 182 if (rc) 183 goto err_out_state; 184 185 setup_timer(&tdata->timer, transient_timer_function, 186 (unsigned long) led_cdev); 187 led_cdev->activated = true; 188 189 return; 190 191err_out_state: 192 device_remove_file(led_cdev->dev, &dev_attr_duration); 193err_out_duration: 194 device_remove_file(led_cdev->dev, &dev_attr_activate); 195err_out: 196 dev_err(led_cdev->dev, "unable to register transient trigger\n"); 197 led_cdev->trigger_data = NULL; 198 kfree(tdata); 199} 200 201static void transient_trig_deactivate(struct led_classdev *led_cdev) 202{ 203 struct transient_trig_data *transient_data = led_cdev->trigger_data; 204 205 if (led_cdev->activated) { 206 del_timer_sync(&transient_data->timer); 207 led_set_brightness_async(led_cdev, 208 transient_data->restore_state); 209 device_remove_file(led_cdev->dev, &dev_attr_activate); 210 device_remove_file(led_cdev->dev, &dev_attr_duration); 211 device_remove_file(led_cdev->dev, &dev_attr_state); 212 led_cdev->trigger_data = NULL; 213 led_cdev->activated = false; 214 kfree(transient_data); 215 } 216} 217 218static struct led_trigger transient_trigger = { 219 .name = "transient", 220 .activate = transient_trig_activate, 221 .deactivate = transient_trig_deactivate, 222}; 223 224static int __init transient_trig_init(void) 225{ 226 return led_trigger_register(&transient_trigger); 227} 228 229static void __exit transient_trig_exit(void) 230{ 231 led_trigger_unregister(&transient_trigger); 232} 233 234module_init(transient_trig_init); 235module_exit(transient_trig_exit); 236 237MODULE_AUTHOR("Shuah Khan <shuahkhan@gmail.com>"); 238MODULE_DESCRIPTION("Transient LED trigger"); 239MODULE_LICENSE("GPL"); 240