Skip to content

Commit c5be998

Browse files
committed
drivers: led: Provide a software implementation of blink API
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>
1 parent ba127f6 commit c5be998

File tree

3 files changed

+150
-3
lines changed

3 files changed

+150
-3
lines changed

drivers/led/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ config LED_SHELL
2626
help
2727
Enable LED shell for testing.
2828

29+
config LED_BLINK_SOFTWARE
30+
bool "LED Blink fallback"
31+
help
32+
Provides a software implementation of LED blink API.
33+
This could be used when the LED controller doesn't support blinking.
34+
2935
# zephyr-keep-sorted-start
3036
source "drivers/led/Kconfig.axp192"
3137
source "drivers/led/Kconfig.dac"

drivers/led/led_blink.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) 2025 BayLibre SAS
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_DRIVERS_LED_LED_BLINK_H_
8+
#define ZEPHYR_DRIVERS_LED_LED_BLINK_H_
9+
10+
#define LED_BLINK_SOFTWARE_DATA_INIT(node_id) {}
11+
12+
#define LED_BLINK_SOFTWARE_DATA(inst, name) \
13+
COND_CODE_1(CONFIG_LED_BLINK_SOFTWARE, ( \
14+
.name = (struct led_blink_software_data[]) {\
15+
DT_INST_FOREACH_CHILD_SEP(inst, LED_BLINK_SOFTWARE_DATA_INIT, (,)) \
16+
},), ())
17+
18+
#ifdef CONFIG_LED_BLINK_SOFTWARE
19+
struct led_blink_software_data {
20+
const struct device *dev;
21+
uint32_t led;
22+
struct k_work_delayable work;
23+
uint32_t delay_on;
24+
uint32_t delay_off;
25+
};
26+
27+
int led_blink_software_start(const struct device *dev, uint32_t led, uint32_t delay_on,
28+
uint32_t delay_off);
29+
#else
30+
struct led_blink_software_data;
31+
static inline int led_blink_software_start(const struct device *dev, uint32_t led,
32+
uint32_t delay_on, uint32_t delay_off)
33+
{
34+
return -ENOSYS;
35+
}
36+
#endif
37+
38+
#endif /* ZEPHYR_DRIVERS_LED_LED_BLINK_H_ */

drivers/led/led_core.c

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
*/
66

77
#include <zephyr/drivers/led.h>
8+
#include <zephyr/kernel.h>
89

9-
int z_impl_led_on(const struct device *dev, uint32_t led)
10+
#include "led_blink.h"
11+
12+
static int led_core_on(const struct device *dev, uint32_t led)
1013
{
1114
const struct led_driver_api *api = (const struct led_driver_api *)dev->api;
1215

@@ -21,7 +24,7 @@ int z_impl_led_on(const struct device *dev, uint32_t led)
2124
return api->on(dev, led);
2225
}
2326

24-
int z_impl_led_off(const struct device *dev, uint32_t led)
27+
static int led_core_off(const struct device *dev, uint32_t led)
2528
{
2629
const struct led_driver_api *api = (const struct led_driver_api *)dev->api;
2730

@@ -36,10 +39,110 @@ int z_impl_led_off(const struct device *dev, uint32_t led)
3639
return api->off(dev, led);
3740
}
3841

42+
#ifdef CONFIG_LED_BLINK_SOFTWARE
43+
static void led_blink_software_off(struct k_work *work);
44+
static struct led_blink_software_data *led_blink_software_get_data(const struct device *dev,
45+
uint32_t led)
46+
{
47+
const struct led_driver_api *api = (const struct led_driver_api *)dev->api;
48+
49+
if (!api->get_blink_data) {
50+
return NULL;
51+
}
52+
53+
return api->get_blink_data(dev, led);
54+
}
55+
56+
static void led_blink_software_on(struct k_work *work)
57+
{
58+
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
59+
struct led_blink_software_data *data =
60+
CONTAINER_OF(dwork, struct led_blink_software_data, work);
61+
62+
led_core_on(data->dev, data->led);
63+
k_work_init_delayable(&data->work, led_blink_software_off);
64+
k_work_schedule(&data->work, K_MSEC(data->delay_on));
65+
}
66+
67+
static void led_blink_software_off(struct k_work *work)
68+
{
69+
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
70+
struct led_blink_software_data *data =
71+
CONTAINER_OF(dwork, struct led_blink_software_data, work);
72+
73+
led_core_off(data->dev, data->led);
74+
k_work_init_delayable(&data->work, led_blink_software_on);
75+
k_work_schedule(&data->work, K_MSEC(data->delay_off));
76+
}
77+
78+
int led_blink_software_start(const struct device *dev, uint32_t led, uint32_t delay_on,
79+
uint32_t delay_off)
80+
{
81+
struct led_blink_software_data *data;
82+
83+
data = led_blink_software_get_data(dev, led);
84+
if (!data) {
85+
return -EINVAL;
86+
}
87+
88+
if (!delay_on && !delay_off) {
89+
/* Default 1Hz blinking when delay_on and delay_off are 0 */
90+
delay_on = 500;
91+
delay_off = 500;
92+
} else if (!delay_on) {
93+
/* Always off */
94+
led_core_off(dev, led);
95+
} else if (!delay_off) {
96+
/* Always on */
97+
led_core_on(dev, led);
98+
}
99+
100+
data->dev = dev;
101+
data->led = led;
102+
data->delay_on = delay_on;
103+
data->delay_off = delay_off;
104+
105+
k_work_init_delayable(&data->work, led_blink_software_on);
106+
return k_work_schedule(&data->work, K_NO_WAIT);
107+
}
108+
109+
static void led_blink_software_stop(const struct device *dev, uint32_t led)
110+
{
111+
struct led_blink_software_data *data;
112+
113+
data = led_blink_software_get_data(dev, led);
114+
if (data) {
115+
struct k_work_sync sync;
116+
117+
k_work_cancel_delayable_sync(&data->work, &sync);
118+
}
119+
}
120+
#else
121+
static inline void led_blink_software_stop(const struct device *dev, uint32_t led)
122+
{
123+
}
124+
#endif /* CONFIG_LED_BLINK_SOFTWARE */
125+
126+
int z_impl_led_on(const struct device *dev, uint32_t led)
127+
{
128+
led_blink_software_stop(dev, led);
129+
return led_core_on(dev, led);
130+
}
131+
132+
int z_impl_led_off(const struct device *dev, uint32_t led)
133+
{
134+
led_blink_software_stop(dev, led);
135+
return led_core_off(dev, led);
136+
}
137+
39138
int z_impl_led_set_brightness(const struct device *dev, uint32_t led, uint8_t value)
40139
{
41140
const struct led_driver_api *api = (const struct led_driver_api *)dev->api;
42141

142+
if (!value) {
143+
led_blink_software_stop(dev, led);
144+
}
145+
43146
if (api->set_brightness == NULL) {
44147
if (api->on == NULL || api->off == NULL) {
45148
return -ENOSYS;
@@ -66,7 +169,7 @@ int z_impl_led_blink(const struct device *dev, uint32_t led, uint32_t delay_on,
66169
const struct led_driver_api *api = (const struct led_driver_api *)dev->api;
67170

68171
if (api->blink == NULL) {
69-
return -ENOSYS;
172+
return led_blink_software_start(dev, led, delay_on, delay_off);
70173
}
71174
return api->blink(dev, led, delay_on, delay_off);
72175
}

0 commit comments

Comments
 (0)