vxKernel/src/driver/mpu/sh/sh7305/keysc/keycache.c

168 lines
3.5 KiB
C

#include <vhex/driver/mpu/sh/sh7305/keysc.h>
#include <vhex/driver/mpu/sh/sh7305/tmu.h>
#include <vhex/keyboard.h>
#include <vhex/timer.h>
#include <vhex/driver/cpu.h>
#include <stdlib.h>
#include <string.h>
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();
}