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