kbd.c

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