diff options
Diffstat (limited to 'drivers/leds/led-class.c')
| -rw-r--r-- | drivers/leds/led-class.c | 105 | 
1 files changed, 104 insertions, 1 deletions
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 260660076507..211e21f34bd5 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -81,6 +81,79 @@ static struct device_attribute led_class_attrs[] = {  	__ATTR_NULL,  }; +static void led_timer_function(unsigned long data) +{ +	struct led_classdev *led_cdev = (void *)data; +	unsigned long brightness; +	unsigned long delay; + +	if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) { +		led_set_brightness(led_cdev, LED_OFF); +		return; +	} + +	brightness = led_get_brightness(led_cdev); +	if (!brightness) { +		/* Time to switch the LED on. */ +		brightness = led_cdev->blink_brightness; +		delay = led_cdev->blink_delay_on; +	} else { +		/* Store the current brightness value to be able +		 * to restore it when the delay_off period is over. +		 */ +		led_cdev->blink_brightness = brightness; +		brightness = LED_OFF; +		delay = led_cdev->blink_delay_off; +	} + +	led_set_brightness(led_cdev, brightness); + +	mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay)); +} + +static void led_stop_software_blink(struct led_classdev *led_cdev) +{ +	/* deactivate previous settings */ +	del_timer_sync(&led_cdev->blink_timer); +	led_cdev->blink_delay_on = 0; +	led_cdev->blink_delay_off = 0; +} + +static void led_set_software_blink(struct led_classdev *led_cdev, +				   unsigned long delay_on, +				   unsigned long delay_off) +{ +	int current_brightness; + +	current_brightness = led_get_brightness(led_cdev); +	if (current_brightness) +		led_cdev->blink_brightness = current_brightness; +	if (!led_cdev->blink_brightness) +		led_cdev->blink_brightness = led_cdev->max_brightness; + +	if (delay_on == led_cdev->blink_delay_on && +	    delay_off == led_cdev->blink_delay_off) +		return; + +	led_stop_software_blink(led_cdev); + +	led_cdev->blink_delay_on = delay_on; +	led_cdev->blink_delay_off = delay_off; + +	/* never on - don't blink */ +	if (!delay_on) +		return; + +	/* never off - just set to brightness */ +	if (!delay_off) { +		led_set_brightness(led_cdev, led_cdev->blink_brightness); +		return; +	} + +	mod_timer(&led_cdev->blink_timer, jiffies + 1); +} + +  /**   * led_classdev_suspend - suspend an led_classdev.   * @led_cdev: the led_classdev to suspend. @@ -148,6 +221,10 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)  	led_update_brightness(led_cdev); +	init_timer(&led_cdev->blink_timer); +	led_cdev->blink_timer.function = led_timer_function; +	led_cdev->blink_timer.data = (unsigned long)led_cdev; +  #ifdef CONFIG_LEDS_TRIGGERS  	led_trigger_set_default(led_cdev);  #endif @@ -157,7 +234,6 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)  	return 0;  } -  EXPORT_SYMBOL_GPL(led_classdev_register);  /** @@ -175,6 +251,9 @@ void led_classdev_unregister(struct led_classdev *led_cdev)  	up_write(&led_cdev->trigger_lock);  #endif +	/* Stop blinking */ +	led_brightness_set(led_cdev, LED_OFF); +  	device_unregister(led_cdev->dev);  	down_write(&leds_list_lock); @@ -183,6 +262,30 @@ void led_classdev_unregister(struct led_classdev *led_cdev)  }  EXPORT_SYMBOL_GPL(led_classdev_unregister); +void led_blink_set(struct led_classdev *led_cdev, +		   unsigned long *delay_on, +		   unsigned long *delay_off) +{ +	if (led_cdev->blink_set && +	    led_cdev->blink_set(led_cdev, delay_on, delay_off)) +		return; + +	/* blink with 1 Hz as default if nothing specified */ +	if (!*delay_on && !*delay_off) +		*delay_on = *delay_off = 500; + +	led_set_software_blink(led_cdev, *delay_on, *delay_off); +} +EXPORT_SYMBOL(led_blink_set); + +void led_brightness_set(struct led_classdev *led_cdev, +			enum led_brightness brightness) +{ +	led_stop_software_blink(led_cdev); +	led_cdev->brightness_set(led_cdev, brightness); +} +EXPORT_SYMBOL(led_brightness_set); +  static int __init leds_init(void)  {  	leds_class = class_create(THIS_MODULE, "leds");  |