/* LED control * * (C) 2015-2017 by Harald Welte * (C) 2018 by sysmocom -s.f.m.c. GmbH, Author: Kevin Redon * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include "board.h" #include "utils.h" #include "led.h" #ifdef PINS_LEDS static const Pin pinsLeds[] = { PINS_LEDS } ; static void led_set(enum led led, int on) { ASSERT(led < PIO_LISTSIZE(pinsLeds)); if (on) PIO_Clear(&pinsLeds[led]); else PIO_Set(&pinsLeds[led]); } /* LED blinking code */ /* a single state in a sequence of blinking */ struct blink_state { /* duration of the state in ms */ uint16_t duration; /* brightness of LED during the state */ uint8_t on; } __attribute__((packed)); static const struct blink_state bs_off[] = { { 0, 0 } }; static const struct blink_state bs_on[] = { { 0, 1 } }; static const struct blink_state bs_5on_5off[] = { { 500, 1 }, { 500, 0 } }; static const struct blink_state bs_3on_5off[] = { { 300, 1 }, { 500, 0 } }; static const struct blink_state bs_3on_30off[] = { { 300, 1 }, { 3000, 0 } }; static const struct blink_state bs_3on_1off_3on_30off[] = { { 300, 1 }, { 100, 0 }, { 300, 1 }, { 3000, 0 } }; static const struct blink_state bs_3on_1off_3on_1off_3on_30off[] = { { 300, 1 }, { 100, 0 }, { 300, 1 }, { 100, 0 }, { 300, 1 }, { 3000, 0 } }; static const struct blink_state bs_2on_off[] = { { 200, 1 }, { 0, 0 }, }; static const struct blink_state bs_200on_off[] = { { 20000, 1 }, { 0, 0 }, }; static const struct blink_state bs_600on_off[] = { { 60000, 1 }, { 0, 0 }, }; static const struct blink_state bs_2off_on[] = { { 200, 0 }, { 0, 1 }, }; /* a blink pattern is an array of blink_states */ struct blink_pattern { const struct blink_state *states; uint16_t size; }; /* compiled-in default blinking patterns */ static const struct blink_pattern patterns[] = { [BLINK_ALWAYS_OFF] = { .states = bs_off, .size = ARRAY_SIZE(bs_off), }, [BLINK_ALWAYS_ON] = { .states = bs_on, .size = ARRAY_SIZE(bs_on), }, [BLINK_5O_5F] = { .states = bs_5on_5off, .size = ARRAY_SIZE(bs_5on_5off), }, [BLINK_3O_5F] = { .states = bs_3on_5off, .size = ARRAY_SIZE(bs_3on_5off), }, [BLINK_3O_30F] = { .states = bs_3on_30off, .size = ARRAY_SIZE(bs_3on_30off), }, [BLINK_3O_1F_3O_30F] = { .states = bs_3on_1off_3on_30off, .size = ARRAY_SIZE(bs_3on_1off_3on_30off), }, [BLINK_3O_1F_3O_1F_3O_30F] = { .states = bs_3on_1off_3on_1off_3on_30off, .size = ARRAY_SIZE(bs_3on_1off_3on_1off_3on_30off), }, [BLINK_2O_F] = { .states = bs_2on_off, .size = ARRAY_SIZE(bs_2on_off), }, [BLINK_200O_F] = { .states = bs_200on_off, .size = ARRAY_SIZE(bs_200on_off), }, [BLINK_600O_F] = { .states = bs_600on_off, .size = ARRAY_SIZE(bs_600on_off), }, [BLINK_2F_O] = { .states = bs_2off_on, .size = ARRAY_SIZE(bs_2off_on), }, }; struct led_state { /* which led are we handling */ enum led led; /* timer */ struct osmo_timer_list timer; /* pointer and size of blink array */ const struct blink_pattern *pattern; unsigned int cur_state; unsigned int illuminated; /* static allocated space for custom blinking pattern */ struct blink_pattern pattern_cust; struct blink_state blink_cust[10]; }; static unsigned int cur_state_inc(struct led_state *ls) { ls->cur_state = (ls->cur_state + 1) % ls->pattern->size; return ls->cur_state; } static const struct blink_state * next_blink_state(struct led_state *ls) { return &ls->pattern->states[cur_state_inc(ls)]; } /* apply the next state to the LED */ static void apply_blinkstate(struct led_state *ls, const struct blink_state *bs) { led_set(ls->led, bs->on); ls->illuminated = bs->on; /* re-schedule the timer */ if (bs->duration) { uint32_t us = bs->duration * 1000; osmo_timer_schedule(&ls->timer, us / 1000000, us % 1000000); } } static void blink_tmr_cb(void *data) { struct led_state *ls = data; const struct blink_state *next_bs = next_blink_state(ls); /* apply the next state to the LED */ apply_blinkstate(ls, next_bs); } static struct led_state led_state[] = { [LED_RED] = { .led = LED_RED, .timer.cb = blink_tmr_cb, .timer.data = &led_state[LED_RED], }, [LED_GREEN] = { .led = LED_GREEN, .timer.cb = blink_tmr_cb, .timer.data = &led_state[LED_GREEN], }, }; #endif /* PINS_LEDS */ void led_blink(enum led led, enum led_pattern blink) { #ifdef PINS_LEDS struct led_state *ls; if (led >= ARRAY_SIZE(led_state)) return; ls = &led_state[led]; /* stop previous blinking, if any */ osmo_timer_del(&ls->timer); led_set(led, 0); ls->illuminated = 0; ls->pattern = NULL; ls->cur_state = 0; switch (blink) { case BLINK_CUSTOM: ls->pattern = &ls->pattern_cust; break; default: if (blink >= ARRAY_SIZE(patterns)) return; ls->pattern = &patterns[blink]; break; } if (ls->pattern && ls->pattern->size > 0) apply_blinkstate(ls, &ls->pattern->states[0]); #endif } enum led_pattern led_get(enum led led) { #ifdef PINS_LEDS struct led_state *ls; unsigned int i; if (led >= ARRAY_SIZE(led_state)) return -1; ls = &led_state[led]; if (ls->pattern == &ls->pattern_cust) return BLINK_CUSTOM; for (i = 0; i < ARRAY_SIZE(patterns); i++) { if (ls->pattern == &patterns[i]) return i; } #endif /* default case, shouldn't be reached */ return -1; } void led_start(void) { led_set(LED_GREEN, led_state[LED_GREEN].illuminated); led_set(LED_RED, led_state[LED_RED].illuminated); } void led_stop(void) { led_set(LED_GREEN, 0); led_set(LED_RED, 0); } void led_init(void) { #ifdef PINS_LEDS PIO_Configure(pinsLeds, PIO_LISTSIZE(pinsLeds)); led_set(LED_GREEN, 0); led_set(LED_RED, 0); #endif } void led_fini(void) { #ifdef PINS_LEDS /* we don't actually need to do this, but just in case... */ osmo_timer_del(&led_state[LED_RED].timer); osmo_timer_del(&led_state[LED_GREEN].timer); led_set(LED_GREEN, 0); led_set(LED_RED, 0); #endif }