From: Joe Xue <lgxue@xxxxxxxxxxx> Allow LEDs blink in burst mode. Three parameters are exported to sysfs: freq, delay_off and times. new file: Documentation/leds/ledtrig-burst.txt modified: drivers/leds/trigger/Kconfig modified: drivers/leds/trigger/Makefile new file: drivers/leds/trigger/ledtrig-burst.c Signed-off-by: Joe Xue <lgxue@xxxxxxxxxxx> --- Documentation/leds/ledtrig-burst.txt | 58 ++++++++ drivers/leds/trigger/Kconfig | 10 ++ drivers/leds/trigger/Makefile | 1 + drivers/leds/trigger/ledtrig-burst.c | 250 +++++++++++++++++++++++++++++++++++ 4 files changed, 319 insertions(+) create mode 100644 Documentation/leds/ledtrig-burst.txt create mode 100644 drivers/leds/trigger/ledtrig-burst.c diff --git a/Documentation/leds/ledtrig-burst.txt b/Documentation/leds/ledtrig-burst.txt new file mode 100644 index 0000000..50a7955 --- /dev/null +++ b/Documentation/leds/ledtrig-burst.txt @@ -0,0 +1,58 @@ +LED Burst Trigger +================= + +0. Introduction + +Sometimes, the system has only one no-color led to indicate different stats. +The LED timer trigger can let LEDs to blink in different frequency, but most +people maybe just can discriminate two states, slow and fast. + +In this case, Morse code maybe is a good choice :-), but who can bear it. + +Besides the Morse code, another way is using burst mode. People can easily tell +how many times the LED blinks in one cycle. + +Burst trigger is designed for this purpose. + +1. How to use + +Burst trigger can be enabled and disabled from user space on led class +devices that support this trigger as shown below: + + echo burst > trigger + +Three properties are exported. They are freq, times and delay_off. + + freq - the blink frequency, default value is 3 means 3HZ + times - burst blink times in one cycle, no default value + delay_off - off time between two burst blinks, default value is 500 ms + +2. Case studies + + Example 1 + + echo burst > trigger + echo 2 > times + + The behaviour is like below: + + on(1/6s)off(1/6s)on(1/6s)off(1/6m) + ...off 500ms... + on(1/6s)off(1/6s)on(1/3s)off(1/6m) + ...off 500ms + ... + + Example 2 + + echo burst > trigger + echo 4 > freq + echo 3 > times + echo 1000 > delay_off + + The behaviour is like below: + + on(1/8s)off(1/8s)on(1/8s)off(1/8m)on(1/8s)off(1/8m) + ...off 1s... + on(1/8s)off(1/8s)on(1/8s)off(1/8m)on(1/8s)off(1/8m) + ...off 1s + ... diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig index 49794b4..8f4ebbf 100644 --- a/drivers/leds/trigger/Kconfig +++ b/drivers/leds/trigger/Kconfig @@ -108,4 +108,14 @@ config LEDS_TRIGGER_CAMERA This enables direct flash/torch on/off by the driver, kernel space. If unsure, say Y. +config LEDS_TRIGGER_BURST + tristate "LED Burst Trigger" + depends on LEDS_TRIGGERS + help + This allows LEDs to blink in burst mode with parameters + controlled via sysfs. It's useful to notify different states + by using one led. + For more details read Documentation/leds/leds-burst.txt. + If unsure, say Y. + endif # LEDS_TRIGGERS diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile index 1abf48d..6c48517 100644 --- a/drivers/leds/trigger/Makefile +++ b/drivers/leds/trigger/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_LEDS_TRIGGER_CPU) += ledtrig-cpu.o obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o +obj-$(CONFIG_LEDS_TRIGGER_BURST) += ledtrig-burst.o diff --git a/drivers/leds/trigger/ledtrig-burst.c b/drivers/leds/trigger/ledtrig-burst.c new file mode 100644 index 0000000..95eda2a --- /dev/null +++ b/drivers/leds/trigger/ledtrig-burst.c @@ -0,0 +1,250 @@ +/* + * LED Kernel Burst Trigger + * + * Copyright (C) 2013 Joe Xue <lgxue@xxxxxxxxxxx> + * + * Based on Richard Purdie's ledtrig-timer.c and Atsushi Nemoto's + * ledtrig-heartbeat.c and Shuah Khan's ledtrig-burst.c + * + * 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. + * + */ +/* + * Burst trigger allows LEDs to blink in burst mode. The difference + * between burst trigger and timer trigger is timer trigger makes the + * LEDs blink continually in a frequency while burst trigger makes the + * LEDs blink some times in a special frequency then have a stop. + * Burst trigger allows LEDs to indicate different stats. Users can easy + * to describe it to support engineers by saying 3/4/5/X times burst + * blink. +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/timer.h> +#include <linux/leds.h> +#include "../leds.h" + +struct burst_trig_data { + int state; + unsigned long freq; + unsigned long times; + unsigned long count; + unsigned long delay_off; + int brightness_on; + struct timer_list timer; +}; + +static void burst_timer_function(unsigned long data) +{ + struct led_classdev *led_cdev = (struct led_classdev *) data; + struct burst_trig_data *burst_data = led_cdev->trigger_data; + + if (burst_data->count > 0) { + /* revert the light state */ + burst_data->state = 1 - burst_data->state; + __led_set_brightness(led_cdev, + burst_data->state*burst_data->brightness_on); + burst_data->count--; + /* the delay time for on and off are 1000/(freq*2) = 500/freq */ + mod_timer(&burst_data->timer, + jiffies + msecs_to_jiffies(500/burst_data->freq)); + } else { + burst_data->count = burst_data->times * 2; + mod_timer(&burst_data->timer, + jiffies + msecs_to_jiffies(burst_data->delay_off)); + } +} + +static ssize_t burst_delay_off_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct burst_trig_data *burst_data = led_cdev->trigger_data; + + return sprintf(buf, "%lu\n", burst_data->delay_off); +} + +static ssize_t burst_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); + struct burst_trig_data *burst_data = led_cdev->trigger_data; + unsigned long state; + ssize_t ret = -EINVAL; + + ret = kstrtoul(buf, 10, &state); + if (ret) + return ret; + + burst_data->delay_off = state; + + return size; +} + +static ssize_t burst_times_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct burst_trig_data *burst_data = led_cdev->trigger_data; + + return sprintf(buf, "%lu\n", burst_data->times); +} + +static ssize_t burst_times_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct burst_trig_data *burst_data = led_cdev->trigger_data; + unsigned long state; + ssize_t ret = -EINVAL; + + ret = kstrtoul(buf, 10, &state); + if (ret) + return ret; + + /* if the times is larger then 0 then use it else stop burst */ + if (state > 0) { + burst_data->times = state; + burst_data->count = state*2; + del_timer_sync(&burst_data->timer); + mod_timer(&burst_data->timer, jiffies + 1); + } else { + del_timer_sync(&burst_data->timer); + } + + return size; +} + +static ssize_t burst_freq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct burst_trig_data *burst_data = led_cdev->trigger_data; + + return sprintf(buf, "%lu\n", burst_data->freq); +} + +static ssize_t burst_freq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct burst_trig_data *burst_data = led_cdev->trigger_data; + unsigned long state; + ssize_t ret = -EINVAL; + + ret = kstrtoul(buf, 10, &state); + if (ret) + return ret; + + /* the frequency can not be 0 */ + if (state == 0) + return -EINVAL; + + burst_data->freq = state; + + return size; +} + +static DEVICE_ATTR(freq, 0644, burst_freq_show, burst_freq_store); +static DEVICE_ATTR(times, 0644, burst_times_show, burst_times_store); +static DEVICE_ATTR(delay_off, 0644, + burst_delay_off_show, burst_delay_off_store); + +static void burst_trig_activate(struct led_classdev *led_cdev) +{ + int rc; + struct burst_trig_data *tdata; + + tdata = kzalloc(sizeof(struct burst_trig_data), GFP_KERNEL); + if (!tdata) { + dev_err(led_cdev->dev, + "unable to allocate burst trigger\n"); + return; + } + /* default frequency 3HZ */ + tdata->freq = 3; + /* default delay_off 500ms */ + tdata->delay_off = 500; + + tdata->state = 0; + + led_cdev->trigger_data = tdata; + + rc = device_create_file(led_cdev->dev, &dev_attr_freq); + if (rc) + goto err_out; + + rc = device_create_file(led_cdev->dev, &dev_attr_times); + if (rc) + goto err_out_times; + + rc = device_create_file(led_cdev->dev, &dev_attr_delay_off); + if (rc) + goto err_out_delay_off; + + setup_timer(&tdata->timer, burst_timer_function, + (unsigned long) led_cdev); + + tdata->brightness_on = led_get_brightness(led_cdev); + if (tdata->brightness_on == LED_OFF) + tdata->brightness_on = led_cdev->max_brightness; + + __led_set_brightness(led_cdev, LED_OFF); + led_cdev->activated = true; + + return; + +err_out_delay_off: + device_remove_file(led_cdev->dev, &dev_attr_times); +err_out_times: + device_remove_file(led_cdev->dev, &dev_attr_freq); +err_out: + dev_err(led_cdev->dev, "unable to register burst trigger\n"); + led_cdev->trigger_data = NULL; + kfree(tdata); +} + +static void burst_trig_deactivate(struct led_classdev *led_cdev) +{ + struct burst_trig_data *burst_data = led_cdev->trigger_data; + + if (led_cdev->activated) { + del_timer_sync(&burst_data->timer); + device_remove_file(led_cdev->dev, &dev_attr_freq); + device_remove_file(led_cdev->dev, &dev_attr_times); + device_remove_file(led_cdev->dev, &dev_attr_delay_off); + led_cdev->trigger_data = NULL; + led_cdev->activated = false; + kfree(burst_data); + } + __led_set_brightness(led_cdev, LED_OFF); +} + +static struct led_trigger burst_trigger = { + .name = "burst", + .activate = burst_trig_activate, + .deactivate = burst_trig_deactivate, +}; + +static int __init burst_trig_init(void) +{ + return led_trigger_register(&burst_trigger); +} + +static void __exit burst_trig_exit(void) +{ + led_trigger_unregister(&burst_trigger); +} + +module_init(burst_trig_init); +module_exit(burst_trig_exit); + +MODULE_AUTHOR("Joe Xue <lgxue@xxxxxxxxxxx"); +MODULE_DESCRIPTION("Burst LED trigger"); +MODULE_LICENSE("GPL"); -- 1.8.1.2 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html