ledtrig-transient.c (5377B)
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.rst 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 23struct 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 32static 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 42static 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 51static 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 95static 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 103static 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 119static 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 130static 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 149static DEVICE_ATTR(activate, 0644, transient_activate_show, 150 transient_activate_store); 151static DEVICE_ATTR(duration, 0644, transient_duration_show, 152 transient_duration_store); 153static DEVICE_ATTR(state, 0644, transient_state_show, transient_state_store); 154 155static struct attribute *transient_trig_attrs[] = { 156 &dev_attr_activate.attr, 157 &dev_attr_duration.attr, 158 &dev_attr_state.attr, 159 NULL 160}; 161ATTRIBUTE_GROUPS(transient_trig); 162 163static 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 179static 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 188static struct led_trigger transient_trigger = { 189 .name = "transient", 190 .activate = transient_trig_activate, 191 .deactivate = transient_trig_deactivate, 192 .groups = transient_trig_groups, 193}; 194module_led_trigger(transient_trigger); 195 196MODULE_AUTHOR("Shuah Khan <shuahkhan@gmail.com>"); 197MODULE_DESCRIPTION("Transient LED trigger"); 198MODULE_LICENSE("GPL v2");