leds: trigger: pattern: Add per-tuple RGB color

- Extend pattern trigger to parse optional RGB per tuple
- Plumb color data through include/linux/leds.h
- Preserve existing tuple semantics when color omitted

Signed-off-by: Maxim Laschinsky <maxim.laschinsky@wago.com>
This commit is contained in:
Maxim Laschinksy
2026-01-21 08:27:41 +01:00
committed by Heinrich Toews
parent 995017b4bf
commit 91007560a1
2 changed files with 128 additions and 18 deletions
+114 -15
View File
@@ -13,6 +13,9 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/led-class-multicolor.h>
#define MAX_PATTERNS 1024
/*
@@ -34,8 +37,56 @@ struct pattern_trig_data {
bool is_indefinite;
bool is_hw_pattern;
struct timer_list timer;
#if IS_ENABLED(CONFIG_LEDS_CLASS_MULTICOLOR)
struct led_classdev_mc *mcled;
#endif
};
#if IS_ENABLED(CONFIG_LEDS_CLASS_MULTICOLOR)
static void pattern_trig_apply_color(struct pattern_trig_data *data,
const struct led_pattern *pat)
{
int i;
unsigned int max_intensity;
bool has_red = false, has_green = false, has_blue = false;
if (!data->mcled || !pat->has_color)
return;
max_intensity = data->mcled->led_cdev.max_brightness;
for (i = 0; i < data->mcled->num_colors; i++) {
switch (data->mcled->subled_info[i].color_index) {
case LED_COLOR_ID_RED:
data->mcled->subled_info[i].intensity =
DIV_ROUND_CLOSEST(pat->red * max_intensity, 255);
has_red = true;
break;
case LED_COLOR_ID_GREEN:
data->mcled->subled_info[i].intensity =
DIV_ROUND_CLOSEST(pat->green * max_intensity, 255);
has_green = true;
break;
case LED_COLOR_ID_BLUE:
data->mcled->subled_info[i].intensity =
DIV_ROUND_CLOSEST(pat->blue * max_intensity, 255);
has_blue = true;
break;
default:
break;
}
}
if ((!has_red && pat->red) || (!has_green && pat->green) ||
(!has_blue && pat->blue))
dev_warn_once(data->led_cdev->dev,
"pattern: color component set for absent channel\n");
}
#else
static inline void pattern_trig_apply_color(struct pattern_trig_data *data,
const struct led_pattern *pat) { }
#endif
static void pattern_trig_update_patterns(struct pattern_trig_data *data)
{
data->curr = data->next;
@@ -81,6 +132,7 @@ static void pattern_trig_timer_function(struct timer_list *t)
if (data->curr->brightness == data->next->brightness) {
/* Step change of brightness */
pattern_trig_apply_color(data, data->curr);
led_set_brightness(data->led_cdev,
data->curr->brightness);
mod_timer(&data->timer,
@@ -104,6 +156,8 @@ static void pattern_trig_timer_function(struct timer_list *t)
continue;
}
if (data->delta_t == 0)
pattern_trig_apply_color(data, data->curr);
led_set_brightness(data->led_cdev,
pattern_trig_compute_brightness(data));
mod_timer(&data->timer,
@@ -207,10 +261,18 @@ static ssize_t pattern_trig_show_patterns(struct pattern_trig_data *data,
goto out;
for (i = 0; i < data->npatterns; i++) {
count += scnprintf(buf + count, PAGE_SIZE - count,
"%d %u ",
data->patterns[i].brightness,
data->patterns[i].delta_t);
const struct led_pattern *p = &data->patterns[i];
if (!hw_pattern && p->has_color)
count += scnprintf(buf + count, PAGE_SIZE - count,
"%d:%02x%02x%02x %u ",
p->brightness, p->red, p->green,
p->blue, p->delta_t);
else
count += scnprintf(buf + count, PAGE_SIZE - count,
"%d %u ",
p->brightness,
p->delta_t);
}
buf[count - 1] = '\n';
@@ -223,22 +285,52 @@ out:
static int pattern_trig_store_patterns_string(struct pattern_trig_data *data,
const char *buf, size_t count)
{
int ccount, cr, offset = 0;
int consumed, ccount;
unsigned int brightness, delta_t;
size_t offset = 0;
u8 r, g, b;
while (offset < count - 1 && data->npatterns < MAX_PATTERNS) {
cr = 0;
ccount = sscanf(buf + offset, "%u %u %n",
&data->patterns[data->npatterns].brightness,
&data->patterns[data->npatterns].delta_t, &cr);
while (offset < count && data->npatterns < MAX_PATTERNS) {
while (offset < count && isspace(buf[offset]))
offset++;
if (offset >= count)
break;
if (ccount != 2 ||
data->patterns[data->npatterns].brightness > data->led_cdev->max_brightness) {
if (!data->is_hw_pattern) {
consumed = 0;
ccount = sscanf(buf + offset,
"%u:%2hhx%2hhx%2hhx %u %n",
&brightness, &r, &g, &b,
&delta_t, &consumed);
if (ccount == 5) {
if (brightness > data->led_cdev->max_brightness) {
data->npatterns = 0;
return -EINVAL;
}
data->patterns[data->npatterns].brightness = brightness;
data->patterns[data->npatterns].delta_t = delta_t;
data->patterns[data->npatterns].has_color = true;
data->patterns[data->npatterns].red = r;
data->patterns[data->npatterns].green = g;
data->patterns[data->npatterns].blue = b;
offset += consumed;
data->npatterns++;
continue;
}
}
consumed = 0;
ccount = sscanf(buf + offset, "%u %u %n", &brightness, &delta_t, &consumed);
if (ccount != 2 || brightness > data->led_cdev->max_brightness) {
data->npatterns = 0;
return -EINVAL;
}
offset += cr;
data->patterns[data->npatterns].brightness = brightness;
data->patterns[data->npatterns].delta_t = delta_t;
data->npatterns++;
if (consumed == 0)
break;
offset += consumed;
}
return 0;
@@ -274,6 +366,8 @@ static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev,
data->is_hw_pattern = hw_pattern;
data->npatterns = 0;
/* Zero all pattern entries so per-tuple color field clearing is unnecessary */
memset(data->patterns, 0, sizeof(data->patterns));
if (buf)
err = pattern_trig_store_patterns_string(data, buf, count);
@@ -404,6 +498,11 @@ static int pattern_trig_activate(struct led_classdev *led_cdev)
data->last_repeat = -1;
mutex_init(&data->lock);
data->led_cdev = led_cdev;
#if IS_ENABLED(CONFIG_LEDS_CLASS_MULTICOLOR)
if (led_cdev->color == LED_COLOR_ID_MULTI ||
led_cdev->color == LED_COLOR_ID_RGB)
data->mcled = lcdev_to_mccdev(led_cdev);
#endif
led_set_trigger_data(led_cdev, data);
timer_setup(&data->timer, pattern_trig_timer_function, 0);
led_cdev->activated = true;
@@ -459,5 +558,5 @@ module_exit(pattern_trig_exit);
MODULE_AUTHOR("Raphael Teysseyre <rteysseyre@gmail.com>");
MODULE_AUTHOR("Baolin Wang <baolin.wang@linaro.org>");
MODULE_DESCRIPTION("LED Pattern trigger");
MODULE_DESCRIPTION("LED Pattern trigger (optional per-node RGB)");
MODULE_LICENSE("GPL v2");
+14 -3
View File
@@ -706,13 +706,24 @@ static inline void led_classdev_notify_brightness_hw_changed(
#endif
/**
* struct led_pattern - pattern interval settings
* @delta_t: pattern interval delay, in milliseconds
* @brightness: pattern interval brightness
* struct led_pattern - pattern interval settings (optional RGB per node)
* @delta_t: pattern interval delay (ms)
* @brightness: pattern interval brightness (0..max_brightness)
* @has_color: true if this node specifies new RGB intensities
* @red: red component (0..255, valid if has_color)
* @green: green component (0..255, valid if has_color)
* @blue: blue component (0..255, valid if has_color)
*
* If has_color is false the previously applied color (if any) remains
* unchanged; only brightness/duration are processed.
*/
struct led_pattern {
u32 delta_t;
int brightness;
bool has_color;
u8 red;
u8 green;
u8 blue;
};
enum led_audio {