aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/leds
diff options
context:
space:
mode:
authorShuah Khan <shuahkhan@gmail.com>2012-05-29 18:07:30 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-05-29 19:22:32 -0400
commit44e1e9f8e70506728b02a18e6d03599a6485d67f (patch)
treecb50e9faaf6017d44faf6bd5224e209df42aa9d8 /drivers/leds
parent49dca5aebfdeadd4bf27b6cb4c60392147dc35a4 (diff)
leds: add new transient trigger for one shot timer activation
The leds timer trigger does not currently have an interface to activate a one shot timer. The current support allows for setting two timers, one for specifying how long a state to be on, and the second for how long the state to be off. The delay_on value specifies the time period an LED should stay in on state, followed by a delay_off value that specifies how long the LED should stay in off state. The on and off cycle repeats until the trigger gets deactivated. There is no provision for one time activation to implement features that require an on or off state to be held just once and then stay in the original state forever. Without one shot timer interface, user space can still use timer trigger to set a timer to hold a state, however when user space application crashes or goes away without deactivating the timer, the hardware will be left in that state permanently. As a specific example of this use-case, let's look at vibrate feature on phones. Vibrate function on phones is implemented using PWM pins on SoC or PMIC. There is a need to activate one shot timer to control the vibrate feature, to prevent user space crashes leaving the phone in vibrate mode permanently causing the battery to drain. This trigger exports three properties, activate, state, and duration When transient trigger is activated these properties are set to default values. - duration allows setting timer value in msecs. The initial value is 0. - activate allows activating and deactivating the timer specified by duration as needed. The initial and default value is 0. This will allow duration to be set after trigger activation. - state allows user to specify a transient state to be held for the specified duration. Signed-off-by: Shuah Khan <shuahkhan@gmail.com> Cc: Jonas Bonn <jonas@southpole.se> Cc: Richard Purdie <rpurdie@rpsys.net> Cc: NeilBrown <neilb@suse.de> Cc: Bryan Wu <bryan.wu@canonical.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/Kconfig8
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/ledtrig-transient.c237
3 files changed, 246 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index cede3397bb12..6b2e1e4fdeb8 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -479,4 +479,12 @@ config LEDS_TRIGGER_DEFAULT_ON
479comment "iptables trigger is under Netfilter config (LED target)" 479comment "iptables trigger is under Netfilter config (LED target)"
480 depends on LEDS_TRIGGERS 480 depends on LEDS_TRIGGERS
481 481
482config LEDS_TRIGGER_TRANSIENT
483 tristate "LED Transient Trigger"
484 depends on LEDS_TRIGGERS
485 help
486 This allows one time activation of a transient state on
487 GPIO/PWM based hadrware.
488 If unsure, say Y.
489
482endif # NEW_LEDS 490endif # NEW_LEDS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 900f9294bd8c..4a4b96e8c3eb 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -57,3 +57,4 @@ obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
57obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o 57obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o
58obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o 58obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o
59obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o 59obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
60obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o
diff --git a/drivers/leds/ledtrig-transient.c b/drivers/leds/ledtrig-transient.c
new file mode 100644
index 000000000000..83179f435e1e
--- /dev/null
+++ b/drivers/leds/ledtrig-transient.c
@@ -0,0 +1,237 @@
1/*
2 * LED Kernel Transient Trigger
3 *
4 * Copyright (C) 2012 Shuah Khan <shuahkhan@gmail.com>
5 *
6 * Based on Richard Purdie's ledtrig-timer.c and Atsushi Nemoto's
7 * ledtrig-heartbeat.c
8 * Design and use-case input from Jonas Bonn <jonas@southpole.se> and
9 * Neil Brown <neilb@suse.de>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 *
15 */
16/*
17 * Transient trigger allows one shot timer activation. Please refer to
18 * Documentation/leds/ledtrig-transient.txt for details
19*/
20
21#include <linux/module.h>
22#include <linux/kernel.h>
23#include <linux/init.h>
24#include <linux/device.h>
25#include <linux/slab.h>
26#include <linux/timer.h>
27#include <linux/leds.h>
28#include "leds.h"
29
30struct transient_trig_data {
31 int activate;
32 int state;
33 int restore_state;
34 unsigned long duration;
35 struct timer_list timer;
36};
37
38static void transient_timer_function(unsigned long data)
39{
40 struct led_classdev *led_cdev = (struct led_classdev *) data;
41 struct transient_trig_data *transient_data = led_cdev->trigger_data;
42
43 transient_data->activate = 0;
44 led_set_brightness(led_cdev, transient_data->restore_state);
45}
46
47static ssize_t transient_activate_show(struct device *dev,
48 struct device_attribute *attr, char *buf)
49{
50 struct led_classdev *led_cdev = dev_get_drvdata(dev);
51 struct transient_trig_data *transient_data = led_cdev->trigger_data;
52
53 return sprintf(buf, "%d\n", transient_data->activate);
54}
55
56static ssize_t transient_activate_store(struct device *dev,
57 struct device_attribute *attr, const char *buf, size_t size)
58{
59 struct led_classdev *led_cdev = dev_get_drvdata(dev);
60 struct transient_trig_data *transient_data = led_cdev->trigger_data;
61 unsigned long state;
62 ssize_t ret;
63
64 ret = kstrtoul(buf, 10, &state);
65 if (ret)
66 return ret;
67
68 if (state != 1 && state != 0)
69 return -EINVAL;
70
71 /* cancel the running timer */
72 if (state == 0 && transient_data->activate == 1) {
73 del_timer(&transient_data->timer);
74 transient_data->activate = state;
75 led_set_brightness(led_cdev, transient_data->restore_state);
76 return size;
77 }
78
79 /* start timer if there is no active timer */
80 if (state == 1 && transient_data->activate == 0 &&
81 transient_data->duration != 0) {
82 transient_data->activate = state;
83 led_set_brightness(led_cdev, transient_data->state);
84 transient_data->restore_state =
85 (transient_data->state == LED_FULL) ? LED_OFF : LED_FULL;
86 mod_timer(&transient_data->timer,
87 jiffies + transient_data->duration);
88 }
89
90 /* state == 0 && transient_data->activate == 0
91 timer is not active - just return */
92 /* state == 1 && transient_data->activate == 1
93 timer is already active - just return */
94
95 return size;
96}
97
98static ssize_t transient_duration_show(struct device *dev,
99 struct device_attribute *attr, char *buf)
100{
101 struct led_classdev *led_cdev = dev_get_drvdata(dev);
102 struct transient_trig_data *transient_data = led_cdev->trigger_data;
103
104 return sprintf(buf, "%lu\n", transient_data->duration);
105}
106
107static ssize_t transient_duration_store(struct device *dev,
108 struct device_attribute *attr, const char *buf, size_t size)
109{
110 struct led_classdev *led_cdev = dev_get_drvdata(dev);
111 struct transient_trig_data *transient_data = led_cdev->trigger_data;
112 unsigned long state;
113 ssize_t ret;
114
115 ret = kstrtoul(buf, 10, &state);
116 if (ret)
117 return ret;
118
119 transient_data->duration = state;
120 return size;
121}
122
123static ssize_t transient_state_show(struct device *dev,
124 struct device_attribute *attr, char *buf)
125{
126 struct led_classdev *led_cdev = dev_get_drvdata(dev);
127 struct transient_trig_data *transient_data = led_cdev->trigger_data;
128 int state;
129
130 state = (transient_data->state == LED_FULL) ? 1 : 0;
131 return sprintf(buf, "%d\n", state);
132}
133
134static ssize_t transient_state_store(struct device *dev,
135 struct device_attribute *attr, const char *buf, size_t size)
136{
137 struct led_classdev *led_cdev = dev_get_drvdata(dev);
138 struct transient_trig_data *transient_data = led_cdev->trigger_data;
139 unsigned long state;
140 ssize_t ret;
141
142 ret = kstrtoul(buf, 10, &state);
143 if (ret)
144 return ret;
145
146 if (state != 1 && state != 0)
147 return -EINVAL;
148
149 transient_data->state = (state == 1) ? LED_FULL : LED_OFF;
150 return size;
151}
152
153static DEVICE_ATTR(activate, 0644, transient_activate_show,
154 transient_activate_store);
155static DEVICE_ATTR(duration, 0644, transient_duration_show,
156 transient_duration_store);
157static DEVICE_ATTR(state, 0644, transient_state_show, transient_state_store);
158
159static void transient_trig_activate(struct led_classdev *led_cdev)
160{
161 int rc;
162 struct transient_trig_data *tdata;
163
164 tdata = kzalloc(sizeof(struct transient_trig_data), GFP_KERNEL);
165 if (!tdata) {
166 dev_err(led_cdev->dev,
167 "unable to allocate transient trigger\n");
168 return;
169 }
170 led_cdev->trigger_data = tdata;
171
172 rc = device_create_file(led_cdev->dev, &dev_attr_activate);
173 if (rc)
174 goto err_out;
175
176 rc = device_create_file(led_cdev->dev, &dev_attr_duration);
177 if (rc)
178 goto err_out_duration;
179
180 rc = device_create_file(led_cdev->dev, &dev_attr_state);
181 if (rc)
182 goto err_out_state;
183
184 setup_timer(&tdata->timer, transient_timer_function,
185 (unsigned long) led_cdev);
186 led_cdev->activated = true;
187
188 return;
189
190err_out_state:
191 device_remove_file(led_cdev->dev, &dev_attr_duration);
192err_out_duration:
193 device_remove_file(led_cdev->dev, &dev_attr_activate);
194err_out:
195 dev_err(led_cdev->dev, "unable to register transient trigger\n");
196 led_cdev->trigger_data = NULL;
197 kfree(tdata);
198}
199
200static void transient_trig_deactivate(struct led_classdev *led_cdev)
201{
202 struct transient_trig_data *transient_data = led_cdev->trigger_data;
203
204 if (led_cdev->activated) {
205 del_timer_sync(&transient_data->timer);
206 led_set_brightness(led_cdev, transient_data->restore_state);
207 device_remove_file(led_cdev->dev, &dev_attr_activate);
208 device_remove_file(led_cdev->dev, &dev_attr_duration);
209 device_remove_file(led_cdev->dev, &dev_attr_state);
210 led_cdev->trigger_data = NULL;
211 led_cdev->activated = false;
212 kfree(transient_data);
213 }
214}
215
216static struct led_trigger transient_trigger = {
217 .name = "transient",
218 .activate = transient_trig_activate,
219 .deactivate = transient_trig_deactivate,
220};
221
222static int __init transient_trig_init(void)
223{
224 return led_trigger_register(&transient_trigger);
225}
226
227static void __exit transient_trig_exit(void)
228{
229 led_trigger_unregister(&transient_trigger);
230}
231
232module_init(transient_trig_init);
233module_exit(transient_trig_exit);
234
235MODULE_AUTHOR("Shuah Khan <shuahkhan@gmail.com>");
236MODULE_DESCRIPTION("Transient LED trigger");
237MODULE_LICENSE("GPL");