From: Richard Purdie Add an example of a complex LED trigger in the form of a generic timer which triggers the LED its attached to at a user specified frequency and duty cycle. Signed-off-by: Richard Purdie Cc: Russell King Signed-off-by: Andrew Morton --- drivers/leds/Kconfig | 7 + drivers/leds/Makefile | 3 drivers/leds/led-triggers.c | 8 - drivers/leds/ledtrig-timer.c | 174 +++++++++++++++++++++++++++++++++ 4 files changed, 188 insertions(+), 4 deletions(-) diff -puN drivers/leds/Kconfig~led-add-led-timer-trigger drivers/leds/Kconfig --- devel/drivers/leds/Kconfig~led-add-led-timer-trigger 2006-02-10 02:16:23.000000000 -0800 +++ devel-akpm/drivers/leds/Kconfig 2006-02-10 02:16:23.000000000 -0800 @@ -22,5 +22,12 @@ config LEDS_TRIGGERS These triggers allow kernel events to drive the LEDs and can be configured via sysfs. If unsure, say Y. +config LEDS_TRIGGER_TIMER + tristate "LED Timer Trigger" + depends LEDS_TRIGGERS + help + This allows LEDs to be controlled by a programmable timer + via sysfs. If unsure, say Y. + endmenu diff -puN /dev/null drivers/leds/ledtrig-timer.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ devel-akpm/drivers/leds/ledtrig-timer.c 2006-02-10 02:16:30.000000000 -0800 @@ -0,0 +1,174 @@ +/* + * LED Kernel Timer Trigger + * + * Copyright 2005-2006 Openedhand Ltd. + * + * Author: Richard Purdie + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "leds.h" + +struct timer_trig_data { + unsigned long delay_on; /* milliseconds on */ + unsigned long delay_off; /* milliseconds off */ + struct timer_list timer; +}; + +static void led_timer_function(unsigned long data) +{ + struct led_classdev *led_cdev = (struct led_classdev *) data; + struct timer_trig_data *timer_data = led_cdev->trigger_data; + unsigned long brightness = LED_OFF; + unsigned long delay = timer_data->delay_off; + + if (!timer_data->delay_on || !timer_data->delay_off) { + write_lock(&led_cdev->lock); + led_set_brightness(led_cdev, LED_OFF); + write_unlock(&led_cdev->lock); + return; + } + + if (!led_cdev->brightness) { + brightness = LED_FULL; + delay = timer_data->delay_on; + } + + write_lock(&led_cdev->lock); + led_set_brightness(led_cdev, brightness); + write_unlock(&led_cdev->lock); + + mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay)); +} + +static ssize_t led_delay_on_show(struct class_device *dev, char *buf) +{ + struct led_classdev *led_cdev = class_get_devdata(dev); + struct timer_trig_data *timer_data = led_cdev->trigger_data; + + sprintf(buf, "%lu\n", timer_data->delay_on); + + return strlen(buf) + 1; +} + +static ssize_t led_delay_on_store(struct class_device *dev, const char *buf, + size_t size) +{ + struct led_classdev *led_cdev = class_get_devdata(dev); + struct timer_trig_data *timer_data = led_cdev->trigger_data; + int ret = -EINVAL; + char *after; + unsigned long state = simple_strtoul(buf, &after, 10); + + if (after - buf > 0) { + timer_data->delay_on = state; + mod_timer(&timer_data->timer, jiffies + 1); + ret = after - buf; + } + + return ret; +} + +static ssize_t led_delay_off_show(struct class_device *dev, char *buf) +{ + struct led_classdev *led_cdev = class_get_devdata(dev); + struct timer_trig_data *timer_data = led_cdev->trigger_data; + + sprintf(buf, "%lu\n", timer_data->delay_off); + + return strlen(buf) + 1; +} + +static ssize_t led_delay_off_store(struct class_device *dev, const char *buf, + size_t size) +{ + struct led_classdev *led_cdev = class_get_devdata(dev); + struct timer_trig_data *timer_data = led_cdev->trigger_data; + int ret = -EINVAL; + char *after; + unsigned long state = simple_strtoul(buf, &after, 10); + + if (after - buf > 0) { + timer_data->delay_off = state; + mod_timer(&timer_data->timer, jiffies + 1); + ret = after - buf; + } + + return ret; +} + +static CLASS_DEVICE_ATTR(delay_on, 0644, led_delay_on_show, + led_delay_on_store); +static CLASS_DEVICE_ATTR(delay_off, 0644, led_delay_off_show, + led_delay_off_store); + +static void timer_trig_activate(struct led_classdev *led_cdev) +{ + struct timer_trig_data *timer_data; + + timer_data = kzalloc(sizeof(struct timer_trig_data), GFP_KERNEL); + if (!timer_data) + return; + + led_cdev->trigger_data = timer_data; + + init_timer(&timer_data->timer); + timer_data->timer.function = led_timer_function; + timer_data->timer.data = (unsigned long) led_cdev; + + class_device_create_file(led_cdev->class_dev, + &class_device_attr_delay_on); + class_device_create_file(led_cdev->class_dev, + &class_device_attr_delay_off); +} + +static void timer_trig_deactivate(struct led_classdev *led_cdev) +{ + struct timer_trig_data *timer_data = led_cdev->trigger_data; + + if (timer_data) { + class_device_remove_file(led_cdev->class_dev, + &class_device_attr_delay_on); + class_device_remove_file(led_cdev->class_dev, + &class_device_attr_delay_off); + del_timer_sync(&timer_data->timer); + kfree(timer_data); + } +} + +static struct led_trigger timer_led_trigger = { + .name = "timer", + .activate = timer_trig_activate, + .deactivate = timer_trig_deactivate, +}; + +static int __init timer_trig_init(void) +{ + return led_trigger_register(&timer_led_trigger); +} + +static void __exit timer_trig_exit(void) +{ + led_trigger_unregister(&timer_led_trigger); +} + +module_init(timer_trig_init); +module_exit(timer_trig_exit); + +MODULE_AUTHOR("Richard Purdie "); +MODULE_DESCRIPTION("Timer LED trigger"); +MODULE_LICENSE("GPL"); diff -puN drivers/leds/Makefile~led-add-led-timer-trigger drivers/leds/Makefile --- devel/drivers/leds/Makefile~led-add-led-timer-trigger 2006-02-10 02:16:23.000000000 -0800 +++ devel-akpm/drivers/leds/Makefile 2006-02-10 02:16:23.000000000 -0800 @@ -3,3 +3,6 @@ obj-$(CONFIG_NEW_LEDS) += led-core.o obj-$(CONFIG_LEDS_CLASS) += led-class.o obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o + +# LED Triggers +obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o diff -puN drivers/leds/led-triggers.c~led-add-led-timer-trigger drivers/leds/led-triggers.c --- devel/drivers/leds/led-triggers.c~led-add-led-timer-trigger 2006-02-10 02:16:29.000000000 -0800 +++ devel-akpm/drivers/leds/led-triggers.c 2006-02-10 02:16:29.000000000 -0800 @@ -167,7 +167,7 @@ int led_trigger_register(struct led_trig write_unlock(&triggers_list_lock); /* Register with any LEDs that have this as a default trigger */ - read_lock(&leds_list); + read_lock(&leds_list_lock); list_for_each_entry(led_cdev, &leds_list, node) { write_lock(&led_cdev->trigger_lock); if (!led_cdev->trigger && led_cdev->default_trigger && @@ -175,7 +175,7 @@ int led_trigger_register(struct led_trig led_trigger_set(led_cdev, trigger); write_unlock(&led_cdev->trigger_lock); } - read_unlock(&leds_list); + read_unlock(&leds_list_lock); return 0; } @@ -203,14 +203,14 @@ void led_trigger_unregister(struct led_t write_unlock(&triggers_list_lock); /* Remove anyone actively using this trigger */ - read_lock(&leds_list); + read_lock(&leds_list_lock); list_for_each_entry(led_cdev, &leds_list, node) { write_lock(&led_cdev->trigger_lock); if (led_cdev->trigger == trigger) led_trigger_set(led_cdev, NULL); write_unlock(&led_cdev->trigger_lock); } - read_unlock(&leds_list); + read_unlock(&leds_list_lock); } void led_trigger_unregister_simple(struct led_trigger *trigger) _