Make keyboard repeat mask run-time configurable.
[bertos.git] / drv / kbd.c
index d078418cf1dceae5218413e96cb37d3d23ca489e..4dabd3b3b942931931d0cfed67ed388e19c95851 100755 (executable)
--- a/drv/kbd.c
+++ b/drv/kbd.c
 
 /*#*
  *#* $Log$
+ *#* Revision 1.7  2006/06/03 13:57:36  bernie
+ *#* Make keyboard repeat mask run-time configurable.
+ *#*
+ *#* Revision 1.6  2006/03/20 17:50:17  bernie
+ *#* Add FreeRTOS and Observers support.
+ *#*
+ *#* Revision 1.5  2006/02/27 22:39:45  bernie
+ *#* Misc build and doc fixes from project_grl.
+ *#*
+ *#* Revision 1.4  2006/02/24 00:27:14  bernie
+ *#* Use new naming convention for list macros.
+ *#*
+ *#* Revision 1.3  2006/02/17 21:15:42  bernie
+ *#* Add MOD_CHECK() checks.
+ *#*
+ *#* Revision 1.2  2006/02/10 12:36:20  bernie
+ *#* Add preliminary FreeRTOS support; Enforce CONFIG_* definitions.
+ *#*
  *#* Revision 1.1  2005/06/27 21:28:45  bernie
  *#* Import generic keyboard driver.
  *#*
 
 #include <drv/timer.h>
 #include <drv/kbd.h>
-#include <drv/buzzer.h>
 
 #include <cfg/debug.h>
+#include <cfg/module.h>
+#include <appconfig.h>
 
+/* Configuration sanity checks */
+#if !defined(CONFIG_KBD_POLL) || (CONFIG_KBD_POLL != KBD_POLL_SOFTINT && CONFIG_KBD_POLL != CONFIG_POLL_FREERTOS)
+       #error CONFIG_KBD_POLL must be defined to either KBD_POLL_SOFTINT or CONFIG_POLL_FREERTOS
+#endif
+#if !defined(CONFIG_KBD_BEEP) || (CONFIG_KBD_BEEP != 0 && CONFIG_KBD_BEEP != 1)
+       #error CONFIG_KBD_BEEP must be defined to either 0 or 1
+#endif
+#if !defined(CONFIG_KBD_OBSERVER) || (CONFIG_KBD_OBSERVER != 0 && CONFIG_KBD_OBSERVER != 1)
+       #error CONFIG_KBD_OBSERVER must be defined to either 0 or 1
+#endif
 
-
+#if CONFIG_KBD_BEEP
+       #include <drv/buzzer.h>
+#endif
 
 #define KBD_CHECK_INTERVAL  10  /*!< (ms) Timing for kbd softint */
 #define KBD_DEBOUNCE_TIME   30  /*!< (ms) Debounce time */
@@ -51,8 +82,11 @@ static enum { KS_IDLE, KS_REPDELAY, KS_REPEAT } kbd_rptStatus;
 
 static volatile keymask_t kbd_buf; /*!< Single entry keyboard buffer */
 static volatile keymask_t kbd_cnt; /*!< Number of keypress events in \c kbd_buf */
+static keymask_t kbd_rpt_mask;     /**< Mask of repeatable keys. */
 
-static Timer kbd_timer;            /*!< KeyboardKBD_BEEP_TIME softtimer */
+#if CONFIG_KBD_POLL == KBD_POLL_SOFTINT
+static Timer kbd_timer;            /*!< Keyboard softtimer */
+#endif
 
 static List kbd_rawHandlers;       /*!< Raw keyboard handlers */
 static List kbd_handlers;          /*!< Cooked keyboard handlers */
@@ -64,12 +98,22 @@ static KbdHandler kbd_rptHandler;  /*!< Auto-repeat keyboard handler */
 static KbdHandler kbd_lngHandler;  /*!< Long pression keys handler */
 #endif
 
+#if CONFIG_KBD_OBSERVER
+       #include <mware/observer.h>
+       Subject kbd_subject;
+#endif
 
 
 /*!
- * Keyboard soft-irq handler.
+ * Poll keyboard and dispatch keys to handlers.
+ *
+ * Read the key states and invoke all keyboard
+ * handlers to process the new state.
+ *
+ * Call this function periodically using a software
+ * timer, an interrupt or a process.
  */
-static void kbd_softint(UNUSED_ARG(iptr_t, arg))
+static void kbd_poll(void)
 {
        /*! Currently depressed key */
        static keymask_t current_key;
@@ -78,7 +122,7 @@ static void kbd_softint(UNUSED_ARG(iptr_t, arg))
        keymask_t key = kbd_readkeys();
 
        /* Call raw input handlers */
-       FOREACHNODE(handler, &kbd_rawHandlers)
+       FOREACH_NODE(handler, &kbd_rawHandlers)
                key = handler->hook(key);
 
        /* If this key was not previously pressed */
@@ -88,13 +132,37 @@ static void kbd_softint(UNUSED_ARG(iptr_t, arg))
                current_key = key;
 
                /* Call cooked input handlers */
-               FOREACHNODE(handler, &kbd_handlers)
+               FOREACH_NODE(handler, &kbd_handlers)
                        key = handler->hook(key);
        }
+}
+
+#if CONFIG_KBD_POLL == KBD_POLL_SOFTINT
 
+/*!
+ * Keyboard soft-irq handler.
+ */
+static void kbd_softint(UNUSED_ARG(iptr_t, arg))
+{
+       kbd_poll();
        timer_add(&kbd_timer);
 }
 
+#elif CONFIG_KBD_POLL == CONFIG_POLL_FREERTOS
+
+#include "FreeRTOS.h"
+#include "task.h"
+
+static portTASK_FUNCTION(kbd_task, arg)
+{
+       for (;;)
+       {
+               kbd_poll();
+               timer_delay(KBD_CHECK_INTERVAL);
+       }
+}
+
+#endif /* CONFIG_KBD_POLL */
 
 /*!
  * \brief Read a key from the keyboard buffer.
@@ -112,6 +180,11 @@ keymask_t kbd_peek(void)
 {
        keymask_t key = 0;
 
+// FIXME
+       /* Let other tasks run for a while */
+       extern void schedule(void);
+       schedule();
+
        /* Extract an event from the keyboard buffer */
        IRQ_DISABLE;
        if (kbd_cnt)
@@ -124,7 +197,6 @@ keymask_t kbd_peek(void)
        return key;
 }
 
-
 /*!
  * Wait for a keypress and return the mask of depressed keys.
  *
@@ -135,6 +207,7 @@ keymask_t kbd_get(void)
        keymask_t key;
 
        while (!(key = kbd_peek())) {}
+
        return key;
 }
 
@@ -177,12 +250,12 @@ void kbd_addHandler(struct KbdHandler *handler)
         * Search for the first node whose priority
         * is lower than the timer we want to add.
         */
-       FOREACHNODE(node,list)
+       FOREACH_NODE(node,list)
                if (node->pri < handler->pri)
                        break;
 
        /* Enqueue handler in the handlers chain */
-       INSERTBEFORE(&handler->link, &node->link);
+       INSERT_BEFORE(&handler->link, &node->link);
 
        IRQ_RESTORE(flags);
 }
@@ -209,8 +282,14 @@ static keymask_t kbd_defHandlerFunc(keymask_t key)
                kbd_buf = key;
                kbd_cnt = 1;
 
-               if (!(key & K_REPEAT))
-                       buz_beep(KBD_BEEP_TIME);
+               #if CONFIG_KBD_OBSERVER
+                       observer_notify(&kbd_subject, KBD_EVENT_KEY, &key);
+               #endif
+
+               #if CONFIG_KBD_BEEP
+                       if (!(key & K_REPEAT))
+                               buz_beep(KBD_BEEP_TIME);
+               #endif
        }
 
        /* Eat all input */
@@ -251,35 +330,44 @@ static keymask_t kbd_debHandlerFunc(keymask_t key)
 }
 
 #ifdef  K_LNG_MASK
-/*!
- * Handle long pression keys
+/**
+ * Handle long pression keys.
  */
 static keymask_t kbd_lngHandlerFunc(keymask_t key)
 {
-       static ticks_t stop;
+       static ticks_t start;
        ticks_t now = timer_clock();
 
        if (key & K_LNG_MASK)
        {
-               if (now - stop > 0)
-                       key &= K_LNG_MASK;
-               else
-                       key &= ~K_LNG_MASK;
+               if (now - start > ms_to_ticks(KBD_LNG_DELAY))
+                       key |= K_LONG;
        }
        else
-               stop = now + ms_to_ticks(KBD_LNG_DELAY);
+               start = now;
        return key;
 }
 #endif
 
+/**
+ * Set current mask of repeatable keys.
+ */
+keymask_t kbd_setRepeatMask(keymask_t mask)
+{
+       keymask_t oldmask = kbd_rpt_mask;
+       ATOMIC(kbd_rpt_mask = mask);
+       return oldmask;
+}
+
 /*!
  * Handle keyboard repeat
  */
 static keymask_t kbd_rptHandlerFunc(keymask_t key)
 {
-       /*! Timer for keyboard repeat events */
+       /* Timer for keyboard repeat events. */
        static ticks_t repeat_time;
 
+       /* Current repeat rate (for acceleration). */
        static ticks_t repeat_rate; /*! Current repeat rate (for acceleration) */
 
        ticks_t now = timer_clock();
@@ -287,7 +375,7 @@ static keymask_t kbd_rptHandlerFunc(keymask_t key)
        switch (kbd_rptStatus)
        {
                case KS_IDLE:
-                       if (key & K_RPT_MASK)
+                       if (key & kbd_rpt_mask)
                        {
                                repeat_time = now;
                                kbd_rptStatus = KS_REPDELAY;
@@ -295,11 +383,11 @@ static keymask_t kbd_rptHandlerFunc(keymask_t key)
                        break;
 
                case KS_REPDELAY:
-                       if (key & K_RPT_MASK)
+                       if (key & kbd_rpt_mask)
                        {
                                if (now - repeat_time > ms_to_ticks(KBD_REPEAT_DELAY))
                                {
-                                       key = (key & K_RPT_MASK) | K_REPEAT;
+                                       key = (key & kbd_rpt_mask) | K_REPEAT;
                                        repeat_time = now;
                                        repeat_rate = ms_to_ticks(KBD_REPEAT_RATE);
                                        kbd_rptStatus = KS_REPEAT;
@@ -312,12 +400,12 @@ static keymask_t kbd_rptHandlerFunc(keymask_t key)
                        break;
 
                case KS_REPEAT:
-                       if (key & K_RPT_MASK)
+                       if (key & kbd_rpt_mask)
                        {
                                if (now - repeat_time > repeat_rate)
                                {
                                        /* Enqueue a new event in the buffer */
-                                       key = (key & K_RPT_MASK) | K_REPEAT;
+                                       key = (key & kbd_rpt_mask) | K_REPEAT;
                                        repeat_time = now;
 
                                        /* Repeat rate acceleration */
@@ -337,20 +425,19 @@ static keymask_t kbd_rptHandlerFunc(keymask_t key)
 }
 
 
+MOD_DEFINE(kbd)
 
 /*!
  * Initialize keyboard ports and softtimer
  */
 void kbd_init(void)
 {
-
-       cpuflags_t flags;
-       IRQ_SAVE_DISABLE(flags);
+#if CONFIG_KBD_BEEP
+       MOD_CHECK(buzzer);
+#endif
 
        KBD_HW_INIT;
 
-       IRQ_RESTORE(flags);
-
        /* Init handlers lists */
        LIST_INIT(&kbd_handlers);
        LIST_INIT(&kbd_rawHandlers);
@@ -369,21 +456,39 @@ void kbd_init(void)
        kbd_addHandler(&kbd_lngHandler);
        #endif
 
-
        /* Add repeat keyboard handler */
        kbd_rptHandler.hook = kbd_rptHandlerFunc;
        kbd_rptHandler.pri = 80; /* high priority */
        kbd_rptHandler.flags = KHF_RAWKEYS;
        kbd_addHandler(&kbd_rptHandler);
 
-
        /* Add default keyboard handler */
        kbd_defHandler.hook = kbd_defHandlerFunc;
        kbd_defHandler.pri = -128; /* lowest priority */
        kbd_addHandler(&kbd_defHandler);
 
+#if CONFIG_KBD_OBSERVER
+       observer_InitSubject(&kbd_subject);
+#endif
+
+#if CONFIG_KBD_POLL == KBD_POLL_SOFTINT
+
+       MOD_CHECK(timer);
+
        /* Add kbd handler to soft timers list */
        event_initSoftInt(&kbd_timer.expire, kbd_softint, 0);
        timer_setDelay(&kbd_timer, ms_to_ticks(KBD_CHECK_INTERVAL));
        timer_add(&kbd_timer);
+
+#elif CONFIG_KBD_POLL == CONFIG_POLL_FREERTOS
+
+       /* Create a timer specific thread */
+       xTaskCreate(kbd_task, "kbd", CONFIG_STACK_KBD,
+                       NULL, CONFIG_PRI_KBD, NULL);
+
+#else
+       #error "Define keyboard poll method"
+#endif
+
+       MOD_INIT(kbd);
 }