diff options
Diffstat (limited to 'drivers/leds/led-class.c')
| -rw-r--r-- | drivers/leds/led-class.c | 167 |
1 files changed, 77 insertions, 90 deletions
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 211e21f34bd..f37d63cf726 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -15,7 +15,6 @@ #include <linux/list.h> #include <linux/spinlock.h> #include <linux/device.h> -#include <linux/sysdev.h> #include <linux/timer.h> #include <linux/err.h> #include <linux/ctype.h> @@ -30,7 +29,7 @@ static void led_update_brightness(struct led_classdev *led_cdev) led_cdev->brightness = led_cdev->brightness_get(led_cdev); } -static ssize_t led_brightness_show(struct device *dev, +static ssize_t brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { struct led_classdev *led_cdev = dev_get_drvdata(dev); @@ -41,28 +40,24 @@ static ssize_t led_brightness_show(struct device *dev, return sprintf(buf, "%u\n", led_cdev->brightness); } -static ssize_t led_brightness_store(struct device *dev, +static ssize_t brightness_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct led_classdev *led_cdev = dev_get_drvdata(dev); + unsigned long state; ssize_t ret = -EINVAL; - char *after; - unsigned long state = simple_strtoul(buf, &after, 10); - size_t count = after - buf; - if (isspace(*after)) - count++; + ret = kstrtoul(buf, 10, &state); + if (ret) + return ret; - if (count == size) { - ret = count; + if (state == LED_OFF) + led_trigger_remove(led_cdev); + __led_set_brightness(led_cdev, state); - if (state == LED_OFF) - led_trigger_remove(led_cdev); - led_set_brightness(led_cdev, state); - } - - return ret; + return size; } +static DEVICE_ATTR_RW(brightness); static ssize_t led_max_brightness_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -71,14 +66,35 @@ static ssize_t led_max_brightness_show(struct device *dev, return sprintf(buf, "%u\n", led_cdev->max_brightness); } +static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL); + +#ifdef CONFIG_LEDS_TRIGGERS +static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); +static struct attribute *led_trigger_attrs[] = { + &dev_attr_trigger.attr, + NULL, +}; +static const struct attribute_group led_trigger_group = { + .attrs = led_trigger_attrs, +}; +#endif + +static struct attribute *led_class_attrs[] = { + &dev_attr_brightness.attr, + &dev_attr_max_brightness.attr, + NULL, +}; + +static const struct attribute_group led_group = { + .attrs = led_class_attrs, +}; -static struct device_attribute led_class_attrs[] = { - __ATTR(brightness, 0644, led_brightness_show, led_brightness_store), - __ATTR(max_brightness, 0444, led_max_brightness_show, NULL), +static const struct attribute_group *led_groups[] = { + &led_group, #ifdef CONFIG_LEDS_TRIGGERS - __ATTR(trigger, 0644, led_trigger_show, led_trigger_store), + &led_trigger_group, #endif - __ATTR_NULL, + NULL, }; static void led_timer_function(unsigned long data) @@ -88,7 +104,12 @@ static void led_timer_function(unsigned long data) unsigned long delay; if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) { - led_set_brightness(led_cdev, LED_OFF); + __led_set_brightness(led_cdev, LED_OFF); + return; + } + + if (led_cdev->flags & LED_BLINK_ONESHOT_STOP) { + led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP; return; } @@ -106,54 +127,35 @@ static void led_timer_function(unsigned long data) delay = led_cdev->blink_delay_off; } - led_set_brightness(led_cdev, brightness); + __led_set_brightness(led_cdev, brightness); + + /* Return in next iteration if led is in one-shot mode and we are in + * the final blink state so that the led is toggled each delay_on + + * delay_off milliseconds in worst case. + */ + if (led_cdev->flags & LED_BLINK_ONESHOT) { + if (led_cdev->flags & LED_BLINK_INVERT) { + if (brightness) + led_cdev->flags |= LED_BLINK_ONESHOT_STOP; + } else { + if (!brightness) + led_cdev->flags |= LED_BLINK_ONESHOT_STOP; + } + } mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay)); } -static void led_stop_software_blink(struct led_classdev *led_cdev) +static void set_brightness_delayed(struct work_struct *ws) { - /* 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; + struct led_classdev *led_cdev = + container_of(ws, struct led_classdev, set_brightness_work); 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_set_brightness(led_cdev, led_cdev->delayed_set_value); } - /** * led_classdev_suspend - suspend an led_classdev. * @led_cdev: the led_classdev to suspend. @@ -176,7 +178,7 @@ void led_classdev_resume(struct led_classdev *led_cdev) } EXPORT_SYMBOL_GPL(led_classdev_resume); -static int led_suspend(struct device *dev, pm_message_t state) +static int led_suspend(struct device *dev) { struct led_classdev *led_cdev = dev_get_drvdata(dev); @@ -196,6 +198,11 @@ static int led_resume(struct device *dev) return 0; } +static const struct dev_pm_ops leds_class_dev_pm_ops = { + .suspend = led_suspend, + .resume = led_resume, +}; + /** * led_classdev_register - register a new object of led_classdev class. * @parent: The device to register. @@ -221,6 +228,8 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) led_update_brightness(led_cdev); + INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed); + init_timer(&led_cdev->blink_timer); led_cdev->blink_timer.function = led_timer_function; led_cdev->blink_timer.data = (unsigned long)led_cdev; @@ -229,7 +238,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) led_trigger_set_default(led_cdev); #endif - printk(KERN_DEBUG "Registered led device: %s\n", + dev_dbg(parent, "Registered led device: %s\n", led_cdev->name); return 0; @@ -251,8 +260,11 @@ void led_classdev_unregister(struct led_classdev *led_cdev) up_write(&led_cdev->trigger_lock); #endif + cancel_work_sync(&led_cdev->set_brightness_work); + /* Stop blinking */ - led_brightness_set(led_cdev, LED_OFF); + led_stop_software_blink(led_cdev); + led_set_brightness(led_cdev, LED_OFF); device_unregister(led_cdev->dev); @@ -262,38 +274,13 @@ 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"); if (IS_ERR(leds_class)) return PTR_ERR(leds_class); - leds_class->suspend = led_suspend; - leds_class->resume = led_resume; - leds_class->dev_attrs = led_class_attrs; + leds_class->pm = &leds_class_dev_pm_ops; + leds_class->dev_groups = led_groups; return 0; } |
