The LED pattern trigger allows LEDs blink in user defined pattern. new file: Documentation/leds/ledtrig-pattern.txt modified: drivers/leds/trigger/Kconfig modified: drivers/leds/trigger/Makefile new file: drivers/leds/trigger/ledtrig-pattern.c Suggested-by: Pavel Machek <pavel@xxxxxx> Signed-off-by: Joe Xue <lgxue@xxxxxxxxxxx> --- Documentation/leds/ledtrig-pattern.txt | 60 ++++++++++ drivers/leds/trigger/Kconfig | 9 ++ drivers/leds/trigger/Makefile | 1 + drivers/leds/trigger/ledtrig-pattern.c | 207 +++++++++++++++++++++++++++++++++ 4 files changed, 277 insertions(+) create mode 100644 Documentation/leds/ledtrig-pattern.txt create mode 100644 drivers/leds/trigger/ledtrig-pattern.c diff --git a/Documentation/leds/ledtrig-pattern.txt b/Documentation/leds/ledtrig-pattern.txt new file mode 100644 index 0000000..4b546c3 --- /dev/null +++ b/Documentation/leds/ledtrig-pattern.txt @@ -0,0 +1,60 @@ +LED Pattern Trigger +=================== + +0. Introduction + +LED Pattern trigger is designed to let LEDs indicate the patterns defined by +users. This is very useful for those scenarios where only one non-color led +needs to indicate different states. + +1. How to use + +Pattern trigger can be enabled and disabled from user space on led class +devices that support this trigger as shown below: + + echo pattern > trigger + +When the pattern trigger is activated, it will get the current brightness as +its brightness if the led is on. Otherwise, it will use the maximum brightness. + +If the led supports different values rather than ON/OFF, the brightness can be +set in advance before enabling the trigger. + + echo 128 > brightness + echo pattern > trigger + +Two properties are exported. They are delay_unit and pattern. + + delay_unit - a delay time unit, default value is 125 ms + pattern - blink pattern, includes three legal characters + '#', ' '(space), and '/' + '#' let LED on and last delay_unit long time + ' ' let LED off and last delay_unit long time + '/' stop pattern + pattern will be repeated without it + +3. Examples + + Example 1 + + echo pattern > trigger + echo "# ## /" + + The behaviour is like below: + + on(125ms)off(125ms)on(250ms)off + This is Morse code 'A' + + Example 2 + + echo pattern > trigger + echo 200 > delay_unit + echo "# # " + + The behaviour is like below: + + on(200ms)off(200ms)on(200ms)off(800ms) + on(200ms)off(200ms)on(200ms)off(800ms) + ...(Repeat) + + This is 2 times burst blinking. diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig index 49794b4..23d0967 100644 --- a/drivers/leds/trigger/Kconfig +++ b/drivers/leds/trigger/Kconfig @@ -108,4 +108,13 @@ config LEDS_TRIGGER_CAMERA This enables direct flash/torch on/off by the driver, kernel space. If unsure, say Y. +config LEDS_TRIGGER_PATTERN + tristate "LED Pattern Trigger" + depends on LEDS_TRIGGERS + help + This allows LEDs to blink in a user defined pattern controlled via + sysfs. It's useful to notify different states by using one led. + For more details read Documentation/leds/leds-pattern.txt. + If unsure, say Y. + endif # LEDS_TRIGGERS diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile index 1abf48d..a739429 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_PATTERN) += ledtrig-pattern.o diff --git a/drivers/leds/trigger/ledtrig-pattern.c b/drivers/leds/trigger/ledtrig-pattern.c new file mode 100644 index 0000000..80fc238 --- /dev/null +++ b/drivers/leds/trigger/ledtrig-pattern.c @@ -0,0 +1,207 @@ +/* + * LED Kernel Morse Code 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-transient.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. + * + */ + +#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" + +#define MAX_PATTEN_LEN 255 + +struct pattern_trig_data { + unsigned long delay_unit; + unsigned long pattern_len; + unsigned long count; + int brightness_on; + char pattern[MAX_PATTEN_LEN + 1]; + struct timer_list timer; +}; + +static void pattern_timer_function(unsigned long data) +{ + struct led_classdev *led_cdev = (struct led_classdev *) data; + struct pattern_trig_data *pattern_data = led_cdev->trigger_data; + + if (pattern_data->pattern[pattern_data->count] == '#') { + __led_set_brightness(led_cdev, pattern_data->brightness_on); + mod_timer(&pattern_data->timer, + jiffies + msecs_to_jiffies(pattern_data->delay_unit)); + } else if (pattern_data->pattern[pattern_data->count] == ' ') { + __led_set_brightness(led_cdev, LED_OFF); + mod_timer(&pattern_data->timer, + jiffies + msecs_to_jiffies(pattern_data->delay_unit)); + /* stop blinking */ + } else if (pattern_data->pattern[pattern_data->count] == '/') { + return; + } + + pattern_data->count++; + if (pattern_data->count == pattern_data->pattern_len) + pattern_data->count = 0; +} + +static ssize_t pattern_delay_unit_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct pattern_trig_data *pattern_data = led_cdev->trigger_data; + + return sprintf(buf, "%lu\n", pattern_data->delay_unit); +} + +static ssize_t pattern_delay_unit_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct pattern_trig_data *pattern_data = led_cdev->trigger_data; + unsigned long state; + ssize_t ret = -EINVAL; + + ret = kstrtoul(buf, 10, &state); + if (ret) + return ret; + + pattern_data->delay_unit = state; + + return size; +} + +static ssize_t pattern_pattern_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct pattern_trig_data *pattern_data = led_cdev->trigger_data; + + return sprintf(buf, "%s\n", pattern_data->pattern); +} + +static ssize_t pattern_pattern_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct pattern_trig_data *pattern_data = led_cdev->trigger_data; + int i; + ssize_t ret = -EINVAL; + + int len = (size > MAX_PATTEN_LEN) ? MAX_PATTEN_LEN : (size - 1); + + /* legality check */ + for (i = 0; i < len; i++) { + if (buf[i] != ' ' && buf[i] != '#' && buf[i] != '/') + return ret; + } + + del_timer_sync(&pattern_data->timer); + + memcpy(pattern_data->pattern, buf, len); + pattern_data->pattern[len] = '\0'; + pattern_data->pattern_len = len; + pattern_data->count = 0; + + mod_timer(&pattern_data->timer, jiffies + 1); + + return size; +} + +static DEVICE_ATTR(pattern, 0644, pattern_pattern_show, pattern_pattern_store); +static DEVICE_ATTR(delay_unit, 0644, + pattern_delay_unit_show, pattern_delay_unit_store); + +static void pattern_trig_activate(struct led_classdev *led_cdev) +{ + int rc; + struct pattern_trig_data *tdata; + + tdata = kzalloc(sizeof(struct pattern_trig_data), GFP_KERNEL); + if (!tdata) { + dev_err(led_cdev->dev, + "unable to allocate pattern trigger\n"); + return; + } + + led_cdev->trigger_data = tdata; + + rc = device_create_file(led_cdev->dev, &dev_attr_pattern); + if (rc) + goto err_out; + + rc = device_create_file(led_cdev->dev, &dev_attr_delay_unit); + if (rc) + goto err_out_delay_unit; + + memset(tdata->pattern, 0, MAX_PATTEN_LEN + 1); + /* default delay_unit 125ms */ + tdata->delay_unit = 125; + + setup_timer(&tdata->timer, pattern_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_unit: + device_remove_file(led_cdev->dev, &dev_attr_pattern); +err_out: + dev_err(led_cdev->dev, "unable to register pattern trigger\n"); + led_cdev->trigger_data = NULL; + kfree(tdata); +} + +static void pattern_trig_deactivate(struct led_classdev *led_cdev) +{ + struct pattern_trig_data *pattern_data = led_cdev->trigger_data; + + if (led_cdev->activated) { + del_timer_sync(&pattern_data->timer); + device_remove_file(led_cdev->dev, &dev_attr_pattern); + device_remove_file(led_cdev->dev, &dev_attr_delay_unit); + led_cdev->trigger_data = NULL; + led_cdev->activated = false; + kfree(pattern_data); + } + __led_set_brightness(led_cdev, LED_OFF); +} + +static struct led_trigger pattern_trigger = { + .name = "pattern", + .activate = pattern_trig_activate, + .deactivate = pattern_trig_deactivate, +}; + +static int __init pattern_trig_init(void) +{ + return led_trigger_register(&pattern_trigger); +} + +static void __exit pattern_trig_exit(void) +{ + led_trigger_unregister(&pattern_trigger); +} + +module_init(pattern_trig_init); +module_exit(pattern_trig_exit); + +MODULE_AUTHOR("Joe Xue <lgxue@xxxxxxxxxxx"); +MODULE_DESCRIPTION("Patten 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