#include "opt_kbd.h"
#include "opt_evdev.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/caps.h>
#include <sys/conf.h>
#include <sys/proc.h>
#include <sys/tty.h>
#include <sys/event.h>
#include <sys/vnode.h>
#include <sys/uio.h>
#include <sys/thread.h>
#include <machine/console.h>
#include "kbdreg.h"
#ifdef EVDEV_SUPPORT
#include <dev/misc/evdev/evdev.h>
#include <dev/misc/evdev/input.h>
#endif
#if 0
#define lwkt_gettoken(x)
#define lwkt_reltoken(x)
#endif
#define KBD_INDEX(dev) minor(dev)
#define KB_QSIZE 512
#define KB_BUFSIZE 64
struct genkbd_softc {
int gkb_flags;
#define KB_ASLEEP (1 << 0)
struct kqinfo gkb_rkq;
char gkb_q[KB_QSIZE];
unsigned int gkb_q_start;
unsigned int gkb_q_length;
};
typedef struct genkbd_softc *genkbd_softc_t;
static SLIST_HEAD(, keyboard_driver) keyboard_drivers =
SLIST_HEAD_INITIALIZER(keyboard_drivers);
SET_DECLARE(kbddriver_set, const keyboard_driver_t);
static keyboard_t *keyboard[KBD_MAXKEYBOARDS];
keyboard_switch_t *kbdsw[KBD_MAXKEYBOARDS];
void
kbd_reinit_struct(keyboard_t *kbd, int config, int pref)
{
lwkt_gettoken(&kbd_token);
kbd->kb_flags |= KB_NO_DEVICE;
kbd->kb_config = config & ~KB_CONF_PROBE_ONLY;
kbd->kb_led = 0;
kbd->kb_data = NULL;
kbd->kb_keymap = NULL;
kbd->kb_accentmap = NULL;
kbd->kb_fkeytab = NULL;
kbd->kb_fkeytab_size = 0;
kbd->kb_delay1 = KB_DELAY1;
kbd->kb_delay2 = KB_DELAY2;
kbd->kb_count = 0;
kbd->kb_pref = pref;
bzero(kbd->kb_lastact, sizeof(kbd->kb_lastact));
lwkt_reltoken(&kbd_token);
}
void
kbd_init_struct(keyboard_t *kbd, char *name, int type, int unit, int config,
int pref, int port, int port_size)
{
lwkt_gettoken(&kbd_token);
kbd->kb_flags = 0;
kbd->kb_name = name;
kbd->kb_type = type;
kbd->kb_unit = unit;
kbd->kb_io_base = port;
kbd->kb_io_size = port_size;
kbd_reinit_struct(kbd, config, pref);
lockinit(&kbd->kb_lock, name, 0, LK_CANRECURSE);
lwkt_reltoken(&kbd_token);
}
void
kbd_set_maps(keyboard_t *kbd, keymap_t *keymap, accentmap_t *accmap,
fkeytab_t *fkeymap, int fkeymap_size)
{
lwkt_gettoken(&kbd_token);
kbd->kb_keymap = keymap;
kbd->kb_accentmap = accmap;
kbd->kb_fkeytab = fkeymap;
kbd->kb_fkeytab_size = fkeymap_size;
lwkt_reltoken(&kbd_token);
}
int
kbd_add_driver(keyboard_driver_t *driver)
{
lwkt_gettoken(&kbd_token);
if (SLIST_NEXT(driver, link)) {
lwkt_reltoken(&kbd_token);
return EINVAL;
}
SLIST_INSERT_HEAD(&keyboard_drivers, driver, link);
lwkt_reltoken(&kbd_token);
return 0;
}
int
kbd_delete_driver(keyboard_driver_t *driver)
{
lwkt_gettoken(&kbd_token);
SLIST_REMOVE(&keyboard_drivers, driver, keyboard_driver, link);
SLIST_NEXT(driver, link) = NULL;
lwkt_reltoken(&kbd_token);
return 0;
}
int
kbd_register(keyboard_t *kbd)
{
const keyboard_driver_t **list;
const keyboard_driver_t *p;
keyboard_t *mux;
keyboard_info_t ki;
int index;
lwkt_gettoken(&kbd_token);
mux = kbd_get_keyboard(kbd_find_keyboard("kbdmux", -1));
for (index = 0; index < KBD_MAXKEYBOARDS; ++index) {
if (keyboard[index] == NULL)
break;
}
if (index >= KBD_MAXKEYBOARDS) {
lwkt_reltoken(&kbd_token);
return -1;
}
kbd->kb_index = index;
KBD_UNBUSY(kbd);
KBD_VALID(kbd);
kbd->kb_active = 0;
kbd->kb_token = NULL;
kbd->kb_callback.kc_func = NULL;
kbd->kb_callback.kc_arg = NULL;
callout_init_mp(&kbd->kb_atkbd_timeout_ch);
SLIST_FOREACH(p, &keyboard_drivers, link) {
if (strcmp(p->name, kbd->kb_name) == 0) {
keyboard[index] = kbd;
kbdsw[index] = p->kbdsw;
if (mux != NULL) {
bzero(&ki, sizeof(ki));
strcpy(ki.kb_name, kbd->kb_name);
ki.kb_unit = kbd->kb_unit;
kbd_ioctl(mux, KBADDKBD, (caddr_t) &ki);
}
lwkt_reltoken(&kbd_token);
return index;
}
}
SET_FOREACH(list, kbddriver_set) {
p = *list;
if (strcmp(p->name, kbd->kb_name) == 0) {
keyboard[index] = kbd;
kbdsw[index] = p->kbdsw;
if (mux != NULL) {
bzero(&ki, sizeof(ki));
strcpy(ki.kb_name, kbd->kb_name);
ki.kb_unit = kbd->kb_unit;
kbd_ioctl(mux, KBADDKBD, (caddr_t) &ki);
}
lwkt_reltoken(&kbd_token);
return index;
}
}
lwkt_reltoken(&kbd_token);
return -1;
}
int
kbd_unregister(keyboard_t *kbd)
{
int error;
KBD_LOCK_ASSERT(kbd);
lwkt_gettoken(&kbd_token);
if ((kbd->kb_index < 0) || (kbd->kb_index >= KBD_MAXKEYBOARDS)) {
lwkt_reltoken(&kbd_token);
return ENOENT;
}
if (keyboard[kbd->kb_index] != kbd) {
lwkt_reltoken(&kbd_token);
return ENOENT;
}
callout_stop(&kbd->kb_atkbd_timeout_ch);
if (KBD_IS_BUSY(kbd)) {
error = (*kbd->kb_callback.kc_func)(kbd, KBDIO_UNLOADING,
kbd->kb_callback.kc_arg);
if (error) {
lwkt_reltoken(&kbd_token);
return error;
}
if (KBD_IS_BUSY(kbd)) {
lwkt_reltoken(&kbd_token);
return EBUSY;
}
}
KBD_CONFIG_LOST(kbd);
KBD_INVALID(kbd);
keyboard[kbd->kb_index] = NULL;
kbdsw[kbd->kb_index] = NULL;
KBD_ALWAYS_UNLOCK(kbd);
lockuninit(&kbd->kb_lock);
lwkt_reltoken(&kbd_token);
return 0;
}
keyboard_switch_t *
kbd_get_switch(char *driver)
{
const keyboard_driver_t **list;
const keyboard_driver_t *p;
lwkt_gettoken(&kbd_token);
SLIST_FOREACH(p, &keyboard_drivers, link) {
if (strcmp(p->name, driver) == 0) {
lwkt_reltoken(&kbd_token);
return p->kbdsw;
}
}
SET_FOREACH(list, kbddriver_set) {
p = *list;
if (strcmp(p->name, driver) == 0) {
lwkt_reltoken(&kbd_token);
return p->kbdsw;
}
}
lwkt_reltoken(&kbd_token);
return NULL;
}
int
kbd_find_keyboard2(char *driver, int unit, int index, int legacy)
{
int i;
int pref;
int pref_index;
pref = 0;
pref_index = -1;
lwkt_gettoken(&kbd_token);
if ((index < 0) || (index >= KBD_MAXKEYBOARDS)) {
lwkt_reltoken(&kbd_token);
return (-1);
}
for (i = index; i < KBD_MAXKEYBOARDS; ++i) {
if (keyboard[i] == NULL)
continue;
if (!KBD_IS_VALID(keyboard[i]))
continue;
if (strcmp("*", driver) && strcmp(keyboard[i]->kb_name, driver))
continue;
if ((unit != -1) && (keyboard[i]->kb_unit != unit))
continue;
if (legacy) {
if (pref <= keyboard[i]->kb_pref) {
pref = keyboard[i]->kb_pref;
pref_index = i;
}
} else {
lwkt_reltoken(&kbd_token);
return i;
}
}
if (!legacy)
KKASSERT(pref_index == -1);
lwkt_reltoken(&kbd_token);
return (pref_index);
}
int
kbd_find_keyboard(char *driver, int unit)
{
return (kbd_find_keyboard2(driver, unit, 0, 1));
}
int
kbd_allocate(char *driver, int unit, void *id, kbd_callback_func_t *func,
void *arg)
{
int index;
if (func == NULL)
return -1;
lwkt_gettoken(&kbd_token);
index = kbd_find_keyboard(driver, unit);
if (index >= 0) {
if (KBD_IS_BUSY(keyboard[index])) {
lwkt_reltoken(&kbd_token);
return -1;
}
keyboard[index]->kb_token = id;
KBD_BUSY(keyboard[index]);
keyboard[index]->kb_callback.kc_func = func;
keyboard[index]->kb_callback.kc_arg = arg;
kbd_clear_state(keyboard[index]);
}
lwkt_reltoken(&kbd_token);
return index;
}
int
kbd_release(keyboard_t *kbd, void *id)
{
int error;
lwkt_gettoken(&kbd_token);
if (!KBD_IS_VALID(kbd) || !KBD_IS_BUSY(kbd)) {
error = EINVAL;
} else if (kbd->kb_token != id) {
error = EPERM;
} else {
kbd->kb_token = NULL;
KBD_UNBUSY(kbd);
kbd->kb_callback.kc_func = NULL;
kbd->kb_callback.kc_arg = NULL;
kbd_clear_state(kbd);
error = 0;
}
lwkt_reltoken(&kbd_token);
return error;
}
int
kbd_change_callback(keyboard_t *kbd, void *id, kbd_callback_func_t *func,
void *arg)
{
int error;
lwkt_gettoken(&kbd_token);
if (!KBD_IS_VALID(kbd) || !KBD_IS_BUSY(kbd)) {
error = EINVAL;
} else if (kbd->kb_token != id) {
error = EPERM;
} else if (func == NULL) {
error = EINVAL;
} else {
kbd->kb_callback.kc_func = func;
kbd->kb_callback.kc_arg = arg;
error = 0;
}
lwkt_reltoken(&kbd_token);
return error;
}
keyboard_t *
kbd_get_keyboard(int index)
{
keyboard_t *kbd;
lwkt_gettoken(&kbd_token);
if ((index < 0) || (index >= KBD_MAXKEYBOARDS)) {
lwkt_reltoken(&kbd_token);
return NULL;
}
if (keyboard[index] == NULL) {
lwkt_reltoken(&kbd_token);
return NULL;
}
if (!KBD_IS_VALID(keyboard[index])) {
lwkt_reltoken(&kbd_token);
return NULL;
}
kbd = keyboard[index];
lwkt_reltoken(&kbd_token);
return kbd;
}
int
kbd_configure(int flags)
{
const keyboard_driver_t **list;
const keyboard_driver_t *p;
lwkt_gettoken(&kbd_token);
SLIST_FOREACH(p, &keyboard_drivers, link) {
if (p->configure != NULL)
(*p->configure)(flags);
}
SET_FOREACH(list, kbddriver_set) {
p = *list;
if (p->configure != NULL)
(*p->configure)(flags);
}
lwkt_reltoken(&kbd_token);
return 0;
}
#ifdef KBD_INSTALL_CDEV
#define KBD_UNIT(dev) minor(dev)
static d_open_t genkbdopen;
static d_close_t genkbdclose;
static d_read_t genkbdread;
static d_write_t genkbdwrite;
static d_ioctl_t genkbdioctl;
static d_kqfilter_t genkbdkqfilter;
static void genkbdfiltdetach(struct knote *);
static int genkbdfilter(struct knote *, long);
static struct dev_ops kbd_ops = {
{ "kbd", 0, D_MPSAFE },
.d_open = genkbdopen,
.d_close = genkbdclose,
.d_read = genkbdread,
.d_write = genkbdwrite,
.d_ioctl = genkbdioctl,
.d_kqfilter = genkbdkqfilter
};
int
kbd_attach(keyboard_t *kbd)
{
cdev_t dev;
char tbuf[MAKEDEV_MINNBUF];
lwkt_gettoken(&kbd_token);
if (kbd->kb_index >= KBD_MAXKEYBOARDS) {
lwkt_reltoken(&kbd_token);
return EINVAL;
}
if (keyboard[kbd->kb_index] != kbd) {
lwkt_reltoken(&kbd_token);
return EINVAL;
}
if (kbd->kb_dev == NULL) {
kbd->kb_dev = make_dev(&kbd_ops, kbd->kb_index,
UID_ROOT, GID_WHEEL, 0600, "kbd%s",
makedev_unit_b32(tbuf, kbd->kb_index));
}
dev = kbd->kb_dev;
if (dev->si_drv1 == NULL) {
dev->si_drv1 = kmalloc(sizeof(struct genkbd_softc), M_DEVBUF,
M_WAITOK);
}
bzero(dev->si_drv1, sizeof(struct genkbd_softc));
kprintf("kbd%d at %s%d\n", kbd->kb_index, kbd->kb_name, kbd->kb_unit);
lwkt_reltoken(&kbd_token);
return 0;
}
int
kbd_detach(keyboard_t *kbd)
{
cdev_t dev;
lwkt_gettoken(&kbd_token);
if (kbd->kb_index >= KBD_MAXKEYBOARDS) {
lwkt_reltoken(&kbd_token);
return EINVAL;
}
if (keyboard[kbd->kb_index] != kbd) {
lwkt_reltoken(&kbd_token);
return EINVAL;
}
if ((dev = kbd->kb_dev) != NULL) {
if (dev->si_drv1) {
kfree(dev->si_drv1, M_DEVBUF);
dev->si_drv1 = NULL;
}
kbd->kb_dev = NULL;
}
dev_ops_remove_minor(&kbd_ops, kbd->kb_index);
lwkt_reltoken(&kbd_token);
return 0;
}
static void
genkbd_putc(genkbd_softc_t sc, char c)
{
unsigned int p;
lwkt_gettoken(&kbd_token);
if (sc->gkb_q_length == KB_QSIZE) {
lwkt_reltoken(&kbd_token);
return;
}
p = (sc->gkb_q_start + sc->gkb_q_length) % KB_QSIZE;
sc->gkb_q[p] = c;
sc->gkb_q_length++;
lwkt_reltoken(&kbd_token);
}
static size_t
genkbd_getc(genkbd_softc_t sc, char *buf, size_t len)
{
lwkt_gettoken(&kbd_token);
if (sc->gkb_q_length == 0) {
lwkt_reltoken(&kbd_token);
return (0);
}
if (len >= sc->gkb_q_length)
len = sc->gkb_q_length;
if (len >= KB_QSIZE - sc->gkb_q_start)
len = KB_QSIZE - sc->gkb_q_start;
memcpy(buf, sc->gkb_q + sc->gkb_q_start, len);
sc->gkb_q_start = (sc->gkb_q_start + len) % KB_QSIZE;
sc->gkb_q_length -= len;
lwkt_reltoken(&kbd_token);
return (len);
}
static kbd_callback_func_t genkbd_event;
static int
genkbdopen(struct dev_open_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
keyboard_t *kbd;
genkbd_softc_t sc;
int i;
if (caps_priv_check_self(SYSCAP_RESTRICTEDROOT))
return (EPERM);
lwkt_gettoken(&kbd_token);
sc = dev->si_drv1;
kbd = kbd_get_keyboard(KBD_INDEX(dev));
if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
lwkt_reltoken(&kbd_token);
return ENXIO;
}
i = kbd_allocate(kbd->kb_name, kbd->kb_unit, sc,
genkbd_event, sc);
if (i < 0) {
lwkt_reltoken(&kbd_token);
return EBUSY;
}
sc->gkb_q_length = 0;
lwkt_reltoken(&kbd_token);
return 0;
}
static int
genkbdclose(struct dev_close_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
keyboard_t *kbd;
genkbd_softc_t sc;
lwkt_gettoken(&kbd_token);
sc = dev->si_drv1;
kbd = kbd_get_keyboard(KBD_INDEX(dev));
if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
} else {
kbd_release(kbd, sc);
}
lwkt_reltoken(&kbd_token);
return 0;
}
static int
genkbdread(struct dev_read_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct uio *uio = ap->a_uio;
keyboard_t *kbd;
genkbd_softc_t sc;
u_char buffer[KB_BUFSIZE];
int len;
int error;
lwkt_gettoken(&kbd_token);
sc = dev->si_drv1;
kbd = kbd_get_keyboard(KBD_INDEX(dev));
if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
lwkt_reltoken(&kbd_token);
return ENXIO;
}
while (sc->gkb_q_length == 0) {
if (ap->a_ioflag & IO_NDELAY) {
lwkt_reltoken(&kbd_token);
return EWOULDBLOCK;
}
sc->gkb_flags |= KB_ASLEEP;
error = tsleep((caddr_t)sc, PCATCH, "kbdrea", 0);
kbd = kbd_get_keyboard(KBD_INDEX(dev));
if ((kbd == NULL) || !KBD_IS_VALID(kbd)) {
lwkt_reltoken(&kbd_token);
return ENXIO;
}
if (error) {
sc->gkb_flags &= ~KB_ASLEEP;
lwkt_reltoken(&kbd_token);
return error;
}
}
lwkt_reltoken(&kbd_token);
error = 0;
while (uio->uio_resid > 0) {
len = (int)szmin(uio->uio_resid, sizeof(buffer));
len = genkbd_getc(sc, buffer, len);
if (len <= 0)
break;
error = uiomove(buffer, (size_t)len, uio);
if (error)
break;
}
return error;
}
static int
genkbdwrite(struct dev_write_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
keyboard_t *kbd;
lwkt_gettoken(&kbd_token);
kbd = kbd_get_keyboard(KBD_INDEX(dev));
if ((kbd == NULL) || !KBD_IS_VALID(kbd)) {
lwkt_reltoken(&kbd_token);
return ENXIO;
}
lwkt_reltoken(&kbd_token);
return ENODEV;
}
static int
genkbdioctl(struct dev_ioctl_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
keyboard_t *kbd;
int error;
lwkt_gettoken(&kbd_token);
kbd = kbd_get_keyboard(KBD_INDEX(dev));
if ((kbd == NULL) || !KBD_IS_VALID(kbd)) {
lwkt_reltoken(&kbd_token);
return ENXIO;
}
error = kbd_ioctl(kbd, ap->a_cmd, ap->a_data);
if (error == ENOIOCTL)
error = ENODEV;
lwkt_reltoken(&kbd_token);
return error;
}
static struct filterops genkbdfiltops =
{ FILTEROP_ISFD, NULL, genkbdfiltdetach, genkbdfilter };
static int
genkbdkqfilter(struct dev_kqfilter_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct knote *kn = ap->a_kn;
genkbd_softc_t sc;
struct klist *klist;
ap->a_result = 0;
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &genkbdfiltops;
kn->kn_hook = (caddr_t)dev;
break;
default:
ap->a_result = EOPNOTSUPP;
return (0);
}
sc = dev->si_drv1;
klist = &sc->gkb_rkq.ki_note;
knote_insert(klist, kn);
return (0);
}
static void
genkbdfiltdetach(struct knote *kn)
{
cdev_t dev = (cdev_t)kn->kn_hook;
genkbd_softc_t sc;
struct klist *klist;
sc = dev->si_drv1;
klist = &sc->gkb_rkq.ki_note;
knote_remove(klist, kn);
}
static int
genkbdfilter(struct knote *kn, long hint)
{
cdev_t dev = (cdev_t)kn->kn_hook;
keyboard_t *kbd;
genkbd_softc_t sc;
int ready = 0;
lwkt_gettoken(&kbd_token);
sc = dev->si_drv1;
kbd = kbd_get_keyboard(KBD_INDEX(dev));
if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
kn->kn_flags |= (EV_EOF | EV_NODATA);
ready = 1;
} else {
if (sc->gkb_q_length > 0)
ready = 1;
}
lwkt_reltoken(&kbd_token);
return (ready);
}
static int
genkbd_event(keyboard_t *kbd, int event, void *arg)
{
genkbd_softc_t sc;
size_t len;
u_char *cp;
int mode;
int c;
lwkt_gettoken(&kbd_token);
sc = (genkbd_softc_t)arg;
switch (event) {
case KBDIO_KEYINPUT:
break;
case KBDIO_UNLOADING:
kbd_release(kbd, sc);
if (sc->gkb_flags & KB_ASLEEP) {
sc->gkb_flags &= ~KB_ASLEEP;
wakeup((caddr_t)sc);
}
KNOTE(&sc->gkb_rkq.ki_note, 0);
lwkt_reltoken(&kbd_token);
return 0;
default:
lwkt_reltoken(&kbd_token);
return EINVAL;
}
if (kbd_ioctl(kbd, KDGKBMODE, (caddr_t)&mode))
mode = K_XLATE;
while (kbd_check_char(kbd)) {
c = kbd_read_char(kbd, FALSE);
if (c == NOKEY)
continue;
if (c == ERRKEY)
continue;
if (!KBD_IS_BUSY(kbd))
continue;
if (mode != K_XLATE) {
genkbd_putc(sc, KEYCHAR(c));
continue;
}
if (c & RELKEY)
continue;
if (c & SPCLKEY) {
switch (KEYCHAR(c)) {
default:
continue;
case BTAB:
genkbd_putc(sc, 0x1b);
genkbd_putc(sc, '[');
genkbd_putc(sc, 'Z');
continue;
}
}
switch (KEYFLAGS(c)) {
case 0:
genkbd_putc(sc, KEYCHAR(c));
break;
case MKEY:
genkbd_putc(sc, 0x1b);
genkbd_putc(sc, KEYCHAR(c));
break;
case FKEY | SPCLKEY:
cp = kbd_get_fkeystr(kbd, KEYCHAR(c), &len);
if (cp != NULL) {
while (len-- > 0)
genkbd_putc(sc, *cp++);
}
break;
}
}
if (sc->gkb_q_length > 0) {
if (sc->gkb_flags & KB_ASLEEP) {
sc->gkb_flags &= ~KB_ASLEEP;
wakeup((caddr_t)sc);
}
KNOTE(&sc->gkb_rkq.ki_note, 0);
}
lwkt_reltoken(&kbd_token);
return 0;
}
#endif
int
genkbd_commonioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
keyarg_t *keyp;
fkeyarg_t *fkeyp;
int i;
lwkt_gettoken(&kbd_token);
switch (cmd) {
case KDGKBINFO:
((keyboard_info_t *)arg)->kb_index = kbd->kb_index;
i = imin(strlen(kbd->kb_name) + 1,
sizeof(((keyboard_info_t *)arg)->kb_name));
bcopy(kbd->kb_name, ((keyboard_info_t *)arg)->kb_name, i);
((keyboard_info_t *)arg)->kb_unit = kbd->kb_unit;
((keyboard_info_t *)arg)->kb_type = kbd->kb_type;
((keyboard_info_t *)arg)->kb_config = kbd->kb_config;
((keyboard_info_t *)arg)->kb_flags = kbd->kb_flags;
break;
case KDGKBTYPE:
*(int *)arg = kbd->kb_type;
break;
case KDGETREPEAT:
((int *)arg)[0] = kbd->kb_delay1;
((int *)arg)[1] = kbd->kb_delay2;
break;
case GIO_KEYMAP:
bcopy(kbd->kb_keymap, arg, sizeof(*kbd->kb_keymap));
break;
case PIO_KEYMAP:
#ifndef KBD_DISABLE_KEYMAP_LOAD
bzero(kbd->kb_accentmap, sizeof(*kbd->kb_accentmap));
bcopy(arg, kbd->kb_keymap, sizeof(*kbd->kb_keymap));
break;
#else
lwkt_reltoken(&kbd_token);
return ENODEV;
#endif
case GIO_KEYMAPENT:
keyp = (keyarg_t *)arg;
if (keyp->keynum >= sizeof(kbd->kb_keymap->key)
/sizeof(kbd->kb_keymap->key[0])) {
lwkt_reltoken(&kbd_token);
return EINVAL;
}
bcopy(&kbd->kb_keymap->key[keyp->keynum], &keyp->key,
sizeof(keyp->key));
break;
case PIO_KEYMAPENT:
#ifndef KBD_DISABLE_KEYMAP_LOAD
keyp = (keyarg_t *)arg;
if (keyp->keynum >= sizeof(kbd->kb_keymap->key)
/sizeof(kbd->kb_keymap->key[0])) {
lwkt_reltoken(&kbd_token);
return EINVAL;
}
bcopy(&keyp->key, &kbd->kb_keymap->key[keyp->keynum],
sizeof(keyp->key));
break;
#else
lwkt_reltoken(&kbd_token);
return ENODEV;
#endif
case GIO_DEADKEYMAP:
bcopy(kbd->kb_accentmap, arg, sizeof(*kbd->kb_accentmap));
break;
case PIO_DEADKEYMAP:
#ifndef KBD_DISABLE_KEYMAP_LOAD
bcopy(arg, kbd->kb_accentmap, sizeof(*kbd->kb_accentmap));
break;
#else
lwkt_reltoken(&kbd_token);
return ENODEV;
#endif
case GETFKEY:
fkeyp = (fkeyarg_t *)arg;
if (fkeyp->keynum >= kbd->kb_fkeytab_size) {
lwkt_reltoken(&kbd_token);
return EINVAL;
}
bcopy(kbd->kb_fkeytab[fkeyp->keynum].str, fkeyp->keydef,
kbd->kb_fkeytab[fkeyp->keynum].len);
fkeyp->flen = kbd->kb_fkeytab[fkeyp->keynum].len;
break;
case SETFKEY:
#ifndef KBD_DISABLE_KEYMAP_LOAD
fkeyp = (fkeyarg_t *)arg;
if (fkeyp->keynum >= kbd->kb_fkeytab_size) {
lwkt_reltoken(&kbd_token);
return EINVAL;
}
kbd->kb_fkeytab[fkeyp->keynum].len = imin(fkeyp->flen, MAXFK);
bcopy(fkeyp->keydef, kbd->kb_fkeytab[fkeyp->keynum].str,
kbd->kb_fkeytab[fkeyp->keynum].len);
break;
#else
lwkt_reltoken(&kbd_token);
return ENODEV;
#endif
default:
lwkt_reltoken(&kbd_token);
return ENOIOCTL;
}
lwkt_reltoken(&kbd_token);
return 0;
}
u_char *
genkbd_get_fkeystr(keyboard_t *kbd, int fkey, size_t *len)
{
u_char *ch;
if (kbd == NULL)
return NULL;
lwkt_gettoken(&kbd_token);
fkey -= F_FN;
if (fkey > kbd->kb_fkeytab_size) {
lwkt_reltoken(&kbd_token);
return NULL;
}
*len = kbd->kb_fkeytab[fkey].len;
ch = kbd->kb_fkeytab[fkey].str;
lwkt_reltoken(&kbd_token);
return ch;
}
static char *
get_kbd_type_name(int type)
{
static struct {
int type;
char *name;
} name_table[] = {
{ KB_84, "AT 84" },
{ KB_101, "AT 101/102" },
{ KB_OTHER, "generic" },
};
int i;
for (i = 0; i < NELEM(name_table); ++i) {
if (type == name_table[i].type)
return name_table[i].name;
}
return "unknown";
}
void
genkbd_diag(keyboard_t *kbd, int level)
{
if (level > 0) {
kprintf("kbd%d: %s%d, %s (%d), config:0x%x, flags:0x%x",
kbd->kb_index, kbd->kb_name, kbd->kb_unit,
get_kbd_type_name(kbd->kb_type), kbd->kb_type,
kbd->kb_config, kbd->kb_flags);
if (kbd->kb_io_base > 0)
kprintf(", port:0x%x-0x%x", kbd->kb_io_base,
kbd->kb_io_base + kbd->kb_io_size - 1);
kprintf("\n");
}
}
#define set_lockkey_state(k, s, l) \
if (!((s) & l ## DOWN)) { \
int i; \
(s) |= l ## DOWN; \
(s) ^= l ## ED; \
i = (s) & LOCK_MASK; \
kbd_ioctl((k), KDSETLED, (caddr_t)&i); \
}
static u_int
save_accent_key(keyboard_t *kbd, u_int key, int *accents)
{
int i;
lwkt_gettoken(&kbd_token);
i = key - F_ACC + 1;
if ((i > kbd->kb_accentmap->n_accs)
|| (kbd->kb_accentmap->acc[i - 1].accchar == 0)) {
*accents = 0;
lwkt_reltoken(&kbd_token);
return ERRKEY;
}
if (i == *accents) {
key = kbd->kb_accentmap->acc[i - 1].accchar;
*accents = 0;
lwkt_reltoken(&kbd_token);
return key;
}
*accents = i;
lwkt_reltoken(&kbd_token);
return NOKEY;
}
static u_int
make_accent_char(keyboard_t *kbd, u_int ch, int *accents)
{
struct acc_t *acc;
int i;
lwkt_gettoken(&kbd_token);
acc = &kbd->kb_accentmap->acc[*accents - 1];
*accents = 0;
if (ch == ' ') {
lwkt_reltoken(&kbd_token);
return acc->accchar;
}
for (i = 0; i < NUM_ACCENTCHARS; ++i) {
if (acc->map[i][0] == 0)
break;
if (acc->map[i][0] == ch) {
lwkt_reltoken(&kbd_token);
return acc->map[i][1];
}
}
lwkt_reltoken(&kbd_token);
return ERRKEY;
}
int
genkbd_keyaction(keyboard_t *kbd, int keycode, int up, int *shiftstate,
int *accents)
{
struct keyent_t *key;
int state = *shiftstate;
int action;
int f;
int i;
lwkt_gettoken(&kbd_token);
i = keycode;
f = state & (AGRS | ALKED);
if ((f == AGRS1) || (f == AGRS2) || (f == ALKED))
i += ALTGR_OFFSET;
key = &kbd->kb_keymap->key[i];
i = ((state & SHIFTS) ? 1 : 0)
| ((state & CTLS) ? 2 : 0)
| ((state & ALTS) ? 4 : 0);
if (((key->flgs & FLAG_LOCK_C) && (state & CLKED))
|| ((key->flgs & FLAG_LOCK_N) && (state & NLKED)) )
i ^= 1;
if (up) {
action = kbd->kb_lastact[keycode];
kbd->kb_lastact[keycode] = NOP;
switch (action) {
case LSHA:
if (state & SHIFTAON) {
set_lockkey_state(kbd, state, ALK);
state &= ~ALKDOWN;
}
action = LSH;
case LSH:
state &= ~SHIFTS1;
break;
case RSHA:
if (state & SHIFTAON) {
set_lockkey_state(kbd, state, ALK);
state &= ~ALKDOWN;
}
action = RSH;
case RSH:
state &= ~SHIFTS2;
break;
case LCTRA:
if (state & SHIFTAON) {
set_lockkey_state(kbd, state, ALK);
state &= ~ALKDOWN;
}
action = LCTR;
case LCTR:
state &= ~CTLS1;
break;
case RCTRA:
if (state & SHIFTAON) {
set_lockkey_state(kbd, state, ALK);
state &= ~ALKDOWN;
}
action = RCTR;
case RCTR:
state &= ~CTLS2;
break;
case LALTA:
if (state & SHIFTAON) {
set_lockkey_state(kbd, state, ALK);
state &= ~ALKDOWN;
}
action = LALT;
case LALT:
state &= ~ALTS1;
break;
case RALTA:
if (state & SHIFTAON) {
set_lockkey_state(kbd, state, ALK);
state &= ~ALKDOWN;
}
action = RALT;
case RALT:
state &= ~ALTS2;
break;
case ASH:
state &= ~AGRS1;
break;
case META:
state &= ~METAS1;
break;
case NLK:
state &= ~NLKDOWN;
break;
case CLK:
state &= ~CLKDOWN;
break;
case SLK:
state &= ~SLKDOWN;
break;
case ALK:
state &= ~ALKDOWN;
break;
case NOP:
*shiftstate &= ~SHIFTAON;
lwkt_reltoken(&kbd_token);
return NOKEY;
}
*shiftstate = state & ~SHIFTAON;
lwkt_reltoken(&kbd_token);
return (SPCLKEY | RELKEY | action);
} else {
action = key->map[i];
state &= ~SHIFTAON;
if (key->spcl & (0x80 >> i)) {
if (kbd->kb_lastact[keycode] == NOP)
kbd->kb_lastact[keycode] = action;
if (kbd->kb_lastact[keycode] != action)
action = NOP;
switch (action) {
case NLK:
set_lockkey_state(kbd, state, NLK);
break;
case CLK:
set_lockkey_state(kbd, state, CLK);
break;
case SLK:
set_lockkey_state(kbd, state, SLK);
break;
case ALK:
set_lockkey_state(kbd, state, ALK);
break;
case SPSC: case RBT: case SUSP: case STBY:
case DBG: case NEXT: case PREV: case PNC:
case HALT: case PDWN:
*accents = 0;
break;
case BTAB:
*accents = 0;
action |= BKEY;
break;
case LSHA:
state |= SHIFTAON;
action = LSH;
case LSH:
state |= SHIFTS1;
break;
case RSHA:
state |= SHIFTAON;
action = RSH;
case RSH:
state |= SHIFTS2;
break;
case LCTRA:
state |= SHIFTAON;
action = LCTR;
case LCTR:
state |= CTLS1;
break;
case RCTRA:
state |= SHIFTAON;
action = RCTR;
case RCTR:
state |= CTLS2;
break;
case LALTA:
state |= SHIFTAON;
action = LALT;
case LALT:
state |= ALTS1;
break;
case RALTA:
state |= SHIFTAON;
action = RALT;
case RALT:
state |= ALTS2;
break;
case ASH:
state |= AGRS1;
break;
case META:
state |= METAS1;
break;
case NOP:
*shiftstate = state;
lwkt_reltoken(&kbd_token);
return NOKEY;
default:
*shiftstate = state;
if (action >= F_ACC && action <= L_ACC) {
action = save_accent_key(kbd, action,
accents);
switch (action) {
case NOKEY:
case ERRKEY:
lwkt_reltoken(&kbd_token);
return action;
default:
if (state & METAS) {
lwkt_reltoken(&kbd_token);
return (action | MKEY);
} else {
lwkt_reltoken(&kbd_token);
return action;
}
}
}
if (*accents > 0) {
*accents = 0;
lwkt_reltoken(&kbd_token);
return ERRKEY;
}
if (action >= F_FN && action <= L_FN)
action |= FKEY;
lwkt_reltoken(&kbd_token);
return (SPCLKEY | action);
}
*shiftstate = state;
lwkt_reltoken(&kbd_token);
return (SPCLKEY | action);
} else {
kbd->kb_lastact[keycode] = NOP;
*shiftstate = state;
if (*accents > 0) {
action = make_accent_char(kbd, action, accents);
if (action == ERRKEY) {
lwkt_reltoken(&kbd_token);
return action;
}
}
if (state & METAS)
action |= MKEY;
lwkt_reltoken(&kbd_token);
return action;
}
}
lwkt_reltoken(&kbd_token);
}
#ifdef EVDEV_SUPPORT
void
kbd_ev_event(keyboard_t *kbd, uint16_t type, uint16_t code, int32_t value)
{
int delay[2], led = 0, leds, oleds;
if (type == EV_LED) {
leds = oleds = KBD_LED_VAL(kbd);
switch (code) {
case LED_CAPSL:
led = CLKED;
break;
case LED_NUML:
led = NLKED;
break;
case LED_SCROLLL:
led = SLKED;
break;
}
if (value)
leds |= led;
else
leds &= ~led;
if (leds != oleds)
kbd_ioctl(kbd, KDSETLED, (caddr_t)&leds);
} else if (type == EV_REP && code == REP_DELAY) {
delay[0] = value;
delay[1] = kbd->kb_delay2;
kbd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay);
} else if (type == EV_REP && code == REP_PERIOD) {
delay[0] = kbd->kb_delay1;
delay[1] = value;
kbd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay);
}
}
#endif