1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // LED Kernel Transient Trigger 4 // 5 // Transient trigger allows one shot timer activation. Please refer to 6 // Documentation/leds/ledtrig-transient.txt for details 7 // Copyright (C) 2012 Shuah Khan <shuahkhan@gmail.com> 8 // 9 // Based on Richard Purdie's ledtrig-timer.c and Atsushi Nemoto's 10 // ledtrig-heartbeat.c 11 // Design and use-case input from Jonas Bonn <jonas@southpole.se> and 12 // Neil Brown <neilb@suse.de> 13 14 #include <linux/module.h> 15 #include <linux/kernel.h> 16 #include <linux/init.h> 17 #include <linux/device.h> 18 #include <linux/slab.h> 19 #include <linux/timer.h> 20 #include <linux/leds.h> 21 #include "../leds.h" 22 23 struct transient_trig_data { 24 int activate; 25 int state; 26 int restore_state; 27 unsigned long duration; 28 struct timer_list timer; 29 struct led_classdev *led_cdev; 30 }; 31 32 static void transient_timer_function(struct timer_list *t) 33 { 34 struct transient_trig_data *transient_data = 35 from_timer(transient_data, t, timer); 36 struct led_classdev *led_cdev = transient_data->led_cdev; 37 38 transient_data->activate = 0; 39 led_set_brightness_nosleep(led_cdev, transient_data->restore_state); 40 } 41 42 static ssize_t transient_activate_show(struct device *dev, 43 struct device_attribute *attr, char *buf) 44 { 45 struct led_classdev *led_cdev = dev_get_drvdata(dev); 46 struct transient_trig_data *transient_data = led_cdev->trigger_data; 47 48 return sprintf(buf, "%d\n", transient_data->activate); 49 } 50 51 static ssize_t transient_activate_store(struct device *dev, 52 struct device_attribute *attr, const char *buf, size_t size) 53 { 54 struct led_classdev *led_cdev = dev_get_drvdata(dev); 55 struct transient_trig_data *transient_data = led_cdev->trigger_data; 56 unsigned long state; 57 ssize_t ret; 58 59 ret = kstrtoul(buf, 10, &state); 60 if (ret) 61 return ret; 62 63 if (state != 1 && state != 0) 64 return -EINVAL; 65 66 /* cancel the running timer */ 67 if (state == 0 && transient_data->activate == 1) { 68 del_timer(&transient_data->timer); 69 transient_data->activate = state; 70 led_set_brightness_nosleep(led_cdev, 71 transient_data->restore_state); 72 return size; 73 } 74 75 /* start timer if there is no active timer */ 76 if (state == 1 && transient_data->activate == 0 && 77 transient_data->duration != 0) { 78 transient_data->activate = state; 79 led_set_brightness_nosleep(led_cdev, transient_data->state); 80 transient_data->restore_state = 81 (transient_data->state == LED_FULL) ? LED_OFF : LED_FULL; 82 mod_timer(&transient_data->timer, 83 jiffies + msecs_to_jiffies(transient_data->duration)); 84 } 85 86 /* state == 0 && transient_data->activate == 0 87 timer is not active - just return */ 88 /* state == 1 && transient_data->activate == 1 89 timer is already active - just return */ 90 91 return size; 92 } 93 94 static ssize_t transient_duration_show(struct device *dev, 95 struct device_attribute *attr, char *buf) 96 { 97 struct led_classdev *led_cdev = dev_get_drvdata(dev); 98 struct transient_trig_data *transient_data = led_cdev->trigger_data; 99 100 return sprintf(buf, "%lu\n", transient_data->duration); 101 } 102 103 static ssize_t transient_duration_store(struct device *dev, 104 struct device_attribute *attr, const char *buf, size_t size) 105 { 106 struct led_classdev *led_cdev = dev_get_drvdata(dev); 107 struct transient_trig_data *transient_data = led_cdev->trigger_data; 108 unsigned long state; 109 ssize_t ret; 110 111 ret = kstrtoul(buf, 10, &state); 112 if (ret) 113 return ret; 114 115 transient_data->duration = state; 116 return size; 117 } 118 119 static ssize_t transient_state_show(struct device *dev, 120 struct device_attribute *attr, char *buf) 121 { 122 struct led_classdev *led_cdev = dev_get_drvdata(dev); 123 struct transient_trig_data *transient_data = led_cdev->trigger_data; 124 int state; 125 126 state = (transient_data->state == LED_FULL) ? 1 : 0; 127 return sprintf(buf, "%d\n", state); 128 } 129 130 static ssize_t transient_state_store(struct device *dev, 131 struct device_attribute *attr, const char *buf, size_t size) 132 { 133 struct led_classdev *led_cdev = dev_get_drvdata(dev); 134 struct transient_trig_data *transient_data = led_cdev->trigger_data; 135 unsigned long state; 136 ssize_t ret; 137 138 ret = kstrtoul(buf, 10, &state); 139 if (ret) 140 return ret; 141 142 if (state != 1 && state != 0) 143 return -EINVAL; 144 145 transient_data->state = (state == 1) ? LED_FULL : LED_OFF; 146 return size; 147 } 148 149 static DEVICE_ATTR(activate, 0644, transient_activate_show, 150 transient_activate_store); 151 static DEVICE_ATTR(duration, 0644, transient_duration_show, 152 transient_duration_store); 153 static DEVICE_ATTR(state, 0644, transient_state_show, transient_state_store); 154 155 static void transient_trig_activate(struct led_classdev *led_cdev) 156 { 157 int rc; 158 struct transient_trig_data *tdata; 159 160 tdata = kzalloc(sizeof(struct transient_trig_data), GFP_KERNEL); 161 if (!tdata) { 162 dev_err(led_cdev->dev, 163 "unable to allocate transient trigger\n"); 164 return; 165 } 166 led_cdev->trigger_data = tdata; 167 tdata->led_cdev = led_cdev; 168 169 rc = device_create_file(led_cdev->dev, &dev_attr_activate); 170 if (rc) 171 goto err_out; 172 173 rc = device_create_file(led_cdev->dev, &dev_attr_duration); 174 if (rc) 175 goto err_out_duration; 176 177 rc = device_create_file(led_cdev->dev, &dev_attr_state); 178 if (rc) 179 goto err_out_state; 180 181 timer_setup(&tdata->timer, transient_timer_function, 0); 182 led_cdev->activated = true; 183 184 return; 185 186 err_out_state: 187 device_remove_file(led_cdev->dev, &dev_attr_duration); 188 err_out_duration: 189 device_remove_file(led_cdev->dev, &dev_attr_activate); 190 err_out: 191 dev_err(led_cdev->dev, "unable to register transient trigger\n"); 192 led_cdev->trigger_data = NULL; 193 kfree(tdata); 194 } 195 196 static void transient_trig_deactivate(struct led_classdev *led_cdev) 197 { 198 struct transient_trig_data *transient_data = led_cdev->trigger_data; 199 200 if (led_cdev->activated) { 201 del_timer_sync(&transient_data->timer); 202 led_set_brightness_nosleep(led_cdev, 203 transient_data->restore_state); 204 device_remove_file(led_cdev->dev, &dev_attr_activate); 205 device_remove_file(led_cdev->dev, &dev_attr_duration); 206 device_remove_file(led_cdev->dev, &dev_attr_state); 207 led_cdev->trigger_data = NULL; 208 led_cdev->activated = false; 209 kfree(transient_data); 210 } 211 } 212 213 static struct led_trigger transient_trigger = { 214 .name = "transient", 215 .activate = transient_trig_activate, 216 .deactivate = transient_trig_deactivate, 217 }; 218 219 static int __init transient_trig_init(void) 220 { 221 return led_trigger_register(&transient_trigger); 222 } 223 224 static void __exit transient_trig_exit(void) 225 { 226 led_trigger_unregister(&transient_trigger); 227 } 228 229 module_init(transient_trig_init); 230 module_exit(transient_trig_exit); 231 232 MODULE_AUTHOR("Shuah Khan <shuahkhan@gmail.com>"); 233 MODULE_DESCRIPTION("Transient LED trigger"); 234 MODULE_LICENSE("GPL v2"); 235