#include #include #include #include #include #include #include struct { key_event_t keycache[96]; struct { key_event_t *list; int nb_evt_max; int idx; } queue; struct { tid_t id; volatile uint16_t counter; } timer; } keyinfo; //--- // kernel-level API //--- static int __sh7305_keycache_keyframe(volatile uint16_t *counter) { *counter += 1; return TIMER_CONTINUE; } /* sh7305_keycache_init() : initialise the keycache information */ void sh7305_keycache_init(void) { for (int i = 0; i < 96 ; ++i) { keyinfo.keycache[i].rep_type = KEYEV_REP_NONE; keyinfo.keycache[i].type = KEYEV_UP; keyinfo.keycache[i].key = i; } keyinfo.queue.idx = -1; keyinfo.queue.nb_evt_max = 16; keyinfo.queue.list = calloc( keyinfo.queue.nb_evt_max, sizeof(key_event_t) ); keyinfo.timer.id = sh7305_tmu_configure( 7215, TIMER_CALL( &__sh7305_keycache_keyframe, (uintptr_t)&keyinfo.timer.counter ) ); sh7305_tmu_start(keyinfo.timer.id); } /* sh7305_keycache_update() : involved by the interrupt handler Note : this function SHOULD be involved with atomic context !! */ void sh7305_keycache_update(key_t key, int status) { status = (status != 0) ? KEYEV_DOWN : KEYEV_UP; if (keyinfo.keycache[key].type == status) return; keyinfo.keycache[key].type = status; sh7305_keycache_event_push(&keyinfo.keycache[key]); } /* sh7305_keycache_quit() : quit the keycache */ void sh7305_keycache_quit(void) { sh7305_tmu_stop(keyinfo.timer.id); free(keyinfo.queue.list); keyinfo.queue.idx = -1; keyinfo.queue.nb_evt_max = 0; } //--- // driver-level API //--- /* sh7305_keycache_keydown() : return the key status */ int sh7305_keycache_keydown(key_t key) { int info; cpu_atomic_start(); info = keyinfo.keycache[key].type; cpu_atomic_end(); return info == KEYEV_DOWN; } /* sh7305_keycache_event_wait() : wait event or timeout != 0 */ void sh7305_keycache_event_wait(key_event_t *event, volatile int *timeout) { key_event_t e; do { sh7305_keycache_event_pop(&e); if (e.type != KEYEV_NONE) { event->time = e.time; event->type = e.type; event->key = e.key; return; } if (timeout != NULL && timeout != 0) { event->type = KEYEV_NONE; event->key = KEY_NONE; return; } __asm__("sleep"); } while (1); } /* sh7305_keycache_event_push() : push event on the keycache */ void sh7305_keycache_event_push(key_event_t *event) { cpu_atomic_start(); keyinfo.queue.idx += 1; if (keyinfo.queue.idx >= keyinfo.queue.nb_evt_max) { keyinfo.queue.nb_evt_max += keyinfo.queue.nb_evt_max; keyinfo.queue.list = reallocarray( keyinfo.queue.list, keyinfo.queue.nb_evt_max, sizeof(key_event_t) ); } keyinfo.queue.list[keyinfo.queue.idx].time = keyinfo.timer.counter; keyinfo.queue.list[keyinfo.queue.idx].type = event->type; keyinfo.queue.list[keyinfo.queue.idx].key = event->key; cpu_atomic_end(); } /* sh7305_keycache_event_push() : pop event from the keycache */ void sh7305_keycache_event_pop(key_event_t *event) { cpu_atomic_start(); if (keyinfo.queue.idx < 0) { event->key = KEY_NONE; event->type = KEYEV_NONE; } else { event->time = keyinfo.queue.list[0].time; event->type = keyinfo.queue.list[0].type; event->key = keyinfo.queue.list[0].key; memcpy( &keyinfo.queue.list[0], &keyinfo.queue.list[1], sizeof(key_event_t) * keyinfo.queue.idx ); keyinfo.queue.idx -= 1; } cpu_atomic_end(); }