#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 <sys/sunldi.h>
#include <sys/class.h>
#include <sys/spl.h>
#include "kbtrans_lower.h"
#include "kbtrans_streams.h"
#ifdef DEBUG
int kbtrans_errmask;
int kbtrans_errlevel;
#endif
#define KB_NR_FUNCKEYS 12
int kbtrans_repeat_count = -1;
int kbtrans_repeat_rate;
int kbtrans_repeat_delay;
int kbtrans_overflow_msg = 1;
int kbtrans_ignore_scroll_lock = 1;
int kbtrans_downs_size = 15;
extern struct mod_ops mod_miscops;
static struct modlmisc modlmisc = {
&mod_miscops,
"kbtrans (key translation)"
};
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));
}
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 *, mblk_t *);
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 *);
static int kbtrans_gkey(struct kbtrans_lower *, struct kiockeymap *);
struct keyboard_callback untrans_event_callback = {
kbtrans_untrans_keypressed_raw,
kbtrans_untrans_keyreleased_raw,
NULL,
NULL,
NULL,
NULL,
NULL,
};
struct keyboard_callback ascii_callback = {
NULL,
NULL,
kbtrans_ascii_keypressed,
kbtrans_ascii_keyreleased,
kbtrans_ascii_setup_repeat,
kbtrans_cancelrpt,
kbtrans_setled,
};
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,
};
static void
progressbar_key_abort_thread(struct kbtrans *upper)
{
ldi_ident_t li;
extern void progressbar_key_abort(ldi_ident_t);
if (ldi_ident_from_stream(upper->kbtrans_streams_readq, &li) != 0) {
cmn_err(CE_NOTE, "!ldi_ident_from_stream failed");
} else {
mutex_enter(&upper->progressbar_key_abort_lock);
while (upper->progressbar_key_abort_flag == 0)
cv_wait(&upper->progressbar_key_abort_cv,
&upper->progressbar_key_abort_lock);
if (upper->progressbar_key_abort_flag == 1) {
mutex_exit(&upper->progressbar_key_abort_lock);
progressbar_key_abort(li);
} else {
mutex_exit(&upper->progressbar_key_abort_lock);
}
ldi_ident_release(li);
}
thread_exit();
}
int
kbtrans_streams_init(queue_t *q, int sflag, 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;
kthread_t *tid;
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[];
if (!kbtrans_repeat_rate) {
kbtrans_repeat_rate = (hz + 29) / 30;
kbtrans_repeat_delay = hz / 2;
}
switch (sflag) {
case MODOPEN:
break;
case CLONEOPEN:
DPRINTF(PRINT_L1, PRINT_MASK_OPEN, (NULL,
"kbtrans_streams_init: Clone open not supported"));
return (EINVAL);
}
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;
kbtrans_set_translation_callback(upper);
lower = &upper->kbtrans_lower;
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;
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;
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;
upper->progressbar_key_abort_flag = 0;
cv_init(&upper->progressbar_key_abort_cv, NULL, CV_DEFAULT, NULL);
mutex_init(&upper->progressbar_key_abort_lock, NULL, MUTEX_SPIN,
(void *)ipltospl(12));
tid = thread_create(NULL, 0, progressbar_key_abort_thread, upper,
0, &p0, TS_RUN, minclsyspri);
upper->progressbar_key_abort_t_did = tid->t_did;
DPRINTF(PRINT_L1, PRINT_MASK_OPEN, (upper, "kbtrans_streams_init "
"exiting"));
return (0);
}
int
kbtrans_streams_fini(struct kbtrans *upper)
{
upper->kbtrans_streams_flags = 0;
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);
mutex_enter(&upper->progressbar_key_abort_lock);
if (upper->progressbar_key_abort_flag == 0) {
upper->progressbar_key_abort_flag = 2;
cv_signal(&upper->progressbar_key_abort_cv);
mutex_exit(&upper->progressbar_key_abort_lock);
thread_join(upper->progressbar_key_abort_t_did);
} else {
mutex_exit(&upper->progressbar_key_abort_lock);
}
cv_destroy(&upper->progressbar_key_abort_cv);
mutex_destroy(&upper->progressbar_key_abort_lock);
kmem_free(upper, sizeof (struct kbtrans));
DPRINTF(PRINT_L1, PRINT_MASK_CLOSE, (upper, "kbtrans_streams_fini "
"exiting"));
return (0);
}
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"));
for (i = 0, ke = upper->kbtrans_streams_downs;
i < upper->kbtrans_streams_num_downs_entries; i++, ke++) {
if (ke->key_station) {
kbtrans_keyreleased(upper, ke->key_station);
}
}
}
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"));
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);
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);
}
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;
mutex_enter(&upper->progressbar_key_abort_lock);
if (upper->progressbar_key_abort_flag == 0) {
upper->progressbar_key_abort_flag = 1;
cv_signal(&upper->progressbar_key_abort_cv);
}
mutex_exit(&upper->progressbar_key_abort_lock);
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;
}
if (key == (kbtrans_key_t)kp->k_newabort1 ||
key == (kbtrans_key_t)kp->k_newabort1a) {
upper->kbtrans_streams_abort_state =
NEW_ABORT_ABORT1_RECEIVED;
upper->kbtrans_streams_new_abort1_key = key;
}
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);
}
break;
case NEW_ABORT_ABORT1_RECEIVED:
upper->kbtrans_streams_abort_state = ABORT_NORMAL;
if (state == KEY_PRESSED &&
key == (kbtrans_key_t)kp->k_newabort2) {
abort_sequence_enter((char *)NULL);
kbtrans_processkey(lower,
upper->kbtrans_streams_callback,
upper->kbtrans_streams_new_abort1_key,
KEY_RELEASED);
return;
}
}
}
kbtrans_processkey(lower, upper->kbtrans_streams_callback, key, state);
}
void
kbtrans_streams_set_keyboard(struct kbtrans *upper, int id, struct keyboard *k)
{
upper->kbtrans_lower.kbtrans_keyboard = k;
upper->kbtrans_streams_id = id;
}
void
kbtrans_streams_has_reset(struct kbtrans *upper)
{
}
void
kbtrans_streams_enable(struct kbtrans *upper)
{
kbtrans_setled(upper);
}
void
kbtrans_streams_setled(struct kbtrans *upper, int led_state)
{
struct kbtrans_lower *lower;
lower = &upper->kbtrans_lower;
lower->kbtrans_led_state = (uchar_t)led_state;
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;
}
void
kbtrans_streams_set_queue(struct kbtrans *upper, queue_t *q)
{
upper->kbtrans_streams_readq = q;
}
queue_t *
kbtrans_streams_get_queue(struct kbtrans *upper)
{
return (upper->kbtrans_streams_readq);
}
void
kbtrans_streams_untimeout(struct kbtrans *upper)
{
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;
}
}
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) {
upper->kbtrans_streams_iocpending = NULL;
(void) kbtrans_ioctl(upper, mp);
}
}
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)
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)
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;
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)
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)
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);
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);
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;
datap->b_wptr += sizeof (int);
if (mp->b_cont)
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)
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
break;
case CONSSETABORTENABLE:
if (iocp->ioc_count != TRANSPARENT) {
err = EINVAL;
break;
}
upper->kbtrans_streams_abortable =
(boolean_t)*(intptr_t *)mp->b_cont->b_rptr;
return (KBTRANS_MESSAGE_NOT_HANDLED);
case KIOCGRPTCOUNT:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCGRPTCOUNT\n"));
if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
ioctlrespsize = sizeof (int);
goto allocfailure;
}
*(int *)datap->b_wptr = kbtrans_repeat_count;
datap->b_wptr += sizeof (int);
if (mp->b_cont != NULL)
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
break;
case KIOCSRPTCOUNT:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCSRPTCOUNT\n"));
err = miocpullup(mp, sizeof (int));
if (err != 0)
break;
if (*(int *)mp->b_cont->b_rptr < -1) {
err = EINVAL;
break;
}
kbtrans_repeat_count = (*(int *)mp->b_cont->b_rptr);
break;
case KIOCGRPTDELAY:
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);
if (mp->b_cont != NULL)
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
break;
case KIOCSRPTDELAY:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCSRPTDELAY\n"));
err = miocpullup(mp, sizeof (int));
if (err != 0)
break;
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:
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);
if (mp->b_cont != NULL)
freemsg(mp->b_cont);
mp->b_cont = datap;
iocp->ioc_count = sizeof (int);
break;
case KIOCSRPTRATE:
DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCSRPTRATE\n"));
err = miocpullup(mp, sizeof (int));
if (err != 0)
break;
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);
}
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;
mp->b_datap->db_type = M_IOCACK;
}
putnext(upper->kbtrans_streams_readq, mp);
return (KBTRANS_MESSAGE_HANDLED);
allocfailure:
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);
return (KBTRANS_MESSAGE_HANDLED);
}
static void
kbtrans_flush(register struct kbtrans *upper)
{
register queue_t *q;
if ((q = upper->kbtrans_streams_readq) != NULL && q->q_next != NULL)
(void) putnextctl1(q, M_FLUSH, FLUSHR);
bzero(upper->kbtrans_streams_downs, upper->kbtrans_streams_downs_bytes);
kbtrans_cancelrpt(upper);
}
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);
}
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;
upper->kbtrans_streams_count++;
if (kbtrans_repeat_count > 0) {
if (upper->kbtrans_streams_count > kbtrans_repeat_count)
lower->kbtrans_repeatkey = 0;
}
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);
}
}
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;
}
}
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;
kbtrans_queueevent(upper, &fe);
fe.value = 0;
kbtrans_queueevent(upper, &fe);
}
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);
}
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;
if (fe->id < (ushort_t)VKEY_FIRST) {
if (lower->kbtrans_shiftmask & (CTRLMASK | CTLSMASK)) {
keymap_entry_t *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:
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);
}
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;
for (i = 0, ke = upper->kbtrans_streams_downs;
i < upper->kbtrans_streams_num_downs_entries; i++, ke++) {
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);
}
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;
}
for (i = 0, ke = upper->kbtrans_streams_downs;
i < upper->kbtrans_streams_num_downs_entries;
i++, ke++) {
if (ke->key_station == key_station) {
ke->key_station = 0;
ke->event.value = 0;
kbtrans_queueevent(upper, &ke->event);
}
}
}
static void
kbtrans_putcode(register struct kbtrans *upper, uint_t code)
{
register mblk_t *bp;
if (!canputnext(upper->kbtrans_streams_readq)) {
return;
}
if ((bp = allocb(sizeof (uint_t), BPRI_HI)) == NULL) {
cmn_err(CE_WARN, "kbtrans_putcode: "
"Can't allocate block for keycode.");
return;
}
code = KEYCHAR(code);
if (code < 0x80) {
*bp->b_wptr++ = (char)code;
} else if (code < 0x800) {
*bp->b_wptr++ = 0xc0 | (code >> 6);
*bp->b_wptr++ = 0x80 | (code & 0x3f);
} else if (code < 0x10000) {
*bp->b_wptr++ = 0xe0 | (code >> 12);
*bp->b_wptr++ = 0x80 | ((code >> 6) & 0x3f);
*bp->b_wptr++ = 0x80 | (code & 0x3f);
} else {
*bp->b_wptr++ = 0xf0 | (code >> 18);
*bp->b_wptr++ = 0x80 | ((code >> 12) & 0x3f);
*bp->b_wptr++ = 0x80 | ((code >> 6) & 0x3f);
*bp->b_wptr++ = 0x80 | (code & 0x3f);
}
(void) putnext(upper->kbtrans_streams_readq, bp);
}
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);
}
}
}
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);
}
}
}
static void
kbtrans_set_translation_callback(register struct kbtrans *upper)
{
switch (upper->kbtrans_streams_translate_mode) {
default:
case TR_ASCII:
upper->vt_switch_keystate = VT_SWITCH_KEY_NONE;
upper->kbtrans_lower.kbtrans_shiftmask &=
~(CTRLMASK | ALTMASK | SHIFTMASK);
upper->kbtrans_lower.kbtrans_togglemask &=
~(CTRLMASK | ALTMASK | SHIFTMASK);
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;
}
}
static void
kbtrans_untrans_keypressed_raw(struct kbtrans *upper, kbtrans_key_t key)
{
Firm_event fe;
bzero(&fe, sizeof (fe));
fe.id = (unsigned short)key;
fe.value = 1;
kbtrans_queuepress(upper, key, &fe);
}
static void
kbtrans_untrans_keyreleased_raw(struct kbtrans *upper, kbtrans_key_t key)
{
kbtrans_keyreleased(upper, key);
}
static void
kbtrans_vt_compose(struct kbtrans *upper, unsigned short keyid,
boolean_t is_arrow_key, char *buf)
{
char *bufp;
bufp = buf;
*bufp++ = '\033';
*bufp++ = 'Q';
if (is_arrow_key) {
*bufp++ = 'A';
switch (keyid) {
case UPARROW:
*bufp++ = 'L';
break;
case LEFTARROW:
*bufp++ = 'B';
break;
case RIGHTARROW:
*bufp++ = 'F';
break;
default:
break;
}
} else {
*bufp++ = keyid +
(upper->vt_switch_keystate - VT_SWITCH_KEY_ALT) *
KB_NR_FUNCKEYS + 'A';
*bufp++ = 'H';
}
*bufp++ = 'z';
*bufp = '\0';
kbtrans_putbuf(buf, upper->kbtrans_streams_readq);
}
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];
unsigned short keyid;
struct kbtrans_lower *lower = &upper->kbtrans_lower;
switch (entrytype) {
case BUCKYBITS:
return;
case SHIFTKEYS:
keyid = entry & 0xFF;
if (keyid == ALT) {
upper->vt_switch_keystate = VT_SWITCH_KEY_ALT;
} else if (keyid == ALTGRAPH) {
upper->vt_switch_keystate = VT_SWITCH_KEY_ALTGR;
}
return;
case FUNNY:
return;
case FUNCKEYS:
if (upper->vt_switch_keystate > VT_SWITCH_KEY_NONE) {
if (entry >= TOPFUNC &&
entry < (TOPFUNC + KB_NR_FUNCKEYS)) {
keyid = (entry & 0xF) + 1;
kbtrans_vt_compose(upper, keyid, B_FALSE, buf);
return;
}
}
if (kbtrans_ignore_scroll_lock && entry == RF(3)) {
return;
}
bufp = buf;
cp = kbtrans_strsetwithdecimal(bufp + 2,
(uint_t)((entry & 0x003F) + 192),
sizeof (buf) - 5);
*bufp++ = '\033';
*bufp++ = '[';
while (*cp != '\0')
*bufp++ = *cp++;
*bufp++ = 'z';
*bufp = '\0';
kbtrans_putbuf(buf, upper->kbtrans_streams_readq);
return;
case STRING:
if (upper->vt_switch_keystate > VT_SWITCH_KEY_NONE) {
keyid = entry & 0xFF;
if (keyid == UPARROW ||
keyid == RIGHTARROW ||
keyid == LEFTARROW) {
kbtrans_vt_compose(upper, keyid, B_TRUE, buf);
return;
}
}
cp = &lower->kbtrans_keystringtab[entry & 0x0F][0];
while (*cp != '\0') {
kbtrans_putcode(upper, (uchar_t)*cp);
cp++;
}
return;
case PADKEYS:
kbtrans_putcode(upper,
lower->kbtrans_numlock_table[entry&0x1F]);
return;
case 0:
default:
break;
}
kbtrans_putcode(upper, entry);
}
#define KB_SCANCODE_ALT 0xe2
#define KB_SCANCODE_ALTGRAPH 0xe6
static void
kbtrans_ascii_keyreleased(struct kbtrans *upper, kbtrans_key_t key)
{
if (key == KB_SCANCODE_ALT || key == KB_SCANCODE_ALTGRAPH) {
upper->vt_switch_keystate = VT_SWITCH_KEY_NONE;
}
}
static void
kbtrans_ascii_setup_repeat(struct kbtrans *upper, uint_t entrytype,
kbtrans_key_t key)
{
struct kbtrans_lower *lower = &upper->kbtrans_lower;
kbtrans_cancelrpt(upper);
lower->kbtrans_repeatkey = key;
upper->kbtrans_streams_count = 0;
upper->kbtrans_streams_rptid = qtimeout(upper->kbtrans_streams_readq,
kbtrans_rpt, (caddr_t)upper, kbtrans_repeat_delay);
}
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;
switch (entrytype) {
case SHIFTKEYS:
fe.id = SHIFT_CAPSLOCK + (entry & 0x0F);
fe.value = 1;
kbtrans_keypressed(upper, key, &fe, fe.id);
return;
case BUCKYBITS:
fe.id = SHIFT_META + (entry & 0x0F);
fe.value = 1;
kbtrans_keypressed(upper, key, &fe, fe.id);
return;
case FUNCKEYS:
fe.id = KEY_LEFTFIRST + (entry & 0x003F);
fe.value = 1;
kbtrans_keypressed(upper, key, &fe, fe.id);
return;
case STRING:
cp = &lower->kbtrans_keystringtab[entry & 0x0F][0];
while (*cp != '\0') {
kbtrans_send_esc_event(*cp, upper);
cp++;
}
return;
case PADKEYS:
fe.id = VKEY_FIRSTPAD + (entry & 0x001F);
fe.value = 1;
kbtrans_keypressed(upper, key, &fe, fe.id);
return;
case FUNNY:
switch (entry) {
case IDLE:
case RESET:
case ERROR:
kbtrans_streams_releaseall(upper);
break;
}
return;
case 0:
default:
break;
}
fe.id = entry;
fe.value = 1;
kbtrans_queueevent(upper, &fe);
}
static void
kbtrans_trans_event_keyreleased(struct kbtrans *upper, kbtrans_key_t key)
{
kbtrans_keyreleased(upper, 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;
if (entrytype == FUNCKEYS || entrytype == PADKEYS) {
return;
}
kbtrans_cancelrpt(upper);
lower->kbtrans_repeatkey = key;
upper->kbtrans_streams_count = 0;
upper->kbtrans_streams_rptid = qtimeout(upper->kbtrans_streams_readq,
kbtrans_rpt, (caddr_t)upper, kbtrans_repeat_delay);
}
#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
static keymap_entry_t special_old_to_new[] = {
SHIFTKEYS,
BUCKYBITS,
FUNNY,
STRING,
LEFTFUNC,
RIGHTFUNC,
TOPFUNC,
BOTTOMFUNC,
};
static int
kbtrans_setkey(struct kbtrans_lower *lower, struct kiockey *key, cred_t *cr)
{
int strtabindex, i;
keymap_entry_t *ke;
register int tablemask;
register keymap_entry_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;
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);
}
static uchar_t special_new_to_old[] = {
0,
OLD_SHIFTKEYS,
OLD_BUCKYBITS,
OLD_FUNNY,
OLD_FA_UMLAUT,
OLD_STRING,
OLD_LEFTFUNC,
};
static int
kbtrans_getkey(struct kbtrans_lower *lower, struct kiockey *key)
{
int strtabindex;
keymap_entry_t *ke;
register keymap_entry_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;
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);
}
static int
kbtrans_skey(struct kbtrans_lower *lower, struct kiockeymap *key, cred_t *cr)
{
int strtabindex, i;
keymap_entry_t *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 <= (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);
}
static int
kbtrans_gkey(struct kbtrans_lower *lower, struct kiockeymap *key)
{
int strtabindex;
keymap_entry_t *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 <= (STRING + 15)) {
strtabindex = key->kio_entry-STRING;
bcopy(lower->kbtrans_keystringtab[strtabindex],
key->kio_string, KTAB_STRLEN);
}
return (0);
}