blob: 4822a6dd0e312bd9c0c224d6b454d20b08034960 [file] [log] [blame]
Kévin Redon9a12d682018-07-08 13:21:16 +02001/* LED control
2 *
3 * (C) 2015-2017 by Harald Welte <hwelte@hmw-consulting.de>
4 * (C) 2018 by sysmocom -s.f.m.c. GmbH, Author: Kevin Redon <kredon@sysmocom.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
19 */
Harald Welteabba8a82017-03-06 16:58:00 +010020#include <stdint.h>
21#include <string.h>
22#include <assert.h>
23
24#include <osmocom/core/timer.h>
25
26#include "board.h"
27#include "utils.h"
28#include "led.h"
29
30#ifdef PINS_LEDS
31static const Pin pinsLeds[] = { PINS_LEDS } ;
32
33static void led_set(enum led led, int on)
34{
35 ASSERT(led < PIO_LISTSIZE(pinsLeds));
36
37 if (on)
Kévin Redon33d1eb72018-07-08 13:58:12 +020038 PIO_Clear(&pinsLeds[led]);
King Kévin4cbdc7c2018-07-04 16:36:17 +020039 else
Kévin Redon33d1eb72018-07-08 13:58:12 +020040 PIO_Set(&pinsLeds[led]);
Harald Welteabba8a82017-03-06 16:58:00 +010041}
42
43/* LED blinking code */
44
45/* a single state in a sequence of blinking */
46struct blink_state {
47 /* duration of the state in ms */
48 uint16_t duration;
Kévin Redon11914d92018-06-27 16:33:01 +020049 /* brightness of LED during the state */
Harald Welteabba8a82017-03-06 16:58:00 +010050 uint8_t on;
51} __attribute__((packed));
52
53static const struct blink_state bs_off[] = {
54 { 0, 0 }
55};
56
57static const struct blink_state bs_on[] = {
58 { 0, 1 }
59};
60
Eric Wild1ad205e2021-07-26 23:54:51 +020061static const struct blink_state bs_5on_5off[] = {
62 { 500, 1 }, { 500, 0 }
63};
64
Harald Welteabba8a82017-03-06 16:58:00 +010065static const struct blink_state bs_3on_5off[] = {
66 { 300, 1 }, { 500, 0 }
67};
68
69static const struct blink_state bs_3on_30off[] = {
70 { 300, 1 }, { 3000, 0 }
71};
72
73static const struct blink_state bs_3on_1off_3on_30off[] = {
74 { 300, 1 }, { 100, 0 }, { 300, 1 }, { 3000, 0 }
75};
76
77static const struct blink_state bs_3on_1off_3on_1off_3on_30off[] = {
78 { 300, 1 }, { 100, 0 }, { 300, 1 }, { 100, 0 }, { 300, 1 }, { 3000, 0 }
79};
Kévin Redond9754112018-07-08 16:29:37 +020080
Kévin Redon11914d92018-06-27 16:33:01 +020081static const struct blink_state bs_2on_off[] = {
82 { 200, 1 }, { 0, 0 },
83};
Kévin Redond9754112018-07-08 16:29:37 +020084
Harald Welteabba8a82017-03-06 16:58:00 +010085static const struct blink_state bs_200on_off[] = {
86 { 20000, 1 }, { 0, 0 },
87};
Kévin Redond9754112018-07-08 16:29:37 +020088
Harald Welteabba8a82017-03-06 16:58:00 +010089static const struct blink_state bs_600on_off[] = {
90 { 60000, 1 }, { 0, 0 },
91};
92
Kévin Redond9754112018-07-08 16:29:37 +020093static const struct blink_state bs_2off_on[] = {
94 { 200, 0 }, { 0, 1 },
95};
96
Harald Welteabba8a82017-03-06 16:58:00 +010097
98/* a blink pattern is an array of blink_states */
99struct blink_pattern {
100 const struct blink_state *states;
101 uint16_t size;
102};
103
104/* compiled-in default blinking patterns */
105static const struct blink_pattern patterns[] = {
106 [BLINK_ALWAYS_OFF] = {
107 .states = bs_off,
108 .size = ARRAY_SIZE(bs_off),
109 },
110 [BLINK_ALWAYS_ON] = {
111 .states = bs_on,
112 .size = ARRAY_SIZE(bs_on),
113 },
Eric Wild1ad205e2021-07-26 23:54:51 +0200114 [BLINK_5O_5F] = {
115 .states = bs_5on_5off,
116 .size = ARRAY_SIZE(bs_5on_5off),
117 },
Harald Welteabba8a82017-03-06 16:58:00 +0100118 [BLINK_3O_5F] = {
119 .states = bs_3on_5off,
120 .size = ARRAY_SIZE(bs_3on_5off),
121 },
122 [BLINK_3O_30F] = {
123 .states = bs_3on_30off,
124 .size = ARRAY_SIZE(bs_3on_30off),
125 },
126 [BLINK_3O_1F_3O_30F] = {
127 .states = bs_3on_1off_3on_30off,
128 .size = ARRAY_SIZE(bs_3on_1off_3on_30off),
129 },
130 [BLINK_3O_1F_3O_1F_3O_30F] = {
131 .states = bs_3on_1off_3on_1off_3on_30off,
132 .size = ARRAY_SIZE(bs_3on_1off_3on_1off_3on_30off),
133 },
Kévin Redon11914d92018-06-27 16:33:01 +0200134 [BLINK_2O_F] = {
135 .states = bs_2on_off,
136 .size = ARRAY_SIZE(bs_2on_off),
137 },
Harald Welteabba8a82017-03-06 16:58:00 +0100138 [BLINK_200O_F] = {
139 .states = bs_200on_off,
140 .size = ARRAY_SIZE(bs_200on_off),
141 },
142 [BLINK_600O_F] = {
143 .states = bs_600on_off,
144 .size = ARRAY_SIZE(bs_600on_off),
145 },
Kévin Redond9754112018-07-08 16:29:37 +0200146 [BLINK_2F_O] = {
147 .states = bs_2off_on,
148 .size = ARRAY_SIZE(bs_2off_on),
149 },
150
Harald Welteabba8a82017-03-06 16:58:00 +0100151};
152
153struct led_state {
154 /* which led are we handling */
155 enum led led;
156
157 /* timer */
158 struct osmo_timer_list timer;
159
160 /* pointer and size of blink array */
161 const struct blink_pattern *pattern;
162
163 unsigned int cur_state;
164 unsigned int illuminated;
165
166 /* static allocated space for custom blinking pattern */
167 struct blink_pattern pattern_cust;
168 struct blink_state blink_cust[10];
169};
170
171static unsigned int cur_state_inc(struct led_state *ls)
172{
173 ls->cur_state = (ls->cur_state + 1) % ls->pattern->size;
174 return ls->cur_state;
175}
176
177static const struct blink_state *
178next_blink_state(struct led_state *ls)
179{
180 return &ls->pattern->states[cur_state_inc(ls)];
181}
182
183/* apply the next state to the LED */
184static void apply_blinkstate(struct led_state *ls,
185 const struct blink_state *bs)
186{
187 led_set(ls->led, bs->on);
188 ls->illuminated = bs->on;
189
190 /* re-schedule the timer */
191 if (bs->duration) {
192 uint32_t us = bs->duration * 1000;
193 osmo_timer_schedule(&ls->timer, us / 1000000, us % 1000000);
194 }
195}
196
197static void blink_tmr_cb(void *data)
198{
199 struct led_state *ls = data;
200 const struct blink_state *next_bs = next_blink_state(ls);
201
202 /* apply the next state to the LED */
203 apply_blinkstate(ls, next_bs);
204}
205
206static struct led_state led_state[] = {
Harald Welteabba8a82017-03-06 16:58:00 +0100207 [LED_RED] = {
208 .led = LED_RED,
209 .timer.cb = blink_tmr_cb,
210 .timer.data = &led_state[LED_RED],
211 },
Kévin Redon11914d92018-06-27 16:33:01 +0200212 [LED_GREEN] = {
213 .led = LED_GREEN,
214 .timer.cb = blink_tmr_cb,
215 .timer.data = &led_state[LED_GREEN],
216 },
Harald Welteabba8a82017-03-06 16:58:00 +0100217};
218#endif /* PINS_LEDS */
219
220void led_blink(enum led led, enum led_pattern blink)
221{
222#ifdef PINS_LEDS
223 struct led_state *ls;
224
225 if (led >= ARRAY_SIZE(led_state))
226 return;
227 ls = &led_state[led];
228
229 /* stop previous blinking, if any */
230 osmo_timer_del(&ls->timer);
231 led_set(led, 0);
232 ls->illuminated = 0;
233 ls->pattern = NULL;
234 ls->cur_state = 0;
235
236 switch (blink) {
237 case BLINK_CUSTOM:
238 ls->pattern = &ls->pattern_cust;
239 break;
240 default:
241 if (blink >= ARRAY_SIZE(patterns))
242 return;
243 ls->pattern = &patterns[blink];
244 break;
245 }
246
247 if (ls->pattern && ls->pattern->size > 0)
248 apply_blinkstate(ls, &ls->pattern->states[0]);
249#endif
250}
251
252enum led_pattern led_get(enum led led)
253{
254#ifdef PINS_LEDS
255 struct led_state *ls;
256 unsigned int i;
257
258 if (led >= ARRAY_SIZE(led_state))
259 return -1;
260 ls = &led_state[led];
261
262 if (ls->pattern == &ls->pattern_cust)
263 return BLINK_CUSTOM;
264
265 for (i = 0; i < ARRAY_SIZE(patterns); i++) {
266 if (ls->pattern == &patterns[i])
267 return i;
268 }
269#endif
270 /* default case, shouldn't be reached */
271 return -1;
272}
273
274void led_start(void)
275{
276 led_set(LED_GREEN, led_state[LED_GREEN].illuminated);
277 led_set(LED_RED, led_state[LED_RED].illuminated);
278}
279
280void led_stop(void)
281{
282 led_set(LED_GREEN, 0);
283 led_set(LED_RED, 0);
284}
285
286void led_init(void)
287{
288#ifdef PINS_LEDS
289 PIO_Configure(pinsLeds, PIO_LISTSIZE(pinsLeds));
290 led_set(LED_GREEN, 0);
291 led_set(LED_RED, 0);
292#endif
293}
294
295void led_fini(void)
296{
297#ifdef PINS_LEDS
298 /* we don't actually need to do this, but just in case... */
299 osmo_timer_del(&led_state[LED_RED].timer);
300 osmo_timer_del(&led_state[LED_GREEN].timer);
301 led_set(LED_GREEN, 0);
302 led_set(LED_RED, 0);
303#endif
304}