Re: [PATCH/RFC 1/8] leds: Add sysfs and kernel internal API for flash LEDs

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




Hi Jacek,

Thanks for the patchset. It's very nice in general. I have a few comments
below.

On Thu, Mar 20, 2014 at 03:51:03PM +0100, Jacek Anaszewski wrote:
> Some LED devices support two operation modes - torch and
> flash. This patch provides support for flash LED devices
> in the LED subsystem by introducing new sysfs attributes
> and kernel internal interface. The attributes being
> introduced are: flash_mode, flash_timeout, max_flash_timeout,
> flash_fault and hw_triggered.
> The modifications aim to be compatible with V4L2 framework
> requirements related to the flash devices management. The
> design assumes that V4L2 driver can take of the LED class
> device control and communicate with it through the kernel
> internal interface. The LED sysfs interface is made
> unavailable then.
> 
> Signed-off-by: Jacek Anaszewski <j.anaszewski@xxxxxxxxxxx>
> Acked-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx>
> Cc: Bryan Wu <cooloney@xxxxxxxxx>
> Cc: Richard Purdie <rpurdie@xxxxxxxxx>
> ---
>  drivers/leds/led-class.c    |  216 +++++++++++++++++++++++++++++++++++++++++--
>  drivers/leds/led-core.c     |  124 +++++++++++++++++++++++--
>  drivers/leds/led-triggers.c |   17 +++-
>  drivers/leds/leds.h         |    9 ++
>  include/linux/leds.h        |  136 +++++++++++++++++++++++++++
>  5 files changed, 486 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
> index f37d63c..0510532 100644
> --- a/drivers/leds/led-class.c
> +++ b/drivers/leds/led-class.c
> @@ -4,6 +4,9 @@
>   * Copyright (C) 2005 John Lenz <lenz@xxxxxxxxxxx>
>   * Copyright (C) 2005-2007 Richard Purdie <rpurdie@xxxxxxxxxxxxxx>
>   *
> + * Copyright (C) 2014 Samsung Electronics Co., Ltd.
> + * Author: Jacek Anaszewski <j.anaszewski@xxxxxxxxxxx>
> + *
>   * 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.
> @@ -13,6 +16,7 @@
>  #include <linux/kernel.h>
>  #include <linux/init.h>
>  #include <linux/list.h>
> +#include <linux/slab.h>

The list seems to be in quite a disarray. Could you order it as you're
adding a new header to it?

>  #include <linux/spinlock.h>
>  #include <linux/device.h>
>  #include <linux/timer.h>
> @@ -45,28 +49,186 @@ static ssize_t brightness_store(struct device *dev,
>  {
>  	struct led_classdev *led_cdev = dev_get_drvdata(dev);
>  	unsigned long state;
> -	ssize_t ret = -EINVAL;
> +	ssize_t ret;
> +
> +	mutex_lock(&led_cdev->led_lock);
> +
> +	if (led_sysfs_is_locked(led_cdev)) {
> +		ret = -EBUSY;
> +		goto exit_unlock;
> +	}
>  
>  	ret = kstrtoul(buf, 10, &state);
>  	if (ret)
> -		return ret;
> +		goto exit_unlock;
>  
>  	if (state == LED_OFF)
>  		led_trigger_remove(led_cdev);
> -	__led_set_brightness(led_cdev, state);
> +	led_set_brightness(led_cdev, state);
> +	ret = size;
>  
> -	return size;
> +exit_unlock:
> +	mutex_unlock(&led_cdev->led_lock);
> +	return ret;
>  }
>  static DEVICE_ATTR_RW(brightness);
>  
> -static ssize_t led_max_brightness_show(struct device *dev,
> +static ssize_t max_brightness_show(struct device *dev,
>  		struct device_attribute *attr, char *buf)
>  {
>  	struct led_classdev *led_cdev = dev_get_drvdata(dev);
>  
>  	return sprintf(buf, "%u\n", led_cdev->max_brightness);
>  }
> -static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL);
> +static DEVICE_ATTR_RO(max_brightness);
> +
> +static ssize_t flash_mode_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 flash_mode;
> +	ssize_t ret;
> +
> +	mutex_lock(&led_cdev->led_lock);
> +
> +	if (led_sysfs_is_locked(led_cdev)) {
> +		ret = -EBUSY;
> +		goto exit_unlock;
> +	}
> +
> +	ret = kstrtoul(buf, 10, &flash_mode);
> +	if (ret)
> +		goto exit_unlock;
> +
> +	if (flash_mode < 0 && flash_mode > 1)
> +		return -EINVAL;
> +
> +	if (led_is_flash_mode(led_cdev) == flash_mode) {
> +		ret = size;
> +		goto exit_unlock;
> +	}
> +
> +	led_trigger_remove(led_cdev);
> +
> +	led_set_flash_mode(led_cdev, flash_mode);
> +	ret = size;
> +
> +exit_unlock:
> +	mutex_unlock(&led_cdev->led_lock);
> +	return ret;
> +}
> +
> +static ssize_t flash_mode_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%u\n", led_is_flash_mode(led_cdev));
> +}
> +static DEVICE_ATTR_RW(flash_mode);
> +
> +static ssize_t flash_timeout_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 flash_timeout;
> +	ssize_t ret;
> +
> +	mutex_lock(&led_cdev->led_lock);
> +
> +	if (led_sysfs_is_locked(led_cdev)) {
> +		ret = -EBUSY;
> +		goto exit_unlock;
> +	}
> +
> +	ret = kstrtoul(buf, 10, &flash_timeout);
> +	if (ret)
> +		goto exit_unlock;
> +
> +	led_set_flash_timeout(led_cdev, flash_timeout);
> +	ret = size;
> +
> +exit_unlock:
> +	mutex_unlock(&led_cdev->led_lock);
> +	return ret;
> +}
> +
> +static ssize_t flash_timeout_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +	struct led_flash *flash = led_cdev->flash;
> +
> +	return sprintf(buf, "%lu\n", flash->timeout);
> +}
> +static DEVICE_ATTR_RW(flash_timeout);
> +
> +static ssize_t max_flash_timeout_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +	struct led_flash *flash = led_cdev->flash;
> +
> +	return sprintf(buf, "%lu\n", flash->max_timeout);
> +}
> +static DEVICE_ATTR_RO(max_flash_timeout);
> +
> +static ssize_t flash_fault_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +	unsigned int fault;
> +	int ret;
> +
> +	ret = led_get_flash_fault(led_cdev, &fault);
> +	if (ret < 0)
> +		return -EINVAL;
> +
> +	return sprintf(buf, "%x\n", fault);

How about ...0x%8.8x... or such?

> +}
> +static DEVICE_ATTR_RO(flash_fault);
> +
> +static ssize_t hw_triggered_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 hw_triggered;
> +	ssize_t ret;
> +
> +	mutex_lock(&led_cdev->led_lock);
> +
> +	if (led_sysfs_is_locked(led_cdev)) {
> +		ret = -EBUSY;
> +		goto unlock;
> +	}
> +
> +	ret = kstrtoul(buf, 10, &hw_triggered);
> +	if (ret)
> +		goto unlock;
> +
> +	if (hw_triggered > 1) {
> +		ret = -EINVAL;
> +		goto unlock;
> +	}
> +
> +	ret = led_set_hw_triggered(led_cdev, hw_triggered);
> +	if (ret < 0)
> +		goto unlock;
> +	ret = size;
> +
> +unlock:
> +	mutex_unlock(&led_cdev->led_lock);
> +	return ret;
> +}
> +
> +static ssize_t hw_triggered_show(struct device *dev,
> +		struct device_attribute *attr, char *buf)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +
> +	return sprintf(buf, "%u\n", led_cdev->hw_triggered);
> +}
> +static DEVICE_ATTR_RW(hw_triggered);
>  
>  #ifdef CONFIG_LEDS_TRIGGERS
>  static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
> @@ -82,6 +244,11 @@ static const struct attribute_group led_trigger_group = {
>  static struct attribute *led_class_attrs[] = {
>  	&dev_attr_brightness.attr,
>  	&dev_attr_max_brightness.attr,
> +	&dev_attr_flash_mode.attr,
> +	&dev_attr_flash_timeout.attr,
> +	&dev_attr_max_flash_timeout.attr,
> +	&dev_attr_flash_fault.attr,
> +	&dev_attr_hw_triggered.attr,
>  	NULL,
>  };
>  
> @@ -204,20 +371,54 @@ static const struct dev_pm_ops leds_class_dev_pm_ops = {
>  };
>  
>  /**
> + * led_classdev_init_flash - add support for flash led
> + * @led_cdev: the device to add flash led support to
> + *
> + * Returns: 0 on success, error code on failure.
> + */
> +int led_classdev_init_flash(struct led_classdev *led_cdev)
> +{
> +	if (led_cdev->flash)
> +		return -EINVAL;
> +
> +	led_cdev->flash = kzalloc(sizeof(struct led_flash), GFP_KERNEL);
> +	if (!led_cdev->flash)
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(led_classdev_init_flash);
> +
> +/**
>   * led_classdev_register - register a new object of led_classdev class.
>   * @parent: The device to register.
>   * @led_cdev: the led_classdev structure for this device.
>   */
>  int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
>  {
> +	struct led_flash_ops *ops;
> +
> +	if (led_cdev->flash) {
> +		ops = &led_cdev->flash->ops;
> +		if (!ops || !ops->strobe_set || !ops->mode_set ||
> +			!ops->fault_get) {
> +			dev_dbg(parent,
> +				"Flash LED ops validation failed for the %s\n"
> +				"LED device", led_cdev->name);

Splitting strings breaks grep... I think the 80 character limit rule is
a lesser problem in this case.

> +			return -EINVAL;
> +		}
> +	}
> +
>  	led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
>  				      "%s", led_cdev->name);
>  	if (IS_ERR(led_cdev->dev))
>  		return PTR_ERR(led_cdev->dev);
>  
> +
>  #ifdef CONFIG_LEDS_TRIGGERS
>  	init_rwsem(&led_cdev->trigger_lock);
>  #endif
> +	mutex_init(&led_cdev->led_lock);
>  	/* add to the list of leds */
>  	down_write(&leds_list_lock);
>  	list_add_tail(&led_cdev->node, &leds_list);
> @@ -271,6 +472,8 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
>  	down_write(&leds_list_lock);
>  	list_del(&led_cdev->node);
>  	up_write(&leds_list_lock);
> +
> +	kfree(led_cdev->flash);

mutex_destroy() here as well?

>  }
>  EXPORT_SYMBOL_GPL(led_classdev_unregister);
>  
> @@ -293,5 +496,6 @@ subsys_initcall(leds_init);
>  module_exit(leds_exit);
>  
>  MODULE_AUTHOR("John Lenz, Richard Purdie");
> +MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@xxxxxxxxxxx>");
>  MODULE_LICENSE("GPL");
>  MODULE_DESCRIPTION("LED Class Interface");
> diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
> index 71b40d3..093703c 100644
> --- a/drivers/leds/led-core.c
> +++ b/drivers/leds/led-core.c
> @@ -5,6 +5,9 @@
>   *
>   * Author: Richard Purdie <rpurdie@xxxxxxxxxxxxxx>
>   *
> + * Copyright (C) 2014 Samsung Electronics Co., Ltd.
> + * Author: Jacek Anaszewski <j.anaszewski@xxxxxxxxxxx>
> + *
>   * 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.
> @@ -71,10 +74,27 @@ static void led_blink_setup(struct led_classdev *led_cdev,
>  	led_set_software_blink(led_cdev, *delay_on, *delay_off);
>  }
>  
> +static int led_flash_strobe_set(struct led_classdev *led_cdev,
> +			enum led_brightness brightness,
> +			unsigned long *timeout)
> +{
> +	if (!get_flash_op(led_cdev, strobe_set))
> +		return -EINVAL;
> +
> +	if (brightness > led_cdev->max_brightness)
> +		brightness = led_cdev->max_brightness;
> +	call_flash_op(strobe_set, led_cdev, brightness, timeout);
> +
> +	return 0;
> +}
> +
>  void led_blink_set(struct led_classdev *led_cdev,
>  		   unsigned long *delay_on,
>  		   unsigned long *delay_off)
>  {
> +	if (led_is_flash_mode(led_cdev))
> +		return;
> +
>  	del_timer_sync(&led_cdev->blink_timer);
>  
>  	led_cdev->flags &= ~LED_BLINK_ONESHOT;
> @@ -89,6 +109,9 @@ void led_blink_set_oneshot(struct led_classdev *led_cdev,
>  			   unsigned long *delay_off,
>  			   int invert)
>  {
> +	if (led_is_flash_mode(led_cdev))
> +		return;
> +
>  	if ((led_cdev->flags & LED_BLINK_ONESHOT) &&
>  	     timer_pending(&led_cdev->blink_timer))
>  		return;
> @@ -116,13 +139,100 @@ EXPORT_SYMBOL_GPL(led_stop_software_blink);
>  void led_set_brightness(struct led_classdev *led_cdev,
>  			enum led_brightness brightness)
>  {
> -	/* delay brightness setting if need to stop soft-blink timer */
> -	if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) {
> -		led_cdev->delayed_set_value = brightness;
> -		schedule_work(&led_cdev->set_brightness_work);
> -		return;
> +	struct led_flash *flash = led_cdev->flash;
> +	int ret;
> +
> +	if (led_is_flash_mode(led_cdev)) {
> +		if (brightness > 0) {
> +			ret = led_flash_strobe_set(led_cdev,
> +					brightness,
> +					&flash->timeout);

Indentation. Could you align the rest of the arguments to the opening
parenthesis?

> +			if (ret < 0)
> +				dev_err(led_cdev->dev,
> +					"Failed to setup flash strobe (%d)",
> +					ret);
> +		} else {
> +			__led_set_brightness(led_cdev, 0);
> +		}
> +	} else {
> +		/* delay brightness setting if need to stop soft-blink timer */
> +		if (led_cdev->blink_delay_on || led_cdev->blink_delay_off) {
> +			led_cdev->delayed_set_value = brightness;
> +			schedule_work(&led_cdev->set_brightness_work);
> +			return;
> +		}
> +		__led_set_brightness(led_cdev, brightness);
>  	}
> -
> -	__led_set_brightness(led_cdev, brightness);
>  }
>  EXPORT_SYMBOL(led_set_brightness);
> +
> +int led_set_flash_mode(struct led_classdev *led_cdev,
> +			bool flash_mode)
> +{
> +	if (!get_flash_op(led_cdev, mode_set))
> +		return -EINVAL;
> +
> +	call_flash_op(mode_set, led_cdev, flash_mode);
> +
> +	if (flash_mode)
> +		led_cdev->flags |= LED_MODE_FLASH;
> +	else
> +		led_cdev->flags &= ~LED_MODE_FLASH;
> +
> +	led_set_brightness(led_cdev, 0);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(led_set_flash_mode);
> +
> +/* Caller must ensure led_cdev->led_lock held */
> +void led_sysfs_lock(struct led_classdev *led_cdev)
> +{
> +	led_cdev->flags |= LED_SYSFS_LOCK;
> +}
> +EXPORT_SYMBOL(led_sysfs_lock);
> +
> +/* Caller must ensure led_cdev->led_lock held */
> +void led_sysfs_unlock(struct led_classdev *led_cdev)
> +{
> +	led_cdev->flags &= ~LED_SYSFS_LOCK;
> +}
> +EXPORT_SYMBOL(led_sysfs_unlock);
> +
> +void led_set_flash_timeout(struct led_classdev *led_cdev, unsigned long timeout)
> +{
> +	struct led_flash *flash = led_cdev->flash;
> +
> +	/*
> +	 * Flash timeout is passed to a flash LED driver
> +	 * through the strobe_set callback - here we only
> +	 * cache the value.
> +	 */
> +	if (timeout > flash->max_timeout)

You could use the min() macro here.

> +		flash->timeout = flash->max_timeout;
> +	else
> +		flash->timeout = timeout;
> +}
> +EXPORT_SYMBOL(led_set_flash_timeout);
> +
> +int led_get_flash_fault(struct led_classdev *led_cdev, unsigned int *fault)
> +{
> +	if (!get_flash_op(led_cdev, fault_get))
> +		return -EINVAL;
> +
> +	return call_flash_op(fault_get, led_cdev, fault);
> +}
> +EXPORT_SYMBOL(led_get_flash_fault);
> +
> +int led_set_hw_triggered(struct led_classdev *led_cdev, bool enable)
> +{
> +	if (led_cdev->has_hw_trig) {
> +		__led_set_brightness(led_cdev, 0);

...why?

> +		led_cdev->hw_triggered = enable;
> +	} else if (enable) {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(led_set_hw_triggered);
> diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
> index df1a7c1..448e7c8 100644
> --- a/drivers/leds/led-triggers.c
> +++ b/drivers/leds/led-triggers.c
> @@ -37,6 +37,15 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
>  	char trigger_name[TRIG_NAME_MAX];
>  	struct led_trigger *trig;
>  	size_t len;
> +	int ret = count;
> +
> +	mutex_lock(&led_cdev->led_lock);
> +
> +	if (led_sysfs_is_locked(led_cdev) ||
> +	    led_is_flash_mode(led_cdev)) {

Fits on the previous line.

> +		ret = -EBUSY;
> +		goto exit_unlock;
> +	}
>  
>  	trigger_name[sizeof(trigger_name) - 1] = '\0';
>  	strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
> @@ -47,7 +56,7 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
>  
>  	if (!strcmp(trigger_name, "none")) {
>  		led_trigger_remove(led_cdev);
> -		return count;
> +		goto exit_unlock;
>  	}
>  
>  	down_read(&triggers_list_lock);
> @@ -58,12 +67,14 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
>  			up_write(&led_cdev->trigger_lock);
>  
>  			up_read(&triggers_list_lock);
> -			return count;
> +			goto exit_unlock;
>  		}
>  	}
>  	up_read(&triggers_list_lock);
>  
> -	return -EINVAL;
> +exit_unlock:
> +	mutex_unlock(&led_cdev->led_lock);
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(led_trigger_store);
>  
> diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h
> index 4c50365..44cc384 100644
> --- a/drivers/leds/leds.h
> +++ b/drivers/leds/leds.h
> @@ -17,6 +17,15 @@
>  #include <linux/rwsem.h>
>  #include <linux/leds.h>
>  
> +

I'd probably spare a newline here.

> +#define call_flash_op(op, args...)		\
> +	((led_cdev)->flash->ops.op(args))
> +
> +#define get_flash_op(led_cdev, op)		\
> +	((led_cdev)->flash ?			\
> +		(led_cdev)->flash->ops.op :	\
> +		0)
> +
>  static inline void __led_set_brightness(struct led_classdev *led_cdev,
>  					enum led_brightness value)
>  {
> diff --git a/include/linux/leds.h b/include/linux/leds.h
> index 0287ab2..1bf0ab3 100644
> --- a/include/linux/leds.h
> +++ b/include/linux/leds.h
> @@ -17,6 +17,14 @@
>  #include <linux/rwsem.h>
>  #include <linux/timer.h>
>  #include <linux/workqueue.h>
> +#include <linux/mutex.h>

mutex.h should be earlier in the list of included files.

> +#include <media/v4l2-device.h>
> +
> +#define LED_FAULT_OVER_VOLTAGE		(1 << 0)
> +#define LED_FAULT_TIMEOUT		(1 << 1)
> +#define LED_FAULT_OVER_TEMPERATURE	(1 << 2)
> +#define LED_FAULT_SHORT_CIRCUIT		(1 << 3)
> +#define LED_FAULT_OVER_CURRENT		(1 << 4)

This patch went in to the media-tree some time ago. I wonder if the relevant
bits should be added here now as well.

commit 935aa6b2e8a911e81baecec0537dd7e478dc8c91
Author: Daniel Jeong <gshark.jeong@xxxxxxxxx>
Date:   Mon Mar 3 06:52:08 2014 -0300

    [media] v4l2-controls.h: Add addtional Flash fault bits
    
    Three Flash fault are added. V4L2_FLASH_FAULT_UNDER_VOLTAGE for the case low
    voltage below the min. limit. V4L2_FLASH_FAULT_INPUT_VOLTAGE for the case
    falling input voltage and chip adjust flash current not occur under voltage
    event. V4L2_FLASH_FAULT_LED_OVER_TEMPERATURE for the case the temperature
    exceed the maximun limit
    
    Signed-off-by: Daniel Jeong <gshark.jeong@xxxxxxxxx>
    Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxx>
    Signed-off-by: Mauro Carvalho Chehab <m.chehab@xxxxxxxxxxx>

>  struct device;
>  /*
> @@ -29,6 +37,33 @@ enum led_brightness {
>  	LED_FULL	= 255,
>  };
>  
> +struct led_classdev;
> +
> +struct led_flash_ops {
> +	/* Set torch/flash LED mode */
> +	void	(*mode_set)(struct led_classdev *led_cdev,
> +					bool flash_mode);
> +	/* Setup flash strobe */
> +	void	(*strobe_set)(struct led_classdev *led_cdev,
> +					enum led_brightness brightness,
> +					unsigned long *timeout);

Can't the above operations fail?

Does this also assume that strobing the flash will always configure the
brightness and timeout as well? It'd be rather nice if it didn't: strobing
the flash is time critical, and as the I2C bus is quite slow, additional
device access should be avoided especially when timing is critical.

> +	/* Get the flash LED fault */
> +	int	(*fault_get)(struct led_classdev *led_cdev,
> +					unsigned int *fault);
> +};
> +
> +struct led_flash {
> +	/* flash led specific ops */
> +	struct led_flash_ops	ops;

const?

> +	/*
> +	 * maximum allowed flash timeout - it is read only and
> +	 * should be initialized by the driver

s/should/must/ ?

> +	 */
> +	unsigned long		max_timeout;
> +	/* current flash timeout */
> +	unsigned long		timeout;
> +};
> +
>  struct led_classdev {
>  	const char		*name;
>  	int			 brightness;
> @@ -42,6 +77,8 @@ struct led_classdev {
>  #define LED_BLINK_ONESHOT	(1 << 17)
>  #define LED_BLINK_ONESHOT_STOP	(1 << 18)
>  #define LED_BLINK_INVERT	(1 << 19)
> +#define LED_MODE_FLASH		(1 << 20)
> +#define LED_SYSFS_LOCK		(1 << 21)
>  
>  	/* Set LED brightness level */
>  	/* Must not sleep, use a workqueue if needed */
> @@ -69,6 +106,19 @@ struct led_classdev {
>  	unsigned long		 blink_delay_on, blink_delay_off;
>  	struct timer_list	 blink_timer;
>  	int			 blink_brightness;
> +	struct led_flash	*flash;
> +	/*
> +	 * Determines whether a device supports triggering a flash led
> +	 * with use of a dedicated hardware pin
> +	 */
> +	bool			has_hw_trig;
> +	/* If true then hardware pin triggers flash strobe */
> +	bool			hw_triggered;
> +	/*
> +	 * Ensures consistent LED sysfs access and protects
> +	 * LED sysfs locking mechanism
> +	 */
> +	struct mutex		led_lock;
>  
>  	struct work_struct	set_brightness_work;
>  	int			delayed_set_value;
> @@ -90,6 +140,7 @@ extern int led_classdev_register(struct device *parent,
>  extern void led_classdev_unregister(struct led_classdev *led_cdev);
>  extern void led_classdev_suspend(struct led_classdev *led_cdev);
>  extern void led_classdev_resume(struct led_classdev *led_cdev);
> +extern int led_classdev_init_flash(struct led_classdev *led_cdev);
>  
>  /**
>   * led_blink_set - set blinking with software fallback
> @@ -138,6 +189,91 @@ extern void led_blink_set_oneshot(struct led_classdev *led_cdev,
>   */
>  extern void led_set_brightness(struct led_classdev *led_cdev,
>  			       enum led_brightness brightness);
> +/**

I think the above line should be moved to the surplus comment stash or
something? :-)

> +/**
> + * led_set_flash_mode - set LED flash mode
> + * @led_cdev: the LED to set
> + * @flash_mode: true - flash mode, false - torch mode
> + *
> + * Returns: 0 on success, -EINVAL on failure
> + *
> + * Switch an LED's flash mode and set brightness to 0.
> + */
> +extern int led_set_flash_mode(struct led_classdev *led_cdev,
> +			      bool flash_mode);
> +
> +/**
> + * led_set_flash_timeout - set flash LED timeout
> + * @led_cdev: the LED to set
> + * @timeout: the flash timeout to be set
> + *
> + * Set the flash strobe duration.
> + */
> +extern void led_set_flash_timeout(struct led_classdev *led_cdev,
> +				  unsigned long timeout);
> +
> +/**
> + * led_get_flash_fault - get the flash LED fault
> + * @led_cdev: the LED to query
> + * @fault: bitmask containing flash faults
> + *
> + * Returns: 0 on success, -EINVAL on failure
> + *
> + * Get the flash LED fault.
> + */
> +extern int led_get_flash_fault(struct led_classdev *led_cdev,
> +			       unsigned int *fault);
> +
> +/**
> + * led_set_hw_triggered - set the flash LED hw_triggered mode
> + * @led_cdev: the LED to set
> + * @enable: the state to set
> + *
> + * Returns: 0 on success, -EINVAL on failure
> + *
> + * Enable/disable triggering the flash LED via hardware pin
> + */
> +extern int led_set_hw_triggered(struct led_classdev *led_cdev, bool enable);
> +
> +/**
> + * led_sysfs_lock - lock LED sysfs interface
> + * @led_cdev: the LED to set
> + *
> + * Lock the LED's sysfs interface
> + */
> +extern void led_sysfs_lock(struct led_classdev *led_cdev);
> +
> +/**
> + * led_sysfs_unlock - unlock LED sysfs interface
> + * @led_cdev: the LED to set
> + *
> + * Unlock the LED's sysfs interface
> + */
> +extern void led_sysfs_unlock(struct led_classdev *led_cdev);
> +
> +/**
> + * led_is_flash_mode
> + * @led_cdev: the LED query
> + *
> + * Returns: true if a led device is in the flash mode, false if it is
> +	    is in the torch mode
> + */
> +static inline bool led_is_flash_mode(struct led_classdev *led_cdev)
> +{
> +	return !!(led_cdev->flags & LED_MODE_FLASH);

I don't think you necessarily need !!() --- converting any non-zero integer
to bool is true. Same below.

> +}
> +
> +/**
> + * led_sysfs_is_locked
> + * @led_cdev: the LED query about
> + *
> + * Returns: true if the sysfs interface of the led is disabled,
> + *	    false otherwise
> + */
> +static inline bool led_sysfs_is_locked(struct led_classdev *led_cdev)
> +{
> +	return !!(led_cdev->flags & LED_SYSFS_LOCK);
> +}
>  
>  /*
>   * LED Triggers

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@xxxxxx	XMPP: sailus@xxxxxxxxxxxxxx
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux