Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ledtrig-transient.c
Go to the documentation of this file.
1 /*
2  * LED Kernel Transient Trigger
3  *
4  * Copyright (C) 2012 Shuah Khan <[email protected]>
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 <[email protected]> and
9  * Neil Brown <[email protected]>
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 
31  int activate;
32  int state;
34  unsigned long duration;
35  struct timer_list timer;
36 };
37 
38 static 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 
47 static 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 
56 static 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 
98 static 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 
107 static 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 
123 static 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 
134 static 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 
153 static DEVICE_ATTR(activate, 0644, transient_activate_show,
154  transient_activate_store);
155 static DEVICE_ATTR(duration, 0644, transient_duration_show,
156  transient_duration_store);
157 static DEVICE_ATTR(state, 0644, transient_state_show, transient_state_store);
158 
159 static 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 
190 err_out_state:
191  device_remove_file(led_cdev->dev, &dev_attr_duration);
192 err_out_duration:
193  device_remove_file(led_cdev->dev, &dev_attr_activate);
194 err_out:
195  dev_err(led_cdev->dev, "unable to register transient trigger\n");
196  led_cdev->trigger_data = NULL;
197  kfree(tdata);
198 }
199 
200 static 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 
216 static struct led_trigger transient_trigger = {
217  .name = "transient",
218  .activate = transient_trig_activate,
219  .deactivate = transient_trig_deactivate,
220 };
221 
222 static int __init transient_trig_init(void)
223 {
224  return led_trigger_register(&transient_trigger);
225 }
226 
227 static void __exit transient_trig_exit(void)
228 {
229  led_trigger_unregister(&transient_trigger);
230 }
231 
232 module_init(transient_trig_init);
233 module_exit(transient_trig_exit);
234 
235 MODULE_AUTHOR("Shuah Khan <[email protected]>");
236 MODULE_DESCRIPTION("Transient LED trigger");
237 MODULE_LICENSE("GPL");