#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <armv7/exynos/crosecvar.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wskbdvar.h>
#include <dev/wscons/wsksymdef.h>
#include <dev/wscons/wsksymvar.h>
void cros_ec_add_task(void *);
void cros_ec_poll_keystate(void *);
int cros_ec_get_keystate(struct cros_ec_softc *);
int cros_ec_keyboard_enable(void *, int);
void cros_ec_keyboard_set_leds(void *, int);
int cros_ec_keyboard_ioctl(void *, u_long, caddr_t, int, struct proc *);
struct wskbd_accessops cros_ec_keyboard_accessops = {
cros_ec_keyboard_enable,
cros_ec_keyboard_set_leds,
cros_ec_keyboard_ioctl,
};
void cros_ec_keyboard_cngetc(void *, u_int *, int *);
void cros_ec_keyboard_cnpollc(void *, int);
struct wskbd_consops cros_ec_keyboard_consops = {
cros_ec_keyboard_cngetc,
cros_ec_keyboard_cnpollc,
};
#define KC(n) KS_KEYCODE(n)
static const keysym_t cros_ec_keyboard_keydesc_us[] = {
KC(1), KS_Caps_Lock,
KC(3), KS_b,
KC(6), KS_n,
KC(10), KS_Alt_R,
KC(14), KS_Escape,
KC(16), KS_g,
KC(19), KS_h,
KC(24), KS_BackSpace,
KC(26), KS_Control_L,
KC(27), KS_Tab, KS_Backtab,
KC(29), KS_t,
KC(32), KS_y,
KC(42), KS_5,
KC(45), KS_6,
KC(52), KS_Control_R,
KC(53), KS_a,
KC(54), KS_d,
KC(55), KS_f,
KC(56), KS_s,
KC(57), KS_k,
KC(58), KS_j,
KC(61), KS_l,
KC(63), KS_Return,
KC(66), KS_z,
KC(67), KS_c,
KC(68), KS_v,
KC(69), KS_x,
KC(70), KS_comma,
KC(71), KS_m,
KC(72), KS_Shift_L,
KC(74), KS_period,
KC(76), KS_space,
KC(79), KS_1,
KC(80), KS_3,
KC(81), KS_4,
KC(82), KS_2,
KC(83), KS_8,
KC(84), KS_7,
KC(86), KS_0,
KC(87), KS_9,
KC(88), KS_Alt_L,
KC(92), KS_q,
KC(93), KS_e,
KC(94), KS_r,
KC(95), KS_w,
KC(96), KS_i,
KC(97), KS_u,
KC(98), KS_Shift_R,
KC(99), KS_p,
KC(100), KS_o,
};
#define KBD_MAP(name, base, map) \
{ name, base, sizeof(map)/sizeof(keysym_t), map }
static const struct wscons_keydesc cros_ec_keyboard_keydesctab[] = {
KBD_MAP(KB_US, 0, cros_ec_keyboard_keydesc_us),
{0, 0, 0, 0}
};
struct wskbd_mapdata cros_ec_keyboard_keymapdata = {
cros_ec_keyboard_keydesctab,
KB_US,
};
int
cros_ec_init_keyboard(struct cros_ec_softc *sc)
{
struct ec_response_cros_ec_info info;
struct wskbddev_attach_args a;
if (cros_ec_info(sc, &info)) {
printf("%s: could not read KBC info\n", __func__);
return (-1);
}
sc->keyboard.rows = info.rows;
sc->keyboard.cols = info.cols;
sc->keyboard.switches = info.switches;
sc->keyboard.state = (uint8_t *)malloc(info.rows*info.cols,
M_DEVBUF, M_WAITOK|M_ZERO);
if (sc->keyboard.state == NULL)
panic("%s: no memory available for keyboard states", __func__);
sc->keyboard.taskq = taskq_create("crosec-keyb", 1, IPL_TTY, 0);
task_set(&sc->keyboard.task, cros_ec_poll_keystate, sc);
timeout_set(&sc->keyboard.timeout, cros_ec_add_task, sc);
wskbd_cnattach(&cros_ec_keyboard_consops, sc, &cros_ec_keyboard_keymapdata);
a.console = 1;
a.keymap = &cros_ec_keyboard_keymapdata;
a.accessops = &cros_ec_keyboard_accessops;
a.accesscookie = sc;
sc->keyboard.wskbddev = config_found((void *)sc, &a, wskbddevprint);
timeout_add_sec(&sc->keyboard.timeout, 10);
return 0;
}
void
cros_ec_add_task(void *arg)
{
struct cros_ec_softc *sc = (struct cros_ec_softc *)arg;
task_add(sc->keyboard.taskq, &sc->keyboard.task);
timeout_add_msec(&sc->keyboard.timeout, 100);
}
void
cros_ec_poll_keystate(void *arg)
{
struct cros_ec_softc *sc = (struct cros_ec_softc *)arg;
cros_ec_get_keystate(sc);
}
int
cros_ec_get_keystate(struct cros_ec_softc *sc)
{
int col, row, s;
uint8_t state[sc->keyboard.cols];
cros_ec_scan_keyboard(sc, state, sc->keyboard.cols);
for (col = 0; col < sc->keyboard.cols; col++) {
for (row = 0; row < sc->keyboard.rows; row++) {
int off = row*sc->keyboard.cols;
int pressed = !!(state[col] & (1 << row));
if (pressed && !sc->keyboard.state[off+col]) {
sc->keyboard.state[off+col] = 1;
if (sc->keyboard.polling)
return off+col;
s = spltty();
wskbd_input(sc->keyboard.wskbddev, WSCONS_EVENT_KEY_DOWN, off+col);
splx(s);
} else if (!pressed && sc->keyboard.state[off+col]) {
sc->keyboard.state[off+col] = 0;
if (sc->keyboard.polling)
return off+col;
s = spltty();
wskbd_input(sc->keyboard.wskbddev, WSCONS_EVENT_KEY_UP, off+col);
splx(s);
} else if (sc->keyboard.state[off+col]) {
}
}
}
return (-1);
}
void
cros_ec_keyboard_cngetc(void *v, u_int *type, int *data)
{
struct cros_ec_softc *sc = v;
int key;
sc->keyboard.polling = 1;
while ((key = cros_ec_get_keystate(sc)) == -1) {
delay(10000);
}
sc->keyboard.polling = 0;
*data = key;
*type = sc->keyboard.state[key] ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN;
}
void
cros_ec_keyboard_cnpollc(void *v, int on)
{
}
int
cros_ec_keyboard_enable(void *v, int on)
{
return 0;
}
void
cros_ec_keyboard_set_leds(void *v, int on)
{
}
int
cros_ec_keyboard_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
#ifdef WSDISPLAY_COMPAT_RAWKBD
struct cros_ec_softc *sc = v;
#endif
switch (cmd) {
case WSKBDIO_GTYPE:
*(int *)data = WSKBD_TYPE_ZAURUS;
return 0;
case WSKBDIO_SETLEDS:
return 0;
case WSKBDIO_GETLEDS:
*(int *)data = 0;
return 0;
#ifdef WSDISPLAY_COMPAT_RAWKBD
case WSKBDIO_SETMODE:
sc->keyboard.rawkbd = *(int *)data == WSKBD_RAW;
return (0);
#endif
}
return -1;
}