* [PATCH v6 0/1] leds: st1202: Add hardware-accelerated blink support @ 2026-08-06 17:18 Manuel Fombuena 2026-08-06 17:25 ` [PATCH v6 1/1] " Manuel Fombuena 0 siblings, 1 reply; 3+ messages in thread From: Manuel Fombuena @ 2026-08-06 17:18 UTC (permalink / raw) To: lee, pavel, vicentiu.galanopulo, linux-leds, linux-kernel This patch adds blink_set() to the ST1202 LED driver, enabling hardware-accelerated blinking via the timer trigger. A series of nine fixes to the pattern engine and brightness handling was recently applied to for-leds-next: https://lore.kernel.org/all/GV1PR08MB8497C0B898789BB73ACE6EE3C5F52@GV1PR08MB8497.eurprd08.prod.outlook.com/ With those fixes in place, the pattern engine can be used reliably to implement blink_set(): a two-step pattern (full brightness for delay_on, off for delay_off) is programmed and started in infinite repeat mode. Requested delays are clamped to the hardware range and rounded up to the nearest 22ms step. During review of the fix series, several pre-existing issues were identified in the driver — including brightness_set() being assigned to a non-blocking callback, the global sequencer affecting all channels on pattern operations, and missing brightness scaling in pattern_set(). These do not affect blink_set(): the callback is not invoked from atomic context, the function explicitly programs all other channels' PWM slots to zero before starting the sequencer, and channel brightness is set directly via the ILED register. The pre-existing issues will be addressed in a follow-up submission. Tested on LED1202 hardware via I2C on a Linksys MX4200v2 router running OpenWrt. Hardware blinking confirmed functional with the timer trigger. --- Changes in v6 --- In response to maintainer (Lee Jones) review on v3: Use standard if statements for zero-delay substitution instead of the short-form ternary introduced in v2. Rename loop variable 'patt' to 'pattern'. --- Changes in v5 --- Use st1202_duration_pattern_write() for pattern duration slots 0 and 1 in blink_set(), consistent with how st1202_led_pattern_set() uses the same helper for the same registers. Fix commit message: ST1202_MILLIS_PATTERN_DUR_MAX was wrapped across two lines. --- Changes in v4 --- Fix delay clamping and rounding order following a review of the changes introduced across v2 and v3: v2 moved roundup() before clamp_val() so that rounding could not push the result past the hardware maximum. However, this left roundup() exposed to integer overflow for extreme inputs near ULONG_MAX, since roundup(x, 22) internally computes x + 21 before dividing. v3 addressed the overflow by prepending a min_t() cap at MAX before roundup(), and retained a trailing clamp_val() for the full range. The trailing clamp_val() turned out to be unreachable: ST1202_MILLIS_PATTERN_DUR_MAX (5610) is an exact multiple of ST1202_MILLIS_PATTERN_DUR_MIN (22), so roundup() on a value already capped at 5610 returns 5610 unchanged, and the result is always within range before the trailing clamp_val() runs. v4 reverts to clamp_val() before roundup(). Clamping first prevents overflow for extreme inputs, and since MAX is an exact multiple of MIN the rounded result cannot exceed MAX, addressing the v1 concern without the redundant operations introduced in v3. --- Changes in v3 --- In response to automated review feedback (Sashiko) on v2: Clamp delay inputs to the hardware maximum before calling roundup() to prevent integer overflow for extreme values near ULONG_MAX. Other pre-existing issues identified by the automated review are outside the scope of this patch and will be addressed in a follow-up submission. --- Changes in v2 --- In response to maintainer (Lee Jones) review on v1: Use short-form ternary for default delay substitution. Perform roundup before clamp_val to ensure rounding cannot exceed the hardware maximum. Capitalise commit subject per LED subsystem convention. Drop Assisted-by tag per maintainer suggestion. v1: https://lore.kernel.org/all/GV1PR08MB8497C5B8CEB2CE19743DFCFFC5FA2@GV1PR08MB8497.eurprd08.prod.outlook.com/ v2: https://lore.kernel.org/all/GV1PR08MB84979858573735294D41A189C5C02@GV1PR08MB8497.eurprd08.prod.outlook.com/ v3: https://lore.kernel.org/all/GV1PR08MB84979E014A407174EBAE9875C5CF2@GV1PR08MB8497.eurprd08.prod.outlook.com/ v4: https://lore.kernel.org/all/GV1PR08MB8497E3103AD32162BD05D907C5CB2@GV1PR08MB8497.eurprd08.prod.outlook.com/ v5: https://lore.kernel.org/all/GV1PR08MB8497E2498ED9D66AF60A3ACFC5D42@GV1PR08MB8497.eurprd08.prod.outlook.com/ Manuel Fombuena (1): leds: st1202: Add hardware-accelerated blink support drivers/leds/leds-st1202.c | 85 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) -- 2.55.0 ^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v6 1/1] leds: st1202: Add hardware-accelerated blink support 2026-08-06 17:18 [PATCH v6 0/1] leds: st1202: Add hardware-accelerated blink support Manuel Fombuena @ 2026-08-06 17:25 ` Manuel Fombuena 2026-08-06 17:42 ` sashiko-bot 0 siblings, 1 reply; 3+ messages in thread From: Manuel Fombuena @ 2026-08-06 17:25 UTC (permalink / raw) To: lee, pavel, vicentiu.galanopulo, linux-leds, linux-kernel Implement blink_set() to enable hardware-accelerated blinking via the timer trigger. The LED1202 pattern engine is used to produce a two-step sequence: full brightness for delay_on, off for delay_off, repeating indefinitely. Requested delays are clamped to the hardware range [22ms, 5610ms] then rounded up to the nearest 22ms step. Clamping before rounding prevents integer overflow in roundup() for extreme input values; since ST1202_MILLIS_PATTERN_DUR_MAX is an exact multiple of ST1202_MILLIS_PATTERN_DUR_MIN, rounding a clamped value cannot exceed the maximum. A zero delay is replaced with the default of 500ms independently for each of delay_on and delay_off. The LED1202 pattern sequencer is global and its timing registers are shared across all channels, so only one blink configuration can be active at a time. Other active channels have their PWM slots zeroed for both pattern steps so they remain dark rather than outputting unintended values when the sequencer runs. The target channel's ILED register is set to full brightness and the channel is enabled, since the timer trigger deactivates the current trigger before calling blink_set which would otherwise leave the channel disabled. Signed-off-by: Manuel Fombuena <fombuena@outlook.com> --- drivers/leds/leds-st1202.c | 85 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c index 168df5ecf27b..ea019d03ad90 100644 --- a/drivers/leds/leds-st1202.c +++ b/drivers/leds/leds-st1202.c @@ -15,6 +15,7 @@ #include <linux/slab.h> #include <linux/string.h> +#define ST1202_BLINK_DEFAULT_DELAY 500 #define ST1202_CHAN_DISABLE_ALL 0x00 #define ST1202_CHAN_ENABLE_HIGH 0x03 #define ST1202_CHAN_ENABLE_LOW 0x02 @@ -275,6 +276,89 @@ static int st1202_led_pattern_set(struct led_classdev *ldev, return 0; } +static int st1202_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, unsigned long *delay_off) +{ + struct st1202_led *led = cdev_to_st1202_led(led_cdev); + struct st1202_chip *chip = led->chip; + unsigned long on, off; + int ret; + + if (!*delay_on) + *delay_on = ST1202_BLINK_DEFAULT_DELAY; + if (!*delay_off) + *delay_off = ST1202_BLINK_DEFAULT_DELAY; + + on = *delay_on; + off = *delay_off; + + on = clamp_val(on, ST1202_MILLIS_PATTERN_DUR_MIN, ST1202_MILLIS_PATTERN_DUR_MAX); + off = clamp_val(off, ST1202_MILLIS_PATTERN_DUR_MIN, ST1202_MILLIS_PATTERN_DUR_MAX); + on = roundup(on, ST1202_MILLIS_PATTERN_DUR_MIN); + off = roundup(off, ST1202_MILLIS_PATTERN_DUR_MIN); + + guard(mutex)(&chip->lock); + + ret = st1202_write_reg(chip, ST1202_CONFIG_REG, ST1202_CONFIG_REG_SHFT); + if (ret) + return ret; + + /* Zero out PWM for all other active channels to prevent them from blinking */ + for (int i = 0; i < ST1202_MAX_LEDS; i++) { + if (!chip->leds[i].is_active || i == led->led_num) + continue; + ret = st1202_pwm_pattern_write(chip, i, 0, LED_OFF); + if (ret) + return ret; + ret = st1202_pwm_pattern_write(chip, i, 1, LED_OFF); + if (ret) + return ret; + } + + ret = st1202_pwm_pattern_write(chip, led->led_num, 0, ST1202_PATTERN_PWM_FULL); + if (ret) + return ret; + ret = st1202_pwm_pattern_write(chip, led->led_num, 1, LED_OFF); + if (ret) + return ret; + + ret = st1202_duration_pattern_write(chip, 0, on); + if (ret) + return ret; + ret = st1202_duration_pattern_write(chip, 1, off); + if (ret) + return ret; + + for (int pattern = 2; pattern < ST1202_MAX_PATTERNS; pattern++) { + ret = st1202_write_reg(chip, ST1202_PATTERN_DUR + pattern, 0); + if (ret) + return ret; + } + + ret = st1202_write_reg(chip, ST1202_PATTERN_REP, U8_MAX); + if (ret) + return ret; + + ret = st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, U8_MAX); + if (ret) + return ret; + + ret = __st1202_channel_set(chip, led->led_num, true); + if (ret) + return ret; + + ret = st1202_write_reg(chip, ST1202_CONFIG_REG, + ST1202_CONFIG_REG_PATSR | ST1202_CONFIG_REG_PATS | + ST1202_CONFIG_REG_SHFT); + if (ret) + return ret; + + *delay_on = on; + *delay_off = off; + + return 0; +} + static int st1202_dt_init(struct st1202_chip *chip) { struct device *dev = &chip->client->dev; @@ -301,6 +385,7 @@ static int st1202_dt_init(struct st1202_chip *chip) led->led_cdev.pattern_set = st1202_led_pattern_set; led->led_cdev.pattern_clear = st1202_led_pattern_clear; led->led_cdev.default_trigger = "pattern"; + led->led_cdev.blink_set = st1202_blink_set; led->led_cdev.brightness_set = st1202_brightness_set; led->led_cdev.brightness_get = st1202_brightness_get; } -- 2.55.0 ^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v6 1/1] leds: st1202: Add hardware-accelerated blink support 2026-08-06 17:25 ` [PATCH v6 1/1] " Manuel Fombuena @ 2026-08-06 17:42 ` sashiko-bot 0 siblings, 0 replies; 3+ messages in thread From: sashiko-bot @ 2026-08-06 17:42 UTC (permalink / raw) To: Manuel Fombuena; +Cc: lee, linux-leds Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: New issues: - [High] Hardware blinking destructively overwrites the PWM state of all other active LEDs, forcing them permanently off. - [Medium] Hardware blink forces maximum brightness, ignoring user-configured brightness. Pre-existing issues: - [High] The driver improperly assigns a sleeping function to the led_cdev->brightness_set callback, causing a 'sleeping in atomic' bug. - [High] A dangling fwnode pointer is stored in `led->fwnode` due to a dropped reference count, leading to a UAF upon dereference. -- --- Patch [1]: [PATCH v6 1/1] leds: st1202: Add hardware-accelerated blink support --- commit d3cba330dd18edf23569d762b5d09a2ae91bdf35 Author: Manuel Fombuena <fombuena@outlook.com> leds: st1202: Add hardware-accelerated blink support This commit implements blink_set() to enable hardware-accelerated blinking via the timer trigger on the ST1202 LED driver. The pattern engine is used to produce a two-step sequence with delays clamped to hardware limits. > diff --git a/drivers/leds/leds-st1202.c b/drivers/leds/leds-st1202.c > index 168df5ecf27b7..ea019d03ad907 100644 > --- a/drivers/leds/leds-st1202.c > +++ b/drivers/leds/leds-st1202.c [ ... ] > @@ -275,6 +276,89 @@ static int st1202_led_pattern_set(struct led_classdev *ldev, [ ... ] > + /* Zero out PWM for all other active channels to prevent them from blinking */ > + for (int i = 0; i < ST1202_MAX_LEDS; i++) { > + if (!chip->leds[i].is_active || i == led->led_num) > + continue; > + ret = st1202_pwm_pattern_write(chip, i, 0, LED_OFF); [Severity: High] Does this loop permanently overwrite the PWM configuration for other active LEDs? If another LED was previously configured to be statically on, it looks like this forces it to be permanently dark. When the hardware blink is stopped, the original PWM configurations for these LEDs are never restored, which would leave them completely desynchronized from their software led_cdev->brightness state. > + if (ret) > + return ret; > + ret = st1202_pwm_pattern_write(chip, i, 1, LED_OFF); > + if (ret) > + return ret; > + } [ ... ] > + for (int pattern = 2; pattern < ST1202_MAX_PATTERNS; pattern++) { > + ret = st1202_write_reg(chip, ST1202_PATTERN_DUR + pattern, 0); > + if (ret) > + return ret; > + } > + > + ret = st1202_write_reg(chip, ST1202_PATTERN_REP, U8_MAX); > + if (ret) > + return ret; > + > + ret = st1202_write_reg(chip, ST1202_ILED_REG0 + led->led_num, U8_MAX); [Severity: Medium] Does this force the LED to maximum brightness during a blink, ignoring the currently configured led_cdev->brightness or led_cdev->blink_brightness? > + if (ret) > + return ret; > + > + ret = __st1202_channel_set(chip, led->led_num, true); [ ... ] > @@ -301,6 +385,7 @@ static int st1202_dt_init(struct st1202_chip *chip) > led->led_cdev.pattern_set = st1202_led_pattern_set; > led->led_cdev.pattern_clear = st1202_led_pattern_clear; > led->led_cdev.default_trigger = "pattern"; > + led->led_cdev.blink_set = st1202_blink_set; > led->led_cdev.brightness_set = st1202_brightness_set; [Severity: High] This is a pre-existing issue, but does assigning st1202_brightness_set() to the non-blocking brightness_set callback cause a "sleeping in atomic" panic? The LED subsystem requires that brightness_set not sleep, as it is called from atomic contexts like software timers. Looking at st1202_brightness_set(), it takes a mutex and performs I2C operations which can sleep: st1202_brightness_set() guard(mutex)(&chip->lock); ... st1202_write_reg() -> i2c_smbus_write_byte_data() Should this be assigned to brightness_set_blocking instead? > led->led_cdev.brightness_get = st1202_brightness_get; > } [Severity: High] This is a pre-existing issue, but I noticed a potential use-after-free relating to how led->fwnode is stored earlier in st1202_dt_init(). The driver iterates over child nodes using for_each_available_child_of_node_scoped(), which automatically decrements the child node's reference count at the end of each iteration. Inside this loop, the fwnode is stored without incrementing its refcount: led->fwnode = of_fwnode_handle(child); Since fwnode_handle_get() is not called, does led->fwnode become a dangling pointer when the loop finishes? This pointer is later passed to the LED core during registration in st1202_probe(), which could cause a UAF if the memory is reclaimed. -- Sashiko AI review · https://sashiko.dev/#/patchset/GV1PR08MB8497362791595D5C0197701DC5D22@GV1PR08MB8497.eurprd08.prod.outlook.com?part=1 ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-08-06 17:42 UTC | newest] Thread overview: 3+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-08-06 17:18 [PATCH v6 0/1] leds: st1202: Add hardware-accelerated blink support Manuel Fombuena 2026-08-06 17:25 ` [PATCH v6 1/1] " Manuel Fombuena 2026-08-06 17:42 ` sashiko-bot
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox