kbdmux - make it work for us
[dragonfly.git] / sys / dev / misc / kbdmux / kbdmux.c
blob1ec93e668f128072a7acc1d3cd8a559fa565cdfd
1 /*
2 * kbdmux.c
3 */
5 /*-
6 * Copyright (c) 2005 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7 * All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
30 * $Id: kbdmux.c,v 1.4 2005/07/14 17:38:35 max Exp $
31 * $FreeBSD$
34 #include <sys/param.h>
35 #include <sys/bus.h>
36 #include <sys/conf.h>
37 #include <sys/consio.h>
38 #include <sys/fcntl.h>
39 #include <sys/kbio.h>
40 #include <sys/kernel.h>
41 #include <sys/limits.h>
42 #include <sys/lock.h>
43 #include <sys/malloc.h>
44 #include <sys/module.h>
45 #include <sys/mutex.h>
46 #include <sys/poll.h>
47 #include <sys/proc.h>
48 #include <sys/queue.h>
49 #include <sys/selinfo.h>
50 #include <sys/systm.h>
51 #include <sys/taskqueue.h>
52 #include <sys/uio.h>
53 #include <dev/misc/kbd/kbdreg.h>
54 #include <dev/misc/kbd/kbdtables.h>
56 #define KEYBOARD_NAME "kbdmux"
58 MALLOC_DECLARE(M_KBDMUX);
59 MALLOC_DEFINE(M_KBDMUX, KEYBOARD_NAME, "Keyboard multiplexor");
61 /*****************************************************************************
62 *****************************************************************************
63 ** Keyboard state
64 *****************************************************************************
65 *****************************************************************************/
67 #define KBDMUX_Q_SIZE 512 /* input queue size */
70 * XXX
71 * For now rely on Giant mutex to protect our data structures.
72 * Just like the rest of keyboard drivers and syscons(4) do.
73 * Note that callout is initialized as not MP-safe to make sure
74 * Giant is held.
75 * XXX: I don't think we are MP-Safing the callout, maybe we
76 * should, using get_mplock() around it? NFI
79 #if 0 /* not yet */
80 #error "This stuff still needs porting!"
81 #define KBDMUX_LOCK_DECL_GLOBAL \
82 struct mtx ks_lock
83 #define KBDMUX_LOCK_INIT(s) \
84 mtx_init(&(s)->ks_lock, "kbdmux", NULL, MTX_DEF|MTX_RECURSE)
85 #define KBDMUX_LOCK_DESTROY(s) \
86 mtx_destroy(&(s)->ks_lock)
87 #define KBDMUX_LOCK(s) \
88 mtx_lock(&(s)->ks_lock)
89 #define KBDMUX_UNLOCK(s) \
90 mtx_unlock(&(s)->ks_lock)
91 #define KBDMUX_LOCK_ASSERT(s, w) \
92 mtx_assert(&(s)->ks_lock, (w))
93 #define KBDMUX_SLEEP(s, f, d, t) \
94 msleep(&(s)->f, &(s)->ks_lock, PCATCH | (PZERO + 1), (d), (t))
95 #define KBDMUX_CALLOUT_INIT(s) \
96 callout_init_mtx(&(s)->ks_timo, &(s)->ks_lock, 0)
97 #define KBDMUX_QUEUE_INTR(s) \
98 taskqueue_enqueue(taskqueue_swi, &(s)->ks_task)
99 #else
100 #define KBDMUX_LOCK_DECL_GLOBAL
102 #define KBDMUX_LOCK_INIT(s)
104 #define KBDMUX_LOCK_DESTROY(s)
106 #define KBDMUX_LOCK(s)
108 #define KBDMUX_UNLOCK(s)
110 #define KBDMUX_LOCK_ASSERT(s, w)
112 #define KBDMUX_SLEEP(s, f, d, t) \
113 tsleep(&(s)->f, PCATCH | (84 + 1), (d), (t))
114 #define KBDMUX_CALLOUT_INIT(s) \
115 callout_init(&(s)->ks_timo)
116 #define KBDMUX_QUEUE_INTR(s) \
117 taskqueue_enqueue(taskqueue_swi, &(s)->ks_task)
118 #endif /* not yet */
121 * kbdmux keyboard
123 struct kbdmux_kbd
125 keyboard_t *kbd; /* keyboard */
126 SLIST_ENTRY(kbdmux_kbd) next; /* link to next */
129 typedef struct kbdmux_kbd kbdmux_kbd_t;
132 * kbdmux state
134 struct kbdmux_state
136 char ks_inq[KBDMUX_Q_SIZE]; /* input chars queue */
137 unsigned int ks_inq_start;
138 unsigned int ks_inq_length;
139 struct task ks_task; /* interrupt task */
140 struct callout ks_timo; /* timeout handler */
141 #define TICKS (hz) /* rate */
143 int ks_flags; /* flags */
144 #define COMPOSE (1 << 0) /* compose char flag */
145 #define POLLING (1 << 1) /* polling */
146 #define TASK (1 << 2) /* interrupt task queued */
148 int ks_mode; /* K_XLATE, K_RAW, K_CODE */
149 int ks_state; /* state */
150 int ks_accents; /* accent key index (> 0) */
151 u_int ks_composed_char; /* composed char code */
152 u_char ks_prefix; /* AT scan code prefix */
154 SLIST_HEAD(, kbdmux_kbd) ks_kbds; /* keyboards */
156 KBDMUX_LOCK_DECL_GLOBAL;
159 typedef struct kbdmux_state kbdmux_state_t;
161 /*****************************************************************************
162 *****************************************************************************
163 ** Helper functions
164 *****************************************************************************
165 *****************************************************************************/
167 static task_fn_t kbdmux_kbd_intr;
168 static timeout_t kbdmux_kbd_intr_timo;
169 static kbd_callback_func_t kbdmux_kbd_event;
171 static void
172 kbdmux_kbd_putc(kbdmux_state_t *state, char c)
174 unsigned int p;
176 if (state->ks_inq_length == KBDMUX_Q_SIZE)
177 return;
179 p = (state->ks_inq_start + state->ks_inq_length) % KBDMUX_Q_SIZE;
180 state->ks_inq[p] = c;
181 state->ks_inq_length++;
184 static int
185 kbdmux_kbd_getc(kbdmux_state_t *state)
187 unsigned char c;
189 if (state->ks_inq_length == 0)
190 return (-1);
192 c = state->ks_inq[state->ks_inq_start];
193 state->ks_inq_start = (state->ks_inq_start + 1) % KBDMUX_Q_SIZE;
194 state->ks_inq_length--;
196 return (c);
200 * Interrupt handler task
202 void
203 kbdmux_kbd_intr(void *xkbd, int pending)
205 keyboard_t *kbd = (keyboard_t *) xkbd;
206 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
208 kbd_intr(kbd, NULL);
210 KBDMUX_LOCK(state);
212 state->ks_flags &= ~TASK;
213 wakeup(&state->ks_task);
215 KBDMUX_UNLOCK(state);
219 * Schedule interrupt handler on timeout. Called with locked state.
221 void
222 kbdmux_kbd_intr_timo(void *xstate)
224 kbdmux_state_t *state = (kbdmux_state_t *) xstate;
226 KBDMUX_LOCK_ASSERT(state, MA_OWNED);
228 if (callout_pending(&state->ks_timo))
229 return; /* callout was reset */
231 if (!callout_active(&state->ks_timo))
232 return; /* callout was stopped */
234 callout_deactivate(&state->ks_timo);
236 /* queue interrupt task if needed */
237 if (state->ks_inq_length > 0 && !(state->ks_flags & TASK) &&
238 KBDMUX_QUEUE_INTR(state) == 0)
239 state->ks_flags |= TASK;
241 /* re-schedule timeout */
242 callout_reset(&state->ks_timo, TICKS, kbdmux_kbd_intr_timo, state);
246 * Process event from one of our keyboards
248 static int
249 kbdmux_kbd_event(keyboard_t *kbd, int event, void *arg)
251 kbdmux_state_t *state = (kbdmux_state_t *) arg;
253 switch (event) {
254 case KBDIO_KEYINPUT: {
255 int c;
257 KBDMUX_LOCK(state);
260 * Read all chars from the keyboard
262 * Turns out that atkbd(4) check_char() method may return
263 * "true" while read_char() method returns NOKEY. If this
264 * happens we could stuck in the loop below. Avoid this
265 * by breaking out of the loop if read_char() method returns
266 * NOKEY.
269 while (kbd_check_char(kbd)) {
270 c = kbd_read_char(kbd, 0);
271 if (c == NOKEY)
272 break;
273 if (c == ERRKEY)
274 continue; /* XXX ring bell */
275 if (!KBD_IS_BUSY(kbd))
276 continue; /* not open - discard the input */
278 kbdmux_kbd_putc(state, c);
281 /* queue interrupt task if needed */
282 if (state->ks_inq_length > 0 && !(state->ks_flags & TASK) &&
283 KBDMUX_QUEUE_INTR(state) == 0)
284 state->ks_flags |= TASK;
286 KBDMUX_UNLOCK(state);
287 } break;
289 case KBDIO_UNLOADING: {
290 kbdmux_kbd_t *k;
292 KBDMUX_LOCK(state);
294 SLIST_FOREACH(k, &state->ks_kbds, next)
295 if (k->kbd == kbd)
296 break;
298 if (k != NULL) {
299 kbd_release(k->kbd, &k->kbd);
300 SLIST_REMOVE(&state->ks_kbds, k, kbdmux_kbd, next);
302 k->kbd = NULL;
304 kfree(k, M_KBDMUX);
307 KBDMUX_UNLOCK(state);
308 } break;
310 default:
311 return (EINVAL);
312 /* NOT REACHED */
315 return (0);
318 /****************************************************************************
319 ****************************************************************************
320 ** Keyboard driver
321 ****************************************************************************
322 ****************************************************************************/
324 static int kbdmux_configure(int flags);
325 static kbd_probe_t kbdmux_probe;
326 static kbd_init_t kbdmux_init;
327 static kbd_term_t kbdmux_term;
328 static kbd_intr_t kbdmux_intr;
329 static kbd_test_if_t kbdmux_test_if;
330 static kbd_enable_t kbdmux_enable;
331 static kbd_disable_t kbdmux_disable;
332 static kbd_read_t kbdmux_read;
333 static kbd_check_t kbdmux_check;
334 static kbd_read_char_t kbdmux_read_char;
335 static kbd_check_char_t kbdmux_check_char;
336 static kbd_ioctl_t kbdmux_ioctl;
337 static kbd_lock_t kbdmux_lock;
338 static void kbdmux_clear_state_locked(kbdmux_state_t *state);
339 static kbd_clear_state_t kbdmux_clear_state;
340 static kbd_get_state_t kbdmux_get_state;
341 static kbd_set_state_t kbdmux_set_state;
342 static kbd_poll_mode_t kbdmux_poll;
344 static keyboard_switch_t kbdmuxsw = {
345 .probe = kbdmux_probe,
346 .init = kbdmux_init,
347 .term = kbdmux_term,
348 .intr = kbdmux_intr,
349 .test_if = kbdmux_test_if,
350 .enable = kbdmux_enable,
351 .disable = kbdmux_disable,
352 .read = kbdmux_read,
353 .check = kbdmux_check,
354 .read_char = kbdmux_read_char,
355 .check_char = kbdmux_check_char,
356 .ioctl = kbdmux_ioctl,
357 .lock = kbdmux_lock,
358 .clear_state = kbdmux_clear_state,
359 .get_state = kbdmux_get_state,
360 .set_state = kbdmux_set_state,
361 .get_fkeystr = genkbd_get_fkeystr,
362 .poll = kbdmux_poll,
363 .diag = genkbd_diag,
367 * Return the number of found keyboards
369 static int
370 kbdmux_configure(int flags)
372 return (1);
376 * Detect a keyboard
378 static int
379 kbdmux_probe(int unit, void *arg, int flags)
381 if (resource_disabled(KEYBOARD_NAME, unit))
382 return (ENXIO);
384 return (0);
388 * Reset and initialize the keyboard (stolen from atkbd.c)
390 static int
391 kbdmux_init(int unit, keyboard_t **kbdp, void *arg, int flags)
393 keyboard_t *kbd = NULL;
394 kbdmux_state_t *state = NULL;
395 keymap_t *keymap = NULL;
396 accentmap_t *accmap = NULL;
397 fkeytab_t *fkeymap = NULL;
398 int error, needfree, fkeymap_size, delay[2];
400 if (*kbdp == NULL) {
401 *kbdp = kbd = kmalloc(sizeof(*kbd), M_KBDMUX, M_NOWAIT | M_ZERO);
402 state = kmalloc(sizeof(*state), M_KBDMUX, M_NOWAIT | M_ZERO);
403 keymap = kmalloc(sizeof(key_map), M_KBDMUX, M_NOWAIT);
404 accmap = kmalloc(sizeof(accent_map), M_KBDMUX, M_NOWAIT);
405 fkeymap = kmalloc(sizeof(fkey_tab), M_KBDMUX, M_NOWAIT);
406 fkeymap_size = sizeof(fkey_tab)/sizeof(fkey_tab[0]);
407 needfree = 1;
409 if ((kbd == NULL) || (state == NULL) || (keymap == NULL) ||
410 (accmap == NULL) || (fkeymap == NULL)) {
411 error = ENOMEM;
412 goto bad;
415 KBDMUX_LOCK_INIT(state);
416 TASK_INIT(&state->ks_task, 0, kbdmux_kbd_intr, (void *) kbd);
417 KBDMUX_CALLOUT_INIT(state);
418 SLIST_INIT(&state->ks_kbds);
419 } else if (KBD_IS_INITIALIZED(*kbdp) && KBD_IS_CONFIGURED(*kbdp)) {
420 return (0);
421 } else {
422 kbd = *kbdp;
423 state = (kbdmux_state_t *) kbd->kb_data;
424 keymap = kbd->kb_keymap;
425 accmap = kbd->kb_accentmap;
426 fkeymap = kbd->kb_fkeytab;
427 fkeymap_size = kbd->kb_fkeytab_size;
428 needfree = 0;
431 if (!KBD_IS_PROBED(kbd)) {
432 /* XXX assume 101/102 keys keyboard */
433 kbd_init_struct(kbd, KEYBOARD_NAME, KB_101, unit, flags,
434 KB_PRI_MUX, 0, 0);
435 bcopy(&key_map, keymap, sizeof(key_map));
436 bcopy(&accent_map, accmap, sizeof(accent_map));
437 bcopy(fkey_tab, fkeymap,
438 imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab)));
439 kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size);
440 kbd->kb_data = (void *)state;
442 KBD_FOUND_DEVICE(kbd);
443 KBD_PROBE_DONE(kbd);
445 KBDMUX_LOCK(state);
446 kbdmux_clear_state_locked(state);
447 state->ks_mode = K_XLATE;
448 KBDMUX_UNLOCK(state);
451 if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) {
452 kbd->kb_config = flags & ~KB_CONF_PROBE_ONLY;
454 kbdmux_ioctl(kbd, KDSETLED, (caddr_t)&state->ks_state);
456 delay[0] = kbd->kb_delay1;
457 delay[1] = kbd->kb_delay2;
458 kbdmux_ioctl(kbd, KDSETREPEAT, (caddr_t)delay);
460 KBD_INIT_DONE(kbd);
463 if (!KBD_IS_CONFIGURED(kbd)) {
464 if (kbd_register(kbd) < 0) {
465 error = ENXIO;
466 goto bad;
469 KBD_CONFIG_DONE(kbd);
471 KBDMUX_LOCK(state);
472 callout_reset(&state->ks_timo, TICKS, kbdmux_kbd_intr_timo, state);
473 KBDMUX_UNLOCK(state);
476 return (0);
477 bad:
478 if (needfree) {
479 if (state != NULL)
480 kfree(state, M_KBDMUX);
481 if (keymap != NULL)
482 kfree(keymap, M_KBDMUX);
483 if (accmap != NULL)
484 kfree(accmap, M_KBDMUX);
485 if (fkeymap != NULL)
486 kfree(fkeymap, M_KBDMUX);
487 if (kbd != NULL) {
488 kfree(kbd, M_KBDMUX);
489 *kbdp = NULL; /* insure ref doesn't leak to caller */
493 return (error);
497 * Finish using this keyboard
499 static int
500 kbdmux_term(keyboard_t *kbd)
502 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
503 kbdmux_kbd_t *k;
505 KBDMUX_LOCK(state);
507 /* kill callout */
508 callout_stop(&state->ks_timo);
510 /* wait for interrupt task */
511 while (state->ks_flags & TASK)
512 KBDMUX_SLEEP(state, ks_task, "kbdmuxc", 0);
514 /* release all keyboards from the mux */
515 while ((k = SLIST_FIRST(&state->ks_kbds)) != NULL) {
516 kbd_release(k->kbd, &k->kbd);
517 SLIST_REMOVE_HEAD(&state->ks_kbds, next);
519 k->kbd = NULL;
521 kfree(k, M_KBDMUX);
524 KBDMUX_UNLOCK(state);
526 kbd_unregister(kbd);
528 KBDMUX_LOCK_DESTROY(state);
529 bzero(state, sizeof(*state));
530 kfree(state, M_KBDMUX);
532 kfree(kbd->kb_keymap, M_KBDMUX);
533 kfree(kbd->kb_accentmap, M_KBDMUX);
534 kfree(kbd->kb_fkeytab, M_KBDMUX);
535 kfree(kbd, M_KBDMUX);
537 return (0);
541 * Keyboard interrupt routine
543 static int
544 kbdmux_intr(keyboard_t *kbd, void *arg)
546 int c;
548 if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) {
549 /* let the callback function to process the input */
550 (*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT,
551 kbd->kb_callback.kc_arg);
552 } else {
553 /* read and discard the input; no one is waiting for input */
554 do {
555 c = kbdmux_read_char(kbd, FALSE);
556 } while (c != NOKEY);
559 return (0);
563 * Test the interface to the device
565 static int
566 kbdmux_test_if(keyboard_t *kbd)
568 return (0);
572 * Enable the access to the device; until this function is called,
573 * the client cannot read from the keyboard.
575 static int
576 kbdmux_enable(keyboard_t *kbd)
578 KBD_ACTIVATE(kbd);
579 return (0);
583 * Disallow the access to the device
585 static int
586 kbdmux_disable(keyboard_t *kbd)
588 KBD_DEACTIVATE(kbd);
589 return (0);
593 * Read one byte from the keyboard if it's allowed
595 static int
596 kbdmux_read(keyboard_t *kbd, int wait)
598 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
599 int c;
601 KBDMUX_LOCK(state);
602 c = kbdmux_kbd_getc(state);
603 KBDMUX_UNLOCK(state);
605 if (c != -1)
606 kbd->kb_count ++;
608 return (KBD_IS_ACTIVE(kbd)? c : -1);
612 * Check if data is waiting
614 static int
615 kbdmux_check(keyboard_t *kbd)
617 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
618 int ready;
620 if (!KBD_IS_ACTIVE(kbd))
621 return (FALSE);
623 KBDMUX_LOCK(state);
624 ready = (state->ks_inq_length > 0) ? TRUE : FALSE;
625 KBDMUX_UNLOCK(state);
627 return (ready);
631 * Read char from the keyboard (stolen from atkbd.c)
633 static u_int
634 kbdmux_read_char(keyboard_t *kbd, int wait)
636 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
637 u_int action;
638 int scancode, keycode;
640 KBDMUX_LOCK(state);
642 next_code:
644 /* do we have a composed char to return? */
645 if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) {
646 action = state->ks_composed_char;
647 state->ks_composed_char = 0;
648 if (action > UCHAR_MAX) {
649 KBDMUX_UNLOCK(state);
651 return (ERRKEY);
654 KBDMUX_UNLOCK(state);
656 return (action);
659 /* see if there is something in the keyboard queue */
660 scancode = kbdmux_kbd_getc(state);
661 if (scancode == -1) {
662 if (state->ks_flags & POLLING) {
663 kbdmux_kbd_t *k;
665 SLIST_FOREACH(k, &state->ks_kbds, next) {
666 while (kbd_check_char(k->kbd)) {
667 scancode = kbd_read_char(k->kbd, 0);
668 if (scancode == NOKEY)
669 break;
670 if (scancode == ERRKEY)
671 continue;
672 if (!KBD_IS_BUSY(k->kbd))
673 continue;
675 kbdmux_kbd_putc(state, scancode);
679 if (state->ks_inq_length > 0)
680 goto next_code;
683 KBDMUX_UNLOCK(state);
684 return (NOKEY);
686 /* XXX FIXME: check for -1 if wait == 1! */
688 kbd->kb_count ++;
690 /* return the byte as is for the K_RAW mode */
691 if (state->ks_mode == K_RAW) {
692 KBDMUX_UNLOCK(state);
693 return (scancode);
696 /* translate the scan code into a keycode */
697 keycode = scancode & 0x7F;
698 switch (state->ks_prefix) {
699 case 0x00: /* normal scancode */
700 switch(scancode) {
701 case 0xB8: /* left alt (compose key) released */
702 if (state->ks_flags & COMPOSE) {
703 state->ks_flags &= ~COMPOSE;
704 if (state->ks_composed_char > UCHAR_MAX)
705 state->ks_composed_char = 0;
707 break;
708 case 0x38: /* left alt (compose key) pressed */
709 if (!(state->ks_flags & COMPOSE)) {
710 state->ks_flags |= COMPOSE;
711 state->ks_composed_char = 0;
713 break;
714 case 0xE0:
715 case 0xE1:
716 state->ks_prefix = scancode;
717 goto next_code;
719 break;
720 case 0xE0: /* 0xE0 prefix */
721 state->ks_prefix = 0;
722 switch (keycode) {
723 case 0x1C: /* right enter key */
724 keycode = 0x59;
725 break;
726 case 0x1D: /* right ctrl key */
727 keycode = 0x5A;
728 break;
729 case 0x35: /* keypad divide key */
730 keycode = 0x5B;
731 break;
732 case 0x37: /* print scrn key */
733 keycode = 0x5C;
734 break;
735 case 0x38: /* right alt key (alt gr) */
736 keycode = 0x5D;
737 break;
738 case 0x46: /* ctrl-pause/break on AT 101 (see below) */
739 keycode = 0x68;
740 break;
741 case 0x47: /* grey home key */
742 keycode = 0x5E;
743 break;
744 case 0x48: /* grey up arrow key */
745 keycode = 0x5F;
746 break;
747 case 0x49: /* grey page up key */
748 keycode = 0x60;
749 break;
750 case 0x4B: /* grey left arrow key */
751 keycode = 0x61;
752 break;
753 case 0x4D: /* grey right arrow key */
754 keycode = 0x62;
755 break;
756 case 0x4F: /* grey end key */
757 keycode = 0x63;
758 break;
759 case 0x50: /* grey down arrow key */
760 keycode = 0x64;
761 break;
762 case 0x51: /* grey page down key */
763 keycode = 0x65;
764 break;
765 case 0x52: /* grey insert key */
766 keycode = 0x66;
767 break;
768 case 0x53: /* grey delete key */
769 keycode = 0x67;
770 break;
771 /* the following 3 are only used on the MS "Natural" keyboard */
772 case 0x5b: /* left Window key */
773 keycode = 0x69;
774 break;
775 case 0x5c: /* right Window key */
776 keycode = 0x6a;
777 break;
778 case 0x5d: /* menu key */
779 keycode = 0x6b;
780 break;
781 case 0x5e: /* power key */
782 keycode = 0x6d;
783 break;
784 case 0x5f: /* sleep key */
785 keycode = 0x6e;
786 break;
787 case 0x63: /* wake key */
788 keycode = 0x6f;
789 break;
790 case 0x64: /* [JP106USB] backslash, underscore */
791 keycode = 0x73;
792 break;
793 default: /* ignore everything else */
794 goto next_code;
796 break;
797 case 0xE1: /* 0xE1 prefix */
799 * The pause/break key on the 101 keyboard produces:
800 * E1-1D-45 E1-9D-C5
801 * Ctrl-pause/break produces:
802 * E0-46 E0-C6 (See above.)
804 state->ks_prefix = 0;
805 if (keycode == 0x1D)
806 state->ks_prefix = 0x1D;
807 goto next_code;
808 /* NOT REACHED */
809 case 0x1D: /* pause / break */
810 state->ks_prefix = 0;
811 if (keycode != 0x45)
812 goto next_code;
813 keycode = 0x68;
814 break;
817 /* XXX assume 101/102 keys AT keyboard */
818 switch (keycode) {
819 case 0x5c: /* print screen */
820 if (state->ks_flags & ALTS)
821 keycode = 0x54; /* sysrq */
822 break;
823 case 0x68: /* pause/break */
824 if (state->ks_flags & CTLS)
825 keycode = 0x6c; /* break */
826 break;
829 /* return the key code in the K_CODE mode */
830 if (state->ks_mode == K_CODE) {
831 KBDMUX_UNLOCK(state);
832 return (keycode | (scancode & 0x80));
835 /* compose a character code */
836 if (state->ks_flags & COMPOSE) {
837 switch (keycode | (scancode & 0x80)) {
838 /* key pressed, process it */
839 case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */
840 state->ks_composed_char *= 10;
841 state->ks_composed_char += keycode - 0x40;
842 if (state->ks_composed_char > UCHAR_MAX) {
843 KBDMUX_UNLOCK(state);
844 return (ERRKEY);
846 goto next_code;
847 case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */
848 state->ks_composed_char *= 10;
849 state->ks_composed_char += keycode - 0x47;
850 if (state->ks_composed_char > UCHAR_MAX) {
851 KBDMUX_UNLOCK(state);
852 return (ERRKEY);
854 goto next_code;
855 case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */
856 state->ks_composed_char *= 10;
857 state->ks_composed_char += keycode - 0x4E;
858 if (state->ks_composed_char > UCHAR_MAX) {
859 KBDMUX_UNLOCK(state);
860 return (ERRKEY);
862 goto next_code;
863 case 0x52: /* keypad 0 */
864 state->ks_composed_char *= 10;
865 if (state->ks_composed_char > UCHAR_MAX) {
866 KBDMUX_UNLOCK(state);
867 return (ERRKEY);
869 goto next_code;
871 /* key released, no interest here */
872 case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */
873 case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */
874 case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */
875 case 0xD2: /* keypad 0 */
876 goto next_code;
878 case 0x38: /* left alt key */
879 break;
881 default:
882 if (state->ks_composed_char > 0) {
883 state->ks_flags &= ~COMPOSE;
884 state->ks_composed_char = 0;
885 KBDMUX_UNLOCK(state);
886 return (ERRKEY);
888 break;
892 /* keycode to key action */
893 action = genkbd_keyaction(kbd, keycode, scancode & 0x80,
894 &state->ks_state, &state->ks_accents);
895 if (action == NOKEY)
896 goto next_code;
898 KBDMUX_UNLOCK(state);
900 return (action);
904 * Check if char is waiting
906 static int
907 kbdmux_check_char(keyboard_t *kbd)
909 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
910 int ready;
912 if (!KBD_IS_ACTIVE(kbd))
913 return (FALSE);
915 KBDMUX_LOCK(state);
917 if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char != 0))
918 ready = TRUE;
919 else
920 ready = (state->ks_inq_length > 0) ? TRUE : FALSE;
922 KBDMUX_UNLOCK(state);
924 return (ready);
928 * Keyboard ioctl's
930 static int
931 kbdmux_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
933 static int delays[] = {
934 250, 500, 750, 1000
937 static int rates[] = {
938 34, 38, 42, 46, 50, 55, 59, 63,
939 68, 76, 84, 92, 100, 110, 118, 126,
940 136, 152, 168, 184, 200, 220, 236, 252,
941 272, 304, 336, 368, 400, 440, 472, 504
944 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
945 kbdmux_kbd_t *k;
946 keyboard_info_t *ki;
947 int error = 0, mode;
949 if (state == NULL)
950 return (ENXIO);
952 switch (cmd) {
953 case KBADDKBD: /* add keyboard to the mux */
954 ki = (keyboard_info_t *) arg;
956 if (ki == NULL || ki->kb_unit < 0 || ki->kb_name[0] == '\0' ||
957 strcmp(ki->kb_name, "*") == 0)
958 return (EINVAL); /* bad input */
960 KBDMUX_LOCK(state);
962 SLIST_FOREACH(k, &state->ks_kbds, next)
963 if (k->kbd->kb_unit == ki->kb_unit &&
964 strcmp(k->kbd->kb_name, ki->kb_name) == 0)
965 break;
967 if (k != NULL) {
968 KBDMUX_UNLOCK(state);
970 return (0); /* keyboard already in the mux */
973 k = kmalloc(sizeof(*k), M_KBDMUX, M_NOWAIT | M_ZERO);
974 if (k == NULL) {
975 KBDMUX_UNLOCK(state);
977 return (ENOMEM); /* out of memory */
980 k->kbd = kbd_get_keyboard(
981 kbd_allocate(
982 ki->kb_name,
983 ki->kb_unit,
984 (void *) &k->kbd,
985 kbdmux_kbd_event, (void *) state));
986 if (k->kbd == NULL) {
987 KBDMUX_UNLOCK(state);
988 kfree(k, M_KBDMUX);
990 return (EINVAL); /* bad keyboard */
993 kbd_enable(k->kbd);
994 kbd_clear_state(k->kbd);
996 /* set K_RAW mode on slave keyboard */
997 mode = K_RAW;
998 error = kbd_ioctl(k->kbd, KDSKBMODE, (caddr_t)&mode);
999 if (error == 0) {
1000 /* set lock keys state on slave keyboard */
1001 mode = state->ks_state & LOCK_MASK;
1002 error = kbd_ioctl(k->kbd, KDSKBSTATE, (caddr_t)&mode);
1005 if (error != 0) {
1006 KBDMUX_UNLOCK(state);
1008 kbd_release(k->kbd, &k->kbd);
1009 k->kbd = NULL;
1011 kfree(k, M_KBDMUX);
1013 return (error); /* could not set mode */
1016 SLIST_INSERT_HEAD(&state->ks_kbds, k, next);
1018 KBDMUX_UNLOCK(state);
1019 break;
1021 case KBRELKBD: /* release keyboard from the mux */
1022 ki = (keyboard_info_t *) arg;
1024 if (ki == NULL || ki->kb_unit < 0 || ki->kb_name[0] == '\0' ||
1025 strcmp(ki->kb_name, "*") == 0)
1026 return (EINVAL); /* bad input */
1028 KBDMUX_LOCK(state);
1030 SLIST_FOREACH(k, &state->ks_kbds, next)
1031 if (k->kbd->kb_unit == ki->kb_unit &&
1032 strcmp(k->kbd->kb_name, ki->kb_name) == 0)
1033 break;
1035 if (k != NULL) {
1036 error = kbd_release(k->kbd, &k->kbd);
1037 if (error == 0) {
1038 SLIST_REMOVE(&state->ks_kbds, k, kbdmux_kbd, next);
1040 k->kbd = NULL;
1042 kfree(k, M_KBDMUX);
1044 } else
1045 error = ENXIO; /* keyboard is not in the mux */
1047 KBDMUX_UNLOCK(state);
1048 break;
1050 case KDGKBMODE: /* get kyboard mode */
1051 KBDMUX_LOCK(state);
1052 *(int *)arg = state->ks_mode;
1053 KBDMUX_UNLOCK(state);
1054 break;
1056 case KDSKBMODE: /* set keyboard mode */
1057 KBDMUX_LOCK(state);
1059 switch (*(int *)arg) {
1060 case K_XLATE:
1061 if (state->ks_mode != K_XLATE) {
1062 /* make lock key state and LED state match */
1063 state->ks_state &= ~LOCK_MASK;
1064 state->ks_state |= KBD_LED_VAL(kbd);
1066 /* FALLTHROUGH */
1068 case K_RAW:
1069 case K_CODE:
1070 if (state->ks_mode != *(int *)arg) {
1071 kbdmux_clear_state_locked(state);
1072 state->ks_mode = *(int *)arg;
1074 break;
1076 default:
1077 error = EINVAL;
1078 break;
1081 KBDMUX_UNLOCK(state);
1082 break;
1084 case KDGETLED: /* get keyboard LED */
1085 KBDMUX_LOCK(state);
1086 *(int *)arg = KBD_LED_VAL(kbd);
1087 KBDMUX_UNLOCK(state);
1088 break;
1090 case KDSETLED: /* set keyboard LED */
1091 KBDMUX_LOCK(state);
1093 /* NOTE: lock key state in ks_state won't be changed */
1094 if (*(int *)arg & ~LOCK_MASK) {
1095 KBDMUX_UNLOCK(state);
1097 return (EINVAL);
1100 KBD_LED_VAL(kbd) = *(int *)arg;
1102 /* KDSETLED on all slave keyboards */
1103 SLIST_FOREACH(k, &state->ks_kbds, next)
1104 kbd_ioctl(k->kbd, KDSETLED, arg);
1106 KBDMUX_UNLOCK(state);
1107 break;
1109 case KDGKBSTATE: /* get lock key state */
1110 KBDMUX_LOCK(state);
1111 *(int *)arg = state->ks_state & LOCK_MASK;
1112 KBDMUX_UNLOCK(state);
1113 break;
1115 case KDSKBSTATE: /* set lock key state */
1116 KBDMUX_LOCK(state);
1118 if (*(int *)arg & ~LOCK_MASK) {
1119 KBDMUX_UNLOCK(state);
1121 return (EINVAL);
1124 state->ks_state &= ~LOCK_MASK;
1125 state->ks_state |= *(int *)arg;
1127 /* KDSKBSTATE on all slave keyboards */
1128 SLIST_FOREACH(k, &state->ks_kbds, next)
1129 kbd_ioctl(k->kbd, KDSKBSTATE, arg);
1131 KBDMUX_UNLOCK(state);
1133 return (kbdmux_ioctl(kbd, KDSETLED, arg));
1134 /* NOT REACHED */
1136 case KDSETREPEAT: /* set keyboard repeat rate (new interface) */
1137 case KDSETRAD: /* set keyboard repeat rate (old interface) */
1138 KBDMUX_LOCK(state);
1140 if (cmd == KDSETREPEAT) {
1141 int i;
1143 /* lookup delay */
1144 for (i = sizeof(delays)/sizeof(delays[0]) - 1; i > 0; i --)
1145 if (((int *)arg)[0] >= delays[i])
1146 break;
1147 mode = i << 5;
1149 /* lookup rate */
1150 for (i = sizeof(rates)/sizeof(rates[0]) - 1; i > 0; i --)
1151 if (((int *)arg)[1] >= rates[i])
1152 break;
1153 mode |= i;
1154 } else
1155 mode = *(int *)arg;
1157 if (mode & ~0x7f) {
1158 KBDMUX_UNLOCK(state);
1160 return (EINVAL);
1163 kbd->kb_delay1 = delays[(mode >> 5) & 3];
1164 kbd->kb_delay2 = rates[mode & 0x1f];
1166 /* perform command on all slave keyboards */
1167 SLIST_FOREACH(k, &state->ks_kbds, next)
1168 kbd_ioctl(k->kbd, cmd, arg);
1170 KBDMUX_UNLOCK(state);
1171 break;
1173 case PIO_KEYMAP: /* set keyboard translation table */
1174 case PIO_KEYMAPENT: /* set keyboard translation table entry */
1175 case PIO_DEADKEYMAP: /* set accent key translation table */
1176 KBDMUX_LOCK(state);
1177 state->ks_accents = 0;
1179 /* perform command on all slave keyboards */
1180 SLIST_FOREACH(k, &state->ks_kbds, next)
1181 kbd_ioctl(k->kbd, cmd, arg);
1183 KBDMUX_UNLOCK(state);
1184 /* FALLTHROUGH */
1186 default:
1187 error = genkbd_commonioctl(kbd, cmd, arg);
1188 break;
1191 return (error);
1195 * Lock the access to the keyboard
1197 static int
1198 kbdmux_lock(keyboard_t *kbd, int lock)
1200 return (1); /* XXX */
1204 * Clear the internal state of the keyboard
1206 static void
1207 kbdmux_clear_state_locked(kbdmux_state_t *state)
1209 KBDMUX_LOCK_ASSERT(state, MA_OWNED);
1211 state->ks_flags &= ~(COMPOSE|POLLING);
1212 state->ks_state &= LOCK_MASK; /* preserve locking key state */
1213 state->ks_accents = 0;
1214 state->ks_composed_char = 0;
1215 /* state->ks_prefix = 0; XXX */
1216 state->ks_inq_length = 0;
1219 static void
1220 kbdmux_clear_state(keyboard_t *kbd)
1222 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
1224 KBDMUX_LOCK(state);
1225 kbdmux_clear_state_locked(state);
1226 KBDMUX_UNLOCK(state);
1230 * Save the internal state
1232 static int
1233 kbdmux_get_state(keyboard_t *kbd, void *buf, size_t len)
1235 if (len == 0)
1236 return (sizeof(kbdmux_state_t));
1237 if (len < sizeof(kbdmux_state_t))
1238 return (-1);
1240 bcopy(kbd->kb_data, buf, sizeof(kbdmux_state_t)); /* XXX locking? */
1242 return (0);
1246 * Set the internal state
1248 static int
1249 kbdmux_set_state(keyboard_t *kbd, void *buf, size_t len)
1251 if (len < sizeof(kbdmux_state_t))
1252 return (ENOMEM);
1254 bcopy(buf, kbd->kb_data, sizeof(kbdmux_state_t)); /* XXX locking? */
1256 return (0);
1260 * Set polling
1262 static int
1263 kbdmux_poll(keyboard_t *kbd, int on)
1265 kbdmux_state_t *state = (kbdmux_state_t *) kbd->kb_data;
1266 kbdmux_kbd_t *k;
1268 KBDMUX_LOCK(state);
1270 if (on)
1271 state->ks_flags |= POLLING;
1272 else
1273 state->ks_flags &= ~POLLING;
1275 /* set poll on slave keyboards */
1276 SLIST_FOREACH(k, &state->ks_kbds, next)
1277 kbd_poll(k->kbd, on);
1279 KBDMUX_UNLOCK(state);
1281 return (0);
1284 /*****************************************************************************
1285 *****************************************************************************
1286 ** Module
1287 *****************************************************************************
1288 *****************************************************************************/
1290 KEYBOARD_DRIVER(kbdmux, kbdmuxsw, kbdmux_configure);
1292 static int
1293 kbdmux_modevent(module_t mod, int type, void *data)
1295 keyboard_switch_t *sw;
1296 keyboard_t *kbd;
1297 int error;
1299 switch (type) {
1300 case MOD_LOAD:
1301 if ((error = kbd_add_driver(&kbdmux_kbd_driver)) != 0)
1302 break;
1304 if ((sw = kbd_get_switch(KEYBOARD_NAME)) == NULL) {
1305 kbd_delete_driver(&kbdmux_kbd_driver);
1306 error = ENXIO;
1307 break;
1310 kbd = NULL;
1312 if ((error = (*sw->probe)(0, NULL, 0)) != 0 ||
1313 (error = (*sw->init)(0, &kbd, NULL, 0)) != 0) {
1314 kbd_delete_driver(&kbdmux_kbd_driver);
1315 break;
1318 #ifdef KBD_INSTALL_CDEV
1319 if ((error = kbd_attach(kbd)) != 0) {
1320 (*sw->term)(kbd);
1321 kbd_delete_driver(&kbdmux_kbd_driver);
1322 break;
1324 #endif
1326 if ((error = (*sw->enable)(kbd)) != 0) {
1327 (*sw->disable)(kbd);
1328 #ifdef KBD_INSTALL_CDEV
1329 kbd_detach(kbd);
1330 #endif
1331 (*sw->term)(kbd);
1332 kbd_delete_driver(&kbdmux_kbd_driver);
1333 break;
1335 break;
1337 case MOD_UNLOAD:
1338 if ((sw = kbd_get_switch(KEYBOARD_NAME)) == NULL)
1339 panic("kbd_get_switch(" KEYBOARD_NAME ") == NULL");
1341 kbd = kbd_get_keyboard(kbd_find_keyboard(KEYBOARD_NAME, 0));
1342 if (kbd != NULL) {
1343 (*sw->disable)(kbd);
1344 #ifdef KBD_INSTALL_CDEV
1345 kbd_detach(kbd);
1346 #endif
1347 (*sw->term)(kbd);
1348 kbd_delete_driver(&kbdmux_kbd_driver);
1350 error = 0;
1351 break;
1353 default:
1354 error = EOPNOTSUPP;
1355 break;
1358 return (error);
1361 DEV_MODULE(kbdmux, kbdmux_modevent, NULL);