Skip to content

drivers: led: Provide a fallback implementation of blink API #90237

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

anobli
Copy link
Contributor

@anobli anobli commented May 20, 2025

The LED blink API is quite useful but this is not supported by all the LED controller.
This provides a software implementation of blink that use a delayable work to blink a LED.
This could be enabled using Kconfig, as well the maximum number of LED that could blink at the same time using blink fallback. This does not requires any changes to driver to be used.

@github-actions github-actions bot added the area: LED Label to identify LED subsystem label May 20, 2025
@anobli anobli force-pushed the abailon/led-blink-fallback branch from 1ea7e7c to 27baa49 Compare May 21, 2025 06:35
Copy link
Contributor

@simonguinot simonguinot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @anobli,

Thanks for this PR but I can't agree with it in its current state. Please see my comments below.

struct k_work_delayable work;
uint32_t delay_on;
uint32_t delay_off;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The blink information should be attached to the LED (i.e. in a LED structure). Have a look at struct led_classdev in Linux and at the software blinking implementation. We are aiming at the same architecture here, maybe more optimized for memory usage

Note that you may have to reintroduce a LED class structure here. We are just about to remove the existing one with #90186 :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a new struct attached to the LED device. I tried to do it in a way that request less changes as possible in driver. I kept the same content in the struct. I think I can probably improve it a little bit by removing the led index and use pointer arithmetic to retrieve it.

@@ -0,0 +1,130 @@
/*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think led-core.c (as in Linux) may be a better name. It may become useful for other LED software features.

k_work_schedule(&data->work, K_MSEC(data->delay_on));
}

static void led_blink_fallback_off(struct k_work *work)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some brightness/blink state should be stored in the data structure (as in Linux). This should allow to have the same work function to turn the LED on or off.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My intent was to save some memory here by only switching the work function.

default 3
help
This set the maximum number of LED that could blink
in the same time using blink fallback.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should limit the number of LED allowed to blink. If there is no enough memory, it will simply fail.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the code to statically allocate a struct for each LED defined in the controller.
Probably not the best choice for memory usage, but, it was easier to manage.
We could probably use DTS to enable software blinking for a specific LED.

@@ -28,3 +28,5 @@ zephyr_library_sources_ifdef(CONFIG_TLC59108 tlc59108.c)
zephyr_library_sources_ifdef(CONFIG_LED_SHELL led_shell.c)

zephyr_library_sources_ifdef(CONFIG_USERSPACE led_handlers.c)

zephyr_library_sources_ifdef(CONFIG_LED_BLINK_FALLBACK blink_fallback.c)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use "software" instead of "fallback".

}

/* Stop blinking, more likely when we use led_on or led_off */
if (delay_on == 0 && delay_off == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No way. Please, just make a dedicated function to disable the blinking.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! And for the specific case where both delay are 0, I use the policy as Linux: 1Hz blinking. I hope this makes sense.

}

/* Stop blinking, more likely when we use led_on or led_off */
if (delay_on == 0 && delay_off == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also you may want to handle some special cases here:

if (!delay_on) {
        /* always off */
}
if (!delay_off) {
        /* always on */
}

@@ -128,6 +128,11 @@ __subsystem struct led_driver_api {
led_api_write_channels write_channels;
};

#ifdef CONFIG_LED_BLINK_FALLBACK
int led_blink_fallback(const struct device *dev, uint32_t led, uint32_t delay_on,
uint32_t delay_off);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion:

#ifdef CONFIG_LED_BLINK_SOFTWARE
int led_blink_start(const struct device *dev, uint32_t led,
                       uint32_t delay_on, uint32_t delay_off);
int led_blink_stop(const struct device *dev, uint32_t led);
#else
#define led_blink_start(dev, led) do {} while (0)
#define led_blink_stop(dev, led) do {} while (0)
#endif

Or something like that...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless we want to make them "public" API, I prefer to keep them out of led.h.
Since I move some functions to led_core.c, this is not needed here anymore.

if (api->blink == NULL) {
led_blink_fallback(dev, led, 0, 0);
}
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And then you can just call led_blink_stop here.

return -ENOSYS;
#endif
}
return api->blink(dev, led, delay_on, delay_off);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be moved into led-core.c as well.

@faxe1008
Copy link
Contributor

Random thought: maybe build it in away that allows to build more complex animations (like pulsing, breathing), and let a user provide a step function of sorts to determine the next brightness value.

@simonguinot
Copy link
Contributor

Random thought: maybe build it in away that allows to build more complex animations (like pulsing, breathing), and let a user provide a step function of sorts to determine the next brightness value.

It should be discussed but I think that more complex LED effects (run-light, breathing, rainbow, etc) and other things (such as synchronization between devices, color correction, etc) should go in a user library, which is what we are missing the most right now. Blinking is a special case to me. It is cheap and commonly used by simple applications. For theses reasons I think it makes sense to provide a software implementation in Zephyr kernel.

Copy link
Contributor

@thedjnK thedjnK left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the whole point in a driver is that it controls hardware, this is doing a complete software thing, it has no place in drivers, if this is wanted then it should be a software component i.e. go in subsys


static void led_blink_fallback_off(struct k_work *work);

static K_MUTEX_DEFINE(led_blink_fallback_mutex);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use a semaphore, lighter implementation, this does not need the extra stuff that comes with mutextes. https://youtu.be/p8OgqVQxklo?si=z8xJ5mp_Qi86zO9V&t=1305

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use static allocation now so this should not be needed anymore.

Comment on lines 32 to 36
if (api->on) {
api->on(data->dev, data->led);
} else {
api->set_brightness(data->dev, data->led, LED_BRIGTHNESS_MAX);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just call led_on, it'll fall back to the right thing for you

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not able using because it was stopping blinking. I added a function that just do on or off without stopping blinking so I don't need anymore to re-implements the fallback.

Comment on lines 49 to 53
if (api->off) {
api->off(data->dev, data->led);
} else {
api->set_brightness(data->dev, data->led, 0);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same, led_off

anobli added 4 commits June 11, 2025 16:15
In order to develop a sotfware implementation for led blinking,
prepare the led API.
This move z_impl_led_on and z_impl_led_off code to led_core.c
so we could use and update them for blinking a led.

Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
Blinking data are expected to be attached to led device.
In order to have a as much as possible generic implementation,
we need a way to retreive the data from the drivers.
This adds a new callback that will be used in that purpose.

Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
The LED blink API is quite useful but this is not supported by all
the LED controller.
This provides a software implementation of blink that uses a delayable
work to blink a LED.
To use it, a driver just have to allocate and init a struct for holding
blinking led data and to call the software blinking functions.

Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
This updates the led_gpio driver to use software blinking.

Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
@anobli anobli force-pushed the abailon/led-blink-fallback branch from 27baa49 to 4c95949 Compare June 11, 2025 19:57
Copy link

@anobli
Copy link
Contributor Author

anobli commented Jun 11, 2025

the whole point in a driver is that it controls hardware, this is doing a complete software thing, it has no place in drivers, if this is wanted then it should be a software component i.e. go in subsys

I am confuse here. Are you talking about the suggestion from @faxe1008 or about doing software blinking in drivers?
If this about doing software blinking in drivers, I disagree. Although there is no hardware support, we are implementing driver API so I think it must be located here.

@anobli
Copy link
Contributor Author

anobli commented Jun 11, 2025

I did a lot of change since the previous version. I hope this is going in the direction even if some improvement are doable.
Software blinking is more complex than I was thinking!
I have experimented different implementation with different pro and cons, and I have not found a solution that fully satisfied me ...
This new version of the PR should have good tradeoff between complexity, memory footprint and the number of change required to enable it in a driver.
I have implemented only the software blinking for the led_gpio driver. I could update the other drivers later once this one get approved.

@thedjnK
Copy link
Contributor

thedjnK commented Jun 15, 2025

the whole point in a driver is that it controls hardware, this is doing a complete software thing, it has no place in drivers, if this is wanted then it should be a software component i.e. go in subsys

I am confuse here. Are you talking about the suggestion from @faxe1008 or about doing software blinking in drivers? If this about doing software blinking in drivers, I disagree. Although there is no hardware support, we are implementing driver API so I think it must be located here.

Have queried and seems people are OK with software implementations for driver functions so can be disregarded

@thedjnK thedjnK self-requested a review June 15, 2025 20:52
@thedjnK thedjnK dismissed their stale review June 15, 2025 20:52

software implementation of driver function is allowed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: LED Label to identify LED subsystem
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants