From cb00e6d086096878dd5086fad481b63e67519f25 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 28 Nov 2010 13:05:16 +0100 Subject: [PATCH] compat: backport LED class software blinking Signed-off-by: Felix Fietkau --- compat/compat-2.6.37.c | 172 ++++++++++++++++++++++++++++++++++ include/linux/compat-2.6.37.h | 13 +++ 2 files changed, 185 insertions(+) diff --git a/compat/compat-2.6.37.c b/compat/compat-2.6.37.c index 81f466f3b58a..669402d880d1 100644 --- a/compat/compat-2.6.37.c +++ b/compat/compat-2.6.37.c @@ -152,3 +152,175 @@ int compat_genl_unregister_family(struct genl_family *family) } EXPORT_SYMBOL(compat_genl_unregister_family); +#ifdef CONFIG_LEDS_CLASS + +#undef led_brightness_set +#undef led_classdev_unregister + +spinlock_t led_lock; +static LIST_HEAD(led_timers); + +struct led_timer { + struct list_head list; + struct led_classdev *cdev; + struct timer_list blink_timer; + unsigned long blink_delay_on; + unsigned long blink_delay_off; + int blink_brightness; +}; + +static void led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + led_cdev->brightness = brightness; + led_cdev->brightness_set(led_cdev, brightness); +} + +static struct led_timer *led_get_timer(struct led_classdev *led_cdev) +{ + struct led_timer *p; + unsigned long flags; + + spin_lock_irqsave(&led_lock, flags); + list_for_each_entry(p, &led_timers, list) { + if (p->cdev == led_cdev) + goto found; + } + p = NULL; +found: + spin_unlock_irqrestore(&led_lock, flags); + return p; +} + +static void led_stop_software_blink(struct led_timer *led) +{ + del_timer_sync(&led->blink_timer); + led->blink_delay_on = 0; + led->blink_delay_off = 0; +} + +static void led_timer_function(unsigned long data) +{ + struct led_timer *led = (struct led_timer *)data; + unsigned long brightness; + unsigned long delay; + + if (!led->blink_delay_on || !led->blink_delay_off) { + led->cdev->brightness_set(led->cdev, LED_OFF); + return; + } + + brightness = led->cdev->brightness; + if (!brightness) { + /* Time to switch the LED on. */ + brightness = led->blink_brightness; + delay = led->blink_delay_on; + } else { + /* Store the current brightness value to be able + * to restore it when the delay_off period is over. + */ + led->blink_brightness = brightness; + brightness = LED_OFF; + delay = led->blink_delay_off; + } + + led_brightness_set(led->cdev, brightness); + mod_timer(&led->blink_timer, jiffies + msecs_to_jiffies(delay)); +} + +static struct led_timer *led_new_timer(struct led_classdev *led_cdev) +{ + struct led_timer *led; + unsigned long flags; + + led = kzalloc(sizeof(struct led_timer), GFP_ATOMIC); + if (!led) + return NULL; + + led->cdev = led_cdev; + init_timer(&led->blink_timer); + led->blink_timer.function = led_timer_function; + led->blink_timer.data = (unsigned long) led; + + spin_lock_irqsave(&led_lock, flags); + list_add(&led->list, &led_timers); + spin_unlock_irqrestore(&led_lock, flags); + + return led; +} + +void led_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct led_timer *led = led_get_timer(led_cdev); + int current_brightness; + + if (!led) { + led = led_new_timer(led_cdev); + if (!led) + return; + } + + /* blink with 1 Hz as default if nothing specified */ + if (!*delay_on && !*delay_off) + *delay_on = *delay_off = 500; + + if (led->blink_delay_on == *delay_on && + led->blink_delay_off == *delay_off) + return; + + current_brightness = led_cdev->brightness; + if (current_brightness) + led->blink_brightness = current_brightness; + if (!led->blink_brightness) + led->blink_brightness = led_cdev->max_brightness; + + led_stop_software_blink(led); + led->blink_delay_on = *delay_on; + led->blink_delay_off = *delay_off; + + /* never on - don't blink */ + if (!*delay_on) + return; + + /* never off - just set to brightness */ + if (!*delay_off) { + led_brightness_set(led_cdev, led->blink_brightness); + return; + } + + mod_timer(&led->blink_timer, jiffies + 1); +} +EXPORT_SYMBOL(led_blink_set); + +void compat_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct led_timer *led = led_get_timer(led_cdev); + + if (led) + led_stop_software_blink(led); + + return led_cdev->brightness_set(led_cdev, brightness); +} +EXPORT_SYMBOL(compat_led_brightness_set); + +void compat_led_classdev_unregister(struct led_classdev *led_cdev) +{ + struct led_timer *led = led_get_timer(led_cdev); + unsigned long flags; + + if (led) { + del_timer_sync(&led->blink_timer); + spin_lock_irqsave(&led_lock, flags); + list_del(&led->list); + spin_unlock_irqrestore(&led_lock, flags); + kfree(led); + } + + led_classdev_unregister(led_cdev); +} +EXPORT_SYMBOL(compat_led_classdev_unregister); + +#endif diff --git a/include/linux/compat-2.6.37.h b/include/linux/compat-2.6.37.h index 6c026a34c318..502d2b198340 100644 --- a/include/linux/compat-2.6.37.h +++ b/include/linux/compat-2.6.37.h @@ -6,6 +6,7 @@ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) #include +#include #define SDIO_CLASS_BT_AMP 0x09 /* Type-A Bluetooth AMP interface */ @@ -93,6 +94,18 @@ int genl_unregister_family(struct genl_family *family); #define genl_register_mc_group(_fam, _grp) genl_register_mc_group(&(_fam)->family, _grp) #define genl_unregister_mc_group(_fam, _grp) genl_unregister_mc_group(&(_fam)->family, _grp) + +extern void led_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off); + +#define led_classdev_unregister compat_led_classdev_unregister +extern void compat_led_classdev_unregister(struct led_classdev *led_cdev); + +#define led_brightness_set compat_led_brightness_set +extern void compat_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness); + #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) */ #endif /* LINUX_26_37_COMPAT_H */ -- 2.30.2