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