Skip to contentSkip to conversation

leds: triggers: add led_trigger_range_event for RGB triggers

PATCH In review Heiner Kallweit 2 participants 3 messages Mar 13, 2016 – Mar 19, 2016 LED

lore
Share & tools
lore
#0 lore raw

PATCH 2/3 leds: triggers: add led_trigger_range_event for RGB triggers

Add led_trigger_range_event() and supporting struct led_trigger_range allowing to map a value within a value range to a color within a color range.
Simple example would be to map a temperature with in a range to a color between green and red.

---
 drivers/leds/led-triggers.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/leds.h        | 14 ++++++++++++++
 2 files changed, 56 insertions(+)
drivers/leds/led-triggers.c +42 -0
index 3ccf88b..5cab10b 100644
@@ -195,6 +195,9 @@ int led_trigger_register(struct led_trigger *trig)
 	struct led_classdev *led_cdev;
 	struct led_trigger *_trig;
 
+	if (trig->range && trig->range->start >= trig->range->end)
+		return -EINVAL;
+
 	rwlock_init(&trig->leddev_list_lock);
 	INIT_LIST_HEAD(&trig->led_cdevs);
 
@@ -294,6 +297,45 @@ void led_trigger_event(struct led_trigger *trig,
 }
 EXPORT_SYMBOL_GPL(led_trigger_event);
 
+static inline int led_trigger_interpolate(int value,
+					  const struct led_trigger_range *range,
+					  int x_start, int x_end)
+{
+		return x_start + DIV_ROUND_CLOSEST((value - range->start) *
+		       (x_end - x_start), range->end - range->start);
+}
+
+void led_trigger_range_event(struct led_trigger *trig, int value)
+{
+	int h, s, v, hs, he, ss, se, vs, ve, hdiff;
+
+	if (!trig->range)
+		return;
+
+	hs = trig->range->color_start >> 16 & 0xff;
+	he = trig->range->color_end >> 16 & 0xff;
+	ss = trig->range->color_start >> 8 & 0xff;
+	se = trig->range->color_end >> 8 & 0xff;
+	vs = trig->range->color_start & 0xff;
+	ve = trig->range->color_end & 0xff;
+	hdiff = (252 + he - hs) % 252;
+
+	/* on color circle, choose shortest way from start to end */
+	if (hdiff <= 126 && he < hs)
+		he += 252;
+	else if (hdiff > 126 && he > hs)
+		hs += 252;
+
+	value = clamp(value, trig->range->start, trig->range->end);
+
+	h = led_trigger_interpolate(value, trig->range, hs, he) % 252;
+	s = led_trigger_interpolate(value, trig->range, ss, se);
+	v = led_trigger_interpolate(value, trig->range, vs, ve);
+
+	led_trigger_event(trig, LED_SET_HUE_SAT | h << 16 | s << 8 | v);
+}
+EXPORT_SYMBOL_GPL(led_trigger_range_event);
+
 static void led_trigger_blink_setup(struct led_trigger *trig,
 			     unsigned long *delay_on,
 			     unsigned long *delay_off,
include/linux/leds.h +14 -0
index 07eb074..263640e 100644
@@ -241,6 +241,14 @@ enum led_brightness led_hsv_to_rgb(enum led_brightness hsv);
 #define DEFINE_LED_TRIGGER(x)		static struct led_trigger *x;
 #define DEFINE_LED_TRIGGER_GLOBAL(x)	struct led_trigger *x;
 
+struct led_trigger_range {
+	int start;
+	int end;
+	/* HSV color model */
+	u32 color_start;
+	u32 color_end;
+};
+
 #ifdef CONFIG_LEDS_TRIGGERS
 
 #define TRIG_NAME_MAX 50
@@ -251,6 +259,9 @@ struct led_trigger {
 	u8		flags;
 #define LED_TRIG_CAP_RGB	BIT(0)
 
+	/* optional element for usage with led_trigger_range_event */
+	const struct led_trigger_range *range;
+
 	void		(*activate)(struct led_classdev *led_cdev);
 	void		(*deactivate)(struct led_classdev *led_cdev);
 
@@ -278,6 +289,7 @@ extern void led_trigger_register_simple(const char *name,
 extern void led_trigger_unregister_simple(struct led_trigger *trigger);
 extern void led_trigger_event(struct led_trigger *trigger,
 				enum led_brightness event);
+extern void led_trigger_range_event(struct led_trigger *trigger, int value);
 extern void led_trigger_blink(struct led_trigger *trigger,
 			      unsigned long *delay_on,
 			      unsigned long *delay_off);
@@ -324,6 +336,8 @@ static inline void led_trigger_register_simple(const char *name,
 static inline void led_trigger_unregister_simple(struct led_trigger *trigger) {}
 static inline void led_trigger_event(struct led_trigger *trigger,
 				enum led_brightness event) {}
+static inline void led_trigger_range_event(struct led_trigger *trigger,
+					   int value) {}
 static inline void led_trigger_blink(struct led_trigger *trigger,
 				      unsigned long *delay_on,
 				      unsigned long *delay_off) {}
Signature · 1 line
2.7.2
#1 lore raw
Quoted 17 lines · Heiner Kallweit

Add led_trigger_range_event() and supporting struct led_trigger_range allowing to map a value within a value range to a color within a color range.
Simple example would be to map a temperature with in a range to a color between green and red.

---
  drivers/leds/led-triggers.c | 42 ++++++++++++++++++++++++++++++++++++++++++
  include/linux/leds.h        | 14 ++++++++++++++
  2 files changed, 56 insertions(+)
drivers/leds/led-triggers.c +0 -0
index 3ccf88b..5cab10b 100644

drivers/leds/led-triggers.c int led_trigger_register(struct led_trigger *trig) · line 200

Quoted 6 lines
drivers/leds/led-triggers.c +3 -0
@@ -195,6 +195,9 @@ int led_trigger_register(struct led_trigger *trig)
  	struct led_classdev *led_cdev;
  	struct led_trigger *_trig;
 
+	if (trig->range && trig->range->start >= trig->range->end)
+		return -EINVAL;
+

The "range" property is not needed in struct led_trigger. We can have struct rgb_heartbeat_trig_data and assign it to trigger_data.

Quoted 36 lines
drivers/leds/led-triggers.c +29 -0
  	rwlock_init(&trig->leddev_list_lock);
  	INIT_LIST_HEAD(&trig->led_cdevs);
 
@@ -294,6 +297,45 @@ void led_trigger_event(struct led_trigger *trig,
  }
  EXPORT_SYMBOL_GPL(led_trigger_event);
 
+static inline int led_trigger_interpolate(int value,
+					  const struct led_trigger_range *range,
+					  int x_start, int x_end)
+{
+		return x_start + DIV_ROUND_CLOSEST((value - range->start) *
+		       (x_end - x_start), range->end - range->start);
+}
+
+void led_trigger_range_event(struct led_trigger *trig, int value)
+{
+	int h, s, v, hs, he, ss, se, vs, ve, hdiff;
+
+	if (!trig->range)
+		return;
+
+	hs = trig->range->color_start >> 16 & 0xff;
+	he = trig->range->color_end >> 16 & 0xff;
+	ss = trig->range->color_start >> 8 & 0xff;
+	se = trig->range->color_end >> 8 & 0xff;
+	vs = trig->range->color_start & 0xff;
+	ve = trig->range->color_end & 0xff;
+	hdiff = (252 + he - hs) % 252;
+
+	/* on color circle, choose shortest way from start to end */
+	if (hdiff <= 126 && he < hs)
+		he += 252;
+	else if (hdiff > 126 && he > hs)
+		hs += 252;
+

drivers/leds/led-triggers.c void led_trigger_event(struct led_trigger *trig, · line 338

Quoted 10 lines
drivers/leds/led-triggers.c +10 -0
+	value = clamp(value, trig->range->start, trig->range->end);
+
+	h = led_trigger_interpolate(value, trig->range, hs, he) % 252;
+	s = led_trigger_interpolate(value, trig->range, ss, se);
+	v = led_trigger_interpolate(value, trig->range, vs, ve);
+
+	led_trigger_event(trig, LED_SET_HUE_SAT | h << 16 | s << 8 | v);
+}
+EXPORT_SYMBOL_GPL(led_trigger_range_event);
+

How about making this function a helper returning enum led_brightness and using it in rgb-heartbeat trigger? It would be trigger agnostic then. Later you could also create a new rgb-range trigger, which would expose sysfs attributes for configuring the color range.

It could be also moved to led-rgb-core.c and renamed to led_val_to_color_range or so.

Quoted 23 lines
drivers/leds/led-triggers.c +0 -0
  static void led_trigger_blink_setup(struct led_trigger *trig,
  			     unsigned long *delay_on,
  			     unsigned long *delay_off,
include/linux/leds.h +8 -0
index 07eb074..263640e 100644
@@ -241,6 +241,14 @@ enum led_brightness led_hsv_to_rgb(enum led_brightness hsv);
  #define DEFINE_LED_TRIGGER(x)		static struct led_trigger *x;
  #define DEFINE_LED_TRIGGER_GLOBAL(x)	struct led_trigger *x;
 
+struct led_trigger_range {
+	int start;
+	int end;
+	/* HSV color model */
+	u32 color_start;
+	u32 color_end;
+};
+
  #ifdef CONFIG_LEDS_TRIGGERS
 
  #define TRIG_NAME_MAX 50

include/linux/leds.h struct led_trigger { · line 264

Quoted 6 lines
include/linux/leds.h +3 -0
@@ -251,6 +259,9 @@ struct led_trigger {
  	u8		flags;
  #define LED_TRIG_CAP_RGB	BIT(0)
 
+	/* optional element for usage with led_trigger_range_event */
+	const struct led_trigger_range *range;
+

Let's drop it. trigger can carry this information on.

Quoted 21 lines
include/linux/leds.h +3 -0
  	void		(*activate)(struct led_classdev *led_cdev);
  	void		(*deactivate)(struct led_classdev *led_cdev);
 
@@ -278,6 +289,7 @@ extern void led_trigger_register_simple(const char *name,
  extern void led_trigger_unregister_simple(struct led_trigger *trigger);
  extern void led_trigger_event(struct led_trigger *trigger,
  				enum led_brightness event);
+extern void led_trigger_range_event(struct led_trigger *trigger, int value);
  extern void led_trigger_blink(struct led_trigger *trigger,
  			      unsigned long *delay_on,
  			      unsigned long *delay_off);
@@ -324,6 +336,8 @@ static inline void led_trigger_register_simple(const char *name,
  static inline void led_trigger_unregister_simple(struct led_trigger *trigger) {}
  static inline void led_trigger_event(struct led_trigger *trigger,
  				enum led_brightness event) {}
+static inline void led_trigger_range_event(struct led_trigger *trigger,
+					   int value) {}
  static inline void led_trigger_blink(struct led_trigger *trigger,
  				      unsigned long *delay_on,
  				      unsigned long *delay_off) {}
Signature · 2 lines
Best regards,
Jacek Anaszewski
#2 lore raw

Am 18.03.2016 um 14:09 schrieb Jacek Anaszewski:

Quoted 21 lines · Heiner Kallweit
Quoted 20 lines

Add led_trigger_range_event() and supporting struct led_trigger_range allowing to map a value within a value range to a color within a color range.
Simple example would be to map a temperature with in a range to a color between green and red.

---
  drivers/leds/led-triggers.c | 42 ++++++++++++++++++++++++++++++++++++++++++
  include/linux/leds.h        | 14 ++++++++++++++
  2 files changed, 56 insertions(+)
drivers/leds/led-triggers.c +0 -0
index 3ccf88b..5cab10b 100644
@@ -195,6 +195,9 @@ int led_trigger_register(struct led_trigger *trig)
      struct led_classdev *led_cdev;
      struct led_trigger *_trig;
 
Quoted 7 lines

drivers/leds/led-triggers.c int led_trigger_register(struct led_trigger *trig) · line 200

Quoted 3 lines
drivers/leds/led-triggers.c +3 -0
+    if (trig->range && trig->range->start >= trig->range->end)
+        return -EINVAL;
+

The "range" property is not needed in struct led_trigger. We can have struct rgb_heartbeat_trig_data and assign it to trigger_data.

Mapping a value range to a color range is a generic new feature and independent of the use in the new heartbeat (or better said: system load visualization) trigger.
Having struct led_trigger_range in the trigger struct has the advantage that we can do needed checking only once when registering the trigger and the caller doesn't have to pass a pointer to the struct with each call to led_trigger_range_event().

Quoted 43 lines
Quoted 43 lines
drivers/leds/led-triggers.c +36 -0
      rwlock_init(&trig->leddev_list_lock);
      INIT_LIST_HEAD(&trig->led_cdevs);
 
@@ -294,6 +297,45 @@ void led_trigger_event(struct led_trigger *trig,
  }
  EXPORT_SYMBOL_GPL(led_trigger_event);
 
+static inline int led_trigger_interpolate(int value,
+                      const struct led_trigger_range *range,
+                      int x_start, int x_end)
+{
+        return x_start + DIV_ROUND_CLOSEST((value - range->start) *
+               (x_end - x_start), range->end - range->start);
+}
+
+void led_trigger_range_event(struct led_trigger *trig, int value)
+{
+    int h, s, v, hs, he, ss, se, vs, ve, hdiff;
+
+    if (!trig->range)
+        return;
+
+    hs = trig->range->color_start >> 16 & 0xff;
+    he = trig->range->color_end >> 16 & 0xff;
+    ss = trig->range->color_start >> 8 & 0xff;
+    se = trig->range->color_end >> 8 & 0xff;
+    vs = trig->range->color_start & 0xff;
+    ve = trig->range->color_end & 0xff;
+    hdiff = (252 + he - hs) % 252;
+
+    /* on color circle, choose shortest way from start to end */
+    if (hdiff <= 126 && he < hs)
+        he += 252;
+    else if (hdiff > 126 && he > hs)
+        hs += 252;
+
+    value = clamp(value, trig->range->start, trig->range->end);
+
+    h = led_trigger_interpolate(value, trig->range, hs, he) % 252;
+    s = led_trigger_interpolate(value, trig->range, ss, se);
+    v = led_trigger_interpolate(value, trig->range, vs, ve);
+
+    led_trigger_event(trig, LED_SET_HUE_SAT | h << 16 | s << 8 | v);
Quoted 9 lines

drivers/leds/led-triggers.c void led_trigger_event(struct led_trigger *trig, · line 338

Quoted 3 lines
drivers/leds/led-triggers.c +3 -0
+}
+EXPORT_SYMBOL_GPL(led_trigger_range_event);
+

How about making this function a helper returning enum led_brightness and using it in rgb-heartbeat trigger? It would be trigger agnostic then. Later you could also create a new rgb-range trigger, which would expose sysfs attributes for configuring the color range.

Having the mapping as a helper would be an alternative. However then the caller would always need two calls instead of one:

enum led_brightness brightness = led_val_to_color_range(struct range *, val); led_trigger_event(trig, brightness);

But this might be ok considering that we gain the option to use the mapping feature independent of triggers.

Making the color range configurable via sysfs is an interesting idea. But for this we would first have to introduce generic sysfs handling for triggers I think. Maybe that's something for a next round of extensions.

Quoted 3 lines

It could be also moved to led-rgb-core.c and renamed to led_val_to_color_range or so.

Yes, this would make sense.

Quoted 26 lines
Quoted 26 lines
drivers/leds/led-triggers.c +0 -0
  static void led_trigger_blink_setup(struct led_trigger *trig,
                   unsigned long *delay_on,
                   unsigned long *delay_off,
include/linux/leds.h +8 -0
index 07eb074..263640e 100644
@@ -241,6 +241,14 @@ enum led_brightness led_hsv_to_rgb(enum led_brightness hsv);
  #define DEFINE_LED_TRIGGER(x)        static struct led_trigger *x;
  #define DEFINE_LED_TRIGGER_GLOBAL(x)    struct led_trigger *x;
 
+struct led_trigger_range {
+    int start;
+    int end;
+    /* HSV color model */
+    u32 color_start;
+    u32 color_end;
+};
+
  #ifdef CONFIG_LEDS_TRIGGERS
 
  #define TRIG_NAME_MAX 50
@@ -251,6 +259,9 @@ struct led_trigger {
      u8        flags;
  #define LED_TRIG_CAP_RGB    BIT(0)
 
Quoted 6 lines

include/linux/leds.h struct led_trigger { · line 264

Quoted 3 lines
include/linux/leds.h +3 -0
+    /* optional element for usage with led_trigger_range_event */
+    const struct led_trigger_range *range;
+

Let's drop it. trigger can carry this information on.

If we have the mapping as a helper, independent of triggers, then we won't have the pointer to struct range here anyway.

Quoted 23 lines
Quoted 21 lines
include/linux/leds.h +3 -0
      void        (*activate)(struct led_classdev *led_cdev);
      void        (*deactivate)(struct led_classdev *led_cdev);
 
@@ -278,6 +289,7 @@ extern void led_trigger_register_simple(const char *name,
  extern void led_trigger_unregister_simple(struct led_trigger *trigger);
  extern void led_trigger_event(struct led_trigger *trigger,
                  enum led_brightness event);
+extern void led_trigger_range_event(struct led_trigger *trigger, int value);
  extern void led_trigger_blink(struct led_trigger *trigger,
                    unsigned long *delay_on,
                    unsigned long *delay_off);
@@ -324,6 +336,8 @@ static inline void led_trigger_register_simple(const char *name,
  static inline void led_trigger_unregister_simple(struct led_trigger *trigger) {}
  static inline void led_trigger_event(struct led_trigger *trigger,
                  enum led_brightness event) {}
+static inline void led_trigger_range_event(struct led_trigger *trigger,
+                       int value) {}
  static inline void led_trigger_blink(struct led_trigger *trigger,
                        unsigned long *delay_on,
                        unsigned long *delay_off) {}