kbdmux.c revision 156226
138032Speter/* 238032Speter * kbdmux.c 338032Speter */ 438032Speter 538032Speter/*- 638032Speter * Copyright (c) 2005 Maksim Yevmenkin <m_evmenkin@yahoo.com> 738032Speter * All rights reserved. 838032Speter * 938032Speter * Redistribution and use in source and binary forms, with or without 1038032Speter * modification, are permitted provided that the following conditions 1138032Speter * are met: 1242575Speter * 1. Redistributions of source code must retain the above copyright 1338032Speter * notice, this list of conditions and the following disclaimer. 1438032Speter * 2. Redistributions in binary form must reproduce the above copyright 1538032Speter * notice, this list of conditions and the following disclaimer in the 1638032Speter * documentation and/or other materials provided with the distribution. 1738032Speter * 1838032Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1938032Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2038032Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2138032Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2242575Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2338032Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2438032Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2538032Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2638032Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2738032Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2838032Speter * SUCH DAMAGE. 2938032Speter * 3038032Speter * $Id: kbdmux.c,v 1.4 2005/07/14 17:38:35 max Exp $ 3138032Speter * $FreeBSD: head/sys/dev/kbdmux/kbdmux.c 156226 2006-03-03 00:46:28Z emax $ 3238032Speter */ 3338032Speter 3438032Speter#include "opt_kbd.h" 3538032Speter 3638032Speter#include <sys/param.h> 3738032Speter#include <sys/bus.h> 3838032Speter#include <sys/conf.h> 3938032Speter#include <sys/consio.h> 4038032Speter#include <sys/fcntl.h> 4138032Speter#include <sys/kbio.h> 4238032Speter#include <sys/kernel.h> 4338032Speter#include <sys/limits.h> 4438032Speter#include <sys/lock.h> 4538032Speter#include <sys/malloc.h> 4638032Speter#include <sys/module.h> 4738032Speter#include <sys/mutex.h> 4838032Speter#include <sys/poll.h> 4938032Speter#include <sys/proc.h> 5038032Speter#include <sys/queue.h> 5138032Speter#include <sys/selinfo.h> 5238032Speter#include <sys/systm.h> 5338032Speter#include <sys/taskqueue.h> 5438032Speter#include <sys/tty.h> 5538032Speter#include <sys/uio.h> 5638032Speter#include <dev/kbd/kbdreg.h> 5738032Speter#include <dev/kbd/kbdtables.h> 5838032Speter 5938032Speter#define KEYBOARD_NAME "kbdmux" 6038032Speter 6138032SpeterMALLOC_DECLARE(M_KBDMUX); 6238032SpeterMALLOC_DEFINE(M_KBDMUX, KEYBOARD_NAME, "Keyboard multiplexor"); 6338032Speter 6438032Speter/***************************************************************************** 6538032Speter ***************************************************************************** 6638032Speter ** Keyboard state 6738032Speter ***************************************************************************** 6838032Speter *****************************************************************************/ 6938032Speter 7038032Speter#define KBDMUX_Q_SIZE 512 /* input queue size */ 7138032Speter 7238032Speter/* 7338032Speter * XXX 7438032Speter * For now rely on Giant mutex to protect our data structures. 7538032Speter * Just like the rest of keyboard drivers and syscons(4) do. 7638032Speter * Note that callout is initialized as not MP-safe to make sure 7738032Speter * Giant is held. 7838032Speter */ 7938032Speter 8038032Speter#if 0 /* not yet */ 8138032Speter#define KBDMUX_LOCK_DECL_GLOBAL \ 8238032Speter struct mtx ks_lock 8338032Speter#define KBDMUX_LOCK_INIT(s) \ 8438032Speter mtx_init(&(s)->ks_lock, "kbdmux", NULL, MTX_DEF|MTX_RECURSE) 8538032Speter#define KBDMUX_LOCK_DESTROY(s) \ 8638032Speter mtx_destroy(&(s)->ks_lock) 8738032Speter#define KBDMUX_LOCK(s) \ 8838032Speter mtx_lock(&(s)->ks_lock) 8938032Speter#define KBDMUX_UNLOCK(s) \ 9038032Speter mtx_unlock(&(s)->ks_lock) 9138032Speter#define KBDMUX_LOCK_ASSERT(s, w) \ 9238032Speter mtx_assert(&(s)->ks_lock, (w)) 9338032Speter#define KBDMUX_SLEEP(s, f, d, t) \ 9438032Speter msleep(&(s)->f, &(s)->ks_lock, PCATCH | (PZERO + 1), (d), (t)) 9538032Speter#define KBDMUX_CALLOUT_INIT(s) \ 9638032Speter callout_init_mtx(&(s)->ks_timo, &(s)->ks_lock, 0) 9738032Speter#define KBDMUX_QUEUE_INTR(s) \ 9838032Speter taskqueue_enqueue(taskqueue_swi_giant, &(s)->ks_task) 9938032Speter#else 10038032Speter#define KBDMUX_LOCK_DECL_GLOBAL 10138032Speter 10238032Speter#define KBDMUX_LOCK_INIT(s) 10338032Speter 10438032Speter#define KBDMUX_LOCK_DESTROY(s) 10538032Speter 10638032Speter#define KBDMUX_LOCK(s) 10738032Speter 10838032Speter#define KBDMUX_UNLOCK(s) 10938032Speter 11038032Speter#define KBDMUX_LOCK_ASSERT(s, w) 11138032Speter 11238032Speter#define KBDMUX_SLEEP(s, f, d, t) \ 11338032Speter tsleep(&(s)->f, PCATCH | (PZERO + 1), (d), (t)) 11438032Speter#define KBDMUX_CALLOUT_INIT(s) \ 11538032Speter callout_init(&(s)->ks_timo, 0) 11638032Speter#define KBDMUX_QUEUE_INTR(s) \ 11738032Speter taskqueue_enqueue(taskqueue_swi_giant, &(s)->ks_task) 11838032Speter#endif /* not yet */ 11938032Speter 12038032Speter#define KBDMUX_INTR(kbd, arg) \ 12138032Speter (*kbdsw[(kbd)->kb_index]->intr)((kbd), (arg)) 12238032Speter 12338032Speter#define KBDMUX_IOCTL(kbd, cmd, arg) \ 12438032Speter (*kbdsw[(kbd)->kb_index]->ioctl)((kbd), (cmd), (caddr_t) (arg)) 12538032Speter 12638032Speter#define KBDMUX_CHECK_CHAR(kbd) \ 12738032Speter (*kbdsw[(kbd)->kb_index]->check_char)((kbd)) 12838032Speter 12938032Speter#define KBDMUX_READ_CHAR(kbd, wait) \ 13038032Speter (*kbdsw[(kbd)->kb_index]->read_char)((kbd), (wait)) 13138032Speter 13238032Speter#define KBDMUX_ENABLE(kbd) \ 13338032Speter (*kbdsw[(kbd)->kb_index]->enable)((kbd)) 13438032Speter 13538032Speter#define KBDMUX_POLL(kbd, on) \ 13638032Speter (*kbdsw[(kbd)->kb_index]->poll)((kbd), (on)) 13738032Speter 13838032Speter#define KBDMUX_CLEAR_STATE(kbd) \ 13938032Speter (*kbdsw[(kbd)->kb_index]->clear_state)((kbd)) 14038032Speter 14138032Speter/* 14238032Speter * kbdmux keyboard 14338032Speter */ 14438032Speterstruct kbdmux_kbd 14538032Speter{ 14638032Speter keyboard_t *kbd; /* keyboard */ 14738032Speter SLIST_ENTRY(kbdmux_kbd) next; /* link to next */ 14838032Speter}; 14938032Speter 15038032Spetertypedef struct kbdmux_kbd kbdmux_kbd_t; 15138032Speter 15238032Speter/* 15338032Speter * kbdmux state 15438032Speter */ 15538032Speterstruct kbdmux_state 15638032Speter{ 15738032Speter struct clist ks_inq; /* input chars queue */ 15838032Speter struct task ks_task; /* interrupt task */ 15938032Speter struct callout ks_timo; /* timeout handler */ 16038032Speter#define TICKS (hz) /* rate */ 16138032Speter 16238032Speter int ks_flags; /* flags */ 16338032Speter#define COMPOSE (1 << 0) /* compose char flag */ 16438032Speter#define POLLING (1 << 1) /* polling */ 16538032Speter#define TASK (1 << 2) /* interrupt task queued */ 16638032Speter 16738032Speter int ks_mode; /* K_XLATE, K_RAW, K_CODE */ 16838032Speter int ks_state; /* state */ 16938032Speter int ks_accents; /* accent key index (> 0) */ 17038032Speter u_int ks_composed_char; /* composed char code */ 17138032Speter u_char ks_prefix; /* AT scan code prefix */ 17238032Speter 17338032Speter SLIST_HEAD(, kbdmux_kbd) ks_kbds; /* keyboards */ 17438032Speter 17538032Speter KBDMUX_LOCK_DECL_GLOBAL; 17638032Speter}; 17738032Speter 17838032Spetertypedef struct kbdmux_state kbdmux_state_t; 17938032Speter 18038032Speter/***************************************************************************** 18138032Speter ***************************************************************************** 18238032Speter ** Helper functions 18338032Speter ***************************************************************************** 18438032Speter *****************************************************************************/ 18538032Speter 18638032Speterstatic task_fn_t kbdmux_kbd_intr; 18738032Speterstatic timeout_t kbdmux_kbd_intr_timo; 18838032Speterstatic kbd_callback_func_t kbdmux_kbd_event; 18938032Speter 19038032Speter/* 19138032Speter * Interrupt handler task 19238032Speter */ 19338032Spetervoid 19438032Speterkbdmux_kbd_intr(void *xkbd, int pending) 19538032Speter{ 19638032Speter keyboard_t *kbd = (keyboard_t *) xkbd; 19738032Speter kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data; 19838032Speter 19938032Speter KBDMUX_INTR(kbd, NULL); 20038032Speter 20138032Speter KBDMUX_LOCK(state); 20238032Speter 20338032Speter state->ks_flags &= ~TASK; 20438032Speter wakeup(&state->ks_task); 20538032Speter 20638032Speter KBDMUX_UNLOCK(state); 20738032Speter} 20838032Speter 20938032Speter/* 21038032Speter * Schedule interrupt handler on timeout. Called with locked state. 21138032Speter */ 21238032Spetervoid 21338032Speterkbdmux_kbd_intr_timo(void *xstate) 21438032Speter{ 21538032Speter kbdmux_state_t *state = (kbdmux_state_t *) xstate; 21638032Speter 21738032Speter KBDMUX_LOCK_ASSERT(state, MA_OWNED); 21838032Speter 21938032Speter if (callout_pending(&state->ks_timo)) 22038032Speter return; /* callout was reset */ 22138032Speter 22238032Speter if (!callout_active(&state->ks_timo)) 22338032Speter return; /* callout was stopped */ 22438032Speter 22538032Speter callout_deactivate(&state->ks_timo); 22638032Speter 22738032Speter /* queue interrupt task if needed */ 22838032Speter if (state->ks_inq.c_cc > 0 && !(state->ks_flags & TASK) && 22938032Speter KBDMUX_QUEUE_INTR(state) == 0) 23038032Speter state->ks_flags |= TASK; 23138032Speter 23238032Speter /* re-schedule timeout */ 23338032Speter callout_reset(&state->ks_timo, TICKS, kbdmux_kbd_intr_timo, state); 23438032Speter} 23538032Speter 23638032Speter/* 23738032Speter * Process event from one of our keyboards 23838032Speter */ 23938032Speterstatic int 24038032Speterkbdmux_kbd_event(keyboard_t *kbd, int event, void *arg) 24138032Speter{ 24238032Speter kbdmux_state_t *state = (kbdmux_state_t *) arg; 24338032Speter 24438032Speter switch (event) { 24538032Speter case KBDIO_KEYINPUT: { 24638032Speter int c; 24738032Speter 24838032Speter KBDMUX_LOCK(state); 24938032Speter 25038032Speter /* 25138032Speter * Read all chars from the keyboard 25238032Speter * 25338032Speter * Turns out that atkbd(4) check_char() method may return 25438032Speter * "true" while read_char() method returns NOKEY. If this 25538032Speter * happens we could stuck in the loop below. Avoid this 25638032Speter * by breaking out of the loop if read_char() method returns 25738032Speter * NOKEY. 25838032Speter */ 25938032Speter 26038032Speter while (KBDMUX_CHECK_CHAR(kbd)) { 26138032Speter c = KBDMUX_READ_CHAR(kbd, 0); 26238032Speter if (c == NOKEY) 26338032Speter break; 26438032Speter if (c == ERRKEY) 26538032Speter continue; /* XXX ring bell */ 26638032Speter if (!KBD_IS_BUSY(kbd)) 26738032Speter continue; /* not open - discard the input */ 26838032Speter 26938032Speter putc(c, &state->ks_inq); 27038032Speter } 27138032Speter 27238032Speter /* queue interrupt task if needed */ 27338032Speter if (state->ks_inq.c_cc > 0 && !(state->ks_flags & TASK) && 27438032Speter KBDMUX_QUEUE_INTR(state) == 0) 27538032Speter state->ks_flags |= TASK; 27638032Speter 27738032Speter KBDMUX_UNLOCK(state); 27838032Speter } break; 27938032Speter 28038032Speter case KBDIO_UNLOADING: { 28138032Speter kbdmux_kbd_t *k; 28238032Speter 28338032Speter KBDMUX_LOCK(state); 28438032Speter 28538032Speter SLIST_FOREACH(k, &state->ks_kbds, next) 28638032Speter if (k->kbd == kbd) 28738032Speter break; 28838032Speter 28938032Speter if (k != NULL) { 29038032Speter kbd_release(k->kbd, &k->kbd); 29138032Speter SLIST_REMOVE(&state->ks_kbds, k, kbdmux_kbd, next); 29238032Speter 29338032Speter k->kbd = NULL; 29438032Speter 29538032Speter free(k, M_KBDMUX); 29638032Speter } 29738032Speter 29838032Speter KBDMUX_UNLOCK(state); 29938032Speter } break; 30038032Speter 30138032Speter default: 30238032Speter return (EINVAL); 30338032Speter /* NOT REACHED */ 30438032Speter } 30538032Speter 30638032Speter return (0); 30738032Speter} 30838032Speter 30938032Speter/**************************************************************************** 31038032Speter **************************************************************************** 31138032Speter ** Keyboard driver 31238032Speter **************************************************************************** 31338032Speter ****************************************************************************/ 31438032Speter 31538032Speterstatic int kbdmux_configure(int flags); 31638032Speterstatic kbd_probe_t kbdmux_probe; 31738032Speterstatic kbd_init_t kbdmux_init; 31838032Speterstatic kbd_term_t kbdmux_term; 31938032Speterstatic kbd_intr_t kbdmux_intr; 32038032Speterstatic kbd_test_if_t kbdmux_test_if; 32138032Speterstatic kbd_enable_t kbdmux_enable; 32238032Speterstatic kbd_disable_t kbdmux_disable; 32338032Speterstatic kbd_read_t kbdmux_read; 32438032Speterstatic kbd_check_t kbdmux_check; 32538032Speterstatic kbd_read_char_t kbdmux_read_char; 32638032Speterstatic kbd_check_char_t kbdmux_check_char; 32738032Speterstatic kbd_ioctl_t kbdmux_ioctl; 32838032Speterstatic kbd_lock_t kbdmux_lock; 32938032Speterstatic void kbdmux_clear_state_locked(kbdmux_state_t *state); 33038032Speterstatic kbd_clear_state_t kbdmux_clear_state; 33138032Speterstatic kbd_get_state_t kbdmux_get_state; 33238032Speterstatic kbd_set_state_t kbdmux_set_state; 33338032Speterstatic kbd_poll_mode_t kbdmux_poll; 33438032Speter 33538032Speterstatic keyboard_switch_t kbdmuxsw = { 33638032Speter .probe = kbdmux_probe, 33738032Speter .init = kbdmux_init, 33838032Speter .term = kbdmux_term, 33938032Speter .intr = kbdmux_intr, 34038032Speter .test_if = kbdmux_test_if, 34138032Speter .enable = kbdmux_enable, 34238032Speter .disable = kbdmux_disable, 34338032Speter .read = kbdmux_read, 34438032Speter .check = kbdmux_check, 34538032Speter .read_char = kbdmux_read_char, 34638032Speter .check_char = kbdmux_check_char, 34738032Speter .ioctl = kbdmux_ioctl, 34838032Speter .lock = kbdmux_lock, 34938032Speter .clear_state = kbdmux_clear_state, 35038032Speter .get_state = kbdmux_get_state, 35138032Speter .set_state = kbdmux_set_state, 35238032Speter .get_fkeystr = genkbd_get_fkeystr, 35338032Speter .poll = kbdmux_poll, 35438032Speter .diag = genkbd_diag, 35538032Speter}; 35638032Speter 35738032Speter/* 35838032Speter * Return the number of found keyboards 35938032Speter */ 36038032Speterstatic int 36138032Speterkbdmux_configure(int flags) 36238032Speter{ 36338032Speter return (1); 36438032Speter} 36538032Speter 36638032Speter/* 36738032Speter * Detect a keyboard 36838032Speter */ 36938032Speterstatic int 37038032Speterkbdmux_probe(int unit, void *arg, int flags) 37138032Speter{ 37238032Speter if (resource_disabled(KEYBOARD_NAME, unit)) 37338032Speter return (ENXIO); 37438032Speter 37538032Speter return (0); 37638032Speter} 37738032Speter 37838032Speter/* 37938032Speter * Reset and initialize the keyboard (stolen from atkbd.c) 38038032Speter */ 38138032Speterstatic int 38238032Speterkbdmux_init(int unit, keyboard_t **kbdp, void *arg, int flags) 38338032Speter{ 38438032Speter keyboard_t *kbd = NULL; 38538032Speter kbdmux_state_t *state = NULL; 38638032Speter keymap_t *keymap = NULL; 38738032Speter accentmap_t *accmap = NULL; 38838032Speter fkeytab_t *fkeymap = NULL; 38938032Speter int error, needfree, fkeymap_size, delay[2]; 39038032Speter 39138032Speter if (*kbdp == NULL) { 39238032Speter *kbdp = kbd = malloc(sizeof(*kbd), M_KBDMUX, M_NOWAIT | M_ZERO); 39338032Speter state = malloc(sizeof(*state), M_KBDMUX, M_NOWAIT | M_ZERO); 39438032Speter keymap = malloc(sizeof(key_map), M_KBDMUX, M_NOWAIT); 39538032Speter accmap = malloc(sizeof(accent_map), M_KBDMUX, M_NOWAIT); 39638032Speter fkeymap = malloc(sizeof(fkey_tab), M_KBDMUX, M_NOWAIT); 39738032Speter fkeymap_size = sizeof(fkey_tab)/sizeof(fkey_tab[0]); 39838032Speter needfree = 1; 39938032Speter 40038032Speter if ((kbd == NULL) || (state == NULL) || (keymap == NULL) || 40138032Speter (accmap == NULL) || (fkeymap == NULL)) { 40238032Speter error = ENOMEM; 40338032Speter goto bad; 40438032Speter } 40538032Speter 40638032Speter KBDMUX_LOCK_INIT(state); 40738032Speter clist_alloc_cblocks(&state->ks_inq, 40838032Speter KBDMUX_Q_SIZE, KBDMUX_Q_SIZE / 2); 40938032Speter TASK_INIT(&state->ks_task, 0, kbdmux_kbd_intr, (void *) kbd); 41038032Speter KBDMUX_CALLOUT_INIT(state); 41138032Speter SLIST_INIT(&state->ks_kbds); 41238032Speter } else if (KBD_IS_INITIALIZED(*kbdp) && KBD_IS_CONFIGURED(*kbdp)) { 41338032Speter return (0); 41438032Speter } else { 41538032Speter kbd = *kbdp; 41638032Speter state = (kbdmux_state_t *) kbd->kb_data; 41738032Speter keymap = kbd->kb_keymap; 41838032Speter accmap = kbd->kb_accentmap; 41938032Speter fkeymap = kbd->kb_fkeytab; 42038032Speter fkeymap_size = kbd->kb_fkeytab_size; 42138032Speter needfree = 0; 42238032Speter } 42338032Speter 42438032Speter if (!KBD_IS_PROBED(kbd)) { 42538032Speter /* XXX assume 101/102 keys keyboard */ 42638032Speter kbd_init_struct(kbd, KEYBOARD_NAME, KB_101, unit, flags, 0, 0); 42738032Speter bcopy(&key_map, keymap, sizeof(key_map)); 42838032Speter bcopy(&accent_map, accmap, sizeof(accent_map)); 42938032Speter bcopy(fkey_tab, fkeymap, 43038032Speter imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab))); 43138032Speter kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size); 43238032Speter kbd->kb_data = (void *)state; 43338032Speter 43438032Speter KBD_FOUND_DEVICE(kbd); 43538032Speter KBD_PROBE_DONE(kbd); 43638032Speter 43738032Speter KBDMUX_LOCK(state); 43838032Speter kbdmux_clear_state_locked(state); 43938032Speter state->ks_mode = K_XLATE; 44038032Speter KBDMUX_UNLOCK(state); 44138032Speter } 44238032Speter 44338032Speter if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) { 44438032Speter kbd->kb_config = flags & ~KB_CONF_PROBE_ONLY; 44538032Speter 44638032Speter kbdmux_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state); 44738032Speter 44838032Speter delay[0] = kbd->kb_delay1; 44938032Speter delay[1] = kbd->kb_delay2; 45038032Speter kbdmux_ioctl(kbd, KDSETREPEAT, (caddr_t)delay); 45138032Speter 45238032Speter KBD_INIT_DONE(kbd); 45338032Speter } 45438032Speter 45538032Speter if (!KBD_IS_CONFIGURED(kbd)) { 45638032Speter if (kbd_register(kbd) < 0) { 45738032Speter error = ENXIO; 45838032Speter goto bad; 45938032Speter } 46038032Speter 46138032Speter KBD_CONFIG_DONE(kbd); 46238032Speter 46338032Speter KBDMUX_LOCK(state); 46438032Speter callout_reset(&state->ks_timo, TICKS, kbdmux_kbd_intr_timo, state); 46538032Speter KBDMUX_UNLOCK(state); 46638032Speter } 46738032Speter 46838032Speter return (0); 46938032Speterbad: 47038032Speter if (needfree) { 47138032Speter if (state != NULL) { 47238032Speter clist_free_cblocks(&state->ks_inq); 47338032Speter free(state, M_KBDMUX); 47438032Speter } 47538032Speter if (keymap != NULL) 47638032Speter free(keymap, M_KBDMUX); 47738032Speter if (accmap != NULL) 47838032Speter free(accmap, M_KBDMUX); 47938032Speter if (fkeymap != NULL) 48038032Speter free(fkeymap, M_KBDMUX); 48138032Speter if (kbd != NULL) { 48238032Speter free(kbd, M_KBDMUX); 48338032Speter *kbdp = NULL; /* insure ref doesn't leak to caller */ 48438032Speter } 48538032Speter } 48638032Speter 48738032Speter return (error); 48838032Speter} 48938032Speter 49038032Speter/* 49138032Speter * Finish using this keyboard 49238032Speter */ 49338032Speterstatic int 49438032Speterkbdmux_term(keyboard_t *kbd) 49538032Speter{ 49638032Speter kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data; 49738032Speter kbdmux_kbd_t *k; 49838032Speter 49938032Speter KBDMUX_LOCK(state); 50038032Speter 50138032Speter /* kill callout */ 50238032Speter callout_stop(&state->ks_timo); 50338032Speter 50438032Speter /* wait for interrupt task */ 50538032Speter while (state->ks_flags & TASK) 50638032Speter KBDMUX_SLEEP(state, ks_task, "kbdmuxc", 0); 50738032Speter 50838032Speter /* release all keyboards from the mux */ 50938032Speter while ((k = SLIST_FIRST(&state->ks_kbds)) != NULL) { 51038032Speter kbd_release(k->kbd, &k->kbd); 51138032Speter SLIST_REMOVE_HEAD(&state->ks_kbds, next); 51238032Speter 51338032Speter k->kbd = NULL; 51438032Speter 51538032Speter free(k, M_KBDMUX); 51638032Speter } 51738032Speter 51838032Speter /* flush input queue */ 51938032Speter ndflush(&state->ks_inq, state->ks_inq.c_cc); 52038032Speter clist_free_cblocks(&state->ks_inq); 52138032Speter 52238032Speter KBDMUX_UNLOCK(state); 52338032Speter 52438032Speter kbd_unregister(kbd); 52538032Speter 52638032Speter KBDMUX_LOCK_DESTROY(state); 52738032Speter bzero(state, sizeof(*state)); 52838032Speter free(state, M_KBDMUX); 52938032Speter 53038032Speter free(kbd->kb_keymap, M_KBDMUX); 53138032Speter free(kbd->kb_accentmap, M_KBDMUX); 53238032Speter free(kbd->kb_fkeytab, M_KBDMUX); 53338032Speter free(kbd, M_KBDMUX); 53438032Speter 53538032Speter return (0); 53638032Speter} 53738032Speter 53838032Speter/* 53938032Speter * Keyboard interrupt routine 54038032Speter */ 54138032Speterstatic int 54238032Speterkbdmux_intr(keyboard_t *kbd, void *arg) 54338032Speter{ 54438032Speter int c; 54538032Speter 54638032Speter if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) { 54738032Speter /* let the callback function to process the input */ 54838032Speter (*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT, 54938032Speter kbd->kb_callback.kc_arg); 55038032Speter } else { 55138032Speter /* read and discard the input; no one is waiting for input */ 55238032Speter do { 55338032Speter c = kbdmux_read_char(kbd, FALSE); 55438032Speter } while (c != NOKEY); 55538032Speter } 55638032Speter 55738032Speter return (0); 55838032Speter} 55938032Speter 56038032Speter/* 56138032Speter * Test the interface to the device 56238032Speter */ 56338032Speterstatic int 56438032Speterkbdmux_test_if(keyboard_t *kbd) 56538032Speter{ 56638032Speter return (0); 56738032Speter} 56838032Speter 56938032Speter/* 57038032Speter * Enable the access to the device; until this function is called, 57138032Speter * the client cannot read from the keyboard. 57238032Speter */ 57338032Speterstatic int 57438032Speterkbdmux_enable(keyboard_t *kbd) 57538032Speter{ 57638032Speter KBD_ACTIVATE(kbd); 57738032Speter return (0); 57838032Speter} 57938032Speter 58038032Speter/* 58138032Speter * Disallow the access to the device 58238032Speter */ 58338032Speterstatic int 58438032Speterkbdmux_disable(keyboard_t *kbd) 58538032Speter{ 58638032Speter KBD_DEACTIVATE(kbd); 58738032Speter return (0); 58838032Speter} 58938032Speter 59038032Speter/* 59138032Speter * Read one byte from the keyboard if it's allowed 59238032Speter */ 59338032Speterstatic int 59438032Speterkbdmux_read(keyboard_t *kbd, int wait) 59538032Speter{ 59638032Speter kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data; 59738032Speter int c; 59838032Speter 59938032Speter KBDMUX_LOCK(state); 60038032Speter c = getc(&state->ks_inq); 60138032Speter KBDMUX_UNLOCK(state); 60238032Speter 60338032Speter if (c != -1) 60438032Speter kbd->kb_count ++; 60538032Speter 60638032Speter return (KBD_IS_ACTIVE(kbd)? c : -1); 60738032Speter} 60838032Speter 60938032Speter/* 61038032Speter * Check if data is waiting 61138032Speter */ 61238032Speterstatic int 61338032Speterkbdmux_check(keyboard_t *kbd) 61438032Speter{ 61538032Speter kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data; 61638032Speter int ready; 61738032Speter 61838032Speter if (!KBD_IS_ACTIVE(kbd)) 61938032Speter return (FALSE); 62038032Speter 62138032Speter KBDMUX_LOCK(state); 62238032Speter ready = (state->ks_inq.c_cc > 0)? TRUE : FALSE; 62338032Speter KBDMUX_UNLOCK(state); 62438032Speter 62538032Speter return (ready); 62638032Speter} 62738032Speter 62838032Speter/* 62938032Speter * Read char from the keyboard (stolen from atkbd.c) 63038032Speter */ 63138032Speterstatic u_int 63238032Speterkbdmux_read_char(keyboard_t *kbd, int wait) 63338032Speter{ 63438032Speter kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data; 63538032Speter u_int action; 63638032Speter int scancode, keycode; 63738032Speter 63838032Speter KBDMUX_LOCK(state); 63938032Speter 64038032Speternext_code: 64138032Speter 64238032Speter /* do we have a composed char to return? */ 64338032Speter if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) { 64438032Speter action = state->ks_composed_char; 64538032Speter state->ks_composed_char = 0; 64638032Speter if (action > UCHAR_MAX) { 64738032Speter KBDMUX_UNLOCK(state); 64838032Speter 64938032Speter return (ERRKEY); 65038032Speter } 65138032Speter 65238032Speter KBDMUX_UNLOCK(state); 65338032Speter 65438032Speter return (action); 65538032Speter } 65638032Speter 65738032Speter /* see if there is something in the keyboard queue */ 65838032Speter scancode = getc(&state->ks_inq); 65938032Speter if (scancode == -1) { 66038032Speter KBDMUX_UNLOCK(state); 66138032Speter return (NOKEY); 66238032Speter } 66338032Speter /* XXX FIXME: check for -1 if wait == 1! */ 66438032Speter 66538032Speter kbd->kb_count ++; 66638032Speter 66738032Speter /* return the byte as is for the K_RAW mode */ 66838032Speter if (state->ks_mode == K_RAW) { 66938032Speter KBDMUX_UNLOCK(state); 67038032Speter return (scancode); 67138032Speter } 67238032Speter 67338032Speter /* translate the scan code into a keycode */ 67438032Speter keycode = scancode & 0x7F; 67538032Speter switch (state->ks_prefix) { 67638032Speter case 0x00: /* normal scancode */ 67738032Speter switch(scancode) { 67838032Speter case 0xB8: /* left alt (compose key) released */ 67938032Speter if (state->ks_flags & COMPOSE) { 68038032Speter state->ks_flags &= ~COMPOSE; 68138032Speter if (state->ks_composed_char > UCHAR_MAX) 68238032Speter state->ks_composed_char = 0; 68338032Speter } 68438032Speter break; 68538032Speter case 0x38: /* left alt (compose key) pressed */ 68638032Speter if (!(state->ks_flags & COMPOSE)) { 68742575Speter state->ks_flags |= COMPOSE; 68838032Speter state->ks_composed_char = 0; 68938032Speter } 69038032Speter break; 69138032Speter case 0xE0: 69238032Speter case 0xE1: 69338032Speter state->ks_prefix = scancode; 69438032Speter goto next_code; 69538032Speter } 69638032Speter break; 69738032Speter case 0xE0: /* 0xE0 prefix */ 69838032Speter state->ks_prefix = 0; 69938032Speter switch (keycode) { 70038032Speter case 0x1C: /* right enter key */ 70138032Speter keycode = 0x59; 70238032Speter break; 70338032Speter case 0x1D: /* right ctrl key */ 70438032Speter keycode = 0x5A; 70538032Speter break; 70638032Speter case 0x35: /* keypad divide key */ 70738032Speter keycode = 0x5B; 70838032Speter break; 70938032Speter case 0x37: /* print scrn key */ 71038032Speter keycode = 0x5C; 71138032Speter break; 71238032Speter case 0x38: /* right alt key (alt gr) */ 71338032Speter keycode = 0x5D; 71438032Speter break; 71538032Speter case 0x46: /* ctrl-pause/break on AT 101 (see below) */ 71638032Speter keycode = 0x68; 71738032Speter break; 71838032Speter case 0x47: /* grey home key */ 71938032Speter keycode = 0x5E; 72038032Speter break; 72138032Speter case 0x48: /* grey up arrow key */ 72238032Speter keycode = 0x5F; 72338032Speter break; 72438032Speter case 0x49: /* grey page up key */ 72538032Speter keycode = 0x60; 72638032Speter break; 72738032Speter case 0x4B: /* grey left arrow key */ 72838032Speter keycode = 0x61; 72938032Speter break; 73038032Speter case 0x4D: /* grey right arrow key */ 73138032Speter keycode = 0x62; 73238032Speter break; 73338032Speter case 0x4F: /* grey end key */ 73438032Speter keycode = 0x63; 73538032Speter break; 73638032Speter case 0x50: /* grey down arrow key */ 73738032Speter keycode = 0x64; 73838032Speter break; 73938032Speter case 0x51: /* grey page down key */ 74038032Speter keycode = 0x65; 74138032Speter break; 74238032Speter case 0x52: /* grey insert key */ 74338032Speter keycode = 0x66; 74438032Speter break; 74538032Speter case 0x53: /* grey delete key */ 74638032Speter keycode = 0x67; 74738032Speter break; 74838032Speter /* the following 3 are only used on the MS "Natural" keyboard */ 74938032Speter case 0x5b: /* left Window key */ 75038032Speter keycode = 0x69; 75138032Speter break; 75238032Speter case 0x5c: /* right Window key */ 75338032Speter keycode = 0x6a; 75438032Speter break; 75538032Speter case 0x5d: /* menu key */ 75638032Speter keycode = 0x6b; 75738032Speter break; 75838032Speter case 0x5e: /* power key */ 75938032Speter keycode = 0x6d; 76038032Speter break; 76138032Speter case 0x5f: /* sleep key */ 76238032Speter keycode = 0x6e; 76338032Speter break; 76438032Speter case 0x63: /* wake key */ 76538032Speter keycode = 0x6f; 76638032Speter break; 76738032Speter default: /* ignore everything else */ 76838032Speter goto next_code; 76938032Speter } 77038032Speter break; 77138032Speter case 0xE1: /* 0xE1 prefix */ 77238032Speter /* 77338032Speter * The pause/break key on the 101 keyboard produces: 77438032Speter * E1-1D-45 E1-9D-C5 77538032Speter * Ctrl-pause/break produces: 77638032Speter * E0-46 E0-C6 (See above.) 77738032Speter */ 77838032Speter state->ks_prefix = 0; 77938032Speter if (keycode == 0x1D) 78038032Speter state->ks_prefix = 0x1D; 78138032Speter goto next_code; 78238032Speter /* NOT REACHED */ 78338032Speter case 0x1D: /* pause / break */ 78438032Speter state->ks_prefix = 0; 78538032Speter if (keycode != 0x45) 78638032Speter goto next_code; 78738032Speter keycode = 0x68; 78838032Speter break; 78938032Speter } 79038032Speter 79138032Speter /* XXX assume 101/102 keys AT keyboard */ 79238032Speter switch (keycode) { 79338032Speter case 0x5c: /* print screen */ 79438032Speter if (state->ks_flags & ALTS) 79538032Speter keycode = 0x54; /* sysrq */ 79638032Speter break; 79738032Speter case 0x68: /* pause/break */ 79838032Speter if (state->ks_flags & CTLS) 79938032Speter keycode = 0x6c; /* break */ 80038032Speter break; 80138032Speter } 80238032Speter 80338032Speter /* return the key code in the K_CODE mode */ 80438032Speter if (state->ks_mode == K_CODE) { 80538032Speter KBDMUX_UNLOCK(state); 80638032Speter return (keycode | (scancode & 0x80)); 80738032Speter } 80838032Speter 80938032Speter /* compose a character code */ 81038032Speter if (state->ks_flags & COMPOSE) { 81138032Speter switch (keycode | (scancode & 0x80)) { 81238032Speter /* key pressed, process it */ 81338032Speter case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ 81438032Speter state->ks_composed_char *= 10; 81538032Speter state->ks_composed_char += keycode - 0x40; 81638032Speter if (state->ks_composed_char > UCHAR_MAX) { 81738032Speter KBDMUX_UNLOCK(state); 81838032Speter return (ERRKEY); 81938032Speter } 82038032Speter goto next_code; 82138032Speter case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ 82238032Speter state->ks_composed_char *= 10; 82338032Speter state->ks_composed_char += keycode - 0x47; 82438032Speter if (state->ks_composed_char > UCHAR_MAX) { 82538032Speter KBDMUX_UNLOCK(state); 82638032Speter return (ERRKEY); 82738032Speter } 82838032Speter goto next_code; 82938032Speter case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ 83038032Speter state->ks_composed_char *= 10; 83138032Speter state->ks_composed_char += keycode - 0x4E; 83238032Speter if (state->ks_composed_char > UCHAR_MAX) { 83338032Speter KBDMUX_UNLOCK(state); 83438032Speter return (ERRKEY); 83538032Speter } 83638032Speter goto next_code; 83738032Speter case 0x52: /* keypad 0 */ 83838032Speter state->ks_composed_char *= 10; 83938032Speter if (state->ks_composed_char > UCHAR_MAX) { 84038032Speter KBDMUX_UNLOCK(state); 84138032Speter return (ERRKEY); 84238032Speter } 84338032Speter goto next_code; 84438032Speter 84538032Speter /* key released, no interest here */ 84638032Speter case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */ 84738032Speter case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */ 84838032Speter case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */ 84938032Speter case 0xD2: /* keypad 0 */ 85038032Speter goto next_code; 85138032Speter 85238032Speter case 0x38: /* left alt key */ 85338032Speter break; 85438032Speter 85538032Speter default: 85638032Speter if (state->ks_composed_char > 0) { 85738032Speter state->ks_flags &= ~COMPOSE; 85838032Speter state->ks_composed_char = 0; 85938032Speter KBDMUX_UNLOCK(state); 86038032Speter return (ERRKEY); 86138032Speter } 86238032Speter break; 86338032Speter } 86438032Speter } 86538032Speter 86638032Speter /* keycode to key action */ 86738032Speter action = genkbd_keyaction(kbd, keycode, scancode & 0x80, 86838032Speter &state->ks_state, &state->ks_accents); 86938032Speter if (action == NOKEY) 87038032Speter goto next_code; 87138032Speter 87238032Speter KBDMUX_UNLOCK(state); 87338032Speter 87438032Speter return (action); 87538032Speter} 87638032Speter 87738032Speter/* 87838032Speter * Check if char is waiting 87938032Speter */ 88038032Speterstatic int 88138032Speterkbdmux_check_char(keyboard_t *kbd) 88238032Speter{ 88338032Speter kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data; 88438032Speter int ready; 88538032Speter 88638032Speter if (!KBD_IS_ACTIVE(kbd)) 88738032Speter return (FALSE); 88838032Speter 88938032Speter KBDMUX_LOCK(state); 89038032Speter 89138032Speter if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char != 0)) 89238032Speter ready = TRUE; 89338032Speter else 89438032Speter ready = (state->ks_inq.c_cc > 0)? TRUE : FALSE; 89538032Speter 89638032Speter KBDMUX_UNLOCK(state); 89738032Speter 89838032Speter return (ready); 89938032Speter} 90038032Speter 90138032Speter/* 90238032Speter * Keyboard ioctl's 90338032Speter */ 90438032Speterstatic int 90538032Speterkbdmux_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) 90638032Speter{ 90738032Speter static int delays[] = { 90838032Speter 250, 500, 750, 1000 90938032Speter }; 91038032Speter 91138032Speter static int rates[] = { 91238032Speter 34, 38, 42, 46, 50, 55, 59, 63, 91338032Speter 68, 76, 84, 92, 100, 110, 118, 126, 91438032Speter 136, 152, 168, 184, 200, 220, 236, 252, 91538032Speter 272, 304, 336, 368, 400, 440, 472, 504 91638032Speter }; 91738032Speter 91838032Speter kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data; 91938032Speter kbdmux_kbd_t *k; 92038032Speter keyboard_info_t *ki; 92138032Speter int error = 0, mode; 92238032Speter 92338032Speter if (state == NULL) 92438032Speter return (ENXIO); 92538032Speter 92638032Speter switch (cmd) { 92738032Speter case KBADDKBD: /* add keyboard to the mux */ 92838032Speter ki = (keyboard_info_t *) arg; 92938032Speter 93038032Speter if (ki == NULL || ki->kb_unit < 0 || ki->kb_name[0] == '\0' || 93138032Speter strcmp(ki->kb_name, "*") == 0) 93238032Speter return (EINVAL); /* bad input */ 93338032Speter 93438032Speter KBDMUX_LOCK(state); 93538032Speter 93638032Speter SLIST_FOREACH(k, &state->ks_kbds, next) 93738032Speter if (k->kbd->kb_unit == ki->kb_unit && 93838032Speter strcmp(k->kbd->kb_name, ki->kb_name) == 0) 93938032Speter break; 94038032Speter 94138032Speter if (k != NULL) { 94238032Speter KBDMUX_UNLOCK(state); 94338032Speter 94438032Speter return (0); /* keyboard already in the mux */ 94538032Speter } 94638032Speter 94738032Speter k = malloc(sizeof(*k), M_KBDMUX, M_NOWAIT | M_ZERO); 94838032Speter if (k == NULL) { 94938032Speter KBDMUX_UNLOCK(state); 95038032Speter 95138032Speter return (ENOMEM); /* out of memory */ 95238032Speter } 95338032Speter 95438032Speter k->kbd = kbd_get_keyboard( 95538032Speter kbd_allocate( 95638032Speter ki->kb_name, 95738032Speter ki->kb_unit, 95838032Speter (void *) &k->kbd, 95938032Speter kbdmux_kbd_event, (void *) state)); 96038032Speter if (k->kbd == NULL) { 96138032Speter KBDMUX_UNLOCK(state); 96238032Speter free(k, M_KBDMUX); 96338032Speter 96438032Speter return (EINVAL); /* bad keyboard */ 96538032Speter } 96638032Speter 96738032Speter KBDMUX_ENABLE(k->kbd); 96838032Speter KBDMUX_CLEAR_STATE(k->kbd); 96938032Speter 97038032Speter /* set K_RAW mode on slave keyboard */ 97138032Speter mode = K_RAW; 97238032Speter error = KBDMUX_IOCTL(k->kbd, KDSKBMODE, &mode); 97338032Speter if (error == 0) { 97438032Speter /* set lock keys state on slave keyboard */ 97538032Speter mode = state->ks_state & LOCK_MASK; 97638032Speter error = KBDMUX_IOCTL(k->kbd, KDSKBSTATE, &mode); 97738032Speter } 97838032Speter 97938032Speter if (error != 0) { 98038032Speter KBDMUX_UNLOCK(state); 98138032Speter 98238032Speter kbd_release(k->kbd, &k->kbd); 98338032Speter k->kbd = NULL; 98438032Speter 98538032Speter free(k, M_KBDMUX); 98638032Speter 98738032Speter return (error); /* could not set mode */ 98838032Speter } 98938032Speter 99038032Speter SLIST_INSERT_HEAD(&state->ks_kbds, k, next); 99138032Speter 99238032Speter KBDMUX_UNLOCK(state); 99338032Speter break; 99438032Speter 99538032Speter case KBRELKBD: /* release keyboard from the mux */ 99638032Speter ki = (keyboard_info_t *) arg; 99738032Speter 99838032Speter if (ki == NULL || ki->kb_unit < 0 || ki->kb_name[0] == '\0' || 99938032Speter strcmp(ki->kb_name, "*") == 0) 100038032Speter return (EINVAL); /* bad input */ 100138032Speter 100238032Speter KBDMUX_LOCK(state); 100338032Speter 100438032Speter SLIST_FOREACH(k, &state->ks_kbds, next) 100538032Speter if (k->kbd->kb_unit == ki->kb_unit && 100638032Speter strcmp(k->kbd->kb_name, ki->kb_name) == 0) 100738032Speter break; 100838032Speter 100938032Speter if (k != NULL) { 101038032Speter error = kbd_release(k->kbd, &k->kbd); 101138032Speter if (error == 0) { 101242575Speter SLIST_REMOVE(&state->ks_kbds, k, kbdmux_kbd, next); 101338032Speter 101438032Speter k->kbd = NULL; 101538032Speter 101638032Speter free(k, M_KBDMUX); 101738032Speter } 101838032Speter } else 101938032Speter error = ENXIO; /* keyboard is not in the mux */ 102038032Speter 102138032Speter KBDMUX_UNLOCK(state); 102238032Speter break; 102338032Speter 102438032Speter case KDGKBMODE: /* get kyboard mode */ 102538032Speter KBDMUX_LOCK(state); 102638032Speter *((intptr_t *) arg) = state->ks_mode; 102738032Speter KBDMUX_UNLOCK(state); 102838032Speter break; 102938032Speter 103038032Speter case KDSKBMODE: /* set keyboard mode */ 103138032Speter KBDMUX_LOCK(state); 103238032Speter 103338032Speter switch (*((intptr_t *) arg)) { 103438032Speter case K_XLATE: 103538032Speter if (state->ks_mode != K_XLATE) { 103638032Speter /* make lock key state and LED state match */ 103738032Speter state->ks_state &= ~LOCK_MASK; 103838032Speter state->ks_state |= KBD_LED_VAL(kbd); 103938032Speter } 104038032Speter /* FALLTHROUGH */ 104138032Speter 104238032Speter case K_RAW: 104338032Speter case K_CODE: 104438032Speter if (state->ks_mode != *((intptr_t *) arg)) { 104538032Speter kbdmux_clear_state_locked(state); 104638032Speter state->ks_mode = *((intptr_t *) arg); 104738032Speter } 104838032Speter break; 104938032Speter 105038032Speter default: 105138032Speter error = EINVAL; 105238032Speter break; 105338032Speter } 105438032Speter 105538032Speter KBDMUX_UNLOCK(state); 105638032Speter break; 105738032Speter 105838032Speter case KDGETLED: /* get keyboard LED */ 105938032Speter KBDMUX_LOCK(state); 106038032Speter *((intptr_t *) arg) = KBD_LED_VAL(kbd); 106138032Speter KBDMUX_UNLOCK(state); 106238032Speter break; 106338032Speter 106438032Speter case KDSETLED: /* set keyboard LED */ 106538032Speter KBDMUX_LOCK(state); 106638032Speter 106738032Speter /* NOTE: lock key state in ks_state won't be changed */ 106838032Speter if (*((intptr_t *) arg) & ~LOCK_MASK) { 106938032Speter KBDMUX_UNLOCK(state); 107038032Speter 107138032Speter return (EINVAL); 107238032Speter } 107338032Speter 107438032Speter KBD_LED_VAL(kbd) = *((intptr_t *) arg); 107538032Speter 107638032Speter /* KDSETLED on all slave keyboards */ 107738032Speter SLIST_FOREACH(k, &state->ks_kbds, next) 107838032Speter KBDMUX_IOCTL(k->kbd, KDSETLED, arg); 107938032Speter 108038032Speter KBDMUX_UNLOCK(state); 108138032Speter break; 108242575Speter 108338032Speter case KDGKBSTATE: /* get lock key state */ 108438032Speter KBDMUX_LOCK(state); 108538032Speter *((intptr_t *) arg) = state->ks_state & LOCK_MASK; 108638032Speter KBDMUX_UNLOCK(state); 108738032Speter break; 108838032Speter 108938032Speter case KDSKBSTATE: /* set lock key state */ 109038032Speter KBDMUX_LOCK(state); 109138032Speter 109238032Speter if (*((intptr_t *) arg) & ~LOCK_MASK) { 109338032Speter KBDMUX_UNLOCK(state); 109438032Speter 109538032Speter return (EINVAL); 109638032Speter } 109738032Speter 109838032Speter state->ks_state &= ~LOCK_MASK; 109938032Speter state->ks_state |= *((intptr_t *) arg); 110038032Speter 110138032Speter /* KDSKBSTATE on all slave keyboards */ 110238032Speter SLIST_FOREACH(k, &state->ks_kbds, next) 110338032Speter KBDMUX_IOCTL(k->kbd, KDSKBSTATE, arg); 110438032Speter 110538032Speter KBDMUX_UNLOCK(state); 110638032Speter 110738032Speter return (kbdmux_ioctl(kbd, KDSETLED, arg)); 110838032Speter /* NOT REACHED */ 110938032Speter 111038032Speter case KDSETREPEAT: /* set keyboard repeat rate (new interface) */ 111138032Speter case KDSETRAD: /* set keyboard repeat rate (old interface) */ 111238032Speter KBDMUX_LOCK(state); 111338032Speter 111438032Speter if (cmd == KDSETREPEAT) { 111538032Speter int i; 111638032Speter 111738032Speter /* lookup delay */ 111838032Speter for (i = sizeof(delays)/sizeof(delays[0]) - 1; i > 0; i --) 111938032Speter if (((intptr_t *) arg)[0] >= delays[i]) 112038032Speter break; 112138032Speter mode = i << 5; 112238032Speter 112338032Speter /* lookup rate */ 112438032Speter for (i = sizeof(rates)/sizeof(rates[0]) - 1; i > 0; i --) 112542575Speter if (((intptr_t *) arg)[1] >= rates[i]) 112638032Speter break; 112738032Speter mode |= i; 112838032Speter } else 112938032Speter mode = *((intptr_t *) arg); 113038032Speter 113138032Speter if (mode & ~0x7f) { 113238032Speter KBDMUX_UNLOCK(state); 113338032Speter 113438032Speter return (EINVAL); 113538032Speter } 113638032Speter 113738032Speter kbd->kb_delay1 = delays[(mode >> 5) & 3]; 113838032Speter kbd->kb_delay2 = rates[mode & 0x1f]; 113938032Speter 114038032Speter /* perform command on all slave keyboards */ 114138032Speter SLIST_FOREACH(k, &state->ks_kbds, next) 114238032Speter KBDMUX_IOCTL(k->kbd, cmd, arg); 114338032Speter 114438032Speter KBDMUX_UNLOCK(state); 114538032Speter break; 114638032Speter 114738032Speter case PIO_KEYMAP: /* set keyboard translation table */ 114838032Speter case PIO_KEYMAPENT: /* set keyboard translation table entry */ 114938032Speter case PIO_DEADKEYMAP: /* set accent key translation table */ 115038032Speter KBDMUX_LOCK(state); 115138032Speter state->ks_accents = 0; 115238032Speter 115338032Speter /* perform command on all slave keyboards */ 115438032Speter SLIST_FOREACH(k, &state->ks_kbds, next) 115538032Speter KBDMUX_IOCTL(k->kbd, cmd, arg); 115638032Speter 115738032Speter KBDMUX_UNLOCK(state); 115838032Speter /* FALLTHROUGH */ 115938032Speter 116038032Speter default: 116138032Speter error = genkbd_commonioctl(kbd, cmd, arg); 116238032Speter break; 116338032Speter } 116438032Speter 116538032Speter return (error); 116638032Speter} 116738032Speter 116838032Speter/* 116938032Speter * Lock the access to the keyboard 117038032Speter */ 117138032Speterstatic int 117238032Speterkbdmux_lock(keyboard_t *kbd, int lock) 117338032Speter{ 117438032Speter return (1); /* XXX */ 117538032Speter} 117638032Speter 117738032Speter/* 117838032Speter * Clear the internal state of the keyboard 117938032Speter */ 118038032Speterstatic void 118138032Speterkbdmux_clear_state_locked(kbdmux_state_t *state) 118238032Speter{ 118338032Speter KBDMUX_LOCK_ASSERT(state, MA_OWNED); 118438032Speter 118538032Speter state->ks_flags &= ~(COMPOSE|POLLING); 118638032Speter state->ks_state &= LOCK_MASK; /* preserve locking key state */ 118738032Speter state->ks_accents = 0; 118838032Speter state->ks_composed_char = 0; 118942575Speter/* state->ks_prefix = 0; XXX */ 119038032Speter 119138032Speter ndflush(&state->ks_inq, state->ks_inq.c_cc); 119238032Speter} 119338032Speter 119438032Speterstatic void 119538032Speterkbdmux_clear_state(keyboard_t *kbd) 119638032Speter{ 119738032Speter kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data; 119838032Speter 119938032Speter KBDMUX_LOCK(state); 120038032Speter kbdmux_clear_state_locked(state); 120138032Speter KBDMUX_UNLOCK(state); 120238032Speter} 120338032Speter 120438032Speter/* 120538032Speter * Save the internal state 120638032Speter */ 120738032Speterstatic int 120838032Speterkbdmux_get_state(keyboard_t *kbd, void *buf, size_t len) 120938032Speter{ 121038032Speter if (len == 0) 121138032Speter return (sizeof(kbdmux_state_t)); 121238032Speter if (len < sizeof(kbdmux_state_t)) 121338032Speter return (-1); 121438032Speter 121538032Speter bcopy(kbd->kb_data, buf, sizeof(kbdmux_state_t)); /* XXX locking? */ 121638032Speter 121738032Speter return (0); 121838032Speter} 121938032Speter 122038032Speter/* 122138032Speter * Set the internal state 122238032Speter */ 122338032Speterstatic int 122438032Speterkbdmux_set_state(keyboard_t *kbd, void *buf, size_t len) 122538032Speter{ 122638032Speter if (len < sizeof(kbdmux_state_t)) 122742575Speter return (ENOMEM); 122838032Speter 122938032Speter bcopy(buf, kbd->kb_data, sizeof(kbdmux_state_t)); /* XXX locking? */ 123038032Speter 123138032Speter return (0); 123238032Speter} 123338032Speter 123438032Speter/* 123538032Speter * Set polling 123638032Speter */ 123738032Speterstatic int 123838032Speterkbdmux_poll(keyboard_t *kbd, int on) 123938032Speter{ 124038032Speter kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data; 124138032Speter kbdmux_kbd_t *k; 124238032Speter 124338032Speter KBDMUX_LOCK(state); 124438032Speter 124538032Speter if (on) 124638032Speter state->ks_flags |= POLLING; 124738032Speter else 124838032Speter state->ks_flags &= ~POLLING; 124938032Speter 125038032Speter /* set poll on slave keyboards */ 125138032Speter SLIST_FOREACH(k, &state->ks_kbds, next) 125238032Speter KBDMUX_POLL(k->kbd, on); 125338032Speter 125438032Speter KBDMUX_UNLOCK(state); 125538032Speter 125638032Speter return (0); 125738032Speter} 125838032Speter 125938032Speter/***************************************************************************** 126038032Speter ***************************************************************************** 126138032Speter ** Module 126238032Speter ***************************************************************************** 126338032Speter *****************************************************************************/ 126438032Speter 126538032SpeterKEYBOARD_DRIVER(kbdmux, kbdmuxsw, kbdmux_configure); 126638032Speter 126738032Speterstatic int 126838032Speterkbdmux_modevent(module_t mod, int type, void *data) 126938032Speter{ 127038032Speter keyboard_switch_t *sw; 127138032Speter keyboard_t *kbd; 127238032Speter int error; 127338032Speter 127438032Speter switch (type) { 127538032Speter case MOD_LOAD: 127638032Speter if ((error = kbd_add_driver(&kbdmux_kbd_driver)) != 0) 127738032Speter break; 127838032Speter 127938032Speter if ((sw = kbd_get_switch(KEYBOARD_NAME)) == NULL) { 128038032Speter kbd_delete_driver(&kbdmux_kbd_driver); 128138032Speter error = ENXIO; 128238032Speter break; 128338032Speter } 128438032Speter 128538032Speter kbd = NULL; 128638032Speter 128738032Speter if ((error = (*sw->probe)(0, NULL, 0)) != 0 || 128838032Speter (error = (*sw->init)(0, &kbd, NULL, 0)) != 0) { 128938032Speter kbd_delete_driver(&kbdmux_kbd_driver); 129038032Speter break; 129138032Speter } 129242575Speter 129342575Speter#ifdef KBD_INSTALL_CDEV 129442575Speter if ((error = kbd_attach(kbd)) != 0) { 129538032Speter (*sw->term)(kbd); 129638032Speter kbd_delete_driver(&kbdmux_kbd_driver); 129738032Speter break; 129842575Speter } 129938032Speter#endif 130038032Speter 130138032Speter if ((error = (*sw->enable)(kbd)) != 0) { 130238032Speter (*sw->disable)(kbd); 130338032Speter#ifdef KBD_INSTALL_CDEV 130438032Speter kbd_detach(kbd); 130538032Speter#endif 130638032Speter (*sw->term)(kbd); 130738032Speter kbd_delete_driver(&kbdmux_kbd_driver); 130838032Speter break; 130938032Speter } 131038032Speter break; 131138032Speter 131238032Speter case MOD_UNLOAD: 131338032Speter if ((sw = kbd_get_switch(KEYBOARD_NAME)) == NULL) 131438032Speter panic("kbd_get_switch(" KEYBOARD_NAME ") == NULL"); 131538032Speter 131638032Speter kbd = kbd_get_keyboard(kbd_find_keyboard(KEYBOARD_NAME, 0)); 131738032Speter if (kbd == NULL) 131838032Speter panic("kbd_get_keyboard(kbd_find_keyboard(" KEYBOARD_NAME ", 0)) == NULL"); 131938032Speter 132038032Speter (*sw->disable)(kbd); 132138032Speter#ifdef KBD_INSTALL_CDEV 132238032Speter kbd_detach(kbd); 132338032Speter#endif 132438032Speter (*sw->term)(kbd); 132538032Speter kbd_delete_driver(&kbdmux_kbd_driver); 132638032Speter error = 0; 132738032Speter break; 132838032Speter 132938032Speter default: 133038032Speter error = EOPNOTSUPP; 133138032Speter break; 133238032Speter } 133338032Speter 133438032Speter return (0); 133538032Speter} 133638032Speter 133738032SpeterDEV_MODULE(kbdmux, kbdmux_modevent, NULL); 133838032Speter 133938032Speter