f20058955c
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
893 lines
20 KiB
C
893 lines
20 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* 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 <sys/cdefs.h>
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/module.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/kbio.h>
|
|
#include <sys/condvar.h>
|
|
#include <sys/callout.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <machine/bus.h>
|
|
|
|
#include "opt_kbd.h"
|
|
#include <dev/kbd/kbdreg.h>
|
|
#include <dev/kbd/kbdtables.h>
|
|
#include <dev/ofw/openfirm.h>
|
|
#include <dev/ofw/ofw_bus.h>
|
|
|
|
#include <vm/vm.h>
|
|
#include <vm/pmap.h>
|
|
|
|
#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),
|
|
};
|
|
|
|
DRIVER_MODULE(akbd, adb, adb_kbd_driver, 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,
|
|
.poll = akbd_poll,
|
|
};
|
|
|
|
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 | CTLFLAG_NEEDGIANT, 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 PIO_KEYMAPENT:
|
|
case PIO_DEADKEYMAP:
|
|
#ifdef COMPAT_FREEBSD13
|
|
case OPIO_KEYMAP:
|
|
case OPIO_DEADKEYMAP:
|
|
#endif /* COMPAT_FREEBSD13 */
|
|
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);
|