diff options
| author | Fabio Baltieri <fabio.baltieri@gmail.com> | 2012-06-06 18:11:05 -0400 |
|---|---|---|
| committer | Bryan Wu <bryan.wu@canonical.com> | 2012-07-23 19:52:34 -0400 |
| commit | 5e417281cde2ef56e9eb1a95d080d6254402e794 (patch) | |
| tree | 142bdedb0e099b96276c0e094bf89a10251a7bc4 | |
| parent | 437864828d82b9dee50b5741106fbf5fa12b139a (diff) | |
leds: add oneshot trigger
Add oneshot trigger to blink a led with configurale parameters via
sysfs.
Signed-off-by: Fabio Baltieri <fabio.baltieri@gmail.com>
Cc: Shuah Khan <shuahkhan@gmail.com>
Signed-off-by: Bryan Wu <bryan.wu@canonical.com>
| -rw-r--r-- | Documentation/leds/ledtrig-oneshot.txt | 59 | ||||
| -rw-r--r-- | drivers/leds/Kconfig | 14 | ||||
| -rw-r--r-- | drivers/leds/Makefile | 1 | ||||
| -rw-r--r-- | drivers/leds/ledtrig-oneshot.c | 204 |
4 files changed, 278 insertions, 0 deletions
diff --git a/Documentation/leds/ledtrig-oneshot.txt b/Documentation/leds/ledtrig-oneshot.txt new file mode 100644 index 000000000000..07cd1fa41a3a --- /dev/null +++ b/Documentation/leds/ledtrig-oneshot.txt | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | One-shot LED Trigger | ||
| 2 | ==================== | ||
| 3 | |||
| 4 | This is a LED trigger useful for signaling the user of an event where there are | ||
| 5 | no clear trap points to put standard led-on and led-off settings. Using this | ||
| 6 | trigger, the application needs only to signal the trigger when an event has | ||
| 7 | happened, than the trigger turns the LED on and than keeps it off for a | ||
| 8 | specified amount of time. | ||
| 9 | |||
| 10 | This trigger is meant to be usable both for sporadic and dense events. In the | ||
| 11 | first case, the trigger produces a clear single controlled blink for each | ||
| 12 | event, while in the latter it keeps blinking at constant rate, as to signal | ||
| 13 | that the events are arriving continuously. | ||
| 14 | |||
| 15 | A one-shot LED only stays in a constant state when there are no events. An | ||
| 16 | additional "invert" property specifies if the LED has to stay off (normal) or | ||
| 17 | on (inverted) when not rearmed. | ||
| 18 | |||
| 19 | The trigger can be activated from user space on led class devices as shown | ||
| 20 | below: | ||
| 21 | |||
| 22 | echo oneshot > trigger | ||
| 23 | |||
| 24 | This adds the following sysfs attributes to the LED: | ||
| 25 | |||
| 26 | delay_on - specifies for how many milliseconds the LED has to stay at | ||
| 27 | LED_FULL brightness after it has been armed. | ||
| 28 | Default to 100 ms. | ||
| 29 | |||
| 30 | delay_off - specifies for how many milliseconds the LED has to stay at | ||
| 31 | LED_OFF brightness after it has been armed. | ||
| 32 | Default to 100 ms. | ||
| 33 | |||
| 34 | invert - reverse the blink logic. If set to 0 (default) blink on for delay_on | ||
| 35 | ms, then blink off for delay_off ms, leaving the LED normally off. If | ||
| 36 | set to 1, blink off for delay_off ms, then blink on for delay_on ms, | ||
| 37 | leaving the LED normally on. | ||
| 38 | Setting this value also immediately change the LED state. | ||
| 39 | |||
| 40 | shot - write any non-empty string to signal an events, this starts a blink | ||
| 41 | sequence if not already running. | ||
| 42 | |||
| 43 | Example use-case: network devices, initialization: | ||
| 44 | |||
| 45 | echo oneshot > trigger # set trigger for this led | ||
| 46 | echo 33 > delay_on # blink at 1 / (33 + 33) Hz on continuous traffic | ||
| 47 | echo 33 > delay_off | ||
| 48 | |||
| 49 | interface goes up: | ||
| 50 | |||
| 51 | echo 1 > invert # set led as normally-on, turn the led on | ||
| 52 | |||
| 53 | packet received/transmitted: | ||
| 54 | |||
| 55 | echo 1 > shot # led starts blinking, ignored if already blinking | ||
| 56 | |||
| 57 | interface goes down | ||
| 58 | |||
| 59 | echo 0 > invert # set led as normally-off, turn the led off | ||
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 12b2b55c519e..54dd1a396fa9 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
| @@ -443,6 +443,20 @@ config LEDS_TRIGGER_TIMER | |||
| 443 | 443 | ||
| 444 | If unsure, say Y. | 444 | If unsure, say Y. |
| 445 | 445 | ||
| 446 | config LEDS_TRIGGER_ONESHOT | ||
| 447 | tristate "LED One-shot Trigger" | ||
| 448 | depends on LEDS_TRIGGERS | ||
| 449 | help | ||
| 450 | This allows LEDs to blink in one-shot pulses with parameters | ||
| 451 | controlled via sysfs. It's useful to notify the user on | ||
| 452 | sporadic events, when there are no clear begin and end trap points, | ||
| 453 | or on dense events, where this blinks the LED at constant rate if | ||
| 454 | rearmed continuously. | ||
| 455 | |||
| 456 | It also shows how to use the led_blink_set_oneshot() function. | ||
| 457 | |||
| 458 | If unsure, say Y. | ||
| 459 | |||
| 446 | config LEDS_TRIGGER_IDE_DISK | 460 | config LEDS_TRIGGER_IDE_DISK |
| 447 | bool "LED IDE Disk Trigger" | 461 | bool "LED IDE Disk Trigger" |
| 448 | depends on IDE_GD_ATA | 462 | depends on IDE_GD_ATA |
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index f8958cd6cf6e..9112d518f9d4 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
| @@ -53,6 +53,7 @@ obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o | |||
| 53 | 53 | ||
| 54 | # LED Triggers | 54 | # LED Triggers |
| 55 | obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o | 55 | obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o |
| 56 | obj-$(CONFIG_LEDS_TRIGGER_ONESHOT) += ledtrig-oneshot.o | ||
| 56 | obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o | 57 | obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o |
| 57 | obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o | 58 | obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o |
| 58 | obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o | 59 | obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o |
diff --git a/drivers/leds/ledtrig-oneshot.c b/drivers/leds/ledtrig-oneshot.c new file mode 100644 index 000000000000..5c9edf7f489c --- /dev/null +++ b/drivers/leds/ledtrig-oneshot.c | |||
| @@ -0,0 +1,204 @@ | |||
| 1 | /* | ||
| 2 | * One-shot LED Trigger | ||
| 3 | * | ||
| 4 | * Copyright 2012, Fabio Baltieri <fabio.baltieri@gmail.com> | ||
| 5 | * | ||
| 6 | * Based on ledtrig-timer.c by Richard Purdie <rpurdie@openedhand.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/kernel.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/device.h> | ||
| 18 | #include <linux/ctype.h> | ||
| 19 | #include <linux/slab.h> | ||
| 20 | #include <linux/leds.h> | ||
| 21 | #include "leds.h" | ||
| 22 | |||
| 23 | #define DEFAULT_DELAY 100 | ||
| 24 | |||
| 25 | struct oneshot_trig_data { | ||
| 26 | unsigned int invert; | ||
| 27 | }; | ||
| 28 | |||
| 29 | static ssize_t led_shot(struct device *dev, | ||
| 30 | struct device_attribute *attr, const char *buf, size_t size) | ||
| 31 | { | ||
| 32 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
| 33 | struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data; | ||
| 34 | |||
| 35 | led_blink_set_oneshot(led_cdev, | ||
| 36 | &led_cdev->blink_delay_on, &led_cdev->blink_delay_off, | ||
| 37 | oneshot_data->invert); | ||
| 38 | |||
| 39 | /* content is ignored */ | ||
| 40 | return size; | ||
| 41 | } | ||
| 42 | static ssize_t led_invert_show(struct device *dev, | ||
| 43 | struct device_attribute *attr, char *buf) | ||
| 44 | { | ||
| 45 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
| 46 | struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data; | ||
| 47 | |||
| 48 | return sprintf(buf, "%u\n", oneshot_data->invert); | ||
| 49 | } | ||
| 50 | |||
| 51 | static ssize_t led_invert_store(struct device *dev, | ||
| 52 | struct device_attribute *attr, const char *buf, size_t size) | ||
| 53 | { | ||
| 54 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
| 55 | struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data; | ||
| 56 | unsigned long state; | ||
| 57 | int ret; | ||
| 58 | |||
| 59 | ret = kstrtoul(buf, 0, &state); | ||
| 60 | if (ret) | ||
| 61 | return ret; | ||
| 62 | |||
| 63 | oneshot_data->invert = !!state; | ||
| 64 | |||
| 65 | if (oneshot_data->invert) | ||
| 66 | led_set_brightness(led_cdev, LED_FULL); | ||
| 67 | else | ||
| 68 | led_set_brightness(led_cdev, LED_OFF); | ||
| 69 | |||
| 70 | return size; | ||
| 71 | } | ||
| 72 | |||
| 73 | static ssize_t led_delay_on_show(struct device *dev, | ||
| 74 | struct device_attribute *attr, char *buf) | ||
| 75 | { | ||
| 76 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
| 77 | |||
| 78 | return sprintf(buf, "%lu\n", led_cdev->blink_delay_on); | ||
| 79 | } | ||
| 80 | |||
| 81 | static ssize_t led_delay_on_store(struct device *dev, | ||
| 82 | struct device_attribute *attr, const char *buf, size_t size) | ||
| 83 | { | ||
| 84 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
| 85 | unsigned long state; | ||
| 86 | int ret; | ||
| 87 | |||
| 88 | ret = kstrtoul(buf, 0, &state); | ||
| 89 | if (ret) | ||
| 90 | return ret; | ||
| 91 | |||
| 92 | led_cdev->blink_delay_on = state; | ||
| 93 | |||
| 94 | return size; | ||
| 95 | } | ||
| 96 | static ssize_t led_delay_off_show(struct device *dev, | ||
| 97 | struct device_attribute *attr, char *buf) | ||
| 98 | { | ||
| 99 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
| 100 | |||
| 101 | return sprintf(buf, "%lu\n", led_cdev->blink_delay_off); | ||
| 102 | } | ||
| 103 | |||
| 104 | static ssize_t led_delay_off_store(struct device *dev, | ||
| 105 | struct device_attribute *attr, const char *buf, size_t size) | ||
| 106 | { | ||
| 107 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
| 108 | unsigned long state; | ||
| 109 | int ret; | ||
| 110 | |||
| 111 | ret = kstrtoul(buf, 0, &state); | ||
| 112 | if (ret) | ||
| 113 | return ret; | ||
| 114 | |||
| 115 | led_cdev->blink_delay_off = state; | ||
| 116 | |||
| 117 | return size; | ||
| 118 | } | ||
| 119 | |||
| 120 | static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store); | ||
| 121 | static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store); | ||
| 122 | static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store); | ||
| 123 | static DEVICE_ATTR(shot, 0200, NULL, led_shot); | ||
| 124 | |||
| 125 | static void oneshot_trig_activate(struct led_classdev *led_cdev) | ||
| 126 | { | ||
| 127 | struct oneshot_trig_data *oneshot_data; | ||
| 128 | int rc; | ||
| 129 | |||
| 130 | oneshot_data = kzalloc(sizeof(*oneshot_data), GFP_KERNEL); | ||
| 131 | if (!oneshot_data) | ||
| 132 | return; | ||
| 133 | |||
| 134 | led_cdev->trigger_data = oneshot_data; | ||
| 135 | |||
| 136 | rc = device_create_file(led_cdev->dev, &dev_attr_delay_on); | ||
| 137 | if (rc) | ||
| 138 | goto err_out_trig_data; | ||
| 139 | rc = device_create_file(led_cdev->dev, &dev_attr_delay_off); | ||
| 140 | if (rc) | ||
| 141 | goto err_out_delayon; | ||
| 142 | rc = device_create_file(led_cdev->dev, &dev_attr_invert); | ||
| 143 | if (rc) | ||
| 144 | goto err_out_delayoff; | ||
| 145 | rc = device_create_file(led_cdev->dev, &dev_attr_shot); | ||
| 146 | if (rc) | ||
| 147 | goto err_out_invert; | ||
| 148 | |||
| 149 | led_cdev->blink_delay_on = DEFAULT_DELAY; | ||
| 150 | led_cdev->blink_delay_off = DEFAULT_DELAY; | ||
| 151 | |||
| 152 | led_cdev->activated = true; | ||
| 153 | |||
| 154 | return; | ||
| 155 | |||
| 156 | err_out_invert: | ||
| 157 | device_remove_file(led_cdev->dev, &dev_attr_invert); | ||
| 158 | err_out_delayoff: | ||
| 159 | device_remove_file(led_cdev->dev, &dev_attr_delay_off); | ||
| 160 | err_out_delayon: | ||
| 161 | device_remove_file(led_cdev->dev, &dev_attr_delay_on); | ||
| 162 | err_out_trig_data: | ||
| 163 | kfree(led_cdev->trigger_data); | ||
| 164 | } | ||
| 165 | |||
| 166 | static void oneshot_trig_deactivate(struct led_classdev *led_cdev) | ||
| 167 | { | ||
| 168 | struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data; | ||
| 169 | |||
| 170 | if (led_cdev->activated) { | ||
| 171 | device_remove_file(led_cdev->dev, &dev_attr_delay_on); | ||
| 172 | device_remove_file(led_cdev->dev, &dev_attr_delay_off); | ||
| 173 | device_remove_file(led_cdev->dev, &dev_attr_invert); | ||
| 174 | device_remove_file(led_cdev->dev, &dev_attr_shot); | ||
| 175 | kfree(oneshot_data); | ||
| 176 | led_cdev->activated = false; | ||
| 177 | } | ||
| 178 | |||
| 179 | /* Stop blinking */ | ||
| 180 | led_brightness_set(led_cdev, LED_OFF); | ||
| 181 | } | ||
| 182 | |||
| 183 | static struct led_trigger oneshot_led_trigger = { | ||
| 184 | .name = "oneshot", | ||
| 185 | .activate = oneshot_trig_activate, | ||
| 186 | .deactivate = oneshot_trig_deactivate, | ||
| 187 | }; | ||
| 188 | |||
| 189 | static int __init oneshot_trig_init(void) | ||
| 190 | { | ||
| 191 | return led_trigger_register(&oneshot_led_trigger); | ||
| 192 | } | ||
| 193 | |||
| 194 | static void __exit oneshot_trig_exit(void) | ||
| 195 | { | ||
| 196 | led_trigger_unregister(&oneshot_led_trigger); | ||
| 197 | } | ||
| 198 | |||
| 199 | module_init(oneshot_trig_init); | ||
| 200 | module_exit(oneshot_trig_exit); | ||
| 201 | |||
| 202 | MODULE_AUTHOR("Fabio Baltieri <fabio.baltieri@gmail.com>"); | ||
| 203 | MODULE_DESCRIPTION("One-shot LED trigger"); | ||
| 204 | MODULE_LICENSE("GPL"); | ||
