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