freebsd-dev/sys/dev/gpio/gpiokeys.c
Stefan Eßer f20058955c sys/kbio.h: make pre-unicode keymap support optional
FreeBSD-9 had introduced support for the full set of Unicode
characters to the parsing and processing of keymap character tables.

This support has been extended to cover the table for accented
characters that are reached via dead key combinations in FreeBSD-13.2.

New ioctls have been introduced to support both the pre-Unicode and
the Unicode formats and keyboard drivers have been extended to support
those ioctls.

This commit makes the ABI compatibility functions in the kernel
optional and dependent on COMPAT_FREEBSD13 in -CURRENT.

The kbdcontrol command in -CURRENT and 13-STABLE (before 13.2) has
been made ABI compatible with old kernels to allow a new world to be
run on an old kernel (that does not have full Unicode support for
keymaps).

This commit is not to merged back to 12-STABLE or 13-STABLE. It is
part of review D38465, which has been split into 3 separate commits
due to different MFC and life-time requirements of either commit.

Approved by:	imp
Differential Revision:	https://reviews.freebsd.org/D38465
2023-02-14 14:03:28 +01:00

1065 lines
24 KiB
C

/*-
* Copyright (c) 2015-2016 Oleksandr Tymoshenko <gonzo@freebsd.org>
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_platform.h"
#include "opt_kbd.h"
#include "opt_evdev.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/gpio.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/kdb.h>
#include <sys/ioccom.h>
#include <sys/filio.h>
#include <sys/kbio.h>
#include <dev/kbd/kbdreg.h>
#include <dev/kbd/kbdtables.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/gpio/gpiobusvar.h>
#include <dev/gpio/gpiokeys.h>
#ifdef EVDEV_SUPPORT
#include <dev/evdev/evdev.h>
#include <dev/evdev/input.h>
#endif
#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;
#ifdef EVDEV_SUPPORT
uint32_t evcode;
#endif
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;
#ifdef EVDEV_SUPPORT
struct evdev_dev *sc_evdev;
#endif
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, struct gpiokey *key, int pressed)
{
uint32_t code;
GPIOKEYS_LOCK(sc);
#ifdef EVDEV_SUPPORT
if (key->evcode != GPIOKEY_NONE &&
(evdev_rcpt_mask & EVDEV_RCPT_HW_KBD) != 0) {
evdev_push_key(sc->sc_evdev, key->evcode, pressed);
evdev_sync(sc->sc_evdev);
}
if (evdev_is_grabbed(sc->sc_evdev)) {
GPIOKEYS_UNLOCK(sc);
return;
}
#endif
if (key->keycode != GPIOKEY_NONE) {
code = key->keycode & SCAN_KEYCODE_MASK;
if (!pressed)
code |= KEY_RELEASE;
if (key->keycode & SCAN_PREFIX_E0)
gpiokeys_put_key(sc, 0xe0);
else if (key->keycode & SCAN_PREFIX_E1)
gpiokeys_put_key(sc, 0xe1);
gpiokeys_put_key(sc, code);
}
GPIOKEYS_UNLOCK(sc);
if (key->keycode != GPIOKEY_NONE)
gpiokeys_event_keyinput(sc);
}
static void
gpiokey_autorepeat(void *arg)
{
struct gpiokey *key;
key = arg;
gpiokeys_key_event(key->parent_sc, key, 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;
gpio_pin_is_active(key->pin, &active);
if (active) {
gpiokeys_key_event(key->parent_sc, key, 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, 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", (void **)&name) == -1)
OF_getprop_alloc(node, "name", (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);
#ifdef EVDEV_SUPPORT
key->evcode = code;
evdev_support_key(sc->sc_evdev, code);
#endif
}
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)
{
struct gpiokeys_softc *sc;
keyboard_t *kbd;
#ifdef EVDEV_SUPPORT
char *name;
#endif
phandle_t keys, child;
int total_keys;
int unit;
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);
}
#ifdef EVDEV_SUPPORT
sc->sc_evdev = evdev_alloc();
evdev_set_name(sc->sc_evdev, device_get_desc(dev));
OF_getprop_alloc(keys, "name", (void **)&name);
evdev_set_phys(sc->sc_evdev, name != NULL ? name : "unknown");
OF_prop_free(name);
evdev_set_id(sc->sc_evdev, BUS_VIRTUAL, 0, 0, 0);
evdev_support_event(sc->sc_evdev, EV_SYN);
evdev_support_event(sc->sc_evdev, EV_KEY);
#endif
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++;
}
}
#ifdef EVDEV_SUPPORT
if (evdev_register_mtx(sc->sc_evdev, &sc->sc_mtx) != 0) {
device_printf(dev, "failed to register evdev device\n");
goto detach;
}
#endif
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);
#ifdef EVDEV_SUPPORT
evdev_free(sc->sc_evdev);
#endif
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 PIO_KEYMAPENT: /* set keyboard translation table
* entry */
case PIO_DEADKEYMAP: /* set accent key translation table */
#ifdef COMPAT_FREEBSD13
case OPIO_KEYMAP: /* set keyboard translation table
* (compat) */
case OPIO_DEADKEYMAP: /* set accent key translation table
* (compat) */
#endif /* COMPAT_FREEBSD13 */
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,
.poll = &gpiokeys_poll,
};
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 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_driver_load, NULL);
MODULE_VERSION(gpiokeys, 1);