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 }; 37 38 static 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 47 static 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 56 static 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 98 static 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 107 static 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 123 static 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 134 static 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 153 static DEVICE_ATTR(activate, 0644, transient_activate_show, 154 transient_activate_store); 155 static DEVICE_ATTR(duration, 0644, transient_duration_show, 156 transient_duration_store); 157 static DEVICE_ATTR(state, 0644, transient_state_show, transient_state_store); 158 159 static 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 190 err_out_state: 191 device_remove_file(led_cdev->dev, &dev_attr_duration); 192 err_out_duration: 193 device_remove_file(led_cdev->dev, &dev_attr_activate); 194 err_out: 195 dev_err(led_cdev->dev, "unable to register transient trigger\n"); 196 led_cdev->trigger_data = NULL; 197 kfree(tdata); 198 } 199 200 static 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 216 static struct led_trigger transient_trigger = { 217 .name = "transient", 218 .activate = transient_trig_activate, 219 .deactivate = transient_trig_deactivate, 220 }; 221 222 static int __init transient_trig_init(void) 223 { 224 return led_trigger_register(&transient_trigger); 225 } 226 227 static void __exit transient_trig_exit(void) 228 { 229 led_trigger_unregister(&transient_trigger); 230 } 231 232 module_init(transient_trig_init); 233 module_exit(transient_trig_exit); 234 235 MODULE_AUTHOR("Shuah Khan <shuahkhan@gmail.com>"); 236 MODULE_DESCRIPTION("Transient LED trigger"); 237 MODULE_LICENSE("GPL"); 238