Skip to contentSkip to conversation

leds: core: add generic support for color LED's

PATCH Superseded Heiner Kallweit 2 participants 2 messages Feb 7, 2016 – Feb 12, 2016 LED

v1 of 8 Superseded Latest is v8 →

lore
Share & tools
lore
#0 lore raw

PATCH 1/2 leds: core: add generic support for color LED's

Add generic support for color LED's.

Basic idea is to use enum led_brightness also for the hue and saturation color components.This allows to implement the color extension w/o changes to struct led_classdev.

Flag LED_BRIGHTNESS_SET_COLOR BIT(24) allows to specify that hue / saturation should be overridden even if the provided values are zero.

Some examples for writing values to /sys/class/leds/<xx>/brightness:
(now also hex notation can be used)

255 -> set full brightness and keep existing color if set
0 -> switch LED off but keep existing color so that it can be restored
if the LED is switched on again later
0x1000000 -> switch LED off and set also hue and saturation to 0 0x00ffff -> set full brightness, full saturation and set hue to 0 (red)

Setting delayed_set_value in case of LED_BLINK_DISABLE was moved to allow using the color information if provided by the caller of led_set_brightness.

---
 drivers/leds/led-class.c |  7 +++++--
 drivers/leds/led-core.c  | 41 ++++++++++++++++++++++++++++++++++-------
 include/linux/leds.h     |  4 ++++
 3 files changed, 43 insertions(+), 9 deletions(-)
drivers/leds/led-class.c +5 -2
index aa84e5b..18a4558 100644
@@ -32,7 +32,10 @@ static ssize_t brightness_show(struct device *dev,
 	/* no lock needed for this */
 	led_update_brightness(led_cdev);
 
-	return sprintf(buf, "%u\n", led_cdev->brightness);
+	if (led_cdev->brightness > LED_FULL)
+		return sprintf(buf, "%#06x\n", led_cdev->brightness);
+	else
+		return sprintf(buf, "%u\n", led_cdev->brightness);
 }
 
 static ssize_t brightness_store(struct device *dev,
@@ -49,7 +52,7 @@ static ssize_t brightness_store(struct device *dev,
 		goto unlock;
 	}
 
-	ret = kstrtoul(buf, 10, &state);
+	ret = kstrtoul(buf, 0, &state);
 	if (ret)
 		goto unlock;
 
drivers/leds/led-core.c +34 -7

99 lines not on this page · raw message

include/linux/leds.h +4 -0
index f203a8f..8e7db72 100644
@@ -50,6 +50,7 @@ struct led_classdev {
 #define LED_SYSFS_DISABLE	(1 << 22)
 #define LED_DEV_CAP_FLASH	(1 << 23)
 #define LED_HW_PLUGGABLE	(1 << 24)
+#define LED_DEV_CAP_COLOR	(1 << 25)
 
 	/* Set LED brightness level
 	 * Must not sleep. Use brightness_set_blocking for drivers
@@ -153,6 +154,9 @@ extern void led_blink_set_oneshot(struct led_classdev *led_cdev,
 				  unsigned long *delay_on,
 				  unsigned long *delay_off,
 				  int invert);
+
+#define LED_BRIGHTNESS_SET_COLOR	BIT(24)
+
 /**
  * led_set_brightness - set LED brightness
  * @led_cdev: the LED to set
Signature · 1 line
2.7.0
#1 lore raw

Hi Heiner,

Thanks for the patches. Generally, I'd like these changes had minimum impact on LED core. This code is not required by existing LED class devices, so we should make it disabled by default and turn it on only for drivers that will use it.

Creating a wrapper would be too much, as you're not adding any new sysfs attributes, but I think that we could add led-hsv-core.c.

Please also create a patch adding a description of RGB LEDs handling to the Documentation/leds/leds-class.txt. It will serve as a set of requirements that we could refer to during review.

Please find my other comments in the code.

Quoted 47 lines · Heiner Kallweit

Add generic support for color LED's.

Basic idea is to use enum led_brightness also for the hue and saturation color components.This allows to implement the color extension w/o changes to struct led_classdev.

Flag LED_BRIGHTNESS_SET_COLOR BIT(24) allows to specify that hue / saturation should be overridden even if the provided values are zero.

Some examples for writing values to /sys/class/leds/<xx>/brightness:
(now also hex notation can be used)

255 -> set full brightness and keep existing color if set
0 -> switch LED off but keep existing color so that it can be restored
if the LED is switched on again later
0x1000000 -> switch LED off and set also hue and saturation to 0

0x00ffff -> set full brightness, full saturation and set hue to 0 (red)

Setting delayed_set_value in case of LED_BLINK_DISABLE was moved to allow using the color information if provided by the caller of led_set_brightness.

---
  drivers/leds/led-class.c |  7 +++++--
  drivers/leds/led-core.c  | 41 ++++++++++++++++++++++++++++++++++-------
  include/linux/leds.h     |  4 ++++
  3 files changed, 43 insertions(+), 9 deletions(-)
drivers/leds/led-class.c +4 -1
index aa84e5b..18a4558 100644
@@ -32,7 +32,10 @@ static ssize_t brightness_show(struct device *dev,
  	/* no lock needed for this */
  	led_update_brightness(led_cdev);
 
-	return sprintf(buf, "%u\n", led_cdev->brightness);
+	if (led_cdev->brightness > LED_FULL)
+		return sprintf(buf, "%#06x\n", led_cdev->brightness);
+	else
+		return sprintf(buf, "%u\n", led_cdev->brightness);
  }
 
  static ssize_t brightness_store(struct device *dev,

drivers/leds/led-class.c static ssize_t brightness_store(struct device *dev, · line 55

Quoted 5 lines
drivers/leds/led-class.c +1 -1
@@ -49,7 +52,7 @@ static ssize_t brightness_store(struct device *dev,
  		goto unlock;
  	}
 
-	ret = kstrtoul(buf, 10, &state);
+	ret = kstrtoul(buf, 0, &state);

Please split this change to the separate patch.

Quoted 11 lines
drivers/leds/led-class.c +0 -0
  	if (ret)
  		goto unlock;
 
drivers/leds/led-core.c +0 -0
index c29e4c9..798e31e 100644
@@ -19,12 +19,32 @@
  #include <linux/rwsem.h>
  #include "leds.h"
 

drivers/leds/led-core.c line 31

Quoted 10 lines
drivers/leds/led-core.c +4 -0
+#define LED_HUE_SAT_MASK	0x00ffff00
+#define LED_BRIGHTNESS_MASK	0x000000ff
+
  DECLARE_RWSEM(leds_list_lock);
  EXPORT_SYMBOL_GPL(leds_list_lock);
 
  LIST_HEAD(leds_list);
  EXPORT_SYMBOL_GPL(leds_list);
 
+static inline enum led_brightness to_hsv(struct led_classdev *cdev,

s/cdev/led_cdev/

Please adhere to the existing naming convention.

Quoted 6 lines
drivers/leds/led-core.c +6 -0
+					 enum led_brightness value)
+{
+	enum led_brightness ret;
+
+	/*
+	 * if LED_BRIGHTNESS_SET_COLOR is set set hue and

drivers/leds/led-core.c line 47

Quoted 10 lines
drivers/leds/led-core.c +10 -0
+	 * saturation even if both are zero
+	 */
+	if (value & LED_BRIGHTNESS_SET_COLOR || value > LED_FULL)
+		ret = value & LED_HUE_SAT_MASK;
+	else
+		ret = cdev->brightness & ~LED_BRIGHTNESS_MASK;
+
+	return ret | min(value & LED_BRIGHTNESS_MASK, cdev->max_brightness);
+}
+

This could be placed in led-hsv-core.c.

Quoted 46 lines
drivers/leds/led-core.c +6 -4
  static int led_set_output(struct led_classdev *cdev,
  			  enum led_brightness value)
  {
@@ -62,7 +82,7 @@ static void led_timer_function(unsigned long data)
  	}
 
  	brightness = led_get_brightness(led_cdev);
-	if (!brightness) {
+	if (!(brightness & LED_BRIGHTNESS_MASK)) {
  		/* Time to switch the LED on. */
  		brightness = led_cdev->blink_brightness;
  		delay = led_cdev->blink_delay_on;
@@ -106,7 +126,6 @@ static void set_brightness_delayed(struct work_struct *ws)
  	int ret = 0;
 
  	if (led_cdev->flags & LED_BLINK_DISABLE) {
-		led_cdev->delayed_set_value = LED_OFF;
  		led_stop_software_blink(led_cdev);
  		led_cdev->flags &= ~LED_BLINK_DISABLE;
  	}
@@ -133,7 +152,7 @@ static void led_set_software_blink(struct led_classdev *led_cdev,
  	if (current_brightness)
  		led_cdev->blink_brightness = current_brightness;
  	if (!led_cdev->blink_brightness)
-		led_cdev->blink_brightness = led_cdev->max_brightness;
+		led_cdev->blink_brightness = to_hsv(led_cdev, LED_FULL);
 
  	led_cdev->blink_delay_on = delay_on;
  	led_cdev->blink_delay_off = delay_off;
@@ -225,6 +244,8 @@ EXPORT_SYMBOL_GPL(led_stop_software_blink);
  void led_set_brightness(struct led_classdev *led_cdev,
  			enum led_brightness brightness)
  {
+	if (!(led_cdev->flags & LED_DEV_CAP_COLOR))
+		brightness &= LED_BRIGHTNESS_MASK;
  	/*
  	 * In case blinking is on delay brightness setting
  	 * until the next timer tick.
@@ -235,12 +256,15 @@ void led_set_brightness(struct led_classdev *led_cdev,
  		 * work queue task to avoid problems in case we are called
  		 * from hard irq context.
  		 */
-		if (brightness == LED_OFF) {
+		if ((brightness & LED_BRIGHTNESS_MASK) == LED_OFF) {
  			led_cdev->flags |= LED_BLINK_DISABLE;
+			led_cdev->delayed_set_value =

drivers/leds/led-core.c void led_set_brightness(struct led_classdev *led_cdev, · line 270

Quoted 10 lines
drivers/leds/led-core.c +3 -1
+					to_hsv(led_cdev, brightness);
  			schedule_work(&led_cdev->set_brightness_work);
  		} else {
  			led_cdev->flags |= LED_BLINK_BRIGHTNESS_CHANGE;
-			led_cdev->blink_brightness = brightness;
+			led_cdev->blink_brightness =
+					to_hsv(led_cdev, brightness);
  		}
  		return;
  	}

It would be cleaner if we provided a separate blink_timer callback in led-hsv-core.c. Relevant version could be chosen in led_init_core() basing on the LED_DEV_CAP_COLOR flag state.

drivers/leds/led-core.c EXPORT_SYMBOL_GPL(led_set_brightness_nopm); · line 292

Quoted 6 lines
drivers/leds/led-core.c +1 -1
@@ -265,7 +289,7 @@ EXPORT_SYMBOL_GPL(led_set_brightness_nopm);
  void led_set_brightness_nosleep(struct led_classdev *led_cdev,
  				enum led_brightness value)
  {
-	led_cdev->brightness = min(value, led_cdev->max_brightness);
+	led_cdev->brightness = to_hsv(led_cdev, value);

This is misleading. One could wonder what hsv has in common with their monochromatic LED. I propose to add a helper that will choose suitable method of constraining brightness value depending on whether LED_DEV_CAP_COLOR flag is set.

drivers/leds/led-core.c int led_set_brightness_sync(struct led_classdev *led_cdev, · line 310

Quoted 12 lines
drivers/leds/led-core.c +4 -1
  	if (led_cdev->flags & LED_SUSPENDED)
  		return;
@@ -280,7 +304,10 @@ int led_set_brightness_sync(struct led_classdev *led_cdev,
  	if (led_cdev->blink_delay_on || led_cdev->blink_delay_off)
  		return -EBUSY;
 
-	led_cdev->brightness = min(value, led_cdev->max_brightness);
+	if (!(led_cdev->flags & LED_DEV_CAP_COLOR))
+		value &= LED_BRIGHTNESS_MASK;
+
+	led_cdev->brightness = to_hsv(led_cdev, value);

Ditto.

Quoted 15 lines
drivers/leds/led-core.c +0 -0
  	if (led_cdev->flags & LED_SUSPENDED)
  		return 0;
include/linux/leds.h +1 -0
index f203a8f..8e7db72 100644
@@ -50,6 +50,7 @@ struct led_classdev {
  #define LED_SYSFS_DISABLE	(1 << 22)
  #define LED_DEV_CAP_FLASH	(1 << 23)
  #define LED_HW_PLUGGABLE	(1 << 24)
+#define LED_DEV_CAP_COLOR	(1 << 25)
 
  	/* Set LED brightness level
  	 * Must not sleep. Use brightness_set_blocking for drivers

include/linux/leds.h extern void led_blink_set_oneshot(struct led_classdev *led_cdev, · line 159

Quoted 6 lines
include/linux/leds.h +3 -0
@@ -153,6 +154,9 @@ extern void led_blink_set_oneshot(struct led_classdev *led_cdev,
  				  unsigned long *delay_on,
  				  unsigned long *delay_off,
  				  int invert);
+
+#define LED_BRIGHTNESS_SET_COLOR	BIT(24)
+

Please drop BRIGHTNESS segment.

Quoted 4 lines
include/linux/leds.h +0 -0
  /**
   * led_set_brightness - set LED brightness
   * @led_cdev: the LED to set
Signature · 2 lines
Best regards,
Jacek Anaszewski