kbtrans_streams.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Generic keyboard support: streams and administration.
*/
#define KEYMAP_SIZE_VARIABLE
#include <sys/types.h>
#include <sys/cred.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/strsun.h>
#include <sys/ddi.h>
#include <sys/vuid_event.h>
#include <sys/modctl.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/cmn_err.h>
#include <sys/kbd.h>
#include <sys/kbio.h>
#include <sys/consdev.h>
#include <sys/kbtrans.h>
#include <sys/policy.h>
#include "kbtrans_lower.h"
#include "kbtrans_streams.h"
#ifdef DEBUG
int kbtrans_errmask;
int kbtrans_errlevel;
#endif
/*
* Repeat rates set in static variables so they can be tweeked with
* debugger.
*/
static int kbtrans_repeat_rate;
static int kbtrans_repeat_delay;
/* Printing message on q overflow */
static int kbtrans_overflow_msg = 1;
/*
* This value corresponds approximately to max 10 fingers
*/
static int kbtrans_downs_size = 15;
/*
* modload support
*/
extern struct mod_ops mod_miscops;
static struct modlmisc modlmisc = {
&mod_miscops, /* Type of module */
"kbtrans (key translation) 1.32"
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modlmisc, NULL
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
/*
* Internal Function Prototypes
*/
static char *kbtrans_strsetwithdecimal(char *, uint_t, uint_t);
static void kbtrans_set_translation_callback(struct kbtrans *);
static void kbtrans_reioctl(void *);
static void kbtrans_send_esc_event(char, struct kbtrans *);
static void kbtrans_keypressed(struct kbtrans *, uchar_t, Firm_event *,
ushort_t);
static void kbtrans_putbuf(char *, queue_t *);
static void kbtrans_cancelrpt(struct kbtrans *);
static void kbtrans_queuepress(struct kbtrans *, uchar_t, Firm_event *);
static void kbtrans_putcode(register struct kbtrans *, uint_t);
static void kbtrans_keyreleased(struct kbtrans *, uchar_t);
static void kbtrans_queueevent(struct kbtrans *, Firm_event *);
static void kbtrans_untrans_keypressed_raw(struct kbtrans *, kbtrans_key_t);
static void kbtrans_untrans_keyreleased_raw(struct kbtrans *,
kbtrans_key_t);
static void kbtrans_ascii_keypressed(struct kbtrans *, uint_t,
kbtrans_key_t, uint_t);
static void kbtrans_ascii_keyreleased(struct kbtrans *, kbtrans_key_t);
static void kbtrans_ascii_setup_repeat(struct kbtrans *, uint_t,
kbtrans_key_t);
static void kbtrans_trans_event_keypressed(struct kbtrans *, uint_t,
kbtrans_key_t, uint_t);
static void kbtrans_trans_event_keyreleased(struct kbtrans *,
kbtrans_key_t);
static void kbtrans_trans_event_setup_repeat(struct kbtrans *, uint_t,
kbtrans_key_t);
static void kbtrans_rpt(void *);
static void kbtrans_setled(struct kbtrans *);
static void kbtrans_flush(struct kbtrans *);
static enum kbtrans_message_response kbtrans_ioctl(struct kbtrans *upper,
mblk_t *mp);
static int kbtrans_setkey(struct kbtrans_lower *, struct kiockey *,
cred_t *);
static int kbtrans_getkey(struct kbtrans_lower *, struct kiockey *);
static int kbtrans_skey(struct kbtrans_lower *, struct kiockeymap *,
cred_t *cr);
static int kbtrans_gkey(struct kbtrans_lower *, struct kiockeymap *);
/*
* Keyboard Translation Mode (TR_NONE)
*
* Functions to be called when keyboard translation is turned off
* and up/down key codes are reported.
*/
struct keyboard_callback untrans_event_callback = {
kbtrans_untrans_keypressed_raw,
kbtrans_untrans_keyreleased_raw,
NULL,
NULL,
NULL,
NULL,
NULL,
};
/*
* Keyboard Translation Mode (TR_ASCII)
*
* Functions to be called when ISO 8859/1 codes are reported
*/
struct keyboard_callback ascii_callback = {
NULL,
NULL,
kbtrans_ascii_keypressed,
kbtrans_ascii_keyreleased,
kbtrans_ascii_setup_repeat,
kbtrans_cancelrpt,
kbtrans_setled,
};
/*
* Keyboard Translation Mode (TR_EVENT)
*
* Functions to be called when firm_events are reported.
*/
struct keyboard_callback trans_event_callback = {
NULL,
NULL,
kbtrans_trans_event_keypressed,
kbtrans_trans_event_keyreleased,
kbtrans_trans_event_setup_repeat,
kbtrans_cancelrpt,
kbtrans_setled,
};
/*
* kbtrans_streams_init:
* Initialize the stream, keytables, callbacks, etc.
*/
int
kbtrans_streams_init(
queue_t *q,
int sflag,
cred_t *crp,
struct kbtrans_hardware *hw,
struct kbtrans_callbacks *hw_cb,
struct kbtrans **ret_kbd,
int initial_leds,
int initial_led_mask)
{
struct kbtrans *upper;
struct kbtrans_lower *lower;
int err;
/*
* Default to relatively generic tables.
*/
extern signed char kb_compose_map[];
extern struct compose_sequence_t kb_compose_table[];
extern struct fltaccent_sequence_t kb_fltaccent_table[];
extern char keystringtab[][KTAB_STRLEN];
extern unsigned char kb_numlock_table[];
/* Set these up only once so that they could be changed from adb */
if (!kbtrans_repeat_rate) {
kbtrans_repeat_rate = (hz+29)/30;
kbtrans_repeat_delay = hz/2;
}
/*
* Only allow open requests to succeed for privileged users. This
* necessary to prevent users from pushing the this module again
* on the stream associated with /dev/kbd.
*/
err = secpolicy_console(crp);
if (err != 0) {
return (err);
}
switch (sflag) {
case MODOPEN:
break;
case CLONEOPEN:
DPRINTF(PRINT_L1, PRINT_MASK_OPEN, (NULL,
"kbtrans_streams_init: Clone open not supported"));
return (EINVAL);
}
/* allocate keyboard state structure */
upper = kmem_zalloc(sizeof (struct kbtrans), KM_SLEEP);
*ret_kbd = upper;
upper->kbtrans_polled_buf[0] = '\0';
upper->kbtrans_polled_pending_chars = upper->kbtrans_polled_buf;
upper->kbtrans_streams_hw = hw;
upper->kbtrans_streams_hw_callbacks = hw_cb;
upper->kbtrans_streams_readq = q;
upper->kbtrans_streams_iocpending = NULL;
upper->kbtrans_streams_translatable = TR_CAN;
upper->kbtrans_overflow_cnt = 0;
upper->kbtrans_streams_translate_mode = TR_ASCII;
/* Set the translation callback based on the translation type */
kbtrans_set_translation_callback(upper);
lower = &upper->kbtrans_lower;
/*
* Set defaults for relatively generic tables.
*/
lower->kbtrans_compose_map = kb_compose_map;
lower->kbtrans_compose_table = kb_compose_table;
lower->kbtrans_fltaccent_table = kb_fltaccent_table;
lower->kbtrans_numlock_table = kb_numlock_table;
lower->kbtrans_keystringtab = keystringtab;
lower->kbtrans_upper = upper;
lower->kbtrans_compat = 1;
/*
* We have a generic default for the LED state, and let the
* hardware-specific driver supply overrides.
*/
lower->kbtrans_led_state = 0;
lower->kbtrans_led_state &= ~initial_led_mask;
lower->kbtrans_led_state |= initial_leds;
lower->kbtrans_togglemask = 0;
if (lower->kbtrans_led_state & LED_CAPS_LOCK)
lower->kbtrans_togglemask |= CAPSMASK;
if (lower->kbtrans_led_state & LED_NUM_LOCK)
lower->kbtrans_togglemask |= NUMLOCKMASK;
#if defined(SCROLLMASK)
if (lower->kbtrans_led_state & LED_SCROLL_LOCK)
lower->kbtrans_togglemask |= SCROLLMASK;
#endif
lower->kbtrans_shiftmask = lower->kbtrans_togglemask;
upper->kbtrans_streams_vuid_addr.ascii = ASCII_FIRST;
upper->kbtrans_streams_vuid_addr.top = TOP_FIRST;
upper->kbtrans_streams_vuid_addr.vkey = VKEY_FIRST;
/* Allocate dynamic memory for downs table */
upper->kbtrans_streams_num_downs_entries = kbtrans_downs_size;
upper->kbtrans_streams_downs_bytes =
(uint32_t)(kbtrans_downs_size * sizeof (Key_event));
upper->kbtrans_streams_downs =
kmem_zalloc(upper->kbtrans_streams_downs_bytes, KM_SLEEP);
upper->kbtrans_streams_abortable = B_FALSE;
upper->kbtrans_streams_flags = KBTRANS_STREAMS_OPEN;
DPRINTF(PRINT_L1, PRINT_MASK_OPEN, (upper, "kbtrans_streams_init "
"exiting"));
return (0);
}
/*
* kbtrans_streams_fini:
* Free structures and uninitialize the stream
*/
int
kbtrans_streams_fini(struct kbtrans *upper)
{
/*
* Since we're about to destroy our private data, turn off
* our open flag first, so we don't accept any more input
* and try to use that data.
*/
upper->kbtrans_streams_flags = 0;
/* clear all timeouts */
if (upper->kbtrans_streams_bufcallid) {
qunbufcall(upper->kbtrans_streams_readq,
upper->kbtrans_streams_bufcallid);
}
if (upper->kbtrans_streams_rptid) {
(void) quntimeout(upper->kbtrans_streams_readq,
upper->kbtrans_streams_rptid);
}
kmem_free(upper->kbtrans_streams_downs,
upper->kbtrans_streams_downs_bytes);
kmem_free(upper, sizeof (struct kbtrans));
DPRINTF(PRINT_L1, PRINT_MASK_CLOSE, (upper, "kbtrans_streams_fini "
"exiting"));
return (0);
}
/*
* kbtrans_streams_releaseall :
* This function releases all the held keys.
*/
void
kbtrans_streams_releaseall(struct kbtrans *upper)
{
register struct key_event *ke;
register int i;
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (NULL, "USBKBM RELEASE ALL\n"));
/* Scan table of down key stations */
for (i = 0, ke = upper->kbtrans_streams_downs;
i < upper->kbtrans_streams_num_downs_entries; i++, ke++) {
/* Key station not zero */
if (ke->key_station) {
kbtrans_keyreleased(upper, ke->key_station);
/* kbtrans_keyreleased resets downs entry */
}
}
}
/*
* kbtrans_streams_message:
* keyboard module output queue put procedure: handles M_IOCTL
* messages.
*
* Return KBTRANS_MESSAGE_HANDLED if the message was handled by
* kbtrans and KBTRANS_MESSAGE_NOT_HANDLED otherwise. If
* KBTRANS_MESSAGE_HANDLED is returned, no further action is required.
* If KBTRANS_MESSAGE_NOT_HANDLED is returned, the hardware module
* is responsible for any action.
*/
enum kbtrans_message_response
kbtrans_streams_message(struct kbtrans *upper, register mblk_t *mp)
{
queue_t *q = upper->kbtrans_streams_readq;
enum kbtrans_message_response ret;
DPRINTF(PRINT_L1, PRINT_MASK_ALL, (upper,
"kbtrans_streams_message entering"));
/*
* Process M_FLUSH, and some M_IOCTL, messages here; pass
* everything else down.
*/
switch (mp->b_datap->db_type) {
case M_IOCTL:
ret = kbtrans_ioctl(upper, mp);
break;
case M_FLUSH:
if (*mp->b_rptr & FLUSHW)
flushq(q, FLUSHDATA);
if (*mp->b_rptr & FLUSHR)
flushq(RD(q), FLUSHDATA);
/*
* White lie: we say we didn't handle the message,
* so that it gets handled by our client.
*/
ret = KBTRANS_MESSAGE_NOT_HANDLED;
break;
default:
ret = KBTRANS_MESSAGE_NOT_HANDLED;
break;
}
DPRINTF(PRINT_L1, PRINT_MASK_ALL, (upper,
"kbtrans_streams_message exiting\n"));
return (ret);
}
/*
* kbtrans_streams_key:
* When a key is pressed or released, the hardware module should
* call kbtrans, passing the key number and its new
* state. kbtrans is responsible for autorepeat handling;
* the hardware module should report only actual press/release
* events, suppressing any hardware-generated autorepeat.
*/
void
kbtrans_streams_key(
struct kbtrans *upper,
kbtrans_key_t key,
enum keystate state)
{
struct kbtrans_lower *lower;
struct keyboard *kp;
lower = &upper->kbtrans_lower;
kp = lower->kbtrans_keyboard;
if (upper->kbtrans_streams_abortable) {
switch (upper->kbtrans_streams_abort_state) {
case ABORT_NORMAL:
if (state != KEY_PRESSED)
break;
if (key == (kbtrans_key_t)kp->k_abort1 ||
key == (kbtrans_key_t)kp->k_abort1a) {
upper->kbtrans_streams_abort_state =
ABORT_ABORT1_RECEIVED;
upper->kbtrans_streams_abort1_key = key;
return;
}
break;
case ABORT_ABORT1_RECEIVED:
upper->kbtrans_streams_abort_state = ABORT_NORMAL;
if (state == KEY_PRESSED &&
key == (kbtrans_key_t)kp->k_abort2) {
abort_sequence_enter((char *)NULL);
return;
} else {
kbtrans_processkey(lower,
upper->kbtrans_streams_callback,
upper->kbtrans_streams_abort1_key,
KEY_PRESSED);
}
}
}
kbtrans_processkey(lower, upper->kbtrans_streams_callback, key, state);
}
/*
* kbtrans_streams_set_keyboard:
* At any time after calling kbtrans_streams_init, the hardware
* module should make this call to report the id of the keyboard
* attached. id is the keyboard type, typically KB_SUN4,
* KB_PC, or KB_USB.
*/
void
kbtrans_streams_set_keyboard(
struct kbtrans *upper,
int id,
struct keyboard *k)
{
upper->kbtrans_lower.kbtrans_keyboard = k;
upper->kbtrans_streams_id = id;
}
/*
* kbtrans_streams_has_reset:
* At any time between kbtrans_streams_init and kbtrans_streams_fini,
* the hardware module can call this routine to report that the
* keyboard has been reset, e.g. by being unplugged and reattached.
*/
/*ARGSUSED*/
void
kbtrans_streams_has_reset(struct kbtrans *upper)
{
/*
* If this routine is implemented it should probably (a)
* simulate releases of all pressed keys and (b) call
* the hardware module to set the LEDs.
*/
}
/*
* kbtrans_streams_enable:
* This is the routine that is called back when the the stream is ready
* to take messages.
*/
void
kbtrans_streams_enable(struct kbtrans *upper)
{
/* Set the LED's */
kbtrans_setled(upper);
}
/*
* kbtrans_streams_set_queue:
* Set the overlying queue, to support multiplexors.
*/
void
kbtrans_streams_set_queue(struct kbtrans *upper, queue_t *q)
{
upper->kbtrans_streams_readq = q;
}
/*
* kbtrans_streams_get_queue:
* Return the overlying queue.
*/
queue_t *
kbtrans_streams_get_queue(struct kbtrans *upper)
{
return (upper->kbtrans_streams_readq);
}
/*
* kbtrans_streams_untimeout
* Cancell all timeout
*/
void
kbtrans_streams_untimeout(struct kbtrans *upper)
{
/* clear all timeouts */
if (upper->kbtrans_streams_bufcallid) {
qunbufcall(upper->kbtrans_streams_readq,
upper->kbtrans_streams_bufcallid);
upper->kbtrans_streams_bufcallid = 0;
}
if (upper->kbtrans_streams_rptid) {
(void) quntimeout(upper->kbtrans_streams_readq,
upper->kbtrans_streams_rptid);
upper->kbtrans_streams_rptid = 0;
}
}
/*
* kbtrans_reioctl:
* This function is set up as call-back function should an ioctl fail
* to allocate required resources.
*/
static void
kbtrans_reioctl(void *arg)
{
struct kbtrans *upper = (struct kbtrans *)arg;
mblk_t *mp;
upper->kbtrans_streams_bufcallid = 0;
if ((mp = upper->kbtrans_streams_iocpending) != NULL) {
/* not pending any more */
upper->kbtrans_streams_iocpending = NULL;
(void) kbtrans_ioctl(upper, mp);
}
}
/*
* kbtrans_ioctl:
* process ioctls we recognize and own. Otherwise, pass it down.
*/
static enum kbtrans_message_response
kbtrans_ioctl(struct kbtrans *upper, register mblk_t *mp)
{
register struct iocblk *iocp;
register short new_translate;
register Vuid_addr_probe *addr_probe;
register short *addr_ptr;
size_t ioctlrespsize;
int err = 0;
struct kbtrans_lower *lower;
mblk_t *datap;
int translate;
static int kiocgetkey, kiocsetkey;
lower = &upper->kbtrans_lower;
iocp = (struct iocblk *)mp->b_rptr;
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper,
"kbtrans_ioctl: ioc_cmd 0x%x - ", iocp->ioc_cmd));
switch (iocp->ioc_cmd) {
case VUIDSFORMAT:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "VUIDSFORMAT\n"));
err = miocpullup(mp, sizeof (int));
if (err != 0)
break;
new_translate = (*(int *)mp->b_cont->b_rptr == VUID_NATIVE) ?
TR_ASCII : TR_EVENT;
if (new_translate == upper->kbtrans_streams_translate_mode)
break;
upper->kbtrans_streams_translate_mode = new_translate;
kbtrans_set_translation_callback(upper);
kbtrans_flush(upper);
break;
case KIOCTRANS:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCTRANS\n"));
err = miocpullup(mp, sizeof (int));
if (err != 0)
break;
new_translate = *(int *)mp->b_cont->b_rptr;
if (new_translate == upper->kbtrans_streams_translate_mode)
break;
upper->kbtrans_streams_translate_mode = new_translate;
kbtrans_set_translation_callback(upper);
kbtrans_flush(upper);
break;
case KIOCSLED:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCSLED\n"));
err = miocpullup(mp, sizeof (uchar_t));
if (err != 0)
break;
lower->kbtrans_led_state = *(uchar_t *)mp->b_cont->b_rptr;
kbtrans_setled(upper);
break;
case KIOCGLED:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCGLED\n"));
if ((datap = allocb(sizeof (uchar_t), BPRI_HI)) == NULL) {
ioctlrespsize = sizeof (int);
goto allocfailure;
}
*(uchar_t *)datap->b_wptr = lower->kbtrans_led_state;
datap->b_wptr += sizeof (uchar_t);
if (mp->b_cont)
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (uchar_t);
break;
case VUIDGFORMAT:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "VUIDGFORMAT\n"));
if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
ioctlrespsize = sizeof (int);
goto allocfailure;
}
*(int *)datap->b_wptr =
(upper->kbtrans_streams_translate_mode == TR_EVENT ||
upper->kbtrans_streams_translate_mode == TR_UNTRANS_EVENT) ?
VUID_FIRM_EVENT: VUID_NATIVE;
datap->b_wptr += sizeof (int);
if (mp->b_cont) /* free msg to prevent memory leak */
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
break;
case KIOCGTRANS:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCGTRANS\n"));
if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
ioctlrespsize = sizeof (int);
goto allocfailure;
}
*(int *)datap->b_wptr = upper->kbtrans_streams_translate_mode;
datap->b_wptr += sizeof (int);
if (mp->b_cont) /* free msg to prevent memory leak */
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
break;
case VUIDSADDR:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "VUIDSADDR\n"));
err = miocpullup(mp, sizeof (Vuid_addr_probe));
if (err != 0)
break;
addr_probe = (Vuid_addr_probe *)mp->b_cont->b_rptr;
switch (addr_probe->base) {
case ASCII_FIRST:
addr_ptr = &upper->kbtrans_streams_vuid_addr.ascii;
break;
case TOP_FIRST:
addr_ptr = &upper->kbtrans_streams_vuid_addr.top;
break;
case VKEY_FIRST:
addr_ptr = &upper->kbtrans_streams_vuid_addr.vkey;
break;
default:
err = ENODEV;
}
if ((err == 0) && (*addr_ptr != addr_probe->data.next)) {
*addr_ptr = addr_probe->data.next;
kbtrans_flush(upper);
}
break;
case VUIDGADDR:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "VUIDGADDR\n"));
err = miocpullup(mp, sizeof (Vuid_addr_probe));
if (err != 0)
break;
addr_probe = (Vuid_addr_probe *)mp->b_cont->b_rptr;
switch (addr_probe->base) {
case ASCII_FIRST:
addr_probe->data.current =
upper->kbtrans_streams_vuid_addr.ascii;
break;
case TOP_FIRST:
addr_probe->data.current =
upper->kbtrans_streams_vuid_addr.top;
break;
case VKEY_FIRST:
addr_probe->data.current =
upper->kbtrans_streams_vuid_addr.vkey;
break;
default:
err = ENODEV;
}
break;
case KIOCTRANSABLE:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCTRANSABLE\n"));
err = miocpullup(mp, sizeof (int));
if (err != 0)
break;
/*
* called during console setup in kbconfig()
* If set to false, means we are a serial keyboard,
* and we should pass all data up without modification.
*/
translate = *(int *)mp->b_cont->b_rptr;
if (upper->kbtrans_streams_translatable != translate)
upper->kbtrans_streams_translatable = translate;
if (translate != TR_CAN)
DPRINTF(PRINT_L4, PRINT_MASK_ALL, (upper,
"Cannot translate keyboard using tables.\n"));
break;
case KIOCGTRANSABLE:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCGTRANSABLE\n"));
if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
ioctlrespsize = sizeof (int);
goto allocfailure;
}
*(int *)datap->b_wptr = upper->kbtrans_streams_translatable;
datap->b_wptr += sizeof (int);
if (mp->b_cont) /* free msg to prevent memory leak */
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
break;
case KIOCSCOMPAT:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCSCOMPAT\n"));
err = miocpullup(mp, sizeof (int));
if (err != 0)
break;
lower->kbtrans_compat = *(int *)mp->b_cont->b_rptr;
break;
case KIOCGCOMPAT:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCGCOMPAT\n"));
if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
ioctlrespsize = sizeof (int);
goto allocfailure;
}
*(int *)datap->b_wptr = lower->kbtrans_compat;
datap->b_wptr += sizeof (int);
if (mp->b_cont) /* free msg to prevent memory leak */
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
break;
case KIOCSETKEY:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCSETKEY %d\n",
kiocsetkey++));
err = miocpullup(mp, sizeof (struct kiockey));
if (err != 0)
break;
err = kbtrans_setkey(&upper->kbtrans_lower,
(struct kiockey *)mp->b_cont->b_rptr, iocp->ioc_cr);
/*
* Since this only affects any subsequent key presses,
* don't flush soft state. One might want to
* toggle the keytable entries dynamically.
*/
break;
case KIOCGETKEY:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCGETKEY %d\n",
kiocgetkey++));
err = miocpullup(mp, sizeof (struct kiockey));
if (err != 0)
break;
err = kbtrans_getkey(&upper->kbtrans_lower,
(struct kiockey *)mp->b_cont->b_rptr);
break;
case KIOCSKEY:
err = miocpullup(mp, sizeof (struct kiockeymap));
if (err != 0)
break;
err = kbtrans_skey(&upper->kbtrans_lower,
(struct kiockeymap *)mp->b_cont->b_rptr, iocp->ioc_cr);
/*
* Since this only affects any subsequent key presses,
* don't flush soft state. One might want to
* toggle the keytable entries dynamically.
*/
break;
case KIOCGKEY:
err = miocpullup(mp, sizeof (struct kiockeymap));
if (err != 0)
break;
err = kbtrans_gkey(&upper->kbtrans_lower,
(struct kiockeymap *)mp->b_cont->b_rptr);
break;
case KIOCSDIRECT:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCSDIRECT\n"));
kbtrans_flush(upper);
break;
case KIOCGDIRECT:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCSGDIRECT\n"));
if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
ioctlrespsize = sizeof (int);
goto allocfailure;
}
*(int *)datap->b_wptr = 1; /* always direct */
datap->b_wptr += sizeof (int);
if (mp->b_cont) /* free msg to prevent memory leak */
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
break;
case KIOCTYPE:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCTYPE\n"));
if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
ioctlrespsize = sizeof (int);
goto allocfailure;
}
*(int *)datap->b_wptr = upper->kbtrans_streams_id;
datap->b_wptr += sizeof (int);
if (mp->b_cont) /* free msg to prevent memory leak */
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
break;
case CONSSETABORTENABLE:
/*
* Peek as it goes by; must be a TRANSPARENT ioctl.
*/
if (iocp->ioc_count != TRANSPARENT) {
err = EINVAL;
break;
}
upper->kbtrans_streams_abortable =
(boolean_t)*(intptr_t *)mp->b_cont->b_rptr;
/*
* Let the hardware module see it too.
*/
return (KBTRANS_MESSAGE_NOT_HANDLED);
case KIOCGRPTDELAY:
/*
* Report the autorepeat delay, unit in millisecond
*/
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCGRPTDELAY\n"));
if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
ioctlrespsize = sizeof (int);
goto allocfailure;
}
*(int *)datap->b_wptr = TICK_TO_MSEC(kbtrans_repeat_delay);
datap->b_wptr += sizeof (int);
/* free msg to prevent memory leak */
if (mp->b_cont != NULL)
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
break;
case KIOCSRPTDELAY:
/*
* Set the autorepeat delay
*/
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCSRPTDELAY\n"));
err = miocpullup(mp, sizeof (int));
if (err != 0)
break;
/* validate the input */
if (*(int *)mp->b_cont->b_rptr < KIOCRPTDELAY_MIN) {
err = EINVAL;
break;
}
kbtrans_repeat_delay = MSEC_TO_TICK(*(int *)mp->b_cont->b_rptr);
if (kbtrans_repeat_delay <= 0)
kbtrans_repeat_delay = 1;
break;
case KIOCGRPTRATE:
/*
* Report the autorepeat rate
*/
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCGRPTRATE\n"));
if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
ioctlrespsize = sizeof (int);
goto allocfailure;
}
*(int *)datap->b_wptr = TICK_TO_MSEC(kbtrans_repeat_rate);
datap->b_wptr += sizeof (int);
/* free msg to prevent memory leak */
if (mp->b_cont != NULL)
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
break;
case KIOCSRPTRATE:
/*
* Set the autorepeat rate
*/
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCSRPTRATE\n"));
err = miocpullup(mp, sizeof (int));
if (err != 0)
break;
/* validate the input */
if (*(int *)mp->b_cont->b_rptr < KIOCRPTRATE_MIN) {
err = EINVAL;
break;
}
kbtrans_repeat_rate = MSEC_TO_TICK(*(int *)mp->b_cont->b_rptr);
if (kbtrans_repeat_rate <= 0)
kbtrans_repeat_rate = 1;
break;
default:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "unknown\n"));
return (KBTRANS_MESSAGE_NOT_HANDLED);
} /* end switch */
if (err != 0) {
iocp->ioc_rval = 0;
iocp->ioc_error = err;
mp->b_datap->db_type = M_IOCNAK;
} else {
iocp->ioc_rval = 0;
iocp->ioc_error = 0; /* brain rot */
mp->b_datap->db_type = M_IOCACK;
}
putnext(upper->kbtrans_streams_readq, mp);
return (KBTRANS_MESSAGE_HANDLED);
allocfailure:
/*
* We needed to allocate something to handle this "ioctl", but
* couldn't; save this "ioctl" and arrange to get called back when
* it's more likely that we can get what we need.
* If there's already one being saved, throw it out, since it
* must have timed out.
*/
if (upper->kbtrans_streams_iocpending != NULL)
freemsg(upper->kbtrans_streams_iocpending);
upper->kbtrans_streams_iocpending = mp;
if (upper->kbtrans_streams_bufcallid) {
qunbufcall(upper->kbtrans_streams_readq,
upper->kbtrans_streams_bufcallid);
}
upper->kbtrans_streams_bufcallid =
qbufcall(upper->kbtrans_streams_readq, ioctlrespsize, BPRI_HI,
kbtrans_reioctl, upper);
/*
* This is a white lie... we *will* handle it, eventually.
*/
return (KBTRANS_MESSAGE_HANDLED);
}
/*
* kbtrans_flush:
* Flush data upstream
*/
static void
kbtrans_flush(register struct kbtrans *upper)
{
register queue_t *q;
/* Flush pending data already sent upstream */
if ((q = upper->kbtrans_streams_readq) != NULL && q->q_next != NULL)
(void) putnextctl1(q, M_FLUSH, FLUSHR);
/* Flush pending ups */
bzero(upper->kbtrans_streams_downs, upper->kbtrans_streams_downs_bytes);
kbtrans_cancelrpt(upper);
}
/*
* kbtrans_setled:
* Update the keyboard LEDs to match the current keyboard state.
*/
static void
kbtrans_setled(struct kbtrans *upper)
{
upper->kbtrans_streams_hw_callbacks->kbtrans_streams_setled(
upper->kbtrans_streams_hw,
upper->kbtrans_lower.kbtrans_led_state);
}
/*
* kbtrans_rpt:
* If a key is held down, this function is set up to be called
* after kbtrans_repeat_rate time elapses.
*/
static void
kbtrans_rpt(void *arg)
{
struct kbtrans *upper = arg;
struct kbtrans_lower *lower = &upper->kbtrans_lower;
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (NULL,
"kbtrans_rpt: repeat key %X\n",
lower->kbtrans_repeatkey));
upper->kbtrans_streams_rptid = 0;
/*
* NB: polled code zaps kbtrans_repeatkey without cancelling
* timeout.
*/
if (lower->kbtrans_repeatkey != 0) {
kbtrans_keyreleased(upper, lower->kbtrans_repeatkey);
kbtrans_processkey(lower,
upper->kbtrans_streams_callback,
lower->kbtrans_repeatkey,
KEY_PRESSED);
upper->kbtrans_streams_rptid =
qtimeout(upper->kbtrans_streams_readq, kbtrans_rpt,
(caddr_t)upper, kbtrans_repeat_rate);
}
}
/*
* kbtrans_cancelrpt:
* Cancel the repeating key
*/
static void
kbtrans_cancelrpt(struct kbtrans *upper)
{
upper->kbtrans_lower.kbtrans_repeatkey = 0;
if (upper->kbtrans_streams_rptid != 0) {
(void) quntimeout(upper->kbtrans_streams_readq,
upper->kbtrans_streams_rptid);
upper->kbtrans_streams_rptid = 0;
}
}
/*
* kbtrans_send_esc_event:
* Send character up stream. Used for the case of
* sending strings upstream.
*/
static void
kbtrans_send_esc_event(char c, register struct kbtrans *upper)
{
Firm_event fe;
fe.id = c;
fe.value = 1;
fe.pair_type = FE_PAIR_NONE;
fe.pair = 0;
/*
* Pretend as if each cp pushed and released
* Calling kbtrans_queueevent avoids addr translation
* and pair base determination of kbtrans_keypressed.
*/
kbtrans_queueevent(upper, &fe);
fe.value = 0;
kbtrans_queueevent(upper, &fe);
}
/*
* kbtrans_strsetwithdecimal:
* Used for expanding a function key to the ascii equivalent
*/
static char *
kbtrans_strsetwithdecimal(char *buf, uint_t val, uint_t maxdigs)
{
int hradix = 5;
char *bp;
int lowbit;
char *tab = "0123456789abcdef";
bp = buf + maxdigs;
*(--bp) = '\0';
while (val) {
lowbit = val & 1;
val = (val >> 1);
*(--bp) = tab[val % hradix * 2 + lowbit];
val /= hradix;
}
return (bp);
}
/*
* kbtrans_keypressed:
* Modify Firm event to be sent up the stream
*/
static void
kbtrans_keypressed(struct kbtrans *upper, uchar_t key_station,
Firm_event *fe, ushort_t base)
{
register short id_addr;
struct kbtrans_lower *lower = &upper->kbtrans_lower;
/* Set pair values */
if (fe->id < (ushort_t)VKEY_FIRST) {
/*
* If CTRLed, find the ID that would have been used had it
* not been CTRLed.
*/
if (lower->kbtrans_shiftmask & (CTRLMASK | CTLSMASK)) {
unsigned short *ke;
unsigned int mask;
mask = lower->kbtrans_shiftmask &
~(CTRLMASK | CTLSMASK | UPMASK);
ke = kbtrans_find_entry(lower, mask, key_station);
if (ke == NULL)
return;
base = *ke;
}
if (base != fe->id) {
fe->pair_type = FE_PAIR_SET;
fe->pair = (uchar_t)base;
goto send;
}
}
fe->pair_type = FE_PAIR_NONE;
fe->pair = 0;
send:
/* Adjust event id address for multiple keyboard/workstation support */
switch (vuid_id_addr(fe->id)) {
case ASCII_FIRST:
id_addr = upper->kbtrans_streams_vuid_addr.ascii;
break;
case TOP_FIRST:
id_addr = upper->kbtrans_streams_vuid_addr.top;
break;
case VKEY_FIRST:
id_addr = upper->kbtrans_streams_vuid_addr.vkey;
break;
default:
id_addr = vuid_id_addr(fe->id);
break;
}
fe->id = vuid_id_offset(fe->id) | id_addr;
kbtrans_queuepress(upper, key_station, fe);
}
/*
* kbtrans_queuepress:
* Add keypress to the "downs" table
*/
static void
kbtrans_queuepress(struct kbtrans *upper,
uchar_t key_station, Firm_event *fe)
{
register struct key_event *ke, *ke_free;
register int i;
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (NULL, "kbtrans_queuepress:"
" key=%d", key_station));
ke_free = 0;
/* Scan table of down key stations */
for (i = 0, ke = upper->kbtrans_streams_downs;
i < upper->kbtrans_streams_num_downs_entries; i++, ke++) {
/* Keycode already down? */
if (ke->key_station == key_station) {
DPRINTF(PRINT_L0, PRINT_MASK_ALL,
(NULL, "kbtrans: Double "
"entry in downs table (%d,%d)!\n",
key_station, i));
goto add_event;
}
if (ke->key_station == 0)
ke_free = ke;
}
if (ke_free) {
ke = ke_free;
goto add_event;
}
ke = upper->kbtrans_streams_downs;
add_event:
ke->key_station = key_station;
ke->event = *fe;
kbtrans_queueevent(upper, fe);
}
/*
* kbtrans_keyreleased:
* Remove entry from the downs table
*/
static void
kbtrans_keyreleased(register struct kbtrans *upper, uchar_t key_station)
{
register struct key_event *ke;
register int i;
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (NULL, "RELEASE key=%d\n",
key_station));
if (upper->kbtrans_streams_translate_mode != TR_EVENT &&
upper->kbtrans_streams_translate_mode != TR_UNTRANS_EVENT) {
return;
}
/* Scan table of down key stations */
for (i = 0, ke = upper->kbtrans_streams_downs;
i < upper->kbtrans_streams_num_downs_entries;
i++, ke++) {
/* Found? */
if (ke->key_station == key_station) {
ke->key_station = 0;
ke->event.value = 0;
kbtrans_queueevent(upper, &ke->event);
}
}
/*
* Ignore if couldn't find because may be called twice
* for the same key station in the case of the kbtrans_rpt
* routine being called unnecessarily.
*/
}
/*
* kbtrans_putcode:
* Pass a keycode up the stream, if you can, otherwise throw it away.
*/
static void
kbtrans_putcode(register struct kbtrans *upper, uint_t code)
{
register mblk_t *bp;
/*
* If we can't send it up, then we just drop it.
*/
if (!canputnext(upper->kbtrans_streams_readq)) {
return;
}
/*
* Allocate a messsage block to send up.
*/
if ((bp = allocb(sizeof (uint_t), BPRI_HI)) == NULL) {
cmn_err(CE_WARN, "kbtrans_putcode: Can't allocate block\
for keycode.");
return;
}
/*
* We will strip out any high order information here.
*/
/* NOTE the implicit cast here */
*bp->b_wptr++ = (uchar_t)code;
/*
* Send the message up.
*/
(void) putnext(upper->kbtrans_streams_readq, bp);
}
/*
* kbtrans_putbuf:
* Pass generated keycode sequence to upstream, if possible.
*/
static void
kbtrans_putbuf(char *buf, queue_t *q)
{
register mblk_t *bp;
if (!canputnext(q)) {
cmn_err(CE_WARN, "kbtrans_putbuf: Can't put block for keycode");
} else {
if ((bp = allocb((int)strlen(buf), BPRI_HI)) == NULL) {
cmn_err(CE_WARN, "kbtrans_putbuf: "
"Can't allocate block for keycode");
} else {
while (*buf) {
*bp->b_wptr++ = *buf;
buf++;
}
putnext(q, bp);
}
}
}
/*
* kbtrans_queueevent:
* Pass a VUID "firm event" up the stream, if you can.
*/
static void
kbtrans_queueevent(struct kbtrans *upper, Firm_event *fe)
{
register queue_t *q;
register mblk_t *bp;
if ((q = upper->kbtrans_streams_readq) == NULL)
return;
if (!canputnext(q)) {
if (kbtrans_overflow_msg) {
DPRINTF(PRINT_L2, PRINT_MASK_ALL, (NULL,
"kbtrans: Buffer flushed when overflowed."));
}
kbtrans_flush(upper);
upper->kbtrans_overflow_cnt++;
} else {
if ((bp = allocb(sizeof (Firm_event), BPRI_HI)) == NULL) {
cmn_err(CE_WARN, "kbtrans_queueevent: Can't allocate \
block for event.");
} else {
uniqtime32(&fe->time);
*(Firm_event *)bp->b_wptr = *fe;
bp->b_wptr += sizeof (Firm_event);
(void) putnext(q, bp);
}
}
}
/*
* kbtrans_set_translation_callback:
* This code sets the translation_callback pointer based on the
* translation mode.
*/
static void
kbtrans_set_translation_callback(register struct kbtrans *upper)
{
switch (upper->kbtrans_streams_translate_mode) {
default:
case TR_ASCII:
upper->kbtrans_streams_callback = &ascii_callback;
break;
case TR_EVENT:
upper->kbtrans_streams_callback = &trans_event_callback;
break;
case TR_UNTRANS_EVENT:
upper->kbtrans_streams_callback = &untrans_event_callback;
break;
}
}
/*
* kbtrans_untrans_keypressed_raw:
* This is the callback we get if we are in TR_UNTRANS_EVENT and a
* key is pressed. This code will just send the scancode up the
* stream.
*/
static void
kbtrans_untrans_keypressed_raw(struct kbtrans *upper, kbtrans_key_t key)
{
Firm_event fe;
bzero(&fe, sizeof (fe));
/*
* fill in the event
*/
fe.id = (unsigned short)key;
fe.value = 1;
/*
* Send the event upstream.
*/
kbtrans_queuepress(upper, key, &fe);
}
/*
* kbtrans_untrans_keyreleased_raw:
* This is the callback we get if we are in TR_UNTRANS_EVENT mode
* and a key is released. This code will just send the scancode up
* the stream.
*/
static void
kbtrans_untrans_keyreleased_raw(struct kbtrans *upper, kbtrans_key_t key)
{
/*
* Deal with a key released event.
*/
kbtrans_keyreleased(upper, key);
}
/*
* kbtrans_ascii_keypressed:
* This is the code if we are in TR_ASCII mode and a key
* is pressed. This is where we will do any special processing that
* is specific to ASCII key translation.
*/
/* ARGSUSED */
static void
kbtrans_ascii_keypressed(
struct kbtrans *upper,
uint_t entrytype,
kbtrans_key_t key,
uint_t entry)
{
register char *cp;
register char *bufp;
char buf[14];
struct kbtrans_lower *lower = &upper->kbtrans_lower;
/*
* Based on the type of key, we may need to do some ASCII
* specific post processing.
*/
switch (entrytype) {
case BUCKYBITS:
case SHIFTKEYS:
case FUNNY:
/*
* There is no ascii equivalent. We will ignore these
* keys
*/
return;
case FUNCKEYS:
/*
* We need to expand this key to get the ascii
* equivalent. These are the function keys (F1, F2 ...)
*/
bufp = buf;
cp = kbtrans_strsetwithdecimal(bufp + 2,
(uint_t)((entry & 0x003F) + 192),
sizeof (buf) - 5);
*bufp++ = '\033'; /* Escape */
*bufp++ = '[';
while (*cp != '\0')
*bufp++ = *cp++;
*bufp++ = 'z';
*bufp = '\0';
/*
* Send the result upstream.
*/
kbtrans_putbuf(buf, upper->kbtrans_streams_readq);
return;
case STRING:
/*
* These are the multi byte keys (Home, Up, Down ...)
*/
cp = &lower->kbtrans_keystringtab[entry & 0x0F][0];
/*
* Copy the string from the keystringtable, and send it
* upstream a character at a time.
*/
while (*cp != '\0') {
kbtrans_putcode(upper, (uchar_t)*cp);
cp++;
}
return;
case PADKEYS:
/*
* These are the keys on the keypad. Look up the
* answer in the kb_numlock_table and send it upstream.
*/
kbtrans_putcode(upper,
lower->kbtrans_numlock_table[entry&0x1F]);
return;
case 0: /* normal character */
default:
break;
}
/*
* Send the byte upstream.
*/
kbtrans_putcode(upper, entry);
}
/*
* kbtrans_ascii_keyreleased:
* This is the function if we are in TR_ASCII mode and a key
* is released. ASCII doesn't have the concept of released keys,
* or make/break codes. So there is nothing for us to do.
*/
/* ARGSUSED */
static void
kbtrans_ascii_keyreleased(struct kbtrans *upper, kbtrans_key_t key)
{
/* Nothing to do ... for now */
}
/*
* kbtrans_ascii_setup_repeat:
* This is the function if we are in TR_ASCII mode and the
* translation module has decided that a key needs to be repeated.
*/
/* ARGSUSED */
static void
kbtrans_ascii_setup_repeat(
struct kbtrans *upper,
uint_t entrytype,
kbtrans_key_t key)
{
struct kbtrans_lower *lower = &upper->kbtrans_lower;
/*
* Cancel any currently repeating keys. This will be a new
* key to repeat.
*/
kbtrans_cancelrpt(upper);
/*
* Set the value of the key to be repeated.
*/
lower->kbtrans_repeatkey = key;
/*
* Start the timeout for repeating this key. kbtrans_rpt will
* be called to repeat the key.
*/
upper->kbtrans_streams_rptid = qtimeout(upper->kbtrans_streams_readq,
kbtrans_rpt, (caddr_t)upper, kbtrans_repeat_delay);
}
/*
* kbtrans_trans_event_keypressed:
* This is the function if we are in TR_EVENT mode and a key
* is pressed. This is where we will do any special processing that
* is specific to EVENT key translation.
*/
static void
kbtrans_trans_event_keypressed(
struct kbtrans *upper,
uint_t entrytype,
kbtrans_key_t key,
uint_t entry)
{
Firm_event fe;
register char *cp;
struct kbtrans_lower *lower = &upper->kbtrans_lower;
/*
* Based on the type of key, we may need to do some EVENT
* specific post processing.
*/
switch (entrytype) {
case SHIFTKEYS:
/*
* Relying on ordinal correspondence between
* vuid_event.h SHIFT_META-SHIFT_TOP &
* kbd.h METABIT-SYSTEMBIT in order to
* correctly translate entry into fe.id.
*/
fe.id = SHIFT_CAPSLOCK + (entry & 0x0F);
fe.value = 1;
kbtrans_keypressed(upper, key, &fe, fe.id);
return;
case BUCKYBITS:
/*
* Relying on ordinal correspondence between
* vuid_event.h SHIFT_CAPSLOCK-SHIFT_RIGHTCTRL &
* kbd.h CAPSLOCK-RIGHTCTRL in order to
* correctly translate entry into fe.id.
*/
fe.id = SHIFT_META + (entry & 0x0F);
fe.value = 1;
kbtrans_keypressed(upper, key, &fe, fe.id);
return;
case FUNCKEYS:
/*
* Take advantage of the similar
* ordering of kbd.h function keys and
* vuid_event.h function keys to do a
* simple translation to achieve a
* mapping between the 2 different
* address spaces.
*/
fe.id = KEY_LEFTFIRST + (entry & 0x003F);
fe.value = 1;
/*
* Assume "up" table only generates
* shift changes.
*/
kbtrans_keypressed(upper, key, &fe, fe.id);
/*
* Function key events can be expanded
* by terminal emulator software to
* produce the standard escape sequence
* generated by the TR_ASCII case above
* if a function key event is not used
* by terminal emulator software
* directly.
*/
return;
case STRING:
/*
* These are the multi byte keys (Home, Up, Down ...)
*/
cp = &lower->kbtrans_keystringtab[entry & 0x0F][0];
/*
* Copy the string from the keystringtable, and send it
* upstream a character at a time.
*/
while (*cp != '\0') {
kbtrans_send_esc_event(*cp, upper);
cp++;
}
return;
case PADKEYS:
/*
* Take advantage of the similar
* ordering of kbd.h keypad keys and
* vuid_event.h keypad keys to do a
* simple translation to achieve a
* mapping between the 2 different
* address spaces.
*/
fe.id = VKEY_FIRSTPAD + (entry & 0x001F);
fe.value = 1;
/*
* Assume "up" table only generates
* shift changes.
*/
kbtrans_keypressed(upper, key, &fe, fe.id);
/*
* Keypad key events can be expanded
* by terminal emulator software to
* produce the standard ascii character
* generated by the TR_ASCII case above
* if a keypad key event is not used
* by terminal emulator software
* directly.
*/
return;
case FUNNY:
/*
* These are not events.
*/
switch (entry) {
case IDLE:
case RESET:
case ERROR:
/*
* Something has happened. Mark all keys as released.
*/
kbtrans_streams_releaseall(upper);
break;
}
return;
case 0: /* normal character */
default:
break;
}
/*
* Send the event upstream.
*/
fe.id = entry;
fe.value = 1;
kbtrans_queueevent(upper, &fe);
}
/*
* kbtrans_trans_event_keyreleased:
* This is the function if we are in TR_EVENT mode and a key
* is released.
*/
/* ARGSUSED */
static void
kbtrans_trans_event_keyreleased(struct kbtrans *upper, kbtrans_key_t key)
{
/*
* Mark the key as released and send an event upstream.
*/
kbtrans_keyreleased(upper, key);
}
/*
* kbtrans_trans_event_setup_repeat:
* This is the function if we are in TR_EVENT mode and the
* translation module has decided that a key needs to be repeated.
* We will set a timeout to retranslate the repeat key.
*/
static void
kbtrans_trans_event_setup_repeat(
struct kbtrans *upper,
uint_t entrytype,
kbtrans_key_t key)
{
struct kbtrans_lower *lower = &upper->kbtrans_lower;
/*
* Function keys and keypad keys do not repeat when we are in
* EVENT mode.
*/
if (entrytype == FUNCKEYS || entrytype == PADKEYS) {
return;
}
/*
* Cancel any currently repeating keys. This will be a new
* key to repeat.
*/
kbtrans_cancelrpt(upper);
/*
* Set the value of the key to be repeated.
*/
lower->kbtrans_repeatkey = key;
/*
* Start the timeout for repeating this key. kbtrans_rpt will
* be called to repeat the key.
*/
upper->kbtrans_streams_rptid = qtimeout(upper->kbtrans_streams_readq,
kbtrans_rpt, (caddr_t)upper, kbtrans_repeat_delay);
}
/*
* Administer the key tables.
*/
/*
* Old special codes.
*/
#define OLD_SHIFTKEYS 0x80
#define OLD_BUCKYBITS 0x90
#define OLD_FUNNY 0xA0
#define OLD_FA_UMLAUT 0xA9
#define OLD_FA_CFLEX 0xAA
#define OLD_FA_TILDE 0xAB
#define OLD_FA_CEDILLA 0xAC
#define OLD_FA_ACUTE 0xAD
#define OLD_FA_GRAVE 0xAE
#define OLD_ISOCHAR 0xAF
#define OLD_STRING 0xB0
#define OLD_LEFTFUNC 0xC0
#define OLD_RIGHTFUNC 0xD0
#define OLD_TOPFUNC 0xE0
#define OLD_BOTTOMFUNC 0xF0
/*
* Map old special codes to new ones.
* Indexed by ((old special code) >> 4) & 0x07; add (old special code) & 0x0F.
*/
static ushort_t special_old_to_new[] = {
SHIFTKEYS,
BUCKYBITS,
FUNNY,
STRING,
LEFTFUNC,
RIGHTFUNC,
TOPFUNC,
BOTTOMFUNC,
};
/*
* kbtrans_setkey:
* Set individual keystation translation from old-style entry.
*/
static int
kbtrans_setkey(struct kbtrans_lower *lower, struct kiockey *key, cred_t *cr)
{
int strtabindex, i;
unsigned short *ke;
register int tablemask;
register ushort_t entry;
register struct keyboard *kp;
kp = lower->kbtrans_keyboard;
if (key->kio_station >= kp->k_keymap_size)
return (EINVAL);
if (lower->kbtrans_keyboard == NULL)
return (EINVAL);
tablemask = key->kio_tablemask;
switch (tablemask) {
case KIOCABORT1:
case KIOCABORT1A:
case KIOCABORT2:
i = secpolicy_console(cr);
if (i != 0)
return (i);
switch (tablemask) {
case KIOCABORT1:
kp->k_abort1 = key->kio_station;
break;
case KIOCABORT1A:
kp->k_abort1a = key->kio_station;
break;
case KIOCABORT2:
kp->k_abort2 = key->kio_station;
break;
}
return (0);
}
if (tablemask & ALTGRAPHMASK)
return (EINVAL);
ke = kbtrans_find_entry(lower, (uint_t)tablemask, key->kio_station);
if (ke == NULL)
return (EINVAL);
if (key->kio_entry >= (uchar_t)OLD_STRING &&
key->kio_entry <= (uchar_t)(OLD_STRING + 15)) {
strtabindex = key->kio_entry - OLD_STRING;
bcopy(key->kio_string,
lower->kbtrans_keystringtab[strtabindex], KTAB_STRLEN);
lower->kbtrans_keystringtab[strtabindex][KTAB_STRLEN-1] = '\0';
}
entry = key->kio_entry;
/*
* There's nothing we need do with OLD_ISOCHAR.
*/
if (entry != OLD_ISOCHAR) {
if (entry & 0x80) {
if (entry >= OLD_FA_UMLAUT && entry <= OLD_FA_GRAVE)
entry = FA_CLASS + (entry & 0x0F) - 9;
else
entry =
special_old_to_new[entry >> 4 & 0x07]
+ (entry & 0x0F);
}
}
*ke = entry;
return (0);
}
/*
* Map new special codes to old ones.
* Indexed by (new special code) >> 8; add (new special code) & 0xFF.
*/
static uchar_t special_new_to_old[] = {
0, /* normal */
OLD_SHIFTKEYS, /* SHIFTKEYS */
OLD_BUCKYBITS, /* BUCKYBITS */
OLD_FUNNY, /* FUNNY */
OLD_FA_UMLAUT, /* FA_CLASS */
OLD_STRING, /* STRING */
OLD_LEFTFUNC, /* FUNCKEYS */
};
/*
* kbtrans_getkey:
* Get individual keystation translation as old-style entry.
*/
static int
kbtrans_getkey(struct kbtrans_lower *lower, struct kiockey *key)
{
int strtabindex;
unsigned short *ke;
register ushort_t entry;
struct keyboard *kp;
kp = lower->kbtrans_keyboard;
if (key->kio_station >= kp->k_keymap_size)
return (EINVAL);
if (lower->kbtrans_keyboard == NULL)
return (EINVAL);
switch (key->kio_tablemask) {
case KIOCABORT1:
key->kio_station = kp->k_abort1;
return (0);
case KIOCABORT1A:
key->kio_station = kp->k_abort1a;
return (0);
case KIOCABORT2:
key->kio_station = kp->k_abort2;
return (0);
}
ke = kbtrans_find_entry(lower, (uint_t)key->kio_tablemask,
key->kio_station);
if (ke == NULL)
return (EINVAL);
entry = *ke;
if (entry & 0xFF00)
key->kio_entry =
special_new_to_old[(ushort_t)(entry & 0xFF00) >> 8]
+ (entry & 0x00FF);
else {
if (entry & 0x80)
key->kio_entry = (ushort_t)OLD_ISOCHAR; /* you lose */
else
key->kio_entry = (ushort_t)entry;
}
if (entry >= STRING && entry <= (uchar_t)(STRING + 15)) {
strtabindex = entry - STRING;
bcopy(lower->kbtrans_keystringtab[strtabindex],
key->kio_string, KTAB_STRLEN);
}
return (0);
}
/*
* kbtrans_skey:
* Set individual keystation translation from new-style entry.
*/
static int
kbtrans_skey(struct kbtrans_lower *lower, struct kiockeymap *key, cred_t *cr)
{
int strtabindex, i;
unsigned short *ke;
struct keyboard *kp;
kp = lower->kbtrans_keyboard;
if (key->kio_station >= kp->k_keymap_size) {
return (EINVAL);
}
if (lower->kbtrans_keyboard == NULL) {
return (EINVAL);
}
switch (key->kio_tablemask) {
case KIOCABORT1:
case KIOCABORT1A:
case KIOCABORT2:
i = secpolicy_console(cr);
if (i != 0)
return (i);
switch (key->kio_tablemask) {
case KIOCABORT1:
kp->k_abort1 = key->kio_station;
break;
case KIOCABORT1A:
kp->k_abort1a = key->kio_station;
break;
case KIOCABORT2:
kp->k_abort2 = key->kio_station;
break;
}
return (0);
}
ke = kbtrans_find_entry(lower, (uint_t)key->kio_tablemask,
key->kio_station);
if (ke == NULL)
return (EINVAL);
if (key->kio_entry >= STRING &&
key->kio_entry <= (ushort_t)(STRING + 15)) {
strtabindex = key->kio_entry-STRING;
bcopy(key->kio_string,
lower->kbtrans_keystringtab[strtabindex], KTAB_STRLEN);
lower->kbtrans_keystringtab[strtabindex][KTAB_STRLEN-1] = '\0';
}
*ke = key->kio_entry;
return (0);
}
/*
* kbtrans_gkey:
* Get individual keystation translation as new-style entry.
*/
static int
kbtrans_gkey(struct kbtrans_lower *lower, struct kiockeymap *key)
{
int strtabindex;
unsigned short *ke;
struct keyboard *kp;
kp = lower->kbtrans_keyboard;
if (key->kio_station >= kp->k_keymap_size)
return (EINVAL);
if (lower->kbtrans_keyboard == NULL)
return (EINVAL);
switch (key->kio_tablemask) {
case KIOCABORT1:
key->kio_station = kp->k_abort1;
return (0);
case KIOCABORT1A:
key->kio_station = kp->k_abort1a;
return (0);
case KIOCABORT2:
key->kio_station = kp->k_abort2;
return (0);
}
ke = kbtrans_find_entry(lower, (uint_t)key->kio_tablemask,
key->kio_station);
if (ke == NULL)
return (EINVAL);
key->kio_entry = *ke;
if (key->kio_entry >= STRING &&
key->kio_entry <= (ushort_t)(STRING + 15)) {
strtabindex = key->kio_entry-STRING;
bcopy(lower->kbtrans_keystringtab[strtabindex],
key->kio_string, KTAB_STRLEN);
}
return (0);
}