Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // LED Kernel Transient Trigger
0004 //
0005 // Transient trigger allows one shot timer activation. Please refer to
0006 // Documentation/leds/ledtrig-transient.rst for details
0007 // Copyright (C) 2012 Shuah Khan <shuahkhan@gmail.com>
0008 //
0009 // Based on Richard Purdie's ledtrig-timer.c and Atsushi Nemoto's
0010 // ledtrig-heartbeat.c
0011 // Design and use-case input from Jonas Bonn <jonas@southpole.se> and
0012 // Neil Brown <neilb@suse.de>
0013 
0014 #include <linux/module.h>
0015 #include <linux/kernel.h>
0016 #include <linux/init.h>
0017 #include <linux/device.h>
0018 #include <linux/slab.h>
0019 #include <linux/timer.h>
0020 #include <linux/leds.h>
0021 #include "../leds.h"
0022 
0023 struct transient_trig_data {
0024     int activate;
0025     int state;
0026     int restore_state;
0027     unsigned long duration;
0028     struct timer_list timer;
0029     struct led_classdev *led_cdev;
0030 };
0031 
0032 static void transient_timer_function(struct timer_list *t)
0033 {
0034     struct transient_trig_data *transient_data =
0035         from_timer(transient_data, t, timer);
0036     struct led_classdev *led_cdev = transient_data->led_cdev;
0037 
0038     transient_data->activate = 0;
0039     led_set_brightness_nosleep(led_cdev, transient_data->restore_state);
0040 }
0041 
0042 static ssize_t transient_activate_show(struct device *dev,
0043         struct device_attribute *attr, char *buf)
0044 {
0045     struct transient_trig_data *transient_data =
0046         led_trigger_get_drvdata(dev);
0047 
0048     return sprintf(buf, "%d\n", transient_data->activate);
0049 }
0050 
0051 static ssize_t transient_activate_store(struct device *dev,
0052         struct device_attribute *attr, const char *buf, size_t size)
0053 {
0054     struct led_classdev *led_cdev = led_trigger_get_led(dev);
0055     struct transient_trig_data *transient_data =
0056         led_trigger_get_drvdata(dev);
0057     unsigned long state;
0058     ssize_t ret;
0059 
0060     ret = kstrtoul(buf, 10, &state);
0061     if (ret)
0062         return ret;
0063 
0064     if (state != 1 && state != 0)
0065         return -EINVAL;
0066 
0067     /* cancel the running timer */
0068     if (state == 0 && transient_data->activate == 1) {
0069         del_timer(&transient_data->timer);
0070         transient_data->activate = state;
0071         led_set_brightness_nosleep(led_cdev,
0072                     transient_data->restore_state);
0073         return size;
0074     }
0075 
0076     /* start timer if there is no active timer */
0077     if (state == 1 && transient_data->activate == 0 &&
0078         transient_data->duration != 0) {
0079         transient_data->activate = state;
0080         led_set_brightness_nosleep(led_cdev, transient_data->state);
0081         transient_data->restore_state =
0082             (transient_data->state == LED_FULL) ? LED_OFF : LED_FULL;
0083         mod_timer(&transient_data->timer,
0084               jiffies + msecs_to_jiffies(transient_data->duration));
0085     }
0086 
0087     /* state == 0 && transient_data->activate == 0
0088         timer is not active - just return */
0089     /* state == 1 && transient_data->activate == 1
0090         timer is already active - just return */
0091 
0092     return size;
0093 }
0094 
0095 static ssize_t transient_duration_show(struct device *dev,
0096         struct device_attribute *attr, char *buf)
0097 {
0098     struct transient_trig_data *transient_data = led_trigger_get_drvdata(dev);
0099 
0100     return sprintf(buf, "%lu\n", transient_data->duration);
0101 }
0102 
0103 static ssize_t transient_duration_store(struct device *dev,
0104         struct device_attribute *attr, const char *buf, size_t size)
0105 {
0106     struct transient_trig_data *transient_data =
0107         led_trigger_get_drvdata(dev);
0108     unsigned long state;
0109     ssize_t ret;
0110 
0111     ret = kstrtoul(buf, 10, &state);
0112     if (ret)
0113         return ret;
0114 
0115     transient_data->duration = state;
0116     return size;
0117 }
0118 
0119 static ssize_t transient_state_show(struct device *dev,
0120         struct device_attribute *attr, char *buf)
0121 {
0122     struct transient_trig_data *transient_data =
0123         led_trigger_get_drvdata(dev);
0124     int state;
0125 
0126     state = (transient_data->state == LED_FULL) ? 1 : 0;
0127     return sprintf(buf, "%d\n", state);
0128 }
0129 
0130 static ssize_t transient_state_store(struct device *dev,
0131         struct device_attribute *attr, const char *buf, size_t size)
0132 {
0133     struct transient_trig_data *transient_data =
0134         led_trigger_get_drvdata(dev);
0135     unsigned long state;
0136     ssize_t ret;
0137 
0138     ret = kstrtoul(buf, 10, &state);
0139     if (ret)
0140         return ret;
0141 
0142     if (state != 1 && state != 0)
0143         return -EINVAL;
0144 
0145     transient_data->state = (state == 1) ? LED_FULL : LED_OFF;
0146     return size;
0147 }
0148 
0149 static DEVICE_ATTR(activate, 0644, transient_activate_show,
0150            transient_activate_store);
0151 static DEVICE_ATTR(duration, 0644, transient_duration_show,
0152            transient_duration_store);
0153 static DEVICE_ATTR(state, 0644, transient_state_show, transient_state_store);
0154 
0155 static struct attribute *transient_trig_attrs[] = {
0156     &dev_attr_activate.attr,
0157     &dev_attr_duration.attr,
0158     &dev_attr_state.attr,
0159     NULL
0160 };
0161 ATTRIBUTE_GROUPS(transient_trig);
0162 
0163 static int transient_trig_activate(struct led_classdev *led_cdev)
0164 {
0165     struct transient_trig_data *tdata;
0166 
0167     tdata = kzalloc(sizeof(struct transient_trig_data), GFP_KERNEL);
0168     if (!tdata)
0169         return -ENOMEM;
0170 
0171     led_set_trigger_data(led_cdev, tdata);
0172     tdata->led_cdev = led_cdev;
0173 
0174     timer_setup(&tdata->timer, transient_timer_function, 0);
0175 
0176     return 0;
0177 }
0178 
0179 static void transient_trig_deactivate(struct led_classdev *led_cdev)
0180 {
0181     struct transient_trig_data *transient_data = led_get_trigger_data(led_cdev);
0182 
0183     del_timer_sync(&transient_data->timer);
0184     led_set_brightness_nosleep(led_cdev, transient_data->restore_state);
0185     kfree(transient_data);
0186 }
0187 
0188 static struct led_trigger transient_trigger = {
0189     .name     = "transient",
0190     .activate = transient_trig_activate,
0191     .deactivate = transient_trig_deactivate,
0192     .groups = transient_trig_groups,
0193 };
0194 module_led_trigger(transient_trigger);
0195 
0196 MODULE_AUTHOR("Shuah Khan <shuahkhan@gmail.com>");
0197 MODULE_DESCRIPTION("Transient LED trigger");
0198 MODULE_LICENSE("GPL v2");