xref: /dflybsd-src/sys/dev/misc/kbdmux/kbdmux.c (revision d147c94391cf5cc415970d2b885fcc931026c34e)
1fc16838fSAlex Hornung /*-
222ff886eSAlex Hornung  * (MPSAFE)
322ff886eSAlex Hornung  *
4fc16838fSAlex Hornung  * Copyright (c) 2005 Maksim Yevmenkin <m_evmenkin@yahoo.com>
5fc16838fSAlex Hornung  * All rights reserved.
6fc16838fSAlex Hornung  *
7fc16838fSAlex Hornung  * Redistribution and use in source and binary forms, with or without
8fc16838fSAlex Hornung  * modification, are permitted provided that the following conditions
9fc16838fSAlex Hornung  * are met:
10fc16838fSAlex Hornung  * 1. Redistributions of source code must retain the above copyright
11fc16838fSAlex Hornung  *    notice, this list of conditions and the following disclaimer.
12fc16838fSAlex Hornung  * 2. Redistributions in binary form must reproduce the above copyright
13fc16838fSAlex Hornung  *    notice, this list of conditions and the following disclaimer in the
14fc16838fSAlex Hornung  *    documentation and/or other materials provided with the distribution.
15fc16838fSAlex Hornung  *
16fc16838fSAlex Hornung  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17fc16838fSAlex Hornung  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18fc16838fSAlex Hornung  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19fc16838fSAlex Hornung  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20fc16838fSAlex Hornung  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21fc16838fSAlex Hornung  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22fc16838fSAlex Hornung  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23fc16838fSAlex Hornung  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24fc16838fSAlex Hornung  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25fc16838fSAlex Hornung  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26fc16838fSAlex Hornung  * SUCH DAMAGE.
27fc16838fSAlex Hornung  *
28fc16838fSAlex Hornung  * $Id: kbdmux.c,v 1.4 2005/07/14 17:38:35 max Exp $
29fc16838fSAlex Hornung  * $FreeBSD$
30fc16838fSAlex Hornung  */
31fc16838fSAlex Hornung 
323d1294c7SPeeter Must #include "opt_evdev.h"
33b272d910SAlex Hornung #include "opt_kbd.h"
34b272d910SAlex Hornung 
35fc16838fSAlex Hornung #include <sys/param.h>
36fc16838fSAlex Hornung #include <sys/bus.h>
37fc16838fSAlex Hornung #include <sys/conf.h>
38fc16838fSAlex Hornung #include <sys/consio.h>
39fc16838fSAlex Hornung #include <sys/fcntl.h>
40fc16838fSAlex Hornung #include <sys/kbio.h>
41fc16838fSAlex Hornung #include <sys/kernel.h>
42fc16838fSAlex Hornung #include <sys/limits.h>
43fc16838fSAlex Hornung #include <sys/lock.h>
44fc16838fSAlex Hornung #include <sys/malloc.h>
45fc16838fSAlex Hornung #include <sys/module.h>
46fc16838fSAlex Hornung #include <sys/poll.h>
47fc16838fSAlex Hornung #include <sys/proc.h>
48fc16838fSAlex Hornung #include <sys/queue.h>
495b22f1a7SSamuel J. Greear #include <sys/event.h>
50fc16838fSAlex Hornung #include <sys/systm.h>
51fc16838fSAlex Hornung #include <sys/taskqueue.h>
52fc16838fSAlex Hornung #include <sys/uio.h>
53bcc53404SAlex Hornung #include <dev/misc/kbd/kbdreg.h>
54bcc53404SAlex Hornung #include <dev/misc/kbd/kbdtables.h>
55fc16838fSAlex Hornung 
563d1294c7SPeeter Must #ifdef EVDEV_SUPPORT
573d1294c7SPeeter Must #include <dev/misc/evdev/evdev.h>
583d1294c7SPeeter Must #include <dev/misc/evdev/input.h>
593d1294c7SPeeter Must #endif
603d1294c7SPeeter Must 
61fc16838fSAlex Hornung #define KEYBOARD_NAME	"kbdmux"
62fc16838fSAlex Hornung 
63fc16838fSAlex Hornung MALLOC_DECLARE(M_KBDMUX);
64fc16838fSAlex Hornung MALLOC_DEFINE(M_KBDMUX, KEYBOARD_NAME, "Keyboard multiplexor");
65fc16838fSAlex Hornung 
66fc16838fSAlex Hornung /*****************************************************************************
67fc16838fSAlex Hornung  *****************************************************************************
68fc16838fSAlex Hornung  **                             Keyboard state
69fc16838fSAlex Hornung  *****************************************************************************
70fc16838fSAlex Hornung  *****************************************************************************/
71fc16838fSAlex Hornung 
72fc16838fSAlex Hornung #define	KBDMUX_Q_SIZE	512	/* input queue size */
73fc16838fSAlex Hornung 
74fc16838fSAlex Hornung /*
75fc16838fSAlex Hornung  * kbdmux keyboard
76fc16838fSAlex Hornung  */
77fc16838fSAlex Hornung struct kbdmux_kbd
78fc16838fSAlex Hornung {
79fc16838fSAlex Hornung 	keyboard_t		*kbd;	/* keyboard */
80fc16838fSAlex Hornung 	SLIST_ENTRY(kbdmux_kbd)	 next;	/* link to next */
81fc16838fSAlex Hornung };
82fc16838fSAlex Hornung 
83fc16838fSAlex Hornung typedef struct kbdmux_kbd	kbdmux_kbd_t;
84fc16838fSAlex Hornung 
85fc16838fSAlex Hornung /*
86fc16838fSAlex Hornung  * kbdmux state
87fc16838fSAlex Hornung  */
88fc16838fSAlex Hornung struct kbdmux_state
89fc16838fSAlex Hornung {
90fc16838fSAlex Hornung 	char			 ks_inq[KBDMUX_Q_SIZE]; /* input chars queue */
91fc16838fSAlex Hornung 	unsigned int		 ks_inq_start;
92fc16838fSAlex Hornung 	unsigned int		 ks_inq_length;
93fc16838fSAlex Hornung 	struct task		 ks_task;	/* interrupt task */
94fc16838fSAlex Hornung 
95fc16838fSAlex Hornung 	int			 ks_flags;	/* flags */
96fc16838fSAlex Hornung #define COMPOSE			(1 << 0)	/* compose char flag */
97fc16838fSAlex Hornung #define POLLING			(1 << 1)	/* polling */
98fc16838fSAlex Hornung 
99fc16838fSAlex Hornung 	int			 ks_mode;	/* K_XLATE, K_RAW, K_CODE */
100fc16838fSAlex Hornung 	int			 ks_state;	/* state */
101fc16838fSAlex Hornung 	int			 ks_accents;	/* accent key index (> 0) */
102fc16838fSAlex Hornung 	u_int			 ks_composed_char; /* composed char code */
103fc16838fSAlex Hornung 	u_char			 ks_prefix;	/* AT scan code prefix */
104fc16838fSAlex Hornung 
1053d1294c7SPeeter Must #ifdef EVDEV_SUPPORT
1063d1294c7SPeeter Must 	struct evdev_dev *	 ks_evdev;
1073d1294c7SPeeter Must 	int			 ks_evdev_state;
1083d1294c7SPeeter Must #endif
1093d1294c7SPeeter Must 
110fc16838fSAlex Hornung 	SLIST_HEAD(, kbdmux_kbd) ks_kbds;	/* keyboards */
111fc16838fSAlex Hornung };
112fc16838fSAlex Hornung 
113fc16838fSAlex Hornung typedef struct kbdmux_state	kbdmux_state_t;
114fc16838fSAlex Hornung 
115fc16838fSAlex Hornung /*****************************************************************************
116fc16838fSAlex Hornung  *****************************************************************************
117fc16838fSAlex Hornung  **                             Helper functions
118fc16838fSAlex Hornung  *****************************************************************************
119fc16838fSAlex Hornung  *****************************************************************************/
120fc16838fSAlex Hornung 
121fc16838fSAlex Hornung static task_fn_t		kbdmux_kbd_intr;
122fc16838fSAlex Hornung static kbd_callback_func_t	kbdmux_kbd_event;
123fc16838fSAlex Hornung 
124fc16838fSAlex Hornung static void
kbdmux_kbd_putc(kbdmux_state_t * state,char c)125fc16838fSAlex Hornung kbdmux_kbd_putc(kbdmux_state_t *state, char c)
126fc16838fSAlex Hornung {
127fc16838fSAlex Hornung 	unsigned int p;
128fc16838fSAlex Hornung 
129fc16838fSAlex Hornung 	if (state->ks_inq_length == KBDMUX_Q_SIZE)
130fc16838fSAlex Hornung 		return;
131fc16838fSAlex Hornung 
132fc16838fSAlex Hornung 	p = (state->ks_inq_start + state->ks_inq_length) % KBDMUX_Q_SIZE;
133fc16838fSAlex Hornung 	state->ks_inq[p] = c;
134fc16838fSAlex Hornung 	state->ks_inq_length++;
135fc16838fSAlex Hornung }
136fc16838fSAlex Hornung 
137fc16838fSAlex Hornung static int
kbdmux_kbd_getc(kbdmux_state_t * state)138fc16838fSAlex Hornung kbdmux_kbd_getc(kbdmux_state_t *state)
139fc16838fSAlex Hornung {
140fc16838fSAlex Hornung 	unsigned char c;
141fc16838fSAlex Hornung 
142fc16838fSAlex Hornung 	if (state->ks_inq_length == 0)
143fc16838fSAlex Hornung 		return (-1);
144fc16838fSAlex Hornung 
145fc16838fSAlex Hornung 	c = state->ks_inq[state->ks_inq_start];
146fc16838fSAlex Hornung 	state->ks_inq_start = (state->ks_inq_start + 1) % KBDMUX_Q_SIZE;
147fc16838fSAlex Hornung 	state->ks_inq_length--;
148fc16838fSAlex Hornung 
149fc16838fSAlex Hornung 	return (c);
150fc16838fSAlex Hornung }
151fc16838fSAlex Hornung 
152fc16838fSAlex Hornung /*
153fc16838fSAlex Hornung  * Interrupt handler task
154fc16838fSAlex Hornung  */
1558406cf70SSascha Wildner static void
kbdmux_kbd_intr(void * xkbd,int pending)156fc16838fSAlex Hornung kbdmux_kbd_intr(void *xkbd, int pending)
157fc16838fSAlex Hornung {
158fc16838fSAlex Hornung 	keyboard_t	*kbd = (keyboard_t *) xkbd;
159fc9700a1SMatthew Dillon 	KBD_LOCK_DECLARE;
160fc16838fSAlex Hornung 
161fc9700a1SMatthew Dillon 	KBD_LOCK(kbd);		/* recursive so ok */
162bcc53404SAlex Hornung 	kbd_intr(kbd, NULL);
163fc9700a1SMatthew Dillon 	KBD_UNLOCK(kbd);
164fc16838fSAlex Hornung }
165fc16838fSAlex Hornung 
166fc16838fSAlex Hornung /*
167fc16838fSAlex Hornung  * Process event from one of our keyboards
168fc16838fSAlex Hornung  */
169fc16838fSAlex Hornung static int
kbdmux_kbd_event(keyboard_t * kbd,int event,void * arg)170fc16838fSAlex Hornung kbdmux_kbd_event(keyboard_t *kbd, int event, void *arg)
171fc16838fSAlex Hornung {
172fc16838fSAlex Hornung 	kbdmux_state_t	*state = (kbdmux_state_t *) arg;
173fc16838fSAlex Hornung 
174fc16838fSAlex Hornung 	switch (event) {
175fc16838fSAlex Hornung 	case KBDIO_KEYINPUT: {
176fc16838fSAlex Hornung 		int	c;
177fc16838fSAlex Hornung 
178fc16838fSAlex Hornung 		/*
179fc16838fSAlex Hornung 		 * Read all chars from the keyboard
180fc16838fSAlex Hornung 		 *
181fc16838fSAlex Hornung 		 * Turns out that atkbd(4) check_char() method may return
182fc16838fSAlex Hornung 		 * "true" while read_char() method returns NOKEY. If this
183fc16838fSAlex Hornung 		 * happens we could stuck in the loop below. Avoid this
184fc16838fSAlex Hornung 		 * by breaking out of the loop if read_char() method returns
185fc16838fSAlex Hornung 		 * NOKEY.
186fc16838fSAlex Hornung 		 */
187fc16838fSAlex Hornung 
188bcc53404SAlex Hornung 		while (kbd_check_char(kbd)) {
189bcc53404SAlex Hornung 			c = kbd_read_char(kbd, 0);
190fc16838fSAlex Hornung 			if (c == NOKEY)
191fc16838fSAlex Hornung 				break;
192fc16838fSAlex Hornung 			if (c == ERRKEY)
193fc16838fSAlex Hornung 				continue; /* XXX ring bell */
194fc16838fSAlex Hornung 			if (!KBD_IS_BUSY(kbd))
195fc16838fSAlex Hornung 				continue; /* not open - discard the input */
196fc16838fSAlex Hornung 
197fc16838fSAlex Hornung 			kbdmux_kbd_putc(state, c);
198fc16838fSAlex Hornung 		}
199fc16838fSAlex Hornung 
200fc16838fSAlex Hornung 		/* queue interrupt task if needed */
20151b03d73SImre Vadász 		if (state->ks_inq_length > 0)
20251b03d73SImre Vadász 			taskqueue_enqueue(taskqueue_swi, &state->ks_task);
203fc16838fSAlex Hornung 
204fc16838fSAlex Hornung 		} break;
205fc16838fSAlex Hornung 
206fc16838fSAlex Hornung 	case KBDIO_UNLOADING: {
207fc16838fSAlex Hornung 		kbdmux_kbd_t	*k;
208fc16838fSAlex Hornung 
209fc16838fSAlex Hornung 		SLIST_FOREACH(k, &state->ks_kbds, next)
210fc16838fSAlex Hornung 			if (k->kbd == kbd)
211fc16838fSAlex Hornung 				break;
212fc16838fSAlex Hornung 
213fc16838fSAlex Hornung 		if (k != NULL) {
214fc16838fSAlex Hornung 			kbd_release(k->kbd, &k->kbd);
215fc16838fSAlex Hornung 			SLIST_REMOVE(&state->ks_kbds, k, kbdmux_kbd, next);
216fc16838fSAlex Hornung 
217fc16838fSAlex Hornung 			k->kbd = NULL;
218fc16838fSAlex Hornung 
219bcc53404SAlex Hornung 			kfree(k, M_KBDMUX);
220fc16838fSAlex Hornung 		}
221fc16838fSAlex Hornung 
222fc16838fSAlex Hornung 		} break;
223fc16838fSAlex Hornung 
224fc16838fSAlex Hornung 	default:
225fc16838fSAlex Hornung 		return (EINVAL);
226fc16838fSAlex Hornung 		/* NOT REACHED */
227fc16838fSAlex Hornung 	}
228fc16838fSAlex Hornung 	return (0);
229fc16838fSAlex Hornung }
230fc16838fSAlex Hornung 
231fc16838fSAlex Hornung /****************************************************************************
232fc16838fSAlex Hornung  ****************************************************************************
233fc16838fSAlex Hornung  **                              Keyboard driver
234fc16838fSAlex Hornung  ****************************************************************************
235fc16838fSAlex Hornung  ****************************************************************************/
236fc16838fSAlex Hornung 
237fc16838fSAlex Hornung static int		kbdmux_configure(int flags);
238fc16838fSAlex Hornung static kbd_probe_t	kbdmux_probe;
239fc16838fSAlex Hornung static kbd_init_t	kbdmux_init;
240fc16838fSAlex Hornung static kbd_term_t	kbdmux_term;
241fc16838fSAlex Hornung static kbd_intr_t	kbdmux_intr;
242fc16838fSAlex Hornung static kbd_test_if_t	kbdmux_test_if;
243fc16838fSAlex Hornung static kbd_enable_t	kbdmux_enable;
244fc16838fSAlex Hornung static kbd_disable_t	kbdmux_disable;
245fc16838fSAlex Hornung static kbd_read_t	kbdmux_read;
246fc16838fSAlex Hornung static kbd_check_t	kbdmux_check;
247fc16838fSAlex Hornung static kbd_read_char_t	kbdmux_read_char;
248fc16838fSAlex Hornung static kbd_check_char_t	kbdmux_check_char;
249fc16838fSAlex Hornung static kbd_ioctl_t	kbdmux_ioctl;
250fc16838fSAlex Hornung static kbd_lock_t	kbdmux_lock;
251fc16838fSAlex Hornung static kbd_clear_state_t kbdmux_clear_state;
252fc16838fSAlex Hornung static kbd_get_state_t	kbdmux_get_state;
253fc16838fSAlex Hornung static kbd_set_state_t	kbdmux_set_state;
254fc16838fSAlex Hornung static kbd_poll_mode_t	kbdmux_poll;
255fc16838fSAlex Hornung 
256fc16838fSAlex Hornung static keyboard_switch_t kbdmuxsw = {
257fc16838fSAlex Hornung 	.probe =	kbdmux_probe,
258fc16838fSAlex Hornung 	.init =		kbdmux_init,
259fc16838fSAlex Hornung 	.term =		kbdmux_term,
260fc16838fSAlex Hornung 	.intr =		kbdmux_intr,
261fc16838fSAlex Hornung 	.test_if =	kbdmux_test_if,
262fc16838fSAlex Hornung 	.enable =	kbdmux_enable,
263fc16838fSAlex Hornung 	.disable =	kbdmux_disable,
264fc16838fSAlex Hornung 	.read =		kbdmux_read,
265fc16838fSAlex Hornung 	.check =	kbdmux_check,
266fc16838fSAlex Hornung 	.read_char =	kbdmux_read_char,
267fc16838fSAlex Hornung 	.check_char =	kbdmux_check_char,
268fc16838fSAlex Hornung 	.ioctl =	kbdmux_ioctl,
269fc16838fSAlex Hornung 	.lock =		kbdmux_lock,
270fc16838fSAlex Hornung 	.clear_state =	kbdmux_clear_state,
271fc16838fSAlex Hornung 	.get_state =	kbdmux_get_state,
272fc16838fSAlex Hornung 	.set_state =	kbdmux_set_state,
273fc16838fSAlex Hornung 	.get_fkeystr =	genkbd_get_fkeystr,
274fc16838fSAlex Hornung 	.poll =		kbdmux_poll,
275fc16838fSAlex Hornung 	.diag =		genkbd_diag,
276fc16838fSAlex Hornung };
277fc16838fSAlex Hornung 
2783d1294c7SPeeter Must #ifdef EVDEV_SUPPORT
2793d1294c7SPeeter Must static const struct evdev_methods kbdmux_evdev_methods = {
2803d1294c7SPeeter Must 	.ev_event = evdev_ev_kbd_event,
2813d1294c7SPeeter Must };
2823d1294c7SPeeter Must #endif
2833d1294c7SPeeter Must 
284fc16838fSAlex Hornung /*
285fc16838fSAlex Hornung  * Return the number of found keyboards
286fc16838fSAlex Hornung  */
287fc16838fSAlex Hornung static int
kbdmux_configure(int flags)288fc16838fSAlex Hornung kbdmux_configure(int flags)
289fc16838fSAlex Hornung {
290fc16838fSAlex Hornung 	return (1);
291fc16838fSAlex Hornung }
292fc16838fSAlex Hornung 
293fc16838fSAlex Hornung /*
294fc16838fSAlex Hornung  * Detect a keyboard
295fc16838fSAlex Hornung  */
296fc16838fSAlex Hornung static int
kbdmux_probe(int unit,void * arg,int flags)297fc16838fSAlex Hornung kbdmux_probe(int unit, void *arg, int flags)
298fc16838fSAlex Hornung {
299fc16838fSAlex Hornung 	if (resource_disabled(KEYBOARD_NAME, unit))
300fc16838fSAlex Hornung 		return (ENXIO);
301fc16838fSAlex Hornung 
302fc16838fSAlex Hornung 	return (0);
303fc16838fSAlex Hornung }
304fc16838fSAlex Hornung 
305fc16838fSAlex Hornung /*
306fc16838fSAlex Hornung  * Reset and initialize the keyboard (stolen from atkbd.c)
307fc9700a1SMatthew Dillon  *
308fc9700a1SMatthew Dillon  * Called without kbd lock held.
309fc16838fSAlex Hornung  */
310fc16838fSAlex Hornung static int
kbdmux_init(int unit,keyboard_t ** kbdp,void * arg,int flags)311fc16838fSAlex Hornung kbdmux_init(int unit, keyboard_t **kbdp, void *arg, int flags)
312fc16838fSAlex Hornung {
313fc16838fSAlex Hornung 	kbdmux_state_t	*state = NULL;
314fc16838fSAlex Hornung 	keymap_t	*keymap = NULL;
315fc16838fSAlex Hornung         accentmap_t	*accmap = NULL;
316fc16838fSAlex Hornung         fkeytab_t	*fkeymap = NULL;
317fc9700a1SMatthew Dillon 	keyboard_t	*kbd = NULL;
318fc16838fSAlex Hornung 	int		 error, needfree, fkeymap_size, delay[2];
3193d1294c7SPeeter Must #ifdef EVDEV_SUPPORT
3203d1294c7SPeeter Must 	struct evdev_dev *evdev;
3213d1294c7SPeeter Must 	char		 phys_loc[NAMELEN];
3223d1294c7SPeeter Must #endif
323fc16838fSAlex Hornung 
324fc16838fSAlex Hornung 	if (*kbdp == NULL) {
325bcc53404SAlex Hornung 		*kbdp = kbd = kmalloc(sizeof(*kbd), M_KBDMUX, M_NOWAIT | M_ZERO);
326bcc53404SAlex Hornung 		state = kmalloc(sizeof(*state), M_KBDMUX, M_NOWAIT | M_ZERO);
327bcc53404SAlex Hornung 		keymap = kmalloc(sizeof(key_map), M_KBDMUX, M_NOWAIT);
328bcc53404SAlex Hornung 		accmap = kmalloc(sizeof(accent_map), M_KBDMUX, M_NOWAIT);
329bcc53404SAlex Hornung 		fkeymap = kmalloc(sizeof(fkey_tab), M_KBDMUX, M_NOWAIT);
330c157ff7aSSascha Wildner 		fkeymap_size = NELEM(fkey_tab);
331fc16838fSAlex Hornung 		needfree = 1;
332fc16838fSAlex Hornung 
333fc16838fSAlex Hornung 		if ((kbd == NULL) || (state == NULL) || (keymap == NULL) ||
334fc16838fSAlex Hornung 		    (accmap == NULL) || (fkeymap == NULL)) {
335fc16838fSAlex Hornung 			error = ENOMEM;
336fc16838fSAlex Hornung 			goto bad;
337fc16838fSAlex Hornung 		}
338fc16838fSAlex Hornung 
339fc16838fSAlex Hornung 		TASK_INIT(&state->ks_task, 0, kbdmux_kbd_intr, (void *) kbd);
340fc16838fSAlex Hornung 		SLIST_INIT(&state->ks_kbds);
341fc16838fSAlex Hornung 	} else if (KBD_IS_INITIALIZED(*kbdp) && KBD_IS_CONFIGURED(*kbdp)) {
342fc16838fSAlex Hornung 		return (0);
343fc16838fSAlex Hornung 	} else {
344fc16838fSAlex Hornung 		kbd = *kbdp;
345fc16838fSAlex Hornung 		state = (kbdmux_state_t *) kbd->kb_data;
346fc16838fSAlex Hornung 		keymap = kbd->kb_keymap;
347fc16838fSAlex Hornung 		accmap = kbd->kb_accentmap;
348fc16838fSAlex Hornung 		fkeymap = kbd->kb_fkeytab;
349fc16838fSAlex Hornung 		fkeymap_size = kbd->kb_fkeytab_size;
350fc16838fSAlex Hornung 		needfree = 0;
351fc16838fSAlex Hornung 	}
352fc16838fSAlex Hornung 
353fc16838fSAlex Hornung 	if (!KBD_IS_PROBED(kbd)) {
354fc16838fSAlex Hornung 		/* XXX assume 101/102 keys keyboard */
355bcc53404SAlex Hornung 		kbd_init_struct(kbd, KEYBOARD_NAME, KB_101, unit, flags,
356bcc53404SAlex Hornung 			    KB_PRI_MUX, 0, 0);
357fc16838fSAlex Hornung 		bcopy(&key_map, keymap, sizeof(key_map));
358fc16838fSAlex Hornung 		bcopy(&accent_map, accmap, sizeof(accent_map));
359fc16838fSAlex Hornung 		bcopy(fkey_tab, fkeymap,
360fc16838fSAlex Hornung 			imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab)));
361fc16838fSAlex Hornung 		kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size);
362fc16838fSAlex Hornung 		kbd->kb_data = (void *)state;
363fc16838fSAlex Hornung 
364fc16838fSAlex Hornung 		KBD_FOUND_DEVICE(kbd);
365fc16838fSAlex Hornung 		KBD_PROBE_DONE(kbd);
366fc16838fSAlex Hornung 
367fc9700a1SMatthew Dillon 		kbdmux_clear_state(kbd);
368fc16838fSAlex Hornung 		state->ks_mode = K_XLATE;
369fc16838fSAlex Hornung 	}
370fc16838fSAlex Hornung 
371fc16838fSAlex Hornung 	if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) {
372fc16838fSAlex Hornung 		kbd->kb_config = flags & ~KB_CONF_PROBE_ONLY;
373fc16838fSAlex Hornung 
374fc16838fSAlex Hornung 		kbdmux_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state);
375fc16838fSAlex Hornung 
376fc16838fSAlex Hornung 		delay[0] = kbd->kb_delay1;
377fc16838fSAlex Hornung 		delay[1] = kbd->kb_delay2;
378fc16838fSAlex Hornung 		kbdmux_ioctl(kbd, KDSETREPEAT, (caddr_t)delay);
379fc16838fSAlex Hornung 
3803d1294c7SPeeter Must #ifdef EVDEV_SUPPORT
3813d1294c7SPeeter Must 		/* register as evdev provider */
3823d1294c7SPeeter Must 		evdev = evdev_alloc();
3833d1294c7SPeeter Must 		evdev_set_name(evdev, "System keyboard multiplexer");
3843d1294c7SPeeter Must 		ksnprintf(phys_loc, NAMELEN, KEYBOARD_NAME"%d", unit);
3853d1294c7SPeeter Must 		evdev_set_phys(evdev, phys_loc);
3863d1294c7SPeeter Must 		evdev_set_id(evdev, BUS_VIRTUAL, 0, 0, 0);
3873d1294c7SPeeter Must 		evdev_set_methods(evdev, kbd, &kbdmux_evdev_methods);
3883d1294c7SPeeter Must 		evdev_support_event(evdev, EV_SYN);
3893d1294c7SPeeter Must 		evdev_support_event(evdev, EV_KEY);
3903d1294c7SPeeter Must 		evdev_support_event(evdev, EV_LED);
3913d1294c7SPeeter Must 		evdev_support_event(evdev, EV_REP);
3923d1294c7SPeeter Must 		evdev_support_all_known_keys(evdev);
3933d1294c7SPeeter Must 		evdev_support_led(evdev, LED_NUML);
3943d1294c7SPeeter Must 		evdev_support_led(evdev, LED_CAPSL);
3953d1294c7SPeeter Must 		evdev_support_led(evdev, LED_SCROLLL);
3963d1294c7SPeeter Must 
3973d1294c7SPeeter Must 		if (evdev_register(evdev))
3983d1294c7SPeeter Must 			evdev_free(evdev);
3993d1294c7SPeeter Must 		else
4003d1294c7SPeeter Must 			state->ks_evdev = evdev;
4013d1294c7SPeeter Must 		state->ks_evdev_state = 0;
4023d1294c7SPeeter Must #endif
4033d1294c7SPeeter Must 
404fc16838fSAlex Hornung 		KBD_INIT_DONE(kbd);
405fc16838fSAlex Hornung 	}
406fc16838fSAlex Hornung 
407fc16838fSAlex Hornung 	if (!KBD_IS_CONFIGURED(kbd)) {
408fc16838fSAlex Hornung 		if (kbd_register(kbd) < 0) {
409fc16838fSAlex Hornung 			error = ENXIO;
410fc16838fSAlex Hornung 			goto bad;
411fc16838fSAlex Hornung 		}
412fc16838fSAlex Hornung 
413fc16838fSAlex Hornung 		KBD_CONFIG_DONE(kbd);
414fc16838fSAlex Hornung 	}
415fc16838fSAlex Hornung 
416fc16838fSAlex Hornung 	return (0);
417fc16838fSAlex Hornung bad:
418fc16838fSAlex Hornung 	if (needfree) {
419fc16838fSAlex Hornung 		if (state != NULL)
420bcc53404SAlex Hornung 			kfree(state, M_KBDMUX);
421fc16838fSAlex Hornung 		if (keymap != NULL)
422bcc53404SAlex Hornung 			kfree(keymap, M_KBDMUX);
423fc16838fSAlex Hornung 		if (accmap != NULL)
424bcc53404SAlex Hornung 			kfree(accmap, M_KBDMUX);
425fc16838fSAlex Hornung 		if (fkeymap != NULL)
426bcc53404SAlex Hornung 			kfree(fkeymap, M_KBDMUX);
427fc16838fSAlex Hornung 		if (kbd != NULL) {
428bcc53404SAlex Hornung 			kfree(kbd, M_KBDMUX);
429fc16838fSAlex Hornung 			*kbdp = NULL;	/* insure ref doesn't leak to caller */
430fc16838fSAlex Hornung 		}
431fc16838fSAlex Hornung 	}
432fc16838fSAlex Hornung 
433fc16838fSAlex Hornung 	return (error);
434fc16838fSAlex Hornung }
435fc16838fSAlex Hornung 
436fc16838fSAlex Hornung /*
437fc16838fSAlex Hornung  * Finish using this keyboard
438fc9700a1SMatthew Dillon  *
439fc9700a1SMatthew Dillon  * NOTE: deregistration automatically unlocks lock.
440fc16838fSAlex Hornung  */
441fc16838fSAlex Hornung static int
kbdmux_term(keyboard_t * kbd)442fc16838fSAlex Hornung kbdmux_term(keyboard_t *kbd)
443fc16838fSAlex Hornung {
444fc16838fSAlex Hornung 	kbdmux_state_t	*state = (kbdmux_state_t *) kbd->kb_data;
445fc16838fSAlex Hornung 	kbdmux_kbd_t	*k;
446fc16838fSAlex Hornung 
447fc16838fSAlex Hornung 	/* wait for interrupt task */
44851b03d73SImre Vadász 	while (taskqueue_cancel(taskqueue_swi, &state->ks_task, NULL) != 0)
44951b03d73SImre Vadász 		taskqueue_drain(taskqueue_swi, &state->ks_task);
450fc16838fSAlex Hornung 
451fc16838fSAlex Hornung 	/* release all keyboards from the mux */
452fc16838fSAlex Hornung 	while ((k = SLIST_FIRST(&state->ks_kbds)) != NULL) {
453fc16838fSAlex Hornung 		kbd_release(k->kbd, &k->kbd);
454fc16838fSAlex Hornung 		SLIST_REMOVE_HEAD(&state->ks_kbds, next);
455fc16838fSAlex Hornung 
456fc16838fSAlex Hornung 		k->kbd = NULL;
457fc16838fSAlex Hornung 
458bcc53404SAlex Hornung 		kfree(k, M_KBDMUX);
459fc16838fSAlex Hornung 	}
460fc16838fSAlex Hornung 
461fc16838fSAlex Hornung 	kbd_unregister(kbd);
462fc16838fSAlex Hornung 
4633d1294c7SPeeter Must #ifdef EVDEV_SUPPORT
4643d1294c7SPeeter Must 	evdev_free(state->ks_evdev);
4653d1294c7SPeeter Must #endif
4663d1294c7SPeeter Must 
467fc16838fSAlex Hornung 	bzero(state, sizeof(*state));
468bcc53404SAlex Hornung 	kfree(state, M_KBDMUX);
469fc16838fSAlex Hornung 
470bcc53404SAlex Hornung 	kfree(kbd->kb_keymap, M_KBDMUX);
471bcc53404SAlex Hornung 	kfree(kbd->kb_accentmap, M_KBDMUX);
472bcc53404SAlex Hornung 	kfree(kbd->kb_fkeytab, M_KBDMUX);
473bcc53404SAlex Hornung 	kfree(kbd, M_KBDMUX);
474fc16838fSAlex Hornung 
475fc16838fSAlex Hornung 	return (0);
476fc16838fSAlex Hornung }
477fc16838fSAlex Hornung 
478fc16838fSAlex Hornung /*
479fc16838fSAlex Hornung  * Keyboard interrupt routine
480fc16838fSAlex Hornung  */
481fc16838fSAlex Hornung static int
kbdmux_intr(keyboard_t * kbd,void * arg)482fc16838fSAlex Hornung kbdmux_intr(keyboard_t *kbd, void *arg)
483fc16838fSAlex Hornung {
484fc16838fSAlex Hornung 	int	c;
485fc16838fSAlex Hornung 
486fc16838fSAlex Hornung 	if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) {
487fc16838fSAlex Hornung 		/* let the callback function to process the input */
488fc16838fSAlex Hornung 		(*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT,
489fc16838fSAlex Hornung 					    kbd->kb_callback.kc_arg);
490fc16838fSAlex Hornung 	} else {
491fc16838fSAlex Hornung 		/* read and discard the input; no one is waiting for input */
492fc16838fSAlex Hornung 		do {
493fc16838fSAlex Hornung 			c = kbdmux_read_char(kbd, FALSE);
494fc16838fSAlex Hornung 		} while (c != NOKEY);
495fc16838fSAlex Hornung 	}
496fc16838fSAlex Hornung 
497fc16838fSAlex Hornung 	return (0);
498fc16838fSAlex Hornung }
499fc16838fSAlex Hornung 
500fc16838fSAlex Hornung /*
501fc16838fSAlex Hornung  * Test the interface to the device
502fc16838fSAlex Hornung  */
503fc16838fSAlex Hornung static int
kbdmux_test_if(keyboard_t * kbd)504fc16838fSAlex Hornung kbdmux_test_if(keyboard_t *kbd)
505fc16838fSAlex Hornung {
506fc16838fSAlex Hornung 	return (0);
507fc16838fSAlex Hornung }
508fc16838fSAlex Hornung 
509fc16838fSAlex Hornung /*
510fc16838fSAlex Hornung  * Enable the access to the device; until this function is called,
511fc16838fSAlex Hornung  * the client cannot read from the keyboard.
512fc16838fSAlex Hornung  */
513fc16838fSAlex Hornung static int
kbdmux_enable(keyboard_t * kbd)514fc16838fSAlex Hornung kbdmux_enable(keyboard_t *kbd)
515fc16838fSAlex Hornung {
516fc16838fSAlex Hornung 	KBD_ACTIVATE(kbd);
517fc16838fSAlex Hornung 	return (0);
518fc16838fSAlex Hornung }
519fc16838fSAlex Hornung 
520fc16838fSAlex Hornung /*
521fc16838fSAlex Hornung  * Disallow the access to the device
522fc16838fSAlex Hornung  */
523fc16838fSAlex Hornung static int
kbdmux_disable(keyboard_t * kbd)524fc16838fSAlex Hornung kbdmux_disable(keyboard_t *kbd)
525fc16838fSAlex Hornung {
526fc16838fSAlex Hornung 	KBD_DEACTIVATE(kbd);
527fc16838fSAlex Hornung 	return (0);
528fc16838fSAlex Hornung }
529fc16838fSAlex Hornung 
530fc16838fSAlex Hornung /*
531fc16838fSAlex Hornung  * Read one byte from the keyboard if it's allowed
532fc16838fSAlex Hornung  */
533fc16838fSAlex Hornung static int
kbdmux_read(keyboard_t * kbd,int wait)534fc16838fSAlex Hornung kbdmux_read(keyboard_t *kbd, int wait)
535fc16838fSAlex Hornung {
536fc16838fSAlex Hornung 	kbdmux_state_t	*state = (kbdmux_state_t *) kbd->kb_data;
53722ff886eSAlex Hornung 	int		 c, ret;
538fc16838fSAlex Hornung 
539786d7861SMatthew Dillon 	do {
540fc16838fSAlex Hornung 		c = kbdmux_kbd_getc(state);
541786d7861SMatthew Dillon 	} while (c == -1 && wait);
542fc16838fSAlex Hornung 
543fc16838fSAlex Hornung 	if (c != -1)
544fc16838fSAlex Hornung 		kbd->kb_count++;
545fc16838fSAlex Hornung 
54622ff886eSAlex Hornung 	ret = (KBD_IS_ACTIVE(kbd)? c : -1);
54722ff886eSAlex Hornung 
54822ff886eSAlex Hornung 	return ret;
549fc16838fSAlex Hornung }
550fc16838fSAlex Hornung 
551fc16838fSAlex Hornung /*
552fc16838fSAlex Hornung  * Check if data is waiting
553fc16838fSAlex Hornung  */
554fc16838fSAlex Hornung static int
kbdmux_check(keyboard_t * kbd)555fc16838fSAlex Hornung kbdmux_check(keyboard_t *kbd)
556fc16838fSAlex Hornung {
557fc16838fSAlex Hornung 	kbdmux_state_t	*state = (kbdmux_state_t *) kbd->kb_data;
558fc16838fSAlex Hornung 	int		 ready;
559fc16838fSAlex Hornung 
560fc9700a1SMatthew Dillon 	if (!KBD_IS_ACTIVE(kbd))
561fc16838fSAlex Hornung 		return (FALSE);
562fc16838fSAlex Hornung 
563fc16838fSAlex Hornung 	ready = (state->ks_inq_length > 0) ? TRUE : FALSE;
564fc16838fSAlex Hornung 
565fc16838fSAlex Hornung 	return (ready);
566fc16838fSAlex Hornung }
567fc16838fSAlex Hornung 
568fc16838fSAlex Hornung /*
569fc16838fSAlex Hornung  * Read char from the keyboard (stolen from atkbd.c)
570786d7861SMatthew Dillon  *
571786d7861SMatthew Dillon  * Note: We do not attempt to detect the case where no keyboards are
572786d7861SMatthew Dillon  *	 present in the wait case.  If the kernel is sitting at the
573786d7861SMatthew Dillon  *	 debugger prompt we want someone to be able to plug in a keyboard
574786d7861SMatthew Dillon  *	 and have it work, and not just panic or fall through or do
575786d7861SMatthew Dillon  *	 something equally nasty.
576fc16838fSAlex Hornung  */
577fc16838fSAlex Hornung static u_int
kbdmux_read_char(keyboard_t * kbd,int wait)578fc16838fSAlex Hornung kbdmux_read_char(keyboard_t *kbd, int wait)
579fc16838fSAlex Hornung {
580fc16838fSAlex Hornung 	kbdmux_state_t	*state = (kbdmux_state_t *) kbd->kb_data;
581fc16838fSAlex Hornung 	u_int		 action;
582fc16838fSAlex Hornung 	int		 scancode, keycode;
583fc16838fSAlex Hornung 
584fc16838fSAlex Hornung next_code:
585fc16838fSAlex Hornung 
586fc16838fSAlex Hornung 	/* do we have a composed char to return? */
587fc16838fSAlex Hornung 	if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) {
588fc16838fSAlex Hornung 		action = state->ks_composed_char;
589fc16838fSAlex Hornung 		state->ks_composed_char = 0;
590fc16838fSAlex Hornung 		if (action > UCHAR_MAX) {
591fc16838fSAlex Hornung 			return (ERRKEY);
592fc16838fSAlex Hornung 		}
593fc16838fSAlex Hornung 		return (action);
594fc16838fSAlex Hornung 	}
595fc16838fSAlex Hornung 
596786d7861SMatthew Dillon 	/*
597786d7861SMatthew Dillon 	 * See if there is something in the keyboard queue
598786d7861SMatthew Dillon 	 */
599fc16838fSAlex Hornung 	scancode = kbdmux_kbd_getc(state);
600786d7861SMatthew Dillon 
601fc16838fSAlex Hornung 	if (scancode == -1) {
602fc16838fSAlex Hornung 		if (state->ks_flags & POLLING) {
603fc16838fSAlex Hornung 			kbdmux_kbd_t	*k;
604fc16838fSAlex Hornung 
605fc16838fSAlex Hornung 			SLIST_FOREACH(k, &state->ks_kbds, next) {
606bcc53404SAlex Hornung 				while (kbd_check_char(k->kbd)) {
607bcc53404SAlex Hornung 					scancode = kbd_read_char(k->kbd, 0);
608fc16838fSAlex Hornung 					if (scancode == ERRKEY)
609fc16838fSAlex Hornung 						continue;
610786d7861SMatthew Dillon 					if (scancode == NOKEY)
611786d7861SMatthew Dillon 						break;
612fc16838fSAlex Hornung 					if (!KBD_IS_BUSY(k->kbd))
613fc16838fSAlex Hornung 						continue;
614fc16838fSAlex Hornung 					kbdmux_kbd_putc(state, scancode);
615fc16838fSAlex Hornung 				}
616fc16838fSAlex Hornung 			}
617fc16838fSAlex Hornung 
618fc16838fSAlex Hornung 			if (state->ks_inq_length > 0)
619fc16838fSAlex Hornung 				goto next_code;
620786d7861SMatthew Dillon 			if (wait)
621786d7861SMatthew Dillon 				goto next_code;
622786d7861SMatthew Dillon 		} else {
623786d7861SMatthew Dillon 			if (wait) {
624*f57dbfa8SMatthew Dillon 				if (kbd->kb_flags & KB_POLLED) {
625*f57dbfa8SMatthew Dillon 					tsleep(&state->ks_task, PCATCH,
6262d57b795SSascha Wildner 						"kbdwai", hz/10);
627*f57dbfa8SMatthew Dillon 				} else {
628*f57dbfa8SMatthew Dillon 					lksleep(&state->ks_task,
629*f57dbfa8SMatthew Dillon 						&kbd->kb_lock, PCATCH,
630*f57dbfa8SMatthew Dillon 						"kbdwai", hz/10);
631*f57dbfa8SMatthew Dillon 				}
632786d7861SMatthew Dillon 				goto next_code;
633786d7861SMatthew Dillon 			}
634fc16838fSAlex Hornung 		}
635fc16838fSAlex Hornung 		return (NOKEY);
636fc16838fSAlex Hornung 	}
637fc16838fSAlex Hornung 
638fc16838fSAlex Hornung 	kbd->kb_count++;
639fc16838fSAlex Hornung 
6403d1294c7SPeeter Must #ifdef EVDEV_SUPPORT
6413d1294c7SPeeter Must 	/* push evdev event */
6423d1294c7SPeeter Must 	if (evdev_rcpt_mask & EVDEV_RCPT_KBDMUX && state->ks_evdev != NULL) {
6433d1294c7SPeeter Must 		uint16_t key = evdev_scancode2key(&state->ks_evdev_state,
6443d1294c7SPeeter Must 		    scancode);
6453d1294c7SPeeter Must 
6463d1294c7SPeeter Must 		if (key != KEY_RESERVED) {
6473d1294c7SPeeter Must 			evdev_push_event(state->ks_evdev, EV_KEY,
6483d1294c7SPeeter Must 			    key, scancode & 0x80 ? 0 : 1);
6493d1294c7SPeeter Must 			evdev_sync(state->ks_evdev);
6503d1294c7SPeeter Must 		}
6513d1294c7SPeeter Must 	}
6523d1294c7SPeeter Must #endif
6533d1294c7SPeeter Must 
654fc16838fSAlex Hornung 	/* return the byte as is for the K_RAW mode */
655fc9700a1SMatthew Dillon 	if (state->ks_mode == K_RAW)
656fc16838fSAlex Hornung 		return (scancode);
657fc16838fSAlex Hornung 
658fc16838fSAlex Hornung 	/* translate the scan code into a keycode */
659fc16838fSAlex Hornung 	keycode = scancode & 0x7F;
660fc16838fSAlex Hornung 	switch (state->ks_prefix) {
661fc16838fSAlex Hornung 	case 0x00:	/* normal scancode */
662fc16838fSAlex Hornung 		switch(scancode) {
663fc16838fSAlex Hornung 		case 0xB8:	/* left alt (compose key) released */
664fc16838fSAlex Hornung 			if (state->ks_flags & COMPOSE) {
665fc16838fSAlex Hornung 				state->ks_flags &= ~COMPOSE;
666fc16838fSAlex Hornung 				if (state->ks_composed_char > UCHAR_MAX)
667fc16838fSAlex Hornung 					state->ks_composed_char = 0;
668fc16838fSAlex Hornung 			}
669fc16838fSAlex Hornung 			break;
670fc16838fSAlex Hornung 		case 0x38:	/* left alt (compose key) pressed */
671fc16838fSAlex Hornung 			if (!(state->ks_flags & COMPOSE)) {
672fc16838fSAlex Hornung 				state->ks_flags |= COMPOSE;
673fc16838fSAlex Hornung 				state->ks_composed_char = 0;
674fc16838fSAlex Hornung 			}
675fc16838fSAlex Hornung 			break;
676fc16838fSAlex Hornung 		case 0xE0:
677fc16838fSAlex Hornung 		case 0xE1:
678fc16838fSAlex Hornung 			state->ks_prefix = scancode;
679fc16838fSAlex Hornung 			goto next_code;
680fc16838fSAlex Hornung 		}
681fc16838fSAlex Hornung 		break;
682fc16838fSAlex Hornung 	case 0xE0:      /* 0xE0 prefix */
683fc16838fSAlex Hornung 		state->ks_prefix = 0;
684fc16838fSAlex Hornung 		switch (keycode) {
685fc16838fSAlex Hornung 		case 0x1C:	/* right enter key */
686fc16838fSAlex Hornung 			keycode = 0x59;
687fc16838fSAlex Hornung 			break;
688fc16838fSAlex Hornung 		case 0x1D:	/* right ctrl key */
689fc16838fSAlex Hornung 			keycode = 0x5A;
690fc16838fSAlex Hornung 			break;
691fc16838fSAlex Hornung 		case 0x35:	/* keypad divide key */
692fc16838fSAlex Hornung 			keycode = 0x5B;
693fc16838fSAlex Hornung 			break;
694fc16838fSAlex Hornung 		case 0x37:	/* print scrn key */
695fc16838fSAlex Hornung 			keycode = 0x5C;
696fc16838fSAlex Hornung 			break;
697fc16838fSAlex Hornung 		case 0x38:	/* right alt key (alt gr) */
698fc16838fSAlex Hornung 			keycode = 0x5D;
699fc16838fSAlex Hornung 			break;
700fc16838fSAlex Hornung 		case 0x46:	/* ctrl-pause/break on AT 101 (see below) */
701fc16838fSAlex Hornung 			keycode = 0x68;
702fc16838fSAlex Hornung 			break;
703fc16838fSAlex Hornung 		case 0x47:	/* grey home key */
704fc16838fSAlex Hornung 			keycode = 0x5E;
705fc16838fSAlex Hornung 			break;
706fc16838fSAlex Hornung 		case 0x48:	/* grey up arrow key */
707fc16838fSAlex Hornung 			keycode = 0x5F;
708fc16838fSAlex Hornung 			break;
709fc16838fSAlex Hornung 		case 0x49:	/* grey page up key */
710fc16838fSAlex Hornung 			keycode = 0x60;
711fc16838fSAlex Hornung 			break;
712fc16838fSAlex Hornung 		case 0x4B:	/* grey left arrow key */
713fc16838fSAlex Hornung 			keycode = 0x61;
714fc16838fSAlex Hornung 			break;
715fc16838fSAlex Hornung 		case 0x4D:	/* grey right arrow key */
716fc16838fSAlex Hornung 			keycode = 0x62;
717fc16838fSAlex Hornung 			break;
718fc16838fSAlex Hornung 		case 0x4F:	/* grey end key */
719fc16838fSAlex Hornung 			keycode = 0x63;
720fc16838fSAlex Hornung 			break;
721fc16838fSAlex Hornung 		case 0x50:	/* grey down arrow key */
722fc16838fSAlex Hornung 			keycode = 0x64;
723fc16838fSAlex Hornung 			break;
724fc16838fSAlex Hornung 		case 0x51:	/* grey page down key */
725fc16838fSAlex Hornung 			keycode = 0x65;
726fc16838fSAlex Hornung 			break;
727fc16838fSAlex Hornung 		case 0x52:	/* grey insert key */
728fc16838fSAlex Hornung 			keycode = 0x66;
729fc16838fSAlex Hornung 			break;
730fc16838fSAlex Hornung 		case 0x53:	/* grey delete key */
731fc16838fSAlex Hornung 			keycode = 0x67;
732fc16838fSAlex Hornung 			break;
733fc16838fSAlex Hornung 		/* the following 3 are only used on the MS "Natural" keyboard */
734fc16838fSAlex Hornung 		case 0x5b:	/* left Window key */
735fc16838fSAlex Hornung 			keycode = 0x69;
736fc16838fSAlex Hornung 			break;
737fc16838fSAlex Hornung 		case 0x5c:	/* right Window key */
738fc16838fSAlex Hornung 			keycode = 0x6a;
739fc16838fSAlex Hornung 			break;
740fc16838fSAlex Hornung 		case 0x5d:	/* menu key */
741fc16838fSAlex Hornung 			keycode = 0x6b;
742fc16838fSAlex Hornung 			break;
743fc16838fSAlex Hornung 		case 0x5e:	/* power key */
744fc16838fSAlex Hornung 			keycode = 0x6d;
745fc16838fSAlex Hornung 			break;
746fc16838fSAlex Hornung 		case 0x5f:	/* sleep key */
747fc16838fSAlex Hornung 			keycode = 0x6e;
748fc16838fSAlex Hornung 			break;
749fc16838fSAlex Hornung 		case 0x63:	/* wake key */
750fc16838fSAlex Hornung 			keycode = 0x6f;
751fc16838fSAlex Hornung 			break;
752fc16838fSAlex Hornung 		case 0x64:	/* [JP106USB] backslash, underscore */
753fc16838fSAlex Hornung 			keycode = 0x73;
754fc16838fSAlex Hornung 			break;
755fc16838fSAlex Hornung 		default:	/* ignore everything else */
756fc16838fSAlex Hornung 			goto next_code;
757fc16838fSAlex Hornung 		}
758fc16838fSAlex Hornung 		break;
759fc16838fSAlex Hornung 	case 0xE1:	/* 0xE1 prefix */
760fc16838fSAlex Hornung 		/*
761fc16838fSAlex Hornung 		 * The pause/break key on the 101 keyboard produces:
762fc16838fSAlex Hornung 		 * E1-1D-45 E1-9D-C5
763fc16838fSAlex Hornung 		 * Ctrl-pause/break produces:
764fc16838fSAlex Hornung 		 * E0-46 E0-C6 (See above.)
765fc16838fSAlex Hornung 		 */
766fc16838fSAlex Hornung 		state->ks_prefix = 0;
767fc16838fSAlex Hornung 		if (keycode == 0x1D)
768fc16838fSAlex Hornung 			state->ks_prefix = 0x1D;
769fc16838fSAlex Hornung 		goto next_code;
770fc16838fSAlex Hornung 		/* NOT REACHED */
771fc16838fSAlex Hornung 	case 0x1D:	/* pause / break */
772fc16838fSAlex Hornung 		state->ks_prefix = 0;
773fc16838fSAlex Hornung 		if (keycode != 0x45)
774fc16838fSAlex Hornung 			goto next_code;
775fc16838fSAlex Hornung 		keycode = 0x68;
776fc16838fSAlex Hornung 		break;
777fc16838fSAlex Hornung 	}
778fc16838fSAlex Hornung 
779fc16838fSAlex Hornung 	/* XXX assume 101/102 keys AT keyboard */
780fc16838fSAlex Hornung 	switch (keycode) {
781fc16838fSAlex Hornung 	case 0x5c:	/* print screen */
782fc16838fSAlex Hornung 		if (state->ks_flags & ALTS)
783fc16838fSAlex Hornung 			keycode = 0x54;	/* sysrq */
784fc16838fSAlex Hornung 		break;
785fc16838fSAlex Hornung 	case 0x68:	/* pause/break */
786fc16838fSAlex Hornung 		if (state->ks_flags & CTLS)
787fc16838fSAlex Hornung 			keycode = 0x6c;	/* break */
788fc16838fSAlex Hornung 		break;
789fc16838fSAlex Hornung 	}
790fc16838fSAlex Hornung 
791fc16838fSAlex Hornung 	/* return the key code in the K_CODE mode */
792fc9700a1SMatthew Dillon 	if (state->ks_mode == K_CODE)
793fc16838fSAlex Hornung 		return (keycode | (scancode & 0x80));
794fc16838fSAlex Hornung 
795fc16838fSAlex Hornung 	/* compose a character code */
796fc16838fSAlex Hornung 	if (state->ks_flags & COMPOSE) {
797fc16838fSAlex Hornung 		switch (keycode | (scancode & 0x80)) {
798fc16838fSAlex Hornung 		/* key pressed, process it */
799fc16838fSAlex Hornung 		case 0x47: case 0x48: case 0x49:	/* keypad 7,8,9 */
800fc16838fSAlex Hornung 			state->ks_composed_char *= 10;
801fc16838fSAlex Hornung 			state->ks_composed_char += keycode - 0x40;
802fc9700a1SMatthew Dillon 			if (state->ks_composed_char > UCHAR_MAX)
803fc16838fSAlex Hornung 				return (ERRKEY);
804fc16838fSAlex Hornung 			goto next_code;
805fc16838fSAlex Hornung 		case 0x4B: case 0x4C: case 0x4D:	/* keypad 4,5,6 */
806fc16838fSAlex Hornung 			state->ks_composed_char *= 10;
807fc16838fSAlex Hornung 			state->ks_composed_char += keycode - 0x47;
808fc9700a1SMatthew Dillon 			if (state->ks_composed_char > UCHAR_MAX)
809fc16838fSAlex Hornung 				return (ERRKEY);
810fc16838fSAlex Hornung 			goto next_code;
811fc16838fSAlex Hornung 		case 0x4F: case 0x50: case 0x51:	/* keypad 1,2,3 */
812fc16838fSAlex Hornung 			state->ks_composed_char *= 10;
813fc16838fSAlex Hornung 			state->ks_composed_char += keycode - 0x4E;
814fc9700a1SMatthew Dillon 			if (state->ks_composed_char > UCHAR_MAX)
815fc16838fSAlex Hornung 				return (ERRKEY);
816fc16838fSAlex Hornung 			goto next_code;
817fc16838fSAlex Hornung 		case 0x52:	/* keypad 0 */
818fc16838fSAlex Hornung 			state->ks_composed_char *= 10;
819fc9700a1SMatthew Dillon 			if (state->ks_composed_char > UCHAR_MAX)
820fc16838fSAlex Hornung 				return (ERRKEY);
821fc16838fSAlex Hornung 			goto next_code;
822fc16838fSAlex Hornung 
823fc16838fSAlex Hornung 		/* key released, no interest here */
824fc16838fSAlex Hornung 		case 0xC7: case 0xC8: case 0xC9:	/* keypad 7,8,9 */
825fc16838fSAlex Hornung 		case 0xCB: case 0xCC: case 0xCD:	/* keypad 4,5,6 */
826fc16838fSAlex Hornung 		case 0xCF: case 0xD0: case 0xD1:	/* keypad 1,2,3 */
827fc16838fSAlex Hornung 		case 0xD2:				/* keypad 0 */
828fc16838fSAlex Hornung 			goto next_code;
829fc16838fSAlex Hornung 
830fc16838fSAlex Hornung 		case 0x38:				/* left alt key */
831fc16838fSAlex Hornung 			break;
832fc16838fSAlex Hornung 
833fc16838fSAlex Hornung 		default:
834fc16838fSAlex Hornung 			if (state->ks_composed_char > 0) {
835fc16838fSAlex Hornung 				state->ks_flags &= ~COMPOSE;
836fc16838fSAlex Hornung 				state->ks_composed_char = 0;
837fc16838fSAlex Hornung 				return (ERRKEY);
838fc16838fSAlex Hornung 			}
839fc16838fSAlex Hornung 			break;
840fc16838fSAlex Hornung 		}
841fc16838fSAlex Hornung 	}
842fc16838fSAlex Hornung 
843fc16838fSAlex Hornung 	/* keycode to key action */
844fc16838fSAlex Hornung 	action = genkbd_keyaction(kbd, keycode, scancode & 0x80,
845fc16838fSAlex Hornung 			&state->ks_state, &state->ks_accents);
846fc16838fSAlex Hornung 	if (action == NOKEY)
847fc16838fSAlex Hornung 		goto next_code;
848fc16838fSAlex Hornung 
849fc16838fSAlex Hornung 	return (action);
850fc16838fSAlex Hornung }
851fc16838fSAlex Hornung 
852fc16838fSAlex Hornung /*
853fc16838fSAlex Hornung  * Check if char is waiting
854fc16838fSAlex Hornung  */
855fc16838fSAlex Hornung static int
kbdmux_check_char(keyboard_t * kbd)856fc16838fSAlex Hornung kbdmux_check_char(keyboard_t *kbd)
857fc16838fSAlex Hornung {
858fc16838fSAlex Hornung 	kbdmux_state_t	*state = (kbdmux_state_t *) kbd->kb_data;
859fc16838fSAlex Hornung 	int		 ready;
860fc16838fSAlex Hornung 
861fc9700a1SMatthew Dillon 	if (!KBD_IS_ACTIVE(kbd))
862fc16838fSAlex Hornung 		return (FALSE);
863fc16838fSAlex Hornung 
864fc16838fSAlex Hornung 	if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char != 0))
865fc16838fSAlex Hornung 		ready = TRUE;
866fc16838fSAlex Hornung 	else
867fc16838fSAlex Hornung 		ready = (state->ks_inq_length > 0) ? TRUE : FALSE;
868fc16838fSAlex Hornung 
869fc16838fSAlex Hornung 	return (ready);
870fc16838fSAlex Hornung }
871fc16838fSAlex Hornung 
872fc16838fSAlex Hornung /*
873fc16838fSAlex Hornung  * Keyboard ioctl's
874fc16838fSAlex Hornung  */
875fc16838fSAlex Hornung static int
kbdmux_ioctl(keyboard_t * kbd,u_long cmd,caddr_t arg)876fc16838fSAlex Hornung kbdmux_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
877fc16838fSAlex Hornung {
878fc16838fSAlex Hornung 	static int	 delays[] = {
879fc16838fSAlex Hornung 		250, 500, 750, 1000
880fc16838fSAlex Hornung 	};
881fc16838fSAlex Hornung 
882fc16838fSAlex Hornung 	static int	 rates[]  =  {
883fc16838fSAlex Hornung 		34,  38,  42,  46,  50,   55,  59,  63,
884fc16838fSAlex Hornung 		68,  76,  84,  92,  100, 110, 118, 126,
885fc16838fSAlex Hornung 		136, 152, 168, 184, 200, 220, 236, 252,
886fc16838fSAlex Hornung 		272, 304, 336, 368, 400, 440, 472, 504
887fc16838fSAlex Hornung 	};
888fc16838fSAlex Hornung 
889fc16838fSAlex Hornung 	kbdmux_state_t	*state = (kbdmux_state_t *) kbd->kb_data;
890fc16838fSAlex Hornung 	kbdmux_kbd_t	*k;
891fc16838fSAlex Hornung 	keyboard_info_t	*ki;
89227332c1bSImre Vadász 	int		 error = 0, mode, i;
893fc16838fSAlex Hornung 
894fc9700a1SMatthew Dillon 	if (state == NULL)
895fc16838fSAlex Hornung 		return (ENXIO);
896fc16838fSAlex Hornung 
897fc16838fSAlex Hornung 	switch (cmd) {
898fc16838fSAlex Hornung 	case KBADDKBD: /* add keyboard to the mux */
899fc16838fSAlex Hornung 		ki = (keyboard_info_t *) arg;
900fc16838fSAlex Hornung 
901fc16838fSAlex Hornung 		if (ki == NULL || ki->kb_unit < 0 || ki->kb_name[0] == '\0' ||
90222ff886eSAlex Hornung 		    strcmp(ki->kb_name, "*") == 0) {
903fc16838fSAlex Hornung 			return (EINVAL); /* bad input */
90422ff886eSAlex Hornung 		}
905fc16838fSAlex Hornung 
906fc16838fSAlex Hornung 		SLIST_FOREACH(k, &state->ks_kbds, next)
907fc16838fSAlex Hornung 			if (k->kbd->kb_unit == ki->kb_unit &&
908fc16838fSAlex Hornung 			    strcmp(k->kbd->kb_name, ki->kb_name) == 0)
909fc16838fSAlex Hornung 				break;
910fc16838fSAlex Hornung 
911fc9700a1SMatthew Dillon 		if (k != NULL)
912fc16838fSAlex Hornung 			return (0); /* keyboard already in the mux */
913fc16838fSAlex Hornung 
914bcc53404SAlex Hornung 		k = kmalloc(sizeof(*k), M_KBDMUX, M_NOWAIT | M_ZERO);
915fc9700a1SMatthew Dillon 		if (k == NULL)
916fc16838fSAlex Hornung 			return (ENOMEM); /* out of memory */
917fc16838fSAlex Hornung 
918fc16838fSAlex Hornung 		k->kbd = kbd_get_keyboard(
919fc16838fSAlex Hornung 				kbd_allocate(
920fc16838fSAlex Hornung 					ki->kb_name,
921fc16838fSAlex Hornung 					ki->kb_unit,
922fc16838fSAlex Hornung 					(void *) &k->kbd,
923fc16838fSAlex Hornung 					kbdmux_kbd_event, (void *) state));
924fc16838fSAlex Hornung 		if (k->kbd == NULL) {
925bcc53404SAlex Hornung 			kfree(k, M_KBDMUX);
926fc16838fSAlex Hornung 			return (EINVAL); /* bad keyboard */
927fc16838fSAlex Hornung 		}
928fc16838fSAlex Hornung 
929bcc53404SAlex Hornung 		kbd_enable(k->kbd);
930bcc53404SAlex Hornung 		kbd_clear_state(k->kbd);
931fc16838fSAlex Hornung 
932fc16838fSAlex Hornung 		/* set K_RAW mode on slave keyboard */
933fc16838fSAlex Hornung 		mode = K_RAW;
934bcc53404SAlex Hornung 		error = kbd_ioctl(k->kbd, KDSKBMODE, (caddr_t)&mode);
935fc16838fSAlex Hornung 		if (error == 0) {
936fc16838fSAlex Hornung 			/* set lock keys state on slave keyboard */
937fc16838fSAlex Hornung 			mode = state->ks_state & LOCK_MASK;
938bcc53404SAlex Hornung 			error = kbd_ioctl(k->kbd, KDSKBSTATE, (caddr_t)&mode);
939fc16838fSAlex Hornung 		}
940fc16838fSAlex Hornung 
941fc16838fSAlex Hornung 		if (error != 0) {
942fc16838fSAlex Hornung 			kbd_release(k->kbd, &k->kbd);
943fc16838fSAlex Hornung 			k->kbd = NULL;
944bcc53404SAlex Hornung 			kfree(k, M_KBDMUX);
945fc16838fSAlex Hornung 			return (error); /* could not set mode */
946fc16838fSAlex Hornung 		}
947fc16838fSAlex Hornung 
948fc16838fSAlex Hornung 		SLIST_INSERT_HEAD(&state->ks_kbds, k, next);
949fc16838fSAlex Hornung 		break;
950fc16838fSAlex Hornung 
951fc16838fSAlex Hornung 	case KBRELKBD: /* release keyboard from the mux */
952fc16838fSAlex Hornung 		ki = (keyboard_info_t *) arg;
953fc16838fSAlex Hornung 
954fc16838fSAlex Hornung 		if (ki == NULL || ki->kb_unit < 0 || ki->kb_name[0] == '\0' ||
95522ff886eSAlex Hornung 		    strcmp(ki->kb_name, "*") == 0) {
956fc16838fSAlex Hornung 			return (EINVAL); /* bad input */
95722ff886eSAlex Hornung 		}
958fc16838fSAlex Hornung 
959fc16838fSAlex Hornung 		SLIST_FOREACH(k, &state->ks_kbds, next)
960fc16838fSAlex Hornung 			if (k->kbd->kb_unit == ki->kb_unit &&
961fc16838fSAlex Hornung 			    strcmp(k->kbd->kb_name, ki->kb_name) == 0)
962fc16838fSAlex Hornung 				break;
963fc16838fSAlex Hornung 
964fc16838fSAlex Hornung 		if (k != NULL) {
965fc16838fSAlex Hornung 			error = kbd_release(k->kbd, &k->kbd);
966fc16838fSAlex Hornung 			if (error == 0) {
967fc16838fSAlex Hornung 				SLIST_REMOVE(&state->ks_kbds, k, kbdmux_kbd, next);
968fc16838fSAlex Hornung 
969fc16838fSAlex Hornung 				k->kbd = NULL;
970fc16838fSAlex Hornung 
971bcc53404SAlex Hornung 				kfree(k, M_KBDMUX);
972fc16838fSAlex Hornung 			}
973fc16838fSAlex Hornung 		} else
974fc16838fSAlex Hornung 			error = ENXIO; /* keyboard is not in the mux */
975fc16838fSAlex Hornung 
976fc16838fSAlex Hornung 		break;
977fc16838fSAlex Hornung 
978fc16838fSAlex Hornung 	case KDGKBMODE: /* get kyboard mode */
979fc16838fSAlex Hornung 		*(int *)arg = state->ks_mode;
980fc16838fSAlex Hornung 		break;
981fc16838fSAlex Hornung 
982fc16838fSAlex Hornung 	case KDSKBMODE: /* set keyboard mode */
983fc16838fSAlex Hornung 		switch (*(int *)arg) {
984fc16838fSAlex Hornung 		case K_XLATE:
985fc16838fSAlex Hornung 			if (state->ks_mode != K_XLATE) {
986fc16838fSAlex Hornung 				/* make lock key state and LED state match */
987fc16838fSAlex Hornung 				state->ks_state &= ~LOCK_MASK;
988fc16838fSAlex Hornung 				state->ks_state |= KBD_LED_VAL(kbd);
989fc16838fSAlex Hornung                         }
990fc16838fSAlex Hornung                         /* FALLTHROUGH */
991fc16838fSAlex Hornung 
992fc16838fSAlex Hornung 		case K_RAW:
993fc16838fSAlex Hornung 		case K_CODE:
994fc16838fSAlex Hornung 			if (state->ks_mode != *(int *)arg) {
995fc9700a1SMatthew Dillon 				kbdmux_clear_state(kbd);
996fc16838fSAlex Hornung 				state->ks_mode = *(int *)arg;
997fc16838fSAlex Hornung 			}
998fc16838fSAlex Hornung 			break;
999fc16838fSAlex Hornung 
1000fc16838fSAlex Hornung                 default:
1001fc16838fSAlex Hornung 			error = EINVAL;
1002fc16838fSAlex Hornung 			break;
1003fc16838fSAlex Hornung 		}
1004fc16838fSAlex Hornung 		break;
1005fc16838fSAlex Hornung 
1006fc16838fSAlex Hornung 	case KDGETLED: /* get keyboard LED */
1007fc16838fSAlex Hornung 		*(int *)arg = KBD_LED_VAL(kbd);
1008fc16838fSAlex Hornung 		break;
1009fc16838fSAlex Hornung 
1010fc16838fSAlex Hornung 	case KDSETLED: /* set keyboard LED */
1011fc16838fSAlex Hornung 		/* NOTE: lock key state in ks_state won't be changed */
1012fc9700a1SMatthew Dillon 		if (*(int *)arg & ~LOCK_MASK)
1013fc16838fSAlex Hornung 			return (EINVAL);
1014fc16838fSAlex Hornung 
1015fc16838fSAlex Hornung 		KBD_LED_VAL(kbd) = *(int *)arg;
10163d1294c7SPeeter Must #ifdef EVDEV_SUPPORT
10173d1294c7SPeeter Must 		if (state->ks_evdev != NULL &&
10183d1294c7SPeeter Must 		    evdev_rcpt_mask & EVDEV_RCPT_KBDMUX)
10193d1294c7SPeeter Must 			evdev_push_leds(state->ks_evdev, *(int *)arg);
10203d1294c7SPeeter Must #endif
1021fc16838fSAlex Hornung 		/* KDSETLED on all slave keyboards */
1022fc16838fSAlex Hornung 		SLIST_FOREACH(k, &state->ks_kbds, next)
1023bcc53404SAlex Hornung 			kbd_ioctl(k->kbd, KDSETLED, arg);
1024fc16838fSAlex Hornung 		break;
1025fc16838fSAlex Hornung 
1026fc16838fSAlex Hornung 	case KDGKBSTATE: /* get lock key state */
1027fc16838fSAlex Hornung 		*(int *)arg = state->ks_state & LOCK_MASK;
1028fc16838fSAlex Hornung 		break;
1029fc16838fSAlex Hornung 
1030fc16838fSAlex Hornung 	case KDSKBSTATE: /* set lock key state */
1031fc9700a1SMatthew Dillon 		if (*(int *)arg & ~LOCK_MASK)
1032fc16838fSAlex Hornung 			return (EINVAL);
1033fc16838fSAlex Hornung 
1034fc16838fSAlex Hornung 		state->ks_state &= ~LOCK_MASK;
1035fc16838fSAlex Hornung 		state->ks_state |= *(int *)arg;
1036fc16838fSAlex Hornung 
1037fc16838fSAlex Hornung 		/* KDSKBSTATE on all slave keyboards */
1038fc16838fSAlex Hornung 		SLIST_FOREACH(k, &state->ks_kbds, next)
1039bcc53404SAlex Hornung 			kbd_ioctl(k->kbd, KDSKBSTATE, arg);
1040fc16838fSAlex Hornung 
1041fc16838fSAlex Hornung 		return (kbdmux_ioctl(kbd, KDSETLED, arg));
1042fc16838fSAlex Hornung 		/* NOT REACHED */
1043fc16838fSAlex Hornung 
1044fc16838fSAlex Hornung 	case KDSETREPEAT: /* set keyboard repeat rate (new interface) */
1045fc16838fSAlex Hornung 		/* lookup delay */
1046c157ff7aSSascha Wildner 		for (i = NELEM(delays) - 1; i > 0; i --)
1047fc16838fSAlex Hornung 			if (((int *)arg)[0] >= delays[i])
1048fc16838fSAlex Hornung 				break;
1049fc16838fSAlex Hornung 		mode = i << 5;
1050fc16838fSAlex Hornung 
1051fc16838fSAlex Hornung 		/* lookup rate */
1052c157ff7aSSascha Wildner 		for (i = NELEM(rates) - 1; i > 0; i --)
1053fc16838fSAlex Hornung 			if (((int *)arg)[1] >= rates[i])
1054fc16838fSAlex Hornung 				break;
1055fc16838fSAlex Hornung 		mode |= i;
1056fc16838fSAlex Hornung 
1057fc9700a1SMatthew Dillon 		if (mode & ~0x7f)
1058fc16838fSAlex Hornung 			return (EINVAL);
1059fc16838fSAlex Hornung 
1060fc16838fSAlex Hornung 		kbd->kb_delay1 = delays[(mode >> 5) & 3];
1061fc16838fSAlex Hornung 		kbd->kb_delay2 = rates[mode & 0x1f];
10623d1294c7SPeeter Must #ifdef EVDEV_SUPPORT
10633d1294c7SPeeter Must 		if (state->ks_evdev != NULL &&
10643d1294c7SPeeter Must 		    evdev_rcpt_mask & EVDEV_RCPT_KBDMUX)
10653d1294c7SPeeter Must 			evdev_push_repeats(state->ks_evdev, kbd);
10663d1294c7SPeeter Must #endif
1067fc16838fSAlex Hornung 		/* perform command on all slave keyboards */
1068fc16838fSAlex Hornung 		SLIST_FOREACH(k, &state->ks_kbds, next)
1069bcc53404SAlex Hornung 			kbd_ioctl(k->kbd, cmd, arg);
1070fc16838fSAlex Hornung 		break;
1071fc16838fSAlex Hornung 
1072fc16838fSAlex Hornung 	case PIO_KEYMAP:	/* set keyboard translation table */
1073fc16838fSAlex Hornung 	case PIO_KEYMAPENT:	/* set keyboard translation table entry */
1074fc16838fSAlex Hornung 	case PIO_DEADKEYMAP:	/* set accent key translation table */
1075fc16838fSAlex Hornung                 state->ks_accents = 0;
1076fc16838fSAlex Hornung 
1077fc16838fSAlex Hornung 		/* perform command on all slave keyboards */
1078fc16838fSAlex Hornung 		SLIST_FOREACH(k, &state->ks_kbds, next)
1079bcc53404SAlex Hornung 			kbd_ioctl(k->kbd, cmd, arg);
1080fc16838fSAlex Hornung                 /* FALLTHROUGH */
1081fc16838fSAlex Hornung 
1082fc16838fSAlex Hornung 	default:
1083fc16838fSAlex Hornung 		error = genkbd_commonioctl(kbd, cmd, arg);
1084fc16838fSAlex Hornung 		break;
1085fc16838fSAlex Hornung 	}
1086fc16838fSAlex Hornung 	return (error);
1087fc16838fSAlex Hornung }
1088fc16838fSAlex Hornung 
1089fc16838fSAlex Hornung /*
1090fc16838fSAlex Hornung  * Lock the access to the keyboard
1091fc16838fSAlex Hornung  */
1092fc16838fSAlex Hornung static int
kbdmux_lock(keyboard_t * kbd,int lock)1093fc16838fSAlex Hornung kbdmux_lock(keyboard_t *kbd, int lock)
1094fc16838fSAlex Hornung {
1095fc16838fSAlex Hornung 	return (1); /* XXX */
1096fc16838fSAlex Hornung }
1097fc16838fSAlex Hornung 
1098fc16838fSAlex Hornung /*
1099fc16838fSAlex Hornung  * Clear the internal state of the keyboard
1100fc9700a1SMatthew Dillon  *
1101fc9700a1SMatthew Dillon  * NOTE: May be called unlocked from init
1102fc16838fSAlex Hornung  */
1103fc16838fSAlex Hornung static void
kbdmux_clear_state(keyboard_t * kbd)1104fc9700a1SMatthew Dillon kbdmux_clear_state(keyboard_t *kbd)
1105fc16838fSAlex Hornung {
1106fc9700a1SMatthew Dillon 	kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
1107fc16838fSAlex Hornung 
1108fc16838fSAlex Hornung 	state->ks_flags &= ~(COMPOSE|POLLING);
1109fc16838fSAlex Hornung 	state->ks_state &= LOCK_MASK;	/* preserve locking key state */
1110fc16838fSAlex Hornung 	state->ks_accents = 0;
1111fc16838fSAlex Hornung 	state->ks_composed_char = 0;
1112fc16838fSAlex Hornung /*	state->ks_prefix = 0;		XXX */
1113fc16838fSAlex Hornung 	state->ks_inq_length = 0;
1114fc16838fSAlex Hornung }
1115fc16838fSAlex Hornung 
1116fc16838fSAlex Hornung /*
1117fc16838fSAlex Hornung  * Save the internal state
1118fc16838fSAlex Hornung  */
1119fc16838fSAlex Hornung static int
kbdmux_get_state(keyboard_t * kbd,void * buf,size_t len)1120fc16838fSAlex Hornung kbdmux_get_state(keyboard_t *kbd, void *buf, size_t len)
1121fc16838fSAlex Hornung {
1122fc16838fSAlex Hornung 	if (len == 0)
1123fc16838fSAlex Hornung 		return (sizeof(kbdmux_state_t));
1124fc16838fSAlex Hornung 	if (len < sizeof(kbdmux_state_t))
1125fc16838fSAlex Hornung 		return (-1);
1126fc16838fSAlex Hornung 
1127fc16838fSAlex Hornung 	bcopy(kbd->kb_data, buf, sizeof(kbdmux_state_t)); /* XXX locking? */
1128fc16838fSAlex Hornung 
1129fc16838fSAlex Hornung 	return (0);
1130fc16838fSAlex Hornung }
1131fc16838fSAlex Hornung 
1132fc16838fSAlex Hornung /*
1133fc16838fSAlex Hornung  * Set the internal state
1134fc16838fSAlex Hornung  */
1135fc16838fSAlex Hornung static int
kbdmux_set_state(keyboard_t * kbd,void * buf,size_t len)1136fc16838fSAlex Hornung kbdmux_set_state(keyboard_t *kbd, void *buf, size_t len)
1137fc16838fSAlex Hornung {
1138fc16838fSAlex Hornung 	if (len < sizeof(kbdmux_state_t))
1139fc16838fSAlex Hornung 		return (ENOMEM);
1140fc16838fSAlex Hornung 
1141fc16838fSAlex Hornung 	bcopy(buf, kbd->kb_data, sizeof(kbdmux_state_t)); /* XXX locking? */
1142fc16838fSAlex Hornung 
1143fc16838fSAlex Hornung 	return (0);
1144fc16838fSAlex Hornung }
1145fc16838fSAlex Hornung 
1146fc16838fSAlex Hornung /*
1147fc16838fSAlex Hornung  * Set polling
1148fc9700a1SMatthew Dillon  *
1149fc9700a1SMatthew Dillon  * Caller interlocks all keyboard calls.  We must not lock here.
1150fc16838fSAlex Hornung  */
1151fc16838fSAlex Hornung static int
kbdmux_poll(keyboard_t * kbd,int on)1152fc16838fSAlex Hornung kbdmux_poll(keyboard_t *kbd, int on)
1153fc16838fSAlex Hornung {
1154fc16838fSAlex Hornung 	kbdmux_state_t	*state = (kbdmux_state_t *) kbd->kb_data;
1155fc16838fSAlex Hornung 	kbdmux_kbd_t	*k;
1156fc16838fSAlex Hornung 
1157fc16838fSAlex Hornung 	if (on)
1158fc16838fSAlex Hornung 		state->ks_flags |= POLLING;
1159fc16838fSAlex Hornung 	else
1160fc16838fSAlex Hornung 		state->ks_flags &= ~POLLING;
1161fc16838fSAlex Hornung 
1162fc16838fSAlex Hornung 	/* set poll on slave keyboards */
1163fc16838fSAlex Hornung 	SLIST_FOREACH(k, &state->ks_kbds, next)
1164bcc53404SAlex Hornung 		kbd_poll(k->kbd, on);
1165fc16838fSAlex Hornung 
1166fc16838fSAlex Hornung 	return (0);
1167fc16838fSAlex Hornung }
1168fc16838fSAlex Hornung 
1169fc16838fSAlex Hornung /*****************************************************************************
1170fc16838fSAlex Hornung  *****************************************************************************
1171fc16838fSAlex Hornung  **                                    Module
1172fc16838fSAlex Hornung  *****************************************************************************
1173fc16838fSAlex Hornung  *****************************************************************************/
1174fc16838fSAlex Hornung 
1175fc16838fSAlex Hornung KEYBOARD_DRIVER(kbdmux, kbdmuxsw, kbdmux_configure);
1176fc16838fSAlex Hornung 
1177fc16838fSAlex Hornung static int
kbdmux_modevent(module_t mod,int type,void * data)1178fc16838fSAlex Hornung kbdmux_modevent(module_t mod, int type, void *data)
1179fc16838fSAlex Hornung {
1180fc16838fSAlex Hornung 	keyboard_switch_t	*sw;
1181fc16838fSAlex Hornung 	keyboard_t		*kbd;
1182fc16838fSAlex Hornung 	int			 error;
1183fc16838fSAlex Hornung 
1184fc16838fSAlex Hornung 	switch (type) {
1185fc16838fSAlex Hornung 	case MOD_LOAD:
1186fc16838fSAlex Hornung 		if ((error = kbd_add_driver(&kbdmux_kbd_driver)) != 0)
1187fc16838fSAlex Hornung 			break;
1188fc16838fSAlex Hornung 
1189fc16838fSAlex Hornung 		if ((sw = kbd_get_switch(KEYBOARD_NAME)) == NULL) {
1190fc16838fSAlex Hornung 			kbd_delete_driver(&kbdmux_kbd_driver);
1191fc16838fSAlex Hornung 			error = ENXIO;
1192fc16838fSAlex Hornung 			break;
1193fc16838fSAlex Hornung 		}
1194fc16838fSAlex Hornung 
1195fc16838fSAlex Hornung 		kbd = NULL;
1196fc16838fSAlex Hornung 
1197fc16838fSAlex Hornung 		if ((error = (*sw->probe)(0, NULL, 0)) != 0 ||
1198fc16838fSAlex Hornung 		    (error = (*sw->init)(0, &kbd, NULL, 0)) != 0) {
1199fc16838fSAlex Hornung 			kbd_delete_driver(&kbdmux_kbd_driver);
1200fc16838fSAlex Hornung 			break;
1201fc16838fSAlex Hornung 		}
1202fc16838fSAlex Hornung 
1203fc16838fSAlex Hornung #ifdef KBD_INSTALL_CDEV
1204fc16838fSAlex Hornung 		if ((error = kbd_attach(kbd)) != 0) {
1205fc16838fSAlex Hornung 			(*sw->term)(kbd);
1206fc16838fSAlex Hornung 			kbd_delete_driver(&kbdmux_kbd_driver);
1207fc16838fSAlex Hornung 			break;
1208fc16838fSAlex Hornung 		}
1209fc16838fSAlex Hornung #endif
1210fc16838fSAlex Hornung 
1211fc16838fSAlex Hornung 		if ((error = (*sw->enable)(kbd)) != 0) {
1212fc16838fSAlex Hornung 			(*sw->disable)(kbd);
1213fc16838fSAlex Hornung #ifdef KBD_INSTALL_CDEV
1214fc16838fSAlex Hornung 			kbd_detach(kbd);
1215fc16838fSAlex Hornung #endif
1216fc16838fSAlex Hornung 			(*sw->term)(kbd);
1217fc16838fSAlex Hornung 			kbd_delete_driver(&kbdmux_kbd_driver);
1218fc16838fSAlex Hornung 			break;
1219fc16838fSAlex Hornung 		}
1220fc16838fSAlex Hornung 		break;
1221fc16838fSAlex Hornung 
1222fc16838fSAlex Hornung 	case MOD_UNLOAD:
1223fc16838fSAlex Hornung 		if ((sw = kbd_get_switch(KEYBOARD_NAME)) == NULL)
1224fc16838fSAlex Hornung 			panic("kbd_get_switch(" KEYBOARD_NAME ") == NULL");
1225fc16838fSAlex Hornung 
1226fc16838fSAlex Hornung 		kbd = kbd_get_keyboard(kbd_find_keyboard(KEYBOARD_NAME, 0));
1227fc16838fSAlex Hornung 		if (kbd != NULL) {
1228fc16838fSAlex Hornung 			(*sw->disable)(kbd);
1229fc16838fSAlex Hornung #ifdef KBD_INSTALL_CDEV
1230fc16838fSAlex Hornung 			kbd_detach(kbd);
1231fc16838fSAlex Hornung #endif
1232fc16838fSAlex Hornung 			(*sw->term)(kbd);
1233fc16838fSAlex Hornung 			kbd_delete_driver(&kbdmux_kbd_driver);
1234fc16838fSAlex Hornung 		}
1235fc16838fSAlex Hornung 		error = 0;
1236fc16838fSAlex Hornung 		break;
1237fc16838fSAlex Hornung 
1238fc16838fSAlex Hornung 	default:
1239fc16838fSAlex Hornung 		error = EOPNOTSUPP;
1240fc16838fSAlex Hornung 		break;
1241fc16838fSAlex Hornung 	}
1242fc16838fSAlex Hornung 	return (error);
1243fc16838fSAlex Hornung }
1244fc16838fSAlex Hornung 
1245fc16838fSAlex Hornung DEV_MODULE(kbdmux, kbdmux_modevent, NULL);
12463d1294c7SPeeter Must #ifdef EVDEV_SUPPORT
12473d1294c7SPeeter Must MODULE_DEPEND(kbdmux, evdev, 1, 1, 1);
12483d1294c7SPeeter Must #endif
1249