Skip to content

Commit

Permalink
leds: add oneshot trigger
Browse files Browse the repository at this point in the history
Add oneshot trigger to blink a led with configurale parameters via
sysfs.

Signed-off-by: Fabio Baltieri <[email protected]>
Cc: Shuah Khan <[email protected]>
Signed-off-by: Bryan Wu <[email protected]>
  • Loading branch information
fabiobaltieri authored and Bryan Wu committed Jul 23, 2012
1 parent 4378648 commit 5e41728
Show file tree
Hide file tree
Showing 4 changed files with 278 additions and 0 deletions.
59 changes: 59 additions & 0 deletions Documentation/leds/ledtrig-oneshot.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
One-shot LED Trigger
====================

This is a LED trigger useful for signaling the user of an event where there are
no clear trap points to put standard led-on and led-off settings. Using this
trigger, the application needs only to signal the trigger when an event has
happened, than the trigger turns the LED on and than keeps it off for a
specified amount of time.

This trigger is meant to be usable both for sporadic and dense events. In the
first case, the trigger produces a clear single controlled blink for each
event, while in the latter it keeps blinking at constant rate, as to signal
that the events are arriving continuously.

A one-shot LED only stays in a constant state when there are no events. An
additional "invert" property specifies if the LED has to stay off (normal) or
on (inverted) when not rearmed.

The trigger can be activated from user space on led class devices as shown
below:

echo oneshot > trigger

This adds the following sysfs attributes to the LED:

delay_on - specifies for how many milliseconds the LED has to stay at
LED_FULL brightness after it has been armed.
Default to 100 ms.

delay_off - specifies for how many milliseconds the LED has to stay at
LED_OFF brightness after it has been armed.
Default to 100 ms.

invert - reverse the blink logic. If set to 0 (default) blink on for delay_on
ms, then blink off for delay_off ms, leaving the LED normally off. If
set to 1, blink off for delay_off ms, then blink on for delay_on ms,
leaving the LED normally on.
Setting this value also immediately change the LED state.

shot - write any non-empty string to signal an events, this starts a blink
sequence if not already running.

Example use-case: network devices, initialization:

echo oneshot > trigger # set trigger for this led
echo 33 > delay_on # blink at 1 / (33 + 33) Hz on continuous traffic
echo 33 > delay_off

interface goes up:

echo 1 > invert # set led as normally-on, turn the led on

packet received/transmitted:

echo 1 > shot # led starts blinking, ignored if already blinking

interface goes down

echo 0 > invert # set led as normally-off, turn the led off
14 changes: 14 additions & 0 deletions drivers/leds/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,20 @@ config LEDS_TRIGGER_TIMER

If unsure, say Y.

config LEDS_TRIGGER_ONESHOT
tristate "LED One-shot Trigger"
depends on LEDS_TRIGGERS
help
This allows LEDs to blink in one-shot pulses with parameters
controlled via sysfs. It's useful to notify the user on
sporadic events, when there are no clear begin and end trap points,
or on dense events, where this blinks the LED at constant rate if
rearmed continuously.

It also shows how to use the led_blink_set_oneshot() function.

If unsure, say Y.

config LEDS_TRIGGER_IDE_DISK
bool "LED IDE Disk Trigger"
depends on IDE_GD_ATA
Expand Down
1 change: 1 addition & 0 deletions drivers/leds/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o

# LED Triggers
obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
obj-$(CONFIG_LEDS_TRIGGER_ONESHOT) += ledtrig-oneshot.o
obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o
obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o
Expand Down
204 changes: 204 additions & 0 deletions drivers/leds/ledtrig-oneshot.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
/*
* One-shot LED Trigger
*
* Copyright 2012, Fabio Baltieri <[email protected]>
*
* Based on ledtrig-timer.c by Richard Purdie <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/leds.h>
#include "leds.h"

#define DEFAULT_DELAY 100

struct oneshot_trig_data {
unsigned int invert;
};

static ssize_t led_shot(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;

led_blink_set_oneshot(led_cdev,
&led_cdev->blink_delay_on, &led_cdev->blink_delay_off,
oneshot_data->invert);

/* content is ignored */
return size;
}
static ssize_t led_invert_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;

return sprintf(buf, "%u\n", oneshot_data->invert);
}

static ssize_t led_invert_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
unsigned long state;
int ret;

ret = kstrtoul(buf, 0, &state);
if (ret)
return ret;

oneshot_data->invert = !!state;

if (oneshot_data->invert)
led_set_brightness(led_cdev, LED_FULL);
else
led_set_brightness(led_cdev, LED_OFF);

return size;
}

static ssize_t led_delay_on_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);

return sprintf(buf, "%lu\n", led_cdev->blink_delay_on);
}

static ssize_t led_delay_on_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
unsigned long state;
int ret;

ret = kstrtoul(buf, 0, &state);
if (ret)
return ret;

led_cdev->blink_delay_on = state;

return size;
}
static ssize_t led_delay_off_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);

return sprintf(buf, "%lu\n", led_cdev->blink_delay_off);
}

static ssize_t led_delay_off_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
unsigned long state;
int ret;

ret = kstrtoul(buf, 0, &state);
if (ret)
return ret;

led_cdev->blink_delay_off = state;

return size;
}

static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);
static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
static DEVICE_ATTR(shot, 0200, NULL, led_shot);

static void oneshot_trig_activate(struct led_classdev *led_cdev)
{
struct oneshot_trig_data *oneshot_data;
int rc;

oneshot_data = kzalloc(sizeof(*oneshot_data), GFP_KERNEL);
if (!oneshot_data)
return;

led_cdev->trigger_data = oneshot_data;

rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
if (rc)
goto err_out_trig_data;
rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
if (rc)
goto err_out_delayon;
rc = device_create_file(led_cdev->dev, &dev_attr_invert);
if (rc)
goto err_out_delayoff;
rc = device_create_file(led_cdev->dev, &dev_attr_shot);
if (rc)
goto err_out_invert;

led_cdev->blink_delay_on = DEFAULT_DELAY;
led_cdev->blink_delay_off = DEFAULT_DELAY;

led_cdev->activated = true;

return;

err_out_invert:
device_remove_file(led_cdev->dev, &dev_attr_invert);
err_out_delayoff:
device_remove_file(led_cdev->dev, &dev_attr_delay_off);
err_out_delayon:
device_remove_file(led_cdev->dev, &dev_attr_delay_on);
err_out_trig_data:
kfree(led_cdev->trigger_data);
}

static void oneshot_trig_deactivate(struct led_classdev *led_cdev)
{
struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;

if (led_cdev->activated) {
device_remove_file(led_cdev->dev, &dev_attr_delay_on);
device_remove_file(led_cdev->dev, &dev_attr_delay_off);
device_remove_file(led_cdev->dev, &dev_attr_invert);
device_remove_file(led_cdev->dev, &dev_attr_shot);
kfree(oneshot_data);
led_cdev->activated = false;
}

/* Stop blinking */
led_brightness_set(led_cdev, LED_OFF);
}

static struct led_trigger oneshot_led_trigger = {
.name = "oneshot",
.activate = oneshot_trig_activate,
.deactivate = oneshot_trig_deactivate,
};

static int __init oneshot_trig_init(void)
{
return led_trigger_register(&oneshot_led_trigger);
}

static void __exit oneshot_trig_exit(void)
{
led_trigger_unregister(&oneshot_led_trigger);
}

module_init(oneshot_trig_init);
module_exit(oneshot_trig_exit);

MODULE_AUTHOR("Fabio Baltieri <[email protected]>");
MODULE_DESCRIPTION("One-shot LED trigger");
MODULE_LICENSE("GPL");

0 comments on commit 5e41728

Please sign in to comment.