diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/src/os/blinkcode.c | 101 | ||||
-rw-r--r-- | firmware/src/os/blinkcode.h | 14 | ||||
-rw-r--r-- | firmware/src/os/pit.c | 87 | ||||
-rw-r--r-- | firmware/src/os/pit.h | 16 |
4 files changed, 217 insertions, 1 deletions
diff --git a/firmware/src/os/blinkcode.c b/firmware/src/os/blinkcode.c new file mode 100644 index 0000000..8485392 --- /dev/null +++ b/firmware/src/os/blinkcode.c @@ -0,0 +1,101 @@ +/* SAM7DFU blink code support + * (C) 2006 by Harald Welte <laforge@gnumonks.org> + * + */ + +#include <os/pit.h> +#include <os/dbgu.h> +#include <os/led.h> +#include <os/blinkcode.h> + +#define NUM_LEDS 2 + +enum blinkcode_state { + BLINKCODE_STATE_NONE, + BLINKCODE_STATE_SILENT, /* period of silence at start */ + BLINKCODE_STATE_INIT, /* initial long light */ + BLINKCODE_STATE_BLINK_OFF, /* blinking out, currently off */ + BLINKCODE_STATE_BLINK_ON, /* blinking out, currently on */ + BLINKCODE_STATE_DONE, +}; + +#define TIME_SILENT (1000) +#define TIME_INIT (1000) +#define TIME_BLINK (100) + +struct blinker { + struct timer_list timer; + enum blinkcode_state state; + int8_t num; + u_int8_t led; +}; + +static struct blinker blink_state[NUM_LEDS]; + +static void blinkcode_cb(void *data) +{ + /* we got called back by the timer */ + struct blinker *bl = data; + + switch (bl->state) { + case BLINKCODE_STATE_NONE: + led_switch(bl->led, 0); + bl->state = BLINKCODE_STATE_SILENT; + bl->timer.expires = jiffies + TIME_SILENT; + break; + case BLINKCODE_STATE_SILENT: + /* we've finished the period of silence, turn led on */ + led_switch(bl->led, 1); + bl->state = BLINKCODE_STATE_INIT; + bl->timer.expires = jiffies + TIME_INIT; + break; + case BLINKCODE_STATE_INIT: + /* we've finished the period of init */ + led_switch(bl->led, 0); + bl->state = BLINKCODE_STATE_BLINK_OFF; + bl->timer.expires = jiffies + TIME_BLINK; + break; + case BLINKCODE_STATE_BLINK_OFF: + /* we've been off, turn on */ + led_switch(bl->led, 1); + bl->state = BLINKCODE_STATE_BLINK_ON; + bl->num--; + if (bl->num <= 0) { + bl->state = BLINKCODE_STATE_NONE; + return; + } + break; + case BLINKCODE_STATE_BLINK_ON: + /* we've been on, turn off */ + led_switch(bl->led, 0); + bl->state = BLINKCODE_STATE_BLINK_OFF; + break; + } + /* default case: re-add the timer */ + timer_add(&bl->timer); +} + +void blinkcode_set(int led, enum blinkcode_num num) +{ + if (led >= NUM_LEDS) + return; + + timer_del(&blink_state[led].timer); + + blink_state[led].num = num; + blink_state[led].state = BLINKCODE_STATE_NONE; + + if (num != BLINKCODE_NONE) + timer_add(&blink_state[led].timer); +} + +void blinkcode_init(void) +{ + int i; + + for (i = 0; i < NUM_LEDS; i++) { + blink_state[i].num = 0; + blink_state[i].state = BLINKCODE_STATE_NONE; + blink_state[i].led = i+1; + } +} diff --git a/firmware/src/os/blinkcode.h b/firmware/src/os/blinkcode.h new file mode 100644 index 0000000..142c148 --- /dev/null +++ b/firmware/src/os/blinkcode.h @@ -0,0 +1,14 @@ +#ifndef _BLINKCODE_H +#define _BLINKCODE_H + +enum blinkcode_num { + BLINKCODE_NONE, + BLINKCODE_IDLE, + BLINKCODE_DFU_MODE, + BLINKCODE_DFU_PROBLEM, + BLINKCODE_ +}; + +extern void blinkcode_set(int led, enum blinkcode_num); +extern void blinkcode_init(void); +#endif diff --git a/firmware/src/os/pit.c b/firmware/src/os/pit.c index 5726f06..5c096cc 100644 --- a/firmware/src/os/pit.c +++ b/firmware/src/os/pit.c @@ -15,20 +15,105 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * + * TODO: handle jiffies wrap correctly! */ #include <errno.h> #include <sys/types.h> +#include <asm/system.h> + #include <lib_AT91SAM7.h> #include <AT91SAM7.h> +#include <os/pit.h> + #include "../openpcd.h" /* PIT runs at MCK/16 (= 3MHz) */ #define PIV_MS(x) (x * 3000) +static struct timer_list *timers; + +unsigned long jiffies; + +static void __timer_insert(struct timer_list *new) +{ + struct timer_list *tl, *tl_prev = NULL; + + if (!timers) { + /* optimize for the common, fast case */ + new->next = NULL; + timers = new; + return; + } + + for (tl = timers; tl != NULL; tl = tl->next) { + if (tl->expires < new->expires) { + /* we need ot add just before this one */ + if (!tl_prev) { + new->next = timers; + timers = new; + } else { + new->next = tl; + tl_prev->next = new; + } + } + tl_prev = tl; + } +} + +static void __timer_remove(struct timer_list *old) +{ + struct timer_list *tl, *tl_prev = NULL; + + for (tl = timers; tl != NULL; tl = tl->next) { + if (tl == old) { + if (tl == timers) + timers = tl->next; + else + tl_prev->next = tl->next; + } + tl_prev = tl; + } +} + +int timer_del(struct timer_list *tl) +{ + unsigned long flags; + + local_irq_save(flags); + __timer_remove(tl); + local_irq_restore(flags); +} + +void timer_add(struct timer_list *tl) +{ + unsigned long flags; + + local_irq_save(flags); + __timer_insert(tl); + local_irq_restore(flags); +} + static void pit_irq(void) { - /* FIXME: do something */ + struct timer_list *tl; + unsigned long flags; + + jiffies++; + + /* this is the most simple/stupid algorithm one can come up with, but + * hey, we're on a small embedded platform with only a hand ful + * of timers, at all */ + for (tl = timers; tl != 0; tl = tl->next) { + if (tl->expires >= jiffies) { + tl->function(tl->data); + local_irq_save(flags); + /* delete timer from list */ + __timer_remove(tl); + local_irq_restore(flags); + } + } } void pit_mdelay(u_int32_t ms) diff --git a/firmware/src/os/pit.h b/firmware/src/os/pit.h index 92426e9..e941aeb 100644 --- a/firmware/src/os/pit.h +++ b/firmware/src/os/pit.h @@ -1,6 +1,22 @@ #ifndef _PIT_H #define _PIT_H +#include <sys/types.h> + +/* This API (but not the code) is modelled after the Linux API */ + +struct timer_list { + struct timer_list *next; + unsigned long expires; + void (*function)(void *data); + void *data; +}; + +extern unsigned long jiffies; + +extern void timer_add(struct timer_list *timer); +extern int timer_del(struct timer_list *timer); + extern void pit_init(void); extern void pit_mdelay(u_int32_t ms); |