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 transient_trig_data *transient_data = 46 led_trigger_get_drvdata(dev); 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 = led_trigger_get_led(dev); 55 struct transient_trig_data *transient_data = 56 led_trigger_get_drvdata(dev); 57 unsigned long state; 58 ssize_t ret; 59 60 ret = kstrtoul(buf, 10, &state); 61 if (ret) 62 return ret; 63 64 if (state != 1 && state != 0) 65 return -EINVAL; 66 67 /* cancel the running timer */ 68 if (state == 0 && transient_data->activate == 1) { 69 del_timer(&transient_data->timer); 70 transient_data->activate = state; 71 led_set_brightness_nosleep(led_cdev, 72 transient_data->restore_state); 73 return size; 74 } 75 76 /* start timer if there is no active timer */ 77 if (state == 1 && transient_data->activate == 0 && 78 transient_data->duration != 0) { 79 transient_data->activate = state; 80 led_set_brightness_nosleep(led_cdev, transient_data->state); 81 transient_data->restore_state = 82 (transient_data->state == LED_FULL) ? LED_OFF : LED_FULL; 83 mod_timer(&transient_data->timer, 84 jiffies + msecs_to_jiffies(transient_data->duration)); 85 } 86 87 /* state == 0 && transient_data->activate == 0 88 timer is not active - just return */ 89 /* state == 1 && transient_data->activate == 1 90 timer is already active - just return */ 91 92 return size; 93 } 94 95 static ssize_t transient_duration_show(struct device *dev, 96 struct device_attribute *attr, char *buf) 97 { 98 struct transient_trig_data *transient_data = led_trigger_get_drvdata(dev); 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 transient_trig_data *transient_data = 107 led_trigger_get_drvdata(dev); 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 transient_trig_data *transient_data = 123 led_trigger_get_drvdata(dev); 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 transient_trig_data *transient_data = 134 led_trigger_get_drvdata(dev); 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 struct attribute *transient_trig_attrs[] = { 156 &dev_attr_activate.attr, 157 &dev_attr_duration.attr, 158 &dev_attr_state.attr, 159 NULL 160 }; 161 ATTRIBUTE_GROUPS(transient_trig); 162 163 static int transient_trig_activate(struct led_classdev *led_cdev) 164 { 165 struct transient_trig_data *tdata; 166 167 tdata = kzalloc(sizeof(struct transient_trig_data), GFP_KERNEL); 168 if (!tdata) 169 return -ENOMEM; 170 171 led_set_trigger_data(led_cdev, tdata); 172 tdata->led_cdev = led_cdev; 173 174 timer_setup(&tdata->timer, transient_timer_function, 0); 175 176 return 0; 177 } 178 179 static void transient_trig_deactivate(struct led_classdev *led_cdev) 180 { 181 struct transient_trig_data *transient_data = led_get_trigger_data(led_cdev); 182 183 del_timer_sync(&transient_data->timer); 184 led_set_brightness_nosleep(led_cdev, transient_data->restore_state); 185 kfree(transient_data); 186 } 187 188 static struct led_trigger transient_trigger = { 189 .name = "transient", 190 .activate = transient_trig_activate, 191 .deactivate = transient_trig_deactivate, 192 .groups = transient_trig_groups, 193 }; 194 module_led_trigger(transient_trigger); 195 196 MODULE_AUTHOR("Shuah Khan <shuahkhan@gmail.com>"); 197 MODULE_DESCRIPTION("Transient LED trigger"); 198 MODULE_LICENSE("GPL v2"); 199