kbd.c

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