/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2019 Joyent, Inc. */ /* * Generic keyboard support: STREAMS and administration. */ #define KEYMAP_SIZE_VARIABLE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kbtrans_lower.h" #include "kbtrans_streams.h" #ifdef DEBUG int kbtrans_errmask; int kbtrans_errlevel; #endif #define KB_NR_FUNCKEYS 12 /* * Overrides for tuning keyboard autorepeat behaviour: */ int kbtrans_repeat_count = -1; int kbtrans_repeat_rate; int kbtrans_repeat_delay; /* * Override whether to print a message when the keyboard event queue overflows: */ int kbtrans_overflow_msg = 1; /* * Override whether to drop "Scroll Lock" key presses in TR_ASCII mode: */ int kbtrans_ignore_scroll_lock = 1; /* * Override for the number of key stations we will track in the down state at * one time. By default we assume most keyboards will be operated by people * with up to ten fingers, plus some safety margin. This size is only read * once at attach time. */ 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)" }; 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 *, 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 *); /* * 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, }; 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(); } /* * kbtrans_streams_init: * Initialize the stream, keytables, callbacks, etc. */ 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; /* * 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; } 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; upper->progressbar_key_abort_flag = 0; cv_init(&upper->progressbar_key_abort_cv, NULL, CV_DEFAULT, NULL); /* this counts on no keyboards being above ipl 12 */ 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); } /* * 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); 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); } /* * 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; /* trigger switch back to text mode */ 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; } /* Shift key needs to be sent to upper immediately */ 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); } /* * 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_setled(): * This is the routine that is called to only update the led state * in kbtrans. */ 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; } /* * 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 KIOCGRPTCOUNT: /* * Report the autorepeat count */ 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); /* 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 KIOCSRPTCOUNT: /* * Set the autorepeat count */ DPRINTF(PRINT_L0, PRINT_MASK_ALL, (upper, "KIOCSRPTCOUNT\n")); err = miocpullup(mp, sizeof (int)); if (err != 0) break; /* validate the input */ if (*(int *)mp->b_cont->b_rptr < -1) { err = EINVAL; break; } kbtrans_repeat_count = (*(int *)mp->b_cont->b_rptr); break; 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; upper->kbtrans_streams_count++; /* * NB: polled code zaps kbtrans_repeatkey without cancelling * timeout. */ if (kbtrans_repeat_count > 0) { /* If limit is set and reached, stop there. */ 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); } } /* * 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)) { 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: /* 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. * Convert to UTF-8. */ 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); } /* * 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->vt_switch_keystate = VT_SWITCH_KEY_NONE; /* Discard any obsolete CTRL/ALT/SHIFT keys */ 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; } } /* * 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_vt_compose: * To compose the key sequences for virtual terminal switching. * * 'ALTL + F#' for 1-12 terminals * 'ALTGR + F#' for 13-24 terminals * 'ALT + UPARROW' for last terminal * 'ALT + LEFTARROW' for previous terminal * 'ALT + RIGHTARROW' for next terminal * * the vt switching message is encoded as: * * ------------------------------------------------------------- * | \033 | 'Q' | vtno + 'A' | opcode | 'z' | '\0' | * ------------------------------------------------------------- * * opcode: * 'B' to switch to previous terminal * 'F' to switch to next terminal * 'L' to switch to last terminal * 'H' to switch to the terminal as specified by vtno, * which is from 1 to 24. * * Here keyid is the keycode of UPARROW, LEFTARROW, or RIGHTARROW * when it is a kind of arrow key as indicated by is_arrow_key, * otherwise it indicates a function key and keyid is the number * corresponding to that function 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'; /* Escape */ *bufp++ = 'Q'; if (is_arrow_key) { *bufp++ = 'A'; switch (keyid) { case UPARROW: /* last vt */ *bufp++ = 'L'; break; case LEFTARROW: /* previous vt */ *bufp++ = 'B'; break; case RIGHTARROW: /* next vt */ *bufp++ = 'F'; break; default: break; } } else { /* this is funckey specifying vtno for switch */ *bufp++ = keyid + (upper->vt_switch_keystate - VT_SWITCH_KEY_ALT) * KB_NR_FUNCKEYS + 'A'; *bufp++ = 'H'; } *bufp++ = 'z'; *bufp = '\0'; /* * Send the result upstream. */ kbtrans_putbuf(buf, upper->kbtrans_streams_readq); } /* * 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]; unsigned short keyid; struct kbtrans_lower *lower = &upper->kbtrans_lower; /* * Based on the type of key, we may need to do some ASCII * specific post processing. Note that the translated entry * is constructed as the actual keycode plus entrytype. See * sys/kbd.h for details of each entrytype. */ 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: /* * There is no ascii equivalent. We will ignore these * keys */ return; case FUNCKEYS: if (upper->vt_switch_keystate > VT_SWITCH_KEY_NONE) { if (entry >= TOPFUNC && entry < (TOPFUNC + KB_NR_FUNCKEYS)) { /* * keyid is the number correspoding to F# * and its value is from 1 to 12. */ keyid = (entry & 0xF) + 1; kbtrans_vt_compose(upper, keyid, B_FALSE, buf); return; } } if (kbtrans_ignore_scroll_lock && entry == RF(3)) { /* * We do not perform full handling of the "Scroll Lock" * key. It does not change the console scrolling mode * or even impact the keyboard indicator light. * * Some hypervisor keyboard emulators (e.g., SPICE in * QEMU) seek to keep the scroll lock state in sync * with the local client keyboard state by aggressively * generating emulated Scroll Lock press and release * events. Emitting the function key control sequence * (which is already of dubious utility) makes the * console unusable; instead, we ignore Scroll Lock * completely here. */ return; } /* * 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: 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; } } /* * 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 char upstream. */ kbtrans_putcode(upper, entry); } #define KB_SCANCODE_ALT 0xe2 #define KB_SCANCODE_ALTGRAPH 0xe6 /* * 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 except * checking 'Alt/AltGraph' release key in order to reset the state * of vt switch key sequence. */ /* ARGSUSED */ 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; } } /* * 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_count = 0; 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_count = 0; 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 keymap_entry_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; 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; /* * 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; 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; /* 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; 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); } /* * kbtrans_gkey: * Get individual keystation translation as new-style entry. */ 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); }