Index: stable/11/sys/arm/samsung/exynos/chrome_kb.c
===================================================================
--- stable/11/sys/arm/samsung/exynos/chrome_kb.c (revision 356012)
+++ stable/11/sys/arm/samsung/exynos/chrome_kb.c (revision 356013)
@@ -1,921 +1,919 @@
/*-
* Copyright (c) 2014 Ruslan Bukin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Samsung Chromebook Keyboard
*/
#include
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "gpio_if.h"
#include
#include
#include
#include
#define CKB_LOCK() mtx_lock(&Giant)
#define CKB_UNLOCK() mtx_unlock(&Giant)
#ifdef INVARIANTS
/*
* Assert that the lock is held in all contexts
* where the code can be executed.
*/
#define CKB_LOCK_ASSERT() mtx_assert(&Giant, MA_OWNED)
/*
* Assert that the lock is held in the contexts
* where it really has to be so.
*/
#define CKB_CTX_LOCK_ASSERT() \
do { \
if (!kdb_active && panicstr == NULL) \
mtx_assert(&Giant, MA_OWNED); \
} while (0)
#else
#define CKB_LOCK_ASSERT() (void)0
#define CKB_CTX_LOCK_ASSERT() (void)0
#endif
/*
* Define a stub keyboard driver in case one hasn't been
* compiled into the kernel
*/
#include
#include
#include
#define CKB_NFKEY 12
#define CKB_FLAG_COMPOSE 0x1
#define CKB_FLAG_POLLING 0x2
#define KBD_DRIVER_NAME "ckbd"
struct ckb_softc {
keyboard_t sc_kbd;
keymap_t sc_keymap;
accentmap_t sc_accmap;
fkeytab_t sc_fkeymap[CKB_NFKEY];
struct resource* sc_mem_res;
struct resource* sc_irq_res;
void* sc_intr_hl;
int sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
int sc_state; /* shift/lock key state */
int sc_accents; /* accent key index (> 0) */
int sc_flags; /* flags */
struct callout sc_repeat_callout;
int sc_repeat_key;
int sc_repeating;
int flag;
int rows;
int cols;
int gpio;
device_t dev;
device_t gpio_dev;
struct thread *sc_poll_thread;
uint16_t *keymap;
uint8_t *scan_local;
uint8_t *scan;
};
/* prototypes */
static int ckb_set_typematic(keyboard_t *, int);
static uint32_t ckb_read_char(keyboard_t *, int);
static void ckb_clear_state(keyboard_t *);
static int ckb_ioctl(keyboard_t *, u_long, caddr_t);
static int ckb_enable(keyboard_t *);
static int ckb_disable(keyboard_t *);
static void
ckb_repeat(void *arg)
{
struct ckb_softc *sc;
sc = arg;
if (KBD_IS_ACTIVE(&sc->sc_kbd) && KBD_IS_BUSY(&sc->sc_kbd)) {
if (sc->sc_repeat_key != -1) {
sc->sc_repeating = 1;
sc->sc_kbd.kb_callback.kc_func(&sc->sc_kbd,
KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg);
}
}
}
/* detect a keyboard, not used */
static int
ckb__probe(int unit, void *arg, int flags)
{
return (ENXIO);
}
/* reset and initialize the device, not used */
static int
ckb_init(int unit, keyboard_t **kbdp, void *arg, int flags)
{
return (ENXIO);
}
/* test the interface to the device, not used */
static int
ckb_test_if(keyboard_t *kbd)
{
return (0);
}
/* finish using this keyboard, not used */
static int
ckb_term(keyboard_t *kbd)
{
return (ENXIO);
}
/* keyboard interrupt routine, not used */
static int
ckb_intr(keyboard_t *kbd, void *arg)
{
return (0);
}
/* lock the access to the keyboard, not used */
static int
ckb_lock(keyboard_t *kbd, int lock)
{
return (1);
}
/* clear the internal state of the keyboard */
static void
ckb_clear_state(keyboard_t *kbd)
{
struct ckb_softc *sc;
sc = kbd->kb_data;
CKB_CTX_LOCK_ASSERT();
sc->sc_flags &= ~(CKB_FLAG_COMPOSE | CKB_FLAG_POLLING);
sc->sc_state &= LOCK_MASK; /* preserve locking key state */
sc->sc_accents = 0;
}
/* save the internal state, not used */
static int
ckb_get_state(keyboard_t *kbd, void *buf, size_t len)
{
return (len == 0) ? 1 : -1;
}
/* set the internal state, not used */
static int
ckb_set_state(keyboard_t *kbd, void *buf, size_t len)
{
return (EINVAL);
}
/* check if data is waiting */
static int
ckb_check(keyboard_t *kbd)
{
struct ckb_softc *sc;
int i;
sc = kbd->kb_data;
CKB_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (0);
if (sc->sc_flags & CKB_FLAG_POLLING) {
return (1);
}
for (i = 0; i < sc->cols; i++)
if (sc->scan_local[i] != sc->scan[i]) {
return (1);
}
if (sc->sc_repeating)
return (1);
return (0);
}
/* check if char is waiting */
static int
ckb_check_char_locked(keyboard_t *kbd)
{
CKB_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (0);
return (ckb_check(kbd));
}
static int
ckb_check_char(keyboard_t *kbd)
{
int result;
CKB_LOCK();
result = ckb_check_char_locked(kbd);
CKB_UNLOCK();
return (result);
}
/* read one byte from the keyboard if it's allowed */
/* Currently unused. */
static int
ckb_read(keyboard_t *kbd, int wait)
{
CKB_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (-1);
printf("Implement ME: %s\n", __func__);
return (0);
}
static uint16_t
keymap_read(struct ckb_softc *sc, int col, int row)
{
KASSERT(sc->keymap != NULL, ("keymap_read: no keymap"));
if (col >= 0 && col < sc->cols &&
row >= 0 && row < sc->rows) {
return sc->keymap[row * sc->cols + col];
}
return (0);
}
static int
keymap_write(struct ckb_softc *sc, int col, int row, uint16_t key)
{
KASSERT(sc->keymap != NULL, ("keymap_write: no keymap"));
if (col >= 0 && col < sc->cols &&
row >= 0 && row < sc->rows) {
sc->keymap[row * sc->cols + col] = key;
return (0);
}
return (-1);
}
/* read char from the keyboard */
static uint32_t
ckb_read_char_locked(keyboard_t *kbd, int wait)
{
struct ckb_softc *sc;
int i,j;
uint16_t key;
int oldbit;
int newbit;
int status;
sc = kbd->kb_data;
CKB_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (NOKEY);
if (sc->sc_repeating) {
sc->sc_repeating = 0;
callout_reset(&sc->sc_repeat_callout, hz / 10,
ckb_repeat, sc);
return (sc->sc_repeat_key);
}
if (sc->sc_flags & CKB_FLAG_POLLING) {
for (;;) {
GPIO_PIN_GET(sc->gpio_dev, sc->gpio, &status);
if (status == 0) {
if (ec_command(EC_CMD_MKBP_STATE, sc->scan,
sc->cols,
sc->scan, sc->cols)) {
return (NOKEY);
}
break;
}
if (!wait) {
return (NOKEY);
}
DELAY(1000);
}
}
for (i = 0; i < sc->cols; i++) {
for (j = 0; j < sc->rows; j++) {
oldbit = (sc->scan_local[i] & (1 << j));
newbit = (sc->scan[i] & (1 << j));
if (oldbit == newbit)
continue;
key = keymap_read(sc, i, j);
if (key == 0) {
continue;
}
if (newbit > 0) {
/* key pressed */
sc->scan_local[i] |= (1 << j);
/* setup repeating */
sc->sc_repeat_key = key;
callout_reset(&sc->sc_repeat_callout,
hz / 2, ckb_repeat, sc);
} else {
/* key released */
sc->scan_local[i] &= ~(1 << j);
/* release flag */
key |= 0x80;
/* unsetup repeating */
sc->sc_repeat_key = -1;
callout_stop(&sc->sc_repeat_callout);
}
return (key);
}
}
return (NOKEY);
}
/* Currently wait is always false. */
static uint32_t
ckb_read_char(keyboard_t *kbd, int wait)
{
uint32_t keycode;
CKB_LOCK();
keycode = ckb_read_char_locked(kbd, wait);
CKB_UNLOCK();
return (keycode);
}
/* some useful control functions */
static int
ckb_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
struct ckb_softc *sc;
int i;
sc = kbd->kb_data;
CKB_LOCK_ASSERT();
switch (cmd) {
case KDGKBMODE: /* get keyboard mode */
*(int *)arg = sc->sc_mode;
break;
case KDSKBMODE: /* set keyboard mode */
switch (*(int *)arg) {
case K_XLATE:
if (sc->sc_mode != K_XLATE) {
/* make lock key state and LED state match */
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= KBD_LED_VAL(kbd);
}
/* FALLTHROUGH */
case K_RAW:
case K_CODE:
if (sc->sc_mode != *(int *)arg) {
if ((sc->sc_flags & CKB_FLAG_POLLING) == 0)
ckb_clear_state(kbd);
sc->sc_mode = *(int *)arg;
}
break;
default:
return (EINVAL);
}
break;
case KDGETLED: /* get keyboard LED */
*(int *)arg = KBD_LED_VAL(kbd);
break;
case KDSETLED: /* set keyboard LED */
/* NOTE: lock key state in "sc_state" won't be changed */
if (*(int *)arg & ~LOCK_MASK)
return (EINVAL);
i = *(int *)arg;
/* replace CAPS LED with ALTGR LED for ALTGR keyboards */
if (sc->sc_mode == K_XLATE &&
kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
if (i & ALKED)
i |= CLKED;
else
i &= ~CLKED;
}
if (KBD_HAS_DEVICE(kbd)) {
/* Configure LED */
}
KBD_LED_VAL(kbd) = *(int *)arg;
break;
case KDGKBSTATE: /* get lock key state */
*(int *)arg = sc->sc_state & LOCK_MASK;
break;
case KDSKBSTATE: /* set lock key state */
if (*(int *)arg & ~LOCK_MASK) {
return (EINVAL);
}
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= *(int *)arg;
/* set LEDs and quit */
return (ckb_ioctl(kbd, KDSETLED, arg));
case KDSETREPEAT: /* set keyboard repeat rate (new
* interface) */
if (!KBD_HAS_DEVICE(kbd)) {
return (0);
}
if (((int *)arg)[1] < 0) {
return (EINVAL);
}
if (((int *)arg)[0] < 0) {
return (EINVAL);
}
if (((int *)arg)[0] < 200) /* fastest possible value */
kbd->kb_delay1 = 200;
else
kbd->kb_delay1 = ((int *)arg)[0];
kbd->kb_delay2 = ((int *)arg)[1];
return (0);
case KDSETRAD: /* set keyboard repeat rate (old
* interface) */
return (ckb_set_typematic(kbd, *(int *)arg));
case PIO_KEYMAP: /* set keyboard translation table */
case OPIO_KEYMAP: /* set keyboard translation table
* (compat) */
case PIO_KEYMAPENT: /* set keyboard translation table
* entry */
case PIO_DEADKEYMAP: /* set accent key translation table */
sc->sc_accents = 0;
/* FALLTHROUGH */
default:
return (genkbd_commonioctl(kbd, cmd, arg));
}
return (0);
}
static int
ckb_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
int result;
/*
* XXX KDGKBSTATE, KDSKBSTATE and KDSETLED can be called from any
* context where printf(9) can be called, which among other things
* includes interrupt filters and threads with any kinds of locks
* already held. For this reason it would be dangerous to acquire
* the Giant here unconditionally. On the other hand we have to
* have it to handle the ioctl.
* So we make our best effort to auto-detect whether we can grab
* the Giant or not. Blame syscons(4) for this.
*/
switch (cmd) {
case KDGKBSTATE:
case KDSKBSTATE:
case KDSETLED:
if (!mtx_owned(&Giant) && !SCHEDULER_STOPPED())
return (EDEADLK); /* best I could come up with */
/* FALLTHROUGH */
default:
CKB_LOCK();
result = ckb_ioctl_locked(kbd, cmd, arg);
CKB_UNLOCK();
return (result);
}
}
/*
* Enable the access to the device; until this function is called,
* the client cannot read from the keyboard.
*/
static int
ckb_enable(keyboard_t *kbd)
{
CKB_LOCK();
KBD_ACTIVATE(kbd);
CKB_UNLOCK();
return (0);
}
/* disallow the access to the device */
static int
ckb_disable(keyboard_t *kbd)
{
CKB_LOCK();
KBD_DEACTIVATE(kbd);
CKB_UNLOCK();
return (0);
}
/* local functions */
static int
ckb_set_typematic(keyboard_t *kbd, int code)
{
static const int delays[] = {250, 500, 750, 1000};
static const int rates[] = {34, 38, 42, 46, 50, 55, 59, 63,
68, 76, 84, 92, 100, 110, 118, 126,
136, 152, 168, 184, 200, 220, 236, 252,
272, 304, 336, 368, 400, 440, 472, 504};
if (code & ~0x7f) {
return (EINVAL);
}
kbd->kb_delay1 = delays[(code >> 5) & 3];
kbd->kb_delay2 = rates[code & 0x1f];
return (0);
}
static int
ckb_poll(keyboard_t *kbd, int on)
{
struct ckb_softc *sc;
sc = kbd->kb_data;
CKB_LOCK();
if (on) {
sc->sc_flags |= CKB_FLAG_POLLING;
sc->sc_poll_thread = curthread;
} else {
sc->sc_flags &= ~CKB_FLAG_POLLING;
}
CKB_UNLOCK();
return (0);
}
/* local functions */
static int dummy_kbd_configure(int flags);
keyboard_switch_t ckbdsw = {
.probe = &ckb__probe,
.init = &ckb_init,
.term = &ckb_term,
.intr = &ckb_intr,
.test_if = &ckb_test_if,
.enable = &ckb_enable,
.disable = &ckb_disable,
.read = &ckb_read,
.check = &ckb_check,
.read_char = &ckb_read_char,
.check_char = &ckb_check_char,
.ioctl = &ckb_ioctl,
.lock = &ckb_lock,
.clear_state = &ckb_clear_state,
.get_state = &ckb_get_state,
.set_state = &ckb_set_state,
- .get_fkeystr = &genkbd_get_fkeystr,
.poll = &ckb_poll,
- .diag = &genkbd_diag,
};
static int
dummy_kbd_configure(int flags)
{
return (0);
}
KEYBOARD_DRIVER(ckbd, ckbdsw, dummy_kbd_configure);
/*
* Parses 'keymap' into sc->keymap.
* Requires sc->cols and sc->rows to be set.
*/
static int
parse_keymap(struct ckb_softc *sc, pcell_t *keymap, size_t len)
{
int i;
sc->keymap = malloc(sc->cols * sc->rows * sizeof(sc->keymap[0]),
M_DEVBUF, M_NOWAIT | M_ZERO);
if (sc->keymap == NULL) {
return (ENOMEM);
}
for (i = 0; i < len; i++) {
/*
* Return value is ignored, we just write whatever fits into
* specified number of rows and columns and silently ignore
* everything else.
* Keymap entries follow this format: 0xRRCCKKKK
* RR - row number, CC - column number, KKKK - key code
*/
keymap_write(sc, (keymap[i] >> 16) & 0xff,
(keymap[i] >> 24) & 0xff,
keymap[i] & 0xffff);
}
return (0);
}
/* Allocates a new array for keymap and returns it in 'keymap'. */
static int
read_keymap(phandle_t node, const char *prop, pcell_t **keymap, size_t *len)
{
if ((*len = OF_getproplen(node, prop)) <= 0) {
return (ENXIO);
}
if ((*keymap = malloc(*len, M_DEVBUF, M_NOWAIT)) == NULL) {
return (ENOMEM);
}
if (OF_getencprop(node, prop, *keymap, *len) != *len) {
return (ENXIO);
}
return (0);
}
static int
parse_dts(struct ckb_softc *sc)
{
phandle_t node;
pcell_t dts_value;
pcell_t *keymap;
int len, ret;
const char *keymap_prop = NULL;
if ((node = ofw_bus_get_node(sc->dev)) == -1)
return (ENXIO);
if ((len = OF_getproplen(node, "google,key-rows")) <= 0)
return (ENXIO);
OF_getencprop(node, "google,key-rows", &dts_value, len);
sc->rows = dts_value;
if ((len = OF_getproplen(node, "google,key-columns")) <= 0)
return (ENXIO);
OF_getencprop(node, "google,key-columns", &dts_value, len);
sc->cols = dts_value;
if ((len = OF_getproplen(node, "freebsd,intr-gpio")) <= 0)
return (ENXIO);
OF_getencprop(node, "freebsd,intr-gpio", &dts_value, len);
sc->gpio = dts_value;
if (OF_hasprop(node, "freebsd,keymap")) {
keymap_prop = "freebsd,keymap";
device_printf(sc->dev, "using FreeBSD-specific keymap from FDT\n");
} else if (OF_hasprop(node, "linux,keymap")) {
keymap_prop = "linux,keymap";
device_printf(sc->dev, "using Linux keymap from FDT\n");
} else {
device_printf(sc->dev, "using built-in keymap\n");
}
if (keymap_prop != NULL) {
if ((ret = read_keymap(node, keymap_prop, &keymap, &len))) {
device_printf(sc->dev,
"failed to read keymap from FDT: %d\n", ret);
return (ret);
}
ret = parse_keymap(sc, keymap, len);
free(keymap, M_DEVBUF);
if (ret) {
return (ret);
}
} else {
if ((ret = parse_keymap(sc, default_keymap, KEYMAP_LEN))) {
return (ret);
}
}
if ((sc->rows == 0) || (sc->cols == 0) || (sc->gpio == 0))
return (ENXIO);
return (0);
}
void
ckb_ec_intr(void *arg)
{
struct ckb_softc *sc;
sc = arg;
if (sc->sc_flags & CKB_FLAG_POLLING)
return;
ec_command(EC_CMD_MKBP_STATE, sc->scan, sc->cols,
sc->scan, sc->cols);
(sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT,
sc->sc_kbd.kb_callback.kc_arg);
};
static int
chrome_kb_attach(device_t dev)
{
struct ckb_softc *sc;
keyboard_t *kbd;
int error;
int rid;
int i;
sc = device_get_softc(dev);
sc->dev = dev;
sc->keymap = NULL;
if ((error = parse_dts(sc)) != 0)
return error;
sc->gpio_dev = devclass_get_device(devclass_find("gpio"), 0);
if (sc->gpio_dev == NULL) {
device_printf(sc->dev, "Can't find gpio device.\n");
return (ENXIO);
}
#if 0
device_printf(sc->dev, "Keyboard matrix [%dx%d]\n",
sc->cols, sc->rows);
#endif
pad_setup_intr(sc->gpio, ckb_ec_intr, sc);
kbd = &sc->sc_kbd;
rid = 0;
sc->scan_local = malloc(sc->cols, M_DEVBUF, M_NOWAIT);
sc->scan = malloc(sc->cols, M_DEVBUF, M_NOWAIT);
for (i = 0; i < sc->cols; i++) {
sc->scan_local[i] = 0;
sc->scan[i] = 0;
}
kbd_init_struct(kbd, KBD_DRIVER_NAME, KB_OTHER,
device_get_unit(dev), 0, 0, 0);
kbd->kb_data = (void *)sc;
sc->sc_keymap = key_map;
sc->sc_accmap = accent_map;
for (i = 0; i < CKB_NFKEY; i++) {
sc->sc_fkeymap[i] = fkey_tab[i];
}
kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap,
sc->sc_fkeymap, CKB_NFKEY);
KBD_FOUND_DEVICE(kbd);
ckb_clear_state(kbd);
KBD_PROBE_DONE(kbd);
callout_init(&sc->sc_repeat_callout, 0);
KBD_INIT_DONE(kbd);
if (kbd_register(kbd) < 0) {
return (ENXIO);
}
KBD_CONFIG_DONE(kbd);
return (0);
}
static int
chrome_kb_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (ofw_bus_is_compatible(dev, "google,cros-ec-keyb") ||
ofw_bus_is_compatible(dev, "google,mkbp-keyb")) {
device_set_desc(dev, "Chrome EC Keyboard");
return (BUS_PROBE_DEFAULT);
}
return (ENXIO);
}
static int
chrome_kb_detach(device_t dev)
{
struct ckb_softc *sc;
sc = device_get_softc(dev);
if (sc->keymap != NULL) {
free(sc->keymap, M_DEVBUF);
}
return 0;
}
static device_method_t chrome_kb_methods[] = {
DEVMETHOD(device_probe, chrome_kb_probe),
DEVMETHOD(device_attach, chrome_kb_attach),
DEVMETHOD(device_detach, chrome_kb_detach),
{ 0, 0 }
};
static driver_t chrome_kb_driver = {
"chrome_kb",
chrome_kb_methods,
sizeof(struct ckb_softc),
};
static devclass_t chrome_kb_devclass;
DRIVER_MODULE(chrome_kb, simplebus, chrome_kb_driver,
chrome_kb_devclass, 0, 0);
Index: stable/11/sys/dev/adb/adb_kbd.c
===================================================================
--- stable/11/sys/dev/adb/adb_kbd.c (revision 356012)
+++ stable/11/sys/dev/adb/adb_kbd.c (revision 356013)
@@ -1,890 +1,888 @@
/*-
* Copyright (C) 2008 Nathan Whitehorn
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "opt_kbd.h"
#include
#include
#include
#include
#include
#include
#include "adb.h"
#define KBD_DRIVER_NAME "akbd"
#define AKBD_EMULATE_ATKBD 1
static int adb_kbd_probe(device_t dev);
static int adb_kbd_attach(device_t dev);
static int adb_kbd_detach(device_t dev);
static void akbd_repeat(void *xsc);
static int adb_fn_keys(SYSCTL_HANDLER_ARGS);
static u_int adb_kbd_receive_packet(device_t dev, u_char status,
u_char command, u_char reg, int len, u_char *data);
struct adb_kbd_softc {
keyboard_t sc_kbd;
device_t sc_dev;
struct mtx sc_mutex;
struct cv sc_cv;
int sc_mode;
int sc_state;
int have_led_control;
uint8_t buffer[8];
#ifdef AKBD_EMULATE_ATKBD
uint8_t at_buffered_char[2];
#endif
volatile int buffers;
struct callout sc_repeater;
int sc_repeatstart;
int sc_repeatcontinue;
uint8_t last_press;
};
static device_method_t adb_kbd_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, adb_kbd_probe),
DEVMETHOD(device_attach, adb_kbd_attach),
DEVMETHOD(device_detach, adb_kbd_detach),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD(device_suspend, bus_generic_suspend),
DEVMETHOD(device_resume, bus_generic_resume),
/* ADB interface */
DEVMETHOD(adb_receive_packet, adb_kbd_receive_packet),
{ 0, 0 }
};
static driver_t adb_kbd_driver = {
"akbd",
adb_kbd_methods,
sizeof(struct adb_kbd_softc),
};
static devclass_t adb_kbd_devclass;
DRIVER_MODULE(akbd, adb, adb_kbd_driver, adb_kbd_devclass, 0, 0);
#ifdef AKBD_EMULATE_ATKBD
#define SCAN_PRESS 0x000
#define SCAN_RELEASE 0x080
#define SCAN_PREFIX_E0 0x100
#define SCAN_PREFIX_E1 0x200
#define SCAN_PREFIX_CTL 0x400
#define SCAN_PREFIX_SHIFT 0x800
#define SCAN_PREFIX (SCAN_PREFIX_E0 | SCAN_PREFIX_E1 | \
SCAN_PREFIX_CTL | SCAN_PREFIX_SHIFT)
static const uint8_t adb_to_at_scancode_map[128] = { 30, 31, 32, 33, 35, 34,
44, 45, 46, 47, 0, 48, 16, 17, 18, 19, 21, 20, 2, 3, 4, 5, 7, 6, 13,
10, 8, 12, 9, 11, 27, 24, 22, 26, 23, 25, 28, 38, 36, 40, 37, 39, 43,
51, 53, 49, 50, 52, 15, 57, 41, 14, 0, 1, 29, 0, 42, 58, 56, 97, 98,
100, 95, 0, 0, 83, 0, 55, 0, 78, 0, 69, 0, 0, 0, 91, 89, 0, 74, 13, 0,
0, 82, 79, 80, 81, 75, 76, 77, 71, 0, 72, 73, 0, 0, 0, 63, 64, 65, 61,
66, 67, 0, 87, 0, 105, 0, 70, 0, 68, 0, 88, 0, 107, 102, 94, 96, 103,
62, 99, 60, 101, 59, 54, 93, 90, 0, 0 };
static int
keycode2scancode(int keycode, int shift, int up)
{
static const int scan[] = {
/* KP enter, right ctrl, KP divide */
0x1c , 0x1d , 0x35 ,
/* print screen */
0x37 | SCAN_PREFIX_SHIFT,
/* right alt, home, up, page up, left, right, end */
0x38, 0x47, 0x48, 0x49, 0x4b, 0x4d, 0x4f,
/* down, page down, insert, delete */
0x50, 0x51, 0x52, 0x53,
/* pause/break (see also below) */
0x46,
/*
* MS: left window, right window, menu
* also Sun: left meta, right meta, compose
*/
0x5b, 0x5c, 0x5d,
/* Sun type 6 USB */
/* help, stop, again, props, undo, front, copy */
0x68, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63,
/* open, paste, find, cut, audiomute, audiolower, audioraise */
0x64, 0x65, 0x66, 0x67, 0x25, 0x1f, 0x1e,
/* power */
0x20
};
int scancode;
scancode = keycode;
if ((keycode >= 89) && (keycode < 89 + nitems(scan)))
scancode = scan[keycode - 89] | SCAN_PREFIX_E0;
/* pause/break */
if ((keycode == 104) && !(shift & CTLS))
scancode = 0x45 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL;
if (shift & SHIFTS)
scancode &= ~SCAN_PREFIX_SHIFT;
return (scancode | (up ? SCAN_RELEASE : SCAN_PRESS));
}
#endif
/* keyboard driver declaration */
static int akbd_configure(int flags);
static kbd_probe_t akbd_probe;
static kbd_init_t akbd_init;
static kbd_term_t akbd_term;
static kbd_intr_t akbd_interrupt;
static kbd_test_if_t akbd_test_if;
static kbd_enable_t akbd_enable;
static kbd_disable_t akbd_disable;
static kbd_read_t akbd_read;
static kbd_check_t akbd_check;
static kbd_read_char_t akbd_read_char;
static kbd_check_char_t akbd_check_char;
static kbd_ioctl_t akbd_ioctl;
static kbd_lock_t akbd_lock;
static kbd_clear_state_t akbd_clear_state;
static kbd_get_state_t akbd_get_state;
static kbd_set_state_t akbd_set_state;
static kbd_poll_mode_t akbd_poll;
keyboard_switch_t akbdsw = {
.probe = akbd_probe,
.init = akbd_init,
.term = akbd_term,
.intr = akbd_interrupt,
.test_if = akbd_test_if,
.enable = akbd_enable,
.disable = akbd_disable,
.read = akbd_read,
.check = akbd_check,
.read_char = akbd_read_char,
.check_char = akbd_check_char,
.ioctl = akbd_ioctl,
.lock = akbd_lock,
.clear_state = akbd_clear_state,
.get_state = akbd_get_state,
.set_state = akbd_set_state,
- .get_fkeystr = genkbd_get_fkeystr,
.poll = akbd_poll,
- .diag = genkbd_diag,
};
KEYBOARD_DRIVER(akbd, akbdsw, akbd_configure);
static int
adb_kbd_probe(device_t dev)
{
uint8_t type;
type = adb_get_device_type(dev);
if (type != ADB_DEVICE_KEYBOARD)
return (ENXIO);
switch(adb_get_device_handler(dev)) {
case 1:
device_set_desc(dev,"Apple Standard Keyboard");
break;
case 2:
device_set_desc(dev,"Apple Extended Keyboard");
break;
case 4:
device_set_desc(dev,"Apple ISO Keyboard");
break;
case 5:
device_set_desc(dev,"Apple Extended ISO Keyboard");
break;
case 8:
device_set_desc(dev,"Apple Keyboard II");
break;
case 9:
device_set_desc(dev,"Apple ISO Keyboard II");
break;
case 12:
device_set_desc(dev,"PowerBook Keyboard");
break;
case 13:
device_set_desc(dev,"PowerBook ISO Keyboard");
break;
case 24:
device_set_desc(dev,"PowerBook Extended Keyboard");
break;
case 27:
device_set_desc(dev,"Apple Design Keyboard");
break;
case 195:
device_set_desc(dev,"PowerBook G3 Keyboard");
break;
case 196:
device_set_desc(dev,"iBook Keyboard");
break;
default:
device_set_desc(dev,"ADB Keyboard");
break;
}
return (0);
}
static int
ms_to_ticks(int ms)
{
if (hz > 1000)
return ms*(hz/1000);
return ms/(1000/hz);
}
static int
adb_kbd_attach(device_t dev)
{
struct adb_kbd_softc *sc;
keyboard_switch_t *sw;
uint32_t fkeys;
phandle_t handle;
sw = kbd_get_switch(KBD_DRIVER_NAME);
if (sw == NULL) {
return ENXIO;
}
sc = device_get_softc(dev);
sc->sc_dev = dev;
sc->sc_mode = K_RAW;
sc->sc_state = 0;
sc->have_led_control = 0;
sc->buffers = 0;
/* Try stepping forward to the extended keyboard protocol */
adb_set_device_handler(dev,3);
mtx_init(&sc->sc_mutex, KBD_DRIVER_NAME, NULL, MTX_DEF);
cv_init(&sc->sc_cv,KBD_DRIVER_NAME);
callout_init(&sc->sc_repeater, 0);
#ifdef AKBD_EMULATE_ATKBD
kbd_init_struct(&sc->sc_kbd, KBD_DRIVER_NAME, KB_101, 0, 0, 0, 0);
kbd_set_maps(&sc->sc_kbd, &key_map, &accent_map, fkey_tab,
sizeof(fkey_tab) / sizeof(fkey_tab[0]));
#else
#error ADB raw mode not implemented
#endif
KBD_FOUND_DEVICE(&sc->sc_kbd);
KBD_PROBE_DONE(&sc->sc_kbd);
KBD_INIT_DONE(&sc->sc_kbd);
KBD_CONFIG_DONE(&sc->sc_kbd);
(*sw->enable)(&sc->sc_kbd);
kbd_register(&sc->sc_kbd);
#ifdef KBD_INSTALL_CDEV
if (kbd_attach(&sc->sc_kbd)) {
adb_kbd_detach(dev);
return ENXIO;
}
#endif
/* Check if we can read out the LED state from
this keyboard by reading the key state register */
if (adb_read_register(dev, 2, NULL) == 2)
sc->have_led_control = 1;
adb_set_autopoll(dev,1);
handle = OF_finddevice("mac-io/via-pmu/adb/keyboard");
if (handle != -1 && OF_getprop(handle, "AAPL,has-embedded-fn-keys",
&fkeys, sizeof(fkeys)) != -1) {
static const char *key_names[] = {"F1", "F2", "F3", "F4", "F5",
"F6", "F7", "F8", "F9", "F10", "F11", "F12"};
struct sysctl_ctx_list *ctx;
struct sysctl_oid *tree;
int i;
if (bootverbose)
device_printf(dev, "Keyboard has embedded Fn keys\n");
for (i = 0; i < 12; i++) {
uint32_t keyval;
char buf[3];
if (OF_getprop(handle, key_names[i], &keyval,
sizeof(keyval)) < 0)
continue;
buf[0] = 1;
buf[1] = i+1;
buf[2] = keyval;
adb_write_register(dev, 0, 3, buf);
}
adb_write_register(dev, 1, 2, &(uint16_t){0});
ctx = device_get_sysctl_ctx(dev);
tree = device_get_sysctl_tree(dev);
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"fn_keys_function_as_primary", CTLTYPE_INT | CTLFLAG_RW, sc,
0, adb_fn_keys, "I",
"Set the Fn keys to be their F-key type as default");
}
return (0);
}
static int
adb_kbd_detach(device_t dev)
{
struct adb_kbd_softc *sc;
keyboard_t *kbd;
sc = device_get_softc(dev);
adb_set_autopoll(dev,0);
callout_stop(&sc->sc_repeater);
mtx_lock(&sc->sc_mutex);
kbd = kbd_get_keyboard(kbd_find_keyboard(KBD_DRIVER_NAME,
device_get_unit(dev)));
kbdd_disable(kbd);
#ifdef KBD_INSTALL_CDEV
kbd_detach(kbd);
#endif
kbdd_term(kbd);
mtx_unlock(&sc->sc_mutex);
mtx_destroy(&sc->sc_mutex);
cv_destroy(&sc->sc_cv);
return (0);
}
static u_int
adb_kbd_receive_packet(device_t dev, u_char status,
u_char command, u_char reg, int len, u_char *data)
{
struct adb_kbd_softc *sc;
sc = device_get_softc(dev);
if (command != ADB_COMMAND_TALK)
return 0;
if (reg != 0 || len != 2)
return (0);
mtx_lock(&sc->sc_mutex);
/* 0x7f is always the power button */
if (data[0] == 0x7f) {
devctl_notify("PMU", "Button", "pressed", NULL);
mtx_unlock(&sc->sc_mutex);
return (0);
} else if (data[0] == 0xff) {
mtx_unlock(&sc->sc_mutex);
return (0); /* Ignore power button release. */
}
if ((data[0] & 0x7f) == 57 && sc->buffers < 7) {
/* Fake the down/up cycle for caps lock */
sc->buffer[sc->buffers++] = data[0] & 0x7f;
sc->buffer[sc->buffers++] = (data[0] & 0x7f) | (1 << 7);
} else {
sc->buffer[sc->buffers++] = data[0];
}
if (sc->buffer[sc->buffers-1] < 0xff)
sc->last_press = sc->buffer[sc->buffers-1];
if ((data[1] & 0x7f) == 57 && sc->buffers < 7) {
/* Fake the down/up cycle for caps lock */
sc->buffer[sc->buffers++] = data[1] & 0x7f;
sc->buffer[sc->buffers++] = (data[1] & 0x7f) | (1 << 7);
} else {
sc->buffer[sc->buffers++] = data[1];
}
if (sc->buffer[sc->buffers-1] < 0xff)
sc->last_press = sc->buffer[sc->buffers-1];
/* Stop any existing key repeating */
callout_stop(&sc->sc_repeater);
/* Schedule a repeat callback on keydown */
if (!(sc->last_press & (1 << 7))) {
callout_reset(&sc->sc_repeater,
ms_to_ticks(sc->sc_kbd.kb_delay1), akbd_repeat, sc);
}
mtx_unlock(&sc->sc_mutex);
cv_broadcast(&sc->sc_cv);
if (KBD_IS_ACTIVE(&sc->sc_kbd) && KBD_IS_BUSY(&sc->sc_kbd)) {
sc->sc_kbd.kb_callback.kc_func(&sc->sc_kbd,
KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg);
}
return (0);
}
static void
akbd_repeat(void *xsc) {
struct adb_kbd_softc *sc = xsc;
int notify_kbd = 0;
/* Fake an up/down key repeat so long as we have the
free buffers */
mtx_lock(&sc->sc_mutex);
if (sc->buffers < 7) {
sc->buffer[sc->buffers++] = sc->last_press | (1 << 7);
sc->buffer[sc->buffers++] = sc->last_press;
notify_kbd = 1;
}
mtx_unlock(&sc->sc_mutex);
if (notify_kbd && KBD_IS_ACTIVE(&sc->sc_kbd)
&& KBD_IS_BUSY(&sc->sc_kbd)) {
sc->sc_kbd.kb_callback.kc_func(&sc->sc_kbd,
KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg);
}
/* Reschedule the callout */
callout_reset(&sc->sc_repeater, ms_to_ticks(sc->sc_kbd.kb_delay2),
akbd_repeat, sc);
}
static int
akbd_configure(int flags)
{
return 0;
}
static int
akbd_probe(int unit, void *arg, int flags)
{
return 0;
}
static int
akbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
{
return 0;
}
static int
akbd_term(keyboard_t *kbd)
{
return 0;
}
static int
akbd_interrupt(keyboard_t *kbd, void *arg)
{
return 0;
}
static int
akbd_test_if(keyboard_t *kbd)
{
return 0;
}
static int
akbd_enable(keyboard_t *kbd)
{
KBD_ACTIVATE(kbd);
return (0);
}
static int
akbd_disable(keyboard_t *kbd)
{
struct adb_kbd_softc *sc;
sc = (struct adb_kbd_softc *)(kbd);
callout_stop(&sc->sc_repeater);
KBD_DEACTIVATE(kbd);
return (0);
}
static int
akbd_read(keyboard_t *kbd, int wait)
{
return (0);
}
static int
akbd_check(keyboard_t *kbd)
{
struct adb_kbd_softc *sc;
if (!KBD_IS_ACTIVE(kbd))
return (FALSE);
sc = (struct adb_kbd_softc *)(kbd);
mtx_lock(&sc->sc_mutex);
#ifdef AKBD_EMULATE_ATKBD
if (sc->at_buffered_char[0]) {
mtx_unlock(&sc->sc_mutex);
return (TRUE);
}
#endif
if (sc->buffers > 0) {
mtx_unlock(&sc->sc_mutex);
return (TRUE);
}
mtx_unlock(&sc->sc_mutex);
return (FALSE);
}
static u_int
akbd_read_char(keyboard_t *kbd, int wait)
{
struct adb_kbd_softc *sc;
uint16_t key;
uint8_t adb_code;
int i;
sc = (struct adb_kbd_softc *)(kbd);
mtx_lock(&sc->sc_mutex);
#if defined(AKBD_EMULATE_ATKBD)
if (sc->sc_mode == K_RAW && sc->at_buffered_char[0]) {
key = sc->at_buffered_char[0];
if (key & SCAN_PREFIX) {
sc->at_buffered_char[0] = key & ~SCAN_PREFIX;
key = (key & SCAN_PREFIX_E0) ? 0xe0 : 0xe1;
} else {
sc->at_buffered_char[0] = sc->at_buffered_char[1];
sc->at_buffered_char[1] = 0;
}
mtx_unlock(&sc->sc_mutex);
return (key);
}
#endif
if (!sc->buffers && wait)
cv_wait(&sc->sc_cv,&sc->sc_mutex);
if (!sc->buffers) {
mtx_unlock(&sc->sc_mutex);
return (NOKEY);
}
adb_code = sc->buffer[0];
for (i = 1; i < sc->buffers; i++)
sc->buffer[i-1] = sc->buffer[i];
sc->buffers--;
#ifdef AKBD_EMULATE_ATKBD
key = adb_to_at_scancode_map[adb_code & 0x7f];
if (sc->sc_mode == K_CODE) {
/* Add the key-release bit */
key |= adb_code & 0x80;
} else if (sc->sc_mode == K_RAW) {
/*
* In the raw case, we have to emulate the gross
* variable-length AT keyboard thing. Since this code
* is copied from sunkbd, which is the same code
* as ukbd, it might be nice to have this centralized.
*/
key = keycode2scancode(key,
0, adb_code & 0x80);
if (key & SCAN_PREFIX) {
if (key & SCAN_PREFIX_CTL) {
sc->at_buffered_char[0] =
0x1d | (key & SCAN_RELEASE);
sc->at_buffered_char[1] =
key & ~SCAN_PREFIX;
} else if (key & SCAN_PREFIX_SHIFT) {
sc->at_buffered_char[0] =
0x2a | (key & SCAN_RELEASE);
sc->at_buffered_char[1] =
key & ~SCAN_PREFIX_SHIFT;
} else {
sc->at_buffered_char[0] =
key & ~SCAN_PREFIX;
sc->at_buffered_char[1] = 0;
}
key = (key & SCAN_PREFIX_E0) ? 0xe0 : 0xe1;
}
}
#else
key = adb_code;
#endif
mtx_unlock(&sc->sc_mutex);
return (key);
}
static int
akbd_check_char(keyboard_t *kbd)
{
if (!KBD_IS_ACTIVE(kbd))
return (FALSE);
return (akbd_check(kbd));
}
static int
set_typematic(keyboard_t *kbd, int code)
{
/* These numbers are in microseconds, so convert to ticks */
static int delays[] = { 250, 500, 750, 1000 };
static int rates[] = { 34, 38, 42, 46, 50, 55, 59, 63,
68, 76, 84, 92, 100, 110, 118, 126,
136, 152, 168, 184, 200, 220, 236, 252,
272, 304, 336, 368, 400, 440, 472, 504 };
if (code & ~0x7f)
return EINVAL;
kbd->kb_delay1 = delays[(code >> 5) & 3];
kbd->kb_delay2 = rates[code & 0x1f];
return 0;
}
static int akbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t data)
{
struct adb_kbd_softc *sc;
uint16_t r2;
int error;
sc = (struct adb_kbd_softc *)(kbd);
error = 0;
switch (cmd) {
case KDGKBMODE:
*(int *)data = sc->sc_mode;
break;
case KDSKBMODE:
switch (*(int *)data) {
case K_XLATE:
if (sc->sc_mode != K_XLATE) {
/* make lock key state and LED state match */
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= KBD_LED_VAL(kbd);
}
/* FALLTHROUGH */
case K_RAW:
case K_CODE:
if (sc->sc_mode != *(int *)data)
sc->sc_mode = *(int *)data;
break;
default:
error = EINVAL;
break;
}
break;
case KDGETLED:
*(int *)data = KBD_LED_VAL(kbd);
break;
case KDSKBSTATE:
if (*(int *)data & ~LOCK_MASK) {
error = EINVAL;
break;
}
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= *(int *)data;
/* FALLTHROUGH */
case KDSETLED:
KBD_LED_VAL(kbd) = *(int *)data;
if (!sc->have_led_control)
break;
r2 = (~0 & 0x04) | 3;
if (*(int *)data & NLKED)
r2 &= ~1;
if (*(int *)data & CLKED)
r2 &= ~2;
if (*(int *)data & SLKED)
r2 &= ~4;
adb_send_packet(sc->sc_dev,ADB_COMMAND_LISTEN,2,
sizeof(uint16_t),(u_char *)&r2);
break;
case KDGKBSTATE:
*(int *)data = sc->sc_state & LOCK_MASK;
break;
case KDSETREPEAT:
if (!KBD_HAS_DEVICE(kbd))
return 0;
if (((int *)data)[1] < 0)
return EINVAL;
if (((int *)data)[0] < 0)
return EINVAL;
else if (((int *)data)[0] == 0) /* fastest possible value */
kbd->kb_delay1 = 200;
else
kbd->kb_delay1 = ((int *)data)[0];
kbd->kb_delay2 = ((int *)data)[1];
break;
case KDSETRAD:
error = set_typematic(kbd, *(int *)data);
break;
case PIO_KEYMAP:
case OPIO_KEYMAP:
case PIO_KEYMAPENT:
case PIO_DEADKEYMAP:
default:
return (genkbd_commonioctl(kbd, cmd, data));
}
return (error);
}
static int akbd_lock(keyboard_t *kbd, int lock)
{
return (0);
}
static void akbd_clear_state(keyboard_t *kbd)
{
struct adb_kbd_softc *sc;
sc = (struct adb_kbd_softc *)(kbd);
mtx_lock(&sc->sc_mutex);
sc->buffers = 0;
callout_stop(&sc->sc_repeater);
#if defined(AKBD_EMULATE_ATKBD)
sc->at_buffered_char[0] = 0;
sc->at_buffered_char[1] = 0;
#endif
mtx_unlock(&sc->sc_mutex);
}
static int akbd_get_state(keyboard_t *kbd, void *buf, size_t len)
{
return (0);
}
static int akbd_set_state(keyboard_t *kbd, void *buf, size_t len)
{
return (0);
}
static int akbd_poll(keyboard_t *kbd, int on)
{
return (0);
}
static int
akbd_modevent(module_t mod, int type, void *data)
{
switch (type) {
case MOD_LOAD:
kbd_add_driver(&akbd_kbd_driver);
break;
case MOD_UNLOAD:
kbd_delete_driver(&akbd_kbd_driver);
break;
default:
return (EOPNOTSUPP);
}
return (0);
}
static int
adb_fn_keys(SYSCTL_HANDLER_ARGS)
{
struct adb_kbd_softc *sc = arg1;
int error;
uint16_t is_fn_enabled;
unsigned int is_fn_enabled_sysctl;
adb_read_register(sc->sc_dev, 1, &is_fn_enabled);
is_fn_enabled &= 1;
is_fn_enabled_sysctl = is_fn_enabled;
error = sysctl_handle_int(oidp, &is_fn_enabled_sysctl, 0, req);
if (error || !req->newptr)
return (error);
is_fn_enabled = is_fn_enabled_sysctl;
if (is_fn_enabled != 1 && is_fn_enabled != 0)
return (EINVAL);
adb_write_register(sc->sc_dev, 1, 2, &is_fn_enabled);
return (0);
}
DEV_MODULE(akbd, akbd_modevent, NULL);
Index: stable/11/sys/dev/atkbdc/atkbd.c
===================================================================
--- stable/11/sys/dev/atkbdc/atkbd.c (revision 356012)
+++ stable/11/sys/dev/atkbdc/atkbd.c (revision 356013)
@@ -1,1602 +1,1600 @@
/*-
* Copyright (c) 1999 Kazutaka YOKOTA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer as
* the first lines of this file unmodified.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include
__FBSDID("$FreeBSD$");
#include "opt_compat.h"
#include "opt_kbd.h"
#include "opt_atkbd.h"
#include "opt_evdev.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef EVDEV_SUPPORT
#include
#include
#endif
typedef struct atkbd_state {
KBDC kbdc; /* keyboard controller */
int ks_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
int ks_flags; /* flags */
#define COMPOSE (1 << 0)
int ks_polling;
int ks_state; /* shift/lock key state */
int ks_accents; /* accent key index (> 0) */
u_int ks_composed_char; /* composed char code (> 0) */
u_char ks_prefix; /* AT scan code prefix */
struct callout ks_timer;
#ifdef EVDEV_SUPPORT
struct evdev_dev *ks_evdev;
int ks_evdev_state;
#endif
} atkbd_state_t;
static void atkbd_timeout(void *arg);
static void atkbd_shutdown_final(void *v);
static int atkbd_reset(KBDC kbdc, int flags, int c);
#define HAS_QUIRK(p, q) (((atkbdc_softc_t *)(p))->quirks & q)
#define ALLOW_DISABLE_KBD(kbdc) !HAS_QUIRK(kbdc, KBDC_QUIRK_KEEP_ACTIVATED)
#define DEFAULT_DELAY 0x1 /* 500ms */
#define DEFAULT_RATE 0x10 /* 14Hz */
#ifdef EVDEV_SUPPORT
#define PS2_KEYBOARD_VENDOR 1
#define PS2_KEYBOARD_PRODUCT 1
#endif
int
atkbd_probe_unit(device_t dev, int irq, int flags)
{
keyboard_switch_t *sw;
int args[2];
int error;
sw = kbd_get_switch(ATKBD_DRIVER_NAME);
if (sw == NULL)
return ENXIO;
args[0] = device_get_unit(device_get_parent(dev));
args[1] = irq;
error = (*sw->probe)(device_get_unit(dev), args, flags);
if (error)
return error;
return 0;
}
int
atkbd_attach_unit(device_t dev, keyboard_t **kbd, int irq, int flags)
{
keyboard_switch_t *sw;
atkbd_state_t *state;
int args[2];
int error;
int unit;
sw = kbd_get_switch(ATKBD_DRIVER_NAME);
if (sw == NULL)
return ENXIO;
/* reset, initialize and enable the device */
unit = device_get_unit(dev);
args[0] = device_get_unit(device_get_parent(dev));
args[1] = irq;
*kbd = NULL;
error = (*sw->probe)(unit, args, flags);
if (error)
return error;
error = (*sw->init)(unit, kbd, args, flags);
if (error)
return error;
(*sw->enable)(*kbd);
#ifdef KBD_INSTALL_CDEV
/* attach a virtual keyboard cdev */
error = kbd_attach(*kbd);
if (error)
return error;
#endif
/*
* This is a kludge to compensate for lost keyboard interrupts.
* A similar code used to be in syscons. See below. XXX
*/
state = (atkbd_state_t *)(*kbd)->kb_data;
callout_init(&state->ks_timer, 0);
atkbd_timeout(*kbd);
if (bootverbose)
(*sw->diag)(*kbd, bootverbose);
EVENTHANDLER_REGISTER(shutdown_final, atkbd_shutdown_final, *kbd,
SHUTDOWN_PRI_DEFAULT);
return 0;
}
static void
atkbd_timeout(void *arg)
{
atkbd_state_t *state;
keyboard_t *kbd;
int s;
/*
* The original text of the following comments are extracted
* from syscons.c (1.287)
*
* With release 2.1 of the Xaccel server, the keyboard is left
* hanging pretty often. Apparently an interrupt from the
* keyboard is lost, and I don't know why (yet).
* This ugly hack calls the low-level interrupt routine if input
* is ready for the keyboard and conveniently hides the problem. XXX
*
* Try removing anything stuck in the keyboard controller; whether
* it's a keyboard scan code or mouse data. The low-level
* interrupt routine doesn't read the mouse data directly,
* but the keyboard controller driver will, as a side effect.
*/
/*
* And here is bde's original comment about this:
*
* This is necessary to handle edge triggered interrupts - if we
* returned when our IRQ is high due to unserviced input, then there
* would be no more keyboard IRQs until the keyboard is reset by
* external powers.
*
* The keyboard apparently unwedges the irq in most cases.
*/
s = spltty();
kbd = (keyboard_t *)arg;
if (kbdd_lock(kbd, TRUE)) {
/*
* We have seen the lock flag is not set. Let's reset
* the flag early, otherwise the LED update routine fails
* which may want the lock during the interrupt routine.
*/
kbdd_lock(kbd, FALSE);
if (kbdd_check_char(kbd))
kbdd_intr(kbd, NULL);
}
splx(s);
state = (atkbd_state_t *)kbd->kb_data;
callout_reset(&state->ks_timer, hz / 10, atkbd_timeout, arg);
}
/* LOW-LEVEL */
#define ATKBD_DEFAULT 0
/* keyboard driver declaration */
static int atkbd_configure(int flags);
static kbd_probe_t atkbd_probe;
static kbd_init_t atkbd_init;
static kbd_term_t atkbd_term;
static kbd_intr_t atkbd_intr;
static kbd_test_if_t atkbd_test_if;
static kbd_enable_t atkbd_enable;
static kbd_disable_t atkbd_disable;
static kbd_read_t atkbd_read;
static kbd_check_t atkbd_check;
static kbd_read_char_t atkbd_read_char;
static kbd_check_char_t atkbd_check_char;
static kbd_ioctl_t atkbd_ioctl;
static kbd_lock_t atkbd_lock;
static kbd_clear_state_t atkbd_clear_state;
static kbd_get_state_t atkbd_get_state;
static kbd_set_state_t atkbd_set_state;
static kbd_poll_mode_t atkbd_poll;
static keyboard_switch_t atkbdsw = {
.probe = atkbd_probe,
.init = atkbd_init,
.term = atkbd_term,
.intr = atkbd_intr,
.test_if = atkbd_test_if,
.enable = atkbd_enable,
.disable = atkbd_disable,
.read = atkbd_read,
.check = atkbd_check,
.read_char = atkbd_read_char,
.check_char = atkbd_check_char,
.ioctl = atkbd_ioctl,
.lock = atkbd_lock,
.clear_state = atkbd_clear_state,
.get_state = atkbd_get_state,
.set_state = atkbd_set_state,
- .get_fkeystr = genkbd_get_fkeystr,
.poll = atkbd_poll,
- .diag = genkbd_diag,
};
KEYBOARD_DRIVER(atkbd, atkbdsw, atkbd_configure);
/* local functions */
static int set_typematic(keyboard_t *kbd);
static int setup_kbd_port(KBDC kbdc, int port, int intr);
static int get_kbd_echo(KBDC kbdc);
static int probe_keyboard(KBDC kbdc, int flags);
static int init_keyboard(KBDC kbdc, int *type, int flags);
static int write_kbd(KBDC kbdc, int command, int data);
static int get_kbd_id(KBDC kbdc);
static int typematic(int delay, int rate);
static int typematic_delay(int delay);
static int typematic_rate(int rate);
#ifdef EVDEV_SUPPORT
static const struct evdev_methods atkbd_evdev_methods = {
.ev_event = evdev_ev_kbd_event,
};
#endif
/* local variables */
/* the initial key map, accent map and fkey strings */
#ifdef ATKBD_DFLT_KEYMAP
#define KBD_DFLT_KEYMAP
#include "atkbdmap.h"
#endif
#include
/* structures for the default keyboard */
static keyboard_t default_kbd;
static atkbd_state_t default_kbd_state;
static keymap_t default_keymap;
static accentmap_t default_accentmap;
static fkeytab_t default_fkeytab[NUM_FKEYS];
/*
* The back door to the keyboard driver!
* This function is called by the console driver, via the kbdio module,
* to tickle keyboard drivers when the low-level console is being initialized.
* Almost nothing in the kernel has been initialied yet. Try to probe
* keyboards if possible.
* NOTE: because of the way the low-level console is initialized, this routine
* may be called more than once!!
*/
static int
atkbd_configure(int flags)
{
keyboard_t *kbd;
int arg[2];
int i;
/*
* Probe the keyboard controller, if not present or if the driver
* is disabled, unregister the keyboard if any.
*/
if (atkbdc_configure() != 0 ||
resource_disabled("atkbd", ATKBD_DEFAULT)) {
i = kbd_find_keyboard(ATKBD_DRIVER_NAME, ATKBD_DEFAULT);
if (i >= 0) {
kbd = kbd_get_keyboard(i);
kbd_unregister(kbd);
kbd->kb_flags &= ~KB_REGISTERED;
}
return 0;
}
/* XXX: a kludge to obtain the device configuration flags */
if (resource_int_value("atkbd", ATKBD_DEFAULT, "flags", &i) == 0)
flags |= i;
/* probe the default keyboard */
arg[0] = -1;
arg[1] = -1;
kbd = NULL;
if (atkbd_probe(ATKBD_DEFAULT, arg, flags))
return 0;
if (atkbd_init(ATKBD_DEFAULT, &kbd, arg, flags))
return 0;
/* return the number of found keyboards */
return 1;
}
/* low-level functions */
/* detect a keyboard */
static int
atkbd_probe(int unit, void *arg, int flags)
{
KBDC kbdc;
int *data = (int *)arg; /* data[0]: controller, data[1]: irq */
/* XXX */
if (unit == ATKBD_DEFAULT) {
if (KBD_IS_PROBED(&default_kbd))
return 0;
}
kbdc = atkbdc_open(data[0]);
if (kbdc == NULL)
return ENXIO;
if (probe_keyboard(kbdc, flags)) {
if (flags & KB_CONF_FAIL_IF_NO_KBD)
return ENXIO;
}
return 0;
}
/* reset and initialize the device */
static int
atkbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
{
keyboard_t *kbd;
atkbd_state_t *state;
keymap_t *keymap;
accentmap_t *accmap;
fkeytab_t *fkeymap;
int fkeymap_size;
int delay[2];
int *data = (int *)arg; /* data[0]: controller, data[1]: irq */
int error, needfree;
#ifdef EVDEV_SUPPORT
struct evdev_dev *evdev;
char phys_loc[8];
#endif
/* XXX */
if (unit == ATKBD_DEFAULT) {
*kbdp = kbd = &default_kbd;
if (KBD_IS_INITIALIZED(kbd) && KBD_IS_CONFIGURED(kbd))
return 0;
state = &default_kbd_state;
keymap = &default_keymap;
accmap = &default_accentmap;
fkeymap = default_fkeytab;
fkeymap_size = nitems(default_fkeytab);
needfree = 0;
} else if (*kbdp == NULL) {
*kbdp = kbd = malloc(sizeof(*kbd), M_DEVBUF, M_NOWAIT | M_ZERO);
state = malloc(sizeof(*state), M_DEVBUF, M_NOWAIT | M_ZERO);
/* NB: these will always be initialized 'cuz !KBD_IS_PROBED */
keymap = malloc(sizeof(key_map), M_DEVBUF, M_NOWAIT);
accmap = malloc(sizeof(accent_map), M_DEVBUF, M_NOWAIT);
fkeymap = malloc(sizeof(fkey_tab), M_DEVBUF, M_NOWAIT);
fkeymap_size = sizeof(fkey_tab)/sizeof(fkey_tab[0]);
needfree = 1;
if ((kbd == NULL) || (state == NULL) || (keymap == NULL)
|| (accmap == NULL) || (fkeymap == NULL)) {
error = ENOMEM;
goto bad;
}
} else if (KBD_IS_INITIALIZED(*kbdp) && KBD_IS_CONFIGURED(*kbdp)) {
return 0;
} else {
kbd = *kbdp;
state = (atkbd_state_t *)kbd->kb_data;
bzero(state, sizeof(*state));
keymap = kbd->kb_keymap;
accmap = kbd->kb_accentmap;
fkeymap = kbd->kb_fkeytab;
fkeymap_size = kbd->kb_fkeytab_size;
needfree = 0;
}
if (!KBD_IS_PROBED(kbd)) {
state->kbdc = atkbdc_open(data[0]);
if (state->kbdc == NULL) {
error = ENXIO;
goto bad;
}
kbd_init_struct(kbd, ATKBD_DRIVER_NAME, KB_OTHER, unit, flags,
0, 0);
bcopy(&key_map, keymap, sizeof(key_map));
bcopy(&accent_map, accmap, sizeof(accent_map));
bcopy(fkey_tab, fkeymap,
imin(fkeymap_size * sizeof(fkeymap[0]), sizeof(fkey_tab)));
kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size);
kbd->kb_data = (void *)state;
if (probe_keyboard(state->kbdc, flags)) { /* shouldn't happen */
if (flags & KB_CONF_FAIL_IF_NO_KBD) {
error = ENXIO;
goto bad;
}
} else {
KBD_FOUND_DEVICE(kbd);
}
atkbd_clear_state(kbd);
state->ks_mode = K_XLATE;
/*
* FIXME: set the initial value for lock keys in ks_state
* according to the BIOS data?
*/
KBD_PROBE_DONE(kbd);
}
if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) {
kbd->kb_config = flags & ~KB_CONF_PROBE_ONLY;
if (KBD_HAS_DEVICE(kbd)
&& init_keyboard(state->kbdc, &kbd->kb_type, kbd->kb_config)
&& (kbd->kb_config & KB_CONF_FAIL_IF_NO_KBD)) {
kbd_unregister(kbd);
error = ENXIO;
goto bad;
}
atkbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state);
set_typematic(kbd);
delay[0] = kbd->kb_delay1;
delay[1] = kbd->kb_delay2;
atkbd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay);
#ifdef EVDEV_SUPPORT
/* register as evdev provider on first init */
if (state->ks_evdev == NULL) {
snprintf(phys_loc, sizeof(phys_loc), "atkbd%d", unit);
evdev = evdev_alloc();
evdev_set_name(evdev, "AT keyboard");
evdev_set_phys(evdev, phys_loc);
evdev_set_id(evdev, BUS_I8042, PS2_KEYBOARD_VENDOR,
PS2_KEYBOARD_PRODUCT, 0);
evdev_set_methods(evdev, kbd, &atkbd_evdev_methods);
evdev_support_event(evdev, EV_SYN);
evdev_support_event(evdev, EV_KEY);
evdev_support_event(evdev, EV_LED);
evdev_support_event(evdev, EV_REP);
evdev_support_all_known_keys(evdev);
evdev_support_led(evdev, LED_NUML);
evdev_support_led(evdev, LED_CAPSL);
evdev_support_led(evdev, LED_SCROLLL);
if (evdev_register(evdev))
evdev_free(evdev);
else
state->ks_evdev = evdev;
state->ks_evdev_state = 0;
}
#endif
KBD_INIT_DONE(kbd);
}
if (!KBD_IS_CONFIGURED(kbd)) {
if (kbd_register(kbd) < 0) {
error = ENXIO;
goto bad;
}
KBD_CONFIG_DONE(kbd);
}
return 0;
bad:
if (needfree) {
if (state != NULL)
free(state, M_DEVBUF);
if (keymap != NULL)
free(keymap, M_DEVBUF);
if (accmap != NULL)
free(accmap, M_DEVBUF);
if (fkeymap != NULL)
free(fkeymap, M_DEVBUF);
if (kbd != NULL) {
free(kbd, M_DEVBUF);
*kbdp = NULL; /* insure ref doesn't leak to caller */
}
}
return error;
}
/* finish using this keyboard */
static int
atkbd_term(keyboard_t *kbd)
{
atkbd_state_t *state = (atkbd_state_t *)kbd->kb_data;
kbd_unregister(kbd);
callout_drain(&state->ks_timer);
return 0;
}
/* keyboard interrupt routine */
static int
atkbd_intr(keyboard_t *kbd, void *arg)
{
atkbd_state_t *state = (atkbd_state_t *)kbd->kb_data;
int delay[2];
int c;
if (!KBD_HAS_DEVICE(kbd)) {
/*
* The keyboard was not detected before;
* it must have been reconnected!
*/
init_keyboard(state->kbdc, &kbd->kb_type, kbd->kb_config);
KBD_FOUND_DEVICE(kbd);
atkbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state);
set_typematic(kbd);
delay[0] = kbd->kb_delay1;
delay[1] = kbd->kb_delay2;
atkbd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay);
}
if (state->ks_polling)
return 0;
if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) {
/* let the callback function to process the input */
(*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT,
kbd->kb_callback.kc_arg);
} else {
/* read and discard the input; no one is waiting for input */
do {
c = atkbd_read_char(kbd, FALSE);
} while (c != NOKEY);
}
return 0;
}
/* test the interface to the device */
static int
atkbd_test_if(keyboard_t *kbd)
{
int error;
int s;
error = 0;
empty_both_buffers(((atkbd_state_t *)kbd->kb_data)->kbdc, 10);
s = spltty();
if (!test_controller(((atkbd_state_t *)kbd->kb_data)->kbdc))
error = EIO;
else if (test_kbd_port(((atkbd_state_t *)kbd->kb_data)->kbdc) != 0)
error = EIO;
splx(s);
return error;
}
/*
* Enable the access to the device; until this function is called,
* the client cannot read from the keyboard.
*/
static int
atkbd_enable(keyboard_t *kbd)
{
int s;
s = spltty();
KBD_ACTIVATE(kbd);
splx(s);
return 0;
}
/* disallow the access to the device */
static int
atkbd_disable(keyboard_t *kbd)
{
int s;
s = spltty();
KBD_DEACTIVATE(kbd);
splx(s);
return 0;
}
/* read one byte from the keyboard if it's allowed */
static int
atkbd_read(keyboard_t *kbd, int wait)
{
int c;
if (wait)
c = read_kbd_data(((atkbd_state_t *)kbd->kb_data)->kbdc);
else
c = read_kbd_data_no_wait(((atkbd_state_t *)kbd->kb_data)->kbdc);
if (c != -1)
++kbd->kb_count;
return (KBD_IS_ACTIVE(kbd) ? c : -1);
}
/* check if data is waiting */
static int
atkbd_check(keyboard_t *kbd)
{
if (!KBD_IS_ACTIVE(kbd))
return FALSE;
return kbdc_data_ready(((atkbd_state_t *)kbd->kb_data)->kbdc);
}
/* read char from the keyboard */
static u_int
atkbd_read_char(keyboard_t *kbd, int wait)
{
atkbd_state_t *state;
u_int action;
int scancode;
int keycode;
state = (atkbd_state_t *)kbd->kb_data;
next_code:
/* do we have a composed char to return? */
if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) {
action = state->ks_composed_char;
state->ks_composed_char = 0;
if (action > UCHAR_MAX)
return ERRKEY;
return action;
}
/* see if there is something in the keyboard port */
if (wait) {
do {
scancode = read_kbd_data(state->kbdc);
} while (scancode == -1);
} else {
scancode = read_kbd_data_no_wait(state->kbdc);
if (scancode == -1)
return NOKEY;
}
++kbd->kb_count;
#if KBDIO_DEBUG >= 10
printf("atkbd_read_char(): scancode:0x%x\n", scancode);
#endif
#ifdef EVDEV_SUPPORT
/* push evdev event */
if (evdev_rcpt_mask & EVDEV_RCPT_HW_KBD && state->ks_evdev != NULL) {
keycode = evdev_scancode2key(&state->ks_evdev_state,
scancode);
if (keycode != KEY_RESERVED) {
evdev_push_event(state->ks_evdev, EV_KEY,
(uint16_t)keycode, scancode & 0x80 ? 0 : 1);
evdev_sync(state->ks_evdev);
}
}
#endif
/* return the byte as is for the K_RAW mode */
if (state->ks_mode == K_RAW)
return scancode;
/* translate the scan code into a keycode */
keycode = scancode & 0x7F;
switch (state->ks_prefix) {
case 0x00: /* normal scancode */
switch(scancode) {
case 0xB8: /* left alt (compose key) released */
if (state->ks_flags & COMPOSE) {
state->ks_flags &= ~COMPOSE;
if (state->ks_composed_char > UCHAR_MAX)
state->ks_composed_char = 0;
}
break;
case 0x38: /* left alt (compose key) pressed */
if (!(state->ks_flags & COMPOSE)) {
state->ks_flags |= COMPOSE;
state->ks_composed_char = 0;
}
break;
case 0xE0:
case 0xE1:
state->ks_prefix = scancode;
goto next_code;
}
break;
case 0xE0: /* 0xE0 prefix */
state->ks_prefix = 0;
switch (keycode) {
case 0x1C: /* right enter key */
keycode = 0x59;
break;
case 0x1D: /* right ctrl key */
keycode = 0x5A;
break;
case 0x35: /* keypad divide key */
keycode = 0x5B;
break;
case 0x37: /* print scrn key */
keycode = 0x5C;
break;
case 0x38: /* right alt key (alt gr) */
keycode = 0x5D;
break;
case 0x46: /* ctrl-pause/break on AT 101 (see below) */
keycode = 0x68;
break;
case 0x47: /* grey home key */
keycode = 0x5E;
break;
case 0x48: /* grey up arrow key */
keycode = 0x5F;
break;
case 0x49: /* grey page up key */
keycode = 0x60;
break;
case 0x4B: /* grey left arrow key */
keycode = 0x61;
break;
case 0x4D: /* grey right arrow key */
keycode = 0x62;
break;
case 0x4F: /* grey end key */
keycode = 0x63;
break;
case 0x50: /* grey down arrow key */
keycode = 0x64;
break;
case 0x51: /* grey page down key */
keycode = 0x65;
break;
case 0x52: /* grey insert key */
keycode = 0x66;
break;
case 0x53: /* grey delete key */
keycode = 0x67;
break;
/* the following 3 are only used on the MS "Natural" keyboard */
case 0x5b: /* left Window key */
keycode = 0x69;
break;
case 0x5c: /* right Window key */
keycode = 0x6a;
break;
case 0x5d: /* menu key */
keycode = 0x6b;
break;
case 0x5e: /* power key */
keycode = 0x6d;
break;
case 0x5f: /* sleep key */
keycode = 0x6e;
break;
case 0x63: /* wake key */
keycode = 0x6f;
break;
default: /* ignore everything else */
goto next_code;
}
break;
case 0xE1: /* 0xE1 prefix */
/*
* The pause/break key on the 101 keyboard produces:
* E1-1D-45 E1-9D-C5
* Ctrl-pause/break produces:
* E0-46 E0-C6 (See above.)
*/
state->ks_prefix = 0;
if (keycode == 0x1D)
state->ks_prefix = 0x1D;
goto next_code;
/* NOT REACHED */
case 0x1D: /* pause / break */
state->ks_prefix = 0;
if (keycode != 0x45)
goto next_code;
keycode = 0x68;
break;
}
if (kbd->kb_type == KB_84) {
switch (keycode) {
case 0x37: /* *(numpad)/print screen */
if (state->ks_flags & SHIFTS)
keycode = 0x5c; /* print screen */
break;
case 0x45: /* num lock/pause */
if (state->ks_flags & CTLS)
keycode = 0x68; /* pause */
break;
case 0x46: /* scroll lock/break */
if (state->ks_flags & CTLS)
keycode = 0x6c; /* break */
break;
}
} else if (kbd->kb_type == KB_101) {
switch (keycode) {
case 0x5c: /* print screen */
if (state->ks_flags & ALTS)
keycode = 0x54; /* sysrq */
break;
case 0x68: /* pause/break */
if (state->ks_flags & CTLS)
keycode = 0x6c; /* break */
break;
}
}
/* return the key code in the K_CODE mode */
if (state->ks_mode == K_CODE)
return (keycode | (scancode & 0x80));
/* compose a character code */
if (state->ks_flags & COMPOSE) {
switch (keycode | (scancode & 0x80)) {
/* key pressed, process it */
case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */
state->ks_composed_char *= 10;
state->ks_composed_char += keycode - 0x40;
if (state->ks_composed_char > UCHAR_MAX)
return ERRKEY;
goto next_code;
case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */
state->ks_composed_char *= 10;
state->ks_composed_char += keycode - 0x47;
if (state->ks_composed_char > UCHAR_MAX)
return ERRKEY;
goto next_code;
case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */
state->ks_composed_char *= 10;
state->ks_composed_char += keycode - 0x4E;
if (state->ks_composed_char > UCHAR_MAX)
return ERRKEY;
goto next_code;
case 0x52: /* keypad 0 */
state->ks_composed_char *= 10;
if (state->ks_composed_char > UCHAR_MAX)
return ERRKEY;
goto next_code;
/* key released, no interest here */
case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */
case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */
case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */
case 0xD2: /* keypad 0 */
goto next_code;
case 0x38: /* left alt key */
break;
default:
if (state->ks_composed_char > 0) {
state->ks_flags &= ~COMPOSE;
state->ks_composed_char = 0;
return ERRKEY;
}
break;
}
}
/* keycode to key action */
action = genkbd_keyaction(kbd, keycode, scancode & 0x80,
&state->ks_state, &state->ks_accents);
if (action == NOKEY)
goto next_code;
else
return action;
}
/* check if char is waiting */
static int
atkbd_check_char(keyboard_t *kbd)
{
atkbd_state_t *state;
if (!KBD_IS_ACTIVE(kbd))
return FALSE;
state = (atkbd_state_t *)kbd->kb_data;
if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0))
return TRUE;
return kbdc_data_ready(state->kbdc);
}
/* some useful control functions */
static int
atkbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
/* translate LED_XXX bits into the device specific bits */
static u_char ledmap[8] = {
0, 4, 2, 6, 1, 5, 3, 7,
};
atkbd_state_t *state = kbd->kb_data;
int error;
int s;
int i;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
int ival;
#endif
s = spltty();
switch (cmd) {
case KDGKBMODE: /* get keyboard mode */
*(int *)arg = state->ks_mode;
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 7):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBMODE: /* set keyboard mode */
switch (*(int *)arg) {
case K_XLATE:
if (state->ks_mode != K_XLATE) {
/* make lock key state and LED state match */
state->ks_state &= ~LOCK_MASK;
state->ks_state |= KBD_LED_VAL(kbd);
}
/* FALLTHROUGH */
case K_RAW:
case K_CODE:
if (state->ks_mode != *(int *)arg) {
atkbd_clear_state(kbd);
state->ks_mode = *(int *)arg;
}
break;
default:
splx(s);
return EINVAL;
}
break;
case KDGETLED: /* get keyboard LED */
*(int *)arg = KBD_LED_VAL(kbd);
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 66):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETLED: /* set keyboard LED */
/* NOTE: lock key state in ks_state won't be changed */
if (*(int *)arg & ~LOCK_MASK) {
splx(s);
return EINVAL;
}
i = *(int *)arg;
/* replace CAPS LED with ALTGR LED for ALTGR keyboards */
if (state->ks_mode == K_XLATE &&
kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
if (i & ALKED)
i |= CLKED;
else
i &= ~CLKED;
}
if (KBD_HAS_DEVICE(kbd)) {
error = write_kbd(state->kbdc, KBDC_SET_LEDS,
ledmap[i & LED_MASK]);
if (error) {
splx(s);
return error;
}
}
#ifdef EVDEV_SUPPORT
/* push LED states to evdev */
if (state->ks_evdev != NULL &&
evdev_rcpt_mask & EVDEV_RCPT_HW_KBD)
evdev_push_leds(state->ks_evdev, *(int *)arg);
#endif
KBD_LED_VAL(kbd) = *(int *)arg;
break;
case KDGKBSTATE: /* get lock key state */
*(int *)arg = state->ks_state & LOCK_MASK;
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 20):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBSTATE: /* set lock key state */
if (*(int *)arg & ~LOCK_MASK) {
splx(s);
return EINVAL;
}
state->ks_state &= ~LOCK_MASK;
state->ks_state |= *(int *)arg;
splx(s);
/* set LEDs and quit */
return atkbd_ioctl(kbd, KDSETLED, arg);
case KDSETREPEAT: /* set keyboard repeat rate (new interface) */
splx(s);
if (!KBD_HAS_DEVICE(kbd))
return 0;
i = typematic(((int *)arg)[0], ((int *)arg)[1]);
error = write_kbd(state->kbdc, KBDC_SET_TYPEMATIC, i);
if (error == 0) {
kbd->kb_delay1 = typematic_delay(i);
kbd->kb_delay2 = typematic_rate(i);
#ifdef EVDEV_SUPPORT
if (state->ks_evdev != NULL &&
evdev_rcpt_mask & EVDEV_RCPT_HW_KBD)
evdev_push_repeats(state->ks_evdev, kbd);
#endif
}
return error;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 67):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETRAD: /* set keyboard repeat rate (old interface) */
splx(s);
if (!KBD_HAS_DEVICE(kbd))
return 0;
error = write_kbd(state->kbdc, KBDC_SET_TYPEMATIC, *(int *)arg);
if (error == 0) {
kbd->kb_delay1 = typematic_delay(*(int *)arg);
kbd->kb_delay2 = typematic_rate(*(int *)arg);
#ifdef EVDEV_SUPPORT
if (state->ks_evdev != NULL &&
evdev_rcpt_mask & EVDEV_RCPT_HW_KBD)
evdev_push_repeats(state->ks_evdev, kbd);
#endif
}
return error;
case PIO_KEYMAP: /* set keyboard translation table */
case OPIO_KEYMAP: /* set keyboard translation table (compat) */
case PIO_KEYMAPENT: /* set keyboard translation table entry */
case PIO_DEADKEYMAP: /* set accent key translation table */
state->ks_accents = 0;
/* FALLTHROUGH */
default:
splx(s);
return genkbd_commonioctl(kbd, cmd, arg);
}
splx(s);
return 0;
}
/* lock the access to the keyboard */
static int
atkbd_lock(keyboard_t *kbd, int lock)
{
return kbdc_lock(((atkbd_state_t *)kbd->kb_data)->kbdc, lock);
}
/* clear the internal state of the keyboard */
static void
atkbd_clear_state(keyboard_t *kbd)
{
atkbd_state_t *state;
state = (atkbd_state_t *)kbd->kb_data;
state->ks_flags = 0;
state->ks_polling = 0;
state->ks_state &= LOCK_MASK; /* preserve locking key state */
state->ks_accents = 0;
state->ks_composed_char = 0;
#if 0
state->ks_prefix = 0; /* XXX */
#endif
}
/* save the internal state */
static int
atkbd_get_state(keyboard_t *kbd, void *buf, size_t len)
{
if (len == 0)
return sizeof(atkbd_state_t);
if (len < sizeof(atkbd_state_t))
return -1;
bcopy(kbd->kb_data, buf, sizeof(atkbd_state_t));
return 0;
}
/* set the internal state */
static int
atkbd_set_state(keyboard_t *kbd, void *buf, size_t len)
{
if (len < sizeof(atkbd_state_t))
return ENOMEM;
if (((atkbd_state_t *)kbd->kb_data)->kbdc
!= ((atkbd_state_t *)buf)->kbdc)
return ENOMEM;
bcopy(buf, kbd->kb_data, sizeof(atkbd_state_t));
return 0;
}
static int
atkbd_poll(keyboard_t *kbd, int on)
{
atkbd_state_t *state;
int s;
state = (atkbd_state_t *)kbd->kb_data;
s = spltty();
if (on)
++state->ks_polling;
else
--state->ks_polling;
splx(s);
return 0;
}
static void
atkbd_shutdown_final(void *v)
{
#ifdef __sparc64__
keyboard_t *kbd = v;
KBDC kbdc = ((atkbd_state_t *)kbd->kb_data)->kbdc;
/*
* Turn off the translation in preparation for handing the keyboard
* over to the OFW as the OBP driver doesn't use translation and
* also doesn't disable it itself resulting in a broken keymap at
* the boot prompt. Also disable the aux port and the interrupts as
* the OBP driver doesn't use them, i.e. polls the keyboard. Not
* disabling the interrupts doesn't cause real problems but the
* responsiveness is a bit better when they are turned off.
*/
send_kbd_command(kbdc, KBDC_DISABLE_KBD);
set_controller_command_byte(kbdc,
KBD_AUX_CONTROL_BITS | KBD_KBD_CONTROL_BITS | KBD_TRANSLATION,
KBD_DISABLE_AUX_PORT | KBD_DISABLE_KBD_INT | KBD_ENABLE_KBD_PORT);
send_kbd_command(kbdc, KBDC_ENABLE_KBD);
#endif
}
static int
atkbd_reset(KBDC kbdc, int flags, int c)
{
/* reset keyboard hardware */
if (!(flags & KB_CONF_NO_RESET) && !reset_kbd(kbdc)) {
/*
* KEYBOARD ERROR
* Keyboard reset may fail either because the keyboard
* doen't exist, or because the keyboard doesn't pass
* the self-test, or the keyboard controller on the
* motherboard and the keyboard somehow fail to shake hands.
* It is just possible, particularly in the last case,
* that the keyboard controller may be left in a hung state.
* test_controller() and test_kbd_port() appear to bring
* the keyboard controller back (I don't know why and how,
* though.)
*/
empty_both_buffers(kbdc, 10);
test_controller(kbdc);
test_kbd_port(kbdc);
/*
* We could disable the keyboard port and interrupt... but,
* the keyboard may still exist (see above).
*/
set_controller_command_byte(kbdc,
ALLOW_DISABLE_KBD(kbdc) ? 0xff : KBD_KBD_CONTROL_BITS, c);
if (bootverbose)
printf("atkbd: failed to reset the keyboard.\n");
return (EIO);
}
return (0);
}
/* local functions */
static int
set_typematic(keyboard_t *kbd)
{
int val, error;
atkbd_state_t *state = kbd->kb_data;
val = typematic(DEFAULT_DELAY, DEFAULT_RATE);
error = write_kbd(state->kbdc, KBDC_SET_TYPEMATIC, val);
if (error == 0) {
kbd->kb_delay1 = typematic_delay(val);
kbd->kb_delay2 = typematic_rate(val);
}
return (error);
}
static int
setup_kbd_port(KBDC kbdc, int port, int intr)
{
if (!set_controller_command_byte(kbdc,
KBD_KBD_CONTROL_BITS,
((port) ? KBD_ENABLE_KBD_PORT : KBD_DISABLE_KBD_PORT)
| ((intr) ? KBD_ENABLE_KBD_INT : KBD_DISABLE_KBD_INT)))
return 1;
return 0;
}
static int
get_kbd_echo(KBDC kbdc)
{
/* enable the keyboard port, but disable the keyboard intr. */
if (setup_kbd_port(kbdc, TRUE, FALSE))
/* CONTROLLER ERROR: there is very little we can do... */
return ENXIO;
/* see if something is present */
write_kbd_command(kbdc, KBDC_ECHO);
if (read_kbd_data(kbdc) != KBD_ECHO) {
empty_both_buffers(kbdc, 10);
test_controller(kbdc);
test_kbd_port(kbdc);
return ENXIO;
}
/* enable the keyboard port and intr. */
if (setup_kbd_port(kbdc, TRUE, TRUE)) {
/*
* CONTROLLER ERROR
* This is serious; the keyboard intr is left disabled!
*/
return ENXIO;
}
return 0;
}
static int
probe_keyboard(KBDC kbdc, int flags)
{
/*
* Don't try to print anything in this function. The low-level
* console may not have been initialized yet...
*/
int err;
int c;
int m;
if (!kbdc_lock(kbdc, TRUE)) {
/* driver error? */
return ENXIO;
}
/* temporarily block data transmission from the keyboard */
write_controller_command(kbdc, KBDC_DISABLE_KBD_PORT);
/* flush any noise in the buffer */
empty_both_buffers(kbdc, 100);
/* save the current keyboard controller command byte */
m = kbdc_get_device_mask(kbdc) & ~KBD_KBD_CONTROL_BITS;
c = get_controller_command_byte(kbdc);
if (c == -1) {
/* CONTROLLER ERROR */
kbdc_set_device_mask(kbdc, m);
kbdc_lock(kbdc, FALSE);
return ENXIO;
}
/*
* The keyboard may have been screwed up by the boot block.
* We may just be able to recover from error by testing the controller
* and the keyboard port. The controller command byte needs to be
* saved before this recovery operation, as some controllers seem
* to set the command byte to particular values.
*/
test_controller(kbdc);
if (!(flags & KB_CONF_NO_PROBE_TEST))
test_kbd_port(kbdc);
err = get_kbd_echo(kbdc);
/*
* Even if the keyboard doesn't seem to be present (err != 0),
* we shall enable the keyboard port and interrupt so that
* the driver will be operable when the keyboard is attached
* to the system later. It is NOT recommended to hot-plug
* the AT keyboard, but many people do so...
*/
kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS);
setup_kbd_port(kbdc, TRUE, TRUE);
#if 0
if (err == 0) {
kbdc_set_device_mask(kbdc, m | KBD_KBD_CONTROL_BITS);
} else {
/* try to restore the command byte as before */
set_controller_command_byte(kbdc,
ALLOW_DISABLE_KBD(kbdc) ? 0xff : KBD_KBD_CONTROL_BITS, c);
kbdc_set_device_mask(kbdc, m);
}
#endif
kbdc_lock(kbdc, FALSE);
return (HAS_QUIRK(kbdc, KBDC_QUIRK_IGNORE_PROBE_RESULT) ? 0 : err);
}
static int
init_keyboard(KBDC kbdc, int *type, int flags)
{
int codeset;
int id;
int c;
if (!kbdc_lock(kbdc, TRUE)) {
/* driver error? */
return EIO;
}
/* temporarily block data transmission from the keyboard */
write_controller_command(kbdc, KBDC_DISABLE_KBD_PORT);
/* save the current controller command byte */
empty_both_buffers(kbdc, 200);
c = get_controller_command_byte(kbdc);
if (c == -1) {
/* CONTROLLER ERROR */
kbdc_lock(kbdc, FALSE);
printf("atkbd: unable to get the current command byte value.\n");
return EIO;
}
if (bootverbose)
printf("atkbd: the current kbd controller command byte %04x\n",
c);
#if 0
/* override the keyboard lock switch */
c |= KBD_OVERRIDE_KBD_LOCK;
#endif
/* enable the keyboard port, but disable the keyboard intr. */
if (setup_kbd_port(kbdc, TRUE, FALSE)) {
/* CONTROLLER ERROR: there is very little we can do... */
printf("atkbd: unable to set the command byte.\n");
kbdc_lock(kbdc, FALSE);
return EIO;
}
if (HAS_QUIRK(kbdc, KBDC_QUIRK_RESET_AFTER_PROBE) &&
atkbd_reset(kbdc, flags, c)) {
kbdc_lock(kbdc, FALSE);
return EIO;
}
/*
* Check if we have an XT keyboard before we attempt to reset it.
* The procedure assumes that the keyboard and the controller have
* been set up properly by BIOS and have not been messed up
* during the boot process.
*/
codeset = -1;
if (flags & KB_CONF_ALT_SCANCODESET)
/* the user says there is a XT keyboard */
codeset = 1;
#ifdef KBD_DETECT_XT_KEYBOARD
else if ((c & KBD_TRANSLATION) == 0) {
/* SET_SCANCODE_SET is not always supported; ignore error */
if (send_kbd_command_and_data(kbdc, KBDC_SET_SCANCODE_SET, 0)
== KBD_ACK)
codeset = read_kbd_data(kbdc);
}
if (bootverbose)
printf("atkbd: scancode set %d\n", codeset);
#endif /* KBD_DETECT_XT_KEYBOARD */
*type = KB_OTHER;
id = get_kbd_id(kbdc);
switch(id) {
case 0x41ab: /* 101/102/... Enhanced */
case 0x83ab: /* ditto */
case 0x54ab: /* SpaceSaver */
case 0x84ab: /* ditto */
#if 0
case 0x90ab: /* 'G' */
case 0x91ab: /* 'P' */
case 0x92ab: /* 'A' */
#endif
*type = KB_101;
break;
case -1: /* AT 84 keyboard doesn't return ID */
*type = KB_84;
break;
default:
break;
}
if (bootverbose)
printf("atkbd: keyboard ID 0x%x (%d)\n", id, *type);
if (!HAS_QUIRK(kbdc, KBDC_QUIRK_RESET_AFTER_PROBE) &&
atkbd_reset(kbdc, flags, c)) {
kbdc_lock(kbdc, FALSE);
return EIO;
}
/*
* Allow us to set the XT_KEYBD flag so that keyboards
* such as those on the IBM ThinkPad laptop computers can be used
* with the standard console driver.
*/
if (codeset == 1) {
if (send_kbd_command_and_data(kbdc,
KBDC_SET_SCANCODE_SET, codeset) == KBD_ACK) {
/* XT kbd doesn't need scan code translation */
c &= ~KBD_TRANSLATION;
} else {
/*
* KEYBOARD ERROR
* The XT kbd isn't usable unless the proper scan
* code set is selected.
*/
set_controller_command_byte(kbdc, ALLOW_DISABLE_KBD(kbdc)
? 0xff : KBD_KBD_CONTROL_BITS, c);
kbdc_lock(kbdc, FALSE);
printf("atkbd: unable to set the XT keyboard mode.\n");
return EIO;
}
}
#if defined(__sparc64__)
if (send_kbd_command_and_data(
kbdc, KBDC_SET_SCANCODE_SET, 2) != KBD_ACK) {
printf("atkbd: can't set translation.\n");
}
c |= KBD_TRANSLATION;
#endif
/*
* Some keyboards require a SETLEDS command to be sent after
* the reset command before they will send keystrokes to us
*/
if (HAS_QUIRK(kbdc, KBDC_QUIRK_SETLEDS_ON_INIT) &&
send_kbd_command_and_data(kbdc, KBDC_SET_LEDS, 0) != KBD_ACK) {
printf("atkbd: setleds failed\n");
}
if (!ALLOW_DISABLE_KBD(kbdc))
send_kbd_command(kbdc, KBDC_ENABLE_KBD);
/* enable the keyboard port and intr. */
if (!set_controller_command_byte(kbdc,
KBD_KBD_CONTROL_BITS | KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK,
(c & (KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK))
| KBD_ENABLE_KBD_PORT | KBD_ENABLE_KBD_INT)) {
/*
* CONTROLLER ERROR
* This is serious; we are left with the disabled
* keyboard intr.
*/
set_controller_command_byte(kbdc, ALLOW_DISABLE_KBD(kbdc)
? 0xff : (KBD_KBD_CONTROL_BITS | KBD_TRANSLATION |
KBD_OVERRIDE_KBD_LOCK), c);
kbdc_lock(kbdc, FALSE);
printf("atkbd: unable to enable the keyboard port and intr.\n");
return EIO;
}
kbdc_lock(kbdc, FALSE);
return 0;
}
static int
write_kbd(KBDC kbdc, int command, int data)
{
int s;
/* prevent the timeout routine from polling the keyboard */
if (!kbdc_lock(kbdc, TRUE))
return EBUSY;
/* disable the keyboard and mouse interrupt */
s = spltty();
#if 0
c = get_controller_command_byte(kbdc);
if ((c == -1)
|| !set_controller_command_byte(kbdc,
kbdc_get_device_mask(kbdc),
KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
| KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
/* CONTROLLER ERROR */
kbdc_lock(kbdc, FALSE);
splx(s);
return EIO;
}
/*
* Now that the keyboard controller is told not to generate
* the keyboard and mouse interrupts, call `splx()' to allow
* the other tty interrupts. The clock interrupt may also occur,
* but the timeout routine (`scrn_timer()') will be blocked
* by the lock flag set via `kbdc_lock()'
*/
splx(s);
#endif
if (send_kbd_command_and_data(kbdc, command, data) != KBD_ACK)
send_kbd_command(kbdc, KBDC_ENABLE_KBD);
#if 0
/* restore the interrupts */
if (!set_controller_command_byte(kbdc, kbdc_get_device_mask(kbdc),
c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) {
/* CONTROLLER ERROR */
}
#else
splx(s);
#endif
kbdc_lock(kbdc, FALSE);
return 0;
}
static int
get_kbd_id(KBDC kbdc)
{
int id1, id2;
empty_both_buffers(kbdc, 10);
id1 = id2 = -1;
if (send_kbd_command(kbdc, KBDC_SEND_DEV_ID) != KBD_ACK)
return -1;
DELAY(10000); /* 10 msec delay */
id1 = read_kbd_data(kbdc);
if (id1 != -1)
id2 = read_kbd_data(kbdc);
if ((id1 == -1) || (id2 == -1)) {
empty_both_buffers(kbdc, 10);
test_controller(kbdc);
test_kbd_port(kbdc);
return -1;
}
return ((id2 << 8) | id1);
}
static int delays[] = { 250, 500, 750, 1000 };
static int rates[] = { 34, 38, 42, 46, 50, 55, 59, 63,
68, 76, 84, 92, 100, 110, 118, 126,
136, 152, 168, 184, 200, 220, 236, 252,
272, 304, 336, 368, 400, 440, 472, 504 };
static int
typematic_delay(int i)
{
return delays[(i >> 5) & 3];
}
static int
typematic_rate(int i)
{
return rates[i & 0x1f];
}
static int
typematic(int delay, int rate)
{
int value;
int i;
for (i = nitems(delays) - 1; i > 0; --i) {
if (delay >= delays[i])
break;
}
value = i << 5;
for (i = nitems(rates) - 1; i > 0; --i) {
if (rate >= rates[i])
break;
}
value |= i;
return value;
}
Index: stable/11/sys/dev/gpio/gpiokeys.c
===================================================================
--- stable/11/sys/dev/gpio/gpiokeys.c (revision 356012)
+++ stable/11/sys/dev/gpio/gpiokeys.c (revision 356013)
@@ -1,1017 +1,1015 @@
/*-
* Copyright (c) 2015-2016 Oleksandr Tymoshenko
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include
__FBSDID("$FreeBSD$");
#include "opt_platform.h"
#include "opt_kbd.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define KBD_DRIVER_NAME "gpiokeys"
#define GPIOKEYS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
#define GPIOKEYS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
#define GPIOKEYS_LOCK_INIT(_sc) \
mtx_init(&_sc->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
"gpiokeys", MTX_DEF)
#define GPIOKEYS_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx);
#define GPIOKEYS_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
#define GPIOKEY_LOCK(_key) mtx_lock(&(_key)->mtx)
#define GPIOKEY_UNLOCK(_key) mtx_unlock(&(_key)->mtx)
#define GPIOKEY_LOCK_INIT(_key) \
mtx_init(&(_key)->mtx, "gpiokey", "gpiokey", MTX_DEF)
#define GPIOKEY_LOCK_DESTROY(_key) mtx_destroy(&(_key)->mtx);
#define KEY_PRESS 0
#define KEY_RELEASE 0x80
#define SCAN_PRESS 0
#define SCAN_RELEASE 0x80
#define SCAN_CHAR(c) ((c) & 0x7f)
#define GPIOKEYS_GLOBAL_NMOD 8 /* units */
#define GPIOKEYS_GLOBAL_NKEYCODE 6 /* units */
#define GPIOKEYS_GLOBAL_IN_BUF_SIZE (2*(GPIOKEYS_GLOBAL_NMOD + (2*GPIOKEYS_GLOBAL_NKEYCODE))) /* bytes */
#define GPIOKEYS_GLOBAL_IN_BUF_FULL (GPIOKEYS_GLOBAL_IN_BUF_SIZE / 2) /* bytes */
#define GPIOKEYS_GLOBAL_NFKEY (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */
#define GPIOKEYS_GLOBAL_BUFFER_SIZE 64 /* bytes */
#define AUTOREPEAT_DELAY 250
#define AUTOREPEAT_REPEAT 34
struct gpiokeys_softc;
struct gpiokey
{
struct gpiokeys_softc *parent_sc;
gpio_pin_t pin;
int irq_rid;
struct resource *irq_res;
void *intr_hl;
struct mtx mtx;
uint32_t keycode;
int autorepeat;
struct callout debounce_callout;
struct callout repeat_callout;
int repeat_delay;
int repeat;
int debounce_interval;
};
struct gpiokeys_softc
{
device_t sc_dev;
struct mtx sc_mtx;
struct gpiokey *sc_keys;
int sc_total_keys;
keyboard_t sc_kbd;
keymap_t sc_keymap;
accentmap_t sc_accmap;
fkeytab_t sc_fkeymap[GPIOKEYS_GLOBAL_NFKEY];
uint32_t sc_input[GPIOKEYS_GLOBAL_IN_BUF_SIZE]; /* input buffer */
uint32_t sc_time_ms;
#define GPIOKEYS_GLOBAL_FLAG_POLLING 0x00000002
uint32_t sc_flags; /* flags */
int sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
int sc_state; /* shift/lock key state */
int sc_accents; /* accent key index (> 0) */
int sc_kbd_size;
uint16_t sc_inputs;
uint16_t sc_inputhead;
uint16_t sc_inputtail;
uint8_t sc_kbd_id;
};
/* gpio-keys device */
static int gpiokeys_probe(device_t);
static int gpiokeys_attach(device_t);
static int gpiokeys_detach(device_t);
/* kbd methods prototypes */
static int gpiokeys_set_typematic(keyboard_t *, int);
static uint32_t gpiokeys_read_char(keyboard_t *, int);
static void gpiokeys_clear_state(keyboard_t *);
static int gpiokeys_ioctl(keyboard_t *, u_long, caddr_t);
static int gpiokeys_enable(keyboard_t *);
static int gpiokeys_disable(keyboard_t *);
static void gpiokeys_event_keyinput(struct gpiokeys_softc *);
static void
gpiokeys_put_key(struct gpiokeys_softc *sc, uint32_t key)
{
GPIOKEYS_ASSERT_LOCKED(sc);
if (sc->sc_inputs < GPIOKEYS_GLOBAL_IN_BUF_SIZE) {
sc->sc_input[sc->sc_inputtail] = key;
++(sc->sc_inputs);
++(sc->sc_inputtail);
if (sc->sc_inputtail >= GPIOKEYS_GLOBAL_IN_BUF_SIZE) {
sc->sc_inputtail = 0;
}
} else {
device_printf(sc->sc_dev, "input buffer is full\n");
}
}
static void
gpiokeys_key_event(struct gpiokeys_softc *sc, uint16_t keycode, int pressed)
{
uint32_t key;
key = keycode & SCAN_KEYCODE_MASK;
if (!pressed)
key |= KEY_RELEASE;
GPIOKEYS_LOCK(sc);
if (keycode & SCAN_PREFIX_E0)
gpiokeys_put_key(sc, 0xe0);
else if (keycode & SCAN_PREFIX_E1)
gpiokeys_put_key(sc, 0xe1);
gpiokeys_put_key(sc, key);
GPIOKEYS_UNLOCK(sc);
gpiokeys_event_keyinput(sc);
}
static void
gpiokey_autorepeat(void *arg)
{
struct gpiokey *key;
key = arg;
if (key->keycode == GPIOKEY_NONE)
return;
gpiokeys_key_event(key->parent_sc, key->keycode, 1);
callout_reset(&key->repeat_callout, key->repeat,
gpiokey_autorepeat, key);
}
static void
gpiokey_debounced_intr(void *arg)
{
struct gpiokey *key;
bool active;
key = arg;
if (key->keycode == GPIOKEY_NONE)
return;
gpio_pin_is_active(key->pin, &active);
if (active) {
gpiokeys_key_event(key->parent_sc, key->keycode, 1);
if (key->autorepeat) {
callout_reset(&key->repeat_callout, key->repeat_delay,
gpiokey_autorepeat, key);
}
}
else {
if (key->autorepeat &&
callout_pending(&key->repeat_callout))
callout_stop(&key->repeat_callout);
gpiokeys_key_event(key->parent_sc, key->keycode, 0);
}
}
static void
gpiokey_intr(void *arg)
{
struct gpiokey *key;
int debounce_ticks;
key = arg;
GPIOKEY_LOCK(key);
debounce_ticks = (hz * key->debounce_interval) / 1000;
if (debounce_ticks == 0)
debounce_ticks = 1;
if (!callout_pending(&key->debounce_callout))
callout_reset(&key->debounce_callout, debounce_ticks,
gpiokey_debounced_intr, key);
GPIOKEY_UNLOCK(key);
}
static void
gpiokeys_attach_key(struct gpiokeys_softc *sc, phandle_t node,
struct gpiokey *key)
{
pcell_t prop;
char *name;
uint32_t code;
int err;
const char *key_name;
GPIOKEY_LOCK_INIT(key);
key->parent_sc = sc;
callout_init_mtx(&key->debounce_callout, &key->mtx, 0);
callout_init_mtx(&key->repeat_callout, &key->mtx, 0);
name = NULL;
if (OF_getprop_alloc(node, "label", 1, (void **)&name) == -1)
OF_getprop_alloc(node, "name", 1, (void **)&name);
if (name != NULL)
key_name = name;
else
key_name = "unknown";
key->autorepeat = OF_hasprop(node, "autorepeat");
key->repeat_delay = (hz * AUTOREPEAT_DELAY) / 1000;
if (key->repeat_delay == 0)
key->repeat_delay = 1;
key->repeat = (hz * AUTOREPEAT_REPEAT) / 1000;
if (key->repeat == 0)
key->repeat = 1;
if ((OF_getprop(node, "debounce-interval", &prop, sizeof(prop))) > 0)
key->debounce_interval = fdt32_to_cpu(prop);
else
key->debounce_interval = 5;
if ((OF_getprop(node, "freebsd,code", &prop, sizeof(prop))) > 0)
key->keycode = fdt32_to_cpu(prop);
else if ((OF_getprop(node, "linux,code", &prop, sizeof(prop))) > 0) {
code = fdt32_to_cpu(prop);
key->keycode = gpiokey_map_linux_code(code);
if (key->keycode == GPIOKEY_NONE)
device_printf(sc->sc_dev, "<%s> failed to map linux,code value 0x%x\n",
key_name, code);
}
else
device_printf(sc->sc_dev, "<%s> no linux,code or freebsd,code property\n",
key_name);
err = gpio_pin_get_by_ofw_idx(sc->sc_dev, node, 0, &key->pin);
if (err) {
device_printf(sc->sc_dev, "<%s> failed to map pin\n", key_name);
if (name)
OF_prop_free(name);
return;
}
key->irq_res = gpio_alloc_intr_resource(sc->sc_dev, &key->irq_rid,
RF_ACTIVE, key->pin, GPIO_INTR_EDGE_BOTH);
if (!key->irq_res) {
device_printf(sc->sc_dev, "<%s> cannot allocate interrupt\n", key_name);
gpio_pin_release(key->pin);
key->pin = NULL;
if (name)
OF_prop_free(name);
return;
}
if (bus_setup_intr(sc->sc_dev, key->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
NULL, gpiokey_intr, key,
&key->intr_hl) != 0) {
device_printf(sc->sc_dev, "<%s> unable to setup the irq handler\n", key_name);
bus_release_resource(sc->sc_dev, SYS_RES_IRQ, key->irq_rid,
key->irq_res);
gpio_pin_release(key->pin);
key->pin = NULL;
key->irq_res = NULL;
if (name)
OF_prop_free(name);
return;
}
if (bootverbose)
device_printf(sc->sc_dev, "<%s> code=%08x, autorepeat=%d, "\
"repeat=%d, repeat_delay=%d\n", key_name, key->keycode,
key->autorepeat, key->repeat, key->repeat_delay);
if (name)
OF_prop_free(name);
}
static void
gpiokeys_detach_key(struct gpiokeys_softc *sc, struct gpiokey *key)
{
GPIOKEY_LOCK(key);
if (key->intr_hl)
bus_teardown_intr(sc->sc_dev, key->irq_res, key->intr_hl);
if (key->irq_res)
bus_release_resource(sc->sc_dev, SYS_RES_IRQ,
key->irq_rid, key->irq_res);
if (callout_pending(&key->repeat_callout))
callout_drain(&key->repeat_callout);
if (callout_pending(&key->debounce_callout))
callout_drain(&key->debounce_callout);
if (key->pin)
gpio_pin_release(key->pin);
GPIOKEY_UNLOCK(key);
GPIOKEY_LOCK_DESTROY(key);
}
static int
gpiokeys_probe(device_t dev)
{
if (!ofw_bus_is_compatible(dev, "gpio-keys"))
return (ENXIO);
device_set_desc(dev, "GPIO keyboard");
return (0);
}
static int
gpiokeys_attach(device_t dev)
{
int unit;
struct gpiokeys_softc *sc;
keyboard_t *kbd;
phandle_t keys, child;
int total_keys;
if ((keys = ofw_bus_get_node(dev)) == -1)
return (ENXIO);
sc = device_get_softc(dev);
sc->sc_dev = dev;
kbd = &sc->sc_kbd;
GPIOKEYS_LOCK_INIT(sc);
unit = device_get_unit(dev);
kbd_init_struct(kbd, KBD_DRIVER_NAME, KB_OTHER, unit, 0, 0, 0);
kbd->kb_data = (void *)sc;
sc->sc_mode = K_XLATE;
sc->sc_keymap = key_map;
sc->sc_accmap = accent_map;
kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap,
sc->sc_fkeymap, GPIOKEYS_GLOBAL_NFKEY);
KBD_FOUND_DEVICE(kbd);
gpiokeys_clear_state(kbd);
KBD_PROBE_DONE(kbd);
KBD_INIT_DONE(kbd);
if (kbd_register(kbd) < 0) {
goto detach;
}
KBD_CONFIG_DONE(kbd);
gpiokeys_enable(kbd);
#ifdef KBD_INSTALL_CDEV
if (kbd_attach(kbd)) {
goto detach;
}
#endif
if (bootverbose) {
kbdd_diag(kbd, 1);
}
total_keys = 0;
/* Traverse the 'gpio-keys' node and count keys */
for (child = OF_child(keys); child != 0; child = OF_peer(child)) {
if (!OF_hasprop(child, "gpios"))
continue;
total_keys++;
}
if (total_keys) {
sc->sc_keys = malloc(sizeof(struct gpiokey) * total_keys,
M_DEVBUF, M_WAITOK | M_ZERO);
sc->sc_total_keys = 0;
/* Traverse the 'gpio-keys' node and count keys */
for (child = OF_child(keys); child != 0; child = OF_peer(child)) {
if (!OF_hasprop(child, "gpios"))
continue;
gpiokeys_attach_key(sc, child ,&sc->sc_keys[sc->sc_total_keys]);
sc->sc_total_keys++;
}
}
return (0);
detach:
gpiokeys_detach(dev);
return (ENXIO);
}
static int
gpiokeys_detach(device_t dev)
{
struct gpiokeys_softc *sc;
keyboard_t *kbd;
int i;
sc = device_get_softc(dev);
for (i = 0; i < sc->sc_total_keys; i++)
gpiokeys_detach_key(sc, &sc->sc_keys[i]);
kbd = kbd_get_keyboard(kbd_find_keyboard(KBD_DRIVER_NAME,
device_get_unit(dev)));
#ifdef KBD_INSTALL_CDEV
kbd_detach(kbd);
#endif
kbd_unregister(kbd);
GPIOKEYS_LOCK_DESTROY(sc);
if (sc->sc_keys)
free(sc->sc_keys, M_DEVBUF);
return (0);
}
/* early keyboard probe, not supported */
static int
gpiokeys_configure(int flags)
{
return (0);
}
/* detect a keyboard, not used */
static int
gpiokeys__probe(int unit, void *arg, int flags)
{
return (ENXIO);
}
/* reset and initialize the device, not used */
static int
gpiokeys_init(int unit, keyboard_t **kbdp, void *arg, int flags)
{
return (ENXIO);
}
/* test the interface to the device, not used */
static int
gpiokeys_test_if(keyboard_t *kbd)
{
return (0);
}
/* finish using this keyboard, not used */
static int
gpiokeys_term(keyboard_t *kbd)
{
return (ENXIO);
}
/* keyboard interrupt routine, not used */
static int
gpiokeys_intr(keyboard_t *kbd, void *arg)
{
return (0);
}
/* lock the access to the keyboard, not used */
static int
gpiokeys_lock(keyboard_t *kbd, int lock)
{
return (1);
}
/*
* Enable the access to the device; until this function is called,
* the client cannot read from the keyboard.
*/
static int
gpiokeys_enable(keyboard_t *kbd)
{
struct gpiokeys_softc *sc;
sc = kbd->kb_data;
GPIOKEYS_LOCK(sc);
KBD_ACTIVATE(kbd);
GPIOKEYS_UNLOCK(sc);
return (0);
}
/* disallow the access to the device */
static int
gpiokeys_disable(keyboard_t *kbd)
{
struct gpiokeys_softc *sc;
sc = kbd->kb_data;
GPIOKEYS_LOCK(sc);
KBD_DEACTIVATE(kbd);
GPIOKEYS_UNLOCK(sc);
return (0);
}
static void
gpiokeys_do_poll(struct gpiokeys_softc *sc, uint8_t wait)
{
KASSERT((sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING) != 0,
("gpiokeys_do_poll called when not polling\n"));
GPIOKEYS_ASSERT_LOCKED(sc);
if (!kdb_active && !SCHEDULER_STOPPED()) {
while (sc->sc_inputs == 0) {
kern_yield(PRI_UNCHANGED);
if (!wait)
break;
}
return;
}
while ((sc->sc_inputs == 0) && wait) {
printf("POLL!\n");
}
}
/* check if data is waiting */
static int
gpiokeys_check(keyboard_t *kbd)
{
struct gpiokeys_softc *sc = kbd->kb_data;
GPIOKEYS_ASSERT_LOCKED(sc);
if (!KBD_IS_ACTIVE(kbd))
return (0);
if (sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING)
gpiokeys_do_poll(sc, 0);
if (sc->sc_inputs > 0) {
return (1);
}
return (0);
}
/* check if char is waiting */
static int
gpiokeys_check_char_locked(keyboard_t *kbd)
{
if (!KBD_IS_ACTIVE(kbd))
return (0);
return (gpiokeys_check(kbd));
}
static int
gpiokeys_check_char(keyboard_t *kbd)
{
int result;
struct gpiokeys_softc *sc = kbd->kb_data;
GPIOKEYS_LOCK(sc);
result = gpiokeys_check_char_locked(kbd);
GPIOKEYS_UNLOCK(sc);
return (result);
}
static int32_t
gpiokeys_get_key(struct gpiokeys_softc *sc, uint8_t wait)
{
int32_t c;
KASSERT((!kdb_active && !SCHEDULER_STOPPED())
|| (sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING) != 0,
("not polling in kdb or panic\n"));
GPIOKEYS_ASSERT_LOCKED(sc);
if (sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING)
gpiokeys_do_poll(sc, wait);
if (sc->sc_inputs == 0) {
c = -1;
} else {
c = sc->sc_input[sc->sc_inputhead];
--(sc->sc_inputs);
++(sc->sc_inputhead);
if (sc->sc_inputhead >= GPIOKEYS_GLOBAL_IN_BUF_SIZE) {
sc->sc_inputhead = 0;
}
}
return (c);
}
/* read one byte from the keyboard if it's allowed */
static int
gpiokeys_read(keyboard_t *kbd, int wait)
{
struct gpiokeys_softc *sc = kbd->kb_data;
int32_t keycode;
if (!KBD_IS_ACTIVE(kbd))
return (-1);
/* XXX */
keycode = gpiokeys_get_key(sc, (wait == FALSE) ? 0 : 1);
if (!KBD_IS_ACTIVE(kbd) || (keycode == -1))
return (-1);
++(kbd->kb_count);
return (keycode);
}
/* read char from the keyboard */
static uint32_t
gpiokeys_read_char_locked(keyboard_t *kbd, int wait)
{
struct gpiokeys_softc *sc = kbd->kb_data;
uint32_t action;
uint32_t keycode;
if (!KBD_IS_ACTIVE(kbd))
return (NOKEY);
next_code:
/* see if there is something in the keyboard port */
/* XXX */
keycode = gpiokeys_get_key(sc, (wait == FALSE) ? 0 : 1);
++kbd->kb_count;
/* return the byte as is for the K_RAW mode */
if (sc->sc_mode == K_RAW) {
return (keycode);
}
/* return the key code in the K_CODE mode */
/* XXX: keycode |= SCAN_RELEASE; */
if (sc->sc_mode == K_CODE) {
return (keycode);
}
/* keycode to key action */
action = genkbd_keyaction(kbd, SCAN_CHAR(keycode),
(keycode & SCAN_RELEASE),
&sc->sc_state, &sc->sc_accents);
if (action == NOKEY) {
goto next_code;
}
return (action);
}
/* Currently wait is always false. */
static uint32_t
gpiokeys_read_char(keyboard_t *kbd, int wait)
{
uint32_t keycode;
struct gpiokeys_softc *sc = kbd->kb_data;
GPIOKEYS_LOCK(sc);
keycode = gpiokeys_read_char_locked(kbd, wait);
GPIOKEYS_UNLOCK(sc);
return (keycode);
}
/* some useful control functions */
static int
gpiokeys_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
struct gpiokeys_softc *sc = kbd->kb_data;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
int ival;
#endif
switch (cmd) {
case KDGKBMODE: /* get keyboard mode */
*(int *)arg = sc->sc_mode;
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 7):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBMODE: /* set keyboard mode */
switch (*(int *)arg) {
case K_XLATE:
if (sc->sc_mode != K_XLATE) {
/* make lock key state and LED state match */
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= KBD_LED_VAL(kbd);
}
/* FALLTHROUGH */
case K_RAW:
case K_CODE:
if (sc->sc_mode != *(int *)arg) {
if ((sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING) == 0)
gpiokeys_clear_state(kbd);
sc->sc_mode = *(int *)arg;
}
break;
default:
return (EINVAL);
}
break;
case KDGETLED: /* get keyboard LED */
*(int *)arg = KBD_LED_VAL(kbd);
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 66):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETLED: /* set keyboard LED */
KBD_LED_VAL(kbd) = *(int *)arg;
break;
case KDGKBSTATE: /* get lock key state */
*(int *)arg = sc->sc_state & LOCK_MASK;
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 20):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBSTATE: /* set lock key state */
if (*(int *)arg & ~LOCK_MASK) {
return (EINVAL);
}
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= *(int *)arg;
return (0);
case KDSETREPEAT: /* set keyboard repeat rate (new
* interface) */
if (!KBD_HAS_DEVICE(kbd)) {
return (0);
}
if (((int *)arg)[1] < 0) {
return (EINVAL);
}
if (((int *)arg)[0] < 0) {
return (EINVAL);
}
if (((int *)arg)[0] < 200) /* fastest possible value */
kbd->kb_delay1 = 200;
else
kbd->kb_delay1 = ((int *)arg)[0];
kbd->kb_delay2 = ((int *)arg)[1];
return (0);
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 67):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETRAD: /* set keyboard repeat rate (old
* interface) */
return (gpiokeys_set_typematic(kbd, *(int *)arg));
case PIO_KEYMAP: /* set keyboard translation table */
case OPIO_KEYMAP: /* set keyboard translation table
* (compat) */
case PIO_KEYMAPENT: /* set keyboard translation table
* entry */
case PIO_DEADKEYMAP: /* set accent key translation table */
sc->sc_accents = 0;
/* FALLTHROUGH */
default:
return (genkbd_commonioctl(kbd, cmd, arg));
}
return (0);
}
static int
gpiokeys_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
int result;
struct gpiokeys_softc *sc;
sc = kbd->kb_data;
/*
* XXX Check if someone is calling us from a critical section:
*/
if (curthread->td_critnest != 0)
return (EDEADLK);
GPIOKEYS_LOCK(sc);
result = gpiokeys_ioctl_locked(kbd, cmd, arg);
GPIOKEYS_UNLOCK(sc);
return (result);
}
/* clear the internal state of the keyboard */
static void
gpiokeys_clear_state(keyboard_t *kbd)
{
struct gpiokeys_softc *sc = kbd->kb_data;
sc->sc_flags &= ~(GPIOKEYS_GLOBAL_FLAG_POLLING);
sc->sc_state &= LOCK_MASK; /* preserve locking key state */
sc->sc_accents = 0;
}
/* get the internal state, not used */
static int
gpiokeys_get_state(keyboard_t *kbd, void *buf, size_t len)
{
return (len == 0) ? 1 : -1;
}
/* set the internal state, not used */
static int
gpiokeys_set_state(keyboard_t *kbd, void *buf, size_t len)
{
return (EINVAL);
}
static int
gpiokeys_poll(keyboard_t *kbd, int on)
{
struct gpiokeys_softc *sc = kbd->kb_data;
GPIOKEYS_LOCK(sc);
if (on)
sc->sc_flags |= GPIOKEYS_GLOBAL_FLAG_POLLING;
else
sc->sc_flags &= ~GPIOKEYS_GLOBAL_FLAG_POLLING;
GPIOKEYS_UNLOCK(sc);
return (0);
}
static int
gpiokeys_set_typematic(keyboard_t *kbd, int code)
{
static const int delays[] = {250, 500, 750, 1000};
static const int rates[] = {34, 38, 42, 46, 50, 55, 59, 63,
68, 76, 84, 92, 100, 110, 118, 126,
136, 152, 168, 184, 200, 220, 236, 252,
272, 304, 336, 368, 400, 440, 472, 504};
if (code & ~0x7f) {
return (EINVAL);
}
kbd->kb_delay1 = delays[(code >> 5) & 3];
kbd->kb_delay2 = rates[code & 0x1f];
return (0);
}
static void
gpiokeys_event_keyinput(struct gpiokeys_softc *sc)
{
int c;
if ((sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING) != 0)
return;
if (KBD_IS_ACTIVE(&sc->sc_kbd) &&
KBD_IS_BUSY(&sc->sc_kbd)) {
/* let the callback function process the input */
(sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT,
sc->sc_kbd.kb_callback.kc_arg);
} else {
/* read and discard the input, no one is waiting for it */
do {
c = gpiokeys_read_char(&sc->sc_kbd, 0);
} while (c != NOKEY);
}
}
static keyboard_switch_t gpiokeyssw = {
.probe = &gpiokeys__probe,
.init = &gpiokeys_init,
.term = &gpiokeys_term,
.intr = &gpiokeys_intr,
.test_if = &gpiokeys_test_if,
.enable = &gpiokeys_enable,
.disable = &gpiokeys_disable,
.read = &gpiokeys_read,
.check = &gpiokeys_check,
.read_char = &gpiokeys_read_char,
.check_char = &gpiokeys_check_char,
.ioctl = &gpiokeys_ioctl,
.lock = &gpiokeys_lock,
.clear_state = &gpiokeys_clear_state,
.get_state = &gpiokeys_get_state,
.set_state = &gpiokeys_set_state,
- .get_fkeystr = &genkbd_get_fkeystr,
.poll = &gpiokeys_poll,
- .diag = &genkbd_diag,
};
KEYBOARD_DRIVER(gpiokeys, gpiokeyssw, gpiokeys_configure);
static int
gpiokeys_driver_load(module_t mod, int what, void *arg)
{
switch (what) {
case MOD_LOAD:
kbd_add_driver(&gpiokeys_kbd_driver);
break;
case MOD_UNLOAD:
kbd_delete_driver(&gpiokeys_kbd_driver);
break;
}
return (0);
}
static devclass_t gpiokeys_devclass;
static device_method_t gpiokeys_methods[] = {
DEVMETHOD(device_probe, gpiokeys_probe),
DEVMETHOD(device_attach, gpiokeys_attach),
DEVMETHOD(device_detach, gpiokeys_detach),
DEVMETHOD_END
};
static driver_t gpiokeys_driver = {
"gpiokeys",
gpiokeys_methods,
sizeof(struct gpiokeys_softc),
};
DRIVER_MODULE(gpiokeys, simplebus, gpiokeys_driver, gpiokeys_devclass, gpiokeys_driver_load, 0);
MODULE_VERSION(gpiokeys, 1);
Index: stable/11/sys/dev/hyperv/input/hv_kbd.c
===================================================================
--- stable/11/sys/dev/hyperv/input/hv_kbd.c (revision 356012)
+++ stable/11/sys/dev/hyperv/input/hv_kbd.c (revision 356013)
@@ -1,564 +1,562 @@
/*-
* Copyright (c) 2017 Microsoft Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "dev/hyperv/input/hv_kbdc.h"
#define HVKBD_MTX_LOCK(_m) do { \
mtx_lock(_m); \
} while (0)
#define HVKBD_MTX_UNLOCK(_m) do { \
mtx_unlock(_m); \
} while (0)
#define HVKBD_MTX_ASSERT(_m, _t) do { \
mtx_assert(_m, _t); \
} while (0)
#define HVKBD_LOCK() HVKBD_MTX_LOCK(&Giant)
#define HVKBD_UNLOCK() HVKBD_MTX_UNLOCK(&Giant)
#define HVKBD_LOCK_ASSERT() HVKBD_MTX_ASSERT(&Giant, MA_OWNED)
#define HVKBD_FLAG_POLLING 0x00000002
/* early keyboard probe, not supported */
static int
hvkbd_configure(int flags)
{
return (0);
}
/* detect a keyboard, not used */
static int
hvkbd_probe(int unit, void *arg, int flags)
{
return (ENXIO);
}
/* reset and initialize the device, not used */
static int
hvkbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
{
DEBUG_HVKBD(*kbdp, "%s\n", __func__);
return (ENXIO);
}
/* test the interface to the device, not used */
static int
hvkbd_test_if(keyboard_t *kbd)
{
DEBUG_HVKBD(kbd, "%s\n", __func__);
return (0);
}
/* finish using this keyboard, not used */
static int
hvkbd_term(keyboard_t *kbd)
{
DEBUG_HVKBD(kbd, "%s\n", __func__);
return (ENXIO);
}
/* keyboard interrupt routine, not used */
static int
hvkbd_intr(keyboard_t *kbd, void *arg)
{
DEBUG_HVKBD(kbd, "%s\n", __func__);
return (0);
}
/* lock the access to the keyboard, not used */
static int
hvkbd_lock(keyboard_t *kbd, int lock)
{
DEBUG_HVKBD(kbd, "%s\n", __func__);
return (1);
}
/* save the internal state, not used */
static int
hvkbd_get_state(keyboard_t *kbd, void *buf, size_t len)
{
DEBUG_HVKBD(kbd,"%s\n", __func__);
return (len == 0) ? 1 : -1;
}
/* set the internal state, not used */
static int
hvkbd_set_state(keyboard_t *kbd, void *buf, size_t len)
{
DEBUG_HVKBD(kbd, "%s\n", __func__);
return (EINVAL);
}
static int
hvkbd_poll(keyboard_t *kbd, int on)
{
hv_kbd_sc *sc = kbd->kb_data;
HVKBD_LOCK();
/*
* Keep a reference count on polling to allow recursive
* cngrab() during a panic for example.
*/
if (on)
sc->sc_polling++;
else if (sc->sc_polling > 0)
sc->sc_polling--;
if (sc->sc_polling != 0) {
sc->sc_flags |= HVKBD_FLAG_POLLING;
} else {
sc->sc_flags &= ~HVKBD_FLAG_POLLING;
}
HVKBD_UNLOCK();
return (0);
}
/*
* Enable the access to the device; until this function is called,
* the client cannot read from the keyboard.
*/
static int
hvkbd_enable(keyboard_t *kbd)
{
HVKBD_LOCK();
KBD_ACTIVATE(kbd);
HVKBD_UNLOCK();
return (0);
}
/* disallow the access to the device */
static int
hvkbd_disable(keyboard_t *kbd)
{
DEBUG_HVKBD(kbd, "%s\n", __func__);
HVKBD_LOCK();
KBD_DEACTIVATE(kbd);
HVKBD_UNLOCK();
return (0);
}
static void
hvkbd_do_poll(hv_kbd_sc *sc, uint8_t wait)
{
while (!hv_kbd_prod_is_ready(sc)) {
hv_kbd_read_channel(sc->hs_chan, sc);
if (!wait)
break;
}
}
/* check if data is waiting */
/* Currently unused. */
static int
hvkbd_check(keyboard_t *kbd)
{
DEBUG_HVKBD(kbd, "%s\n", __func__);
return (0);
}
/* check if char is waiting */
static int
hvkbd_check_char_locked(keyboard_t *kbd)
{
HVKBD_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (FALSE);
hv_kbd_sc *sc = kbd->kb_data;
if (sc->sc_flags & HVKBD_FLAG_POLLING)
hvkbd_do_poll(sc, 0);
if (hv_kbd_prod_is_ready(sc)) {
return (TRUE);
}
return (FALSE);
}
static int
hvkbd_check_char(keyboard_t *kbd)
{
int result;
HVKBD_LOCK();
result = hvkbd_check_char_locked(kbd);
HVKBD_UNLOCK();
return (result);
}
/* read char from the keyboard */
static uint32_t
hvkbd_read_char_locked(keyboard_t *kbd, int wait)
{
uint32_t scancode = NOKEY;
keystroke ks;
hv_kbd_sc *sc = kbd->kb_data;
HVKBD_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd) || !hv_kbd_prod_is_ready(sc))
return (NOKEY);
if (sc->sc_mode == K_RAW) {
if (hv_kbd_fetch_top(sc, &ks)) {
return (NOKEY);
}
if ((ks.info & IS_E0) || (ks.info & IS_E1)) {
/**
* Emulate the generation of E0 or E1 scancode,
* the real scancode will be consumed next time.
*/
if (ks.info & IS_E0) {
scancode = XTKBD_EMUL0;
ks.info &= ~IS_E0;
} else if (ks.info & IS_E1) {
scancode = XTKBD_EMUL1;
ks.info &= ~IS_E1;
}
/**
* Change the top item to avoid encountering
* E0 or E1 twice.
*/
hv_kbd_modify_top(sc, &ks);
} else if (ks.info & IS_UNICODE) {
/**
* XXX: Hyperv host send unicode to VM through
* 'Type clipboard text', the mapping from
* unicode to scancode depends on the keymap.
* It is so complicated that we do not plan to
* support it yet.
*/
if (bootverbose)
device_printf(sc->dev, "Unsupported unicode\n");
hv_kbd_remove_top(sc);
return (NOKEY);
} else {
scancode = ks.makecode;
if (ks.info & IS_BREAK) {
scancode |= XTKBD_RELEASE;
}
hv_kbd_remove_top(sc);
}
} else {
if (bootverbose)
device_printf(sc->dev, "Unsupported mode: %d\n", sc->sc_mode);
}
++kbd->kb_count;
DEBUG_HVKBD(kbd, "read scan: 0x%x\n", scancode);
return scancode;
}
/* Currently wait is always false. */
static uint32_t
hvkbd_read_char(keyboard_t *kbd, int wait)
{
uint32_t keycode;
HVKBD_LOCK();
keycode = hvkbd_read_char_locked(kbd, wait);
HVKBD_UNLOCK();
return (keycode);
}
/* clear the internal state of the keyboard */
static void
hvkbd_clear_state(keyboard_t *kbd)
{
hv_kbd_sc *sc = kbd->kb_data;
sc->sc_state &= LOCK_MASK; /* preserve locking key state */
sc->sc_flags &= ~HVKBD_FLAG_POLLING;
}
static int
hvkbd_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
int i;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
int ival;
#endif
hv_kbd_sc *sc = kbd->kb_data;
switch (cmd) {
case KDGKBMODE:
*(int *)arg = sc->sc_mode;
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 7):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBMODE: /* set keyboard mode */
DEBUG_HVKBD(kbd, "expected mode: %x\n", *(int *)arg);
switch (*(int *)arg) {
case K_XLATE:
if (sc->sc_mode != K_XLATE) {
/* make lock key state and LED state match */
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= KBD_LED_VAL(kbd);
}
/* FALLTHROUGH */
case K_RAW:
case K_CODE:
if (sc->sc_mode != *(int *)arg) {
DEBUG_HVKBD(kbd, "mod changed to %x\n", *(int *)arg);
if ((sc->sc_flags & HVKBD_FLAG_POLLING) == 0)
hvkbd_clear_state(kbd);
sc->sc_mode = *(int *)arg;
}
break;
default:
return (EINVAL);
}
break;
case KDGKBSTATE: /* get lock key state */
*(int *)arg = sc->sc_state & LOCK_MASK;
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 20):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBSTATE: /* set lock key state */
if (*(int *)arg & ~LOCK_MASK) {
return (EINVAL);
}
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= *(int *)arg;
return hvkbd_ioctl_locked(kbd, KDSETLED, arg);
case KDGETLED: /* get keyboard LED */
*(int *)arg = KBD_LED_VAL(kbd);
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 66):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETLED: /* set keyboard LED */
/* NOTE: lock key state in "sc_state" won't be changed */
if (*(int *)arg & ~LOCK_MASK)
return (EINVAL);
i = *(int *)arg;
/* replace CAPS LED with ALTGR LED for ALTGR keyboards */
if (sc->sc_mode == K_XLATE &&
kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
if (i & ALKED)
i |= CLKED;
else
i &= ~CLKED;
}
if (KBD_HAS_DEVICE(kbd)) {
DEBUG_HVSC(sc, "setled 0x%x\n", *(int *)arg);
}
KBD_LED_VAL(kbd) = *(int *)arg;
break;
default:
return (genkbd_commonioctl(kbd, cmd, arg));
}
return (0);
}
/* some useful control functions */
static int
hvkbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
DEBUG_HVKBD(kbd, "%s: %lx start\n", __func__, cmd);
HVKBD_LOCK();
int ret = hvkbd_ioctl_locked(kbd, cmd, arg);
HVKBD_UNLOCK();
DEBUG_HVKBD(kbd, "%s: %lx end %d\n", __func__, cmd, ret);
return (ret);
}
/* read one byte from the keyboard if it's allowed */
/* Currently unused. */
static int
hvkbd_read(keyboard_t *kbd, int wait)
{
DEBUG_HVKBD(kbd, "%s\n", __func__);
HVKBD_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (-1);
return hvkbd_read_char_locked(kbd, wait);
}
static keyboard_switch_t hvkbdsw = {
.probe = hvkbd_probe, /* not used */
.init = hvkbd_init,
.term = hvkbd_term, /* not used */
.intr = hvkbd_intr, /* not used */
.test_if = hvkbd_test_if, /* not used */
.enable = hvkbd_enable,
.disable = hvkbd_disable,
.read = hvkbd_read,
.check = hvkbd_check,
.read_char = hvkbd_read_char,
.check_char = hvkbd_check_char,
.ioctl = hvkbd_ioctl,
.lock = hvkbd_lock, /* not used */
.clear_state = hvkbd_clear_state,
.get_state = hvkbd_get_state, /* not used */
.set_state = hvkbd_set_state, /* not used */
- .get_fkeystr = genkbd_get_fkeystr,
.poll = hvkbd_poll,
- .diag = genkbd_diag,
};
KEYBOARD_DRIVER(hvkbd, hvkbdsw, hvkbd_configure);
void
hv_kbd_intr(hv_kbd_sc *sc)
{
uint32_t c;
if ((sc->sc_flags & HVKBD_FLAG_POLLING) != 0)
return;
if (KBD_IS_ACTIVE(&sc->sc_kbd) &&
KBD_IS_BUSY(&sc->sc_kbd)) {
/* let the callback function process the input */
(sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT,
sc->sc_kbd.kb_callback.kc_arg);
} else {
/* read and discard the input, no one is waiting for it */
do {
c = hvkbd_read_char(&sc->sc_kbd, 0);
} while (c != NOKEY);
}
}
int
hvkbd_driver_load(module_t mod, int what, void *arg)
{
switch (what) {
case MOD_LOAD:
kbd_add_driver(&hvkbd_kbd_driver);
break;
case MOD_UNLOAD:
kbd_delete_driver(&hvkbd_kbd_driver);
break;
}
return (0);
}
int
hv_kbd_drv_attach(device_t dev)
{
hv_kbd_sc *sc = device_get_softc(dev);
int unit = device_get_unit(dev);
keyboard_t *kbd = &sc->sc_kbd;
keyboard_switch_t *sw;
sw = kbd_get_switch(HVKBD_DRIVER_NAME);
if (sw == NULL) {
return (ENXIO);
}
kbd_init_struct(kbd, HVKBD_DRIVER_NAME, KB_OTHER, unit, 0, 0, 0);
kbd->kb_data = (void *)sc;
kbd_set_maps(kbd, &key_map, &accent_map, fkey_tab, nitems(fkey_tab));
KBD_FOUND_DEVICE(kbd);
hvkbd_clear_state(kbd);
KBD_PROBE_DONE(kbd);
KBD_INIT_DONE(kbd);
sc->sc_mode = K_RAW;
(*sw->enable)(kbd);
if (kbd_register(kbd) < 0) {
goto detach;
}
KBD_CONFIG_DONE(kbd);
#ifdef KBD_INSTALL_CDEV
if (kbd_attach(kbd)) {
goto detach;
}
#endif
if (bootverbose) {
kbdd_diag(kbd, bootverbose);
}
return (0);
detach:
hv_kbd_drv_detach(dev);
return (ENXIO);
}
int
hv_kbd_drv_detach(device_t dev)
{
int error = 0;
hv_kbd_sc *sc = device_get_softc(dev);
hvkbd_disable(&sc->sc_kbd);
if (KBD_IS_CONFIGURED(&sc->sc_kbd)) {
error = kbd_unregister(&sc->sc_kbd);
if (error) {
device_printf(dev, "WARNING: kbd_unregister() "
"returned non-zero! (ignored)\n");
}
}
#ifdef KBD_INSTALL_CDEV
error = kbd_detach(&sc->sc_kbd);
#endif
return (error);
}
Index: stable/11/sys/dev/kbd/kbd.c
===================================================================
--- stable/11/sys/dev/kbd/kbd.c (revision 356012)
+++ stable/11/sys/dev/kbd/kbd.c (revision 356013)
@@ -1,1487 +1,1508 @@
/*-
* Copyright (c) 1999 Kazutaka YOKOTA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer as
* the first lines of this file unmodified.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include
__FBSDID("$FreeBSD$");
#include "opt_kbd.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define KBD_INDEX(dev) dev2unit(dev)
#define KB_QSIZE 512
#define KB_BUFSIZE 64
typedef struct genkbd_softc {
int gkb_flags; /* flag/status bits */
#define KB_ASLEEP (1 << 0)
struct selinfo gkb_rsel;
char gkb_q[KB_QSIZE]; /* input queue */
unsigned int gkb_q_start;
unsigned int gkb_q_length;
} genkbd_softc_t;
static SLIST_HEAD(, keyboard_driver) keyboard_drivers =
SLIST_HEAD_INITIALIZER(keyboard_drivers);
SET_DECLARE(kbddriver_set, const keyboard_driver_t);
/* local arrays */
/*
* We need at least one entry each in order to initialize a keyboard
* for the kernel console. The arrays will be increased dynamically
* when necessary.
*/
static int keyboards = 1;
static keyboard_t *kbd_ini;
static keyboard_t **keyboard = &kbd_ini;
static keyboard_switch_t *kbdsw_ini;
keyboard_switch_t **kbdsw = &kbdsw_ini;
static int keymap_restrict_change;
static SYSCTL_NODE(_hw, OID_AUTO, kbd, CTLFLAG_RD, 0, "kbd");
SYSCTL_INT(_hw_kbd, OID_AUTO, keymap_restrict_change, CTLFLAG_RW,
&keymap_restrict_change, 0, "restrict ability to change keymap");
#define ARRAY_DELTA 4
static int
kbd_realloc_array(void)
{
keyboard_t **new_kbd;
keyboard_switch_t **new_kbdsw;
int newsize;
int s;
s = spltty();
newsize = rounddown(keyboards + ARRAY_DELTA, ARRAY_DELTA);
new_kbd = malloc(sizeof(*new_kbd)*newsize, M_DEVBUF, M_NOWAIT|M_ZERO);
if (new_kbd == NULL) {
splx(s);
return (ENOMEM);
}
new_kbdsw = malloc(sizeof(*new_kbdsw)*newsize, M_DEVBUF,
M_NOWAIT|M_ZERO);
if (new_kbdsw == NULL) {
free(new_kbd, M_DEVBUF);
splx(s);
return (ENOMEM);
}
bcopy(keyboard, new_kbd, sizeof(*keyboard)*keyboards);
bcopy(kbdsw, new_kbdsw, sizeof(*kbdsw)*keyboards);
if (keyboards > 1) {
free(keyboard, M_DEVBUF);
free(kbdsw, M_DEVBUF);
}
keyboard = new_kbd;
kbdsw = new_kbdsw;
keyboards = newsize;
splx(s);
if (bootverbose)
printf("kbd: new array size %d\n", keyboards);
return (0);
}
/*
* Low-level keyboard driver functions
* Keyboard subdrivers, such as the AT keyboard driver and the USB keyboard
* driver, call these functions to initialize the keyboard_t structure
* and register it to the virtual keyboard driver `kbd'.
*/
/* initialize the keyboard_t structure */
void
kbd_init_struct(keyboard_t *kbd, char *name, int type, int unit, int config,
int port, int port_size)
{
kbd->kb_flags = KB_NO_DEVICE; /* device has not been found */
kbd->kb_name = name;
kbd->kb_type = type;
kbd->kb_unit = unit;
kbd->kb_config = config & ~KB_CONF_PROBE_ONLY;
kbd->kb_led = 0; /* unknown */
kbd->kb_io_base = port;
kbd->kb_io_size = port_size;
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; /* these values are advisory only */
kbd->kb_delay2 = KB_DELAY2;
kbd->kb_count = 0L;
bzero(kbd->kb_lastact, sizeof(kbd->kb_lastact));
}
void
kbd_set_maps(keyboard_t *kbd, keymap_t *keymap, accentmap_t *accmap,
fkeytab_t *fkeymap, int fkeymap_size)
{
kbd->kb_keymap = keymap;
kbd->kb_accentmap = accmap;
kbd->kb_fkeytab = fkeymap;
kbd->kb_fkeytab_size = fkeymap_size;
}
/* declare a new keyboard driver */
int
kbd_add_driver(keyboard_driver_t *driver)
{
if (SLIST_NEXT(driver, link))
return (EINVAL);
+ if (driver->kbdsw->get_fkeystr == NULL)
+ driver->kbdsw->get_fkeystr = genkbd_get_fkeystr;
+ if (driver->kbdsw->diag == NULL)
+ driver->kbdsw->diag = genkbd_diag;
SLIST_INSERT_HEAD(&keyboard_drivers, driver, link);
return (0);
}
int
kbd_delete_driver(keyboard_driver_t *driver)
{
SLIST_REMOVE(&keyboard_drivers, driver, keyboard_driver, link);
SLIST_NEXT(driver, link) = NULL;
return (0);
}
/* register a keyboard and associate it with a function table */
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;
mux = kbd_get_keyboard(kbd_find_keyboard("kbdmux", -1));
for (index = 0; index < keyboards; ++index) {
if (keyboard[index] == NULL)
break;
}
if (index >= keyboards) {
if (kbd_realloc_array())
return (-1);
}
kbd->kb_index = index;
KBD_UNBUSY(kbd);
KBD_VALID(kbd);
kbd->kb_active = 0; /* disabled until someone calls kbd_enable() */
kbd->kb_token = NULL;
kbd->kb_callback.kc_func = NULL;
kbd->kb_callback.kc_arg = NULL;
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;
(void)kbdd_ioctl(mux, KBADDKBD, (caddr_t) &ki);
}
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;
(void)kbdd_ioctl(mux, KBADDKBD, (caddr_t) &ki);
}
return (index);
}
}
return (-1);
}
int
kbd_unregister(keyboard_t *kbd)
{
int error;
int s;
if ((kbd->kb_index < 0) || (kbd->kb_index >= keyboards))
return (ENOENT);
if (keyboard[kbd->kb_index] != kbd)
return (ENOENT);
s = spltty();
if (KBD_IS_BUSY(kbd)) {
error = (*kbd->kb_callback.kc_func)(kbd, KBDIO_UNLOADING,
kbd->kb_callback.kc_arg);
if (error) {
splx(s);
return (error);
}
if (KBD_IS_BUSY(kbd)) {
splx(s);
return (EBUSY);
}
}
KBD_INVALID(kbd);
keyboard[kbd->kb_index] = NULL;
kbdsw[kbd->kb_index] = NULL;
splx(s);
return (0);
}
/* find a function table by the driver name */
keyboard_switch_t *
kbd_get_switch(char *driver)
{
const keyboard_driver_t **list;
const keyboard_driver_t *p;
SLIST_FOREACH(p, &keyboard_drivers, link) {
if (strcmp(p->name, driver) == 0)
return (p->kbdsw);
}
SET_FOREACH(list, kbddriver_set) {
p = *list;
if (strcmp(p->name, driver) == 0)
return (p->kbdsw);
}
return (NULL);
}
/*
* Keyboard client functions
* Keyboard clients, such as the console driver `syscons' and the keyboard
* cdev driver, use these functions to claim and release a keyboard for
* exclusive use.
*/
/*
* find the keyboard specified by a driver name and a unit number
* starting at given index
*/
int
kbd_find_keyboard2(char *driver, int unit, int index)
{
int i;
if ((index < 0) || (index >= keyboards))
return (-1);
for (i = index; i < keyboards; ++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;
return (i);
}
return (-1);
}
/* find the keyboard specified by a driver name and a unit number */
int
kbd_find_keyboard(char *driver, int unit)
{
return (kbd_find_keyboard2(driver, unit, 0));
}
/* allocate a keyboard */
int
kbd_allocate(char *driver, int unit, void *id, kbd_callback_func_t *func,
void *arg)
{
int index;
int s;
if (func == NULL)
return (-1);
s = spltty();
index = kbd_find_keyboard(driver, unit);
if (index >= 0) {
if (KBD_IS_BUSY(keyboard[index])) {
splx(s);
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;
kbdd_clear_state(keyboard[index]);
}
splx(s);
return (index);
}
int
kbd_release(keyboard_t *kbd, void *id)
{
int error;
int s;
s = spltty();
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;
kbdd_clear_state(kbd);
error = 0;
}
splx(s);
return (error);
}
int
kbd_change_callback(keyboard_t *kbd, void *id, kbd_callback_func_t *func,
void *arg)
{
int error;
int s;
s = spltty();
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;
}
splx(s);
return (error);
}
/* get a keyboard structure */
keyboard_t *
kbd_get_keyboard(int index)
{
if ((index < 0) || (index >= keyboards))
return (NULL);
if (keyboard[index] == NULL)
return (NULL);
if (!KBD_IS_VALID(keyboard[index]))
return (NULL);
return (keyboard[index]);
}
/*
* The back door for the console driver; configure keyboards
* This function is for the kernel console to initialize keyboards
* at very early stage.
*/
int
kbd_configure(int flags)
{
const keyboard_driver_t **list;
const keyboard_driver_t *p;
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);
}
return (0);
}
#ifdef KBD_INSTALL_CDEV
/*
* Virtual keyboard cdev driver functions
* The virtual keyboard driver dispatches driver functions to
* appropriate subdrivers.
*/
#define KBD_UNIT(dev) dev2unit(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_poll_t genkbdpoll;
static struct cdevsw kbd_cdevsw = {
.d_version = D_VERSION,
.d_flags = D_NEEDGIANT,
.d_open = genkbdopen,
.d_close = genkbdclose,
.d_read = genkbdread,
.d_write = genkbdwrite,
.d_ioctl = genkbdioctl,
.d_poll = genkbdpoll,
.d_name = "kbd",
};
int
kbd_attach(keyboard_t *kbd)
{
if (kbd->kb_index >= keyboards)
return (EINVAL);
if (keyboard[kbd->kb_index] != kbd)
return (EINVAL);
kbd->kb_dev = make_dev(&kbd_cdevsw, kbd->kb_index, UID_ROOT, GID_WHEEL,
0600, "%s%r", kbd->kb_name, kbd->kb_unit);
make_dev_alias(kbd->kb_dev, "kbd%r", kbd->kb_index);
kbd->kb_dev->si_drv1 = malloc(sizeof(genkbd_softc_t), M_DEVBUF,
M_WAITOK | M_ZERO);
printf("kbd%d at %s%d\n", kbd->kb_index, kbd->kb_name, kbd->kb_unit);
return (0);
}
int
kbd_detach(keyboard_t *kbd)
{
if (kbd->kb_index >= keyboards)
return (EINVAL);
if (keyboard[kbd->kb_index] != kbd)
return (EINVAL);
free(kbd->kb_dev->si_drv1, M_DEVBUF);
destroy_dev(kbd->kb_dev);
return (0);
}
/*
* Generic keyboard cdev driver functions
* Keyboard subdrivers may call these functions to implement common
* driver functions.
*/
static void
genkbd_putc(genkbd_softc_t *sc, char c)
{
unsigned int p;
if (sc->gkb_q_length == KB_QSIZE)
return;
p = (sc->gkb_q_start + sc->gkb_q_length) % KB_QSIZE;
sc->gkb_q[p] = c;
sc->gkb_q_length++;
}
static size_t
genkbd_getc(genkbd_softc_t *sc, char *buf, size_t len)
{
/* Determine copy size. */
if (sc->gkb_q_length == 0)
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;
/* Copy out data and progress offset. */
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;
return (len);
}
static kbd_callback_func_t genkbd_event;
static int
genkbdopen(struct cdev *dev, int mode, int flag, struct thread *td)
{
keyboard_t *kbd;
genkbd_softc_t *sc;
int s;
int i;
s = spltty();
sc = dev->si_drv1;
kbd = kbd_get_keyboard(KBD_INDEX(dev));
if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
splx(s);
return (ENXIO);
}
i = kbd_allocate(kbd->kb_name, kbd->kb_unit, sc,
genkbd_event, (void *)sc);
if (i < 0) {
splx(s);
return (EBUSY);
}
/* assert(i == kbd->kb_index) */
/* assert(kbd == kbd_get_keyboard(i)) */
/*
* NOTE: even when we have successfully claimed a keyboard,
* the device may still be missing (!KBD_HAS_DEVICE(kbd)).
*/
sc->gkb_q_length = 0;
splx(s);
return (0);
}
static int
genkbdclose(struct cdev *dev, int mode, int flag, struct thread *td)
{
keyboard_t *kbd;
genkbd_softc_t *sc;
int s;
/*
* NOTE: the device may have already become invalid.
* kbd == NULL || !KBD_IS_VALID(kbd)
*/
s = spltty();
sc = dev->si_drv1;
kbd = kbd_get_keyboard(KBD_INDEX(dev));
if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
/* XXX: we shall be forgiving and don't report error... */
} else {
kbd_release(kbd, (void *)sc);
}
splx(s);
return (0);
}
static int
genkbdread(struct cdev *dev, struct uio *uio, int flag)
{
keyboard_t *kbd;
genkbd_softc_t *sc;
u_char buffer[KB_BUFSIZE];
int len;
int error;
int s;
/* wait for input */
s = spltty();
sc = dev->si_drv1;
kbd = kbd_get_keyboard(KBD_INDEX(dev));
if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
splx(s);
return (ENXIO);
}
while (sc->gkb_q_length == 0) {
if (flag & O_NONBLOCK) {
splx(s);
return (EWOULDBLOCK);
}
sc->gkb_flags |= KB_ASLEEP;
error = tsleep(sc, PZERO | PCATCH, "kbdrea", 0);
kbd = kbd_get_keyboard(KBD_INDEX(dev));
if ((kbd == NULL) || !KBD_IS_VALID(kbd)) {
splx(s);
return (ENXIO); /* our keyboard has gone... */
}
if (error) {
sc->gkb_flags &= ~KB_ASLEEP;
splx(s);
return (error);
}
}
splx(s);
/* copy as much input as possible */
error = 0;
while (uio->uio_resid > 0) {
len = imin(uio->uio_resid, sizeof(buffer));
len = genkbd_getc(sc, buffer, len);
if (len <= 0)
break;
error = uiomove(buffer, len, uio);
if (error)
break;
}
return (error);
}
static int
genkbdwrite(struct cdev *dev, struct uio *uio, int flag)
{
keyboard_t *kbd;
kbd = kbd_get_keyboard(KBD_INDEX(dev));
if ((kbd == NULL) || !KBD_IS_VALID(kbd))
return (ENXIO);
return (ENODEV);
}
static int
genkbdioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
{
keyboard_t *kbd;
int error;
kbd = kbd_get_keyboard(KBD_INDEX(dev));
if ((kbd == NULL) || !KBD_IS_VALID(kbd))
return (ENXIO);
error = kbdd_ioctl(kbd, cmd, arg);
if (error == ENOIOCTL)
error = ENODEV;
return (error);
}
static int
genkbdpoll(struct cdev *dev, int events, struct thread *td)
{
keyboard_t *kbd;
genkbd_softc_t *sc;
int revents;
int s;
revents = 0;
s = spltty();
sc = dev->si_drv1;
kbd = kbd_get_keyboard(KBD_INDEX(dev));
if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) {
revents = POLLHUP; /* the keyboard has gone */
} else if (events & (POLLIN | POLLRDNORM)) {
if (sc->gkb_q_length > 0)
revents = events & (POLLIN | POLLRDNORM);
else
selrecord(td, &sc->gkb_rsel);
}
splx(s);
return (revents);
}
static int
genkbd_event(keyboard_t *kbd, int event, void *arg)
{
genkbd_softc_t *sc;
size_t len;
u_char *cp;
int mode;
u_int c;
/* assert(KBD_IS_VALID(kbd)) */
sc = (genkbd_softc_t *)arg;
switch (event) {
case KBDIO_KEYINPUT:
break;
case KBDIO_UNLOADING:
/* the keyboard is going... */
kbd_release(kbd, (void *)sc);
if (sc->gkb_flags & KB_ASLEEP) {
sc->gkb_flags &= ~KB_ASLEEP;
wakeup(sc);
}
selwakeuppri(&sc->gkb_rsel, PZERO);
return (0);
default:
return (EINVAL);
}
/* obtain the current key input mode */
if (kbdd_ioctl(kbd, KDGKBMODE, (caddr_t)&mode))
mode = K_XLATE;
/* read all pending input */
while (kbdd_check_char(kbd)) {
c = kbdd_read_char(kbd, FALSE);
if (c == NOKEY)
continue;
if (c == ERRKEY) /* XXX: ring bell? */
continue;
if (!KBD_IS_BUSY(kbd))
/* the device is not open, discard the input */
continue;
/* store the byte as is for K_RAW and K_CODE modes */
if (mode != K_XLATE) {
genkbd_putc(sc, KEYCHAR(c));
continue;
}
/* K_XLATE */
if (c & RELKEY) /* key release is ignored */
continue;
/* process special keys; most of them are just ignored... */
if (c & SPCLKEY) {
switch (KEYCHAR(c)) {
default:
/* ignore them... */
continue;
case BTAB: /* a backtab: ESC [ Z */
genkbd_putc(sc, 0x1b);
genkbd_putc(sc, '[');
genkbd_putc(sc, 'Z');
continue;
}
}
/* normal chars, normal chars with the META, function keys */
switch (KEYFLAGS(c)) {
case 0: /* a normal char */
genkbd_putc(sc, KEYCHAR(c));
break;
case MKEY: /* the META flag: prepend ESC */
genkbd_putc(sc, 0x1b);
genkbd_putc(sc, KEYCHAR(c));
break;
case FKEY | SPCLKEY: /* a function key, return string */
cp = kbdd_get_fkeystr(kbd, KEYCHAR(c), &len);
if (cp != NULL) {
while (len-- > 0)
genkbd_putc(sc, *cp++);
}
break;
}
}
/* wake up sleeping/polling processes */
if (sc->gkb_q_length > 0) {
if (sc->gkb_flags & KB_ASLEEP) {
sc->gkb_flags &= ~KB_ASLEEP;
wakeup(sc);
}
selwakeuppri(&sc->gkb_rsel, PZERO);
}
return (0);
}
#endif /* KBD_INSTALL_CDEV */
/*
* Generic low-level keyboard functions
* The low-level functions in the keyboard subdriver may use these
* functions.
*/
#ifndef KBD_DISABLE_KEYMAP_LOAD
static int key_change_ok(struct keyent_t *, struct keyent_t *, struct thread *);
static int keymap_change_ok(keymap_t *, keymap_t *, struct thread *);
static int accent_change_ok(accentmap_t *, accentmap_t *, struct thread *);
static int fkey_change_ok(fkeytab_t *, fkeyarg_t *, struct thread *);
#endif
int
genkbd_commonioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
keymap_t *mapp;
okeymap_t *omapp;
keyarg_t *keyp;
fkeyarg_t *fkeyp;
int s;
int i, j;
int error;
s = spltty();
switch (cmd) {
case KDGKBINFO: /* get keyboard information */
((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: /* get keyboard type */
*(int *)arg = kbd->kb_type;
break;
case KDGETREPEAT: /* get keyboard repeat rate */
((int *)arg)[0] = kbd->kb_delay1;
((int *)arg)[1] = kbd->kb_delay2;
break;
case GIO_KEYMAP: /* get keyboard translation table */
error = copyout(kbd->kb_keymap, *(void **)arg,
sizeof(keymap_t));
splx(s);
return (error);
case OGIO_KEYMAP: /* get keyboard translation table (compat) */
mapp = kbd->kb_keymap;
omapp = (okeymap_t *)arg;
omapp->n_keys = mapp->n_keys;
for (i = 0; i < NUM_KEYS; i++) {
for (j = 0; j < NUM_STATES; j++)
omapp->key[i].map[j] =
mapp->key[i].map[j];
omapp->key[i].spcl = mapp->key[i].spcl;
omapp->key[i].flgs = mapp->key[i].flgs;
}
break;
case PIO_KEYMAP: /* set keyboard translation table */
case OPIO_KEYMAP: /* set keyboard translation table (compat) */
#ifndef KBD_DISABLE_KEYMAP_LOAD
mapp = malloc(sizeof *mapp, M_TEMP, M_WAITOK);
if (cmd == OPIO_KEYMAP) {
omapp = (okeymap_t *)arg;
mapp->n_keys = omapp->n_keys;
for (i = 0; i < NUM_KEYS; i++) {
for (j = 0; j < NUM_STATES; j++)
mapp->key[i].map[j] =
omapp->key[i].map[j];
mapp->key[i].spcl = omapp->key[i].spcl;
mapp->key[i].flgs = omapp->key[i].flgs;
}
} else {
error = copyin(*(void **)arg, mapp, sizeof *mapp);
if (error != 0) {
splx(s);
free(mapp, M_TEMP);
return (error);
}
}
error = keymap_change_ok(kbd->kb_keymap, mapp, curthread);
if (error != 0) {
splx(s);
free(mapp, M_TEMP);
return (error);
}
bzero(kbd->kb_accentmap, sizeof(*kbd->kb_accentmap));
bcopy(mapp, kbd->kb_keymap, sizeof(*kbd->kb_keymap));
free(mapp, M_TEMP);
break;
#else
splx(s);
return (ENODEV);
#endif
case GIO_KEYMAPENT: /* get keyboard translation table entry */
keyp = (keyarg_t *)arg;
if (keyp->keynum >= sizeof(kbd->kb_keymap->key) /
sizeof(kbd->kb_keymap->key[0])) {
splx(s);
return (EINVAL);
}
bcopy(&kbd->kb_keymap->key[keyp->keynum], &keyp->key,
sizeof(keyp->key));
break;
case PIO_KEYMAPENT: /* set keyboard translation table entry */
#ifndef KBD_DISABLE_KEYMAP_LOAD
keyp = (keyarg_t *)arg;
if (keyp->keynum >= sizeof(kbd->kb_keymap->key) /
sizeof(kbd->kb_keymap->key[0])) {
splx(s);
return (EINVAL);
}
error = key_change_ok(&kbd->kb_keymap->key[keyp->keynum],
&keyp->key, curthread);
if (error != 0) {
splx(s);
return (error);
}
bcopy(&keyp->key, &kbd->kb_keymap->key[keyp->keynum],
sizeof(keyp->key));
break;
#else
splx(s);
return (ENODEV);
#endif
case GIO_DEADKEYMAP: /* get accent key translation table */
bcopy(kbd->kb_accentmap, arg, sizeof(*kbd->kb_accentmap));
break;
case PIO_DEADKEYMAP: /* set accent key translation table */
#ifndef KBD_DISABLE_KEYMAP_LOAD
error = accent_change_ok(kbd->kb_accentmap,
(accentmap_t *)arg, curthread);
if (error != 0) {
splx(s);
return (error);
}
bcopy(arg, kbd->kb_accentmap, sizeof(*kbd->kb_accentmap));
break;
#else
splx(s);
return (ENODEV);
#endif
case GETFKEY: /* get functionkey string */
fkeyp = (fkeyarg_t *)arg;
if (fkeyp->keynum >= kbd->kb_fkeytab_size) {
splx(s);
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: /* set functionkey string */
#ifndef KBD_DISABLE_KEYMAP_LOAD
fkeyp = (fkeyarg_t *)arg;
if (fkeyp->keynum >= kbd->kb_fkeytab_size) {
splx(s);
return (EINVAL);
}
error = fkey_change_ok(&kbd->kb_fkeytab[fkeyp->keynum],
fkeyp, curthread);
if (error != 0) {
splx(s);
return (error);
}
kbd->kb_fkeytab[fkeyp->keynum].len = min(fkeyp->flen, MAXFK);
bcopy(fkeyp->keydef, kbd->kb_fkeytab[fkeyp->keynum].str,
kbd->kb_fkeytab[fkeyp->keynum].len);
break;
#else
splx(s);
return (ENODEV);
#endif
default:
splx(s);
return (ENOIOCTL);
}
splx(s);
return (0);
}
#ifndef KBD_DISABLE_KEYMAP_LOAD
#define RESTRICTED_KEY(key, i) \
((key->spcl & (0x80 >> i)) && \
(key->map[i] == RBT || key->map[i] == SUSP || \
key->map[i] == STBY || key->map[i] == DBG || \
key->map[i] == PNC || key->map[i] == HALT || \
key->map[i] == PDWN))
static int
key_change_ok(struct keyent_t *oldkey, struct keyent_t *newkey, struct thread *td)
{
int i;
/* Low keymap_restrict_change means any changes are OK. */
if (keymap_restrict_change <= 0)
return (0);
/* High keymap_restrict_change means only root can change the keymap. */
if (keymap_restrict_change >= 2) {
for (i = 0; i < NUM_STATES; i++)
if (oldkey->map[i] != newkey->map[i])
return priv_check(td, PRIV_KEYBOARD);
if (oldkey->spcl != newkey->spcl)
return priv_check(td, PRIV_KEYBOARD);
if (oldkey->flgs != newkey->flgs)
return priv_check(td, PRIV_KEYBOARD);
return (0);
}
/* Otherwise we have to see if any special keys are being changed. */
for (i = 0; i < NUM_STATES; i++) {
/*
* If either the oldkey or the newkey action is restricted
* then we must make sure that the action doesn't change.
*/
if (!RESTRICTED_KEY(oldkey, i) && !RESTRICTED_KEY(newkey, i))
continue;
if ((oldkey->spcl & (0x80 >> i)) == (newkey->spcl & (0x80 >> i))
&& oldkey->map[i] == newkey->map[i])
continue;
return priv_check(td, PRIV_KEYBOARD);
}
return (0);
}
static int
keymap_change_ok(keymap_t *oldmap, keymap_t *newmap, struct thread *td)
{
int keycode, error;
for (keycode = 0; keycode < NUM_KEYS; keycode++) {
if ((error = key_change_ok(&oldmap->key[keycode],
&newmap->key[keycode], td)) != 0)
return (error);
}
return (0);
}
static int
accent_change_ok(accentmap_t *oldmap, accentmap_t *newmap, struct thread *td)
{
struct acc_t *oldacc, *newacc;
int accent, i;
if (keymap_restrict_change <= 2)
return (0);
if (oldmap->n_accs != newmap->n_accs)
return priv_check(td, PRIV_KEYBOARD);
for (accent = 0; accent < oldmap->n_accs; accent++) {
oldacc = &oldmap->acc[accent];
newacc = &newmap->acc[accent];
if (oldacc->accchar != newacc->accchar)
return priv_check(td, PRIV_KEYBOARD);
for (i = 0; i < NUM_ACCENTCHARS; ++i) {
if (oldacc->map[i][0] != newacc->map[i][0])
return priv_check(td, PRIV_KEYBOARD);
if (oldacc->map[i][0] == 0) /* end of table */
break;
if (oldacc->map[i][1] != newacc->map[i][1])
return priv_check(td, PRIV_KEYBOARD);
}
}
return (0);
}
static int
fkey_change_ok(fkeytab_t *oldkey, fkeyarg_t *newkey, struct thread *td)
{
if (keymap_restrict_change <= 3)
return (0);
if (oldkey->len != newkey->flen ||
bcmp(oldkey->str, newkey->keydef, oldkey->len) != 0)
return priv_check(td, PRIV_KEYBOARD);
return (0);
}
#endif
/* get a pointer to the string associated with the given function key */
u_char *
genkbd_get_fkeystr(keyboard_t *kbd, int fkey, size_t *len)
{
if (kbd == NULL)
return (NULL);
fkey -= F_FN;
if (fkey > kbd->kb_fkeytab_size)
return (NULL);
*len = kbd->kb_fkeytab[fkey].len;
return (kbd->kb_fkeytab[fkey].str);
}
/* diagnostic dump */
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 < nitems(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) {
printf("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)
printf(", port:0x%x-0x%x", kbd->kb_io_base,
kbd->kb_io_base + kbd->kb_io_size - 1);
printf("\n");
}
}
#define set_lockkey_state(k, s, l) \
if (!((s) & l ## DOWN)) { \
int i; \
(s) |= l ## DOWN; \
(s) ^= l ## ED; \
i = (s) & LOCK_MASK; \
(void)kbdd_ioctl((k), KDSETLED, (caddr_t)&i); \
}
static u_int
save_accent_key(keyboard_t *kbd, u_int key, int *accents)
{
int i;
/* make an index into the accent map */
i = key - F_ACC + 1;
if ((i > kbd->kb_accentmap->n_accs)
|| (kbd->kb_accentmap->acc[i - 1].accchar == 0)) {
/* the index is out of range or pointing to an empty entry */
*accents = 0;
return (ERRKEY);
}
/*
* If the same accent key has been hit twice, produce the accent
* char itself.
*/
if (i == *accents) {
key = kbd->kb_accentmap->acc[i - 1].accchar;
*accents = 0;
return (key);
}
/* remember the index and wait for the next key */
*accents = i;
return (NOKEY);
}
static u_int
make_accent_char(keyboard_t *kbd, u_int ch, int *accents)
{
struct acc_t *acc;
int i;
acc = &kbd->kb_accentmap->acc[*accents - 1];
*accents = 0;
/*
* If the accent key is followed by the space key,
* produce the accent char itself.
*/
if (ch == ' ')
return (acc->accchar);
/* scan the accent map */
for (i = 0; i < NUM_ACCENTCHARS; ++i) {
if (acc->map[i][0] == 0) /* end of table */
break;
if (acc->map[i][0] == ch)
return (acc->map[i][1]);
}
/* this char cannot be accented... */
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;
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) { /* break: key released */
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;
/* FALL THROUGH */
case LSH:
state &= ~SHIFTS1;
break;
case RSHA:
if (state & SHIFTAON) {
set_lockkey_state(kbd, state, ALK);
state &= ~ALKDOWN;
}
action = RSH;
/* FALL THROUGH */
case RSH:
state &= ~SHIFTS2;
break;
case LCTRA:
if (state & SHIFTAON) {
set_lockkey_state(kbd, state, ALK);
state &= ~ALKDOWN;
}
action = LCTR;
/* FALL THROUGH */
case LCTR:
state &= ~CTLS1;
break;
case RCTRA:
if (state & SHIFTAON) {
set_lockkey_state(kbd, state, ALK);
state &= ~ALKDOWN;
}
action = RCTR;
/* FALL THROUGH */
case RCTR:
state &= ~CTLS2;
break;
case LALTA:
if (state & SHIFTAON) {
set_lockkey_state(kbd, state, ALK);
state &= ~ALKDOWN;
}
action = LALT;
/* FALL THROUGH */
case LALT:
state &= ~ALTS1;
break;
case RALTA:
if (state & SHIFTAON) {
set_lockkey_state(kbd, state, ALK);
state &= ~ALKDOWN;
}
action = RALT;
/* FALL THROUGH */
case RALT:
state &= ~ALTS2;
break;
case ASH:
state &= ~AGRS1;
break;
case META:
state &= ~METAS1;
break;
case NLK:
state &= ~NLKDOWN;
break;
case CLK:
#ifndef PC98
state &= ~CLKDOWN;
#else
state &= ~CLKED;
i = state & LOCK_MASK;
(void)kbdd_ioctl(kbd, KDSETLED, (caddr_t)&i);
#endif
break;
case SLK:
state &= ~SLKDOWN;
break;
case ALK:
state &= ~ALKDOWN;
break;
case NOP:
/* release events of regular keys are not reported */
*shiftstate &= ~SHIFTAON;
return (NOKEY);
}
*shiftstate = state & ~SHIFTAON;
return (SPCLKEY | RELKEY | action);
} else { /* make: key pressed */
action = key->map[i];
state &= ~SHIFTAON;
if (key->spcl & (0x80 >> i)) {
/* special keys */
if (kbd->kb_lastact[keycode] == NOP)
kbd->kb_lastact[keycode] = action;
if (kbd->kb_lastact[keycode] != action)
action = NOP;
switch (action) {
/* LOCKING KEYS */
case NLK:
set_lockkey_state(kbd, state, NLK);
break;
case CLK:
#ifndef PC98
set_lockkey_state(kbd, state, CLK);
#else
state |= CLKED;
i = state & LOCK_MASK;
(void)kbdd_ioctl(kbd, KDSETLED, (caddr_t)&i);
#endif
break;
case SLK:
set_lockkey_state(kbd, state, SLK);
break;
case ALK:
set_lockkey_state(kbd, state, ALK);
break;
/* NON-LOCKING KEYS */
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;
/* FALL THROUGH */
case LSH:
state |= SHIFTS1;
break;
case RSHA:
state |= SHIFTAON;
action = RSH;
/* FALL THROUGH */
case RSH:
state |= SHIFTS2;
break;
case LCTRA:
state |= SHIFTAON;
action = LCTR;
/* FALL THROUGH */
case LCTR:
state |= CTLS1;
break;
case RCTRA:
state |= SHIFTAON;
action = RCTR;
/* FALL THROUGH */
case RCTR:
state |= CTLS2;
break;
case LALTA:
state |= SHIFTAON;
action = LALT;
/* FALL THROUGH */
case LALT:
state |= ALTS1;
break;
case RALTA:
state |= SHIFTAON;
action = RALT;
/* FALL THROUGH */
case RALT:
state |= ALTS2;
break;
case ASH:
state |= AGRS1;
break;
case META:
state |= METAS1;
break;
case NOP:
*shiftstate = state;
return (NOKEY);
default:
/* is this an accent (dead) key? */
*shiftstate = state;
if (action >= F_ACC && action <= L_ACC) {
action = save_accent_key(kbd, action,
accents);
switch (action) {
case NOKEY:
case ERRKEY:
return (action);
default:
if (state & METAS)
return (action | MKEY);
else
return (action);
}
/* NOT REACHED */
}
/* other special keys */
if (*accents > 0) {
*accents = 0;
return (ERRKEY);
}
if (action >= F_FN && action <= L_FN)
action |= FKEY;
/* XXX: return fkey string for the FKEY? */
return (SPCLKEY | action);
}
*shiftstate = state;
return (SPCLKEY | action);
} else {
/* regular keys */
kbd->kb_lastact[keycode] = NOP;
*shiftstate = state;
if (*accents > 0) {
/* make an accented char */
action = make_accent_char(kbd, action, accents);
if (action == ERRKEY)
return (action);
}
if (state & METAS)
action |= MKEY;
return (action);
}
}
/* NOT REACHED */
}
+
+static void
+kbd_drv_init(void)
+{
+ const keyboard_driver_t **list;
+ const keyboard_driver_t *p;
+
+ SET_FOREACH(list, kbddriver_set) {
+ p = *list;
+ if (p->kbdsw->get_fkeystr == NULL)
+ p->kbdsw->get_fkeystr = genkbd_get_fkeystr;
+ if (p->kbdsw->diag == NULL)
+ p->kbdsw->diag = genkbd_diag;
+ }
+}
+
+SYSINIT(kbd_drv_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, kbd_drv_init, NULL);
Index: stable/11/sys/dev/kbdmux/kbdmux.c
===================================================================
--- stable/11/sys/dev/kbdmux/kbdmux.c (revision 356012)
+++ stable/11/sys/dev/kbdmux/kbdmux.c (revision 356013)
@@ -1,1472 +1,1470 @@
/*
* kbdmux.c
*/
/*-
* Copyright (c) 2005 Maksim Yevmenkin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: kbdmux.c,v 1.4 2005/07/14 17:38:35 max Exp $
* $FreeBSD$
*/
#include "opt_compat.h"
#include "opt_evdev.h"
#include "opt_kbd.h"
#include "opt_kbdmux.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* the initial key map, accent map and fkey strings */
#ifdef KBDMUX_DFLT_KEYMAP
#define KBD_DFLT_KEYMAP
#include "kbdmuxmap.h"
#endif
#include
#ifdef EVDEV_SUPPORT
#include
#include
#endif
#define KEYBOARD_NAME "kbdmux"
MALLOC_DECLARE(M_KBDMUX);
MALLOC_DEFINE(M_KBDMUX, KEYBOARD_NAME, "Keyboard multiplexor");
/*****************************************************************************
*****************************************************************************
** Keyboard state
*****************************************************************************
*****************************************************************************/
#define KBDMUX_Q_SIZE 512 /* input queue size */
/*
* XXX
* For now rely on Giant mutex to protect our data structures.
* Just like the rest of keyboard drivers and syscons(4) do.
* Note that callout is initialized as not MP-safe to make sure
* Giant is held.
*/
#if 0 /* not yet */
#define KBDMUX_LOCK_DECL_GLOBAL \
struct mtx ks_lock
#define KBDMUX_LOCK_INIT(s) \
mtx_init(&(s)->ks_lock, "kbdmux", NULL, MTX_DEF|MTX_RECURSE)
#define KBDMUX_LOCK_DESTROY(s) \
mtx_destroy(&(s)->ks_lock)
#define KBDMUX_LOCK(s) \
mtx_lock(&(s)->ks_lock)
#define KBDMUX_UNLOCK(s) \
mtx_unlock(&(s)->ks_lock)
#define KBDMUX_LOCK_ASSERT(s, w) \
mtx_assert(&(s)->ks_lock, (w))
#define KBDMUX_SLEEP(s, f, d, t) \
msleep(&(s)->f, &(s)->ks_lock, PCATCH | (PZERO + 1), (d), (t))
#define KBDMUX_CALLOUT_INIT(s) \
callout_init_mtx(&(s)->ks_timo, &(s)->ks_lock, 0)
#define KBDMUX_QUEUE_INTR(s) \
taskqueue_enqueue(taskqueue_swi_giant, &(s)->ks_task)
#else
#define KBDMUX_LOCK_DECL_GLOBAL
#define KBDMUX_LOCK_INIT(s)
#define KBDMUX_LOCK_DESTROY(s)
#define KBDMUX_LOCK(s)
#define KBDMUX_UNLOCK(s)
#define KBDMUX_LOCK_ASSERT(s, w)
#define KBDMUX_SLEEP(s, f, d, t) \
tsleep(&(s)->f, PCATCH | (PZERO + 1), (d), (t))
#define KBDMUX_CALLOUT_INIT(s) \
callout_init(&(s)->ks_timo, 0)
#define KBDMUX_QUEUE_INTR(s) \
taskqueue_enqueue(taskqueue_swi_giant, &(s)->ks_task)
#endif /* not yet */
/*
* kbdmux keyboard
*/
struct kbdmux_kbd
{
keyboard_t *kbd; /* keyboard */
SLIST_ENTRY(kbdmux_kbd) next; /* link to next */
};
typedef struct kbdmux_kbd kbdmux_kbd_t;
/*
* kbdmux state
*/
struct kbdmux_state
{
char ks_inq[KBDMUX_Q_SIZE]; /* input chars queue */
unsigned int ks_inq_start;
unsigned int ks_inq_length;
struct task ks_task; /* interrupt task */
struct callout ks_timo; /* timeout handler */
#define TICKS (hz) /* rate */
int ks_flags; /* flags */
#define COMPOSE (1 << 0) /* compose char flag */
#define TASK (1 << 2) /* interrupt task queued */
int ks_polling; /* poll nesting count */
int ks_mode; /* K_XLATE, K_RAW, K_CODE */
int ks_state; /* state */
int ks_accents; /* accent key index (> 0) */
u_int ks_composed_char; /* composed char code */
u_char ks_prefix; /* AT scan code prefix */
#ifdef EVDEV_SUPPORT
struct evdev_dev * ks_evdev;
int ks_evdev_state;
#endif
SLIST_HEAD(, kbdmux_kbd) ks_kbds; /* keyboards */
KBDMUX_LOCK_DECL_GLOBAL;
};
typedef struct kbdmux_state kbdmux_state_t;
/*****************************************************************************
*****************************************************************************
** Helper functions
*****************************************************************************
*****************************************************************************/
static task_fn_t kbdmux_kbd_intr;
static timeout_t kbdmux_kbd_intr_timo;
static kbd_callback_func_t kbdmux_kbd_event;
static void
kbdmux_kbd_putc(kbdmux_state_t *state, char c)
{
unsigned int p;
if (state->ks_inq_length == KBDMUX_Q_SIZE)
return;
p = (state->ks_inq_start + state->ks_inq_length) % KBDMUX_Q_SIZE;
state->ks_inq[p] = c;
state->ks_inq_length++;
}
static int
kbdmux_kbd_getc(kbdmux_state_t *state)
{
unsigned char c;
if (state->ks_inq_length == 0)
return (-1);
c = state->ks_inq[state->ks_inq_start];
state->ks_inq_start = (state->ks_inq_start + 1) % KBDMUX_Q_SIZE;
state->ks_inq_length--;
return (c);
}
/*
* Interrupt handler task
*/
void
kbdmux_kbd_intr(void *xkbd, int pending)
{
keyboard_t *kbd = (keyboard_t *) xkbd;
kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
kbdd_intr(kbd, NULL);
KBDMUX_LOCK(state);
state->ks_flags &= ~TASK;
wakeup(&state->ks_task);
KBDMUX_UNLOCK(state);
}
/*
* Schedule interrupt handler on timeout. Called with locked state.
*/
void
kbdmux_kbd_intr_timo(void *xstate)
{
kbdmux_state_t *state = (kbdmux_state_t *) xstate;
KBDMUX_LOCK_ASSERT(state, MA_OWNED);
if (callout_pending(&state->ks_timo))
return; /* callout was reset */
if (!callout_active(&state->ks_timo))
return; /* callout was stopped */
callout_deactivate(&state->ks_timo);
/* queue interrupt task if needed */
if (state->ks_inq_length > 0 && !(state->ks_flags & TASK) &&
KBDMUX_QUEUE_INTR(state) == 0)
state->ks_flags |= TASK;
/* re-schedule timeout */
callout_reset(&state->ks_timo, TICKS, kbdmux_kbd_intr_timo, state);
}
/*
* Process event from one of our keyboards
*/
static int
kbdmux_kbd_event(keyboard_t *kbd, int event, void *arg)
{
kbdmux_state_t *state = (kbdmux_state_t *) arg;
switch (event) {
case KBDIO_KEYINPUT: {
int c;
KBDMUX_LOCK(state);
/*
* Read all chars from the keyboard
*
* Turns out that atkbd(4) check_char() method may return
* "true" while read_char() method returns NOKEY. If this
* happens we could stuck in the loop below. Avoid this
* by breaking out of the loop if read_char() method returns
* NOKEY.
*/
while (kbdd_check_char(kbd)) {
c = kbdd_read_char(kbd, 0);
if (c == NOKEY)
break;
if (c == ERRKEY)
continue; /* XXX ring bell */
if (!KBD_IS_BUSY(kbd))
continue; /* not open - discard the input */
kbdmux_kbd_putc(state, c);
}
/* queue interrupt task if needed */
if (state->ks_inq_length > 0 && !(state->ks_flags & TASK) &&
KBDMUX_QUEUE_INTR(state) == 0)
state->ks_flags |= TASK;
KBDMUX_UNLOCK(state);
} break;
case KBDIO_UNLOADING: {
kbdmux_kbd_t *k;
KBDMUX_LOCK(state);
SLIST_FOREACH(k, &state->ks_kbds, next)
if (k->kbd == kbd)
break;
if (k != NULL) {
kbd_release(k->kbd, &k->kbd);
SLIST_REMOVE(&state->ks_kbds, k, kbdmux_kbd, next);
k->kbd = NULL;
free(k, M_KBDMUX);
}
KBDMUX_UNLOCK(state);
} break;
default:
return (EINVAL);
/* NOT REACHED */
}
return (0);
}
/****************************************************************************
****************************************************************************
** Keyboard driver
****************************************************************************
****************************************************************************/
static int kbdmux_configure(int flags);
static kbd_probe_t kbdmux_probe;
static kbd_init_t kbdmux_init;
static kbd_term_t kbdmux_term;
static kbd_intr_t kbdmux_intr;
static kbd_test_if_t kbdmux_test_if;
static kbd_enable_t kbdmux_enable;
static kbd_disable_t kbdmux_disable;
static kbd_read_t kbdmux_read;
static kbd_check_t kbdmux_check;
static kbd_read_char_t kbdmux_read_char;
static kbd_check_char_t kbdmux_check_char;
static kbd_ioctl_t kbdmux_ioctl;
static kbd_lock_t kbdmux_lock;
static void kbdmux_clear_state_locked(kbdmux_state_t *state);
static kbd_clear_state_t kbdmux_clear_state;
static kbd_get_state_t kbdmux_get_state;
static kbd_set_state_t kbdmux_set_state;
static kbd_poll_mode_t kbdmux_poll;
static keyboard_switch_t kbdmuxsw = {
.probe = kbdmux_probe,
.init = kbdmux_init,
.term = kbdmux_term,
.intr = kbdmux_intr,
.test_if = kbdmux_test_if,
.enable = kbdmux_enable,
.disable = kbdmux_disable,
.read = kbdmux_read,
.check = kbdmux_check,
.read_char = kbdmux_read_char,
.check_char = kbdmux_check_char,
.ioctl = kbdmux_ioctl,
.lock = kbdmux_lock,
.clear_state = kbdmux_clear_state,
.get_state = kbdmux_get_state,
.set_state = kbdmux_set_state,
- .get_fkeystr = genkbd_get_fkeystr,
.poll = kbdmux_poll,
- .diag = genkbd_diag,
};
#ifdef EVDEV_SUPPORT
static const struct evdev_methods kbdmux_evdev_methods = {
.ev_event = evdev_ev_kbd_event,
};
#endif
/*
* Return the number of found keyboards
*/
static int
kbdmux_configure(int flags)
{
return (1);
}
/*
* Detect a keyboard
*/
static int
kbdmux_probe(int unit, void *arg, int flags)
{
if (resource_disabled(KEYBOARD_NAME, unit))
return (ENXIO);
return (0);
}
/*
* Reset and initialize the keyboard (stolen from atkbd.c)
*/
static int
kbdmux_init(int unit, keyboard_t **kbdp, void *arg, int flags)
{
keyboard_t *kbd = NULL;
kbdmux_state_t *state = NULL;
keymap_t *keymap = NULL;
accentmap_t *accmap = NULL;
fkeytab_t *fkeymap = NULL;
int error, needfree, fkeymap_size, delay[2];
#ifdef EVDEV_SUPPORT
struct evdev_dev *evdev;
char phys_loc[NAMELEN];
#endif
if (*kbdp == NULL) {
*kbdp = kbd = malloc(sizeof(*kbd), M_KBDMUX, M_NOWAIT | M_ZERO);
state = malloc(sizeof(*state), M_KBDMUX, M_NOWAIT | M_ZERO);
keymap = malloc(sizeof(key_map), M_KBDMUX, M_NOWAIT);
accmap = malloc(sizeof(accent_map), M_KBDMUX, M_NOWAIT);
fkeymap = malloc(sizeof(fkey_tab), M_KBDMUX, M_NOWAIT);
fkeymap_size = sizeof(fkey_tab)/sizeof(fkey_tab[0]);
needfree = 1;
if ((kbd == NULL) || (state == NULL) || (keymap == NULL) ||
(accmap == NULL) || (fkeymap == NULL)) {
error = ENOMEM;
goto bad;
}
KBDMUX_LOCK_INIT(state);
TASK_INIT(&state->ks_task, 0, kbdmux_kbd_intr, (void *) kbd);
KBDMUX_CALLOUT_INIT(state);
SLIST_INIT(&state->ks_kbds);
} else if (KBD_IS_INITIALIZED(*kbdp) && KBD_IS_CONFIGURED(*kbdp)) {
return (0);
} else {
kbd = *kbdp;
state = (kbdmux_state_t *) kbd->kb_data;
keymap = kbd->kb_keymap;
accmap = kbd->kb_accentmap;
fkeymap = kbd->kb_fkeytab;
fkeymap_size = kbd->kb_fkeytab_size;
needfree = 0;
}
if (!KBD_IS_PROBED(kbd)) {
/* XXX assume 101/102 keys keyboard */
kbd_init_struct(kbd, KEYBOARD_NAME, KB_101, unit, flags, 0, 0);
bcopy(&key_map, keymap, sizeof(key_map));
bcopy(&accent_map, accmap, sizeof(accent_map));
bcopy(fkey_tab, fkeymap,
imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab)));
kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size);
kbd->kb_data = (void *)state;
KBD_FOUND_DEVICE(kbd);
KBD_PROBE_DONE(kbd);
KBDMUX_LOCK(state);
kbdmux_clear_state_locked(state);
state->ks_mode = K_XLATE;
KBDMUX_UNLOCK(state);
}
if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) {
kbd->kb_config = flags & ~KB_CONF_PROBE_ONLY;
kbdmux_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state);
delay[0] = kbd->kb_delay1;
delay[1] = kbd->kb_delay2;
kbdmux_ioctl(kbd, KDSETREPEAT, (caddr_t)delay);
#ifdef EVDEV_SUPPORT
/* register as evdev provider */
evdev = evdev_alloc();
evdev_set_name(evdev, "System keyboard multiplexer");
snprintf(phys_loc, NAMELEN, KEYBOARD_NAME"%d", unit);
evdev_set_phys(evdev, phys_loc);
evdev_set_id(evdev, BUS_VIRTUAL, 0, 0, 0);
evdev_set_methods(evdev, kbd, &kbdmux_evdev_methods);
evdev_support_event(evdev, EV_SYN);
evdev_support_event(evdev, EV_KEY);
evdev_support_event(evdev, EV_LED);
evdev_support_event(evdev, EV_REP);
evdev_support_all_known_keys(evdev);
evdev_support_led(evdev, LED_NUML);
evdev_support_led(evdev, LED_CAPSL);
evdev_support_led(evdev, LED_SCROLLL);
if (evdev_register(evdev))
evdev_free(evdev);
else
state->ks_evdev = evdev;
state->ks_evdev_state = 0;
#endif
KBD_INIT_DONE(kbd);
}
if (!KBD_IS_CONFIGURED(kbd)) {
if (kbd_register(kbd) < 0) {
error = ENXIO;
goto bad;
}
KBD_CONFIG_DONE(kbd);
KBDMUX_LOCK(state);
callout_reset(&state->ks_timo, TICKS, kbdmux_kbd_intr_timo, state);
KBDMUX_UNLOCK(state);
}
return (0);
bad:
if (needfree) {
if (state != NULL)
free(state, M_KBDMUX);
if (keymap != NULL)
free(keymap, M_KBDMUX);
if (accmap != NULL)
free(accmap, M_KBDMUX);
if (fkeymap != NULL)
free(fkeymap, M_KBDMUX);
if (kbd != NULL) {
free(kbd, M_KBDMUX);
*kbdp = NULL; /* insure ref doesn't leak to caller */
}
}
return (error);
}
/*
* Finish using this keyboard
*/
static int
kbdmux_term(keyboard_t *kbd)
{
kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
kbdmux_kbd_t *k;
KBDMUX_LOCK(state);
/* kill callout */
callout_stop(&state->ks_timo);
/* wait for interrupt task */
while (state->ks_flags & TASK)
KBDMUX_SLEEP(state, ks_task, "kbdmuxc", 0);
/* release all keyboards from the mux */
while ((k = SLIST_FIRST(&state->ks_kbds)) != NULL) {
kbd_release(k->kbd, &k->kbd);
SLIST_REMOVE_HEAD(&state->ks_kbds, next);
k->kbd = NULL;
free(k, M_KBDMUX);
}
KBDMUX_UNLOCK(state);
kbd_unregister(kbd);
#ifdef EVDEV_SUPPORT
evdev_free(state->ks_evdev);
#endif
KBDMUX_LOCK_DESTROY(state);
bzero(state, sizeof(*state));
free(state, M_KBDMUX);
free(kbd->kb_keymap, M_KBDMUX);
free(kbd->kb_accentmap, M_KBDMUX);
free(kbd->kb_fkeytab, M_KBDMUX);
free(kbd, M_KBDMUX);
return (0);
}
/*
* Keyboard interrupt routine
*/
static int
kbdmux_intr(keyboard_t *kbd, void *arg)
{
int c;
if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) {
/* let the callback function to process the input */
(*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT,
kbd->kb_callback.kc_arg);
} else {
/* read and discard the input; no one is waiting for input */
do {
c = kbdmux_read_char(kbd, FALSE);
} while (c != NOKEY);
}
return (0);
}
/*
* Test the interface to the device
*/
static int
kbdmux_test_if(keyboard_t *kbd)
{
return (0);
}
/*
* Enable the access to the device; until this function is called,
* the client cannot read from the keyboard.
*/
static int
kbdmux_enable(keyboard_t *kbd)
{
KBD_ACTIVATE(kbd);
return (0);
}
/*
* Disallow the access to the device
*/
static int
kbdmux_disable(keyboard_t *kbd)
{
KBD_DEACTIVATE(kbd);
return (0);
}
/*
* Read one byte from the keyboard if it's allowed
*/
static int
kbdmux_read(keyboard_t *kbd, int wait)
{
kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
int c;
KBDMUX_LOCK(state);
c = kbdmux_kbd_getc(state);
KBDMUX_UNLOCK(state);
if (c != -1)
kbd->kb_count ++;
return (KBD_IS_ACTIVE(kbd)? c : -1);
}
/*
* Check if data is waiting
*/
static int
kbdmux_check(keyboard_t *kbd)
{
kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
int ready;
if (!KBD_IS_ACTIVE(kbd))
return (FALSE);
KBDMUX_LOCK(state);
ready = (state->ks_inq_length > 0) ? TRUE : FALSE;
KBDMUX_UNLOCK(state);
return (ready);
}
/*
* Read char from the keyboard (stolen from atkbd.c)
*/
static u_int
kbdmux_read_char(keyboard_t *kbd, int wait)
{
kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
u_int action;
int scancode, keycode;
KBDMUX_LOCK(state);
next_code:
/* do we have a composed char to return? */
if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) {
action = state->ks_composed_char;
state->ks_composed_char = 0;
if (action > UCHAR_MAX) {
KBDMUX_UNLOCK(state);
return (ERRKEY);
}
KBDMUX_UNLOCK(state);
return (action);
}
/* see if there is something in the keyboard queue */
scancode = kbdmux_kbd_getc(state);
if (scancode == -1) {
if (state->ks_polling != 0) {
kbdmux_kbd_t *k;
SLIST_FOREACH(k, &state->ks_kbds, next) {
while (kbdd_check_char(k->kbd)) {
scancode = kbdd_read_char(k->kbd, 0);
if (scancode == NOKEY)
break;
if (scancode == ERRKEY)
continue;
if (!KBD_IS_BUSY(k->kbd))
continue;
kbdmux_kbd_putc(state, scancode);
}
}
if (state->ks_inq_length > 0)
goto next_code;
}
KBDMUX_UNLOCK(state);
return (NOKEY);
}
/* XXX FIXME: check for -1 if wait == 1! */
kbd->kb_count ++;
#ifdef EVDEV_SUPPORT
/* push evdev event */
if (evdev_rcpt_mask & EVDEV_RCPT_KBDMUX && state->ks_evdev != NULL) {
uint16_t key = evdev_scancode2key(&state->ks_evdev_state,
scancode);
if (key != KEY_RESERVED) {
evdev_push_event(state->ks_evdev, EV_KEY,
key, scancode & 0x80 ? 0 : 1);
evdev_sync(state->ks_evdev);
}
}
#endif
/* return the byte as is for the K_RAW mode */
if (state->ks_mode == K_RAW) {
KBDMUX_UNLOCK(state);
return (scancode);
}
/* translate the scan code into a keycode */
keycode = scancode & 0x7F;
switch (state->ks_prefix) {
case 0x00: /* normal scancode */
switch(scancode) {
case 0xB8: /* left alt (compose key) released */
if (state->ks_flags & COMPOSE) {
state->ks_flags &= ~COMPOSE;
if (state->ks_composed_char > UCHAR_MAX)
state->ks_composed_char = 0;
}
break;
case 0x38: /* left alt (compose key) pressed */
if (!(state->ks_flags & COMPOSE)) {
state->ks_flags |= COMPOSE;
state->ks_composed_char = 0;
}
break;
case 0xE0:
case 0xE1:
state->ks_prefix = scancode;
goto next_code;
}
break;
case 0xE0: /* 0xE0 prefix */
state->ks_prefix = 0;
switch (keycode) {
case 0x1C: /* right enter key */
keycode = 0x59;
break;
case 0x1D: /* right ctrl key */
keycode = 0x5A;
break;
case 0x35: /* keypad divide key */
keycode = 0x5B;
break;
case 0x37: /* print scrn key */
keycode = 0x5C;
break;
case 0x38: /* right alt key (alt gr) */
keycode = 0x5D;
break;
case 0x46: /* ctrl-pause/break on AT 101 (see below) */
keycode = 0x68;
break;
case 0x47: /* grey home key */
keycode = 0x5E;
break;
case 0x48: /* grey up arrow key */
keycode = 0x5F;
break;
case 0x49: /* grey page up key */
keycode = 0x60;
break;
case 0x4B: /* grey left arrow key */
keycode = 0x61;
break;
case 0x4D: /* grey right arrow key */
keycode = 0x62;
break;
case 0x4F: /* grey end key */
keycode = 0x63;
break;
case 0x50: /* grey down arrow key */
keycode = 0x64;
break;
case 0x51: /* grey page down key */
keycode = 0x65;
break;
case 0x52: /* grey insert key */
keycode = 0x66;
break;
case 0x53: /* grey delete key */
keycode = 0x67;
break;
/* the following 3 are only used on the MS "Natural" keyboard */
case 0x5b: /* left Window key */
keycode = 0x69;
break;
case 0x5c: /* right Window key */
keycode = 0x6a;
break;
case 0x5d: /* menu key */
keycode = 0x6b;
break;
case 0x5e: /* power key */
keycode = 0x6d;
break;
case 0x5f: /* sleep key */
keycode = 0x6e;
break;
case 0x63: /* wake key */
keycode = 0x6f;
break;
case 0x64: /* [JP106USB] backslash, underscore */
keycode = 0x73;
break;
default: /* ignore everything else */
goto next_code;
}
break;
case 0xE1: /* 0xE1 prefix */
/*
* The pause/break key on the 101 keyboard produces:
* E1-1D-45 E1-9D-C5
* Ctrl-pause/break produces:
* E0-46 E0-C6 (See above.)
*/
state->ks_prefix = 0;
if (keycode == 0x1D)
state->ks_prefix = 0x1D;
goto next_code;
/* NOT REACHED */
case 0x1D: /* pause / break */
state->ks_prefix = 0;
if (keycode != 0x45)
goto next_code;
keycode = 0x68;
break;
}
/* XXX assume 101/102 keys AT keyboard */
switch (keycode) {
case 0x5c: /* print screen */
if (state->ks_flags & ALTS)
keycode = 0x54; /* sysrq */
break;
case 0x68: /* pause/break */
if (state->ks_flags & CTLS)
keycode = 0x6c; /* break */
break;
}
/* return the key code in the K_CODE mode */
if (state->ks_mode == K_CODE) {
KBDMUX_UNLOCK(state);
return (keycode | (scancode & 0x80));
}
/* compose a character code */
if (state->ks_flags & COMPOSE) {
switch (keycode | (scancode & 0x80)) {
/* key pressed, process it */
case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */
state->ks_composed_char *= 10;
state->ks_composed_char += keycode - 0x40;
if (state->ks_composed_char > UCHAR_MAX) {
KBDMUX_UNLOCK(state);
return (ERRKEY);
}
goto next_code;
case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */
state->ks_composed_char *= 10;
state->ks_composed_char += keycode - 0x47;
if (state->ks_composed_char > UCHAR_MAX) {
KBDMUX_UNLOCK(state);
return (ERRKEY);
}
goto next_code;
case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */
state->ks_composed_char *= 10;
state->ks_composed_char += keycode - 0x4E;
if (state->ks_composed_char > UCHAR_MAX) {
KBDMUX_UNLOCK(state);
return (ERRKEY);
}
goto next_code;
case 0x52: /* keypad 0 */
state->ks_composed_char *= 10;
if (state->ks_composed_char > UCHAR_MAX) {
KBDMUX_UNLOCK(state);
return (ERRKEY);
}
goto next_code;
/* key released, no interest here */
case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */
case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */
case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */
case 0xD2: /* keypad 0 */
goto next_code;
case 0x38: /* left alt key */
break;
default:
if (state->ks_composed_char > 0) {
state->ks_flags &= ~COMPOSE;
state->ks_composed_char = 0;
KBDMUX_UNLOCK(state);
return (ERRKEY);
}
break;
}
}
/* keycode to key action */
action = genkbd_keyaction(kbd, keycode, scancode & 0x80,
&state->ks_state, &state->ks_accents);
if (action == NOKEY)
goto next_code;
KBDMUX_UNLOCK(state);
return (action);
}
/*
* Check if char is waiting
*/
static int
kbdmux_check_char(keyboard_t *kbd)
{
kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
int ready;
if (!KBD_IS_ACTIVE(kbd))
return (FALSE);
KBDMUX_LOCK(state);
if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char != 0))
ready = TRUE;
else
ready = (state->ks_inq_length > 0) ? TRUE : FALSE;
KBDMUX_UNLOCK(state);
return (ready);
}
/*
* Keyboard ioctl's
*/
static int
kbdmux_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
static int delays[] = {
250, 500, 750, 1000
};
static int rates[] = {
34, 38, 42, 46, 50, 55, 59, 63,
68, 76, 84, 92, 100, 110, 118, 126,
136, 152, 168, 184, 200, 220, 236, 252,
272, 304, 336, 368, 400, 440, 472, 504
};
kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
kbdmux_kbd_t *k;
keyboard_info_t *ki;
int error = 0, mode;
#ifdef COMPAT_FREEBSD6
int ival;
#endif
if (state == NULL)
return (ENXIO);
switch (cmd) {
case KBADDKBD: /* add keyboard to the mux */
ki = (keyboard_info_t *) arg;
if (ki == NULL || ki->kb_unit < 0 || ki->kb_name[0] == '\0' ||
strcmp(ki->kb_name, "*") == 0)
return (EINVAL); /* bad input */
KBDMUX_LOCK(state);
SLIST_FOREACH(k, &state->ks_kbds, next)
if (k->kbd->kb_unit == ki->kb_unit &&
strcmp(k->kbd->kb_name, ki->kb_name) == 0)
break;
if (k != NULL) {
KBDMUX_UNLOCK(state);
return (0); /* keyboard already in the mux */
}
k = malloc(sizeof(*k), M_KBDMUX, M_NOWAIT | M_ZERO);
if (k == NULL) {
KBDMUX_UNLOCK(state);
return (ENOMEM); /* out of memory */
}
k->kbd = kbd_get_keyboard(
kbd_allocate(
ki->kb_name,
ki->kb_unit,
(void *) &k->kbd,
kbdmux_kbd_event, (void *) state));
if (k->kbd == NULL) {
KBDMUX_UNLOCK(state);
free(k, M_KBDMUX);
return (EINVAL); /* bad keyboard */
}
kbdd_enable(k->kbd);
kbdd_clear_state(k->kbd);
/* set K_RAW mode on slave keyboard */
mode = K_RAW;
error = kbdd_ioctl(k->kbd, KDSKBMODE, (caddr_t)&mode);
if (error == 0) {
/* set lock keys state on slave keyboard */
mode = state->ks_state & LOCK_MASK;
error = kbdd_ioctl(k->kbd, KDSKBSTATE, (caddr_t)&mode);
}
if (error != 0) {
KBDMUX_UNLOCK(state);
kbd_release(k->kbd, &k->kbd);
k->kbd = NULL;
free(k, M_KBDMUX);
return (error); /* could not set mode */
}
SLIST_INSERT_HEAD(&state->ks_kbds, k, next);
KBDMUX_UNLOCK(state);
break;
case KBRELKBD: /* release keyboard from the mux */
ki = (keyboard_info_t *) arg;
if (ki == NULL || ki->kb_unit < 0 || ki->kb_name[0] == '\0' ||
strcmp(ki->kb_name, "*") == 0)
return (EINVAL); /* bad input */
KBDMUX_LOCK(state);
SLIST_FOREACH(k, &state->ks_kbds, next)
if (k->kbd->kb_unit == ki->kb_unit &&
strcmp(k->kbd->kb_name, ki->kb_name) == 0)
break;
if (k != NULL) {
error = kbd_release(k->kbd, &k->kbd);
if (error == 0) {
SLIST_REMOVE(&state->ks_kbds, k, kbdmux_kbd, next);
k->kbd = NULL;
free(k, M_KBDMUX);
}
} else
error = ENXIO; /* keyboard is not in the mux */
KBDMUX_UNLOCK(state);
break;
case KDGKBMODE: /* get kyboard mode */
KBDMUX_LOCK(state);
*(int *)arg = state->ks_mode;
KBDMUX_UNLOCK(state);
break;
#ifdef COMPAT_FREEBSD6
case _IO('K', 7):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBMODE: /* set keyboard mode */
KBDMUX_LOCK(state);
switch (*(int *)arg) {
case K_XLATE:
if (state->ks_mode != K_XLATE) {
/* make lock key state and LED state match */
state->ks_state &= ~LOCK_MASK;
state->ks_state |= KBD_LED_VAL(kbd);
}
/* FALLTHROUGH */
case K_RAW:
case K_CODE:
if (state->ks_mode != *(int *)arg) {
kbdmux_clear_state_locked(state);
state->ks_mode = *(int *)arg;
}
break;
default:
error = EINVAL;
break;
}
KBDMUX_UNLOCK(state);
break;
case KDGETLED: /* get keyboard LED */
KBDMUX_LOCK(state);
*(int *)arg = KBD_LED_VAL(kbd);
KBDMUX_UNLOCK(state);
break;
#ifdef COMPAT_FREEBSD6
case _IO('K', 66):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETLED: /* set keyboard LED */
KBDMUX_LOCK(state);
/* NOTE: lock key state in ks_state won't be changed */
if (*(int *)arg & ~LOCK_MASK) {
KBDMUX_UNLOCK(state);
return (EINVAL);
}
KBD_LED_VAL(kbd) = *(int *)arg;
#ifdef EVDEV_SUPPORT
if (state->ks_evdev != NULL &&
evdev_rcpt_mask & EVDEV_RCPT_KBDMUX)
evdev_push_leds(state->ks_evdev, *(int *)arg);
#endif
/* KDSETLED on all slave keyboards */
SLIST_FOREACH(k, &state->ks_kbds, next)
(void)kbdd_ioctl(k->kbd, KDSETLED, arg);
KBDMUX_UNLOCK(state);
break;
case KDGKBSTATE: /* get lock key state */
KBDMUX_LOCK(state);
*(int *)arg = state->ks_state & LOCK_MASK;
KBDMUX_UNLOCK(state);
break;
#ifdef COMPAT_FREEBSD6
case _IO('K', 20):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBSTATE: /* set lock key state */
KBDMUX_LOCK(state);
if (*(int *)arg & ~LOCK_MASK) {
KBDMUX_UNLOCK(state);
return (EINVAL);
}
state->ks_state &= ~LOCK_MASK;
state->ks_state |= *(int *)arg;
/* KDSKBSTATE on all slave keyboards */
SLIST_FOREACH(k, &state->ks_kbds, next)
(void)kbdd_ioctl(k->kbd, KDSKBSTATE, arg);
KBDMUX_UNLOCK(state);
return (kbdmux_ioctl(kbd, KDSETLED, arg));
/* NOT REACHED */
#ifdef COMPAT_FREEBSD6
case _IO('K', 67):
cmd = KDSETRAD;
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETREPEAT: /* set keyboard repeat rate (new interface) */
case KDSETRAD: /* set keyboard repeat rate (old interface) */
KBDMUX_LOCK(state);
if (cmd == KDSETREPEAT) {
int i;
/* lookup delay */
for (i = sizeof(delays)/sizeof(delays[0]) - 1; i > 0; i --)
if (((int *)arg)[0] >= delays[i])
break;
mode = i << 5;
/* lookup rate */
for (i = sizeof(rates)/sizeof(rates[0]) - 1; i > 0; i --)
if (((int *)arg)[1] >= rates[i])
break;
mode |= i;
} else
mode = *(int *)arg;
if (mode & ~0x7f) {
KBDMUX_UNLOCK(state);
return (EINVAL);
}
kbd->kb_delay1 = delays[(mode >> 5) & 3];
kbd->kb_delay2 = rates[mode & 0x1f];
#ifdef EVDEV_SUPPORT
if (state->ks_evdev != NULL &&
evdev_rcpt_mask & EVDEV_RCPT_KBDMUX)
evdev_push_repeats(state->ks_evdev, kbd);
#endif
/* perform command on all slave keyboards */
SLIST_FOREACH(k, &state->ks_kbds, next)
(void)kbdd_ioctl(k->kbd, cmd, arg);
KBDMUX_UNLOCK(state);
break;
case PIO_KEYMAP: /* set keyboard translation table */
case OPIO_KEYMAP: /* set keyboard translation table (compat) */
case PIO_KEYMAPENT: /* set keyboard translation table entry */
case PIO_DEADKEYMAP: /* set accent key translation table */
KBDMUX_LOCK(state);
state->ks_accents = 0;
/* perform command on all slave keyboards */
SLIST_FOREACH(k, &state->ks_kbds, next)
(void)kbdd_ioctl(k->kbd, cmd, arg);
KBDMUX_UNLOCK(state);
/* FALLTHROUGH */
default:
error = genkbd_commonioctl(kbd, cmd, arg);
break;
}
return (error);
}
/*
* Lock the access to the keyboard
*/
static int
kbdmux_lock(keyboard_t *kbd, int lock)
{
return (1); /* XXX */
}
/*
* Clear the internal state of the keyboard
*/
static void
kbdmux_clear_state_locked(kbdmux_state_t *state)
{
KBDMUX_LOCK_ASSERT(state, MA_OWNED);
state->ks_flags &= ~COMPOSE;
state->ks_polling = 0;
state->ks_state &= LOCK_MASK; /* preserve locking key state */
state->ks_accents = 0;
state->ks_composed_char = 0;
/* state->ks_prefix = 0; XXX */
state->ks_inq_length = 0;
}
static void
kbdmux_clear_state(keyboard_t *kbd)
{
kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
KBDMUX_LOCK(state);
kbdmux_clear_state_locked(state);
KBDMUX_UNLOCK(state);
}
/*
* Save the internal state
*/
static int
kbdmux_get_state(keyboard_t *kbd, void *buf, size_t len)
{
if (len == 0)
return (sizeof(kbdmux_state_t));
if (len < sizeof(kbdmux_state_t))
return (-1);
bcopy(kbd->kb_data, buf, sizeof(kbdmux_state_t)); /* XXX locking? */
return (0);
}
/*
* Set the internal state
*/
static int
kbdmux_set_state(keyboard_t *kbd, void *buf, size_t len)
{
if (len < sizeof(kbdmux_state_t))
return (ENOMEM);
bcopy(buf, kbd->kb_data, sizeof(kbdmux_state_t)); /* XXX locking? */
return (0);
}
/*
* Set polling
*/
static int
kbdmux_poll(keyboard_t *kbd, int on)
{
kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
kbdmux_kbd_t *k;
KBDMUX_LOCK(state);
if (on)
state->ks_polling++;
else
state->ks_polling--;
/* set poll on slave keyboards */
SLIST_FOREACH(k, &state->ks_kbds, next)
kbdd_poll(k->kbd, on);
KBDMUX_UNLOCK(state);
return (0);
}
/*****************************************************************************
*****************************************************************************
** Module
*****************************************************************************
*****************************************************************************/
KEYBOARD_DRIVER(kbdmux, kbdmuxsw, kbdmux_configure);
static int
kbdmux_modevent(module_t mod, int type, void *data)
{
keyboard_switch_t *sw;
keyboard_t *kbd;
int error;
switch (type) {
case MOD_LOAD:
if ((error = kbd_add_driver(&kbdmux_kbd_driver)) != 0)
break;
if ((sw = kbd_get_switch(KEYBOARD_NAME)) == NULL) {
kbd_delete_driver(&kbdmux_kbd_driver);
error = ENXIO;
break;
}
kbd = NULL;
if ((error = (*sw->probe)(0, NULL, 0)) != 0 ||
(error = (*sw->init)(0, &kbd, NULL, 0)) != 0) {
kbd_delete_driver(&kbdmux_kbd_driver);
break;
}
#ifdef KBD_INSTALL_CDEV
if ((error = kbd_attach(kbd)) != 0) {
(*sw->term)(kbd);
kbd_delete_driver(&kbdmux_kbd_driver);
break;
}
#endif
if ((error = (*sw->enable)(kbd)) != 0) {
(*sw->disable)(kbd);
#ifdef KBD_INSTALL_CDEV
kbd_detach(kbd);
#endif
(*sw->term)(kbd);
kbd_delete_driver(&kbdmux_kbd_driver);
break;
}
break;
case MOD_UNLOAD:
if ((sw = kbd_get_switch(KEYBOARD_NAME)) == NULL)
panic("kbd_get_switch(" KEYBOARD_NAME ") == NULL");
kbd = kbd_get_keyboard(kbd_find_keyboard(KEYBOARD_NAME, 0));
if (kbd != NULL) {
(*sw->disable)(kbd);
#ifdef KBD_INSTALL_CDEV
kbd_detach(kbd);
#endif
(*sw->term)(kbd);
kbd_delete_driver(&kbdmux_kbd_driver);
}
error = 0;
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
DEV_MODULE(kbdmux, kbdmux_modevent, NULL);
#ifdef EVDEV_SUPPORT
MODULE_DEPEND(kbdmux, evdev, 1, 1, 1);
#endif
Index: stable/11/sys/dev/uart/uart_kbd_sun.c
===================================================================
--- stable/11/sys/dev/uart/uart_kbd_sun.c (revision 356012)
+++ stable/11/sys/dev/uart/uart_kbd_sun.c (revision 356013)
@@ -1,869 +1,868 @@
/*-
* Copyright (c) 2003 Jake Burkholder.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include
__FBSDID("$FreeBSD$");
#include "opt_compat.h"
#include "opt_kbd.h"
#include "opt_sunkbd.h"
#if (defined(SUNKBD_EMULATE_ATKBD) && defined(SUNKBD_DFLT_KEYMAP)) || \
!defined(SUNKBD_EMULATE_ATKBD)
#define KBD_DFLT_KEYMAP
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if !defined(SUNKBD_EMULATE_ATKBD)
#include
#endif
#if defined(SUNKBD_EMULATE_ATKBD) && defined(SUNKBD_DFLT_KEYMAP)
#include "sunkbdmap.h"
#endif
#include "uart_if.h"
#define SUNKBD_DRIVER_NAME "sunkbd"
#define TODO printf("%s: unimplemented", __func__)
struct sunkbd_softc {
keyboard_t sc_kbd;
struct uart_softc *sc_uart;
struct uart_devinfo *sc_sysdev;
struct callout sc_repeat_callout;
int sc_repeat_key;
int sc_accents;
int sc_composed_char;
int sc_flags;
#define KPCOMPOSE (1 << 0)
int sc_mode;
int sc_polling;
int sc_repeating;
int sc_state;
#if defined(SUNKBD_EMULATE_ATKBD)
int sc_buffered_char[2];
#endif
};
static int sunkbd_configure(int flags);
static int sunkbd_probe_keyboard(struct uart_devinfo *di);
static int sunkbd_probe(int unit, void *arg, int flags);
static int sunkbd_init(int unit, keyboard_t **kbdp, void *arg, int flags);
static int sunkbd_term(keyboard_t *kbd);
static int sunkbd_intr(keyboard_t *kbd, void *arg);
static int sunkbd_test_if(keyboard_t *kbd);
static int sunkbd_enable(keyboard_t *kbd);
static int sunkbd_disable(keyboard_t *kbd);
static int sunkbd_read(keyboard_t *kbd, int wait);
static int sunkbd_check(keyboard_t *kbd);
static u_int sunkbd_read_char(keyboard_t *kbd, int wait);
static int sunkbd_check_char(keyboard_t *kbd);
static int sunkbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t data);
static int sunkbd_lock(keyboard_t *kbd, int lock);
static void sunkbd_clear_state(keyboard_t *kbd);
static int sunkbd_get_state(keyboard_t *kbd, void *buf, size_t len);
static int sunkbd_set_state(keyboard_t *kbd, void *buf, size_t len);
static int sunkbd_poll_mode(keyboard_t *kbd, int on);
static void sunkbd_diag(keyboard_t *kbd, int level);
static void sunkbd_repeat(void *v);
#if defined(SUNKBD_EMULATE_ATKBD)
static int keycode2scancode(int keycode, int shift, int up);
#endif
static keyboard_switch_t sunkbdsw = {
.probe = sunkbd_probe,
.init = sunkbd_init,
.term = sunkbd_term,
.intr = sunkbd_intr,
.test_if = sunkbd_test_if,
.enable = sunkbd_enable,
.disable = sunkbd_disable,
.read = sunkbd_read,
.check = sunkbd_check,
.read_char = sunkbd_read_char,
.check_char = sunkbd_check_char,
.ioctl = sunkbd_ioctl,
.lock = sunkbd_lock,
.clear_state = sunkbd_clear_state,
.get_state = sunkbd_get_state,
.set_state = sunkbd_set_state,
- .get_fkeystr = genkbd_get_fkeystr,
.poll = sunkbd_poll_mode,
.diag = sunkbd_diag
};
KEYBOARD_DRIVER(sunkbd, sunkbdsw, sunkbd_configure);
static struct sunkbd_softc sunkbd_softc;
static struct uart_devinfo uart_keyboard;
#if defined(SUNKBD_EMULATE_ATKBD)
#define SCAN_PRESS 0x000
#define SCAN_RELEASE 0x080
#define SCAN_PREFIX_E0 0x100
#define SCAN_PREFIX_E1 0x200
#define SCAN_PREFIX_CTL 0x400
#define SCAN_PREFIX_SHIFT 0x800
#define SCAN_PREFIX (SCAN_PREFIX_E0 | SCAN_PREFIX_E1 | \
SCAN_PREFIX_CTL | SCAN_PREFIX_SHIFT)
#define NOTR 0x0 /* no translation */
static const uint8_t sunkbd_trtab[] = {
NOTR, 0x6d, 0x78, 0x6e, 0x79, 0x3b, 0x3c, 0x44, /* 0x00 - 0x07 */
0x3d, 0x57, 0x3e, 0x58, 0x3f, 0x5d, 0x40, NOTR, /* 0x08 - 0x0f */
0x41, 0x42, 0x43, 0x38, 0x5f, 0x68, 0x5c, 0x46, /* 0x10 - 0x17 */
0x61, 0x6f, 0x70, 0x64, 0x62, 0x01, 0x02, 0x03, /* 0x18 - 0x1f */
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, /* 0x20 - 0x27 */
0x0c, 0x0d, 0x29, 0x0e, 0x66, 0x77, 0x5b, 0x37, /* 0x28 - 0x2f */
0x7a, 0x71, 0x53, 0x74, 0x5e, 0x0f, 0x10, 0x11, /* 0x30 - 0x37 */
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, /* 0x38 - 0x3f */
0x1a, 0x1b, 0x67, 0x6b, 0x47, 0x48, 0x49, 0x4a, /* 0x40 - 0x47 */
0x73, 0x72, 0x63, NOTR, 0x1d, 0x1e, 0x1f, 0x20, /* 0x48 - 0x4f */
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, /* 0x50 - 0x57 */
0x2b, 0x1c, 0x59, 0x4b, 0x4c, 0x4d, 0x52, 0x75, /* 0x58 - 0x5f */
0x60, 0x76, 0x45, 0x2a, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x60 - 0x67 */
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, NOTR, /* 0x68 - 0x6f */
0x4f, 0x50, 0x51, NOTR, NOTR, NOTR, 0x6c, 0x3a, /* 0x70 - 0x77 */
0x69, 0x39, 0x6a, 0x65, 0x56, 0x4e, NOTR, NOTR /* 0x78 - 0x7f */
};
#endif
static int
sunkbd_probe_keyboard(struct uart_devinfo *di)
{
int c, id, ltries, tries;
for (tries = 5; tries != 0; tries--) {
uart_putc(di, SKBD_CMD_RESET);
for (ltries = 1000; ltries != 0; ltries--) {
if (uart_poll(di) == SKBD_RSP_RESET)
break;
DELAY(1000);
}
if (ltries == 0)
continue;
id = -1;
for (ltries = 1000; ltries != 0; ltries--) {
switch (c = uart_poll(di)) {
case -1:
break;
case SKBD_RSP_IDLE:
return (id);
default:
id = c;
}
DELAY(1000);
}
}
return (-1);
}
static int sunkbd_attach(struct uart_softc *sc);
static void sunkbd_uart_intr(void *arg);
static int
sunkbd_configure(int flags)
{
struct sunkbd_softc *sc;
/*
* We are only prepared to be used for the high-level console
* when the keyboard is both configured and attached.
*/
if (!(flags & KB_CONF_PROBE_ONLY)) {
if (KBD_IS_INITIALIZED(&sunkbd_softc.sc_kbd))
goto found;
else
return (0);
}
if (uart_cpu_getdev(UART_DEV_KEYBOARD, &uart_keyboard))
return (0);
if (uart_probe(&uart_keyboard))
return (0);
uart_init(&uart_keyboard);
uart_keyboard.type = UART_DEV_KEYBOARD;
uart_keyboard.attach = sunkbd_attach;
uart_add_sysdev(&uart_keyboard);
if (sunkbd_probe_keyboard(&uart_keyboard) != KB_SUN4)
return (0);
sc = &sunkbd_softc;
callout_init(&sc->sc_repeat_callout, 0);
sunkbd_clear_state(&sc->sc_kbd);
#if defined(SUNKBD_EMULATE_ATKBD)
kbd_init_struct(&sc->sc_kbd, SUNKBD_DRIVER_NAME, KB_101, 0, 0, 0, 0);
kbd_set_maps(&sc->sc_kbd, &key_map, &accent_map, fkey_tab,
sizeof(fkey_tab) / sizeof(fkey_tab[0]));
#else
kbd_init_struct(&sc->sc_kbd, SUNKBD_DRIVER_NAME, KB_OTHER, 0, 0, 0, 0);
kbd_set_maps(&sc->sc_kbd, &keymap_sun_us_unix_kbd,
&accentmap_sun_us_unix_kbd, fkey_tab,
sizeof(fkey_tab) / sizeof(fkey_tab[0]));
#endif
sc->sc_mode = K_XLATE;
kbd_register(&sc->sc_kbd);
sc->sc_sysdev = &uart_keyboard;
found:
/* Return number of found keyboards. */
return (1);
}
static int
sunkbd_attach(struct uart_softc *sc)
{
/*
* Don't attach if we didn't probe the keyboard. Note that
* the UART is still marked as a system device in that case.
*/
if (sunkbd_softc.sc_sysdev == NULL) {
device_printf(sc->sc_dev, "keyboard not present\n");
return (0);
}
if (sc->sc_sysdev != NULL) {
sunkbd_softc.sc_uart = sc;
#ifdef KBD_INSTALL_CDEV
kbd_attach(&sunkbd_softc.sc_kbd);
#endif
sunkbd_enable(&sunkbd_softc.sc_kbd);
swi_add(&tty_intr_event, uart_driver_name, sunkbd_uart_intr,
&sunkbd_softc, SWI_TTY, INTR_TYPE_TTY, &sc->sc_softih);
sc->sc_opened = 1;
KBD_INIT_DONE(&sunkbd_softc.sc_kbd);
}
return (0);
}
static void
sunkbd_uart_intr(void *arg)
{
struct sunkbd_softc *sc = arg;
int pend;
if (sc->sc_uart->sc_leaving)
return;
pend = atomic_readandclear_32(&sc->sc_uart->sc_ttypend);
if (!(pend & SER_INT_MASK))
return;
if (pend & SER_INT_RXREADY) {
if (KBD_IS_ACTIVE(&sc->sc_kbd) && KBD_IS_BUSY(&sc->sc_kbd)) {
sc->sc_kbd.kb_callback.kc_func(&sc->sc_kbd,
KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg);
}
}
}
static int
sunkbd_probe(int unit, void *arg, int flags)
{
TODO;
return (0);
}
static int
sunkbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
{
TODO;
return (0);
}
static int
sunkbd_term(keyboard_t *kbd)
{
TODO;
return (0);
}
static int
sunkbd_intr(keyboard_t *kbd, void *arg)
{
TODO;
return (0);
}
static int
sunkbd_test_if(keyboard_t *kbd)
{
TODO;
return (0);
}
static int
sunkbd_enable(keyboard_t *kbd)
{
KBD_ACTIVATE(kbd);
return (0);
}
static int
sunkbd_disable(keyboard_t *kbd)
{
KBD_DEACTIVATE(kbd);
return (0);
}
static int
sunkbd_read(keyboard_t *kbd, int wait)
{
TODO;
return (0);
}
static int
sunkbd_check(keyboard_t *kbd)
{
struct sunkbd_softc *sc;
if (!KBD_IS_ACTIVE(kbd))
return (FALSE);
sc = (struct sunkbd_softc *)kbd;
#if defined(SUNKBD_EMULATE_ATKBD)
if (sc->sc_buffered_char[0])
return (TRUE);
#endif
if (sc->sc_repeating)
return (TRUE);
if (sc->sc_uart != NULL && !uart_rx_empty(sc->sc_uart))
return (TRUE);
if (sc->sc_polling != 0 && sc->sc_sysdev != NULL &&
uart_rxready(sc->sc_sysdev))
return (TRUE);
return (FALSE);
}
static u_int
sunkbd_read_char(keyboard_t *kbd, int wait)
{
struct sunkbd_softc *sc;
int key, release, repeated, suncode;
sc = (struct sunkbd_softc *)kbd;
#if defined(SUNKBD_EMULATE_ATKBD)
if (sc->sc_mode == K_RAW && sc->sc_buffered_char[0]) {
key = sc->sc_buffered_char[0];
if (key & SCAN_PREFIX) {
sc->sc_buffered_char[0] = key & ~SCAN_PREFIX;
return ((key & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
} else {
sc->sc_buffered_char[0] = sc->sc_buffered_char[1];
sc->sc_buffered_char[1] = 0;
return (key);
}
}
#endif
repeated = 0;
if (sc->sc_repeating) {
repeated = 1;
sc->sc_repeating = 0;
callout_reset(&sc->sc_repeat_callout, hz / 10,
sunkbd_repeat, sc);
suncode = sc->sc_repeat_key;
goto process_code;
}
for (;;) {
next_code:
if (!(sc->sc_flags & KPCOMPOSE) && (sc->sc_composed_char > 0)) {
key = sc->sc_composed_char;
sc->sc_composed_char = 0;
if (key > UCHAR_MAX)
return (ERRKEY);
return (key);
}
if (sc->sc_uart != NULL && !uart_rx_empty(sc->sc_uart)) {
suncode = uart_rx_get(sc->sc_uart);
} else if (sc->sc_polling != 0 && sc->sc_sysdev != NULL) {
if (wait)
suncode = uart_getc(sc->sc_sysdev);
else if ((suncode = uart_poll(sc->sc_sysdev)) == -1)
return (NOKEY);
} else {
return (NOKEY);
}
switch (suncode) {
case SKBD_RSP_IDLE:
break;
default:
process_code:
++kbd->kb_count;
key = SKBD_KEY_CHAR(suncode);
release = suncode & SKBD_KEY_RELEASE;
if (!repeated) {
if (release == 0) {
callout_reset(&sc->sc_repeat_callout,
hz / 2, sunkbd_repeat, sc);
sc->sc_repeat_key = suncode;
} else if (sc->sc_repeat_key == key) {
callout_stop(&sc->sc_repeat_callout);
sc->sc_repeat_key = -1;
}
}
#if defined(SUNKBD_EMULATE_ATKBD)
key = sunkbd_trtab[key];
if (key == NOTR)
return (NOKEY);
if (!repeated) {
switch (key) {
case 0x1d: /* ctrl */
if (release != 0)
sc->sc_flags &= ~CTLS;
else
sc->sc_flags |= CTLS;
break;
case 0x2a: /* left shift */
case 0x36: /* right shift */
if (release != 0)
sc->sc_flags &= ~SHIFTS;
else
sc->sc_flags |= SHIFTS;
break;
case 0x38: /* alt */
case 0x5d: /* altgr */
if (release != 0)
sc->sc_flags &= ~ALTS;
else
sc->sc_flags |= ALTS;
break;
}
}
if (sc->sc_mode == K_RAW) {
key = keycode2scancode(key, sc->sc_flags,
release);
if (key & SCAN_PREFIX) {
if (key & SCAN_PREFIX_CTL) {
sc->sc_buffered_char[0] =
0x1d | (key & SCAN_RELEASE);
sc->sc_buffered_char[1] =
key & ~SCAN_PREFIX;
} else if (key & SCAN_PREFIX_SHIFT) {
sc->sc_buffered_char[0] =
0x2a | (key & SCAN_RELEASE);
sc->sc_buffered_char[1] =
key & ~SCAN_PREFIX_SHIFT;
} else {
sc->sc_buffered_char[0] =
key & ~SCAN_PREFIX;
sc->sc_buffered_char[1] = 0;
}
return ((key & SCAN_PREFIX_E0) ?
0xe0 : 0xe1);
}
return (key);
}
switch (key) {
case 0x5c: /* print screen */
if (sc->sc_flags & ALTS)
key = 0x54; /* sysrq */
break;
case 0x68: /* pause/break */
if (sc->sc_flags & CTLS)
key = 0x6c; /* break */
break;
}
if (sc->sc_mode == K_CODE)
return (key | release);
#else
if (sc->sc_mode == K_RAW || sc->sc_mode == K_CODE)
return (suncode);
#endif
#if defined(SUNKBD_EMULATE_ATKBD)
if (key == 0x38) { /* left alt (KP compose key) */
#else
if (key == 0x13) { /* left alt (KP compose key) */
#endif
if (release != 0) {
if (sc->sc_flags & KPCOMPOSE) {
sc->sc_flags &= ~KPCOMPOSE;
if (sc->sc_composed_char >
UCHAR_MAX)
sc->sc_composed_char =
0;
}
} else {
if (!(sc->sc_flags & KPCOMPOSE)) {
sc->sc_flags |= KPCOMPOSE;
sc->sc_composed_char = 0;
}
}
}
if (sc->sc_flags & KPCOMPOSE) {
switch (suncode) {
case 0x44: /* KP 7 */
case 0x45: /* KP 8 */
case 0x46: /* KP 9 */
sc->sc_composed_char *= 10;
sc->sc_composed_char += suncode - 0x3d;
if (sc->sc_composed_char > UCHAR_MAX)
return (ERRKEY);
goto next_code;
case 0x5b: /* KP 4 */
case 0x5c: /* KP 5 */
case 0x5d: /* KP 6 */
sc->sc_composed_char *= 10;
sc->sc_composed_char += suncode - 0x58;
if (sc->sc_composed_char > UCHAR_MAX)
return (ERRKEY);
goto next_code;
case 0x70: /* KP 1 */
case 0x71: /* KP 2 */
case 0x72: /* KP 3 */
sc->sc_composed_char *= 10;
sc->sc_composed_char += suncode - 0x6f;
if (sc->sc_composed_char > UCHAR_MAX)
return (ERRKEY);
goto next_code;
case 0x5e: /* KP 0 */
sc->sc_composed_char *= 10;
if (sc->sc_composed_char > UCHAR_MAX)
return (ERRKEY);
goto next_code;
case 0x44 | SKBD_KEY_RELEASE: /* KP 7 */
case 0x45 | SKBD_KEY_RELEASE: /* KP 8 */
case 0x46 | SKBD_KEY_RELEASE: /* KP 9 */
case 0x5b | SKBD_KEY_RELEASE: /* KP 4 */
case 0x5c | SKBD_KEY_RELEASE: /* KP 5 */
case 0x5d | SKBD_KEY_RELEASE: /* KP 6 */
case 0x70 | SKBD_KEY_RELEASE: /* KP 1 */
case 0x71 | SKBD_KEY_RELEASE: /* KP 2 */
case 0x72 | SKBD_KEY_RELEASE: /* KP 3 */
case 0x5e | SKBD_KEY_RELEASE: /* KP 0 */
goto next_code;
default:
if (sc->sc_composed_char > 0) {
sc->sc_flags &= ~KPCOMPOSE;
sc->sc_composed_char = 0;
return (ERRKEY);
}
}
}
key = genkbd_keyaction(kbd, key, release,
&sc->sc_state, &sc->sc_accents);
if (key != NOKEY || repeated)
return (key);
}
}
return (0);
}
static int
sunkbd_check_char(keyboard_t *kbd)
{
struct sunkbd_softc *sc;
if (!KBD_IS_ACTIVE(kbd))
return (FALSE);
sc = (struct sunkbd_softc *)kbd;
if (!(sc->sc_flags & KPCOMPOSE) && (sc->sc_composed_char > 0))
return (TRUE);
return (sunkbd_check(kbd));
}
static int
sunkbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t data)
{
struct sunkbd_softc *sc;
int c, error;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5)
int ival;
#endif
sc = (struct sunkbd_softc *)kbd;
error = 0;
switch (cmd) {
case KDGKBMODE:
*(int *)data = sc->sc_mode;
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5)
case _IO('K', 7):
ival = IOCPARM_IVAL(data);
data = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBMODE:
switch (*(int *)data) {
case K_XLATE:
if (sc->sc_mode != K_XLATE) {
/* make lock key state and LED state match */
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= KBD_LED_VAL(kbd);
}
/* FALLTHROUGH */
case K_RAW:
case K_CODE:
if (sc->sc_mode != *(int *)data) {
sunkbd_clear_state(kbd);
sc->sc_mode = *(int *)data;
}
break;
default:
error = EINVAL;
break;
}
break;
case KDGETLED:
*(int *)data = KBD_LED_VAL(kbd);
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5)
case _IO('K', 66):
ival = IOCPARM_IVAL(data);
data = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETLED:
if (*(int *)data & ~LOCK_MASK) {
error = EINVAL;
break;
}
if (sc->sc_sysdev == NULL)
break;
c = 0;
if (*(int *)data & CLKED)
c |= SKBD_LED_CAPSLOCK;
if (*(int *)data & NLKED)
c |= SKBD_LED_NUMLOCK;
if (*(int *)data & SLKED)
c |= SKBD_LED_SCROLLLOCK;
uart_lock(sc->sc_sysdev->hwmtx);
sc->sc_sysdev->ops->putc(&sc->sc_sysdev->bas, SKBD_CMD_SETLED);
sc->sc_sysdev->ops->putc(&sc->sc_sysdev->bas, c);
uart_unlock(sc->sc_sysdev->hwmtx);
KBD_LED_VAL(kbd) = *(int *)data;
break;
case KDGKBSTATE:
*(int *)data = sc->sc_state & LOCK_MASK;
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5)
case _IO('K', 20):
ival = IOCPARM_IVAL(data);
data = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBSTATE:
if (*(int *)data & ~LOCK_MASK) {
error = EINVAL;
break;
}
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= *(int *)data;
/* set LEDs and quit */
return (sunkbd_ioctl(kbd, KDSETLED, data));
case KDSETREPEAT:
case KDSETRAD:
break;
case PIO_KEYMAP:
case OPIO_KEYMAP:
case PIO_KEYMAPENT:
case PIO_DEADKEYMAP:
default:
return (genkbd_commonioctl(kbd, cmd, data));
}
return (error);
}
static int
sunkbd_lock(keyboard_t *kbd, int lock)
{
TODO;
return (0);
}
static void
sunkbd_clear_state(keyboard_t *kbd)
{
struct sunkbd_softc *sc;
sc = (struct sunkbd_softc *)kbd;
sc->sc_repeat_key = -1;
sc->sc_accents = 0;
sc->sc_composed_char = 0;
sc->sc_flags = 0;
sc->sc_polling = 0;
sc->sc_repeating = 0;
sc->sc_state &= LOCK_MASK; /* Preserve locking key state. */
#if defined(SUNKBD_EMULATE_ATKBD)
sc->sc_buffered_char[0] = 0;
sc->sc_buffered_char[1] = 0;
#endif
}
static int
sunkbd_get_state(keyboard_t *kbd, void *buf, size_t len)
{
TODO;
return (0);
}
static int
sunkbd_set_state(keyboard_t *kbd, void *buf, size_t len)
{
TODO;
return (0);
}
static int
sunkbd_poll_mode(keyboard_t *kbd, int on)
{
struct sunkbd_softc *sc;
sc = (struct sunkbd_softc *)kbd;
if (on)
sc->sc_polling++;
else
sc->sc_polling--;
return (0);
}
static void
sunkbd_diag(keyboard_t *kbd, int level)
{
TODO;
}
static void
sunkbd_repeat(void *v)
{
struct sunkbd_softc *sc = v;
if (KBD_IS_ACTIVE(&sc->sc_kbd) && KBD_IS_BUSY(&sc->sc_kbd)) {
if (sc->sc_repeat_key != -1) {
sc->sc_repeating = 1;
sc->sc_kbd.kb_callback.kc_func(&sc->sc_kbd,
KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg);
}
}
}
#if defined(SUNKBD_EMULATE_ATKBD)
static int
keycode2scancode(int keycode, int shift, int up)
{
static const int scan[] = {
/* KP enter, right ctrl, KP divide */
0x1c , 0x1d , 0x35 ,
/* print screen */
0x37 | SCAN_PREFIX_SHIFT,
/* right alt, home, up, page up, left, right, end */
0x38, 0x47, 0x48, 0x49, 0x4b, 0x4d, 0x4f,
/* down, page down, insert, delete */
0x50, 0x51, 0x52, 0x53,
/* pause/break (see also below) */
0x46,
/*
* MS: left window, right window, menu
* also Sun: left meta, right meta, compose
*/
0x5b, 0x5c, 0x5d,
/* Sun type 6 USB */
/* help, stop, again, props, undo, front, copy */
0x68, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63,
/* open, paste, find, cut, audiomute, audiolower, audioraise */
0x64, 0x65, 0x66, 0x67, 0x25, 0x1f, 0x1e,
/* power */
0x20
};
int scancode;
scancode = keycode;
if ((keycode >= 89) && (keycode < 89 + nitems(scan)))
scancode = scan[keycode - 89] | SCAN_PREFIX_E0;
/* pause/break */
if ((keycode == 104) && !(shift & CTLS))
scancode = 0x45 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL;
if (shift & SHIFTS)
scancode &= ~SCAN_PREFIX_SHIFT;
return (scancode | (up ? SCAN_RELEASE : SCAN_PRESS));
}
#endif
Index: stable/11/sys/dev/usb/input/ukbd.c
===================================================================
--- stable/11/sys/dev/usb/input/ukbd.c (revision 356012)
+++ stable/11/sys/dev/usb/input/ukbd.c (revision 356013)
@@ -1,2304 +1,2302 @@
#include
__FBSDID("$FreeBSD$");
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Lennart Augustsson (lennart@augustsson.net) at
* Carlstedt Research & Technology.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
* HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
*/
#include "opt_compat.h"
#include "opt_kbd.h"
#include "opt_ukbd.h"
#include "opt_evdev.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define USB_DEBUG_VAR ukbd_debug
#include
#include
#ifdef EVDEV_SUPPORT
#include
#include
#endif
#include
#include
#include
#include
#include
/* the initial key map, accent map and fkey strings */
#if defined(UKBD_DFLT_KEYMAP) && !defined(KLD_MODULE)
#define KBD_DFLT_KEYMAP
#include "ukbdmap.h"
#endif
/* the following file must be included after "ukbdmap.h" */
#include
#ifdef USB_DEBUG
static int ukbd_debug = 0;
static int ukbd_no_leds = 0;
static int ukbd_pollrate = 0;
static SYSCTL_NODE(_hw_usb, OID_AUTO, ukbd, CTLFLAG_RW, 0, "USB keyboard");
SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, debug, CTLFLAG_RWTUN,
&ukbd_debug, 0, "Debug level");
SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, no_leds, CTLFLAG_RWTUN,
&ukbd_no_leds, 0, "Disables setting of keyboard leds");
SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, pollrate, CTLFLAG_RWTUN,
&ukbd_pollrate, 0, "Force this polling rate, 1-1000Hz");
#endif
#define UKBD_EMULATE_ATSCANCODE 1
#define UKBD_DRIVER_NAME "ukbd"
#define UKBD_NMOD 8 /* units */
#define UKBD_NKEYCODE 6 /* units */
#define UKBD_IN_BUF_SIZE (2*(UKBD_NMOD + (2*UKBD_NKEYCODE))) /* bytes */
#define UKBD_IN_BUF_FULL ((UKBD_IN_BUF_SIZE / 2) - 1) /* bytes */
#define UKBD_NFKEY (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */
#define UKBD_BUFFER_SIZE 64 /* bytes */
struct ukbd_data {
uint16_t modifiers;
#define MOD_CONTROL_L 0x01
#define MOD_CONTROL_R 0x10
#define MOD_SHIFT_L 0x02
#define MOD_SHIFT_R 0x20
#define MOD_ALT_L 0x04
#define MOD_ALT_R 0x40
#define MOD_WIN_L 0x08
#define MOD_WIN_R 0x80
/* internal */
#define MOD_EJECT 0x0100
#define MOD_FN 0x0200
uint8_t keycode[UKBD_NKEYCODE];
};
enum {
UKBD_INTR_DT_0,
UKBD_INTR_DT_1,
UKBD_CTRL_LED,
UKBD_N_TRANSFER,
};
struct ukbd_softc {
keyboard_t sc_kbd;
keymap_t sc_keymap;
accentmap_t sc_accmap;
fkeytab_t sc_fkeymap[UKBD_NFKEY];
struct hid_location sc_loc_apple_eject;
struct hid_location sc_loc_apple_fn;
struct hid_location sc_loc_ctrl_l;
struct hid_location sc_loc_ctrl_r;
struct hid_location sc_loc_shift_l;
struct hid_location sc_loc_shift_r;
struct hid_location sc_loc_alt_l;
struct hid_location sc_loc_alt_r;
struct hid_location sc_loc_win_l;
struct hid_location sc_loc_win_r;
struct hid_location sc_loc_events;
struct hid_location sc_loc_numlock;
struct hid_location sc_loc_capslock;
struct hid_location sc_loc_scrolllock;
struct usb_callout sc_callout;
struct ukbd_data sc_ndata;
struct ukbd_data sc_odata;
struct thread *sc_poll_thread;
struct usb_device *sc_udev;
struct usb_interface *sc_iface;
struct usb_xfer *sc_xfer[UKBD_N_TRANSFER];
#ifdef EVDEV_SUPPORT
struct evdev_dev *sc_evdev;
#endif
uint32_t sc_ntime[UKBD_NKEYCODE];
uint32_t sc_otime[UKBD_NKEYCODE];
uint32_t sc_input[UKBD_IN_BUF_SIZE]; /* input buffer */
uint32_t sc_time_ms;
uint32_t sc_composed_char; /* composed char code, if non-zero */
#ifdef UKBD_EMULATE_ATSCANCODE
uint32_t sc_buffered_char[2];
#endif
uint32_t sc_flags; /* flags */
#define UKBD_FLAG_COMPOSE 0x00000001
#define UKBD_FLAG_POLLING 0x00000002
#define UKBD_FLAG_SET_LEDS 0x00000004
#define UKBD_FLAG_ATTACHED 0x00000010
#define UKBD_FLAG_GONE 0x00000020
#define UKBD_FLAG_HID_MASK 0x003fffc0
#define UKBD_FLAG_APPLE_EJECT 0x00000040
#define UKBD_FLAG_APPLE_FN 0x00000080
#define UKBD_FLAG_APPLE_SWAP 0x00000100
#define UKBD_FLAG_TIMER_RUNNING 0x00000200
#define UKBD_FLAG_CTRL_L 0x00000400
#define UKBD_FLAG_CTRL_R 0x00000800
#define UKBD_FLAG_SHIFT_L 0x00001000
#define UKBD_FLAG_SHIFT_R 0x00002000
#define UKBD_FLAG_ALT_L 0x00004000
#define UKBD_FLAG_ALT_R 0x00008000
#define UKBD_FLAG_WIN_L 0x00010000
#define UKBD_FLAG_WIN_R 0x00020000
#define UKBD_FLAG_EVENTS 0x00040000
#define UKBD_FLAG_NUMLOCK 0x00080000
#define UKBD_FLAG_CAPSLOCK 0x00100000
#define UKBD_FLAG_SCROLLLOCK 0x00200000
int sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
int sc_state; /* shift/lock key state */
int sc_accents; /* accent key index (> 0) */
int sc_polling; /* polling recursion count */
int sc_led_size;
int sc_kbd_size;
uint16_t sc_inputs;
uint16_t sc_inputhead;
uint16_t sc_inputtail;
uint16_t sc_modifiers;
uint8_t sc_leds; /* store for async led requests */
uint8_t sc_iface_index;
uint8_t sc_iface_no;
uint8_t sc_id_apple_eject;
uint8_t sc_id_apple_fn;
uint8_t sc_id_ctrl_l;
uint8_t sc_id_ctrl_r;
uint8_t sc_id_shift_l;
uint8_t sc_id_shift_r;
uint8_t sc_id_alt_l;
uint8_t sc_id_alt_r;
uint8_t sc_id_win_l;
uint8_t sc_id_win_r;
uint8_t sc_id_event;
uint8_t sc_id_numlock;
uint8_t sc_id_capslock;
uint8_t sc_id_scrolllock;
uint8_t sc_id_events;
uint8_t sc_kbd_id;
uint8_t sc_buffer[UKBD_BUFFER_SIZE];
};
#define KEY_ERROR 0x01
#define KEY_PRESS 0
#define KEY_RELEASE 0x400
#define KEY_INDEX(c) ((c) & 0xFF)
#define SCAN_PRESS 0
#define SCAN_RELEASE 0x80
#define SCAN_PREFIX_E0 0x100
#define SCAN_PREFIX_E1 0x200
#define SCAN_PREFIX_CTL 0x400
#define SCAN_PREFIX_SHIFT 0x800
#define SCAN_PREFIX (SCAN_PREFIX_E0 | SCAN_PREFIX_E1 | \
SCAN_PREFIX_CTL | SCAN_PREFIX_SHIFT)
#define SCAN_CHAR(c) ((c) & 0x7f)
#define UKBD_LOCK() mtx_lock(&Giant)
#define UKBD_UNLOCK() mtx_unlock(&Giant)
#ifdef INVARIANTS
/*
* Assert that the lock is held in all contexts
* where the code can be executed.
*/
#define UKBD_LOCK_ASSERT() mtx_assert(&Giant, MA_OWNED)
/*
* Assert that the lock is held in the contexts
* where it really has to be so.
*/
#define UKBD_CTX_LOCK_ASSERT() \
do { \
if (!kdb_active && panicstr == NULL) \
mtx_assert(&Giant, MA_OWNED); \
} while (0)
#else
#define UKBD_LOCK_ASSERT() (void)0
#define UKBD_CTX_LOCK_ASSERT() (void)0
#endif
struct ukbd_mods {
uint32_t mask, key;
};
static const struct ukbd_mods ukbd_mods[UKBD_NMOD] = {
{MOD_CONTROL_L, 0xe0},
{MOD_CONTROL_R, 0xe4},
{MOD_SHIFT_L, 0xe1},
{MOD_SHIFT_R, 0xe5},
{MOD_ALT_L, 0xe2},
{MOD_ALT_R, 0xe6},
{MOD_WIN_L, 0xe3},
{MOD_WIN_R, 0xe7},
};
#define NN 0 /* no translation */
/*
* Translate USB keycodes to AT keyboard scancodes.
*/
/*
* FIXME: Mac USB keyboard generates:
* 0x53: keypad NumLock/Clear
* 0x66: Power
* 0x67: keypad =
* 0x68: F13
* 0x69: F14
* 0x6a: F15
*
* USB Apple Keyboard JIS generates:
* 0x90: Kana
* 0x91: Eisu
*/
static const uint8_t ukbd_trtab[256] = {
0, 0, 0, 0, 30, 48, 46, 32, /* 00 - 07 */
18, 33, 34, 35, 23, 36, 37, 38, /* 08 - 0F */
50, 49, 24, 25, 16, 19, 31, 20, /* 10 - 17 */
22, 47, 17, 45, 21, 44, 2, 3, /* 18 - 1F */
4, 5, 6, 7, 8, 9, 10, 11, /* 20 - 27 */
28, 1, 14, 15, 57, 12, 13, 26, /* 28 - 2F */
27, 43, 43, 39, 40, 41, 51, 52, /* 30 - 37 */
53, 58, 59, 60, 61, 62, 63, 64, /* 38 - 3F */
65, 66, 67, 68, 87, 88, 92, 70, /* 40 - 47 */
104, 102, 94, 96, 103, 99, 101, 98, /* 48 - 4F */
97, 100, 95, 69, 91, 55, 74, 78,/* 50 - 57 */
89, 79, 80, 81, 75, 76, 77, 71, /* 58 - 5F */
72, 73, 82, 83, 86, 107, 122, NN, /* 60 - 67 */
NN, NN, NN, NN, NN, NN, NN, NN, /* 68 - 6F */
NN, NN, NN, NN, 115, 108, 111, 113, /* 70 - 77 */
109, 110, 112, 118, 114, 116, 117, 119, /* 78 - 7F */
121, 120, NN, NN, NN, NN, NN, 123, /* 80 - 87 */
124, 125, 126, 127, 128, NN, NN, NN, /* 88 - 8F */
129, 130, NN, NN, NN, NN, NN, NN, /* 90 - 97 */
NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9F */
NN, NN, NN, NN, NN, NN, NN, NN, /* A0 - A7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* A8 - AF */
NN, NN, NN, NN, NN, NN, NN, NN, /* B0 - B7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* B8 - BF */
NN, NN, NN, NN, NN, NN, NN, NN, /* C0 - C7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* C8 - CF */
NN, NN, NN, NN, NN, NN, NN, NN, /* D0 - D7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* D8 - DF */
29, 42, 56, 105, 90, 54, 93, 106, /* E0 - E7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* E8 - EF */
NN, NN, NN, NN, NN, NN, NN, NN, /* F0 - F7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* F8 - FF */
};
static const uint8_t ukbd_boot_desc[] = {
0x05, 0x01, 0x09, 0x06, 0xa1,
0x01, 0x05, 0x07, 0x19, 0xe0,
0x29, 0xe7, 0x15, 0x00, 0x25,
0x01, 0x75, 0x01, 0x95, 0x08,
0x81, 0x02, 0x95, 0x01, 0x75,
0x08, 0x81, 0x01, 0x95, 0x03,
0x75, 0x01, 0x05, 0x08, 0x19,
0x01, 0x29, 0x03, 0x91, 0x02,
0x95, 0x05, 0x75, 0x01, 0x91,
0x01, 0x95, 0x06, 0x75, 0x08,
0x15, 0x00, 0x26, 0xff, 0x00,
0x05, 0x07, 0x19, 0x00, 0x2a,
0xff, 0x00, 0x81, 0x00, 0xc0
};
/* prototypes */
static void ukbd_timeout(void *);
static void ukbd_set_leds(struct ukbd_softc *, uint8_t);
static int ukbd_set_typematic(keyboard_t *, int);
#ifdef UKBD_EMULATE_ATSCANCODE
static int ukbd_key2scan(struct ukbd_softc *, int, int, int);
#endif
static uint32_t ukbd_read_char(keyboard_t *, int);
static void ukbd_clear_state(keyboard_t *);
static int ukbd_ioctl(keyboard_t *, u_long, caddr_t);
static int ukbd_enable(keyboard_t *);
static int ukbd_disable(keyboard_t *);
static void ukbd_interrupt(struct ukbd_softc *);
static void ukbd_event_keyinput(struct ukbd_softc *);
static device_probe_t ukbd_probe;
static device_attach_t ukbd_attach;
static device_detach_t ukbd_detach;
static device_resume_t ukbd_resume;
#ifdef EVDEV_SUPPORT
static const struct evdev_methods ukbd_evdev_methods = {
.ev_event = evdev_ev_kbd_event,
};
#endif
static uint8_t
ukbd_any_key_pressed(struct ukbd_softc *sc)
{
uint8_t i;
uint8_t j;
for (j = i = 0; i < UKBD_NKEYCODE; i++)
j |= sc->sc_odata.keycode[i];
return (j ? 1 : 0);
}
static void
ukbd_start_timer(struct ukbd_softc *sc)
{
sc->sc_flags |= UKBD_FLAG_TIMER_RUNNING;
usb_callout_reset(&sc->sc_callout, hz / 40, &ukbd_timeout, sc);
}
static void
ukbd_put_key(struct ukbd_softc *sc, uint32_t key)
{
UKBD_CTX_LOCK_ASSERT();
DPRINTF("0x%02x (%d) %s\n", key, key,
(key & KEY_RELEASE) ? "released" : "pressed");
#ifdef EVDEV_SUPPORT
if (evdev_rcpt_mask & EVDEV_RCPT_HW_KBD && sc->sc_evdev != NULL) {
evdev_push_event(sc->sc_evdev, EV_KEY,
evdev_hid2key(KEY_INDEX(key)), !(key & KEY_RELEASE));
evdev_sync(sc->sc_evdev);
}
#endif
if (sc->sc_inputs < UKBD_IN_BUF_SIZE) {
sc->sc_input[sc->sc_inputtail] = key;
++(sc->sc_inputs);
++(sc->sc_inputtail);
if (sc->sc_inputtail >= UKBD_IN_BUF_SIZE) {
sc->sc_inputtail = 0;
}
} else {
DPRINTF("input buffer is full\n");
}
}
static void
ukbd_do_poll(struct ukbd_softc *sc, uint8_t wait)
{
UKBD_CTX_LOCK_ASSERT();
KASSERT((sc->sc_flags & UKBD_FLAG_POLLING) != 0,
("ukbd_do_poll called when not polling\n"));
DPRINTFN(2, "polling\n");
if (!kdb_active && !SCHEDULER_STOPPED()) {
/*
* In this context the kernel is polling for input,
* but the USB subsystem works in normal interrupt-driven
* mode, so we just wait on the USB threads to do the job.
* Note that we currently hold the Giant, but it's also used
* as the transfer mtx, so we must release it while waiting.
*/
while (sc->sc_inputs == 0) {
/*
* Give USB threads a chance to run. Note that
* kern_yield performs DROP_GIANT + PICKUP_GIANT.
*/
kern_yield(PRI_UNCHANGED);
if (!wait)
break;
}
return;
}
while (sc->sc_inputs == 0) {
usbd_transfer_poll(sc->sc_xfer, UKBD_N_TRANSFER);
/* Delay-optimised support for repetition of keys */
if (ukbd_any_key_pressed(sc)) {
/* a key is pressed - need timekeeping */
DELAY(1000);
/* 1 millisecond has passed */
sc->sc_time_ms += 1;
}
ukbd_interrupt(sc);
if (!wait)
break;
}
}
static int32_t
ukbd_get_key(struct ukbd_softc *sc, uint8_t wait)
{
int32_t c;
UKBD_CTX_LOCK_ASSERT();
KASSERT((!kdb_active && !SCHEDULER_STOPPED())
|| (sc->sc_flags & UKBD_FLAG_POLLING) != 0,
("not polling in kdb or panic\n"));
if (sc->sc_inputs == 0 &&
(sc->sc_flags & UKBD_FLAG_GONE) == 0) {
/* start transfer, if not already started */
usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_0]);
usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_1]);
}
if (sc->sc_flags & UKBD_FLAG_POLLING)
ukbd_do_poll(sc, wait);
if (sc->sc_inputs == 0) {
c = -1;
} else {
c = sc->sc_input[sc->sc_inputhead];
--(sc->sc_inputs);
++(sc->sc_inputhead);
if (sc->sc_inputhead >= UKBD_IN_BUF_SIZE) {
sc->sc_inputhead = 0;
}
}
return (c);
}
static void
ukbd_interrupt(struct ukbd_softc *sc)
{
uint32_t n_mod;
uint32_t o_mod;
uint32_t now = sc->sc_time_ms;
uint32_t dtime;
uint8_t key;
uint8_t i;
uint8_t j;
UKBD_CTX_LOCK_ASSERT();
if (sc->sc_ndata.keycode[0] == KEY_ERROR)
return;
n_mod = sc->sc_ndata.modifiers;
o_mod = sc->sc_odata.modifiers;
if (n_mod != o_mod) {
for (i = 0; i < UKBD_NMOD; i++) {
if ((n_mod & ukbd_mods[i].mask) !=
(o_mod & ukbd_mods[i].mask)) {
ukbd_put_key(sc, ukbd_mods[i].key |
((n_mod & ukbd_mods[i].mask) ?
KEY_PRESS : KEY_RELEASE));
}
}
}
/* Check for released keys. */
for (i = 0; i < UKBD_NKEYCODE; i++) {
key = sc->sc_odata.keycode[i];
if (key == 0) {
continue;
}
for (j = 0; j < UKBD_NKEYCODE; j++) {
if (sc->sc_ndata.keycode[j] == 0) {
continue;
}
if (key == sc->sc_ndata.keycode[j]) {
goto rfound;
}
}
ukbd_put_key(sc, key | KEY_RELEASE);
rfound: ;
}
/* Check for pressed keys. */
for (i = 0; i < UKBD_NKEYCODE; i++) {
key = sc->sc_ndata.keycode[i];
if (key == 0) {
continue;
}
sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay1;
for (j = 0; j < UKBD_NKEYCODE; j++) {
if (sc->sc_odata.keycode[j] == 0) {
continue;
}
if (key == sc->sc_odata.keycode[j]) {
/* key is still pressed */
sc->sc_ntime[i] = sc->sc_otime[j];
dtime = (sc->sc_otime[j] - now);
if (!(dtime & 0x80000000)) {
/* time has not elapsed */
goto pfound;
}
sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay2;
break;
}
}
ukbd_put_key(sc, key | KEY_PRESS);
/*
* If any other key is presently down, force its repeat to be
* well in the future (100s). This makes the last key to be
* pressed do the autorepeat.
*/
for (j = 0; j != UKBD_NKEYCODE; j++) {
if (j != i)
sc->sc_ntime[j] = now + (100 * 1000);
}
pfound: ;
}
sc->sc_odata = sc->sc_ndata;
memcpy(sc->sc_otime, sc->sc_ntime, sizeof(sc->sc_otime));
ukbd_event_keyinput(sc);
}
static void
ukbd_event_keyinput(struct ukbd_softc *sc)
{
int c;
UKBD_CTX_LOCK_ASSERT();
if ((sc->sc_flags & UKBD_FLAG_POLLING) != 0)
return;
if (sc->sc_inputs == 0)
return;
if (KBD_IS_ACTIVE(&sc->sc_kbd) &&
KBD_IS_BUSY(&sc->sc_kbd)) {
/* let the callback function process the input */
(sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT,
sc->sc_kbd.kb_callback.kc_arg);
} else {
/* read and discard the input, no one is waiting for it */
do {
c = ukbd_read_char(&sc->sc_kbd, 0);
} while (c != NOKEY);
}
}
static void
ukbd_timeout(void *arg)
{
struct ukbd_softc *sc = arg;
UKBD_LOCK_ASSERT();
sc->sc_time_ms += 25; /* milliseconds */
ukbd_interrupt(sc);
/* Make sure any leftover key events gets read out */
ukbd_event_keyinput(sc);
if (ukbd_any_key_pressed(sc) || (sc->sc_inputs != 0)) {
ukbd_start_timer(sc);
} else {
sc->sc_flags &= ~UKBD_FLAG_TIMER_RUNNING;
}
}
static uint8_t
ukbd_apple_fn(uint8_t keycode) {
switch (keycode) {
case 0x28: return 0x49; /* RETURN -> INSERT */
case 0x2a: return 0x4c; /* BACKSPACE -> DEL */
case 0x50: return 0x4a; /* LEFT ARROW -> HOME */
case 0x4f: return 0x4d; /* RIGHT ARROW -> END */
case 0x52: return 0x4b; /* UP ARROW -> PGUP */
case 0x51: return 0x4e; /* DOWN ARROW -> PGDN */
default: return keycode;
}
}
static uint8_t
ukbd_apple_swap(uint8_t keycode) {
switch (keycode) {
case 0x35: return 0x64;
case 0x64: return 0x35;
default: return keycode;
}
}
static void
ukbd_intr_callback(struct usb_xfer *xfer, usb_error_t error)
{
struct ukbd_softc *sc = usbd_xfer_softc(xfer);
struct usb_page_cache *pc;
uint8_t i;
uint8_t offset;
uint8_t id;
int len;
UKBD_LOCK_ASSERT();
usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
pc = usbd_xfer_get_frame(xfer, 0);
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
DPRINTF("actlen=%d bytes\n", len);
if (len == 0) {
DPRINTF("zero length data\n");
goto tr_setup;
}
if (sc->sc_kbd_id != 0) {
/* check and remove HID ID byte */
usbd_copy_out(pc, 0, &id, 1);
offset = 1;
len--;
if (len == 0) {
DPRINTF("zero length data\n");
goto tr_setup;
}
} else {
offset = 0;
id = 0;
}
if (len > UKBD_BUFFER_SIZE)
len = UKBD_BUFFER_SIZE;
/* get data */
usbd_copy_out(pc, offset, sc->sc_buffer, len);
/* clear temporary storage */
memset(&sc->sc_ndata, 0, sizeof(sc->sc_ndata));
/* scan through HID data */
if ((sc->sc_flags & UKBD_FLAG_APPLE_EJECT) &&
(id == sc->sc_id_apple_eject)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_apple_eject))
sc->sc_modifiers |= MOD_EJECT;
else
sc->sc_modifiers &= ~MOD_EJECT;
}
if ((sc->sc_flags & UKBD_FLAG_APPLE_FN) &&
(id == sc->sc_id_apple_fn)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_apple_fn))
sc->sc_modifiers |= MOD_FN;
else
sc->sc_modifiers &= ~MOD_FN;
}
if ((sc->sc_flags & UKBD_FLAG_CTRL_L) &&
(id == sc->sc_id_ctrl_l)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_ctrl_l))
sc-> sc_modifiers |= MOD_CONTROL_L;
else
sc-> sc_modifiers &= ~MOD_CONTROL_L;
}
if ((sc->sc_flags & UKBD_FLAG_CTRL_R) &&
(id == sc->sc_id_ctrl_r)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_ctrl_r))
sc->sc_modifiers |= MOD_CONTROL_R;
else
sc->sc_modifiers &= ~MOD_CONTROL_R;
}
if ((sc->sc_flags & UKBD_FLAG_SHIFT_L) &&
(id == sc->sc_id_shift_l)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_shift_l))
sc->sc_modifiers |= MOD_SHIFT_L;
else
sc->sc_modifiers &= ~MOD_SHIFT_L;
}
if ((sc->sc_flags & UKBD_FLAG_SHIFT_R) &&
(id == sc->sc_id_shift_r)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_shift_r))
sc->sc_modifiers |= MOD_SHIFT_R;
else
sc->sc_modifiers &= ~MOD_SHIFT_R;
}
if ((sc->sc_flags & UKBD_FLAG_ALT_L) &&
(id == sc->sc_id_alt_l)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_alt_l))
sc->sc_modifiers |= MOD_ALT_L;
else
sc->sc_modifiers &= ~MOD_ALT_L;
}
if ((sc->sc_flags & UKBD_FLAG_ALT_R) &&
(id == sc->sc_id_alt_r)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_alt_r))
sc->sc_modifiers |= MOD_ALT_R;
else
sc->sc_modifiers &= ~MOD_ALT_R;
}
if ((sc->sc_flags & UKBD_FLAG_WIN_L) &&
(id == sc->sc_id_win_l)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_win_l))
sc->sc_modifiers |= MOD_WIN_L;
else
sc->sc_modifiers &= ~MOD_WIN_L;
}
if ((sc->sc_flags & UKBD_FLAG_WIN_R) &&
(id == sc->sc_id_win_r)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_win_r))
sc->sc_modifiers |= MOD_WIN_R;
else
sc->sc_modifiers &= ~MOD_WIN_R;
}
sc->sc_ndata.modifiers = sc->sc_modifiers;
if ((sc->sc_flags & UKBD_FLAG_EVENTS) &&
(id == sc->sc_id_events)) {
i = sc->sc_loc_events.count;
if (i > UKBD_NKEYCODE)
i = UKBD_NKEYCODE;
if (i > len)
i = len;
while (i--) {
sc->sc_ndata.keycode[i] =
hid_get_data(sc->sc_buffer + i, len - i,
&sc->sc_loc_events);
}
}
#ifdef USB_DEBUG
DPRINTF("modifiers = 0x%04x\n", (int)sc->sc_modifiers);
for (i = 0; i < UKBD_NKEYCODE; i++) {
if (sc->sc_ndata.keycode[i]) {
DPRINTF("[%d] = 0x%02x\n",
(int)i, (int)sc->sc_ndata.keycode[i]);
}
}
#endif
if (sc->sc_modifiers & MOD_FN) {
for (i = 0; i < UKBD_NKEYCODE; i++) {
sc->sc_ndata.keycode[i] =
ukbd_apple_fn(sc->sc_ndata.keycode[i]);
}
}
if (sc->sc_flags & UKBD_FLAG_APPLE_SWAP) {
for (i = 0; i < UKBD_NKEYCODE; i++) {
sc->sc_ndata.keycode[i] =
ukbd_apple_swap(sc->sc_ndata.keycode[i]);
}
}
ukbd_interrupt(sc);
if (!(sc->sc_flags & UKBD_FLAG_TIMER_RUNNING)) {
if (ukbd_any_key_pressed(sc)) {
ukbd_start_timer(sc);
}
}
case USB_ST_SETUP:
tr_setup:
if (sc->sc_inputs < UKBD_IN_BUF_FULL) {
usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
usbd_transfer_submit(xfer);
} else {
DPRINTF("input queue is full!\n");
}
break;
default: /* Error */
DPRINTF("error=%s\n", usbd_errstr(error));
if (error != USB_ERR_CANCELLED) {
/* try to clear stall first */
usbd_xfer_set_stall(xfer);
goto tr_setup;
}
break;
}
}
static void
ukbd_set_leds_callback(struct usb_xfer *xfer, usb_error_t error)
{
struct ukbd_softc *sc = usbd_xfer_softc(xfer);
struct usb_device_request req;
struct usb_page_cache *pc;
uint8_t id;
uint8_t any;
int len;
UKBD_LOCK_ASSERT();
#ifdef USB_DEBUG
if (ukbd_no_leds)
return;
#endif
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
case USB_ST_SETUP:
if (!(sc->sc_flags & UKBD_FLAG_SET_LEDS))
break;
sc->sc_flags &= ~UKBD_FLAG_SET_LEDS;
req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
req.bRequest = UR_SET_REPORT;
USETW2(req.wValue, UHID_OUTPUT_REPORT, 0);
req.wIndex[0] = sc->sc_iface_no;
req.wIndex[1] = 0;
req.wLength[1] = 0;
memset(sc->sc_buffer, 0, UKBD_BUFFER_SIZE);
id = 0;
any = 0;
/* Assumption: All led bits must be in the same ID. */
if (sc->sc_flags & UKBD_FLAG_NUMLOCK) {
if (sc->sc_leds & NLKED) {
hid_put_data_unsigned(sc->sc_buffer + 1, UKBD_BUFFER_SIZE - 1,
&sc->sc_loc_numlock, 1);
}
id = sc->sc_id_numlock;
any = 1;
}
if (sc->sc_flags & UKBD_FLAG_SCROLLLOCK) {
if (sc->sc_leds & SLKED) {
hid_put_data_unsigned(sc->sc_buffer + 1, UKBD_BUFFER_SIZE - 1,
&sc->sc_loc_scrolllock, 1);
}
id = sc->sc_id_scrolllock;
any = 1;
}
if (sc->sc_flags & UKBD_FLAG_CAPSLOCK) {
if (sc->sc_leds & CLKED) {
hid_put_data_unsigned(sc->sc_buffer + 1, UKBD_BUFFER_SIZE - 1,
&sc->sc_loc_capslock, 1);
}
id = sc->sc_id_capslock;
any = 1;
}
/* if no leds, nothing to do */
if (!any)
break;
#ifdef EVDEV_SUPPORT
if (sc->sc_evdev != NULL)
evdev_push_leds(sc->sc_evdev, sc->sc_leds);
#endif
/* range check output report length */
len = sc->sc_led_size;
if (len > (UKBD_BUFFER_SIZE - 1))
len = (UKBD_BUFFER_SIZE - 1);
/* check if we need to prefix an ID byte */
sc->sc_buffer[0] = id;
pc = usbd_xfer_get_frame(xfer, 1);
if (id != 0) {
len++;
usbd_copy_in(pc, 0, sc->sc_buffer, len);
} else {
usbd_copy_in(pc, 0, sc->sc_buffer + 1, len);
}
req.wLength[0] = len;
usbd_xfer_set_frame_len(xfer, 1, len);
DPRINTF("len=%d, id=%d\n", len, id);
/* setup control request last */
pc = usbd_xfer_get_frame(xfer, 0);
usbd_copy_in(pc, 0, &req, sizeof(req));
usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
/* start data transfer */
usbd_xfer_set_frames(xfer, 2);
usbd_transfer_submit(xfer);
break;
default: /* Error */
DPRINTFN(1, "error=%s\n", usbd_errstr(error));
break;
}
}
static const struct usb_config ukbd_config[UKBD_N_TRANSFER] = {
[UKBD_INTR_DT_0] = {
.type = UE_INTERRUPT,
.endpoint = UE_ADDR_ANY,
.direction = UE_DIR_IN,
.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
.bufsize = 0, /* use wMaxPacketSize */
.callback = &ukbd_intr_callback,
},
[UKBD_INTR_DT_1] = {
.type = UE_INTERRUPT,
.endpoint = UE_ADDR_ANY,
.direction = UE_DIR_IN,
.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
.bufsize = 0, /* use wMaxPacketSize */
.callback = &ukbd_intr_callback,
},
[UKBD_CTRL_LED] = {
.type = UE_CONTROL,
.endpoint = 0x00, /* Control pipe */
.direction = UE_DIR_ANY,
.bufsize = sizeof(struct usb_device_request) + UKBD_BUFFER_SIZE,
.callback = &ukbd_set_leds_callback,
.timeout = 1000, /* 1 second */
},
};
/* A match on these entries will load ukbd */
static const STRUCT_USB_HOST_ID __used ukbd_devs[] = {
{USB_IFACE_CLASS(UICLASS_HID),
USB_IFACE_SUBCLASS(UISUBCLASS_BOOT),
USB_IFACE_PROTOCOL(UIPROTO_BOOT_KEYBOARD),},
};
static int
ukbd_probe(device_t dev)
{
keyboard_switch_t *sw = kbd_get_switch(UKBD_DRIVER_NAME);
struct usb_attach_arg *uaa = device_get_ivars(dev);
void *d_ptr;
int error;
uint16_t d_len;
UKBD_LOCK_ASSERT();
DPRINTFN(11, "\n");
if (sw == NULL) {
return (ENXIO);
}
if (uaa->usb_mode != USB_MODE_HOST) {
return (ENXIO);
}
if (uaa->info.bInterfaceClass != UICLASS_HID)
return (ENXIO);
if (usb_test_quirk(uaa, UQ_KBD_IGNORE))
return (ENXIO);
if ((uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) &&
(uaa->info.bInterfaceProtocol == UIPROTO_BOOT_KEYBOARD))
return (BUS_PROBE_DEFAULT);
error = usbd_req_get_hid_desc(uaa->device, NULL,
&d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex);
if (error)
return (ENXIO);
if (hid_is_keyboard(d_ptr, d_len)) {
if (hid_is_mouse(d_ptr, d_len)) {
/*
* NOTE: We currently don't support USB mouse
* and USB keyboard on the same USB endpoint.
* Let "ums" driver win.
*/
error = ENXIO;
} else {
error = BUS_PROBE_DEFAULT;
}
} else {
error = ENXIO;
}
free(d_ptr, M_TEMP);
return (error);
}
static void
ukbd_parse_hid(struct ukbd_softc *sc, const uint8_t *ptr, uint32_t len)
{
uint32_t flags;
/* reset detected bits */
sc->sc_flags &= ~UKBD_FLAG_HID_MASK;
/* check if there is an ID byte */
sc->sc_kbd_size = hid_report_size(ptr, len,
hid_input, &sc->sc_kbd_id);
/* investigate if this is an Apple Keyboard */
if (hid_locate(ptr, len,
HID_USAGE2(HUP_CONSUMER, HUG_APPLE_EJECT),
hid_input, 0, &sc->sc_loc_apple_eject, &flags,
&sc->sc_id_apple_eject)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_APPLE_EJECT |
UKBD_FLAG_APPLE_SWAP;
DPRINTFN(1, "Found Apple eject-key\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(0xFFFF, 0x0003),
hid_input, 0, &sc->sc_loc_apple_fn, &flags,
&sc->sc_id_apple_fn)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_APPLE_FN;
DPRINTFN(1, "Found Apple FN-key\n");
}
/* figure out some keys */
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0xE0),
hid_input, 0, &sc->sc_loc_ctrl_l, &flags,
&sc->sc_id_ctrl_l)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_CTRL_L;
DPRINTFN(1, "Found left control\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0xE4),
hid_input, 0, &sc->sc_loc_ctrl_r, &flags,
&sc->sc_id_ctrl_r)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_CTRL_R;
DPRINTFN(1, "Found right control\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0xE1),
hid_input, 0, &sc->sc_loc_shift_l, &flags,
&sc->sc_id_shift_l)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_SHIFT_L;
DPRINTFN(1, "Found left shift\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0xE5),
hid_input, 0, &sc->sc_loc_shift_r, &flags,
&sc->sc_id_shift_r)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_SHIFT_R;
DPRINTFN(1, "Found right shift\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0xE2),
hid_input, 0, &sc->sc_loc_alt_l, &flags,
&sc->sc_id_alt_l)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_ALT_L;
DPRINTFN(1, "Found left alt\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0xE6),
hid_input, 0, &sc->sc_loc_alt_r, &flags,
&sc->sc_id_alt_r)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_ALT_R;
DPRINTFN(1, "Found right alt\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0xE3),
hid_input, 0, &sc->sc_loc_win_l, &flags,
&sc->sc_id_win_l)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_WIN_L;
DPRINTFN(1, "Found left GUI\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0xE7),
hid_input, 0, &sc->sc_loc_win_r, &flags,
&sc->sc_id_win_r)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_WIN_R;
DPRINTFN(1, "Found right GUI\n");
}
/* figure out event buffer */
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0x00),
hid_input, 0, &sc->sc_loc_events, &flags,
&sc->sc_id_events)) {
if (flags & HIO_VARIABLE) {
DPRINTFN(1, "Ignoring keyboard event control\n");
} else {
sc->sc_flags |= UKBD_FLAG_EVENTS;
DPRINTFN(1, "Found keyboard event array\n");
}
}
/* figure out leds on keyboard */
sc->sc_led_size = hid_report_size(ptr, len,
hid_output, NULL);
if (hid_locate(ptr, len,
HID_USAGE2(HUP_LEDS, 0x01),
hid_output, 0, &sc->sc_loc_numlock, &flags,
&sc->sc_id_numlock)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_NUMLOCK;
DPRINTFN(1, "Found keyboard numlock\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_LEDS, 0x02),
hid_output, 0, &sc->sc_loc_capslock, &flags,
&sc->sc_id_capslock)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_CAPSLOCK;
DPRINTFN(1, "Found keyboard capslock\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_LEDS, 0x03),
hid_output, 0, &sc->sc_loc_scrolllock, &flags,
&sc->sc_id_scrolllock)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_SCROLLLOCK;
DPRINTFN(1, "Found keyboard scrolllock\n");
}
}
static int
ukbd_attach(device_t dev)
{
struct ukbd_softc *sc = device_get_softc(dev);
struct usb_attach_arg *uaa = device_get_ivars(dev);
int unit = device_get_unit(dev);
keyboard_t *kbd = &sc->sc_kbd;
void *hid_ptr = NULL;
usb_error_t err;
uint16_t n;
uint16_t hid_len;
#ifdef EVDEV_SUPPORT
struct evdev_dev *evdev;
int i;
#endif
#ifdef USB_DEBUG
int rate;
#endif
UKBD_LOCK_ASSERT();
kbd_init_struct(kbd, UKBD_DRIVER_NAME, KB_OTHER, unit, 0, 0, 0);
kbd->kb_data = (void *)sc;
device_set_usb_desc(dev);
sc->sc_udev = uaa->device;
sc->sc_iface = uaa->iface;
sc->sc_iface_index = uaa->info.bIfaceIndex;
sc->sc_iface_no = uaa->info.bIfaceNum;
sc->sc_mode = K_XLATE;
usb_callout_init_mtx(&sc->sc_callout, &Giant, 0);
#ifdef UKBD_NO_POLLING
err = usbd_transfer_setup(uaa->device,
&uaa->info.bIfaceIndex, sc->sc_xfer, ukbd_config,
UKBD_N_TRANSFER, sc, &Giant);
#else
/*
* Setup the UKBD USB transfers one by one, so they are memory
* independent which allows for handling panics triggered by
* the keyboard driver itself, typically via CTRL+ALT+ESC
* sequences. Or if the USB keyboard driver was processing a
* key at the moment of panic.
*/
for (n = 0; n != UKBD_N_TRANSFER; n++) {
err = usbd_transfer_setup(uaa->device,
&uaa->info.bIfaceIndex, sc->sc_xfer + n, ukbd_config + n,
1, sc, &Giant);
if (err)
break;
}
#endif
if (err) {
DPRINTF("error=%s\n", usbd_errstr(err));
goto detach;
}
/* setup default keyboard maps */
sc->sc_keymap = key_map;
sc->sc_accmap = accent_map;
for (n = 0; n < UKBD_NFKEY; n++) {
sc->sc_fkeymap[n] = fkey_tab[n];
}
kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap,
sc->sc_fkeymap, UKBD_NFKEY);
KBD_FOUND_DEVICE(kbd);
ukbd_clear_state(kbd);
/*
* FIXME: set the initial value for lock keys in "sc_state"
* according to the BIOS data?
*/
KBD_PROBE_DONE(kbd);
/* get HID descriptor */
err = usbd_req_get_hid_desc(uaa->device, NULL, &hid_ptr,
&hid_len, M_TEMP, uaa->info.bIfaceIndex);
if (err == 0) {
DPRINTF("Parsing HID descriptor of %d bytes\n",
(int)hid_len);
ukbd_parse_hid(sc, hid_ptr, hid_len);
free(hid_ptr, M_TEMP);
}
/* check if we should use the boot protocol */
if (usb_test_quirk(uaa, UQ_KBD_BOOTPROTO) ||
(err != 0) || (!(sc->sc_flags & UKBD_FLAG_EVENTS))) {
DPRINTF("Forcing boot protocol\n");
err = usbd_req_set_protocol(sc->sc_udev, NULL,
sc->sc_iface_index, 0);
if (err != 0) {
DPRINTF("Set protocol error=%s (ignored)\n",
usbd_errstr(err));
}
ukbd_parse_hid(sc, ukbd_boot_desc, sizeof(ukbd_boot_desc));
}
/* ignore if SETIDLE fails, hence it is not crucial */
usbd_req_set_idle(sc->sc_udev, NULL, sc->sc_iface_index, 0, 0);
ukbd_ioctl(kbd, KDSETLED, (caddr_t)&sc->sc_state);
KBD_INIT_DONE(kbd);
if (kbd_register(kbd) < 0) {
goto detach;
}
KBD_CONFIG_DONE(kbd);
ukbd_enable(kbd);
#ifdef KBD_INSTALL_CDEV
if (kbd_attach(kbd)) {
goto detach;
}
#endif
#ifdef EVDEV_SUPPORT
evdev = evdev_alloc();
evdev_set_name(evdev, device_get_desc(dev));
evdev_set_phys(evdev, device_get_nameunit(dev));
evdev_set_id(evdev, BUS_USB, uaa->info.idVendor,
uaa->info.idProduct, 0);
evdev_set_serial(evdev, usb_get_serial(uaa->device));
evdev_set_methods(evdev, kbd, &ukbd_evdev_methods);
evdev_support_event(evdev, EV_SYN);
evdev_support_event(evdev, EV_KEY);
if (sc->sc_flags & (UKBD_FLAG_NUMLOCK | UKBD_FLAG_CAPSLOCK |
UKBD_FLAG_SCROLLLOCK))
evdev_support_event(evdev, EV_LED);
evdev_support_event(evdev, EV_REP);
for (i = 0x00; i <= 0xFF; i++)
evdev_support_key(evdev, evdev_hid2key(i));
if (sc->sc_flags & UKBD_FLAG_NUMLOCK)
evdev_support_led(evdev, LED_NUML);
if (sc->sc_flags & UKBD_FLAG_CAPSLOCK)
evdev_support_led(evdev, LED_CAPSL);
if (sc->sc_flags & UKBD_FLAG_SCROLLLOCK)
evdev_support_led(evdev, LED_SCROLLL);
if (evdev_register(evdev))
evdev_free(evdev);
else
sc->sc_evdev = evdev;
#endif
sc->sc_flags |= UKBD_FLAG_ATTACHED;
if (bootverbose) {
kbdd_diag(kbd, bootverbose);
}
#ifdef USB_DEBUG
/* check for polling rate override */
rate = ukbd_pollrate;
if (rate > 0) {
if (rate > 1000)
rate = 1;
else
rate = 1000 / rate;
/* set new polling interval in ms */
usbd_xfer_set_interval(sc->sc_xfer[UKBD_INTR_DT_0], rate);
usbd_xfer_set_interval(sc->sc_xfer[UKBD_INTR_DT_1], rate);
}
#endif
/* start the keyboard */
usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_0]);
usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_1]);
return (0); /* success */
detach:
ukbd_detach(dev);
return (ENXIO); /* error */
}
static int
ukbd_detach(device_t dev)
{
struct ukbd_softc *sc = device_get_softc(dev);
int error;
UKBD_LOCK_ASSERT();
DPRINTF("\n");
sc->sc_flags |= UKBD_FLAG_GONE;
usb_callout_stop(&sc->sc_callout);
/* kill any stuck keys */
if (sc->sc_flags & UKBD_FLAG_ATTACHED) {
/* stop receiving events from the USB keyboard */
usbd_transfer_stop(sc->sc_xfer[UKBD_INTR_DT_0]);
usbd_transfer_stop(sc->sc_xfer[UKBD_INTR_DT_1]);
/* release all leftover keys, if any */
memset(&sc->sc_ndata, 0, sizeof(sc->sc_ndata));
/* process releasing of all keys */
ukbd_interrupt(sc);
}
ukbd_disable(&sc->sc_kbd);
#ifdef KBD_INSTALL_CDEV
if (sc->sc_flags & UKBD_FLAG_ATTACHED) {
error = kbd_detach(&sc->sc_kbd);
if (error) {
/* usb attach cannot return an error */
device_printf(dev, "WARNING: kbd_detach() "
"returned non-zero! (ignored)\n");
}
}
#endif
#ifdef EVDEV_SUPPORT
evdev_free(sc->sc_evdev);
#endif
if (KBD_IS_CONFIGURED(&sc->sc_kbd)) {
error = kbd_unregister(&sc->sc_kbd);
if (error) {
/* usb attach cannot return an error */
device_printf(dev, "WARNING: kbd_unregister() "
"returned non-zero! (ignored)\n");
}
}
sc->sc_kbd.kb_flags = 0;
usbd_transfer_unsetup(sc->sc_xfer, UKBD_N_TRANSFER);
usb_callout_drain(&sc->sc_callout);
DPRINTF("%s: disconnected\n",
device_get_nameunit(dev));
return (0);
}
static int
ukbd_resume(device_t dev)
{
struct ukbd_softc *sc = device_get_softc(dev);
UKBD_LOCK_ASSERT();
ukbd_clear_state(&sc->sc_kbd);
return (0);
}
/* early keyboard probe, not supported */
static int
ukbd_configure(int flags)
{
return (0);
}
/* detect a keyboard, not used */
static int
ukbd__probe(int unit, void *arg, int flags)
{
return (ENXIO);
}
/* reset and initialize the device, not used */
static int
ukbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
{
return (ENXIO);
}
/* test the interface to the device, not used */
static int
ukbd_test_if(keyboard_t *kbd)
{
return (0);
}
/* finish using this keyboard, not used */
static int
ukbd_term(keyboard_t *kbd)
{
return (ENXIO);
}
/* keyboard interrupt routine, not used */
static int
ukbd_intr(keyboard_t *kbd, void *arg)
{
return (0);
}
/* lock the access to the keyboard, not used */
static int
ukbd_lock(keyboard_t *kbd, int lock)
{
return (1);
}
/*
* Enable the access to the device; until this function is called,
* the client cannot read from the keyboard.
*/
static int
ukbd_enable(keyboard_t *kbd)
{
UKBD_LOCK();
KBD_ACTIVATE(kbd);
UKBD_UNLOCK();
return (0);
}
/* disallow the access to the device */
static int
ukbd_disable(keyboard_t *kbd)
{
UKBD_LOCK();
KBD_DEACTIVATE(kbd);
UKBD_UNLOCK();
return (0);
}
/* check if data is waiting */
/* Currently unused. */
static int
ukbd_check(keyboard_t *kbd)
{
struct ukbd_softc *sc = kbd->kb_data;
UKBD_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (0);
if (sc->sc_flags & UKBD_FLAG_POLLING)
ukbd_do_poll(sc, 0);
#ifdef UKBD_EMULATE_ATSCANCODE
if (sc->sc_buffered_char[0]) {
return (1);
}
#endif
if (sc->sc_inputs > 0) {
return (1);
}
return (0);
}
/* check if char is waiting */
static int
ukbd_check_char_locked(keyboard_t *kbd)
{
struct ukbd_softc *sc = kbd->kb_data;
UKBD_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (0);
if ((sc->sc_composed_char > 0) &&
(!(sc->sc_flags & UKBD_FLAG_COMPOSE))) {
return (1);
}
return (ukbd_check(kbd));
}
static int
ukbd_check_char(keyboard_t *kbd)
{
int result;
UKBD_LOCK();
result = ukbd_check_char_locked(kbd);
UKBD_UNLOCK();
return (result);
}
/* read one byte from the keyboard if it's allowed */
/* Currently unused. */
static int
ukbd_read(keyboard_t *kbd, int wait)
{
struct ukbd_softc *sc = kbd->kb_data;
int32_t usbcode;
#ifdef UKBD_EMULATE_ATSCANCODE
uint32_t keycode;
uint32_t scancode;
#endif
UKBD_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (-1);
#ifdef UKBD_EMULATE_ATSCANCODE
if (sc->sc_buffered_char[0]) {
scancode = sc->sc_buffered_char[0];
if (scancode & SCAN_PREFIX) {
sc->sc_buffered_char[0] &= ~SCAN_PREFIX;
return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
}
sc->sc_buffered_char[0] = sc->sc_buffered_char[1];
sc->sc_buffered_char[1] = 0;
return (scancode);
}
#endif /* UKBD_EMULATE_ATSCANCODE */
/* XXX */
usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1);
if (!KBD_IS_ACTIVE(kbd) || (usbcode == -1))
return (-1);
++(kbd->kb_count);
#ifdef UKBD_EMULATE_ATSCANCODE
keycode = ukbd_trtab[KEY_INDEX(usbcode)];
if (keycode == NN) {
return -1;
}
return (ukbd_key2scan(sc, keycode, sc->sc_ndata.modifiers,
(usbcode & KEY_RELEASE)));
#else /* !UKBD_EMULATE_ATSCANCODE */
return (usbcode);
#endif /* UKBD_EMULATE_ATSCANCODE */
}
/* read char from the keyboard */
static uint32_t
ukbd_read_char_locked(keyboard_t *kbd, int wait)
{
struct ukbd_softc *sc = kbd->kb_data;
uint32_t action;
uint32_t keycode;
int32_t usbcode;
#ifdef UKBD_EMULATE_ATSCANCODE
uint32_t scancode;
#endif
UKBD_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (NOKEY);
next_code:
/* do we have a composed char to return ? */
if ((sc->sc_composed_char > 0) &&
(!(sc->sc_flags & UKBD_FLAG_COMPOSE))) {
action = sc->sc_composed_char;
sc->sc_composed_char = 0;
if (action > 0xFF) {
goto errkey;
}
goto done;
}
#ifdef UKBD_EMULATE_ATSCANCODE
/* do we have a pending raw scan code? */
if (sc->sc_mode == K_RAW) {
scancode = sc->sc_buffered_char[0];
if (scancode) {
if (scancode & SCAN_PREFIX) {
sc->sc_buffered_char[0] = (scancode & ~SCAN_PREFIX);
return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
}
sc->sc_buffered_char[0] = sc->sc_buffered_char[1];
sc->sc_buffered_char[1] = 0;
return (scancode);
}
}
#endif /* UKBD_EMULATE_ATSCANCODE */
/* see if there is something in the keyboard port */
/* XXX */
usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1);
if (usbcode == -1) {
return (NOKEY);
}
++kbd->kb_count;
#ifdef UKBD_EMULATE_ATSCANCODE
/* USB key index -> key code -> AT scan code */
keycode = ukbd_trtab[KEY_INDEX(usbcode)];
if (keycode == NN) {
return (NOKEY);
}
/* return an AT scan code for the K_RAW mode */
if (sc->sc_mode == K_RAW) {
return (ukbd_key2scan(sc, keycode, sc->sc_ndata.modifiers,
(usbcode & KEY_RELEASE)));
}
#else /* !UKBD_EMULATE_ATSCANCODE */
/* return the byte as is for the K_RAW mode */
if (sc->sc_mode == K_RAW) {
return (usbcode);
}
/* USB key index -> key code */
keycode = ukbd_trtab[KEY_INDEX(usbcode)];
if (keycode == NN) {
return (NOKEY);
}
#endif /* UKBD_EMULATE_ATSCANCODE */
switch (keycode) {
case 0x38: /* left alt (compose key) */
if (usbcode & KEY_RELEASE) {
if (sc->sc_flags & UKBD_FLAG_COMPOSE) {
sc->sc_flags &= ~UKBD_FLAG_COMPOSE;
if (sc->sc_composed_char > 0xFF) {
sc->sc_composed_char = 0;
}
}
} else {
if (!(sc->sc_flags & UKBD_FLAG_COMPOSE)) {
sc->sc_flags |= UKBD_FLAG_COMPOSE;
sc->sc_composed_char = 0;
}
}
break;
/* XXX: I don't like these... */
case 0x5c: /* print screen */
if (sc->sc_flags & ALTS) {
keycode = 0x54; /* sysrq */
}
break;
case 0x68: /* pause/break */
if (sc->sc_flags & CTLS) {
keycode = 0x6c; /* break */
}
break;
}
/* return the key code in the K_CODE mode */
if (usbcode & KEY_RELEASE) {
keycode |= SCAN_RELEASE;
}
if (sc->sc_mode == K_CODE) {
return (keycode);
}
/* compose a character code */
if (sc->sc_flags & UKBD_FLAG_COMPOSE) {
switch (keycode) {
/* key pressed, process it */
case 0x47:
case 0x48:
case 0x49: /* keypad 7,8,9 */
sc->sc_composed_char *= 10;
sc->sc_composed_char += keycode - 0x40;
goto check_composed;
case 0x4B:
case 0x4C:
case 0x4D: /* keypad 4,5,6 */
sc->sc_composed_char *= 10;
sc->sc_composed_char += keycode - 0x47;
goto check_composed;
case 0x4F:
case 0x50:
case 0x51: /* keypad 1,2,3 */
sc->sc_composed_char *= 10;
sc->sc_composed_char += keycode - 0x4E;
goto check_composed;
case 0x52: /* keypad 0 */
sc->sc_composed_char *= 10;
goto check_composed;
/* key released, no interest here */
case SCAN_RELEASE | 0x47:
case SCAN_RELEASE | 0x48:
case SCAN_RELEASE | 0x49: /* keypad 7,8,9 */
case SCAN_RELEASE | 0x4B:
case SCAN_RELEASE | 0x4C:
case SCAN_RELEASE | 0x4D: /* keypad 4,5,6 */
case SCAN_RELEASE | 0x4F:
case SCAN_RELEASE | 0x50:
case SCAN_RELEASE | 0x51: /* keypad 1,2,3 */
case SCAN_RELEASE | 0x52: /* keypad 0 */
goto next_code;
case 0x38: /* left alt key */
break;
default:
if (sc->sc_composed_char > 0) {
sc->sc_flags &= ~UKBD_FLAG_COMPOSE;
sc->sc_composed_char = 0;
goto errkey;
}
break;
}
}
/* keycode to key action */
action = genkbd_keyaction(kbd, SCAN_CHAR(keycode),
(keycode & SCAN_RELEASE),
&sc->sc_state, &sc->sc_accents);
if (action == NOKEY) {
goto next_code;
}
done:
return (action);
check_composed:
if (sc->sc_composed_char <= 0xFF) {
goto next_code;
}
errkey:
return (ERRKEY);
}
/* Currently wait is always false. */
static uint32_t
ukbd_read_char(keyboard_t *kbd, int wait)
{
uint32_t keycode;
UKBD_LOCK();
keycode = ukbd_read_char_locked(kbd, wait);
UKBD_UNLOCK();
return (keycode);
}
/* some useful control functions */
static int
ukbd_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
struct ukbd_softc *sc = kbd->kb_data;
int i;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
int ival;
#endif
UKBD_LOCK_ASSERT();
switch (cmd) {
case KDGKBMODE: /* get keyboard mode */
*(int *)arg = sc->sc_mode;
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 7):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBMODE: /* set keyboard mode */
switch (*(int *)arg) {
case K_XLATE:
if (sc->sc_mode != K_XLATE) {
/* make lock key state and LED state match */
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= KBD_LED_VAL(kbd);
}
/* FALLTHROUGH */
case K_RAW:
case K_CODE:
if (sc->sc_mode != *(int *)arg) {
if ((sc->sc_flags & UKBD_FLAG_POLLING) == 0)
ukbd_clear_state(kbd);
sc->sc_mode = *(int *)arg;
}
break;
default:
return (EINVAL);
}
break;
case KDGETLED: /* get keyboard LED */
*(int *)arg = KBD_LED_VAL(kbd);
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 66):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETLED: /* set keyboard LED */
/* NOTE: lock key state in "sc_state" won't be changed */
if (*(int *)arg & ~LOCK_MASK)
return (EINVAL);
i = *(int *)arg;
/* replace CAPS LED with ALTGR LED for ALTGR keyboards */
if (sc->sc_mode == K_XLATE &&
kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
if (i & ALKED)
i |= CLKED;
else
i &= ~CLKED;
}
if (KBD_HAS_DEVICE(kbd))
ukbd_set_leds(sc, i);
KBD_LED_VAL(kbd) = *(int *)arg;
break;
case KDGKBSTATE: /* get lock key state */
*(int *)arg = sc->sc_state & LOCK_MASK;
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 20):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBSTATE: /* set lock key state */
if (*(int *)arg & ~LOCK_MASK) {
return (EINVAL);
}
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= *(int *)arg;
/* set LEDs and quit */
return (ukbd_ioctl(kbd, KDSETLED, arg));
case KDSETREPEAT: /* set keyboard repeat rate (new
* interface) */
if (!KBD_HAS_DEVICE(kbd)) {
return (0);
}
if (((int *)arg)[1] < 0) {
return (EINVAL);
}
if (((int *)arg)[0] < 0) {
return (EINVAL);
}
if (((int *)arg)[0] < 200) /* fastest possible value */
kbd->kb_delay1 = 200;
else
kbd->kb_delay1 = ((int *)arg)[0];
kbd->kb_delay2 = ((int *)arg)[1];
#ifdef EVDEV_SUPPORT
if (sc->sc_evdev != NULL)
evdev_push_repeats(sc->sc_evdev, kbd);
#endif
return (0);
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 67):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETRAD: /* set keyboard repeat rate (old
* interface) */
return (ukbd_set_typematic(kbd, *(int *)arg));
case PIO_KEYMAP: /* set keyboard translation table */
case OPIO_KEYMAP: /* set keyboard translation table
* (compat) */
case PIO_KEYMAPENT: /* set keyboard translation table
* entry */
case PIO_DEADKEYMAP: /* set accent key translation table */
sc->sc_accents = 0;
/* FALLTHROUGH */
default:
return (genkbd_commonioctl(kbd, cmd, arg));
}
return (0);
}
static int
ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
int result;
/*
* XXX Check if someone is calling us from a critical section:
*/
if (curthread->td_critnest != 0)
return (EDEADLK);
/*
* XXX KDGKBSTATE, KDSKBSTATE and KDSETLED can be called from any
* context where printf(9) can be called, which among other things
* includes interrupt filters and threads with any kinds of locks
* already held. For this reason it would be dangerous to acquire
* the Giant here unconditionally. On the other hand we have to
* have it to handle the ioctl.
* So we make our best effort to auto-detect whether we can grab
* the Giant or not. Blame syscons(4) for this.
*/
switch (cmd) {
case KDGKBSTATE:
case KDSKBSTATE:
case KDSETLED:
if (!mtx_owned(&Giant) && !SCHEDULER_STOPPED())
return (EDEADLK); /* best I could come up with */
/* FALLTHROUGH */
default:
UKBD_LOCK();
result = ukbd_ioctl_locked(kbd, cmd, arg);
UKBD_UNLOCK();
return (result);
}
}
/* clear the internal state of the keyboard */
static void
ukbd_clear_state(keyboard_t *kbd)
{
struct ukbd_softc *sc = kbd->kb_data;
UKBD_CTX_LOCK_ASSERT();
sc->sc_flags &= ~(UKBD_FLAG_COMPOSE | UKBD_FLAG_POLLING);
sc->sc_state &= LOCK_MASK; /* preserve locking key state */
sc->sc_accents = 0;
sc->sc_composed_char = 0;
#ifdef UKBD_EMULATE_ATSCANCODE
sc->sc_buffered_char[0] = 0;
sc->sc_buffered_char[1] = 0;
#endif
memset(&sc->sc_ndata, 0, sizeof(sc->sc_ndata));
memset(&sc->sc_odata, 0, sizeof(sc->sc_odata));
memset(&sc->sc_ntime, 0, sizeof(sc->sc_ntime));
memset(&sc->sc_otime, 0, sizeof(sc->sc_otime));
}
/* save the internal state, not used */
static int
ukbd_get_state(keyboard_t *kbd, void *buf, size_t len)
{
return (len == 0) ? 1 : -1;
}
/* set the internal state, not used */
static int
ukbd_set_state(keyboard_t *kbd, void *buf, size_t len)
{
return (EINVAL);
}
static int
ukbd_poll(keyboard_t *kbd, int on)
{
struct ukbd_softc *sc = kbd->kb_data;
UKBD_LOCK();
/*
* Keep a reference count on polling to allow recursive
* cngrab() during a panic for example.
*/
if (on)
sc->sc_polling++;
else if (sc->sc_polling > 0)
sc->sc_polling--;
if (sc->sc_polling != 0) {
sc->sc_flags |= UKBD_FLAG_POLLING;
sc->sc_poll_thread = curthread;
} else {
sc->sc_flags &= ~UKBD_FLAG_POLLING;
ukbd_start_timer(sc); /* start timer */
}
UKBD_UNLOCK();
return (0);
}
/* local functions */
static void
ukbd_set_leds(struct ukbd_softc *sc, uint8_t leds)
{
UKBD_LOCK_ASSERT();
DPRINTF("leds=0x%02x\n", leds);
sc->sc_leds = leds;
sc->sc_flags |= UKBD_FLAG_SET_LEDS;
/* start transfer, if not already started */
usbd_transfer_start(sc->sc_xfer[UKBD_CTRL_LED]);
}
static int
ukbd_set_typematic(keyboard_t *kbd, int code)
{
#ifdef EVDEV_SUPPORT
struct ukbd_softc *sc = kbd->kb_data;
#endif
static const int delays[] = {250, 500, 750, 1000};
static const int rates[] = {34, 38, 42, 46, 50, 55, 59, 63,
68, 76, 84, 92, 100, 110, 118, 126,
136, 152, 168, 184, 200, 220, 236, 252,
272, 304, 336, 368, 400, 440, 472, 504};
if (code & ~0x7f) {
return (EINVAL);
}
kbd->kb_delay1 = delays[(code >> 5) & 3];
kbd->kb_delay2 = rates[code & 0x1f];
#ifdef EVDEV_SUPPORT
if (sc->sc_evdev != NULL)
evdev_push_repeats(sc->sc_evdev, kbd);
#endif
return (0);
}
#ifdef UKBD_EMULATE_ATSCANCODE
static int
ukbd_key2scan(struct ukbd_softc *sc, int code, int shift, int up)
{
static const int scan[] = {
/* 89 */
0x11c, /* Enter */
/* 90-99 */
0x11d, /* Ctrl-R */
0x135, /* Divide */
0x137 | SCAN_PREFIX_SHIFT, /* PrintScreen */
0x138, /* Alt-R */
0x147, /* Home */
0x148, /* Up */
0x149, /* PageUp */
0x14b, /* Left */
0x14d, /* Right */
0x14f, /* End */
/* 100-109 */
0x150, /* Down */
0x151, /* PageDown */
0x152, /* Insert */
0x153, /* Delete */
0x146, /* XXX Pause/Break */
0x15b, /* Win_L(Super_L) */
0x15c, /* Win_R(Super_R) */
0x15d, /* Application(Menu) */
/* SUN TYPE 6 USB KEYBOARD */
0x168, /* Sun Type 6 Help */
0x15e, /* Sun Type 6 Stop */
/* 110 - 119 */
0x15f, /* Sun Type 6 Again */
0x160, /* Sun Type 6 Props */
0x161, /* Sun Type 6 Undo */
0x162, /* Sun Type 6 Front */
0x163, /* Sun Type 6 Copy */
0x164, /* Sun Type 6 Open */
0x165, /* Sun Type 6 Paste */
0x166, /* Sun Type 6 Find */
0x167, /* Sun Type 6 Cut */
0x125, /* Sun Type 6 Mute */
/* 120 - 130 */
0x11f, /* Sun Type 6 VolumeDown */
0x11e, /* Sun Type 6 VolumeUp */
0x120, /* Sun Type 6 PowerDown */
/* Japanese 106/109 keyboard */
0x73, /* Keyboard Intl' 1 (backslash / underscore) */
0x70, /* Keyboard Intl' 2 (Katakana / Hiragana) */
0x7d, /* Keyboard Intl' 3 (Yen sign) (Not using in jp106/109) */
0x79, /* Keyboard Intl' 4 (Henkan) */
0x7b, /* Keyboard Intl' 5 (Muhenkan) */
0x5c, /* Keyboard Intl' 6 (Keypad ,) (For PC-9821 layout) */
0x71, /* Apple Keyboard JIS (Kana) */
0x72, /* Apple Keyboard JIS (Eisu) */
};
if ((code >= 89) && (code < (int)(89 + nitems(scan)))) {
code = scan[code - 89];
}
/* Pause/Break */
if ((code == 104) && (!(shift & (MOD_CONTROL_L | MOD_CONTROL_R)))) {
code = (0x45 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL);
}
if (shift & (MOD_SHIFT_L | MOD_SHIFT_R)) {
code &= ~SCAN_PREFIX_SHIFT;
}
code |= (up ? SCAN_RELEASE : SCAN_PRESS);
if (code & SCAN_PREFIX) {
if (code & SCAN_PREFIX_CTL) {
/* Ctrl */
sc->sc_buffered_char[0] = (0x1d | (code & SCAN_RELEASE));
sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX);
} else if (code & SCAN_PREFIX_SHIFT) {
/* Shift */
sc->sc_buffered_char[0] = (0x2a | (code & SCAN_RELEASE));
sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX_SHIFT);
} else {
sc->sc_buffered_char[0] = (code & ~SCAN_PREFIX);
sc->sc_buffered_char[1] = 0;
}
return ((code & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
}
return (code);
}
#endif /* UKBD_EMULATE_ATSCANCODE */
static keyboard_switch_t ukbdsw = {
.probe = &ukbd__probe,
.init = &ukbd_init,
.term = &ukbd_term,
.intr = &ukbd_intr,
.test_if = &ukbd_test_if,
.enable = &ukbd_enable,
.disable = &ukbd_disable,
.read = &ukbd_read,
.check = &ukbd_check,
.read_char = &ukbd_read_char,
.check_char = &ukbd_check_char,
.ioctl = &ukbd_ioctl,
.lock = &ukbd_lock,
.clear_state = &ukbd_clear_state,
.get_state = &ukbd_get_state,
.set_state = &ukbd_set_state,
- .get_fkeystr = &genkbd_get_fkeystr,
.poll = &ukbd_poll,
- .diag = &genkbd_diag,
};
KEYBOARD_DRIVER(ukbd, ukbdsw, ukbd_configure);
static int
ukbd_driver_load(module_t mod, int what, void *arg)
{
switch (what) {
case MOD_LOAD:
kbd_add_driver(&ukbd_kbd_driver);
break;
case MOD_UNLOAD:
kbd_delete_driver(&ukbd_kbd_driver);
break;
}
return (0);
}
static devclass_t ukbd_devclass;
static device_method_t ukbd_methods[] = {
DEVMETHOD(device_probe, ukbd_probe),
DEVMETHOD(device_attach, ukbd_attach),
DEVMETHOD(device_detach, ukbd_detach),
DEVMETHOD(device_resume, ukbd_resume),
DEVMETHOD_END
};
static driver_t ukbd_driver = {
.name = "ukbd",
.methods = ukbd_methods,
.size = sizeof(struct ukbd_softc),
};
DRIVER_MODULE(ukbd, uhub, ukbd_driver, ukbd_devclass, ukbd_driver_load, 0);
MODULE_DEPEND(ukbd, usb, 1, 1, 1);
#ifdef EVDEV_SUPPORT
MODULE_DEPEND(ukbd, evdev, 1, 1, 1);
#endif
MODULE_VERSION(ukbd, 1);
USB_PNP_HOST_INFO(ukbd_devs);
Index: stable/11/sys/dev/vkbd/vkbd.c
===================================================================
--- stable/11/sys/dev/vkbd/vkbd.c (revision 356012)
+++ stable/11/sys/dev/vkbd/vkbd.c (revision 356013)
@@ -1,1380 +1,1378 @@
/*
* vkbd.c
*/
/*-
* Copyright (c) 2004 Maksim Yevmenkin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: vkbd.c,v 1.20 2004/11/15 23:53:30 max Exp $
* $FreeBSD$
*/
#include "opt_compat.h"
#include "opt_kbd.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "vkbdctl"
#define KEYBOARD_NAME "vkbd"
MALLOC_DECLARE(M_VKBD);
MALLOC_DEFINE(M_VKBD, KEYBOARD_NAME, "Virtual AT keyboard");
/*****************************************************************************
*****************************************************************************
** Keyboard state
*****************************************************************************
*****************************************************************************/
/*
* XXX
* For now rely on Giant mutex to protect our data structures.
* Just like the rest of keyboard drivers and syscons(4) do.
*/
#if 0 /* not yet */
#define VKBD_LOCK_DECL struct mtx ks_lock
#define VKBD_LOCK_INIT(s) mtx_init(&(s)->ks_lock, "vkbd_lock", NULL, MTX_DEF|MTX_RECURSE)
#define VKBD_LOCK_DESTROY(s) mtx_destroy(&(s)->ks_lock)
#define VKBD_LOCK(s) mtx_lock(&(s)->ks_lock)
#define VKBD_UNLOCK(s) mtx_unlock(&(s)->ks_lock)
#define VKBD_LOCK_ASSERT(s, w) mtx_assert(&(s)->ks_lock, w)
#define VKBD_SLEEP(s, f, d, t) \
msleep(&(s)->f, &(s)->ks_lock, PCATCH | (PZERO + 1), d, t)
#else
#define VKBD_LOCK_DECL
#define VKBD_LOCK_INIT(s)
#define VKBD_LOCK_DESTROY(s)
#define VKBD_LOCK(s)
#define VKBD_UNLOCK(s)
#define VKBD_LOCK_ASSERT(s, w)
#define VKBD_SLEEP(s, f, d, t) tsleep(&(s)->f, PCATCH | (PZERO + 1), d, t)
#endif
#define VKBD_KEYBOARD(d) \
kbd_get_keyboard(kbd_find_keyboard(KEYBOARD_NAME, dev2unit(d)))
/* vkbd queue */
struct vkbd_queue
{
int q[VKBD_Q_SIZE]; /* queue */
int head; /* index of the first code */
int tail; /* index of the last code */
int cc; /* number of codes in queue */
};
typedef struct vkbd_queue vkbd_queue_t;
/* vkbd state */
struct vkbd_state
{
struct cdev *ks_dev; /* control device */
struct selinfo ks_rsel; /* select(2) */
struct selinfo ks_wsel;
vkbd_queue_t ks_inq; /* input key codes queue */
struct task ks_task; /* interrupt task */
int ks_flags; /* flags */
#define OPEN (1 << 0) /* control device is open */
#define COMPOSE (1 << 1) /* compose flag */
#define STATUS (1 << 2) /* status has changed */
#define TASK (1 << 3) /* interrupt task queued */
#define READ (1 << 4) /* read pending */
#define WRITE (1 << 5) /* write pending */
int ks_mode; /* K_XLATE, K_RAW, K_CODE */
int ks_polling; /* polling flag */
int ks_state; /* shift/lock key state */
int ks_accents; /* accent key index (> 0) */
u_int ks_composed_char; /* composed char code */
u_char ks_prefix; /* AT scan code prefix */
VKBD_LOCK_DECL;
};
typedef struct vkbd_state vkbd_state_t;
/*****************************************************************************
*****************************************************************************
** Character device
*****************************************************************************
*****************************************************************************/
static void vkbd_dev_clone(void *, struct ucred *, char *, int,
struct cdev **);
static d_open_t vkbd_dev_open;
static d_close_t vkbd_dev_close;
static d_read_t vkbd_dev_read;
static d_write_t vkbd_dev_write;
static d_ioctl_t vkbd_dev_ioctl;
static d_poll_t vkbd_dev_poll;
static void vkbd_dev_intr(void *, int);
static void vkbd_status_changed(vkbd_state_t *);
static int vkbd_data_ready(vkbd_state_t *);
static int vkbd_data_read(vkbd_state_t *, int);
static struct cdevsw vkbd_dev_cdevsw = {
.d_version = D_VERSION,
.d_flags = D_NEEDGIANT | D_NEEDMINOR,
.d_open = vkbd_dev_open,
.d_close = vkbd_dev_close,
.d_read = vkbd_dev_read,
.d_write = vkbd_dev_write,
.d_ioctl = vkbd_dev_ioctl,
.d_poll = vkbd_dev_poll,
.d_name = DEVICE_NAME,
};
static struct clonedevs *vkbd_dev_clones = NULL;
/* Clone device */
static void
vkbd_dev_clone(void *arg, struct ucred *cred, char *name, int namelen,
struct cdev **dev)
{
int unit;
if (*dev != NULL)
return;
if (strcmp(name, DEVICE_NAME) == 0)
unit = -1;
else if (dev_stdclone(name, NULL, DEVICE_NAME, &unit) != 1)
return; /* don't recognize the name */
/* find any existing device, or allocate new unit number */
if (clone_create(&vkbd_dev_clones, &vkbd_dev_cdevsw, &unit, dev, 0))
*dev = make_dev_credf(MAKEDEV_REF, &vkbd_dev_cdevsw, unit,
cred, UID_ROOT, GID_WHEEL, 0600, DEVICE_NAME "%d",
unit);
}
/* Open device */
static int
vkbd_dev_open(struct cdev *dev, int flag, int mode, struct thread *td)
{
int unit = dev2unit(dev), error;
keyboard_switch_t *sw = NULL;
keyboard_t *kbd = NULL;
vkbd_state_t *state = (vkbd_state_t *) dev->si_drv1;
/* XXX FIXME: dev->si_drv1 locking */
if (state == NULL) {
if ((sw = kbd_get_switch(KEYBOARD_NAME)) == NULL)
return (ENXIO);
if ((error = (*sw->probe)(unit, NULL, 0)) != 0 ||
(error = (*sw->init)(unit, &kbd, NULL, 0)) != 0)
return (error);
state = (vkbd_state_t *) kbd->kb_data;
if ((error = (*sw->enable)(kbd)) != 0) {
(*sw->term)(kbd);
return (error);
}
#ifdef KBD_INSTALL_CDEV
if ((error = kbd_attach(kbd)) != 0) {
(*sw->disable)(kbd);
(*sw->term)(kbd);
return (error);
}
#endif /* def KBD_INSTALL_CDEV */
dev->si_drv1 = kbd->kb_data;
}
VKBD_LOCK(state);
if (state->ks_flags & OPEN) {
VKBD_UNLOCK(state);
return (EBUSY);
}
state->ks_flags |= OPEN;
state->ks_dev = dev;
VKBD_UNLOCK(state);
return (0);
}
/* Close device */
static int
vkbd_dev_close(struct cdev *dev, int foo, int bar, struct thread *td)
{
keyboard_t *kbd = VKBD_KEYBOARD(dev);
vkbd_state_t *state = NULL;
if (kbd == NULL)
return (ENXIO);
if (kbd->kb_data == NULL || kbd->kb_data != dev->si_drv1)
panic("%s: kbd->kb_data != dev->si_drv1\n", __func__);
state = (vkbd_state_t *) kbd->kb_data;
VKBD_LOCK(state);
/* wait for interrupt task */
while (state->ks_flags & TASK)
VKBD_SLEEP(state, ks_task, "vkbdc", 0);
/* wakeup poll()ers */
selwakeuppri(&state->ks_rsel, PZERO + 1);
selwakeuppri(&state->ks_wsel, PZERO + 1);
state->ks_flags &= ~OPEN;
state->ks_dev = NULL;
state->ks_inq.head = state->ks_inq.tail = state->ks_inq.cc = 0;
VKBD_UNLOCK(state);
kbdd_disable(kbd);
#ifdef KBD_INSTALL_CDEV
kbd_detach(kbd);
#endif /* def KBD_INSTALL_CDEV */
kbdd_term(kbd);
/* XXX FIXME: dev->si_drv1 locking */
dev->si_drv1 = NULL;
return (0);
}
/* Read status */
static int
vkbd_dev_read(struct cdev *dev, struct uio *uio, int flag)
{
keyboard_t *kbd = VKBD_KEYBOARD(dev);
vkbd_state_t *state = NULL;
vkbd_status_t status;
int error;
if (kbd == NULL)
return (ENXIO);
if (uio->uio_resid != sizeof(status))
return (EINVAL);
if (kbd->kb_data == NULL || kbd->kb_data != dev->si_drv1)
panic("%s: kbd->kb_data != dev->si_drv1\n", __func__);
state = (vkbd_state_t *) kbd->kb_data;
VKBD_LOCK(state);
if (state->ks_flags & READ) {
VKBD_UNLOCK(state);
return (EALREADY);
}
state->ks_flags |= READ;
again:
if (state->ks_flags & STATUS) {
state->ks_flags &= ~STATUS;
status.mode = state->ks_mode;
status.leds = KBD_LED_VAL(kbd);
status.lock = state->ks_state & LOCK_MASK;
status.delay = kbd->kb_delay1;
status.rate = kbd->kb_delay2;
bzero(status.reserved, sizeof(status.reserved));
error = uiomove(&status, sizeof(status), uio);
} else {
if (flag & O_NONBLOCK) {
error = EWOULDBLOCK;
goto done;
}
error = VKBD_SLEEP(state, ks_flags, "vkbdr", 0);
if (error != 0)
goto done;
goto again;
}
done:
state->ks_flags &= ~READ;
VKBD_UNLOCK(state);
return (error);
}
/* Write scancodes */
static int
vkbd_dev_write(struct cdev *dev, struct uio *uio, int flag)
{
keyboard_t *kbd = VKBD_KEYBOARD(dev);
vkbd_state_t *state = NULL;
vkbd_queue_t *q = NULL;
int error, avail, bytes;
if (kbd == NULL)
return (ENXIO);
if (uio->uio_resid <= 0)
return (EINVAL);
if (kbd->kb_data == NULL || kbd->kb_data != dev->si_drv1)
panic("%s: kbd->kb_data != dev->si_drv1\n", __func__);
state = (vkbd_state_t *) kbd->kb_data;
VKBD_LOCK(state);
if (state->ks_flags & WRITE) {
VKBD_UNLOCK(state);
return (EALREADY);
}
state->ks_flags |= WRITE;
error = 0;
q = &state->ks_inq;
while (uio->uio_resid >= sizeof(q->q[0])) {
if (q->head == q->tail) {
if (q->cc == 0)
avail = nitems(q->q) - q->head;
else
avail = 0; /* queue must be full */
} else if (q->head < q->tail)
avail = nitems(q->q) - q->tail;
else
avail = q->head - q->tail;
if (avail == 0) {
if (flag & O_NONBLOCK) {
error = EWOULDBLOCK;
break;
}
error = VKBD_SLEEP(state, ks_inq, "vkbdw", 0);
if (error != 0)
break;
} else {
bytes = avail * sizeof(q->q[0]);
if (bytes > uio->uio_resid) {
avail = uio->uio_resid / sizeof(q->q[0]);
bytes = avail * sizeof(q->q[0]);
}
error = uiomove((void *) &q->q[q->tail], bytes, uio);
if (error != 0)
break;
q->cc += avail;
q->tail += avail;
if (q->tail == nitems(q->q))
q->tail = 0;
/* queue interrupt task if needed */
if (!(state->ks_flags & TASK) &&
taskqueue_enqueue(taskqueue_swi_giant, &state->ks_task) == 0)
state->ks_flags |= TASK;
}
}
state->ks_flags &= ~WRITE;
VKBD_UNLOCK(state);
return (error);
}
/* Process ioctl */
static int
vkbd_dev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
{
keyboard_t *kbd = VKBD_KEYBOARD(dev);
return ((kbd == NULL)? ENXIO : kbdd_ioctl(kbd, cmd, data));
}
/* Poll device */
static int
vkbd_dev_poll(struct cdev *dev, int events, struct thread *td)
{
vkbd_state_t *state = (vkbd_state_t *) dev->si_drv1;
vkbd_queue_t *q = NULL;
int revents = 0;
if (state == NULL)
return (ENXIO);
VKBD_LOCK(state);
q = &state->ks_inq;
if (events & (POLLIN | POLLRDNORM)) {
if (state->ks_flags & STATUS)
revents |= events & (POLLIN | POLLRDNORM);
else
selrecord(td, &state->ks_rsel);
}
if (events & (POLLOUT | POLLWRNORM)) {
if (q->cc < nitems(q->q))
revents |= events & (POLLOUT | POLLWRNORM);
else
selrecord(td, &state->ks_wsel);
}
VKBD_UNLOCK(state);
return (revents);
}
/* Interrupt handler */
void
vkbd_dev_intr(void *xkbd, int pending)
{
keyboard_t *kbd = (keyboard_t *) xkbd;
vkbd_state_t *state = (vkbd_state_t *) kbd->kb_data;
kbdd_intr(kbd, NULL);
VKBD_LOCK(state);
state->ks_flags &= ~TASK;
wakeup(&state->ks_task);
VKBD_UNLOCK(state);
}
/* Set status change flags */
static void
vkbd_status_changed(vkbd_state_t *state)
{
VKBD_LOCK_ASSERT(state, MA_OWNED);
if (!(state->ks_flags & STATUS)) {
state->ks_flags |= STATUS;
selwakeuppri(&state->ks_rsel, PZERO + 1);
wakeup(&state->ks_flags);
}
}
/* Check if we have data in the input queue */
static int
vkbd_data_ready(vkbd_state_t *state)
{
VKBD_LOCK_ASSERT(state, MA_OWNED);
return (state->ks_inq.cc > 0);
}
/* Read one code from the input queue */
static int
vkbd_data_read(vkbd_state_t *state, int wait)
{
vkbd_queue_t *q = &state->ks_inq;
int c;
VKBD_LOCK_ASSERT(state, MA_OWNED);
if (q->cc == 0)
return (-1);
/* get first code from the queue */
q->cc --;
c = q->q[q->head ++];
if (q->head == nitems(q->q))
q->head = 0;
/* wakeup ks_inq writers/poll()ers */
selwakeuppri(&state->ks_wsel, PZERO + 1);
wakeup(q);
return (c);
}
/****************************************************************************
****************************************************************************
** Keyboard driver
****************************************************************************
****************************************************************************/
static int vkbd_configure(int flags);
static kbd_probe_t vkbd_probe;
static kbd_init_t vkbd_init;
static kbd_term_t vkbd_term;
static kbd_intr_t vkbd_intr;
static kbd_test_if_t vkbd_test_if;
static kbd_enable_t vkbd_enable;
static kbd_disable_t vkbd_disable;
static kbd_read_t vkbd_read;
static kbd_check_t vkbd_check;
static kbd_read_char_t vkbd_read_char;
static kbd_check_char_t vkbd_check_char;
static kbd_ioctl_t vkbd_ioctl;
static kbd_lock_t vkbd_lock;
static void vkbd_clear_state_locked(vkbd_state_t *state);
static kbd_clear_state_t vkbd_clear_state;
static kbd_get_state_t vkbd_get_state;
static kbd_set_state_t vkbd_set_state;
static kbd_poll_mode_t vkbd_poll;
static keyboard_switch_t vkbdsw = {
.probe = vkbd_probe,
.init = vkbd_init,
.term = vkbd_term,
.intr = vkbd_intr,
.test_if = vkbd_test_if,
.enable = vkbd_enable,
.disable = vkbd_disable,
.read = vkbd_read,
.check = vkbd_check,
.read_char = vkbd_read_char,
.check_char = vkbd_check_char,
.ioctl = vkbd_ioctl,
.lock = vkbd_lock,
.clear_state = vkbd_clear_state,
.get_state = vkbd_get_state,
.set_state = vkbd_set_state,
- .get_fkeystr = genkbd_get_fkeystr,
.poll = vkbd_poll,
- .diag = genkbd_diag,
};
static int typematic(int delay, int rate);
static int typematic_delay(int delay);
static int typematic_rate(int rate);
/* Return the number of found keyboards */
static int
vkbd_configure(int flags)
{
return (1);
}
/* Detect a keyboard */
static int
vkbd_probe(int unit, void *arg, int flags)
{
return (0);
}
/* Reset and initialize the keyboard (stolen from atkbd.c) */
static int
vkbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
{
keyboard_t *kbd = NULL;
vkbd_state_t *state = NULL;
keymap_t *keymap = NULL;
accentmap_t *accmap = NULL;
fkeytab_t *fkeymap = NULL;
int fkeymap_size, delay[2];
int error, needfree;
if (*kbdp == NULL) {
*kbdp = kbd = malloc(sizeof(*kbd), M_VKBD, M_NOWAIT | M_ZERO);
state = malloc(sizeof(*state), M_VKBD, M_NOWAIT | M_ZERO);
keymap = malloc(sizeof(key_map), M_VKBD, M_NOWAIT);
accmap = malloc(sizeof(accent_map), M_VKBD, M_NOWAIT);
fkeymap = malloc(sizeof(fkey_tab), M_VKBD, M_NOWAIT);
fkeymap_size = sizeof(fkey_tab)/sizeof(fkey_tab[0]);
needfree = 1;
if ((kbd == NULL) || (state == NULL) || (keymap == NULL) ||
(accmap == NULL) || (fkeymap == NULL)) {
error = ENOMEM;
goto bad;
}
VKBD_LOCK_INIT(state);
state->ks_inq.head = state->ks_inq.tail = state->ks_inq.cc = 0;
TASK_INIT(&state->ks_task, 0, vkbd_dev_intr, (void *) kbd);
} else if (KBD_IS_INITIALIZED(*kbdp) && KBD_IS_CONFIGURED(*kbdp)) {
return (0);
} else {
kbd = *kbdp;
state = (vkbd_state_t *) kbd->kb_data;
keymap = kbd->kb_keymap;
accmap = kbd->kb_accentmap;
fkeymap = kbd->kb_fkeytab;
fkeymap_size = kbd->kb_fkeytab_size;
needfree = 0;
}
if (!KBD_IS_PROBED(kbd)) {
kbd_init_struct(kbd, KEYBOARD_NAME, KB_OTHER, unit, flags, 0, 0);
bcopy(&key_map, keymap, sizeof(key_map));
bcopy(&accent_map, accmap, sizeof(accent_map));
bcopy(fkey_tab, fkeymap,
imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab)));
kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size);
kbd->kb_data = (void *)state;
KBD_FOUND_DEVICE(kbd);
KBD_PROBE_DONE(kbd);
VKBD_LOCK(state);
vkbd_clear_state_locked(state);
state->ks_mode = K_XLATE;
/* FIXME: set the initial value for lock keys in ks_state */
VKBD_UNLOCK(state);
}
if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) {
kbd->kb_config = flags & ~KB_CONF_PROBE_ONLY;
vkbd_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state);
delay[0] = kbd->kb_delay1;
delay[1] = kbd->kb_delay2;
vkbd_ioctl(kbd, KDSETREPEAT, (caddr_t)delay);
KBD_INIT_DONE(kbd);
}
if (!KBD_IS_CONFIGURED(kbd)) {
if (kbd_register(kbd) < 0) {
error = ENXIO;
goto bad;
}
KBD_CONFIG_DONE(kbd);
}
return (0);
bad:
if (needfree) {
if (state != NULL)
free(state, M_VKBD);
if (keymap != NULL)
free(keymap, M_VKBD);
if (accmap != NULL)
free(accmap, M_VKBD);
if (fkeymap != NULL)
free(fkeymap, M_VKBD);
if (kbd != NULL) {
free(kbd, M_VKBD);
*kbdp = NULL; /* insure ref doesn't leak to caller */
}
}
return (error);
}
/* Finish using this keyboard */
static int
vkbd_term(keyboard_t *kbd)
{
vkbd_state_t *state = (vkbd_state_t *) kbd->kb_data;
kbd_unregister(kbd);
VKBD_LOCK_DESTROY(state);
bzero(state, sizeof(*state));
free(state, M_VKBD);
free(kbd->kb_keymap, M_VKBD);
free(kbd->kb_accentmap, M_VKBD);
free(kbd->kb_fkeytab, M_VKBD);
free(kbd, M_VKBD);
return (0);
}
/* Keyboard interrupt routine */
static int
vkbd_intr(keyboard_t *kbd, void *arg)
{
int c;
if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) {
/* let the callback function to process the input */
(*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT,
kbd->kb_callback.kc_arg);
} else {
/* read and discard the input; no one is waiting for input */
do {
c = vkbd_read_char(kbd, FALSE);
} while (c != NOKEY);
}
return (0);
}
/* Test the interface to the device */
static int
vkbd_test_if(keyboard_t *kbd)
{
return (0);
}
/*
* Enable the access to the device; until this function is called,
* the client cannot read from the keyboard.
*/
static int
vkbd_enable(keyboard_t *kbd)
{
KBD_ACTIVATE(kbd);
return (0);
}
/* Disallow the access to the device */
static int
vkbd_disable(keyboard_t *kbd)
{
KBD_DEACTIVATE(kbd);
return (0);
}
/* Read one byte from the keyboard if it's allowed */
static int
vkbd_read(keyboard_t *kbd, int wait)
{
vkbd_state_t *state = (vkbd_state_t *) kbd->kb_data;
int c;
VKBD_LOCK(state);
c = vkbd_data_read(state, wait);
VKBD_UNLOCK(state);
if (c != -1)
kbd->kb_count ++;
return (KBD_IS_ACTIVE(kbd)? c : -1);
}
/* Check if data is waiting */
static int
vkbd_check(keyboard_t *kbd)
{
vkbd_state_t *state = NULL;
int ready;
if (!KBD_IS_ACTIVE(kbd))
return (FALSE);
state = (vkbd_state_t *) kbd->kb_data;
VKBD_LOCK(state);
ready = vkbd_data_ready(state);
VKBD_UNLOCK(state);
return (ready);
}
/* Read char from the keyboard (stolen from atkbd.c) */
static u_int
vkbd_read_char(keyboard_t *kbd, int wait)
{
vkbd_state_t *state = (vkbd_state_t *) kbd->kb_data;
u_int action;
int scancode, keycode;
VKBD_LOCK(state);
next_code:
/* do we have a composed char to return? */
if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) {
action = state->ks_composed_char;
state->ks_composed_char = 0;
if (action > UCHAR_MAX) {
VKBD_UNLOCK(state);
return (ERRKEY);
}
VKBD_UNLOCK(state);
return (action);
}
/* see if there is something in the keyboard port */
scancode = vkbd_data_read(state, wait);
if (scancode == -1) {
VKBD_UNLOCK(state);
return (NOKEY);
}
/* XXX FIXME: check for -1 if wait == 1! */
kbd->kb_count ++;
/* return the byte as is for the K_RAW mode */
if (state->ks_mode == K_RAW) {
VKBD_UNLOCK(state);
return (scancode);
}
/* translate the scan code into a keycode */
keycode = scancode & 0x7F;
switch (state->ks_prefix) {
case 0x00: /* normal scancode */
switch(scancode) {
case 0xB8: /* left alt (compose key) released */
if (state->ks_flags & COMPOSE) {
state->ks_flags &= ~COMPOSE;
if (state->ks_composed_char > UCHAR_MAX)
state->ks_composed_char = 0;
}
break;
case 0x38: /* left alt (compose key) pressed */
if (!(state->ks_flags & COMPOSE)) {
state->ks_flags |= COMPOSE;
state->ks_composed_char = 0;
}
break;
case 0xE0:
case 0xE1:
state->ks_prefix = scancode;
goto next_code;
}
break;
case 0xE0: /* 0xE0 prefix */
state->ks_prefix = 0;
switch (keycode) {
case 0x1C: /* right enter key */
keycode = 0x59;
break;
case 0x1D: /* right ctrl key */
keycode = 0x5A;
break;
case 0x35: /* keypad divide key */
keycode = 0x5B;
break;
case 0x37: /* print scrn key */
keycode = 0x5C;
break;
case 0x38: /* right alt key (alt gr) */
keycode = 0x5D;
break;
case 0x46: /* ctrl-pause/break on AT 101 (see below) */
keycode = 0x68;
break;
case 0x47: /* grey home key */
keycode = 0x5E;
break;
case 0x48: /* grey up arrow key */
keycode = 0x5F;
break;
case 0x49: /* grey page up key */
keycode = 0x60;
break;
case 0x4B: /* grey left arrow key */
keycode = 0x61;
break;
case 0x4D: /* grey right arrow key */
keycode = 0x62;
break;
case 0x4F: /* grey end key */
keycode = 0x63;
break;
case 0x50: /* grey down arrow key */
keycode = 0x64;
break;
case 0x51: /* grey page down key */
keycode = 0x65;
break;
case 0x52: /* grey insert key */
keycode = 0x66;
break;
case 0x53: /* grey delete key */
keycode = 0x67;
break;
/* the following 3 are only used on the MS "Natural" keyboard */
case 0x5b: /* left Window key */
keycode = 0x69;
break;
case 0x5c: /* right Window key */
keycode = 0x6a;
break;
case 0x5d: /* menu key */
keycode = 0x6b;
break;
case 0x5e: /* power key */
keycode = 0x6d;
break;
case 0x5f: /* sleep key */
keycode = 0x6e;
break;
case 0x63: /* wake key */
keycode = 0x6f;
break;
default: /* ignore everything else */
goto next_code;
}
break;
case 0xE1: /* 0xE1 prefix */
/*
* The pause/break key on the 101 keyboard produces:
* E1-1D-45 E1-9D-C5
* Ctrl-pause/break produces:
* E0-46 E0-C6 (See above.)
*/
state->ks_prefix = 0;
if (keycode == 0x1D)
state->ks_prefix = 0x1D;
goto next_code;
/* NOT REACHED */
case 0x1D: /* pause / break */
state->ks_prefix = 0;
if (keycode != 0x45)
goto next_code;
keycode = 0x68;
break;
}
if (kbd->kb_type == KB_84) {
switch (keycode) {
case 0x37: /* *(numpad)/print screen */
if (state->ks_flags & SHIFTS)
keycode = 0x5c; /* print screen */
break;
case 0x45: /* num lock/pause */
if (state->ks_flags & CTLS)
keycode = 0x68; /* pause */
break;
case 0x46: /* scroll lock/break */
if (state->ks_flags & CTLS)
keycode = 0x6c; /* break */
break;
}
} else if (kbd->kb_type == KB_101) {
switch (keycode) {
case 0x5c: /* print screen */
if (state->ks_flags & ALTS)
keycode = 0x54; /* sysrq */
break;
case 0x68: /* pause/break */
if (state->ks_flags & CTLS)
keycode = 0x6c; /* break */
break;
}
}
/* return the key code in the K_CODE mode */
if (state->ks_mode == K_CODE) {
VKBD_UNLOCK(state);
return (keycode | (scancode & 0x80));
}
/* compose a character code */
if (state->ks_flags & COMPOSE) {
switch (keycode | (scancode & 0x80)) {
/* key pressed, process it */
case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */
state->ks_composed_char *= 10;
state->ks_composed_char += keycode - 0x40;
if (state->ks_composed_char > UCHAR_MAX) {
VKBD_UNLOCK(state);
return (ERRKEY);
}
goto next_code;
case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */
state->ks_composed_char *= 10;
state->ks_composed_char += keycode - 0x47;
if (state->ks_composed_char > UCHAR_MAX) {
VKBD_UNLOCK(state);
return (ERRKEY);
}
goto next_code;
case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */
state->ks_composed_char *= 10;
state->ks_composed_char += keycode - 0x4E;
if (state->ks_composed_char > UCHAR_MAX) {
VKBD_UNLOCK(state);
return (ERRKEY);
}
goto next_code;
case 0x52: /* keypad 0 */
state->ks_composed_char *= 10;
if (state->ks_composed_char > UCHAR_MAX) {
VKBD_UNLOCK(state);
return (ERRKEY);
}
goto next_code;
/* key released, no interest here */
case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */
case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */
case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */
case 0xD2: /* keypad 0 */
goto next_code;
case 0x38: /* left alt key */
break;
default:
if (state->ks_composed_char > 0) {
state->ks_flags &= ~COMPOSE;
state->ks_composed_char = 0;
VKBD_UNLOCK(state);
return (ERRKEY);
}
break;
}
}
/* keycode to key action */
action = genkbd_keyaction(kbd, keycode, scancode & 0x80,
&state->ks_state, &state->ks_accents);
if (action == NOKEY)
goto next_code;
VKBD_UNLOCK(state);
return (action);
}
/* Check if char is waiting */
static int
vkbd_check_char(keyboard_t *kbd)
{
vkbd_state_t *state = NULL;
int ready;
if (!KBD_IS_ACTIVE(kbd))
return (FALSE);
state = (vkbd_state_t *) kbd->kb_data;
VKBD_LOCK(state);
if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0))
ready = TRUE;
else
ready = vkbd_data_ready(state);
VKBD_UNLOCK(state);
return (ready);
}
/* Some useful control functions (stolen from atkbd.c) */
static int
vkbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
vkbd_state_t *state = (vkbd_state_t *) kbd->kb_data;
int i;
#ifdef COMPAT_FREEBSD6
int ival;
#endif
VKBD_LOCK(state);
switch (cmd) {
case KDGKBMODE: /* get keyboard mode */
*(int *)arg = state->ks_mode;
break;
#ifdef COMPAT_FREEBSD6
case _IO('K', 7):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBMODE: /* set keyboard mode */
switch (*(int *)arg) {
case K_XLATE:
if (state->ks_mode != K_XLATE) {
/* make lock key state and LED state match */
state->ks_state &= ~LOCK_MASK;
state->ks_state |= KBD_LED_VAL(kbd);
vkbd_status_changed(state);
}
/* FALLTHROUGH */
case K_RAW:
case K_CODE:
if (state->ks_mode != *(int *)arg) {
vkbd_clear_state_locked(state);
state->ks_mode = *(int *)arg;
vkbd_status_changed(state);
}
break;
default:
VKBD_UNLOCK(state);
return (EINVAL);
}
break;
case KDGETLED: /* get keyboard LED */
*(int *)arg = KBD_LED_VAL(kbd);
break;
#ifdef COMPAT_FREEBSD6
case _IO('K', 66):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETLED: /* set keyboard LED */
/* NOTE: lock key state in ks_state won't be changed */
if (*(int *)arg & ~LOCK_MASK) {
VKBD_UNLOCK(state);
return (EINVAL);
}
i = *(int *)arg;
/* replace CAPS LED with ALTGR LED for ALTGR keyboards */
if (state->ks_mode == K_XLATE &&
kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
if (i & ALKED)
i |= CLKED;
else
i &= ~CLKED;
}
KBD_LED_VAL(kbd) = *(int *)arg;
vkbd_status_changed(state);
break;
case KDGKBSTATE: /* get lock key state */
*(int *)arg = state->ks_state & LOCK_MASK;
break;
#ifdef COMPAT_FREEBSD6
case _IO('K', 20):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBSTATE: /* set lock key state */
if (*(int *)arg & ~LOCK_MASK) {
VKBD_UNLOCK(state);
return (EINVAL);
}
state->ks_state &= ~LOCK_MASK;
state->ks_state |= *(int *)arg;
vkbd_status_changed(state);
VKBD_UNLOCK(state);
/* set LEDs and quit */
return (vkbd_ioctl(kbd, KDSETLED, arg));
case KDSETREPEAT: /* set keyboard repeat rate (new interface) */
i = typematic(((int *)arg)[0], ((int *)arg)[1]);
kbd->kb_delay1 = typematic_delay(i);
kbd->kb_delay2 = typematic_rate(i);
vkbd_status_changed(state);
break;
#ifdef COMPAT_FREEBSD6
case _IO('K', 67):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETRAD: /* set keyboard repeat rate (old interface) */
kbd->kb_delay1 = typematic_delay(*(int *)arg);
kbd->kb_delay2 = typematic_rate(*(int *)arg);
vkbd_status_changed(state);
break;
case PIO_KEYMAP: /* set keyboard translation table */
case OPIO_KEYMAP: /* set keyboard translation table (compat) */
case PIO_KEYMAPENT: /* set keyboard translation table entry */
case PIO_DEADKEYMAP: /* set accent key translation table */
state->ks_accents = 0;
/* FALLTHROUGH */
default:
VKBD_UNLOCK(state);
return (genkbd_commonioctl(kbd, cmd, arg));
}
VKBD_UNLOCK(state);
return (0);
}
/* Lock the access to the keyboard */
static int
vkbd_lock(keyboard_t *kbd, int lock)
{
return (1); /* XXX */
}
/* Clear the internal state of the keyboard */
static void
vkbd_clear_state_locked(vkbd_state_t *state)
{
VKBD_LOCK_ASSERT(state, MA_OWNED);
state->ks_flags &= ~COMPOSE;
state->ks_polling = 0;
state->ks_state &= LOCK_MASK; /* preserve locking key state */
state->ks_accents = 0;
state->ks_composed_char = 0;
/* state->ks_prefix = 0; XXX */
/* flush ks_inq and wakeup writers/poll()ers */
state->ks_inq.head = state->ks_inq.tail = state->ks_inq.cc = 0;
selwakeuppri(&state->ks_wsel, PZERO + 1);
wakeup(&state->ks_inq);
}
static void
vkbd_clear_state(keyboard_t *kbd)
{
vkbd_state_t *state = (vkbd_state_t *) kbd->kb_data;
VKBD_LOCK(state);
vkbd_clear_state_locked(state);
VKBD_UNLOCK(state);
}
/* Save the internal state */
static int
vkbd_get_state(keyboard_t *kbd, void *buf, size_t len)
{
if (len == 0)
return (sizeof(vkbd_state_t));
if (len < sizeof(vkbd_state_t))
return (-1);
bcopy(kbd->kb_data, buf, sizeof(vkbd_state_t)); /* XXX locking? */
return (0);
}
/* Set the internal state */
static int
vkbd_set_state(keyboard_t *kbd, void *buf, size_t len)
{
if (len < sizeof(vkbd_state_t))
return (ENOMEM);
bcopy(buf, kbd->kb_data, sizeof(vkbd_state_t)); /* XXX locking? */
return (0);
}
/* Set polling */
static int
vkbd_poll(keyboard_t *kbd, int on)
{
vkbd_state_t *state = NULL;
state = (vkbd_state_t *) kbd->kb_data;
VKBD_LOCK(state);
if (on)
state->ks_polling ++;
else
state->ks_polling --;
VKBD_UNLOCK(state);
return (0);
}
/*
* Local functions
*/
static int delays[] = { 250, 500, 750, 1000 };
static int rates[] = { 34, 38, 42, 46, 50, 55, 59, 63,
68, 76, 84, 92, 100, 110, 118, 126,
136, 152, 168, 184, 200, 220, 236, 252,
272, 304, 336, 368, 400, 440, 472, 504 };
static int
typematic_delay(int i)
{
return (delays[(i >> 5) & 3]);
}
static int
typematic_rate(int i)
{
return (rates[i & 0x1f]);
}
static int
typematic(int delay, int rate)
{
int value;
int i;
for (i = nitems(delays) - 1; i > 0; i --) {
if (delay >= delays[i])
break;
}
value = i << 5;
for (i = nitems(rates) - 1; i > 0; i --) {
if (rate >= rates[i])
break;
}
value |= i;
return (value);
}
/*****************************************************************************
*****************************************************************************
** Module
*****************************************************************************
*****************************************************************************/
KEYBOARD_DRIVER(vkbd, vkbdsw, vkbd_configure);
static int
vkbd_modevent(module_t mod, int type, void *data)
{
static eventhandler_tag tag;
switch (type) {
case MOD_LOAD:
clone_setup(&vkbd_dev_clones);
tag = EVENTHANDLER_REGISTER(dev_clone, vkbd_dev_clone, 0, 1000);
if (tag == NULL) {
clone_cleanup(&vkbd_dev_clones);
return (ENOMEM);
}
kbd_add_driver(&vkbd_kbd_driver);
break;
case MOD_UNLOAD:
kbd_delete_driver(&vkbd_kbd_driver);
EVENTHANDLER_DEREGISTER(dev_clone, tag);
clone_cleanup(&vkbd_dev_clones);
break;
default:
return (EOPNOTSUPP);
}
return (0);
}
DEV_MODULE(vkbd, vkbd_modevent, NULL);
Index: stable/11
===================================================================
--- stable/11 (revision 356012)
+++ stable/11 (revision 356013)
Property changes on: stable/11
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
Merged /head:r355796-355797,355799
Index: stable/12/sys/arm/samsung/exynos/chrome_kb.c
===================================================================
--- stable/12/sys/arm/samsung/exynos/chrome_kb.c (revision 356012)
+++ stable/12/sys/arm/samsung/exynos/chrome_kb.c (revision 356013)
@@ -1,921 +1,919 @@
/*-
* Copyright (c) 2014 Ruslan Bukin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Samsung Chromebook Keyboard
*/
#include
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include