kbd.c

Go to the documentation of this file.
00001 
00043 #include <hw_kbd.h>
00044 
00045 #include <drv/timer.h>
00046 #include <drv/kbd.h>
00047 
00048 #include <cfg/debug.h>
00049 #include <cfg/module.h>
00050 #include <appconfig.h>
00051 
00052 /* Configuration sanity checks */
00053 #if !defined(CONFIG_KBD_POLL) || (CONFIG_KBD_POLL != KBD_POLL_SOFTINT && CONFIG_KBD_POLL != CONFIG_POLL_FREERTOS)
00054     #error CONFIG_KBD_POLL must be defined to either KBD_POLL_SOFTINT or CONFIG_POLL_FREERTOS
00055 #endif
00056 #if !defined(CONFIG_KBD_BEEP) || (CONFIG_KBD_BEEP != 0 && CONFIG_KBD_BEEP != 1)
00057     #error CONFIG_KBD_BEEP must be defined to either 0 or 1
00058 #endif
00059 #if !defined(CONFIG_KBD_OBSERVER) || (CONFIG_KBD_OBSERVER != 0 && CONFIG_KBD_OBSERVER != 1)
00060     #error CONFIG_KBD_OBSERVER must be defined to either 0 or 1
00061 #endif
00062 #if !defined(CONFIG_KBD_LONGPRESS) || (CONFIG_KBD_LONGPRESS != 0 && CONFIG_KBD_LONGPRESS != 1)
00063     #error CONFIG_KBD_LONGPRESS must be defined to either 0 or 1
00064 #endif
00065 
00066 #if CONFIG_KBD_BEEP
00067     #include <drv/buzzer.h>
00068 #endif
00069 
00070 #define KBD_CHECK_INTERVAL  10  
00071 #define KBD_DEBOUNCE_TIME   30  
00072 #define KBD_BEEP_TIME        5  
00074 #define KBD_REPEAT_DELAY   400  
00075 #define KBD_REPEAT_RATE    100  
00076 #define KBD_REPEAT_MAXRATE  20  
00077 #define KBD_REPEAT_ACCEL     5  
00079 #define KBD_LNG_DELAY     1000  
00083 static enum { KS_IDLE, KS_REPDELAY, KS_REPEAT } kbd_rptStatus;
00084 
00085 
00086 static volatile keymask_t kbd_buf; 
00087 static volatile keymask_t kbd_cnt; 
00088 static keymask_t kbd_rpt_mask;     
00090 #if CONFIG_KBD_POLL == KBD_POLL_SOFTINT
00091 static Timer kbd_timer;            
00092 #endif
00093 
00094 static List kbd_rawHandlers;       
00095 static List kbd_handlers;          
00097 static KbdHandler kbd_defHandler;  
00098 static KbdHandler kbd_debHandler;  
00099 static KbdHandler kbd_rptHandler;  
00101 #if CONFIG_KBD_LONGPRESS
00102 static KbdHandler kbd_lngHandler;  
00103 #endif
00104 
00105 #if CONFIG_KBD_OBSERVER
00106     #include <mware/observer.h>
00107     Subject kbd_subject;
00108 #endif
00109 
00110 
00120 static void kbd_poll(void)
00121 {
00123     static keymask_t current_key;
00124 
00125     struct KbdHandler *handler;
00126     keymask_t key = kbd_readkeys();
00127 
00128     /* Call raw input handlers */
00129     FOREACH_NODE(handler, &kbd_rawHandlers)
00130         key = handler->hook(key);
00131 
00132     /* If this key was not previously pressed */
00133     if (key != current_key)
00134     {
00135         /* Remember last key */
00136         current_key = key;
00137 
00138         /* Call cooked input handlers */
00139         FOREACH_NODE(handler, &kbd_handlers)
00140             key = handler->hook(key);
00141     }
00142 }
00143 
00144 #if CONFIG_KBD_POLL == KBD_POLL_SOFTINT
00145 
00149 static void kbd_softint(UNUSED_ARG(iptr_t, arg))
00150 {
00151     kbd_poll();
00152     timer_add(&kbd_timer);
00153 }
00154 
00155 #elif CONFIG_KBD_POLL == CONFIG_POLL_FREERTOS
00156 
00157 #include "FreeRTOS.h"
00158 #include "task.h"
00159 
00160 static portTASK_FUNCTION(kbd_task, arg)
00161 {
00162     for (;;)
00163     {
00164         kbd_poll();
00165         timer_delay(KBD_CHECK_INTERVAL);
00166     }
00167 }
00168 
00169 #endif /* CONFIG_KBD_POLL */
00170 
00185 keymask_t kbd_peek(void)
00186 {
00187     keymask_t key = 0;
00188 
00189 // FIXME: make it optional
00190     /* Let other tasks run for a while */
00191     extern void schedule(void);
00192     schedule();
00193 
00194     /* Extract an event from the keyboard buffer */
00195     IRQ_DISABLE;
00196     if (kbd_cnt)
00197     {
00198         --kbd_cnt;
00199         key = kbd_buf;
00200     }
00201     IRQ_ENABLE;
00202 
00203     return key;
00204 }
00205 
00211 keymask_t kbd_get(void)
00212 {
00213     keymask_t key;
00214 
00215     while (!(key = kbd_peek())) {}
00216 
00217     return key;
00218 }
00219 
00220 
00226 keymask_t kbd_get_timeout(mtime_t timeout)
00227 {
00228     keymask_t key;
00229 
00230     ticks_t start = timer_clock();
00231     ticks_t stop  = ms_to_ticks(timeout);
00232     do
00233     {
00234         if ((key = kbd_peek()))
00235             return key;
00236     }
00237     while (timer_clock() - start < stop);
00238 
00239     return K_TIMEOUT;
00240 }
00241 
00242 
00243 void kbd_addHandler(struct KbdHandler *handler)
00244 {
00245     KbdHandler *node;
00246     List *list;
00247 
00248     cpuflags_t flags;
00249     IRQ_SAVE_DISABLE(flags);
00250 
00251     /* Choose between raw and coocked handlers list */
00252     list = (handler->flags & KHF_RAWKEYS) ?
00253         &kbd_rawHandlers : &kbd_handlers;
00254 
00255     /*
00256      * Search for the first node whose priority
00257      * is lower than the timer we want to add.
00258      */
00259     FOREACH_NODE(node,list)
00260         if (node->pri < handler->pri)
00261             break;
00262 
00263     /* Enqueue handler in the handlers chain */
00264     INSERT_BEFORE(&handler->link, &node->link);
00265 
00266     IRQ_RESTORE(flags);
00267 }
00268 
00269 
00270 void kbd_remHandler(struct KbdHandler *handler)
00271 {
00272     /* Remove the handler */
00273     ATOMIC(REMOVE(&handler->link));
00274 }
00275 
00276 
00283 static keymask_t kbd_defHandlerFunc(keymask_t key)
00284 {
00285     if (key)
00286     {
00287         /* Force a single event in kbd buffer */
00288         kbd_buf = key;
00289         kbd_cnt = 1;
00290 
00291         #if CONFIG_KBD_OBSERVER
00292             observer_notify(&kbd_subject, KBD_EVENT_KEY, &key);
00293         #endif
00294 
00295         #if CONFIG_KBD_BEEP
00296             if (!(key & K_REPEAT))
00297                 buz_beep(KBD_BEEP_TIME);
00298         #endif
00299     }
00300 
00301     /* Eat all input */
00302     return 0;
00303 }
00304 
00308 static keymask_t kbd_debHandlerFunc(keymask_t key)
00309 {
00311     static keymask_t debounce_key;
00312 
00314     static ticks_t debounce_time;
00315 
00317     static keymask_t new_key;
00318 
00319 
00320     ticks_t now = timer_clock();
00321 
00322     if (key != debounce_key)
00323     {
00324         /* Reset debounce timer */
00325         debounce_key = key;
00326         debounce_time = now;
00327     }
00328     else if ((new_key != debounce_key)
00329         && (now - debounce_time > ms_to_ticks(KBD_DEBOUNCE_TIME)))
00330     {
00331         new_key = debounce_key;
00332         debounce_time = now;
00333     }
00334 
00335     return new_key;
00336 }
00337 
00338 #if CONFIG_KBD_LONGPRESS
00339 
00342 static keymask_t kbd_lngHandlerFunc(keymask_t key)
00343 {
00344     static ticks_t start;
00345     ticks_t now = timer_clock();
00346 
00347     if (key & K_LNG_MASK)
00348     {
00349         if (now - start > ms_to_ticks(KBD_LNG_DELAY))
00350             key |= K_LONG;
00351     }
00352     else
00353         start = now;
00354     return key;
00355 }
00356 #endif
00357 
00361 keymask_t kbd_setRepeatMask(keymask_t mask)
00362 {
00363     keymask_t oldmask = kbd_rpt_mask;
00364     ATOMIC(kbd_rpt_mask = mask);
00365     return oldmask;
00366 }
00367 
00371 static keymask_t kbd_rptHandlerFunc(keymask_t key)
00372 {
00373     /* Timer for keyboard repeat events. */
00374     static ticks_t repeat_time;
00375 
00376     /* Current repeat rate (for acceleration). */
00377     static ticks_t repeat_rate; 
00379     ticks_t now = timer_clock();
00380 
00381     switch (kbd_rptStatus)
00382     {
00383         case KS_IDLE:
00384             if (key & kbd_rpt_mask)
00385             {
00386                 repeat_time = now;
00387                 kbd_rptStatus = KS_REPDELAY;
00388             }
00389             break;
00390 
00391         case KS_REPDELAY:
00392             if (key & kbd_rpt_mask)
00393             {
00394                 if (now - repeat_time > ms_to_ticks(KBD_REPEAT_DELAY))
00395                 {
00396                     key = (key & kbd_rpt_mask) | K_REPEAT;
00397                     repeat_time = now;
00398                     repeat_rate = ms_to_ticks(KBD_REPEAT_RATE);
00399                     kbd_rptStatus = KS_REPEAT;
00400                 }
00401                 else
00402                     key = 0;
00403             }
00404             else
00405                 kbd_rptStatus = KS_IDLE;
00406             break;
00407 
00408         case KS_REPEAT:
00409             if (key & kbd_rpt_mask)
00410             {
00411                 if (now - repeat_time > repeat_rate)
00412                 {
00413                     /* Enqueue a new event in the buffer */
00414                     key = (key & kbd_rpt_mask) | K_REPEAT;
00415                     repeat_time = now;
00416 
00417                     /* Repeat rate acceleration */
00418                     if (repeat_rate > ms_to_ticks(KBD_REPEAT_MAXRATE))
00419                         repeat_rate -= ms_to_ticks(KBD_REPEAT_ACCEL);
00420                 }
00421                 else
00422                     key = 0;
00423             }
00424             else
00425                 kbd_rptStatus = KS_IDLE;
00426 
00427             break;
00428     }
00429 
00430     return key;
00431 }
00432 
00433 
00434 MOD_DEFINE(kbd)
00435 
00436 
00439 void kbd_init(void)
00440 {
00441 #if CONFIG_KBD_BEEP
00442     MOD_CHECK(buzzer);
00443 #endif
00444 
00445     KBD_HW_INIT;
00446 
00447     /* Init handlers lists */
00448     LIST_INIT(&kbd_handlers);
00449     LIST_INIT(&kbd_rawHandlers);
00450 
00451     /* Add debounce keyboard handler */
00452     kbd_debHandler.hook = kbd_debHandlerFunc;
00453     kbd_debHandler.pri = 100; /* high priority */
00454     kbd_debHandler.flags = KHF_RAWKEYS;
00455     kbd_addHandler(&kbd_debHandler);
00456 
00457     #if CONFIG_KBD_LONGPRESS
00458     /* Add long pression keyboard handler */
00459     kbd_lngHandler.hook = kbd_lngHandlerFunc;
00460     kbd_lngHandler.pri = 90; /* high priority */
00461     kbd_lngHandler.flags = KHF_RAWKEYS;
00462     kbd_addHandler(&kbd_lngHandler);
00463     #endif
00464 
00465     /* Add repeat keyboard handler */
00466     kbd_rptHandler.hook = kbd_rptHandlerFunc;
00467     kbd_rptHandler.pri = 80; /* high priority */
00468     kbd_rptHandler.flags = KHF_RAWKEYS;
00469     kbd_addHandler(&kbd_rptHandler);
00470 
00471     /* Add default keyboard handler */
00472     kbd_defHandler.hook = kbd_defHandlerFunc;
00473     kbd_defHandler.pri = -128; /* lowest priority */
00474     kbd_addHandler(&kbd_defHandler);
00475 
00476 #if CONFIG_KBD_OBSERVER
00477     observer_InitSubject(&kbd_subject);
00478 #endif
00479 
00480 #if CONFIG_KBD_POLL == KBD_POLL_SOFTINT
00481 
00482     MOD_CHECK(timer);
00483 
00484     /* Add kbd handler to soft timers list */
00485     event_initSoftInt(&kbd_timer.expire, kbd_softint, NULL);
00486     timer_setDelay(&kbd_timer, ms_to_ticks(KBD_CHECK_INTERVAL));
00487     timer_add(&kbd_timer);
00488 
00489 #elif CONFIG_KBD_POLL == CONFIG_POLL_FREERTOS
00490 
00491     /* Create a timer specific thread */
00492     xTaskCreate(kbd_task, "kbd", CONFIG_STACK_KBD,
00493             NULL, CONFIG_PRI_KBD, NULL);
00494 
00495 #else
00496     #error "Define keyboard poll method"
00497 #endif
00498 
00499     MOD_INIT(kbd);
00500 }