de1c561c06bccf8af580360c77a1eedc3ec6a378
[bertos.git] / drv / kbd.c
1 /*!
2  * \file
3  * <!--
4  * Copyright 2003, 2004, 2005 Develer S.r.l. (http://www.develer.com/)
5  * Copyright 1999, 2003 Bernardo Innocenti
6  * This file is part of DevLib - See README.devlib for information.
7  * -->
8  *
9  * \version $Id$
10  *
11  * \author Bernardo Innocenti <bernie@develer.com>
12  * \author Stefano Fedrigo <aleph@develer.com>
13  * \author Francesco Sacchi <batt@develer.com>
14  *
15  * \brief Keyboard driver (implementation)
16  */
17
18 /*#*
19  *#* $Log$
20  *#* Revision 1.5  2006/02/27 22:39:45  bernie
21  *#* Misc build and doc fixes from project_grl.
22  *#*
23  *#* Revision 1.4  2006/02/24 00:27:14  bernie
24  *#* Use new naming convention for list macros.
25  *#*
26  *#* Revision 1.3  2006/02/17 21:15:42  bernie
27  *#* Add MOD_CHECK() checks.
28  *#*
29  *#* Revision 1.2  2006/02/10 12:36:20  bernie
30  *#* Add preliminary FreeRTOS support; Enforce CONFIG_* definitions.
31  *#*
32  *#* Revision 1.1  2005/06/27 21:28:45  bernie
33  *#* Import generic keyboard driver.
34  *#*
35  *#*/
36
37 #include <hw_kbd.h>
38
39 #include <drv/timer.h>
40 #include <drv/kbd.h>
41 #include <drv/buzzer.h>
42
43 #include <cfg/debug.h>
44 #include <cfg/module.h>
45 #include <appconfig.h>
46
47 /* Configuration sanity checks */
48 #if !defined(CONFIG_KBD_POLL) || (CONFIG_KBD_POLL != KBD_POLL_SOFTINT && CONFIG_KBD_POLL != CONFIG_POLL_FREERTOS)
49         #error CONFIG_KBD_POLL must be defined to either KBD_POLL_SOFTINT or CONFIG_POLL_FREERTOS
50 #endif
51
52
53 #define KBD_CHECK_INTERVAL  10  /*!< (ms) Timing for kbd softint */
54 #define KBD_DEBOUNCE_TIME   30  /*!< (ms) Debounce time */
55 #define KBD_BEEP_TIME        5  /*!< (ms) Duration of keybeep */
56
57 #define KBD_REPEAT_DELAY   400  /*!< (ms) Keyboard repeat delay for first character */
58 #define KBD_REPEAT_RATE    100  /*!< (ms) Initial interchar delay for keyboard repeat */
59 #define KBD_REPEAT_MAXRATE  20  /*!< (ms) Minimum delay for keyboard repeat */
60 #define KBD_REPEAT_ACCEL     5  /*!< (ms) Keyboard repeat speed increase */
61
62 #define KBD_LNG_DELAY     1000  /*!< (ms) Keyboard long pression keys delay */
63
64
65 /*! Status for keyboard repeat state machine */
66 static enum { KS_IDLE, KS_REPDELAY, KS_REPEAT } kbd_rptStatus;
67
68
69 static volatile keymask_t kbd_buf; /*!< Single entry keyboard buffer */
70 static volatile keymask_t kbd_cnt; /*!< Number of keypress events in \c kbd_buf */
71
72 #if CONFIG_KBD_POLL == KBD_POLL_SOFTINT
73 static Timer kbd_timer;            /*!< Keyboard softtimer */
74 #endif
75
76 static List kbd_rawHandlers;       /*!< Raw keyboard handlers */
77 static List kbd_handlers;          /*!< Cooked keyboard handlers */
78
79 static KbdHandler kbd_defHandler;  /*!< The default keyboard handler */
80 static KbdHandler kbd_debHandler;  /*!< The debounce keyboard handler */
81 static KbdHandler kbd_rptHandler;  /*!< Auto-repeat keyboard handler */
82 #ifdef  K_LNG_MASK
83 static KbdHandler kbd_lngHandler;  /*!< Long pression keys handler */
84 #endif
85
86
87
88 /*!
89  * Poll keyboard and dispatch keys to handlers.
90  *
91  * Read the key states and invoke all keyboard
92  * handlers to process the new state.
93  *
94  * Call this function periodically using a software
95  * timer, an interrupt or a process.
96  */
97 static void kbd_poll(void)
98 {
99         /*! Currently depressed key */
100         static keymask_t current_key;
101
102         struct KbdHandler *handler;
103         keymask_t key = kbd_readkeys();
104
105         /* Call raw input handlers */
106         FOREACH_NODE(handler, &kbd_rawHandlers)
107                 key = handler->hook(key);
108
109         /* If this key was not previously pressed */
110         if (key != current_key)
111         {
112                 /* Remember last key */
113                 current_key = key;
114
115                 /* Call cooked input handlers */
116                 FOREACH_NODE(handler, &kbd_handlers)
117                         key = handler->hook(key);
118         }
119 }
120
121 #if CONFIG_KBD_POLL == KBD_POLL_SOFTINT
122
123 /*!
124  * Keyboard soft-irq handler.
125  */
126 static void kbd_softint(UNUSED_ARG(iptr_t, arg))
127 {
128         kbd_poll();
129         timer_add(&kbd_timer);
130 }
131
132 #elif CONFIG_KBD_POLL == CONFIG_POLL_FREERTOS
133
134 #include "FreeRTOS.h"
135 #include "task.h"
136
137 static portTASK_FUNCTION(kbd_task, arg)
138 {
139         for (;;)
140         {
141                 kbd_poll();
142                 timer_delay(KBD_CHECK_INTERVAL);
143         }
144 }
145
146 #endif /* CONFIG_KBD_POLL */
147
148 /*!
149  * \brief Read a key from the keyboard buffer.
150  *
151  * When a key is kept depressed between calls of this function a value
152  * is returned only after the time specified with KBD_REPAT_DELAY to
153  * avoid too fast keyboard repeat.
154  *
155  * \note This function is \b not interrupt safe!
156  *
157  * \return The mask of depressed keys or 0 if no keys are depressed.
158  *
159  */
160 keymask_t kbd_peek(void)
161 {
162         keymask_t key = 0;
163
164 // FIXME
165         extern void schedule(void);
166         schedule();
167
168         /* Extract an event from the keyboard buffer */
169         IRQ_DISABLE;
170         if (kbd_cnt)
171         {
172                 --kbd_cnt;
173                 key = kbd_buf;
174         }
175         IRQ_ENABLE;
176
177         return key;
178 }
179
180
181 /*!
182  * Wait for a keypress and return the mask of depressed keys.
183  *
184  * \note This function is \b not interrupt safe!
185  */
186 keymask_t kbd_get(void)
187 {
188         keymask_t key;
189
190         while (!(key = kbd_peek())) {}
191         return key;
192 }
193
194
195 /*!
196  * Wait up to \c timeout ms for a keypress
197  * and return the mask of depressed keys, or K_TIMEOUT
198  * if the timeout was reacked.
199  */
200 keymask_t kbd_get_timeout(mtime_t timeout)
201 {
202         keymask_t key;
203
204         ticks_t start = timer_clock();
205         ticks_t stop  = ms_to_ticks(timeout);
206         do
207         {
208                 if ((key = kbd_peek()))
209                         return key;
210         }
211         while (timer_clock() - start < stop);
212
213         return K_TIMEOUT;
214 }
215
216
217 void kbd_addHandler(struct KbdHandler *handler)
218 {
219         KbdHandler *node;
220         List *list;
221
222         cpuflags_t flags;
223         IRQ_SAVE_DISABLE(flags);
224
225         /* Choose between raw and coocked handlers list */
226         list = (handler->flags & KHF_RAWKEYS) ?
227                 &kbd_rawHandlers : &kbd_handlers;
228
229         /*
230          * Search for the first node whose priority
231          * is lower than the timer we want to add.
232          */
233         FOREACH_NODE(node,list)
234                 if (node->pri < handler->pri)
235                         break;
236
237         /* Enqueue handler in the handlers chain */
238         INSERT_BEFORE(&handler->link, &node->link);
239
240         IRQ_RESTORE(flags);
241 }
242
243
244 void kbd_remHandler(struct KbdHandler *handler)
245 {
246         /* Remove the handler */
247         ATOMIC(REMOVE(&handler->link));
248 }
249
250
251 /*!
252  * This is the default key handler, called after
253  * all other handlers have had their chance to
254  * do their special processing. This handler
255  * pushes all input in the keyboard FIFO buffer.
256  */
257 static keymask_t kbd_defHandlerFunc(keymask_t key)
258 {
259         if (key)
260         {
261                 /* Force a single event in kbd buffer */
262                 kbd_buf = key;
263                 kbd_cnt = 1;
264
265 #if KBD_BEEP_TIME
266                 if (!(key & K_REPEAT))
267                         buz_beep(KBD_BEEP_TIME);
268 #endif
269         }
270
271         /* Eat all input */
272         return 0;
273 }
274
275 /*!
276  * Handle keyboard debounce
277  */
278 static keymask_t kbd_debHandlerFunc(keymask_t key)
279 {
280         /*! Buffer for debounce */
281         static keymask_t debounce_key;
282
283         /*! Timer for keyboard debounce */
284         static ticks_t debounce_time;
285
286         /*! Key aquired after debounce */
287         static keymask_t new_key;
288
289
290         ticks_t now = timer_clock();
291
292         if (key != debounce_key)
293         {
294                 /* Reset debounce timer */
295                 debounce_key = key;
296                 debounce_time = now;
297         }
298         else if ((new_key != debounce_key)
299                 && (now - debounce_time > ms_to_ticks(KBD_DEBOUNCE_TIME)))
300         {
301                 new_key = debounce_key;
302                 debounce_time = now;
303         }
304
305         return new_key;
306 }
307
308 #ifdef  K_LNG_MASK
309 /*!
310  * Handle long pression keys
311  */
312 static keymask_t kbd_lngHandlerFunc(keymask_t key)
313 {
314         static ticks_t stop;
315         ticks_t now = timer_clock();
316
317         if (key & K_LNG_MASK)
318         {
319                 if (now - stop > 0)
320                         key &= K_LNG_MASK;
321                 else
322                         key &= ~K_LNG_MASK;
323         }
324         else
325                 stop = now + ms_to_ticks(KBD_LNG_DELAY);
326         return key;
327 }
328 #endif
329
330 /*!
331  * Handle keyboard repeat
332  */
333 static keymask_t kbd_rptHandlerFunc(keymask_t key)
334 {
335         /*! Timer for keyboard repeat events */
336         static ticks_t repeat_time;
337
338         static ticks_t repeat_rate; /*! Current repeat rate (for acceleration) */
339
340         ticks_t now = timer_clock();
341
342         switch (kbd_rptStatus)
343         {
344                 case KS_IDLE:
345                         if (key & K_RPT_MASK)
346                         {
347                                 repeat_time = now;
348                                 kbd_rptStatus = KS_REPDELAY;
349                         }
350                         break;
351
352                 case KS_REPDELAY:
353                         if (key & K_RPT_MASK)
354                         {
355                                 if (now - repeat_time > ms_to_ticks(KBD_REPEAT_DELAY))
356                                 {
357                                         key = (key & K_RPT_MASK) | K_REPEAT;
358                                         repeat_time = now;
359                                         repeat_rate = ms_to_ticks(KBD_REPEAT_RATE);
360                                         kbd_rptStatus = KS_REPEAT;
361                                 }
362                                 else
363                                         key = 0;
364                         }
365                         else
366                                 kbd_rptStatus = KS_IDLE;
367                         break;
368
369                 case KS_REPEAT:
370                         if (key & K_RPT_MASK)
371                         {
372                                 if (now - repeat_time > repeat_rate)
373                                 {
374                                         /* Enqueue a new event in the buffer */
375                                         key = (key & K_RPT_MASK) | K_REPEAT;
376                                         repeat_time = now;
377
378                                         /* Repeat rate acceleration */
379                                         if (repeat_rate > ms_to_ticks(KBD_REPEAT_MAXRATE))
380                                                 repeat_rate -= ms_to_ticks(KBD_REPEAT_ACCEL);
381                                 }
382                                 else
383                                         key = 0;
384                         }
385                         else
386                                 kbd_rptStatus = KS_IDLE;
387
388                         break;
389         }
390
391         return key;
392 }
393
394
395 MOD_DEFINE(kbd)
396
397 /*!
398  * Initialize keyboard ports and softtimer
399  */
400 void kbd_init(void)
401 {
402         MOD_CHECK(buzzer);
403
404         KBD_HW_INIT;
405
406         /* Init handlers lists */
407         LIST_INIT(&kbd_handlers);
408         LIST_INIT(&kbd_rawHandlers);
409
410         /* Add debounce keyboard handler */
411         kbd_debHandler.hook = kbd_debHandlerFunc;
412         kbd_debHandler.pri = 100; /* high priority */
413         kbd_debHandler.flags = KHF_RAWKEYS;
414         kbd_addHandler(&kbd_debHandler);
415
416         #ifdef  K_LNG_MASK
417         /* Add long pression keyboard handler */
418         kbd_lngHandler.hook = kbd_lngHandlerFunc;
419         kbd_lngHandler.pri = 90; /* high priority */
420         kbd_lngHandler.flags = KHF_RAWKEYS;
421         kbd_addHandler(&kbd_lngHandler);
422         #endif
423
424         /* Add repeat keyboard handler */
425         kbd_rptHandler.hook = kbd_rptHandlerFunc;
426         kbd_rptHandler.pri = 80; /* high priority */
427         kbd_rptHandler.flags = KHF_RAWKEYS;
428         kbd_addHandler(&kbd_rptHandler);
429
430         /* Add default keyboard handler */
431         kbd_defHandler.hook = kbd_defHandlerFunc;
432         kbd_defHandler.pri = -128; /* lowest priority */
433         kbd_addHandler(&kbd_defHandler);
434
435 #if CONFIG_KBD_POLL == KBD_POLL_SOFTINT
436
437         MOD_CHECK(timer);
438
439         /* Add kbd handler to soft timers list */
440         event_initSoftInt(&kbd_timer.expire, kbd_softint, 0);
441         timer_setDelay(&kbd_timer, ms_to_ticks(KBD_CHECK_INTERVAL));
442         timer_add(&kbd_timer);
443
444 #elif CONFIG_KBD_POLL == CONFIG_POLL_FREERTOS
445
446         /* Create a timer specific thread */
447         xTaskCreate(kbd_task, "kbd", CONFIG_STACK_KBD,
448                         NULL, CONFIG_PRI_KBD, NULL);
449
450 #else
451         #error "Define keyboard poll method"
452 #endif
453
454         MOD_INIT(kbd);
455 }