-
Notifications
You must be signed in to change notification settings - Fork 7.7k
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
base: main
Are you sure you want to change the base?
Conversation
1ea7e7c
to
27baa49
Compare
There was a problem hiding this 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.
drivers/led/blink_fallback.c
Outdated
struct k_work_delayable work; | ||
uint32_t delay_on; | ||
uint32_t delay_off; | ||
}; |
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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.
drivers/led/blink_fallback.c
Outdated
@@ -0,0 +1,130 @@ | |||
/* |
There was a problem hiding this comment.
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.
drivers/led/blink_fallback.c
Outdated
k_work_schedule(&data->work, K_MSEC(data->delay_on)); | ||
} | ||
|
||
static void led_blink_fallback_off(struct k_work *work) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
drivers/led/Kconfig
Outdated
default 3 | ||
help | ||
This set the maximum number of LED that could blink | ||
in the same time using blink fallback. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
drivers/led/CMakeLists.txt
Outdated
@@ -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) |
There was a problem hiding this comment.
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".
drivers/led/blink_fallback.c
Outdated
} | ||
|
||
/* Stop blinking, more likely when we use led_on or led_off */ | ||
if (delay_on == 0 && delay_off == 0) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
drivers/led/blink_fallback.c
Outdated
} | ||
|
||
/* Stop blinking, more likely when we use led_on or led_off */ | ||
if (delay_on == 0 && delay_off == 0) { |
There was a problem hiding this comment.
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 */
}
include/zephyr/drivers/led.h
Outdated
@@ -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); |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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.
include/zephyr/drivers/led.h
Outdated
if (api->blink == NULL) { | ||
led_blink_fallback(dev, led, 0, 0); | ||
} | ||
#endif |
There was a problem hiding this comment.
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.
include/zephyr/drivers/led.h
Outdated
return -ENOSYS; | ||
#endif | ||
} | ||
return api->blink(dev, led, delay_on, delay_off); |
There was a problem hiding this comment.
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.
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. |
There was a problem hiding this 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
drivers/led/blink_fallback.c
Outdated
|
||
static void led_blink_fallback_off(struct k_work *work); | ||
|
||
static K_MUTEX_DEFINE(led_blink_fallback_mutex); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
drivers/led/blink_fallback.c
Outdated
if (api->on) { | ||
api->on(data->dev, data->led); | ||
} else { | ||
api->set_brightness(data->dev, data->led, LED_BRIGTHNESS_MAX); | ||
} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
drivers/led/blink_fallback.c
Outdated
if (api->off) { | ||
api->off(data->dev, data->led); | ||
} else { | ||
api->set_brightness(data->dev, data->led, 0); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same, led_off
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>
27baa49
to
4c95949
Compare
|
I am confuse here. Are you talking about the suggestion from @faxe1008 or about doing software blinking in drivers? |
I did a lot of change since the previous version. I hope this is going in the direction even if some improvement are doable. |
Have queried and seems people are OK with software implementations for driver functions so can be disregarded |
software implementation of driver function is allowed
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.